From 5c7820641df22e4337d03aecdcfe2d2b580d9c78 Mon Sep 17 00:00:00 2001 From: Vanessa Scherma Date: Thu, 2 May 2024 13:42:33 +0200 Subject: [PATCH 01/65] configuration changes --- client/config/test.js | 8 ++-- client/package.json | 6 +-- client/yarn.lock | 87 +------------------------------------------ 3 files changed, 9 insertions(+), 92 deletions(-) diff --git a/client/config/test.js b/client/config/test.js index 93d8aa70e..c96f25b4b 100644 --- a/client/config/test.js +++ b/client/config/test.js @@ -1,7 +1,7 @@ if (typeof window !== 'undefined') { window.env = { REACT_APP_ENVIRONMENT: 'test', - REACT_APP_URL: 'http://localhost:4000/', + REACT_APP_URL: 'http://localhost:5000/', REACT_APP_URL_BASENAME: '', REACT_APP_URL_DTLINK: '/lab', REACT_APP_URL_LIBLINK: '', @@ -10,10 +10,10 @@ if (typeof window !== 'undefined') { REACT_APP_WORKBENCHLINK_JUPYTERLAB: '/lab', REACT_APP_WORKBENCHLINK_JUPYTERNOTEBOOK: '', - REACT_APP_CLIENT_ID: '1be55736756190b3ace4c2c4fb19bde386d1dcc748d20b47ea8cfb5935b8446c', + REACT_APP_CLIENT_ID: '26ebf8d8cc544f9a78b3c4541748ebe2a8a7ceb5eba616e24d3054d7accee248', REACT_APP_AUTH_AUTHORITY: 'https://gitlab.com/', - REACT_APP_REDIRECT_URI: 'http://localhost:4000/Library', - REACT_APP_LOGOUT_REDIRECT_URI: 'http://localhost:4000/', + REACT_APP_REDIRECT_URI: 'http://localhost:5000/Library', + REACT_APP_LOGOUT_REDIRECT_URI: 'http://localhost:5000/', REACT_APP_GITLAB_SCOPES: 'openid profile read_user read_repository api', }; }; diff --git a/client/package.json b/client/package.json index 0eb00cb4c..7819fd00b 100644 --- a/client/package.json +++ b/client/package.json @@ -23,11 +23,11 @@ "develop": "npx react-scripts start", "format": "prettier --ignore-path ../.gitignore --write \"**/*.{ts,tsx,css,scss}\"", "graph": "npx madge --image src.svg src && npx madge --image test.svg test", - "start": "serve -s build -l 4000", - "stop": "npx kill-port 4000", + "start": "serve -s build -l 5000", + "stop": "npx kill-port 5000", "syntax": "npx eslint . --fix", "test:all": "yarn test:unit && yarn test:int && yarn test:e2e", - "test:e2e": "yarn build && yarn configapp:test && npx kill-port 4000 && yarn start >/dev/null & playwright test && npx kill-port 4000", + "test:e2e": "yarn build && yarn configapp:test && npx kill-port 5000 && yarn start >/dev/null & playwright test && npx kill-port 5000", "test:int": "jest -c ./jest.integration.config.json ../test/integration --coverage", "test:unit": "jest -c ./jest.config.json ../test/unitTests --coverage" }, diff --git a/client/yarn.lock b/client/yarn.lock index 2b0c412ec..01f2585a5 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -4064,16 +4064,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concat-stream@^1.4.7: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - confusing-browser-globals@^1.0.10, confusing-browser-globals@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" @@ -4178,15 +4168,6 @@ create-jest@^29.7.0: jest-util "^29.7.0" prompts "^2.0.1" -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -7689,14 +7670,6 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -8163,11 +8136,6 @@ optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" -os-shim@^0.1.2: - version "0.1.3" - resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" - integrity sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A== - p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -8921,15 +8889,6 @@ postcss@^8.3.5, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.31, postcss@^8.4. picocolors "^1.0.0" source-map-js "^1.0.2" -pre-commit@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/pre-commit/-/pre-commit-1.2.2.tgz#dbcee0ee9de7235e57f79c56d7ce94641a69eec6" - integrity sha512-qokTiqxD6GjODy5ETAIgzsRgnBWWQHQH2ghy86PU7mIn/wuWeTwF3otyNQZxWBwVn8XNr8Tdzj/QfUXpH+gRZA== - dependencies: - cross-spawn "^5.0.1" - spawn-sync "^1.0.15" - which "1.2.x" - prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -9023,11 +8982,6 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== - psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -9315,7 +9269,7 @@ read-cache@^1.0.0: dependencies: pify "^2.3.0" -readable-stream@^2.0.1, readable-stream@^2.2.2: +readable-stream@^2.0.1: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -9861,13 +9815,6 @@ shallowequal@^1.1.0: resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== - dependencies: - shebang-regex "^1.0.0" - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -9875,11 +9822,6 @@ shebang-command@^2.0.0: dependencies: shebang-regex "^3.0.0" -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== - shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" @@ -10007,14 +9949,6 @@ sourcemap-codec@^1.4.8: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== -spawn-sync@^1.0.15: - version "1.0.15" - resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" - integrity sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw== - dependencies: - concat-stream "^1.4.7" - os-shim "^0.1.2" - spdy-transport@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" @@ -10688,11 +10622,6 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== - typescript@^4.9.5: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" @@ -11150,14 +11079,7 @@ which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" -which@1.2.x: - version "1.2.14" - resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" - integrity sha512-16uPglFkRPzgiUXYMi1Jf8Z5EzN1iB4V0ZtMXcHZnwsBtQhhHeCqoWw7tsUY42hJGNDWtUsVLTjakIa5BgAxCw== - dependencies: - isexe "^2.0.0" - -which@^1.2.9, which@^1.3.1: +which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -11423,11 +11345,6 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== - yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" From 2d2b717ce04ab0c147cfb29a7b47d232a53aa217 Mon Sep 17 00:00:00 2001 From: Vanessa Scherma Date: Thu, 2 May 2024 18:17:49 +0200 Subject: [PATCH 02/65] pre-commit-configuration --- .pre-commit-config.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8eeba7004..ea1991b6b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,21 +28,21 @@ repos: language: system files: "^client/.*" args: ["-c", "cd client && yarn jest . --coverage=false"] - stages: [pre-push] + stages: [push] - id: yarn-test-runner name: yarn test runner entry: bash language: system files: "^servers/execution/runner/.*" args: ["-c", "cd servers/execution/runner && yarn test:nocov"] - stages: [pre-push] + stages: [push] - id: yarn-test-lib name: yarn test lib entry: bash language: system files: "^servers/lib/.*" args: ["-c", "cd servers/lib && yarn test:nocov"] - stages: [pre-push] + stages: [push] - repo: https://github.com/pre-commit/mirrors-prettier rev: v3.1.0 @@ -50,7 +50,7 @@ repos: - id: prettier args: ["--ignore-path", "../.gitignore", "--write"] files: '^(client|servers/execution/runner|servers/lib)/.*\.(ts|tsx|css|scss)$' - stages: [pre-commit] + stages: [commit] - repo: https://github.com/pre-commit/mirrors-eslint rev: v8.54.0 @@ -58,7 +58,7 @@ repos: - id: eslint args: ["--fix"] files: "^(client|servers/execution/runner|servers/lib)/.*" - stages: [pre-commit] + stages: [commit] - repo: https://github.com/igorshubovych/markdownlint-cli rev: v0.37.0 @@ -67,22 +67,22 @@ repos: files: '.*\.md$' entry: ./script/markdownlint-hook.sh verbose: true - stages: [pre-commit] + stages: [commit] - repo: https://github.com/syntaqx/git-hooks rev: v0.0.18 hooks: - id: shellcheck files: '.*' - stages: [pre-commit] + stages: [commit] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - id: check-json files: '^(docs|deploy|script|ssl)/.*' - stages: [pre-commit] + stages: [commit] - id: check-yaml files: '^(docs|deploy|script|ssl)/.*' - stages: [pre-commit] + stages: [commit] \ No newline at end of file From 4a9740bb97b5ca75c60b6c48813277804d678d2a Mon Sep 17 00:00:00 2001 From: vanessa Date: Thu, 20 Jun 2024 22:52:32 +0200 Subject: [PATCH 03/65] class DigitalTwin added --- client/package.json | 3 +- client/src/util/gitlab.ts | 84 +++++++++++++++ client/test/unitTests/Util/gitlab.test.ts | 46 ++++++++ client/yarn.lock | 123 +++++++++++++++++++++- 4 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 client/src/util/gitlab.ts create mode 100644 client/test/unitTests/Util/gitlab.test.ts diff --git a/client/package.json b/client/package.json index d970ebc79..245357069 100644 --- a/client/package.json +++ b/client/package.json @@ -44,6 +44,7 @@ "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@fontsource/roboto": "^5.0.8", + "@gitbeaker/rest": "^40.0.3", "@mui/icons-material": "^5.14.8", "@mui/material": "^5.14.8", "@reduxjs/toolkit": "^1.9.7", @@ -106,4 +107,4 @@ "last 1 safari version" ] } -} \ No newline at end of file +} diff --git a/client/src/util/gitlab.ts b/client/src/util/gitlab.ts new file mode 100644 index 000000000..9492200bd --- /dev/null +++ b/client/src/util/gitlab.ts @@ -0,0 +1,84 @@ +import { Gitlab } from '@gitbeaker/rest'; + +class DigitalTwin { + DTName: string; + api: any; + logs: { status: string; parameters?: Map; error?: any }[]; + + constructor(DTName: string) { + this.DTName = DTName; + + this.api = new Gitlab({ + oauthToken: sessionStorage.access_token, + }); + this.logs = []; + } + + async getProjectId(): Promise { + try { + const project = await this.api.Projects.search(this.DTName); + + return project ? project.id : null; + } catch (error) { + console.error('Error fetching project ID:', error); + return null; + } + } + + + async getTriggerToken(projectId: number): Promise { + try { + const triggers = await this.api.PipelineTriggerTokens.all(projectId); + if (triggers && triggers.length > 0) { + return triggers[0].token; + } else { + console.error('No pipeline triggers found'); + return null; + } + } catch (error) { + console.error('Error fetching pipeline trigger token:', error); + return null; + } + } + + async execute(parameters: Map): Promise { + const projectId = await this.getProjectId(); + if (projectId === null) { + console.error('Project ID could not be determined'); + return false; + } + + const triggerToken = await this.getTriggerToken(projectId); + if (triggerToken === null) { + console.error('Pipeline trigger token could not be determined'); + return false; + } + + const variables = Object.fromEntries(parameters); + + try { + await this.api.PipelineTriggerTokens.trigger( + projectId, + 'main', + triggerToken, + { variables } + ); + this.logs.push({ status: 'success', parameters }); + return true; + } catch (error) { + console.error(error); + this.logs.push({ status: 'error', error, parameters }); + return false; + } + } + + executionStatus(): string[] { + return this.logs.map(log => log.status); + } + + executionLogs(): { status: string; parameters?: Map; error?: any }[] { + return this.logs; + } +} + +export default DigitalTwin; \ No newline at end of file diff --git a/client/test/unitTests/Util/gitlab.test.ts b/client/test/unitTests/Util/gitlab.test.ts new file mode 100644 index 000000000..0eb74ae62 --- /dev/null +++ b/client/test/unitTests/Util/gitlab.test.ts @@ -0,0 +1,46 @@ +import DigitalTwin from '../../../src/util/gitlab'; + +// Mock di @gitbeaker/rest per Jest +jest.mock('@gitbeaker/rest', () => { + const mockProjectsSearch = jest.fn().mockImplementation(async (name: string) => { + switch (name) { + case 'mass-spring-damper': + return { id: 1, name: 'mass-spring-damper' }; + case 'second DT': + return { id: 2, name: 'second DT '}; + case 'third DT': + return { id: 3, name: 'third DT' }; + default: + return null; // Ritorna null se non trova corrispondenze + } + }); + + const mockGitlabConstructor = jest.fn().mockImplementation(() => ({ + Projects: { + search: mockProjectsSearch + }, + PipelineTriggerTokens: { + trigger: jest.fn().mockResolvedValue({}) + } + })); + + return { + Gitlab: mockGitlabConstructor + }; +}); + + +describe('DigitalTwin', () => { + let dt: DigitalTwin; + + beforeEach(() => { + dt = new DigitalTwin('mass-spring-damper'); + console.log(dt); + }); + + it('should fetch project ID successfully', async () => { + const projectId = await dt.getProjectId(); + expect(projectId).toBe(1); + }); + +}); diff --git a/client/yarn.lock b/client/yarn.lock index 66969e099..2dec3f76f 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -1442,6 +1442,33 @@ resolved "https://registry.yarnpkg.com/@fontsource/roboto/-/roboto-5.0.8.tgz#613b477a56f21b5705db1a67e995c033ef317f76" integrity sha512-XxPltXs5R31D6UZeLIV1td3wTXU3jzd3f2DLsXI8tytMGBkIsGcc9sIyiupRtA8y73HAhuSCeweOoBqf6DbWCA== +"@gitbeaker/core@^40.0.3": + version "40.0.3" + resolved "https://registry.yarnpkg.com/@gitbeaker/core/-/core-40.0.3.tgz#27ce6a9778a98673c03d77cd5c79c63727898bd7" + integrity sha512-MzeY4oCtoa9zmPIkQIdC2KU8cGmHIXwnAi0L6jjjouqjy6kcA4BydZf8W5Xsj27Rw5iiyhfj8YC1/O3CgrzvCQ== + dependencies: + "@gitbeaker/requester-utils" "^40.0.3" + qs "^6.11.2" + xcase "^2.0.1" + +"@gitbeaker/requester-utils@^40.0.3": + version "40.0.3" + resolved "https://registry.yarnpkg.com/@gitbeaker/requester-utils/-/requester-utils-40.0.3.tgz#e30f84d3e1cdb2e21b6f92431cd9d6822de7e545" + integrity sha512-L8JpuMIsvXTHfu/2wXzkc5QyfQJSWg4XyEPStHq1ig5SAcbxxqbBoe8ed27eUXLah+PcGrPInMK4cCMxhQm41g== + dependencies: + picomatch-browser "^2.2.6" + qs "^6.11.2" + rate-limiter-flexible "^4.0.0" + xcase "^2.0.1" + +"@gitbeaker/rest@^40.0.3": + version "40.0.3" + resolved "https://registry.yarnpkg.com/@gitbeaker/rest/-/rest-40.0.3.tgz#12ced15b57bf2774c3349efdcdc78b65f1a4f762" + integrity sha512-ihaA0GX3yCo4oUWbISkcjFMIw+WxDAC9L+bEYq2irz4wpv/0EpAU/0jKjggPzY4cGWL9VAyPhew77VeACv4YWw== + dependencies: + "@gitbeaker/core" "^40.0.3" + "@gitbeaker/requester-utils" "^40.0.3" + "@humanwhocodes/config-array@^0.11.13": version "0.11.13" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297" @@ -3755,6 +3782,17 @@ call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: get-intrinsic "^1.2.1" set-function-length "^1.1.1" +call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -4515,6 +4553,15 @@ define-data-property@^1.0.1, define-data-property@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" @@ -4877,6 +4924,18 @@ es-array-method-boxes-properly@^1.0.0: resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + es-get-iterator@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" @@ -5664,6 +5723,17 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -5833,6 +5903,13 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.2.2" +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + has-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" @@ -8294,6 +8371,11 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picomatch-browser@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/picomatch-browser/-/picomatch-browser-2.2.6.tgz#e0626204575eb49f019f2f2feac24fc3b53e7a8a" + integrity sha512-0ypsOQt9D4e3hziV8O4elD9uN0z/jtUEfxVRtNaAAtXIyUx9m/SzlO020i8YNL2aL/E6blOvvHQcin6HZlFy/w== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -9014,6 +9096,13 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +qs@^6.11.2: + version "6.12.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.1.tgz#39422111ca7cbdb70425541cba20c7d7b216599a" + integrity sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ== + dependencies: + side-channel "^1.0.6" + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" @@ -9048,6 +9137,11 @@ range-parser@^1.2.1, range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== +rate-limiter-flexible@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/rate-limiter-flexible/-/rate-limiter-flexible-4.0.1.tgz#79b0ce111abe9c5da41d6fddf7cca93cedd3a8fc" + integrity sha512-2/dGHpDFpeA0+755oUkW+EKyklqLS9lu0go9pDsbhqQjZcxfRyJ6LA4JI0+HAdZ2bemD/oOjUeZQB2lCZqXQfQ== + raw-body@2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" @@ -9791,6 +9885,18 @@ set-function-length@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + set-function-name@^2.0.0, set-function-name@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" @@ -9858,6 +9964,16 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -11325,6 +11441,11 @@ ws@^8.11.0, ws@^8.13.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== +xcase@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9" + integrity sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw== + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" @@ -11404,4 +11525,4 @@ yargs@^17.3.1: yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== \ No newline at end of file + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From e7f29645d3524ac6314bf6f519643bedea42e5b7 Mon Sep 17 00:00:00 2001 From: vanessa Date: Thu, 20 Jun 2024 22:57:35 +0200 Subject: [PATCH 04/65] configuration changes --- .pre-commit-config.yaml | 18 ++-- client/config/local.js | 2 +- client/config/test.js | 8 +- client/package.json | 9 +- client/yarn.lock | 210 ++++++++++++++++------------------------ 5 files changed, 104 insertions(+), 143 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ea1991b6b..8eeba7004 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,21 +28,21 @@ repos: language: system files: "^client/.*" args: ["-c", "cd client && yarn jest . --coverage=false"] - stages: [push] + stages: [pre-push] - id: yarn-test-runner name: yarn test runner entry: bash language: system files: "^servers/execution/runner/.*" args: ["-c", "cd servers/execution/runner && yarn test:nocov"] - stages: [push] + stages: [pre-push] - id: yarn-test-lib name: yarn test lib entry: bash language: system files: "^servers/lib/.*" args: ["-c", "cd servers/lib && yarn test:nocov"] - stages: [push] + stages: [pre-push] - repo: https://github.com/pre-commit/mirrors-prettier rev: v3.1.0 @@ -50,7 +50,7 @@ repos: - id: prettier args: ["--ignore-path", "../.gitignore", "--write"] files: '^(client|servers/execution/runner|servers/lib)/.*\.(ts|tsx|css|scss)$' - stages: [commit] + stages: [pre-commit] - repo: https://github.com/pre-commit/mirrors-eslint rev: v8.54.0 @@ -58,7 +58,7 @@ repos: - id: eslint args: ["--fix"] files: "^(client|servers/execution/runner|servers/lib)/.*" - stages: [commit] + stages: [pre-commit] - repo: https://github.com/igorshubovych/markdownlint-cli rev: v0.37.0 @@ -67,22 +67,22 @@ repos: files: '.*\.md$' entry: ./script/markdownlint-hook.sh verbose: true - stages: [commit] + stages: [pre-commit] - repo: https://github.com/syntaqx/git-hooks rev: v0.0.18 hooks: - id: shellcheck files: '.*' - stages: [commit] + stages: [pre-commit] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - id: check-json files: '^(docs|deploy|script|ssl)/.*' - stages: [commit] + stages: [pre-commit] - id: check-yaml files: '^(docs|deploy|script|ssl)/.*' - stages: [commit] + stages: [pre-commit] \ No newline at end of file diff --git a/client/config/local.js b/client/config/local.js index 9a9fc669b..30de796f0 100644 --- a/client/config/local.js +++ b/client/config/local.js @@ -16,4 +16,4 @@ if (typeof window !== 'undefined') { REACT_APP_LOGOUT_REDIRECT_URI: 'http://localhost/', REACT_APP_GITLAB_SCOPES: 'openid profile read_user read_repository api', }; -}; +}; \ No newline at end of file diff --git a/client/config/test.js b/client/config/test.js index 3c947900e..5d4dfbd71 100644 --- a/client/config/test.js +++ b/client/config/test.js @@ -1,7 +1,7 @@ if (typeof window !== 'undefined') { window.env = { REACT_APP_ENVIRONMENT: 'test', - REACT_APP_URL: 'http://localhost:5000/', + REACT_APP_URL: 'http://localhost:4000/', REACT_APP_URL_BASENAME: '', REACT_APP_URL_DTLINK: '/lab', REACT_APP_URL_LIBLINK: '', @@ -10,10 +10,10 @@ if (typeof window !== 'undefined') { REACT_APP_WORKBENCHLINK_JUPYTERLAB: '/lab', REACT_APP_WORKBENCHLINK_JUPYTERNOTEBOOK: '', - REACT_APP_CLIENT_ID: '26ebf8d8cc544f9a78b3c4541748ebe2a8a7ceb5eba616e24d3054d7accee248', + REACT_APP_CLIENT_ID: '1be55736756190b3ace4c2c4fb19bde386d1dcc748d20b47ea8cfb5935b8446c', REACT_APP_AUTH_AUTHORITY: 'https://gitlab.com/', - REACT_APP_REDIRECT_URI: 'http://localhost:5000/Library', - REACT_APP_LOGOUT_REDIRECT_URI: 'http://localhost:5000/', + REACT_APP_REDIRECT_URI: 'http://localhost:4000/Library', + REACT_APP_LOGOUT_REDIRECT_URI: 'http://localhost:4000/', REACT_APP_GITLAB_SCOPES: 'openid profile read_user read_repository api', }; }; \ No newline at end of file diff --git a/client/package.json b/client/package.json index 245357069..93a2190d3 100644 --- a/client/package.json +++ b/client/package.json @@ -24,11 +24,11 @@ "develop": "npx react-scripts start", "format": "prettier --ignore-path ../.gitignore --write \"**/*.{ts,tsx,css,scss}\"", "graph": "npx madge --image src.svg src && npx madge --image test.svg test", - "start": "serve -s build -l 5000", - "stop": "npx kill-port 5000", + "start": "serve -s build -l 4000", + "stop": "npx kill-port 4000", "syntax": "npx eslint . --fix", "test:all": "yarn test:unit && yarn test:int && yarn test:e2e", - "test:e2e": "yarn build && yarn configapp:test && npx kill-port 5000 && yarn start >/dev/null & playwright test && npx kill-port 5000", + "test:e2e": "yarn build && yarn configapp:test && npx kill-port 4000 && yarn start >/dev/null & playwright test && npx kill-port 4000", "test:int": "jest -c ./jest.integration.config.json ../test/integration --coverage", "test:unit": "jest -c ./jest.config.json ../test/unitTests --coverage" }, @@ -44,7 +44,6 @@ "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@fontsource/roboto": "^5.0.8", - "@gitbeaker/rest": "^40.0.3", "@mui/icons-material": "^5.14.8", "@mui/material": "^5.14.8", "@reduxjs/toolkit": "^1.9.7", @@ -107,4 +106,4 @@ "last 1 safari version" ] } -} +} \ No newline at end of file diff --git a/client/yarn.lock b/client/yarn.lock index 2dec3f76f..e648c3841 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -1442,33 +1442,6 @@ resolved "https://registry.yarnpkg.com/@fontsource/roboto/-/roboto-5.0.8.tgz#613b477a56f21b5705db1a67e995c033ef317f76" integrity sha512-XxPltXs5R31D6UZeLIV1td3wTXU3jzd3f2DLsXI8tytMGBkIsGcc9sIyiupRtA8y73HAhuSCeweOoBqf6DbWCA== -"@gitbeaker/core@^40.0.3": - version "40.0.3" - resolved "https://registry.yarnpkg.com/@gitbeaker/core/-/core-40.0.3.tgz#27ce6a9778a98673c03d77cd5c79c63727898bd7" - integrity sha512-MzeY4oCtoa9zmPIkQIdC2KU8cGmHIXwnAi0L6jjjouqjy6kcA4BydZf8W5Xsj27Rw5iiyhfj8YC1/O3CgrzvCQ== - dependencies: - "@gitbeaker/requester-utils" "^40.0.3" - qs "^6.11.2" - xcase "^2.0.1" - -"@gitbeaker/requester-utils@^40.0.3": - version "40.0.3" - resolved "https://registry.yarnpkg.com/@gitbeaker/requester-utils/-/requester-utils-40.0.3.tgz#e30f84d3e1cdb2e21b6f92431cd9d6822de7e545" - integrity sha512-L8JpuMIsvXTHfu/2wXzkc5QyfQJSWg4XyEPStHq1ig5SAcbxxqbBoe8ed27eUXLah+PcGrPInMK4cCMxhQm41g== - dependencies: - picomatch-browser "^2.2.6" - qs "^6.11.2" - rate-limiter-flexible "^4.0.0" - xcase "^2.0.1" - -"@gitbeaker/rest@^40.0.3": - version "40.0.3" - resolved "https://registry.yarnpkg.com/@gitbeaker/rest/-/rest-40.0.3.tgz#12ced15b57bf2774c3349efdcdc78b65f1a4f762" - integrity sha512-ihaA0GX3yCo4oUWbISkcjFMIw+WxDAC9L+bEYq2irz4wpv/0EpAU/0jKjggPzY4cGWL9VAyPhew77VeACv4YWw== - dependencies: - "@gitbeaker/core" "^40.0.3" - "@gitbeaker/requester-utils" "^40.0.3" - "@humanwhocodes/config-array@^0.11.13": version "0.11.13" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297" @@ -3782,17 +3755,6 @@ call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: get-intrinsic "^1.2.1" set-function-length "^1.1.1" -call-bind@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - set-function-length "^1.2.1" - callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -4102,6 +4064,16 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +concat-stream@^1.4.7: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + confusing-browser-globals@^1.0.10, confusing-browser-globals@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" @@ -4206,6 +4178,15 @@ create-jest@^29.7.0: jest-util "^29.7.0" prompts "^2.0.1" +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -4553,15 +4534,6 @@ define-data-property@^1.0.1, define-data-property@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" -define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" @@ -4924,18 +4896,6 @@ es-array-method-boxes-properly@^1.0.0: resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - es-get-iterator@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" @@ -5723,17 +5683,6 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" -get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -5903,13 +5852,6 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.2.2" -has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - has-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" @@ -7747,6 +7689,14 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -8213,6 +8163,11 @@ optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" +os-shim@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" + integrity sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A== + p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -8371,11 +8326,6 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch-browser@^2.2.6: - version "2.2.6" - resolved "https://registry.yarnpkg.com/picomatch-browser/-/picomatch-browser-2.2.6.tgz#e0626204575eb49f019f2f2feac24fc3b53e7a8a" - integrity sha512-0ypsOQt9D4e3hziV8O4elD9uN0z/jtUEfxVRtNaAAtXIyUx9m/SzlO020i8YNL2aL/E6blOvvHQcin6HZlFy/w== - picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -8971,6 +8921,15 @@ postcss@^8.3.5, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.31, postcss@^8.4. picocolors "^1.0.0" source-map-js "^1.0.2" +pre-commit@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/pre-commit/-/pre-commit-1.2.2.tgz#dbcee0ee9de7235e57f79c56d7ce94641a69eec6" + integrity sha512-qokTiqxD6GjODy5ETAIgzsRgnBWWQHQH2ghy86PU7mIn/wuWeTwF3otyNQZxWBwVn8XNr8Tdzj/QfUXpH+gRZA== + dependencies: + cross-spawn "^5.0.1" + spawn-sync "^1.0.15" + which "1.2.x" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -9064,6 +9023,11 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== + psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -9096,13 +9060,6 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" -qs@^6.11.2: - version "6.12.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.1.tgz#39422111ca7cbdb70425541cba20c7d7b216599a" - integrity sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ== - dependencies: - side-channel "^1.0.6" - querystringify@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" @@ -9137,11 +9094,6 @@ range-parser@^1.2.1, range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -rate-limiter-flexible@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/rate-limiter-flexible/-/rate-limiter-flexible-4.0.1.tgz#79b0ce111abe9c5da41d6fddf7cca93cedd3a8fc" - integrity sha512-2/dGHpDFpeA0+755oUkW+EKyklqLS9lu0go9pDsbhqQjZcxfRyJ6LA4JI0+HAdZ2bemD/oOjUeZQB2lCZqXQfQ== - raw-body@2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" @@ -9363,7 +9315,7 @@ read-cache@^1.0.0: dependencies: pify "^2.3.0" -readable-stream@^2.0.1: +readable-stream@^2.0.1, readable-stream@^2.2.2: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -9885,18 +9837,6 @@ set-function-length@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" -set-function-length@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" - integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - set-function-name@^2.0.0, set-function-name@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" @@ -9921,6 +9861,13 @@ shallowequal@^1.1.0: resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== + dependencies: + shebang-regex "^1.0.0" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -9928,6 +9875,11 @@ shebang-command@^2.0.0: dependencies: shebang-regex "^3.0.0" +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== + shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" @@ -9964,16 +9916,6 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -side-channel@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" - integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== - dependencies: - call-bind "^1.0.7" - es-errors "^1.3.0" - get-intrinsic "^1.2.4" - object-inspect "^1.13.1" - signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -10065,6 +10007,14 @@ sourcemap-codec@^1.4.8: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== +spawn-sync@^1.0.15: + version "1.0.15" + resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" + integrity sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw== + dependencies: + concat-stream "^1.4.7" + os-shim "^0.1.2" + spdy-transport@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" @@ -10738,6 +10688,11 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + typescript@^4.9.5: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" @@ -11195,7 +11150,14 @@ which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" -which@^1.3.1: +which@1.2.x: + version "1.2.14" + resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" + integrity sha512-16uPglFkRPzgiUXYMi1Jf8Z5EzN1iB4V0ZtMXcHZnwsBtQhhHeCqoWw7tsUY42hJGNDWtUsVLTjakIa5BgAxCw== + dependencies: + isexe "^2.0.0" + +which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -11441,11 +11403,6 @@ ws@^8.11.0, ws@^8.13.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== -xcase@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9" - integrity sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw== - xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" @@ -11466,6 +11423,11 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== + yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" @@ -11525,4 +11487,4 @@ yargs@^17.3.1: yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== \ No newline at end of file From 663cc3d9dd72be386b36703a69190c91dc84edd3 Mon Sep 17 00:00:00 2001 From: Vanessa Scherma Date: Tue, 30 Jul 2024 17:56:37 +0200 Subject: [PATCH 05/65] Update gitlab.ts and gitlab.test.ts --- client/package.json | 3 +- client/src/util/gitlab.ts | 52 ++++--- client/test/unitTests/Util/gitlab.test.ts | 165 +++++++++++++++++----- client/yarn.lock | 121 ++++++++++++++++ 4 files changed, 285 insertions(+), 56 deletions(-) diff --git a/client/package.json b/client/package.json index 99c74f152..e2ca66ee4 100644 --- a/client/package.json +++ b/client/package.json @@ -44,6 +44,7 @@ "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@fontsource/roboto": "^5.0.8", + "@gitbeaker/rest": "^40.1.2", "@mui/icons-material": "^5.14.8", "@mui/material": "^5.14.8", "@reduxjs/toolkit": "^1.9.7", @@ -106,4 +107,4 @@ "last 1 safari version" ] } -} \ No newline at end of file +} diff --git a/client/src/util/gitlab.ts b/client/src/util/gitlab.ts index 9492200bd..b8bb14722 100644 --- a/client/src/util/gitlab.ts +++ b/client/src/util/gitlab.ts @@ -1,40 +1,46 @@ -import { Gitlab } from '@gitbeaker/rest'; +import { ProjectSchema, PipelineTriggerTokenSchema } from '@gitbeaker/rest'; + +interface GitlabAPI { + Projects: { + search: (name: string) => Promise; + }; + PipelineTriggerTokens: { + all: (projectId: number) => Promise; + trigger: (projectId: number, ref: string, token: string, variables: { variables: Record }) => Promise; + }; +} + +interface LogEntry { + status: string; + parameters?: Map; + error?: any; +} class DigitalTwin { - DTName: string; - api: any; - logs: { status: string; parameters?: Map; error?: any }[]; + private DTName: string; + private api: GitlabAPI; + private logs: LogEntry[]; - constructor(DTName: string) { + constructor(DTName: string, api: GitlabAPI) { this.DTName = DTName; - - this.api = new Gitlab({ - oauthToken: sessionStorage.access_token, - }); + this.api = api; this.logs = []; } async getProjectId(): Promise { try { - const project = await this.api.Projects.search(this.DTName); - - return project ? project.id : null; + const projects: ProjectSchema[] = await this.api.Projects.search(this.DTName); + return projects.length > 0 ? projects[0].id : null; } catch (error) { console.error('Error fetching project ID:', error); return null; } } - async getTriggerToken(projectId: number): Promise { try { - const triggers = await this.api.PipelineTriggerTokens.all(projectId); - if (triggers && triggers.length > 0) { - return triggers[0].token; - } else { - console.error('No pipeline triggers found'); - return null; - } + const triggers: PipelineTriggerTokenSchema[] = await this.api.PipelineTriggerTokens.all(projectId); + return triggers.length > 0 ? triggers[0].token : null; } catch (error) { console.error('Error fetching pipeline trigger token:', error); return null; @@ -59,14 +65,14 @@ class DigitalTwin { try { await this.api.PipelineTriggerTokens.trigger( projectId, - 'main', + 'main', triggerToken, { variables } ); this.logs.push({ status: 'success', parameters }); return true; } catch (error) { - console.error(error); + console.error('Error triggering pipeline:', error); this.logs.push({ status: 'error', error, parameters }); return false; } @@ -76,7 +82,7 @@ class DigitalTwin { return this.logs.map(log => log.status); } - executionLogs(): { status: string; parameters?: Map; error?: any }[] { + executionLogs(): LogEntry[] { return this.logs; } } diff --git a/client/test/unitTests/Util/gitlab.test.ts b/client/test/unitTests/Util/gitlab.test.ts index 0eb74ae62..4538d2bb9 100644 --- a/client/test/unitTests/Util/gitlab.test.ts +++ b/client/test/unitTests/Util/gitlab.test.ts @@ -1,46 +1,147 @@ -import DigitalTwin from '../../../src/util/gitlab'; - -// Mock di @gitbeaker/rest per Jest -jest.mock('@gitbeaker/rest', () => { - const mockProjectsSearch = jest.fn().mockImplementation(async (name: string) => { - switch (name) { - case 'mass-spring-damper': - return { id: 1, name: 'mass-spring-damper' }; - case 'second DT': - return { id: 2, name: 'second DT '}; - case 'third DT': - return { id: 3, name: 'third DT' }; - default: - return null; // Ritorna null se non trova corrispondenze - } - }); - - const mockGitlabConstructor = jest.fn().mockImplementation(() => ({ - Projects: { - search: mockProjectsSearch - }, - PipelineTriggerTokens: { - trigger: jest.fn().mockResolvedValue({}) - } - })); - - return { - Gitlab: mockGitlabConstructor - }; -}); +import DigitalTwin from '../../../src/util/gitlab'; // Aggiorna il percorso in base alla tua struttura +import { ProjectSchema, PipelineTriggerTokenSchema } from '@gitbeaker/rest'; +const mockApi = { + Projects: { + search: jest.fn() + }, + PipelineTriggerTokens: { + all: jest.fn(), + trigger: jest.fn() + } +}; describe('DigitalTwin', () => { let dt: DigitalTwin; beforeEach(() => { - dt = new DigitalTwin('mass-spring-damper'); - console.log(dt); + dt = new DigitalTwin('test-project', mockApi); }); it('should fetch project ID successfully', async () => { + mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-project' } as ProjectSchema]); + const projectId = await dt.getProjectId(); + expect(projectId).toBe(1); + expect(mockApi.Projects.search).toHaveBeenCalledWith('test-project'); + }); + + it('should handle project ID not found', async () => { + mockApi.Projects.search.mockResolvedValue([]); + + const projectId = await dt.getProjectId(); + + expect(projectId).toBeNull(); + expect(mockApi.Projects.search).toHaveBeenCalledWith('test-project'); + }); + + it('should handle errors fetching project ID', async () => { + mockApi.Projects.search.mockRejectedValue(new Error('API error')); + + const projectId = await dt.getProjectId(); + + expect(projectId).toBeNull(); + expect(mockApi.Projects.search).toHaveBeenCalledWith('test-project'); + }); + + it('should fetch trigger token successfully', async () => { + mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); + + const token = await dt.getTriggerToken(1); + + expect(token).toBe('test-token'); + expect(mockApi.PipelineTriggerTokens.all).toHaveBeenCalledWith(1); + }); + + it('should handle no trigger tokens found', async () => { + mockApi.PipelineTriggerTokens.all.mockResolvedValue([]); + + const token = await dt.getTriggerToken(1); + + expect(token).toBeNull(); + expect(mockApi.PipelineTriggerTokens.all).toHaveBeenCalledWith(1); }); + it('should handle errors fetching trigger token', async () => { + mockApi.PipelineTriggerTokens.all.mockRejectedValue(new Error('API error')); + + const token = await dt.getTriggerToken(1); + + expect(token).toBeNull(); + expect(mockApi.PipelineTriggerTokens.all).toHaveBeenCalledWith(1); + }); + + it('should handle null project ID during pipeline execution', async () => { + mockApi.Projects.search.mockResolvedValue([]); + const parameters = new Map([['DTName', 'digital_twin_name']]); + + const success = await dt.execute(parameters); + + expect(success).toBe(false); + expect(mockApi.PipelineTriggerTokens.trigger).not.toHaveBeenCalled(); // Verifica che non sia stata chiamata + }); + + it('should handle null trigger token during pipeline execution', async () => { + mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-project' } as ProjectSchema]); + mockApi.PipelineTriggerTokens.all.mockResolvedValue([]); + const parameters = new Map([['DTName', 'digital_twin_name']]); + + const success = await dt.execute(parameters); + + expect(success).toBe(false); + expect(mockApi.PipelineTriggerTokens.trigger).not.toHaveBeenCalled(); // Verifica che non sia stata chiamata + }); + + it('should execute pipeline successfully', async () => { + mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-project' } as ProjectSchema]); + mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); + mockApi.PipelineTriggerTokens.trigger.mockResolvedValue(undefined); + + const parameters = new Map([['DTName', 'digital_twin_name'], ['RunnerTag', 'runner_tag']]); + const success = await dt.execute(parameters); + + expect(success).toBe(true); + expect(mockApi.PipelineTriggerTokens.trigger).toHaveBeenCalledWith( + 1, + 'main', + 'test-token', + { variables: { DTName: 'digital_twin_name', RunnerTag: 'runner_tag' } } + ); + expect(dt.executionStatus()).toContain('success'); + }); + + it('should handle pipeline execution failure', async () => { + mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-project' } as ProjectSchema]); + mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); + mockApi.PipelineTriggerTokens.trigger.mockRejectedValue(new Error('Trigger failed')); + + const parameters = new Map([['DTName', 'digital_twin_name'], ['RunnerTag', 'runner_tag']]); + const success = await dt.execute(parameters); + + expect(success).toBe(false); + expect(mockApi.PipelineTriggerTokens.trigger).toHaveBeenCalledWith( + 1, + 'main', + 'test-token', + { variables: { DTName: 'digital_twin_name', RunnerTag: 'runner_tag' } } + ); + expect(dt.executionStatus()).toContain('error'); + expect(dt.executionLogs()[0].error).toBeInstanceOf(Error); + expect(dt.executionLogs()[0].error.message).toBe('Trigger failed'); + }); + + it('should return execution logs', async () => { + mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-project' } as ProjectSchema]); + mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); + mockApi.PipelineTriggerTokens.trigger.mockResolvedValue(undefined); + + const parameters = new Map([['DTName', 'digital_twin_name']]); + await dt.execute(parameters); + + const logs = dt.executionLogs(); + expect(logs).toHaveLength(1); + expect(logs[0].status).toBe('success'); + expect(logs[0].parameters).toEqual(parameters); + }); }); diff --git a/client/yarn.lock b/client/yarn.lock index 869742b40..86696d0f5 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -1442,6 +1442,33 @@ resolved "https://registry.yarnpkg.com/@fontsource/roboto/-/roboto-5.0.8.tgz#613b477a56f21b5705db1a67e995c033ef317f76" integrity sha512-XxPltXs5R31D6UZeLIV1td3wTXU3jzd3f2DLsXI8tytMGBkIsGcc9sIyiupRtA8y73HAhuSCeweOoBqf6DbWCA== +"@gitbeaker/core@^40.1.2": + version "40.1.2" + resolved "https://registry.yarnpkg.com/@gitbeaker/core/-/core-40.1.2.tgz#27683d535f9025fa873e15596154db8575e994ce" + integrity sha512-KjP40EIdPWDX2w+GZUjgc5f9L/a0VsA6A2dkiKdH27m5BUpVrH3eHC3M0h1SsIA5edO3BiebqWFO2ipkGJCBZA== + dependencies: + "@gitbeaker/requester-utils" "^40.1.2" + qs "^6.12.2" + xcase "^2.0.1" + +"@gitbeaker/requester-utils@^40.1.2": + version "40.1.2" + resolved "https://registry.yarnpkg.com/@gitbeaker/requester-utils/-/requester-utils-40.1.2.tgz#90122f573570469ea25dde6188b02ea7a1d12dcc" + integrity sha512-s3c2TzJpBk3VGujEo/57+TAntg36kEhdob28i2kAEDztFWlNp0sfDT/jlDwUywKF6AbmYIJYe3FikLNyAiv+iA== + dependencies: + picomatch-browser "^2.2.6" + qs "^6.12.2" + rate-limiter-flexible "^4.0.1" + xcase "^2.0.1" + +"@gitbeaker/rest@^40.1.2": + version "40.1.2" + resolved "https://registry.yarnpkg.com/@gitbeaker/rest/-/rest-40.1.2.tgz#ab7424dd417fdda38266f13378448bd1693266fe" + integrity sha512-q9FYEC0SQcEbj1Hu+EOVFGRBjxbUFciK/iVD3BhBx+ZUGIPfHYS32EWL4uqVSBLq/zSUEd2gWjLy7LRVq/Pz3A== + dependencies: + "@gitbeaker/core" "^40.1.2" + "@gitbeaker/requester-utils" "^40.1.2" + "@humanwhocodes/config-array@^0.11.13": version "0.11.13" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297" @@ -3755,6 +3782,17 @@ call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: get-intrinsic "^1.2.1" set-function-length "^1.1.1" +call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -4515,6 +4553,15 @@ define-data-property@^1.0.1, define-data-property@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" @@ -4877,6 +4924,18 @@ es-array-method-boxes-properly@^1.0.0: resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + es-get-iterator@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" @@ -5664,6 +5723,17 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -5833,6 +5903,13 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.2.2" +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + has-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" @@ -8294,6 +8371,11 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picomatch-browser@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/picomatch-browser/-/picomatch-browser-2.2.6.tgz#e0626204575eb49f019f2f2feac24fc3b53e7a8a" + integrity sha512-0ypsOQt9D4e3hziV8O4elD9uN0z/jtUEfxVRtNaAAtXIyUx9m/SzlO020i8YNL2aL/E6blOvvHQcin6HZlFy/w== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -9014,6 +9096,13 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +qs@^6.12.2: + version "6.12.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.3.tgz#e43ce03c8521b9c7fd7f1f13e514e5ca37727754" + integrity sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ== + dependencies: + side-channel "^1.0.6" + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" @@ -9048,6 +9137,11 @@ range-parser@^1.2.1, range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== +rate-limiter-flexible@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/rate-limiter-flexible/-/rate-limiter-flexible-4.0.1.tgz#79b0ce111abe9c5da41d6fddf7cca93cedd3a8fc" + integrity sha512-2/dGHpDFpeA0+755oUkW+EKyklqLS9lu0go9pDsbhqQjZcxfRyJ6LA4JI0+HAdZ2bemD/oOjUeZQB2lCZqXQfQ== + raw-body@2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" @@ -9791,6 +9885,18 @@ set-function-length@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + set-function-name@^2.0.0, set-function-name@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" @@ -9858,6 +9964,16 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -11325,6 +11441,11 @@ ws@^8.11.0, ws@^8.13.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== +xcase@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9" + integrity sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw== + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" From c35450d550daae6e487e3ae07ca667a9531fb8c0 Mon Sep 17 00:00:00 2001 From: Vanessa Scherma Date: Tue, 30 Jul 2024 18:17:28 +0200 Subject: [PATCH 06/65] Eslint fixes --- client/src/util/gitlab.ts | 13 +++------ client/test/unitTests/Util/gitlab.test.ts | 34 ++++++++++++++++++++--- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/client/src/util/gitlab.ts b/client/src/util/gitlab.ts index b8bb14722..b024f104d 100644 --- a/client/src/util/gitlab.ts +++ b/client/src/util/gitlab.ts @@ -12,8 +12,8 @@ interface GitlabAPI { interface LogEntry { status: string; - parameters?: Map; - error?: any; + parameters?: Map; + error?: Error; } class DigitalTwin { @@ -32,7 +32,6 @@ class DigitalTwin { const projects: ProjectSchema[] = await this.api.Projects.search(this.DTName); return projects.length > 0 ? projects[0].id : null; } catch (error) { - console.error('Error fetching project ID:', error); return null; } } @@ -42,21 +41,18 @@ class DigitalTwin { const triggers: PipelineTriggerTokenSchema[] = await this.api.PipelineTriggerTokens.all(projectId); return triggers.length > 0 ? triggers[0].token : null; } catch (error) { - console.error('Error fetching pipeline trigger token:', error); return null; } } - async execute(parameters: Map): Promise { + async execute(parameters: Map): Promise { const projectId = await this.getProjectId(); if (projectId === null) { - console.error('Project ID could not be determined'); return false; } const triggerToken = await this.getTriggerToken(projectId); if (triggerToken === null) { - console.error('Pipeline trigger token could not be determined'); return false; } @@ -72,8 +68,7 @@ class DigitalTwin { this.logs.push({ status: 'success', parameters }); return true; } catch (error) { - console.error('Error triggering pipeline:', error); - this.logs.push({ status: 'error', error, parameters }); + this.logs.push({ status: 'error', error: error instanceof Error ? error : new Error(String(error)), parameters }); return false; } } diff --git a/client/test/unitTests/Util/gitlab.test.ts b/client/test/unitTests/Util/gitlab.test.ts index 4538d2bb9..a0c0765d4 100644 --- a/client/test/unitTests/Util/gitlab.test.ts +++ b/client/test/unitTests/Util/gitlab.test.ts @@ -111,10 +111,34 @@ describe('DigitalTwin', () => { expect(dt.executionStatus()).toContain('success'); }); - it('should handle pipeline execution failure', async () => { + it('should handle non-Error thrown during pipeline execution', async () => { mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-project' } as ProjectSchema]); mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); - mockApi.PipelineTriggerTokens.trigger.mockRejectedValue(new Error('Trigger failed')); + + mockApi.PipelineTriggerTokens.trigger.mockRejectedValue('String error message'); + + const parameters = new Map([['DTName', 'digital_twin_name'], ['RunnerTag', 'runner_tag']]); + const success = await dt.execute(parameters); + + expect(success).toBe(false); + expect(mockApi.PipelineTriggerTokens.trigger).toHaveBeenCalledWith( + 1, + 'main', + 'test-token', + { variables: { DTName: 'digital_twin_name', RunnerTag: 'runner_tag' } } + ); + expect(dt.executionStatus()).toContain('error'); + const logs = dt.executionLogs(); + expect(logs).toHaveLength(1); + expect(logs[0].error).toBeInstanceOf(Error); + expect(logs[0].error?.message).toBe('String error message'); + }); + + it('should handle Error thrown during pipeline execution', async () => { + mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-project' } as ProjectSchema]); + mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); + + mockApi.PipelineTriggerTokens.trigger.mockRejectedValue(new Error('Error instance message')); const parameters = new Map([['DTName', 'digital_twin_name'], ['RunnerTag', 'runner_tag']]); const success = await dt.execute(parameters); @@ -127,8 +151,10 @@ describe('DigitalTwin', () => { { variables: { DTName: 'digital_twin_name', RunnerTag: 'runner_tag' } } ); expect(dt.executionStatus()).toContain('error'); - expect(dt.executionLogs()[0].error).toBeInstanceOf(Error); - expect(dt.executionLogs()[0].error.message).toBe('Trigger failed'); + const logs = dt.executionLogs(); + expect(logs).toHaveLength(1); + expect(logs[0].error).toBeInstanceOf(Error); + expect(logs[0].error?.message).toBe('Error instance message'); }); it('should return execution logs', async () => { From 4f8e74aafc388d79a4d7c44e72eb408e607a8ccf Mon Sep 17 00:00:00 2001 From: Vanessa Scherma Date: Thu, 1 Aug 2024 11:38:28 +0200 Subject: [PATCH 07/65] gitlab class updated --- .gitignore | 3 +- client/src/util/gitlab.ts | 38 ++++++++++----- client/test/unitTests/Util/gitlab.test.ts | 58 ++++++++++------------- 3 files changed, 53 insertions(+), 46 deletions(-) diff --git a/.gitignore b/.gitignore index b7a121b8b..4049d877a 100644 --- a/.gitignore +++ b/.gitignore @@ -164,4 +164,5 @@ runner.yml .workspace # command scripts for runner -servers/execution/runner/lifecycle* \ No newline at end of file +servers/execution/runner/lifecycle* +client/test/integration/gitlab.test.ts diff --git a/client/src/util/gitlab.ts b/client/src/util/gitlab.ts index b024f104d..652edd3ba 100644 --- a/client/src/util/gitlab.ts +++ b/client/src/util/gitlab.ts @@ -1,6 +1,6 @@ -import { ProjectSchema, PipelineTriggerTokenSchema } from '@gitbeaker/rest'; +import { Gitlab } from '@gitbeaker/rest'; -interface GitlabAPI { +/*interface GitlabAPI { Projects: { search: (name: string) => Promise; }; @@ -9,27 +9,33 @@ interface GitlabAPI { trigger: (projectId: number, ref: string, token: string, variables: { variables: Record }) => Promise; }; } +*/ interface LogEntry { status: string; - parameters?: Map; + DTName: string; + runnerTag: string; error?: Error; } class DigitalTwin { private DTName: string; - private api: GitlabAPI; + private username: string; + private api: InstanceType; private logs: LogEntry[]; - constructor(DTName: string, api: GitlabAPI) { + constructor(DTName: string) { this.DTName = DTName; - this.api = api; + this.username = sessionStorage.getItem('username') || ''; + this.api = new Gitlab({ + oauthToken: sessionStorage.getItem('access_token') || '', + }); this.logs = []; } async getProjectId(): Promise { try { - const projects: ProjectSchema[] = await this.api.Projects.search(this.DTName); + const projects = await this.api.Projects.search(this.username); return projects.length > 0 ? projects[0].id : null; } catch (error) { return null; @@ -38,14 +44,17 @@ class DigitalTwin { async getTriggerToken(projectId: number): Promise { try { - const triggers: PipelineTriggerTokenSchema[] = await this.api.PipelineTriggerTokens.all(projectId); - return triggers.length > 0 ? triggers[0].token : null; + const triggers = await this.api.PipelineTriggerTokens.all(projectId); + if (triggers.length === 1) { + return triggers[0].token; + } + return null; } catch (error) { return null; } } - async execute(parameters: Map): Promise { + async execute(runnerTag: string): Promise { const projectId = await this.getProjectId(); if (projectId === null) { return false; @@ -56,7 +65,10 @@ class DigitalTwin { return false; } - const variables = Object.fromEntries(parameters); + const variables = { + DTName: this.DTName, + RunnerTag: runnerTag, + } try { await this.api.PipelineTriggerTokens.trigger( @@ -65,10 +77,10 @@ class DigitalTwin { triggerToken, { variables } ); - this.logs.push({ status: 'success', parameters }); + this.logs.push({ status: 'success', DTName: this.DTName, runnerTag: runnerTag }); return true; } catch (error) { - this.logs.push({ status: 'error', error: error instanceof Error ? error : new Error(String(error)), parameters }); + this.logs.push({ status: 'error', error: error instanceof Error ? error : new Error(String(error)), DTName: this.DTName, runnerTag: runnerTag }); return false; } } diff --git a/client/test/unitTests/Util/gitlab.test.ts b/client/test/unitTests/Util/gitlab.test.ts index a0c0765d4..ca6c6a112 100644 --- a/client/test/unitTests/Util/gitlab.test.ts +++ b/client/test/unitTests/Util/gitlab.test.ts @@ -1,5 +1,6 @@ -import DigitalTwin from '../../../src/util/gitlab'; // Aggiorna il percorso in base alla tua struttura +import DigitalTwin from '../../../src/util/gitlab'; import { ProjectSchema, PipelineTriggerTokenSchema } from '@gitbeaker/rest'; +import { Gitlab } from '@gitbeaker/rest'; const mockApi = { Projects: { @@ -15,16 +16,16 @@ describe('DigitalTwin', () => { let dt: DigitalTwin; beforeEach(() => { - dt = new DigitalTwin('test-project', mockApi); + dt = new DigitalTwin('test-DTName'); + (dt as any).api = mockApi as unknown as InstanceType; }); it('should fetch project ID successfully', async () => { - mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-project' } as ProjectSchema]); + mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-username' } as ProjectSchema]); const projectId = await dt.getProjectId(); expect(projectId).toBe(1); - expect(mockApi.Projects.search).toHaveBeenCalledWith('test-project'); }); it('should handle project ID not found', async () => { @@ -33,7 +34,6 @@ describe('DigitalTwin', () => { const projectId = await dt.getProjectId(); expect(projectId).toBeNull(); - expect(mockApi.Projects.search).toHaveBeenCalledWith('test-project'); }); it('should handle errors fetching project ID', async () => { @@ -42,7 +42,6 @@ describe('DigitalTwin', () => { const projectId = await dt.getProjectId(); expect(projectId).toBeNull(); - expect(mockApi.Projects.search).toHaveBeenCalledWith('test-project'); }); it('should fetch trigger token successfully', async () => { @@ -74,58 +73,54 @@ describe('DigitalTwin', () => { it('should handle null project ID during pipeline execution', async () => { mockApi.Projects.search.mockResolvedValue([]); - const parameters = new Map([['DTName', 'digital_twin_name']]); - - const success = await dt.execute(parameters); + + const success = await dt.execute('test-runnerTag'); expect(success).toBe(false); - expect(mockApi.PipelineTriggerTokens.trigger).not.toHaveBeenCalled(); // Verifica che non sia stata chiamata + expect(mockApi.PipelineTriggerTokens.trigger).not.toHaveBeenCalled(); }); it('should handle null trigger token during pipeline execution', async () => { - mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-project' } as ProjectSchema]); + mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-username' } as ProjectSchema]); mockApi.PipelineTriggerTokens.all.mockResolvedValue([]); - const parameters = new Map([['DTName', 'digital_twin_name']]); - - const success = await dt.execute(parameters); + + const success = await dt.execute('test-runnerTag'); expect(success).toBe(false); - expect(mockApi.PipelineTriggerTokens.trigger).not.toHaveBeenCalled(); // Verifica che non sia stata chiamata + expect(mockApi.PipelineTriggerTokens.trigger).not.toHaveBeenCalled(); }); it('should execute pipeline successfully', async () => { - mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-project' } as ProjectSchema]); + mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-username' } as ProjectSchema]); mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); mockApi.PipelineTriggerTokens.trigger.mockResolvedValue(undefined); - const parameters = new Map([['DTName', 'digital_twin_name'], ['RunnerTag', 'runner_tag']]); - const success = await dt.execute(parameters); + const success = await dt.execute('test-runnerTag'); expect(success).toBe(true); expect(mockApi.PipelineTriggerTokens.trigger).toHaveBeenCalledWith( 1, 'main', 'test-token', - { variables: { DTName: 'digital_twin_name', RunnerTag: 'runner_tag' } } + { variables: { DTName: 'test-DTName', RunnerTag: 'test-runnerTag' } } ); expect(dt.executionStatus()).toContain('success'); }); it('should handle non-Error thrown during pipeline execution', async () => { - mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-project' } as ProjectSchema]); + mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-username' } as ProjectSchema]); mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); mockApi.PipelineTriggerTokens.trigger.mockRejectedValue('String error message'); - const parameters = new Map([['DTName', 'digital_twin_name'], ['RunnerTag', 'runner_tag']]); - const success = await dt.execute(parameters); + const success = await dt.execute('test-runnerTag'); expect(success).toBe(false); expect(mockApi.PipelineTriggerTokens.trigger).toHaveBeenCalledWith( 1, 'main', 'test-token', - { variables: { DTName: 'digital_twin_name', RunnerTag: 'runner_tag' } } + { variables: { DTName: 'test-DTName', RunnerTag: 'test-runnerTag' } } ); expect(dt.executionStatus()).toContain('error'); const logs = dt.executionLogs(); @@ -135,20 +130,19 @@ describe('DigitalTwin', () => { }); it('should handle Error thrown during pipeline execution', async () => { - mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-project' } as ProjectSchema]); + mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-username' } as ProjectSchema]); mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); - + mockApi.PipelineTriggerTokens.trigger.mockRejectedValue(new Error('Error instance message')); - const parameters = new Map([['DTName', 'digital_twin_name'], ['RunnerTag', 'runner_tag']]); - const success = await dt.execute(parameters); + const success = await dt.execute('test-runnerTag'); expect(success).toBe(false); expect(mockApi.PipelineTriggerTokens.trigger).toHaveBeenCalledWith( 1, 'main', 'test-token', - { variables: { DTName: 'digital_twin_name', RunnerTag: 'runner_tag' } } + { variables: { DTName: 'test-DTName', RunnerTag: 'test-runnerTag' } } ); expect(dt.executionStatus()).toContain('error'); const logs = dt.executionLogs(); @@ -158,16 +152,16 @@ describe('DigitalTwin', () => { }); it('should return execution logs', async () => { - mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-project' } as ProjectSchema]); + mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-username' } as ProjectSchema]); mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); mockApi.PipelineTriggerTokens.trigger.mockResolvedValue(undefined); - const parameters = new Map([['DTName', 'digital_twin_name']]); - await dt.execute(parameters); + await dt.execute('test-runnerTag'); const logs = dt.executionLogs(); expect(logs).toHaveLength(1); expect(logs[0].status).toBe('success'); - expect(logs[0].parameters).toEqual(parameters); + expect(logs[0].DTName).toBe('test-DTName'); + expect(logs[0].runnerTag).toBe('test-runnerTag'); }); }); From f0f11608bfc9c6bc09b4d072e741afaa0dc3cf71 Mon Sep 17 00:00:00 2001 From: Vanessa Scherma Date: Thu, 1 Aug 2024 17:04:47 +0200 Subject: [PATCH 08/65] Add DigitalTwinSubfolders --- client/src/util/gitlab.ts | 51 +++++++++++++++++------ client/test/unitTests/Util/gitlab.test.ts | 45 +++++++++++++++++++- 2 files changed, 82 insertions(+), 14 deletions(-) diff --git a/client/src/util/gitlab.ts b/client/src/util/gitlab.ts index 652edd3ba..80e7b3a7a 100644 --- a/client/src/util/gitlab.ts +++ b/client/src/util/gitlab.ts @@ -1,16 +1,5 @@ import { Gitlab } from '@gitbeaker/rest'; -/*interface GitlabAPI { - Projects: { - search: (name: string) => Promise; - }; - PipelineTriggerTokens: { - all: (projectId: number) => Promise; - trigger: (projectId: number, ref: string, token: string, variables: { variables: Record }) => Promise; - }; -} -*/ - interface LogEntry { status: string; DTName: string; @@ -94,4 +83,42 @@ class DigitalTwin { } } -export default DigitalTwin; \ No newline at end of file +interface FolderEntry { + name: string; + path: string; +} + +class DigitalTwinSubfolders { + private api: InstanceType; + private subfolders: FolderEntry[] = []; + + constructor() { + this.api = new Gitlab({ + oauthToken: sessionStorage.getItem('access_token') || '', + }); + } + + async getDTSubfolders(projectId: number): Promise { + const folderPath = 'digital_twins'; + + try { + const files = await this.api.Repositories.allRepositoryTrees(projectId, { + path: folderPath, + recursive: true, + }); + + this.subfolders = files + .filter(file => file.type === 'tree' && file.path !== folderPath) + .map(file => ({ + name: file.name, + path: file.path + })); + + return this.subfolders; + } catch (error) { + return []; + } + } +} + +export { DigitalTwin, DigitalTwinSubfolders } \ No newline at end of file diff --git a/client/test/unitTests/Util/gitlab.test.ts b/client/test/unitTests/Util/gitlab.test.ts index ca6c6a112..11e5d74f8 100644 --- a/client/test/unitTests/Util/gitlab.test.ts +++ b/client/test/unitTests/Util/gitlab.test.ts @@ -1,6 +1,6 @@ -import DigitalTwin from '../../../src/util/gitlab'; -import { ProjectSchema, PipelineTriggerTokenSchema } from '@gitbeaker/rest'; +import { DigitalTwin, DigitalTwinSubfolders } from '../../../src/util/gitlab'; import { Gitlab } from '@gitbeaker/rest'; +import { ProjectSchema, PipelineTriggerTokenSchema } from '@gitbeaker/rest'; const mockApi = { Projects: { @@ -9,6 +9,9 @@ const mockApi = { PipelineTriggerTokens: { all: jest.fn(), trigger: jest.fn() + }, + Repositories: { + allRepositoryTrees: jest.fn() } }; @@ -165,3 +168,41 @@ describe('DigitalTwin', () => { expect(logs[0].runnerTag).toBe('test-runnerTag'); }); }); + +describe('DigitalTwinSubfolders', () => { + let subfolders: DigitalTwinSubfolders; + + const mockApi = { + Repositories: { + allRepositoryTrees: jest.fn() + } + }; + + beforeEach(() => { + subfolders = new DigitalTwinSubfolders(); + (subfolders as any).api = mockApi as unknown as InstanceType; + }); + + it('should fetch all files and subfolders successfully', async () => { + const mockFiles = [ + { name: 'file1.txt', path: 'digital_twins/file1.txt', type: 'blob' }, + { name: 'subfolder', path: 'digital_twins/subfolder', type: 'tree' }, + { name: 'file2.txt', path: 'digital_twins/subfolder/file2.txt', type: 'blob' } + ]; + mockApi.Repositories.allRepositoryTrees.mockResolvedValue(mockFiles); + + const folderEntries = await subfolders.getDTSubfolders(1); + + expect(folderEntries).toEqual([ + { name: 'subfolder', path: 'digital_twins/subfolder' } + ]); + }); + + it('should handle errors fetching files and subfolders', async () => { + mockApi.Repositories.allRepositoryTrees.mockRejectedValue(new Error('API error')); + + const folderEntries = await subfolders.getDTSubfolders(1); + + expect(folderEntries).toEqual([]); + }); +}); From 8536453e18bd61accc698e1e7f92eef1ba66d4d2 Mon Sep 17 00:00:00 2001 From: Vanessa Scherma Date: Thu, 1 Aug 2024 17:21:42 +0200 Subject: [PATCH 09/65] Fix --- .gitignore | 1 - client/src/util/gitlab.ts | 42 +---------------- client/src/util/gitlabSubfolders.ts | 41 ++++++++++++++++ client/test/unitTests/Util/gitlab.test.ts | 47 ++----------------- .../unitTests/Util/gitlabSubfolders.test.ts | 40 ++++++++++++++++ 5 files changed, 86 insertions(+), 85 deletions(-) create mode 100644 client/src/util/gitlabSubfolders.ts create mode 100644 client/test/unitTests/Util/gitlabSubfolders.test.ts diff --git a/.gitignore b/.gitignore index 4049d877a..77fa56bb1 100644 --- a/.gitignore +++ b/.gitignore @@ -165,4 +165,3 @@ runner.yml # command scripts for runner servers/execution/runner/lifecycle* -client/test/integration/gitlab.test.ts diff --git a/client/src/util/gitlab.ts b/client/src/util/gitlab.ts index 80e7b3a7a..82d6e9c36 100644 --- a/client/src/util/gitlab.ts +++ b/client/src/util/gitlab.ts @@ -10,7 +10,7 @@ interface LogEntry { class DigitalTwin { private DTName: string; private username: string; - private api: InstanceType; + public api: InstanceType; private logs: LogEntry[]; constructor(DTName: string) { @@ -83,42 +83,4 @@ class DigitalTwin { } } -interface FolderEntry { - name: string; - path: string; -} - -class DigitalTwinSubfolders { - private api: InstanceType; - private subfolders: FolderEntry[] = []; - - constructor() { - this.api = new Gitlab({ - oauthToken: sessionStorage.getItem('access_token') || '', - }); - } - - async getDTSubfolders(projectId: number): Promise { - const folderPath = 'digital_twins'; - - try { - const files = await this.api.Repositories.allRepositoryTrees(projectId, { - path: folderPath, - recursive: true, - }); - - this.subfolders = files - .filter(file => file.type === 'tree' && file.path !== folderPath) - .map(file => ({ - name: file.name, - path: file.path - })); - - return this.subfolders; - } catch (error) { - return []; - } - } -} - -export { DigitalTwin, DigitalTwinSubfolders } \ No newline at end of file +export default DigitalTwin; \ No newline at end of file diff --git a/client/src/util/gitlabSubfolders.ts b/client/src/util/gitlabSubfolders.ts new file mode 100644 index 000000000..38ba7c6b8 --- /dev/null +++ b/client/src/util/gitlabSubfolders.ts @@ -0,0 +1,41 @@ +import { Gitlab } from '@gitbeaker/rest'; + +interface FolderEntry { + name: string; + path: string; +} + +class DigitalTwinSubfolders { + public api: InstanceType; + private subfolders: FolderEntry[] = []; + + constructor() { + this.api = new Gitlab({ + oauthToken: sessionStorage.getItem('access_token') || '', + }); + } + + async getDTSubfolders(projectId: number): Promise { + const folderPath = 'digital_twins'; + + try { + const files = await this.api.Repositories.allRepositoryTrees(projectId, { + path: folderPath, + recursive: true, + }); + + this.subfolders = files + .filter(file => file.type === 'tree' && file.path !== folderPath) + .map(file => ({ + name: file.name, + path: file.path + })); + + return this.subfolders; + } catch (error) { + return []; + } + } +} + +export default DigitalTwinSubfolders; \ No newline at end of file diff --git a/client/test/unitTests/Util/gitlab.test.ts b/client/test/unitTests/Util/gitlab.test.ts index 11e5d74f8..f08007410 100644 --- a/client/test/unitTests/Util/gitlab.test.ts +++ b/client/test/unitTests/Util/gitlab.test.ts @@ -1,4 +1,4 @@ -import { DigitalTwin, DigitalTwinSubfolders } from '../../../src/util/gitlab'; +import DigitalTwin from '../../../src/util/gitlab'; import { Gitlab } from '@gitbeaker/rest'; import { ProjectSchema, PipelineTriggerTokenSchema } from '@gitbeaker/rest'; @@ -9,9 +9,6 @@ const mockApi = { PipelineTriggerTokens: { all: jest.fn(), trigger: jest.fn() - }, - Repositories: { - allRepositoryTrees: jest.fn() } }; @@ -20,7 +17,7 @@ describe('DigitalTwin', () => { beforeEach(() => { dt = new DigitalTwin('test-DTName'); - (dt as any).api = mockApi as unknown as InstanceType; + dt.api = mockApi as unknown as InstanceType; }); it('should fetch project ID successfully', async () => { @@ -167,42 +164,4 @@ describe('DigitalTwin', () => { expect(logs[0].DTName).toBe('test-DTName'); expect(logs[0].runnerTag).toBe('test-runnerTag'); }); -}); - -describe('DigitalTwinSubfolders', () => { - let subfolders: DigitalTwinSubfolders; - - const mockApi = { - Repositories: { - allRepositoryTrees: jest.fn() - } - }; - - beforeEach(() => { - subfolders = new DigitalTwinSubfolders(); - (subfolders as any).api = mockApi as unknown as InstanceType; - }); - - it('should fetch all files and subfolders successfully', async () => { - const mockFiles = [ - { name: 'file1.txt', path: 'digital_twins/file1.txt', type: 'blob' }, - { name: 'subfolder', path: 'digital_twins/subfolder', type: 'tree' }, - { name: 'file2.txt', path: 'digital_twins/subfolder/file2.txt', type: 'blob' } - ]; - mockApi.Repositories.allRepositoryTrees.mockResolvedValue(mockFiles); - - const folderEntries = await subfolders.getDTSubfolders(1); - - expect(folderEntries).toEqual([ - { name: 'subfolder', path: 'digital_twins/subfolder' } - ]); - }); - - it('should handle errors fetching files and subfolders', async () => { - mockApi.Repositories.allRepositoryTrees.mockRejectedValue(new Error('API error')); - - const folderEntries = await subfolders.getDTSubfolders(1); - - expect(folderEntries).toEqual([]); - }); -}); +}); \ No newline at end of file diff --git a/client/test/unitTests/Util/gitlabSubfolders.test.ts b/client/test/unitTests/Util/gitlabSubfolders.test.ts new file mode 100644 index 000000000..3373a6229 --- /dev/null +++ b/client/test/unitTests/Util/gitlabSubfolders.test.ts @@ -0,0 +1,40 @@ +import DigitalTwinSubfolders from '../../../src/util/gitlabSubfolders'; +import { Gitlab } from '@gitbeaker/rest'; + +describe('DigitalTwinSubfolders', () => { + let subfolders: DigitalTwinSubfolders; + + const mockApi = { + Repositories: { + allRepositoryTrees: jest.fn() + } + }; + + beforeEach(() => { + subfolders = new DigitalTwinSubfolders(); + subfolders.api = mockApi as unknown as InstanceType; + }); + + it('should fetch all files and subfolders successfully', async () => { + const mockFiles = [ + { name: 'file1.txt', path: 'digital_twins/file1.txt', type: 'blob' }, + { name: 'subfolder', path: 'digital_twins/subfolder', type: 'tree' }, + { name: 'file2.txt', path: 'digital_twins/subfolder/file2.txt', type: 'blob' } + ]; + mockApi.Repositories.allRepositoryTrees.mockResolvedValue(mockFiles); + + const folderEntries = await subfolders.getDTSubfolders(1); + + expect(folderEntries).toEqual([ + { name: 'subfolder', path: 'digital_twins/subfolder' } + ]); + }); + + it('should handle errors fetching files and subfolders', async () => { + mockApi.Repositories.allRepositoryTrees.mockRejectedValue(new Error('API error')); + + const folderEntries = await subfolders.getDTSubfolders(1); + + expect(folderEntries).toEqual([]); + }); +}); \ No newline at end of file From a86a47bf07d63a3c2a9721c47a405b634f95d459 Mon Sep 17 00:00:00 2001 From: Vanessa Scherma Date: Fri, 2 Aug 2024 13:37:27 +0200 Subject: [PATCH 10/65] Add GitlabDriver and divide GitlabInstance and GitlabDigitalTwin --- client/src/util/gitlab.ts | 137 ++++++++------- client/src/util/gitlabDigitalTwin.ts | 53 ++++++ client/src/util/gitlabDriver.ts | 40 +++++ client/src/util/gitlabSubfolders.ts | 41 ----- client/test/unitTests/Util/gitlab.test.ts | 162 +++++++----------- .../unitTests/Util/gitlabDigitalTwin.test.ts | 123 +++++++++++++ .../unitTests/Util/gitlabSubfolders.test.ts | 40 ----- 7 files changed, 348 insertions(+), 248 deletions(-) create mode 100644 client/src/util/gitlabDigitalTwin.ts create mode 100755 client/src/util/gitlabDriver.ts delete mode 100644 client/src/util/gitlabSubfolders.ts create mode 100644 client/test/unitTests/Util/gitlabDigitalTwin.test.ts delete mode 100644 client/test/unitTests/Util/gitlabSubfolders.test.ts diff --git a/client/src/util/gitlab.ts b/client/src/util/gitlab.ts index 82d6e9c36..4361cb513 100644 --- a/client/src/util/gitlab.ts +++ b/client/src/util/gitlab.ts @@ -1,86 +1,83 @@ -import { Gitlab } from '@gitbeaker/rest'; + import { Gitlab } from '@gitbeaker/rest'; -interface LogEntry { - status: string; - DTName: string; - runnerTag: string; - error?: Error; -} - -class DigitalTwin { - private DTName: string; - private username: string; - public api: InstanceType; - private logs: LogEntry[]; + interface LogEntry { + status: string; + DTName: string; + runnerTag: string; + error?: Error; + } - constructor(DTName: string) { - this.DTName = DTName; - this.username = sessionStorage.getItem('username') || ''; - this.api = new Gitlab({ - oauthToken: sessionStorage.getItem('access_token') || '', - }); - this.logs = []; + interface FolderEntry { + name: string; + path: string; } - async getProjectId(): Promise { - try { - const projects = await this.api.Projects.search(this.username); - return projects.length > 0 ? projects[0].id : null; - } catch (error) { - return null; + class GitlabInstance { + public username: string; + public api: InstanceType; + public logs: LogEntry[]; + public subfolders: FolderEntry[] = []; + + constructor() { + this.username = 'user1'; + this.api = new Gitlab({ + host: 'https://gitlab.com', + token: 'glpat-CJknCUBMj8hSC3oibyxS' + }); + this.logs = []; } - } - async getTriggerToken(projectId: number): Promise { - try { - const triggers = await this.api.PipelineTriggerTokens.all(projectId); - if (triggers.length === 1) { - return triggers[0].token; + async getProjectId(): Promise { + const groupPath = 'DTaaS'; + try { + const group = await this.api.Groups.show(groupPath); + const projects = await this.api.Groups.allProjects(group.id); + const project = projects.find(proj => proj.name === this.username); + if (project) { + return project.id; + } + return null; + } catch (error) { + return null; } - return null; - } catch (error) { - return null; - } - } + } - async execute(runnerTag: string): Promise { - const projectId = await this.getProjectId(); - if (projectId === null) { - return false; + async getTriggerToken(projectId: number): Promise { + try { + const triggers = await this.api.PipelineTriggerTokens.all(projectId); + if (triggers) { + return triggers[0].token; + } + return null; + } catch (error) { + return null; + } } - const triggerToken = await this.getTriggerToken(projectId); - if (triggerToken === null) { - return false; - } + async getDTSubfolders(projectId: number): Promise { + const folderPath = 'digital_twins'; + try { + const files = await this.api.Repositories.allRepositoryTrees(projectId, { + path: folderPath, + recursive: false, + }); - const variables = { - DTName: this.DTName, - RunnerTag: runnerTag, - } + this.subfolders = files + .filter(file => file.type === 'tree' && file.path !== folderPath) + .map(file => ({ + name: file.name, + path: file.path + })); - try { - await this.api.PipelineTriggerTokens.trigger( - projectId, - 'main', - triggerToken, - { variables } - ); - this.logs.push({ status: 'success', DTName: this.DTName, runnerTag: runnerTag }); - return true; - } catch (error) { - this.logs.push({ status: 'error', error: error instanceof Error ? error : new Error(String(error)), DTName: this.DTName, runnerTag: runnerTag }); - return false; + return this.subfolders; + } catch (error) { + return []; + } } - } - executionStatus(): string[] { - return this.logs.map(log => log.status); - } - - executionLogs(): LogEntry[] { - return this.logs; + executionLogs(): LogEntry[] { + return this.logs; + } } -} -export default DigitalTwin; \ No newline at end of file + export default GitlabInstance; \ No newline at end of file diff --git a/client/src/util/gitlabDigitalTwin.ts b/client/src/util/gitlabDigitalTwin.ts new file mode 100644 index 000000000..76df4ee77 --- /dev/null +++ b/client/src/util/gitlabDigitalTwin.ts @@ -0,0 +1,53 @@ +import GitlabInstance from './gitlab'; + +class DigitalTwin { + public DTName: string; + public gitlabInstance: GitlabInstance; + private lastExecutionStatus: string | null = null; + + constructor(DTName: string, gitlabInstance: GitlabInstance) { + this.DTName = DTName; + this.gitlabInstance = gitlabInstance; + } + + async execute(runnerTag: string): Promise { + const projectId = await this.gitlabInstance.getProjectId(); + if (projectId === null) { + this.lastExecutionStatus = 'error'; + return false; + } + + const triggerToken = await this.gitlabInstance.getTriggerToken(projectId); + if (triggerToken === null) { + this.lastExecutionStatus = 'error'; + return false; + } + + const variables = { + DTName: this.DTName, + RunnerTag: runnerTag, + } + + try { + await this.gitlabInstance.api.PipelineTriggerTokens.trigger( + projectId, + 'main', + triggerToken, + { variables } + ); + this.gitlabInstance.logs.push({ status: 'success', DTName: this.DTName, runnerTag: runnerTag }); + this.lastExecutionStatus = 'success'; + return true; + } catch (error) { + this.gitlabInstance.logs.push({ status: 'error', error: error instanceof Error ? error : new Error(String(error)), DTName: this.DTName, runnerTag: runnerTag }); + this.lastExecutionStatus = 'error'; + return false; + } + } + + executionStatus(): string | null { + return this.lastExecutionStatus; + } +} + +export default DigitalTwin; \ No newline at end of file diff --git a/client/src/util/gitlabDriver.ts b/client/src/util/gitlabDriver.ts new file mode 100755 index 000000000..01ba9ba1b --- /dev/null +++ b/client/src/util/gitlabDriver.ts @@ -0,0 +1,40 @@ +import GitlabInstance from './gitlab'; +import DigitalTwin from './gitlabDigitalTwin'; + +class GitlabDriver { + public static async run(): Promise { + const gitlabInstance = new GitlabInstance(); + console.log('GitLab username:', gitlabInstance.username); + console.log('GitLab logs:', gitlabInstance.logs); + console.log('GitLab subfolders:', gitlabInstance.subfolders); + + const projectId = await gitlabInstance.getProjectId() || 0; + console.log('Project id:', projectId); + + const subfolders = await gitlabInstance.getDTSubfolders(projectId); + console.log('Subfolders:', subfolders); + + const dtName = subfolders[0].name; + const runnerTag = 'dtaas'; + + const triggerToken = await gitlabInstance.getTriggerToken(projectId); + console.log('Trigger token:', triggerToken); + + const digitalTwin = new DigitalTwin(dtName, gitlabInstance); + const result = await digitalTwin.execute(runnerTag); + + console.log('Execution Result:', result); + + const lastExecutionStatus = digitalTwin.executionStatus(); + console.log('Execution Status:', lastExecutionStatus); + + const logs = gitlabInstance.executionLogs(); + console.log('Execution Logs:', logs); + } +} + +GitlabDriver.run().catch(error => { + console.error('Error executing GitlabDriver:', error); +}); + +export default GitlabDriver; \ No newline at end of file diff --git a/client/src/util/gitlabSubfolders.ts b/client/src/util/gitlabSubfolders.ts deleted file mode 100644 index 38ba7c6b8..000000000 --- a/client/src/util/gitlabSubfolders.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Gitlab } from '@gitbeaker/rest'; - -interface FolderEntry { - name: string; - path: string; -} - -class DigitalTwinSubfolders { - public api: InstanceType; - private subfolders: FolderEntry[] = []; - - constructor() { - this.api = new Gitlab({ - oauthToken: sessionStorage.getItem('access_token') || '', - }); - } - - async getDTSubfolders(projectId: number): Promise { - const folderPath = 'digital_twins'; - - try { - const files = await this.api.Repositories.allRepositoryTrees(projectId, { - path: folderPath, - recursive: true, - }); - - this.subfolders = files - .filter(file => file.type === 'tree' && file.path !== folderPath) - .map(file => ({ - name: file.name, - path: file.path - })); - - return this.subfolders; - } catch (error) { - return []; - } - } -} - -export default DigitalTwinSubfolders; \ No newline at end of file diff --git a/client/test/unitTests/Util/gitlab.test.ts b/client/test/unitTests/Util/gitlab.test.ts index f08007410..50d00c0bf 100644 --- a/client/test/unitTests/Util/gitlab.test.ts +++ b/client/test/unitTests/Util/gitlab.test.ts @@ -1,45 +1,53 @@ -import DigitalTwin from '../../../src/util/gitlab'; import { Gitlab } from '@gitbeaker/rest'; -import { ProjectSchema, PipelineTriggerTokenSchema } from '@gitbeaker/rest'; +import { ProjectSchema, PipelineTriggerTokenSchema, GroupSchema, RepositoryTreeSchema } from '@gitbeaker/rest'; +import GitlabInstance from '../../../src/util/gitlab'; const mockApi = { - Projects: { - search: jest.fn() + Groups: { + show: jest.fn(), + allProjects: jest.fn() }, PipelineTriggerTokens: { all: jest.fn(), trigger: jest.fn() + }, + Repositories: { + allRepositoryTrees: jest.fn() } }; -describe('DigitalTwin', () => { - let dt: DigitalTwin; +describe('GitlabInstance', () => { + let gitlab: GitlabInstance; beforeEach(() => { - dt = new DigitalTwin('test-DTName'); - dt.api = mockApi as unknown as InstanceType; + gitlab = new GitlabInstance(); + gitlab.api = mockApi as unknown as InstanceType; }); it('should fetch project ID successfully', async () => { - mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-username' } as ProjectSchema]); + mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' } as GroupSchema); + mockApi.Groups.allProjects.mockResolvedValue([{ id: 1, name: 'user1' } as ProjectSchema]); - const projectId = await dt.getProjectId(); + const projectId = await gitlab.getProjectId(); expect(projectId).toBe(1); + expect(mockApi.Groups.show).toHaveBeenCalledWith('DTaaS'); + expect(mockApi.Groups.allProjects).toHaveBeenCalledWith(1); }); it('should handle project ID not found', async () => { - mockApi.Projects.search.mockResolvedValue([]); + mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' } as GroupSchema); + mockApi.Groups.allProjects.mockResolvedValue([]); - const projectId = await dt.getProjectId(); + const projectId = await gitlab.getProjectId(); expect(projectId).toBeNull(); }); it('should handle errors fetching project ID', async () => { - mockApi.Projects.search.mockRejectedValue(new Error('API error')); + mockApi.Groups.show.mockRejectedValue(new Error('API error')); - const projectId = await dt.getProjectId(); + const projectId = await gitlab.getProjectId(); expect(projectId).toBeNull(); }); @@ -47,7 +55,7 @@ describe('DigitalTwin', () => { it('should fetch trigger token successfully', async () => { mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); - const token = await dt.getTriggerToken(1); + const token = await gitlab.getTriggerToken(1); expect(token).toBe('test-token'); expect(mockApi.PipelineTriggerTokens.all).toHaveBeenCalledWith(1); @@ -56,112 +64,72 @@ describe('DigitalTwin', () => { it('should handle no trigger tokens found', async () => { mockApi.PipelineTriggerTokens.all.mockResolvedValue([]); - const token = await dt.getTriggerToken(1); + const token = await gitlab.getTriggerToken(1); expect(token).toBeNull(); expect(mockApi.PipelineTriggerTokens.all).toHaveBeenCalledWith(1); }); - it('should handle errors fetching trigger token', async () => { - mockApi.PipelineTriggerTokens.all.mockRejectedValue(new Error('API error')); + it('should handle undefined trigger tokens', async () => { + mockApi.PipelineTriggerTokens.all.mockResolvedValue(undefined); - const token = await dt.getTriggerToken(1); + const token = await gitlab.getTriggerToken(1); expect(token).toBeNull(); - expect(mockApi.PipelineTriggerTokens.all).toHaveBeenCalledWith(1); - }); - - it('should handle null project ID during pipeline execution', async () => { - mockApi.Projects.search.mockResolvedValue([]); - - const success = await dt.execute('test-runnerTag'); + }); - expect(success).toBe(false); - expect(mockApi.PipelineTriggerTokens.trigger).not.toHaveBeenCalled(); - }); - - it('should handle null trigger token during pipeline execution', async () => { - mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-username' } as ProjectSchema]); - mockApi.PipelineTriggerTokens.all.mockResolvedValue([]); + it('should handle errors fetching trigger token', async () => { + mockApi.PipelineTriggerTokens.all.mockRejectedValue(new Error('API error')); - const success = await dt.execute('test-runnerTag'); + const token = await gitlab.getTriggerToken(1); - expect(success).toBe(false); - expect(mockApi.PipelineTriggerTokens.trigger).not.toHaveBeenCalled(); + expect(token).toBeNull(); + expect(mockApi.PipelineTriggerTokens.all).toHaveBeenCalledWith(1); }); - it('should execute pipeline successfully', async () => { - mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-username' } as ProjectSchema]); - mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); - mockApi.PipelineTriggerTokens.trigger.mockResolvedValue(undefined); - - const success = await dt.execute('test-runnerTag'); - - expect(success).toBe(true); - expect(mockApi.PipelineTriggerTokens.trigger).toHaveBeenCalledWith( - 1, - 'main', - 'test-token', - { variables: { DTName: 'test-DTName', RunnerTag: 'test-runnerTag' } } - ); - expect(dt.executionStatus()).toContain('success'); + it('should fetch DT subfolders successfully', async () => { + mockApi.Repositories.allRepositoryTrees.mockResolvedValue([ + { name: 'subfolder1', path: 'digital_twins/subfolder1', type: 'tree' } as RepositoryTreeSchema, + { name: 'subfolder2', path: 'digital_twins/subfolder2', type: 'tree' } as RepositoryTreeSchema, + { name: 'file1', path: 'digital_twins/file1', type: 'blob' } as RepositoryTreeSchema + ]); + + const subfolders = await gitlab.getDTSubfolders(1); + + expect(subfolders).toHaveLength(2); + expect(subfolders).toEqual([ + { name: 'subfolder1', path: 'digital_twins/subfolder1' }, + { name: 'subfolder2', path: 'digital_twins/subfolder2' } + ]); + expect(mockApi.Repositories.allRepositoryTrees).toHaveBeenCalledWith(1, { + path: 'digital_twins', + recursive: false + }); }); - it('should handle non-Error thrown during pipeline execution', async () => { - mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-username' } as ProjectSchema]); - mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); - - mockApi.PipelineTriggerTokens.trigger.mockRejectedValue('String error message'); + it('should handle errors fetching DT subfolders', async () => { + mockApi.Repositories.allRepositoryTrees.mockRejectedValue(new Error('API error')); - const success = await dt.execute('test-runnerTag'); + const subfolders = await gitlab.getDTSubfolders(1); - expect(success).toBe(false); - expect(mockApi.PipelineTriggerTokens.trigger).toHaveBeenCalledWith( - 1, - 'main', - 'test-token', - { variables: { DTName: 'test-DTName', RunnerTag: 'test-runnerTag' } } - ); - expect(dt.executionStatus()).toContain('error'); - const logs = dt.executionLogs(); - expect(logs).toHaveLength(1); - expect(logs[0].error).toBeInstanceOf(Error); - expect(logs[0].error?.message).toBe('String error message'); + expect(subfolders).toEqual([]); }); - it('should handle Error thrown during pipeline execution', async () => { - mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-username' } as ProjectSchema]); - mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); - - mockApi.PipelineTriggerTokens.trigger.mockRejectedValue(new Error('Error instance message')); - - const success = await dt.execute('test-runnerTag'); - - expect(success).toBe(false); - expect(mockApi.PipelineTriggerTokens.trigger).toHaveBeenCalledWith( - 1, - 'main', - 'test-token', - { variables: { DTName: 'test-DTName', RunnerTag: 'test-runnerTag' } } - ); - expect(dt.executionStatus()).toContain('error'); - const logs = dt.executionLogs(); - expect(logs).toHaveLength(1); - expect(logs[0].error).toBeInstanceOf(Error); - expect(logs[0].error?.message).toBe('Error instance message'); - }); + it('should return execution logs', () => { + const mockLog = { + status: 'success', + DTName: 'test-DTName', + runnerTag: 'test-runnerTag', + error: undefined + }; - it('should return execution logs', async () => { - mockApi.Projects.search.mockResolvedValue([{ id: 1, name: 'test-username' } as ProjectSchema]); - mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); - mockApi.PipelineTriggerTokens.trigger.mockResolvedValue(undefined); + gitlab.logs.push(mockLog); - await dt.execute('test-runnerTag'); + const logs = gitlab.executionLogs(); - const logs = dt.executionLogs(); expect(logs).toHaveLength(1); expect(logs[0].status).toBe('success'); expect(logs[0].DTName).toBe('test-DTName'); expect(logs[0].runnerTag).toBe('test-runnerTag'); }); -}); \ No newline at end of file +}); diff --git a/client/test/unitTests/Util/gitlabDigitalTwin.test.ts b/client/test/unitTests/Util/gitlabDigitalTwin.test.ts new file mode 100644 index 000000000..5f1ad2a89 --- /dev/null +++ b/client/test/unitTests/Util/gitlabDigitalTwin.test.ts @@ -0,0 +1,123 @@ +import DigitalTwin from '../../../src/util/gitlabDigitalTwin'; +import GitlabInstance from '../../../src/util/gitlab'; +import { Gitlab } from '@gitbeaker/rest'; +import { ProjectSchema, PipelineTriggerTokenSchema } from '@gitbeaker/rest'; + +const mockApi = { + Groups: { + show: jest.fn(), + allProjects: jest.fn() + }, + PipelineTriggerTokens: { + all: jest.fn(), + trigger: jest.fn() + }, + Repositories: { + allRepositoryTrees: jest.fn() + } +}; + +describe('DigitalTwin', () => { + let dt: DigitalTwin; + let gitlabInstance: GitlabInstance; + + beforeEach(() => { + gitlabInstance = new GitlabInstance(); + gitlabInstance.api = mockApi as unknown as InstanceType; + dt = new DigitalTwin('test-DTName', gitlabInstance); + }); + + it('should handle null project ID during pipeline execution', async () => { + mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' }); + mockApi.Groups.allProjects.mockResolvedValue([]); + + const success = await dt.execute('test-runnerTag'); + + expect(success).toBe(false); + expect(dt.executionStatus()).toBe('error'); + expect(mockApi.PipelineTriggerTokens.trigger).not.toHaveBeenCalled(); + }); + + it('should handle null trigger token during pipeline execution', async () => { + mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' }); + mockApi.Groups.allProjects.mockResolvedValue([{ id: 1, name: 'user1' } as ProjectSchema]); + mockApi.PipelineTriggerTokens.all.mockResolvedValue([]); + + const success = await dt.execute('test-runnerTag'); + + expect(success).toBe(false); + expect(dt.executionStatus()).toBe('error'); + expect(mockApi.PipelineTriggerTokens.trigger).not.toHaveBeenCalled(); + }); + + it('should execute pipeline successfully', async () => { + mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' }); + mockApi.Groups.allProjects.mockResolvedValue([{ id: 1, name: 'user1' } as ProjectSchema]); + mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); + mockApi.PipelineTriggerTokens.trigger.mockResolvedValue(undefined); + + const success = await dt.execute('test-runnerTag'); + + expect(success).toBe(true); + expect(dt.executionStatus()).toBe('success'); + expect(mockApi.PipelineTriggerTokens.trigger).toHaveBeenCalledWith( + 1, + 'main', + 'test-token', + { variables: { DTName: 'test-DTName', RunnerTag: 'test-runnerTag' } } + ); + }); + + it('should handle non-Error thrown during pipeline execution', async () => { + mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' }); + mockApi.Groups.allProjects.mockResolvedValue([{ id: 1, name: 'user1' } as ProjectSchema]); + mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); + + mockApi.PipelineTriggerTokens.trigger.mockRejectedValue('String error message'); + + const success = await dt.execute('test-runnerTag'); + + expect(success).toBe(false); + expect(dt.executionStatus()).toBe('error'); + expect(mockApi.PipelineTriggerTokens.trigger).toHaveBeenCalledWith( + 1, + 'main', + 'test-token', + { variables: { DTName: 'test-DTName', RunnerTag: 'test-runnerTag' } } + ); + }); + + it('should handle Error thrown during pipeline execution', async () => { + mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' }); + mockApi.Groups.allProjects.mockResolvedValue([{ id: 1, name: 'user1' } as ProjectSchema]); + mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); + + mockApi.PipelineTriggerTokens.trigger.mockRejectedValue(new Error('Error instance message')); + + const success = await dt.execute('test-runnerTag'); + + expect(success).toBe(false); + expect(dt.executionStatus()).toBe('error'); + expect(mockApi.PipelineTriggerTokens.trigger).toHaveBeenCalledWith( + 1, + 'main', + 'test-token', + { variables: { DTName: 'test-DTName', RunnerTag: 'test-runnerTag' } } + ); + }); + + it('should return execution logs', async () => { + mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' }); + mockApi.Groups.allProjects.mockResolvedValue([{ id: 1, name: 'user1' } as ProjectSchema]); + mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); + mockApi.PipelineTriggerTokens.trigger.mockResolvedValue(undefined); + + await dt.execute('test-runnerTag'); + + const logs = dt.gitlabInstance.executionLogs(); + expect(logs).toHaveLength(1); + expect(logs[0].status).toBe('success'); + expect(logs[0].DTName).toBe('test-DTName'); + expect(logs[0].runnerTag).toBe('test-runnerTag'); + }); +}); \ No newline at end of file diff --git a/client/test/unitTests/Util/gitlabSubfolders.test.ts b/client/test/unitTests/Util/gitlabSubfolders.test.ts deleted file mode 100644 index 3373a6229..000000000 --- a/client/test/unitTests/Util/gitlabSubfolders.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import DigitalTwinSubfolders from '../../../src/util/gitlabSubfolders'; -import { Gitlab } from '@gitbeaker/rest'; - -describe('DigitalTwinSubfolders', () => { - let subfolders: DigitalTwinSubfolders; - - const mockApi = { - Repositories: { - allRepositoryTrees: jest.fn() - } - }; - - beforeEach(() => { - subfolders = new DigitalTwinSubfolders(); - subfolders.api = mockApi as unknown as InstanceType; - }); - - it('should fetch all files and subfolders successfully', async () => { - const mockFiles = [ - { name: 'file1.txt', path: 'digital_twins/file1.txt', type: 'blob' }, - { name: 'subfolder', path: 'digital_twins/subfolder', type: 'tree' }, - { name: 'file2.txt', path: 'digital_twins/subfolder/file2.txt', type: 'blob' } - ]; - mockApi.Repositories.allRepositoryTrees.mockResolvedValue(mockFiles); - - const folderEntries = await subfolders.getDTSubfolders(1); - - expect(folderEntries).toEqual([ - { name: 'subfolder', path: 'digital_twins/subfolder' } - ]); - }); - - it('should handle errors fetching files and subfolders', async () => { - mockApi.Repositories.allRepositoryTrees.mockRejectedValue(new Error('API error')); - - const folderEntries = await subfolders.getDTSubfolders(1); - - expect(folderEntries).toEqual([]); - }); -}); \ No newline at end of file From 56b412ed828c000c328f4dbfcaca0b4098b55407 Mon Sep 17 00:00:00 2001 From: Vanessa Scherma Date: Wed, 7 Aug 2024 17:42:16 +0200 Subject: [PATCH 11/65] Change imports --- client/src/util/gitlabDriver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/util/gitlabDriver.ts b/client/src/util/gitlabDriver.ts index 01ba9ba1b..274b17fd4 100755 --- a/client/src/util/gitlabDriver.ts +++ b/client/src/util/gitlabDriver.ts @@ -1,5 +1,5 @@ -import GitlabInstance from './gitlab'; -import DigitalTwin from './gitlabDigitalTwin'; +import GitlabInstance from './gitlab.js'; +import DigitalTwin from './gitlabDigitalTwin.js'; class GitlabDriver { public static async run(): Promise { From c8dc464ba467953ad178936888d88cff503b1514 Mon Sep 17 00:00:00 2001 From: prasadtalasila Date: Fri, 16 Aug 2024 12:56:18 +0200 Subject: [PATCH 12/65] Adds config for managing gitlab code of client --- client/package.json | 4 ++- client/src/util/gitlab.ts | 3 ++ client/src/util/gitlabDigitalTwin.ts | 6 ++-- client/src/util/gitlabDriver.ts | 1 + client/test/unitTests/Util/gitlab.test.ts | 5 ++- .../unitTests/Util/gitlabDigitalTwin.test.ts | 7 ++-- client/tsconfig.gitlab.json | 32 +++++++++++++++++++ 7 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 client/tsconfig.gitlab.json diff --git a/client/package.json b/client/package.json index e2ca66ee4..1972c3785 100644 --- a/client/package.json +++ b/client/package.json @@ -16,13 +16,15 @@ "type": "module", "scripts": { "build": "npx react-scripts build", - "clean": "npx rimraf build/ node_modules/ coverage/ playwright-report/ *.svg", + "clean": "npx rimraf build/ dist/ node_modules/ coverage/ playwright-report/ *.svg", "config:dev": "npx shx cp config/dev.js public/env.js && npx shx cp config/dev.js build/env.js", "config:local": "npx shx cp config/local.js public/env.js && npx shx cp config/local.js build/env.js", "config:prod": "npx shx cp config/prod.js public/env.js && npx shx cp config/prod.js build/env.js", "config:test": "npx shx cp config/test.js public/env.js && npx shx cp config/test.js build/env.js", "develop": "npx react-scripts start", "format": "prettier --ignore-path ../.gitignore --write \"**/*.{ts,tsx,css,scss}\"", + "gitlab:compile": "npx tsc --project tsconfig.gitlab.json", + "gitlab:run": "node dist/gitlabDriver.js", "graph": "npx madge --image src.svg src && npx madge --image test.svg test", "start": "serve -s build -l 4000", "stop": "npx kill-port 4000", diff --git a/client/src/util/gitlab.ts b/client/src/util/gitlab.ts index 4361cb513..8d2b9ffe0 100644 --- a/client/src/util/gitlab.ts +++ b/client/src/util/gitlab.ts @@ -14,8 +14,11 @@ class GitlabInstance { public username: string; + public api: InstanceType; + public logs: LogEntry[]; + public subfolders: FolderEntry[] = []; constructor() { diff --git a/client/src/util/gitlabDigitalTwin.ts b/client/src/util/gitlabDigitalTwin.ts index 76df4ee77..71e502ade 100644 --- a/client/src/util/gitlabDigitalTwin.ts +++ b/client/src/util/gitlabDigitalTwin.ts @@ -2,7 +2,9 @@ import GitlabInstance from './gitlab'; class DigitalTwin { public DTName: string; + public gitlabInstance: GitlabInstance; + private lastExecutionStatus: string | null = null; constructor(DTName: string, gitlabInstance: GitlabInstance) { @@ -35,11 +37,11 @@ class DigitalTwin { triggerToken, { variables } ); - this.gitlabInstance.logs.push({ status: 'success', DTName: this.DTName, runnerTag: runnerTag }); + this.gitlabInstance.logs.push({ status: 'success', DTName: this.DTName, runnerTag }); this.lastExecutionStatus = 'success'; return true; } catch (error) { - this.gitlabInstance.logs.push({ status: 'error', error: error instanceof Error ? error : new Error(String(error)), DTName: this.DTName, runnerTag: runnerTag }); + this.gitlabInstance.logs.push({ status: 'error', error: error instanceof Error ? error : new Error(String(error)), DTName: this.DTName, runnerTag }); this.lastExecutionStatus = 'error'; return false; } diff --git a/client/src/util/gitlabDriver.ts b/client/src/util/gitlabDriver.ts index 274b17fd4..691669068 100755 --- a/client/src/util/gitlabDriver.ts +++ b/client/src/util/gitlabDriver.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ import GitlabInstance from './gitlab.js'; import DigitalTwin from './gitlabDigitalTwin.js'; diff --git a/client/test/unitTests/Util/gitlab.test.ts b/client/test/unitTests/Util/gitlab.test.ts index 50d00c0bf..087f2f76c 100644 --- a/client/test/unitTests/Util/gitlab.test.ts +++ b/client/test/unitTests/Util/gitlab.test.ts @@ -1,6 +1,5 @@ -import { Gitlab } from '@gitbeaker/rest'; -import { ProjectSchema, PipelineTriggerTokenSchema, GroupSchema, RepositoryTreeSchema } from '@gitbeaker/rest'; -import GitlabInstance from '../../../src/util/gitlab'; +import { Gitlab , ProjectSchema, PipelineTriggerTokenSchema, GroupSchema, RepositoryTreeSchema } from '@gitbeaker/rest'; +import GitlabInstance from 'util/gitlab'; const mockApi = { Groups: { diff --git a/client/test/unitTests/Util/gitlabDigitalTwin.test.ts b/client/test/unitTests/Util/gitlabDigitalTwin.test.ts index 5f1ad2a89..b8696a9e3 100644 --- a/client/test/unitTests/Util/gitlabDigitalTwin.test.ts +++ b/client/test/unitTests/Util/gitlabDigitalTwin.test.ts @@ -1,7 +1,6 @@ -import DigitalTwin from '../../../src/util/gitlabDigitalTwin'; -import GitlabInstance from '../../../src/util/gitlab'; -import { Gitlab } from '@gitbeaker/rest'; -import { ProjectSchema, PipelineTriggerTokenSchema } from '@gitbeaker/rest'; +import { Gitlab , ProjectSchema, PipelineTriggerTokenSchema } from '@gitbeaker/rest'; +import DigitalTwin from 'util/gitlabDigitalTwin'; +import GitlabInstance from 'util/gitlab'; const mockApi = { Groups: { diff --git a/client/tsconfig.gitlab.json b/client/tsconfig.gitlab.json new file mode 100644 index 000000000..6d5ffb336 --- /dev/null +++ b/client/tsconfig.gitlab.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "noImplicitAny": true, //raise error on any type + "allowSyntheticDefaultImports": true, //allow default imports from modules with no default export + "sourceMap": true, //generate .map files + "target": "es6", //target es6 + "lib": ["es2022", "webworker", "webworker.importscripts", "webworker.iterable", "scripthost", "es2022.array", "es2022.error", "es2022.intl", "es2022.object", "es2022.sharedmemory", "es2022.string"], + "jsx": "react", //use react + "types": ["react", "node"], //use react and node types + "module": "esnext", //use esnext modules + "moduleResolution": "node", //use node module resolution strategy node + "experimentalDecorators": true, //allow experimental decorators for es7 + "declaration": false, //don't generate declaration '.d.ts' files + "removeComments": true, //remove comments from build + "noImplicitReturns": true, //raise error on implicit returns + "noUnusedLocals": true, //raise error on unused locals + "noUnusedParameters": false, //raise no error on unused parameters + "strict": true, //enable all strict type-checking options + "outDir": "dist", //output directory + "baseUrl": "src", //base url for imports + "typeRoots": [ + "node_modules/@types" //use node_modules/@types for type definitions + ], + "strictNullChecks": true //enable strict null checks + }, + "exclude": ["**/node_modules/*", "babel.config.cjs", "dist", "test"], + "include": [ + "./src/util/gitlab*.ts", + "./test/unitTests/Util/gitlab*.test.ts" + ], + "typeRoots": [ "**/node_modules/@types" ] +} From 8d20c28c09c9d54b82dc7b6d5b491dba173ef61e Mon Sep 17 00:00:00 2001 From: prasadtalasila Date: Fri, 16 Aug 2024 13:01:25 +0200 Subject: [PATCH 13/65] Adds instructions for running gitlab code of client --- client/DEVELOPER.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/client/DEVELOPER.md b/client/DEVELOPER.md index 84ae825ee..4b1530005 100644 --- a/client/DEVELOPER.md +++ b/client/DEVELOPER.md @@ -178,3 +178,16 @@ port and basename options in the docker-based development environment. Each new release of client web application is published as a docker container image. Please see [publishing](../docker/README.md) page for more information publishing docker images. + +## Gitlab Integration + +The client codebase has been using Gitlab for OAuth2 only. There is +an ongoing effort to integrate Gitlab CI/CD capabilities to automate +the execution of Digital Twins. This code is in alpha stage and is +available in `src/util/gitlab*.ts`. +This code can be developed and tested using the following yarn commands. + +```bash +yarn gitlab:compile +yarn gitlab:run +``` From 7806eefcba093140cea17da27e52dda586d57b1a Mon Sep 17 00:00:00 2001 From: Vanessa Scherma Date: Sat, 17 Aug 2024 17:33:54 +0200 Subject: [PATCH 14/65] Comments resolved --- client/src/util/gitlab.ts | 71 ++++++++++--------- client/src/util/gitlabDigitalTwin.ts | 2 +- client/test/unitTests/Util/gitlab.test.ts | 27 +------ .../unitTests/Util/gitlabDigitalTwin.test.ts | 64 ++++++++++------- 4 files changed, 76 insertions(+), 88 deletions(-) diff --git a/client/src/util/gitlab.ts b/client/src/util/gitlab.ts index 8d2b9ffe0..cfb85c2c6 100644 --- a/client/src/util/gitlab.ts +++ b/client/src/util/gitlab.ts @@ -1,5 +1,8 @@ import { Gitlab } from '@gitbeaker/rest'; + const GROUP_NAME = 'DTaaS'; + const DT_DIRECTORY = 'digital_twins'; + interface LogEntry { status: string; DTName: string; @@ -19,7 +22,7 @@ public logs: LogEntry[]; - public subfolders: FolderEntry[] = []; + public subfolders: FolderEntry[]; constructor() { this.username = 'user1'; @@ -28,54 +31,52 @@ token: 'glpat-CJknCUBMj8hSC3oibyxS' }); this.logs = []; + this.subfolders = []; } async getProjectId(): Promise { - const groupPath = 'DTaaS'; - try { - const group = await this.api.Groups.show(groupPath); + let projectId: number | null = null; + + const group = await this.api.Groups.show(GROUP_NAME); const projects = await this.api.Groups.allProjects(group.id); const project = projects.find(proj => proj.name === this.username); + if (project) { - return project.id; + projectId = project.id; } - return null; - } catch (error) { - return null; - } - } + + return projectId; + } + async getTriggerToken(projectId: number): Promise { - try { - const triggers = await this.api.PipelineTriggerTokens.all(projectId); - if (triggers) { - return triggers[0].token; - } - return null; - } catch (error) { - return null; + let token: string | null = null; + + const triggers = await this.api.PipelineTriggerTokens.all(projectId); + if (triggers && triggers.length > 0) { + token = triggers[0].token; } + return token; } + async getDTSubfolders(projectId: number): Promise { - const folderPath = 'digital_twins'; - try { - const files = await this.api.Repositories.allRepositoryTrees(projectId, { - path: folderPath, - recursive: false, - }); - - this.subfolders = files - .filter(file => file.type === 'tree' && file.path !== folderPath) - .map(file => ({ - name: file.name, - path: file.path - })); + let subfolders: FolderEntry[] = []; + + const files = await this.api.Repositories.allRepositoryTrees(projectId, { + path: DT_DIRECTORY, + recursive: false, + }); - return this.subfolders; - } catch (error) { - return []; - } + subfolders = files + .filter(file => file.type === 'tree' && file.path !== DT_DIRECTORY) + .map(file => ({ + name: file.name, + path: file.path + })); + + this.subfolders = subfolders; + return subfolders; } executionLogs(): LogEntry[] { diff --git a/client/src/util/gitlabDigitalTwin.ts b/client/src/util/gitlabDigitalTwin.ts index 71e502ade..b790020e8 100644 --- a/client/src/util/gitlabDigitalTwin.ts +++ b/client/src/util/gitlabDigitalTwin.ts @@ -5,7 +5,7 @@ class DigitalTwin { public gitlabInstance: GitlabInstance; - private lastExecutionStatus: string | null = null; + public lastExecutionStatus: string | null = null; constructor(DTName: string, gitlabInstance: GitlabInstance) { this.DTName = DTName; diff --git a/client/test/unitTests/Util/gitlab.test.ts b/client/test/unitTests/Util/gitlab.test.ts index 087f2f76c..a250348a3 100644 --- a/client/test/unitTests/Util/gitlab.test.ts +++ b/client/test/unitTests/Util/gitlab.test.ts @@ -21,7 +21,7 @@ describe('GitlabInstance', () => { beforeEach(() => { gitlab = new GitlabInstance(); gitlab.api = mockApi as unknown as InstanceType; - }); + }); it('should fetch project ID successfully', async () => { mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' } as GroupSchema); @@ -43,14 +43,6 @@ describe('GitlabInstance', () => { expect(projectId).toBeNull(); }); - it('should handle errors fetching project ID', async () => { - mockApi.Groups.show.mockRejectedValue(new Error('API error')); - - const projectId = await gitlab.getProjectId(); - - expect(projectId).toBeNull(); - }); - it('should fetch trigger token successfully', async () => { mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); @@ -77,15 +69,6 @@ describe('GitlabInstance', () => { expect(token).toBeNull(); }); - it('should handle errors fetching trigger token', async () => { - mockApi.PipelineTriggerTokens.all.mockRejectedValue(new Error('API error')); - - const token = await gitlab.getTriggerToken(1); - - expect(token).toBeNull(); - expect(mockApi.PipelineTriggerTokens.all).toHaveBeenCalledWith(1); - }); - it('should fetch DT subfolders successfully', async () => { mockApi.Repositories.allRepositoryTrees.mockResolvedValue([ { name: 'subfolder1', path: 'digital_twins/subfolder1', type: 'tree' } as RepositoryTreeSchema, @@ -106,14 +89,6 @@ describe('GitlabInstance', () => { }); }); - it('should handle errors fetching DT subfolders', async () => { - mockApi.Repositories.allRepositoryTrees.mockRejectedValue(new Error('API error')); - - const subfolders = await gitlab.getDTSubfolders(1); - - expect(subfolders).toEqual([]); - }); - it('should return execution logs', () => { const mockLog = { status: 'success', diff --git a/client/test/unitTests/Util/gitlabDigitalTwin.test.ts b/client/test/unitTests/Util/gitlabDigitalTwin.test.ts index b8696a9e3..970ab028c 100644 --- a/client/test/unitTests/Util/gitlabDigitalTwin.test.ts +++ b/client/test/unitTests/Util/gitlabDigitalTwin.test.ts @@ -1,46 +1,56 @@ -import { Gitlab , ProjectSchema, PipelineTriggerTokenSchema } from '@gitbeaker/rest'; +import { ProjectSchema, PipelineTriggerTokenSchema } from '@gitbeaker/rest'; import DigitalTwin from 'util/gitlabDigitalTwin'; import GitlabInstance from 'util/gitlab'; +type LogEntry = { status: string; DTName: string; runnerTag: string }; + const mockApi = { Groups: { show: jest.fn(), - allProjects: jest.fn() + allProjects: jest.fn(), }, PipelineTriggerTokens: { all: jest.fn(), - trigger: jest.fn() + trigger: jest.fn(), }, Repositories: { - allRepositoryTrees: jest.fn() - } + allRepositoryTrees: jest.fn(), + }, }; +const mockGitlabInstance = { + api: mockApi as unknown as GitlabInstance['api'], + executionLogs: jest.fn() as jest.Mock, + getProjectId: jest.fn(), + getTriggerToken: jest.fn(), + getDTSubfolders: jest.fn(), + logs: [], +} as unknown as GitlabInstance; + describe('DigitalTwin', () => { let dt: DigitalTwin; - let gitlabInstance: GitlabInstance; beforeEach(() => { - gitlabInstance = new GitlabInstance(); - gitlabInstance.api = mockApi as unknown as InstanceType; - dt = new DigitalTwin('test-DTName', gitlabInstance); + dt = new DigitalTwin('test-DTName', mockGitlabInstance); }); it('should handle null project ID during pipeline execution', async () => { mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' }); mockApi.Groups.allProjects.mockResolvedValue([]); - + (mockGitlabInstance.getProjectId as jest.Mock).mockResolvedValue(null); + const success = await dt.execute('test-runnerTag'); - + expect(success).toBe(false); expect(dt.executionStatus()).toBe('error'); expect(mockApi.PipelineTriggerTokens.trigger).not.toHaveBeenCalled(); - }); + }); it('should handle null trigger token during pipeline execution', async () => { mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' }); mockApi.Groups.allProjects.mockResolvedValue([{ id: 1, name: 'user1' } as ProjectSchema]); mockApi.PipelineTriggerTokens.all.mockResolvedValue([]); + (mockGitlabInstance.getTriggerToken as jest.Mock).mockResolvedValue(null); const success = await dt.execute('test-runnerTag'); @@ -53,7 +63,9 @@ describe('DigitalTwin', () => { mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' }); mockApi.Groups.allProjects.mockResolvedValue([{ id: 1, name: 'user1' } as ProjectSchema]); mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); - mockApi.PipelineTriggerTokens.trigger.mockResolvedValue(undefined); + (mockGitlabInstance.getProjectId as jest.Mock).mockResolvedValue(1); + (mockGitlabInstance.getTriggerToken as jest.Mock).mockResolvedValue('test-token'); + (mockApi.PipelineTriggerTokens.trigger as jest.Mock).mockResolvedValue(undefined); const success = await dt.execute('test-runnerTag'); @@ -71,8 +83,9 @@ describe('DigitalTwin', () => { mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' }); mockApi.Groups.allProjects.mockResolvedValue([{ id: 1, name: 'user1' } as ProjectSchema]); mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); - - mockApi.PipelineTriggerTokens.trigger.mockRejectedValue('String error message'); + (mockGitlabInstance.getProjectId as jest.Mock).mockResolvedValue(1); + (mockGitlabInstance.getTriggerToken as jest.Mock).mockResolvedValue('test-token'); + (mockApi.PipelineTriggerTokens.trigger as jest.Mock).mockRejectedValue('String error message'); const success = await dt.execute('test-runnerTag'); @@ -90,21 +103,16 @@ describe('DigitalTwin', () => { mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' }); mockApi.Groups.allProjects.mockResolvedValue([{ id: 1, name: 'user1' } as ProjectSchema]); mockApi.PipelineTriggerTokens.all.mockResolvedValue([{ token: 'test-token' } as PipelineTriggerTokenSchema]); - + mockApi.PipelineTriggerTokens.trigger.mockRejectedValue(new Error('Error instance message')); - + const success = await dt.execute('test-runnerTag'); - + expect(success).toBe(false); + expect(dt.executionStatus()).toBe('error'); - expect(mockApi.PipelineTriggerTokens.trigger).toHaveBeenCalledWith( - 1, - 'main', - 'test-token', - { variables: { DTName: 'test-DTName', RunnerTag: 'test-runnerTag' } } - ); }); - + it('should return execution logs', async () => { mockApi.Groups.show.mockResolvedValue({ id: 1, name: 'DTaaS' }); mockApi.Groups.allProjects.mockResolvedValue([{ id: 1, name: 'user1' } as ProjectSchema]); @@ -113,10 +121,14 @@ describe('DigitalTwin', () => { await dt.execute('test-runnerTag'); + (mockGitlabInstance.executionLogs as jest.Mock).mockReturnValue([ + { status: 'success', DTName: 'test-DTName', runnerTag: 'test-runnerTag' } + ]); + const logs = dt.gitlabInstance.executionLogs(); expect(logs).toHaveLength(1); expect(logs[0].status).toBe('success'); expect(logs[0].DTName).toBe('test-DTName'); expect(logs[0].runnerTag).toBe('test-runnerTag'); }); -}); \ No newline at end of file +}); From ea3fd034ca2b6f5e88217e5999be611e2cb83a84 Mon Sep 17 00:00:00 2001 From: Vanessa Scherma Date: Sun, 18 Aug 2024 19:30:27 +0200 Subject: [PATCH 15/65] Refactor --- client/src/util/gitlabDigitalTwin.ts | 59 +++++++++++++--------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/client/src/util/gitlabDigitalTwin.ts b/client/src/util/gitlabDigitalTwin.ts index b790020e8..7a2b1254a 100644 --- a/client/src/util/gitlabDigitalTwin.ts +++ b/client/src/util/gitlabDigitalTwin.ts @@ -13,39 +13,36 @@ class DigitalTwin { } async execute(runnerTag: string): Promise { - const projectId = await this.gitlabInstance.getProjectId(); - if (projectId === null) { - this.lastExecutionStatus = 'error'; - return false; - } - - const triggerToken = await this.gitlabInstance.getTriggerToken(projectId); - if (triggerToken === null) { - this.lastExecutionStatus = 'error'; - return false; - } - - const variables = { + const projectId = await this.gitlabInstance.getProjectId(); + if (!projectId) { + this.lastExecutionStatus = 'error'; + return false; + } + + const triggerToken = await this.gitlabInstance.getTriggerToken(projectId); + if (!triggerToken) { + this.lastExecutionStatus = 'error'; + return false; + } + + const variables = { DTName: this.DTName, RunnerTag: runnerTag }; + + try { + await this.gitlabInstance.api.PipelineTriggerTokens.trigger(projectId, 'main', triggerToken, { variables }); + this.gitlabInstance.logs.push({ status: 'success', DTName: this.DTName, runnerTag }); + this.lastExecutionStatus = 'success'; + return true; + } catch (error) { + this.gitlabInstance.logs.push({ + status: 'error', + error: new Error(String(error)), DTName: this.DTName, - RunnerTag: runnerTag, - } - - try { - await this.gitlabInstance.api.PipelineTriggerTokens.trigger( - projectId, - 'main', - triggerToken, - { variables } - ); - this.gitlabInstance.logs.push({ status: 'success', DTName: this.DTName, runnerTag }); - this.lastExecutionStatus = 'success'; - return true; - } catch (error) { - this.gitlabInstance.logs.push({ status: 'error', error: error instanceof Error ? error : new Error(String(error)), DTName: this.DTName, runnerTag }); - this.lastExecutionStatus = 'error'; - return false; - } + runnerTag + }); + this.lastExecutionStatus = 'error'; + return false; } +} executionStatus(): string | null { return this.lastExecutionStatus; From 41cd332dd5a1a37526c97fbf3b79b969675272ea Mon Sep 17 00:00:00 2001 From: Vanessa Scherma Date: Sun, 18 Aug 2024 19:33:25 +0200 Subject: [PATCH 16/65] Refactor --- client/src/util/gitlabDigitalTwin.ts | 51 ++++++++++++---------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/client/src/util/gitlabDigitalTwin.ts b/client/src/util/gitlabDigitalTwin.ts index 7a2b1254a..fd14e5ef5 100644 --- a/client/src/util/gitlabDigitalTwin.ts +++ b/client/src/util/gitlabDigitalTwin.ts @@ -13,36 +13,27 @@ class DigitalTwin { } async execute(runnerTag: string): Promise { - const projectId = await this.gitlabInstance.getProjectId(); - if (!projectId) { - this.lastExecutionStatus = 'error'; - return false; - } - - const triggerToken = await this.gitlabInstance.getTriggerToken(projectId); - if (!triggerToken) { - this.lastExecutionStatus = 'error'; - return false; - } - - const variables = { DTName: this.DTName, RunnerTag: runnerTag }; - - try { - await this.gitlabInstance.api.PipelineTriggerTokens.trigger(projectId, 'main', triggerToken, { variables }); - this.gitlabInstance.logs.push({ status: 'success', DTName: this.DTName, runnerTag }); - this.lastExecutionStatus = 'success'; - return true; - } catch (error) { - this.gitlabInstance.logs.push({ - status: 'error', - error: new Error(String(error)), - DTName: this.DTName, - runnerTag - }); - this.lastExecutionStatus = 'error'; - return false; - } -} + const projectId = await this.gitlabInstance.getProjectId(); + const triggerToken = projectId && await this.gitlabInstance.getTriggerToken(projectId); + + if (!projectId || !triggerToken) { + this.lastExecutionStatus = 'error'; + return false; + } + + const variables = { DTName: this.DTName, RunnerTag: runnerTag }; + + try { + await this.gitlabInstance.api.PipelineTriggerTokens.trigger(projectId, 'main', triggerToken, { variables }); + this.gitlabInstance.logs.push({ status: 'success', DTName: this.DTName, runnerTag }); + this.lastExecutionStatus = 'success'; + return true; + } catch (error) { + this.gitlabInstance.logs.push({ status: 'error', error: new Error(String(error)), DTName: this.DTName, runnerTag }); + this.lastExecutionStatus = 'error'; + return false; + } + } executionStatus(): string | null { return this.lastExecutionStatus; From cb8c5e84d96c228483c3a784548b29b70cc39a6b Mon Sep 17 00:00:00 2001 From: Vanessa Scherma Date: Sun, 18 Aug 2024 19:46:57 +0200 Subject: [PATCH 17/65] Temporary update --- client/src/components/DigitalTwinCard.tsx | 166 +++++++++++++++ .../src/route/digitaltwins/DigitalTwins.tsx | 51 ++++- client/src/util/gitlab.ts | 192 +++++++++++------- client/src/util/gitlabDigitalTwin.ts | 43 +++- client/src/util/gitlabDriver.ts | 2 +- 5 files changed, 370 insertions(+), 84 deletions(-) create mode 100644 client/src/components/DigitalTwinCard.tsx diff --git a/client/src/components/DigitalTwinCard.tsx b/client/src/components/DigitalTwinCard.tsx new file mode 100644 index 000000000..510b4f16a --- /dev/null +++ b/client/src/components/DigitalTwinCard.tsx @@ -0,0 +1,166 @@ +import React, { useState, useEffect } from 'react'; +import { Card, CardContent, CardActions, Typography, Button, FormControl, InputLabel, Select, MenuItem, Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material'; +import { GitlabInstance } from 'util/gitlab'; +import DigitalTwin from 'util/gitlabDigitalTwin'; + +const formatName = (name: string) => + name + .replace(/-/g, ' ') + .replace(/^./, (char) => char.toUpperCase()); + +const DigitalTwinCard: React.FC<{ name: string, gitlabInstance: GitlabInstance }> = (props) => { + const [digitalTwin, setDigitalTwin] = useState(null); + const [description, setDescription] = useState(null); + const [executionStatus, setExecutionStatus] = useState(null); + const [runnerTags, setRunnerTags] = useState([]); + const [selectedTag, setSelectedTag] = useState(''); + const [jobLogs, setJobLogs] = useState<{ jobName: string; log: string }[]>([]); + const [showLog, setShowLog] = useState(false); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const initDigitalTwin = async () => { + setLoading(true); + const dt = new DigitalTwin(props.name, props.gitlabInstance); + await dt.initDescription(); + setDigitalTwin(dt); + setDescription(dt.description); + setLoading(false); + }; + + const fetchRunnerTags = async () => { + const projectId = await props.gitlabInstance.getProjectId(); + if (projectId !== null) { + const tags = await props.gitlabInstance.getRunnerTags(projectId); + setRunnerTags(tags); + setSelectedTag(tags[0] || ''); + } + }; + + initDigitalTwin(); + fetchRunnerTags(); + }, [props.name, props.gitlabInstance]); + + const handleStartStop = async () => { + if (digitalTwin && selectedTag) { + const pipelineId = await digitalTwin.execute(selectedTag); + setExecutionStatus(digitalTwin.executionStatus()); + + const projectId = await props.gitlabInstance.getProjectId(); + + if (projectId && pipelineId) { + // Ottieni i job della pipeline + const jobs = await props.gitlabInstance.getPipelineJobs(projectId, pipelineId); + + // Ottieni i log di ciascun job + const logs = await Promise.all( + jobs.map(async (job) => { + const log = await props.gitlabInstance.getJobTrace(projectId, job.id); + return { jobName: job.name, log }; + }) + ); + + setJobLogs(logs); + } + } + }; + + + const handleToggleLog = () => { + setShowLog((prev) => !prev); + }; + + const handleCloseLog = () => { + setShowLog(false); + }; + + if (loading) { + return Loading...; + } + + if (!description) { + return null; + } + + return ( + <> + + + + {formatName(props.name)} + +
+ + {description} + +
+
+ + + Runner Tag + + + + + + + + {executionStatus && ( + + {executionStatus} + + )} +
+ + + {formatName(props.name)} Log + + {jobLogs.length > 0 ? ( + jobLogs.map((jobLog, index) => ( +
+ {jobLog.jobName} + ---------------------------- + + {jobLog.log} + +
+ )) + ) : ( + No logs available + )} +
+ + + +
+ + + ); +}; + +export default DigitalTwinCard; diff --git a/client/src/route/digitaltwins/DigitalTwins.tsx b/client/src/route/digitaltwins/DigitalTwins.tsx index c6f4580f2..cd81279aa 100644 --- a/client/src/route/digitaltwins/DigitalTwins.tsx +++ b/client/src/route/digitaltwins/DigitalTwins.tsx @@ -1,24 +1,63 @@ import * as React from 'react'; +import { useState, useEffect } from 'react'; import Layout from 'page/Layout'; import TabComponent from 'components/tab/TabComponent'; import Iframe from 'components/Iframe'; +import DigitalTwinCard from 'components/DigitalTwinCard'; import { TabData } from 'components/tab/subcomponents/TabRender'; import { useURLforDT } from 'util/envUtil'; -import { Typography } from '@mui/material'; +import { Typography, Grid, Box } from '@mui/material'; +import { GitlabInstance, FolderEntry } from 'util/gitlab'; import tabs from './DigitalTwinTabData'; function DTContent() { + const [subfolders, setSubfolders] = useState([]); + const [gitlabInstance, setGitlabInstance] = useState(null); const DTurl = useURLforDT(); + const fetchSubfolders = async () => { + const instance = new GitlabInstance(); + setGitlabInstance(instance); + const projectId = await instance.getProjectId(); + if (projectId) { + const subfoldersData = await instance.getDTSubfolders(projectId); + setSubfolders(subfoldersData); + } + }; + + useEffect(() => { + fetchSubfolders(); + }, []); + + const ExecuteComponent = () => ( + + + {subfolders.map((folder) => ( + + {gitlabInstance && ( + + )} + + ))} + + + ); + const DTTab: TabData[] = tabs.map((tab) => ({ label: tab.label, body: ( <> - {tab.body} - <> - {' '} -