diff --git a/NodeApp/package-lock.json b/NodeApp/package-lock.json index ba7bd845261fe22f359c5c422f92c34a7d1d9420..f989951efc4753f32445fed2d24dd47285be787e 100644 --- a/NodeApp/package-lock.json +++ b/NodeApp/package-lock.json @@ -9,7 +9,9 @@ "version": "3.6.0", "license": "AGPLv3", "dependencies": { - "@gitbeaker/rest": "^39.34.3", + "@gitbeaker/core": "^40.0.1", + "@gitbeaker/requester-utils": "^40.0.1", + "@gitbeaker/rest": "^40.0.1", "appdata-path": "^1.0.0", "axios": "^1.6.5", "boxen": "^5.1.2", @@ -28,6 +30,7 @@ "semver": "^7.5.4", "tar-stream": "^3.1.6", "winston": "^3.11.0", + "winston-transport": "^4.7.0", "yaml": "^2.3.4", "zod": "^3.22.4", "zod-validation-error": "^3.0.0" @@ -253,9 +256,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "peer": true, "engines": { @@ -263,11 +266,11 @@ } }, "node_modules/@gitbeaker/core": { - "version": "39.34.3", - "resolved": "https://registry.npmjs.org/@gitbeaker/core/-/core-39.34.3.tgz", - "integrity": "sha512-/3qBXme2MjO38QU2F/MYGon9a4wHKrgtwNzdHHdjpbYJ2/wOGNgbEWSZcibcFkiWVgAjbPXdYqC5sY8hcwGO1w==", + "version": "40.0.1", + "resolved": "https://registry.npmjs.org/@gitbeaker/core/-/core-40.0.1.tgz", + "integrity": "sha512-Zh2eVUgy2kYVnp7Db4gWoFqFbjgsnm2FvBEERbH3UM3cOA/iMqM+tw/of+Qk4yO+gv6tGZ9f4nF7+vK0tQFmDA==", "dependencies": { - "@gitbeaker/requester-utils": "^39.34.3", + "@gitbeaker/requester-utils": "^40.0.1", "qs": "^6.11.2", "xcase": "^2.0.1" }, @@ -276,9 +279,9 @@ } }, "node_modules/@gitbeaker/requester-utils": { - "version": "39.34.3", - "resolved": "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-39.34.3.tgz", - "integrity": "sha512-nMnTkTo4UixHPwPYsYIjp8UdKrmSw3TjvRESexliAeNNq4/LVeyVUyRqBUa1ZI8MXt1nPPnPX3wh8s7rqlm7uA==", + "version": "40.0.1", + "resolved": "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-40.0.1.tgz", + "integrity": "sha512-cn6fltKuQ3TbthoMTg+JsKQfozqGcRcz1jT9Nqzr4gpHWgjdQ/nr5JpjwzKABQNVL2JH3UJWr6Eji60CFZDZ6Q==", "dependencies": { "picomatch-browser": "^2.2.6", "qs": "^6.11.2", @@ -290,12 +293,12 @@ } }, "node_modules/@gitbeaker/rest": { - "version": "39.34.3", - "resolved": "https://registry.npmjs.org/@gitbeaker/rest/-/rest-39.34.3.tgz", - "integrity": "sha512-SuceThS6WhJtqNNcKmW8j0yUU7aXA4k5a29OWcd6bn7peQ3MXlIpbfvLLRnmuUaYUuxHLnUzZhAfuxaNf4DVtQ==", + "version": "40.0.1", + "resolved": "https://registry.npmjs.org/@gitbeaker/rest/-/rest-40.0.1.tgz", + "integrity": "sha512-JEd9WNuzgur7gLiJPMWPYKaWe5uX1ic8CGKR1fMtBityFZ2xyZkTZ+LG0nqWTV1MyiowYnJ1swTh8Yff+kLsKA==", "dependencies": { - "@gitbeaker/core": "^39.34.3", - "@gitbeaker/requester-utils": "^39.34.3" + "@gitbeaker/core": "^40.0.1", + "@gitbeaker/requester-utils": "^40.0.1" }, "engines": { "node": ">=18.0.0" @@ -362,14 +365,14 @@ "peer": true }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -385,9 +388,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" @@ -400,9 +403,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", - "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -904,18 +907,18 @@ } }, "node_modules/@types/node": { - "version": "18.19.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.17.tgz", - "integrity": "sha512-SzyGKgwPzuWp2SHhlpXKzCX0pIOfcI4V2eF37nNBJOhwlegQ83omtVQ1XxZpDE06V/d6AQvfQdPfnw0tRC//Ng==", + "version": "18.19.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.24.tgz", + "integrity": "sha512-eghAz3gnbQbvnHqB+mgB2ZR3aH6RhdEmHGS48BnV75KceQPHqabkxKI0BbUSsqhqy2Ddhc2xD/VAR9ySZd57Lw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, "node_modules/@types/semver": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz", - "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/tar-stream": { @@ -1317,11 +1320,11 @@ } }, "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -1338,9 +1341,9 @@ "dev": true }, "node_modules/bare-events": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.0.tgz", - "integrity": "sha512-Yyyqff4PIFfSuthCZqLlPISTWHmnQxoPuAvkmgzsJEmG3CesdIv6Xweayl0JkCZJSB2yYIdJyEz97tpxNhgjbg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.1.tgz", + "integrity": "sha512-9GYPpsPFvrWBkelIhOhTWtkeZxVxZOdb3VnFTCzlOo3OjvmTvzLoZFUT8kNFACx0vJej6QPney1Cf9BvzCNE/A==", "optional": true }, "node_modules/base64-js": { @@ -1886,9 +1889,9 @@ } }, "node_modules/dotenv-vault": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/dotenv-vault/-/dotenv-vault-1.26.0.tgz", - "integrity": "sha512-2PNnlprtOdFEG9+hAAZxXegcjlJVZMSy88arnRR4YjwU/PwkDbdtk1uzw/D88D5EZ0b84n7YVQ6RccRXmW/Qzg==", + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/dotenv-vault/-/dotenv-vault-1.26.1.tgz", + "integrity": "sha512-v+RK6LXpJQWhaelTT2s0b5FQB0qziRBuGCrAgAeDHtgkDEA0NqF7OXYXsrnKTuCPnwBg0FmNJr4lZebCpJnrFA==", "dev": true, "dependencies": { "@oclif/core": "^1", @@ -1896,7 +1899,7 @@ "@oclif/plugin-not-found": "^2.3.34", "@oclif/plugin-update": "^3.1.16", "@oclif/plugin-warn-if-update-available": "^2.0.46", - "axios": "^0.27.2", + "axios": "^1.6.7", "chalk": "^4.1.2", "dotenv": "^16.3.1" }, @@ -1907,16 +1910,6 @@ "node": ">=16" } }, - "node_modules/dotenv-vault/node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "dev": true, - "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - } - }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -2009,17 +2002,17 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -2446,9 +2439,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -2786,9 +2779,9 @@ } }, "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { "function-bind": "^1.1.2" }, @@ -3625,9 +3618,9 @@ } }, "node_modules/node-abi": { - "version": "3.55.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.55.0.tgz", - "integrity": "sha512-uPEjtyh2tFEvWYt4Jw7McOD5FPcHkcxm/tHZc5PWaDB3JYq0rGFUbgaAK+CT5pYpQddBfsZVWI08OwoRfdfbcQ==", + "version": "3.56.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.56.0.tgz", + "integrity": "sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q==", "dev": true, "dependencies": { "semver": "^7.3.5" @@ -4096,11 +4089,11 @@ } }, "node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.0.tgz", + "integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -4357,16 +4350,16 @@ } }, "node_modules/set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.1.2", + "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4394,11 +4387,11 @@ } }, "node_modules/side-channel": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", - "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" @@ -4753,9 +4746,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", - "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { "node": ">=16" @@ -4849,9 +4842,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -4947,9 +4940,9 @@ } }, "node_modules/winston": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz", - "integrity": "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.12.0.tgz", + "integrity": "sha512-OwbxKaOlESDi01mC9rkM0dQqQt2I8DAUMRLZ/HpbwvDXm85IryEHgoogy5fziQy38PntgZsLlhAYHz//UPHZ5w==", "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", @@ -4961,7 +4954,7 @@ "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", - "winston-transport": "^4.5.0" + "winston-transport": "^4.7.0" }, "engines": { "node": ">= 12.0.0" @@ -5028,9 +5021,12 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", + "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } @@ -5093,9 +5089,9 @@ } }, "node_modules/zod-validation-error": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.0.2.tgz", - "integrity": "sha512-21xGaDmnU7lJZ4J63n5GXWqi+rTzGy3gDHbuZ1jP6xrK/DEQGyOqs/xW7eH96tIfCOYm+ecCuT0bfajBRKEVUw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.0.3.tgz", + "integrity": "sha512-cETTrcMq3Ze58vhdR0zD37uJm/694I6mAxcf/ei5bl89cC++fBNxrC2z8lkFze/8hVMPwrbtrwXHR2LB50fpHw==", "engines": { "node": ">=18.0.0" }, diff --git a/NodeApp/package.json b/NodeApp/package.json index 87b5a5fcb2eda88e01efea2f3a8e4cdcd0b61c1b..0a191ab34aa9a8ac8f9cb73437c916dcdd31dc73 100644 --- a/NodeApp/package.json +++ b/NodeApp/package.json @@ -33,29 +33,31 @@ "test" : "echo \"Error: no test specified\" && exit 1" }, "dependencies" : { - "@gitbeaker/rest" : "^39.34.3", - "appdata-path" : "^1.0.0", - "axios" : "^1.6.5", - "boxen" : "^5.1.2", - "chalk" : "^4.1.2", - "commander" : "^11.1.0", - "dotenv" : "^16.3.1", - "dotenv-expand" : "^10.0.0", - "form-data" : "^4.0.0", - "fs-extra" : "^11.2.0", - "http-status-codes" : "^2.3.0", - "inquirer" : "^8.2.6", - "json5" : "^2.2.3", - "jsonwebtoken" : "^8.5.1", - "open" : "^8.4.2", - "ora" : "^5.4.1", - "semver" : "^7.5.4", - "tar-stream" : "^3.1.6", - "winston" : "^3.11.0", - "winston-transport" : "^4.7.0", - "yaml" : "^2.3.4", - "zod" : "^3.22.4", - "zod-validation-error": "^3.0.0" + "@gitbeaker/rest" : "^40.0.1", + "@gitbeaker/core" : "^40.0.1", + "@gitbeaker/requester-utils": "^40.0.1", + "appdata-path" : "^1.0.0", + "axios" : "^1.6.5", + "boxen" : "^5.1.2", + "chalk" : "^4.1.2", + "commander" : "^11.1.0", + "dotenv" : "^16.3.1", + "dotenv-expand" : "^10.0.0", + "form-data" : "^4.0.0", + "fs-extra" : "^11.2.0", + "http-status-codes" : "^2.3.0", + "inquirer" : "^8.2.6", + "json5" : "^2.2.3", + "jsonwebtoken" : "^8.5.1", + "open" : "^8.4.2", + "ora" : "^5.4.1", + "semver" : "^7.5.4", + "tar-stream" : "^3.1.6", + "winston" : "^3.11.0", + "winston-transport" : "^4.7.0", + "yaml" : "^2.3.4", + "zod" : "^3.22.4", + "zod-validation-error" : "^3.0.0" }, "devDependencies": { "@types/fs-extra" : "^11.0.4", diff --git a/NodeApp/src/commander/assignment/subcommands/AssignmentCreateCommand.ts b/NodeApp/src/commander/assignment/subcommands/AssignmentCreateCommand.ts index fed344e1177221865e279869473aabd492d28215..1bff535cc92cf37eec53fe663314d78e3396a1fa 100644 --- a/NodeApp/src/commander/assignment/subcommands/AssignmentCreateCommand.ts +++ b/NodeApp/src/commander/assignment/subcommands/AssignmentCreateCommand.ts @@ -2,16 +2,23 @@ import CommanderCommand from '../../CommanderCommand'; import ora from 'ora'; import AccessesHelper from '../../../helpers/AccessesHelper'; import Assignment from '../../../sharedByClients/models/Assignment'; -import GitlabUser from '../../../shared/types/Gitlab/GitlabUser'; -import GitlabManager from '../../../managers/GitlabManager'; import DojoBackendManager from '../../../managers/DojoBackendManager'; import Toolbox from '../../../shared/helpers/Toolbox'; +import * as Gitlab from '@gitbeaker/rest'; import TextStyle from '../../../types/TextStyle'; +import GitlabManager from '../../../managers/GitlabManager'; + + +type CommandOptions = { name: string, template?: string, members_id?: Array<number>, members_username?: Array<string>, clone?: string | boolean } class AssignmentCreateCommand extends CommanderCommand { protected commandName: string = 'create'; + private members!: Array<Gitlab.UserSchema> | undefined; + private templateIdOrNamespace: string | null = null; + private assignment!: Assignment; + protected defineCommand() { this.command .description('create a new repository for an assignment') @@ -23,76 +30,71 @@ class AssignmentCreateCommand extends CommanderCommand { .action(this.commandAction.bind(this)); } - protected async commandAction(options: { name: string, template?: string, members_id?: Array<number>, members_username?: Array<string>, clone?: string | boolean }): Promise<void> { - let members!: Array<GitlabUser> | false; - let templateIdOrNamespace: string | null = null; - let assignment!: Assignment; + private async dataRetrieval(options: CommandOptions) { + console.log(TextStyle.BLOCK('Please wait while we verify and retrieve data...')); - // Check access and retrieve data - { - console.log(TextStyle.BLOCK('Please wait while we verify and retrieve data...')); + if ( !await AccessesHelper.checkTeachingStaff() ) { + throw new Error(); + } - if ( !await AccessesHelper.checkTeachingStaff() ) { - return; - } + this.members = await GitlabManager.fetchMembers(options); + if ( !this.members ) { + throw new Error(); + } + + const assignmentGetSpinner: ora.Ora = ora('Checking assignment name availability').start(); + if ( await DojoBackendManager.getAssignment(options.name) ) { + assignmentGetSpinner.fail(`Assignment name "${ options.name }" is already taken. Please choose another one.`); + throw new Error(); + } + assignmentGetSpinner.succeed(`Assignment name "${ options.name }" is available`); + + if ( options.template ) { + this.templateIdOrNamespace = options.template; - members = await GitlabManager.fetchMembers(options); - if ( !members ) { - return; + if ( Number.isNaN(Number(this.templateIdOrNamespace)) ) { + this.templateIdOrNamespace = Toolbox.urlToPath(this.templateIdOrNamespace); } - const assignmentGetSpinner: ora.Ora = ora('Checking assignment name availability').start(); - if ( await DojoBackendManager.getAssignment(options.name) ) { - assignmentGetSpinner.fail(`Assignment name "${ options.name }" is already taken. Please choose another one.`); - return; + if ( !await DojoBackendManager.checkTemplateAccess(this.templateIdOrNamespace) ) { + throw new Error(); } - assignmentGetSpinner.succeed(`Assignment name "${ options.name }" is available`); + } + } - if ( options.template ) { - templateIdOrNamespace = options.template; + private async createAssignment(options: CommandOptions) { + console.log(TextStyle.BLOCK('Please wait while we are creating the assignment (approximately 10 seconds)...')); - if ( Number.isNaN(Number(templateIdOrNamespace)) ) { - templateIdOrNamespace = Toolbox.urlToPath(templateIdOrNamespace); - } + this.assignment = await DojoBackendManager.createAssignment(options.name, this.members!, this.templateIdOrNamespace); - if ( !await DojoBackendManager.checkTemplateAccess(encodeURIComponent(templateIdOrNamespace)) ) { - return; - } - } - } + const oraInfo = (message: string) => { + ora({ + text : message, + indent: 4 + }).start().info(); + }; - // Create the assignment - { - console.log(TextStyle.BLOCK('Please wait while we are creating the assignment (approximately 10 seconds)...')); - - try { - assignment = await DojoBackendManager.createAssignment(options.name, members, templateIdOrNamespace); - - const oraInfo = (message: string) => { - ora({ - text : message, - indent: 4 - }).start().info(); - }; - - oraInfo(`${ TextStyle.LIST_ITEM_NAME('Name:') } ${ assignment.name }`); - oraInfo(`${ TextStyle.LIST_ITEM_NAME('Web URL:') } ${ assignment.gitlabCreationInfo.web_url }`); - oraInfo(`${ TextStyle.LIST_ITEM_NAME('HTTP Repo:') } ${ assignment.gitlabCreationInfo.http_url_to_repo }`); - oraInfo(`${ TextStyle.LIST_ITEM_NAME('SSH Repo:') } ${ assignment.gitlabCreationInfo.ssh_url_to_repo }`); - } catch ( error ) { - return; - } - } + oraInfo(`${ TextStyle.LIST_ITEM_NAME('Name:') } ${ this.assignment.name }`); + oraInfo(`${ TextStyle.LIST_ITEM_NAME('Web URL:') } ${ this.assignment.gitlabCreationInfo.web_url }`); + oraInfo(`${ TextStyle.LIST_ITEM_NAME('HTTP Repo:') } ${ this.assignment.gitlabCreationInfo.http_url_to_repo }`); + oraInfo(`${ TextStyle.LIST_ITEM_NAME('SSH Repo:') } ${ this.assignment.gitlabCreationInfo.ssh_url_to_repo }`); + } - // Clone the repository - { - if ( options.clone ) { - console.log(TextStyle.BLOCK('Please wait while we are cloning the repository...')); + private async cloneRepository(options: CommandOptions) { + if ( options.clone ) { + console.log(TextStyle.BLOCK('Please wait while we are cloning the repository...')); - await GitlabManager.cloneRepository(options.clone, assignment.gitlabCreationInfo.ssh_url_to_repo, undefined, true, 0); - } + await GitlabManager.cloneRepository(options.clone, this.assignment.gitlabCreationInfo.ssh_url_to_repo, undefined, true, 0); } } + + protected async commandAction(options: CommandOptions): Promise<void> { + try { + await this.dataRetrieval(options); + await this.createAssignment(options); + await this.cloneRepository(options); + } catch ( e ) { /* Do nothing */ } + } } diff --git a/NodeApp/src/commander/auth/subcommands/AuthTestCommand.ts b/NodeApp/src/commander/auth/subcommands/AuthTestCommand.ts index d1f57e64ec809f5684fef9e09ae7c0869944cca5..e2d18f8e0cd02004939d24aab7841a5c9b8ed050 100644 --- a/NodeApp/src/commander/auth/subcommands/AuthTestCommand.ts +++ b/NodeApp/src/commander/auth/subcommands/AuthTestCommand.ts @@ -8,8 +8,8 @@ class AuthTestCommand extends CommanderCommand { protected defineCommand() { this.command - .description('test availability of registered Dojo API and Gitlab API sessions') - .action(this.commandAction.bind(this)); + .description('test availability of registered Dojo API and Gitlab API sessions') + .action(this.commandAction.bind(this)); } protected async commandAction(): Promise<void> { diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts index 2fc6bf7bb8da17746bf56cfceed927aed91fb65e..8a0d7ea5aa83a020396d9aa09019c3c6f30552c1 100644 --- a/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts +++ b/NodeApp/src/commander/exercise/subcommands/ExerciseCreateCommand.ts @@ -1,17 +1,24 @@ import CommanderCommand from '../../CommanderCommand'; -import GitlabManager from '../../../managers/GitlabManager'; -import GitlabUser from '../../../shared/types/Gitlab/GitlabUser'; import ora from 'ora'; import DojoBackendManager from '../../../managers/DojoBackendManager'; import AccessesHelper from '../../../helpers/AccessesHelper'; import Assignment from '../../../sharedByClients/models/Assignment'; import Exercise from '../../../sharedByClients/models/Exercise'; +import * as Gitlab from '@gitbeaker/rest'; import TextStyle from '../../../types/TextStyle'; +import GitlabManager from '../../../managers/GitlabManager'; + + +type CommandOptions = { assignment: string, members_id?: Array<number>, members_username?: Array<string>, clone?: string | boolean } class ExerciseCreateCommand extends CommanderCommand { protected commandName: string = 'create'; + private members!: Array<Gitlab.UserSchema> | undefined; + private assignment!: Assignment | undefined; + private exercise!: Exercise; + protected defineCommand() { this.command .description('create a new exercise from an assignment') @@ -22,80 +29,76 @@ class ExerciseCreateCommand extends CommanderCommand { .action(this.commandAction.bind(this)); } - protected async commandAction(options: { assignment: string, members_id?: Array<number>, members_username?: Array<string>, clone?: string | boolean }): Promise<void> { - let members!: Array<GitlabUser> | false; - let assignment!: Assignment | undefined; - let exercise!: Exercise; - - // Check access and retrieve data - { - console.log(TextStyle.BLOCK('Please wait while we verify and retrieve data...')); - - if ( !await AccessesHelper.checkStudent() ) { - return; - } - - members = await GitlabManager.fetchMembers(options); - if ( !members ) { - return; - } - - ora('Checking assignment:').start().info(); - const assignmentGetSpinner: ora.Ora = ora({ - text : 'Checking if assignment exists', - indent: 4 - }).start(); - assignment = await DojoBackendManager.getAssignment(options.assignment); - if ( !assignment ) { - assignmentGetSpinner.fail(`Assignment "${ options.assignment }" doesn't exists`); - return; - } - assignmentGetSpinner.succeed(`Assignment "${ options.assignment }" exists`); - - const assignmentPublishedSpinner: ora.Ora = ora({ - text : 'Checking if assignment is published', - indent: 4 - }).start(); - if ( !assignment.published ) { - assignmentPublishedSpinner.fail(`Assignment "${ assignment.name }" isn't published`); - return; - } - assignmentPublishedSpinner.succeed(`Assignment "${ assignment.name }" is published`); + private async dataRetrieval(options: CommandOptions) { + console.log(TextStyle.BLOCK('Please wait while we verify and retrieve data...')); + + if ( !await AccessesHelper.checkStudent() ) { + throw new Error(); + } + + this.members = await GitlabManager.fetchMembers(options); + if ( !this.members ) { + throw new Error(); } - //Create the exercise - { - console.log(TextStyle.BLOCK('Please wait while we are creating the exercise (approximately 10 seconds)...')); - - try { - exercise = await DojoBackendManager.createExercise(assignment.name, members); - - const oraInfo = (message: string) => { - ora({ - text : message, - indent: 4 - }).start().info(); - }; - - oraInfo(`${ TextStyle.LIST_ITEM_NAME('Id:') } ${ exercise.id }`); - oraInfo(`${ TextStyle.LIST_ITEM_NAME('Name:') } ${ exercise.name }`); - oraInfo(`${ TextStyle.LIST_ITEM_NAME('Web URL:') } ${ exercise.gitlabCreationInfo.web_url }`); - oraInfo(`${ TextStyle.LIST_ITEM_NAME('HTTP Repo:') } ${ exercise.gitlabCreationInfo.http_url_to_repo }`); - oraInfo(`${ TextStyle.LIST_ITEM_NAME('SSH Repo:') } ${ exercise.gitlabCreationInfo.ssh_url_to_repo }`); - } catch ( error ) { - return; - } + ora('Checking assignment:').start().info(); + const assignmentGetSpinner: ora.Ora = ora({ + text : 'Checking if assignment exists', + indent: 4 + }).start(); + this.assignment = await DojoBackendManager.getAssignment(options.assignment); + if ( !this.assignment ) { + assignmentGetSpinner.fail(`Assignment "${ options.assignment }" doesn't exists`); + throw new Error(); + } + assignmentGetSpinner.succeed(`Assignment "${ options.assignment }" exists`); + + const assignmentPublishedSpinner: ora.Ora = ora({ + text : 'Checking if assignment is published', + indent: 4 + }).start(); + if ( !this.assignment.published ) { + assignmentPublishedSpinner.fail(`Assignment "${ this.assignment.name }" isn't published`); + throw new Error(); } + assignmentPublishedSpinner.succeed(`Assignment "${ this.assignment.name }" is published`); + } + + private async createExercise() { + console.log(TextStyle.BLOCK('Please wait while we are creating the exercise (approximately 10 seconds)...')); + + this.exercise = await DojoBackendManager.createExercise(this.assignment!.name, this.members!); - // Clone the repository - { - if ( options.clone ) { - console.log(TextStyle.BLOCK('Please wait while we are cloning the repository...')); + const oraInfo = (message: string) => { + ora({ + text : message, + indent: 4 + }).start().info(); + }; + + oraInfo(`${ TextStyle.LIST_ITEM_NAME('Id:') } ${ this.exercise.id }`); + oraInfo(`${ TextStyle.LIST_ITEM_NAME('Name:') } ${ this.exercise.name }`); + oraInfo(`${ TextStyle.LIST_ITEM_NAME('Web URL:') } ${ this.exercise.gitlabCreationInfo.web_url }`); + oraInfo(`${ TextStyle.LIST_ITEM_NAME('HTTP Repo:') } ${ this.exercise.gitlabCreationInfo.http_url_to_repo }`); + oraInfo(`${ TextStyle.LIST_ITEM_NAME('SSH Repo:') } ${ this.exercise.gitlabCreationInfo.ssh_url_to_repo }`); + } - await GitlabManager.cloneRepository(options.clone, exercise.gitlabCreationInfo.ssh_url_to_repo, `DojoExercise - ${ exercise.assignmentName }`, true, 0); - } + private async cloneRepository(options: CommandOptions) { + if ( options.clone ) { + console.log(TextStyle.BLOCK('Please wait while we are cloning the repository...')); + + await GitlabManager.cloneRepository(options.clone, this.exercise.gitlabCreationInfo.ssh_url_to_repo, `DojoExercise - ${ this.exercise.assignmentName }`, true, 0); } } + + + protected async commandAction(options: CommandOptions): Promise<void> { + try { + await this.dataRetrieval(options); + await this.createExercise(); + await this.cloneRepository(options); + } catch ( e ) { /* Do nothing */ } + } } diff --git a/NodeApp/src/helpers/AutoCompletionHelper.ts b/NodeApp/src/helpers/AutoCompletionHelper.ts index 04464f6205fd62b86cfe19631fc1d3e988ea694d..13760c3927e123a13fc5ddb4c15048cee82944a7 100644 --- a/NodeApp/src/helpers/AutoCompletionHelper.ts +++ b/NodeApp/src/helpers/AutoCompletionHelper.ts @@ -67,7 +67,6 @@ complete -f -c dojo function isHidden(cmd: Command): boolean { return (cmd as Command & { _hidden: boolean })._hidden; - } function isLeaf(cmd: Command): boolean { diff --git a/NodeApp/src/helpers/GlobalHelper.ts b/NodeApp/src/helpers/GlobalHelper.ts index c7dccb10389fda604b31052c01e9fe1e8e54b0cf..8ecac86046acc962b43ca8212468150e3ac0b803 100644 --- a/NodeApp/src/helpers/GlobalHelper.ts +++ b/NodeApp/src/helpers/GlobalHelper.ts @@ -1,5 +1,6 @@ import { Command, Option } from 'commander'; import Config from '../config/Config'; +import SessionManager from '../managers/SessionManager'; class GlobalHelper { @@ -12,6 +13,13 @@ class GlobalHelper { return command; } + + + public readonly refreshGitlabTokenFunction = async () => { + await SessionManager.refreshTokens(); + + return SessionManager.gitlabCredentials.accessToken ?? ''; + }; } diff --git a/NodeApp/src/managers/DojoBackendManager.ts b/NodeApp/src/managers/DojoBackendManager.ts index 9bf65df250b19eae0aa58a8b97885cb651be88c0..5e482210ba707662f857dd8067b974cf7947598a 100644 --- a/NodeApp/src/managers/DojoBackendManager.ts +++ b/NodeApp/src/managers/DojoBackendManager.ts @@ -1,7 +1,6 @@ import axios, { AxiosError } from 'axios'; import ora from 'ora'; import ApiRoute from '../sharedByClients/types/Dojo/ApiRoute'; -import GitlabUser from '../shared/types/Gitlab/GitlabUser'; import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig'; import Assignment from '../sharedByClients/models/Assignment'; import DojoBackendResponse from '../shared/types/Dojo/DojoBackendResponse'; @@ -9,7 +8,9 @@ import Exercise from '../sharedByClients/models/Exercise'; import GitlabToken from '../shared/types/Gitlab/GitlabToken'; import User from '../sharedByClients/models/User'; import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode'; +import * as Gitlab from '@gitbeaker/rest'; import DojoBackendHelper from '../sharedByClients/helpers/Dojo/DojoBackendHelper'; +import GitlabPipelineStatus from '../shared/types/Gitlab/GitlabPipelineStatus'; class DojoBackendManager { @@ -20,20 +21,35 @@ class DojoBackendManager { case DojoStatusCode.ASSIGNMENT_EXERCISE_NOT_RELATED: spinner.fail(`The exercise does not belong to the assignment.`); break; + case DojoStatusCode.ASSIGNMENT_PUBLISH_NO_PIPELINE: + spinner.fail(`No pipeline found for this assignment.`); + break; + case DojoStatusCode.ASSIGNMENT_PUBLISH_PIPELINE_FAILED: + spinner.fail(error.response?.data?.message ?? `Last pipeline status is not "${ GitlabPipelineStatus.SUCCESS }".`); + break; case DojoStatusCode.EXERCISE_CORRECTION_ALREADY_EXIST: spinner.fail(`This exercise is already labelled as a correction. If you want to update it, please use the update command.`); break; case DojoStatusCode.EXERCISE_CORRECTION_NOT_EXIST: spinner.fail(`The exercise is not labelled as a correction so it's not possible to update it.`); break; + case DojoStatusCode.ASSIGNMENT_NAME_CONFLICT: + spinner.fail(`Assignment creation error: The assignment name already exists. Please choose another name.`); + break; case DojoStatusCode.ASSIGNMENT_CREATION_GITLAB_ERROR: - spinner.fail(`Assignment creation error: An unknown error occurred while creating the assignment on Gitlab. Please try again later or contact an administrator.`); + spinner.fail(`Assignment creation error: An unknown error occurred while creating the assignment on GitLab (internal error message: ${ error.response?.data?.description ?? 'unknown error' }). Please try again later or contact an administrator.`); + break; + case DojoStatusCode.ASSIGNMENT_CREATION_INTERNAL_ERROR: + spinner.fail(`Assignment creation error: An unknown error occurred while creating the assignment (internal error message: ${ error.response?.data?.description ?? 'unknown error' }). Please try again later or contact an administrator.`); break; case DojoStatusCode.MAX_EXERCISE_PER_ASSIGNMENT_REACHED: - spinner.fail(`The following users have reached the maximum number of exercise of this assignment : ${ ((error.response.data as DojoBackendResponse<Array<GitlabUser>>).data).map(user => user.name).join(', ') }.`); + spinner.fail(`The following users have reached the maximum number of exercise of this assignment : ${ ((error.response.data as DojoBackendResponse<Array<Gitlab.UserSchema>>).data).map(user => user.name).join(', ') }.`); + break; + case DojoStatusCode.EXERCISE_CREATION_INTERNAL_ERROR: + spinner.fail(`Exercise creation error: An unknown error occurred while creating the exercise (internal error message: ${ error.response?.data?.description ?? 'unknown error' }). Please try again later or contact an administrator.`); break; case DojoStatusCode.EXERCISE_CREATION_GITLAB_ERROR: - spinner.fail(`Exercise creation error: An unknown error occurred while creating the exercise on Gitlab. Please try again later or contact an administrator.`); + spinner.fail(`Exercise creation error: An unknown error occurred while creating the exercise on GitLab (internal error message: ${ error.response?.data?.description ?? 'unknown error' }). Please try again later or contact an administrator.`); break; case DojoStatusCode.GITLAB_TEMPLATE_NOT_FOUND: spinner.fail(`Template not found or access denied. Please check the template ID or url. Also, please check that the template have public/internal visibility or that your and Dojo account (${ ClientsSharedConfig.gitlab.dojoAccount.username }) have at least reporter role to the template (if private).`); @@ -106,7 +122,7 @@ class DojoBackendManager { } } - public async createAssignment(name: string, members: Array<GitlabUser>, templateIdOrNamespace: string | null, verbose: boolean = true): Promise<Assignment> { + public async createAssignment(name: string, members: Array<Gitlab.UserSchema>, templateIdOrNamespace: string | null, verbose: boolean = true): Promise<Assignment> { const spinner: ora.Ora = ora('Creating assignment...'); if ( verbose ) { @@ -131,7 +147,7 @@ class DojoBackendManager { } } - public async createExercise(assignmentName: string, members: Array<GitlabUser>, verbose: boolean = true): Promise<Exercise> { + public async createExercise(assignmentName: string, members: Array<Gitlab.UserSchema>, verbose: boolean = true): Promise<Exercise> { const spinner: ora.Ora = ora('Creating exercise...'); if ( verbose ) { @@ -169,13 +185,7 @@ class DojoBackendManager { return; } catch ( error ) { - if ( verbose ) { - if ( error instanceof AxiosError && error.response ) { - spinner.fail(`Assignment visibility change error: ${ error.response.statusText }`); - } else { - spinner.fail(`Assignment visibility change error: unknown error`); - } - } + this.handleApiError(error, spinner, verbose, `Assignment visibility change error: ${ error }`); throw error; } diff --git a/NodeApp/src/managers/GitlabManager.ts b/NodeApp/src/managers/GitlabManager.ts index f8ee3f16dd36dfc68418dd6eab1baf2384fc64ad..9291be49dced0d043b285bd14824eecd5bb0f702 100644 --- a/NodeApp/src/managers/GitlabManager.ts +++ b/NodeApp/src/managers/GitlabManager.ts @@ -1,16 +1,18 @@ -import axios from 'axios'; -import ora from 'ora'; -import GitlabUser from '../shared/types/Gitlab/GitlabUser'; -import GitlabRoute from '../shared/types/Gitlab/GitlabRoute'; -import SharedConfig from '../shared/config/SharedConfig'; -import GitlabRepository from '../shared/types/Gitlab/GitlabRepository'; -import fs from 'fs-extra'; -import { spawn } from 'child_process'; - - -class GitlabManager { - private getApiUrl(route: GitlabRoute): string { - return `${ SharedConfig.gitlab.apiURL }${ route }`; +import ora from 'ora'; +import fs from 'fs-extra'; +import { spawn } from 'child_process'; +import { NotificationSettingSchema, UserSchema } from '@gitbeaker/rest'; +import * as GitlabCore from '@gitbeaker/core'; +import SharedGitlabManager from '../shared/managers/SharedGitlabManager'; +import GlobalHelper from '../helpers/GlobalHelper'; + + +type getGitlabUser = (param: number | string) => Promise<UserSchema | undefined> + + +class GitlabManager extends SharedGitlabManager { + constructor() { + super('', GlobalHelper.refreshGitlabTokenFunction.bind(GlobalHelper)); } public async testToken(verbose: boolean = true): Promise<[ boolean, boolean ]> { @@ -35,7 +37,7 @@ class GitlabManager { } try { - notificationSettings = (await this.getNotificationSettings()).data as NotificationSettings; + notificationSettings = await this.getNotificationSettings(); result[0] = true; @@ -83,15 +85,15 @@ class GitlabManager { return result; } - public getNotificationSettings() { - return axios.get(this.getApiUrl(GitlabRoute.NOTIFICATION_SETTINGS)); + public getNotificationSettings(): Promise<NotificationSettingSchema> { + return this.executeGitlabRequest(() => this.api.NotificationSettings.show()); } - public setNotificationSettings(newSettings: Record<string, string>) { - return axios.put(this.getApiUrl(GitlabRoute.NOTIFICATION_SETTINGS), { params: new URLSearchParams(newSettings) }); + public setNotificationSettings(newSettings: GitlabCore.EditNotificationSettingsOptions) { + return this.executeGitlabRequest(() => this.api.NotificationSettings.edit(newSettings)); } - private async getGitlabUsers(paramsToSearch: Array<string | number>, paramName: string, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<GitlabUser | undefined>> { + private async getGitlabUsers(paramsToSearch: Array<string | number>, searchFunction: getGitlabUser, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<UserSchema | undefined>> { try { return await Promise.all(paramsToSearch.map(async param => { const spinner: ora.Ora = ora({ @@ -101,18 +103,14 @@ class GitlabManager { if ( verbose ) { spinner.start(); } - const params: { [key: string]: unknown } = {}; - params[paramName] = param; - const user = await axios.get<Array<GitlabUser>>(this.getApiUrl(GitlabRoute.USERS_GET), { params: params }); - if ( user.data[0] ) { - const gitlabUser = user.data[0]; + const user = await searchFunction(param); + if ( user ) { if ( verbose ) { - spinner.succeed(`${ gitlabUser.username } (${ gitlabUser.id })`); + spinner.succeed(`${ user.username } (${ user.id })`); } - - return gitlabUser; + return user; } else { if ( verbose ) { spinner.fail(`${ param }`); @@ -126,30 +124,26 @@ class GitlabManager { } } - public async getUsersById(ids: Array<number>, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<GitlabUser | undefined>> { - return this.getGitlabUsers(ids, 'id', verbose, verboseIndent); + public async getUsersById(ids: Array<number>, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<UserSchema | undefined>> { + return this.getGitlabUsers(ids, this.getUserById.bind(this) as getGitlabUser, verbose, verboseIndent); } - public async getUsersByUsername(usernames: Array<string>, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<GitlabUser | undefined>> { - return this.getGitlabUsers(usernames, 'search', verbose, verboseIndent); + public async getUsersByUsername(usernames: Array<string>, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<UserSchema | undefined>> { + return this.getGitlabUsers(usernames, this.getUserByUsername.bind(this) as getGitlabUser, verbose, verboseIndent); } - public async getRepository(repoId: number): Promise<GitlabRepository> { - return axios.get(this.getApiUrl(GitlabRoute.REPOSITORY_GET).replace('{{id}}', repoId.toString())); - } - - public async fetchMembers(options: { members_id?: Array<number>, members_username?: Array<string> }): Promise<Array<GitlabUser> | false> { + public async fetchMembers(options: { members_id?: Array<number>, members_username?: Array<string> }): Promise<Array<UserSchema> | undefined> { if ( options.members_id || options.members_username ) { ora('Checking Gitlab members:').start().info(); } - let members: Array<GitlabUser> = []; + let members: Array<UserSchema> = []; - async function getMembers<T>(context: unknown, functionName: string, paramsToSearch: Array<T>): Promise<boolean> { - const gitlabUsers = await ((context as { [functionName: string]: (arg: Array<T>, verbose: boolean, verboseIndent: number) => Promise<Array<GitlabUser | undefined>> })[functionName])(paramsToSearch, true, 8); + async function isUsersExists<T>(context: unknown, functionName: string, paramsToSearch: Array<T>): Promise<boolean> { + const users = await ((context as { [functionName: string]: (arg: Array<T>, verbose: boolean, verboseIndent: number) => Promise<Array<UserSchema | undefined>> })[functionName])(paramsToSearch, true, 8); - if ( gitlabUsers.every(user => user) ) { - members = members.concat(gitlabUsers as Array<GitlabUser>); + if ( users.every(user => user) ) { + members = members.concat(users as Array<UserSchema>); return true; } else { return false; @@ -164,7 +158,7 @@ class GitlabManager { indent: 4 }).start().info(); - result = await getMembers(this, 'getUsersById', options.members_id); + result = await isUsersExists(this, 'getUsersById', options.members_id); } if ( options.members_username ) { @@ -173,16 +167,10 @@ class GitlabManager { indent: 4 }).start().info(); - result = result && await getMembers(this, 'getUsersByUsername', options.members_username); + result = result && await isUsersExists(this, 'getUsersByUsername', options.members_username); } - if ( !result ) { - return false; - } - - members = members.removeObjectDuplicates(gitlabUser => gitlabUser.id); - - return members; + return result ? members.removeObjectDuplicates(gitlabUser => gitlabUser.id) : undefined; } public async cloneRepository(clonePath: string | boolean, repositorySshUrl: string, folderName?: string, verbose: boolean = false, verboseIndent: number = 0) { diff --git a/NodeApp/src/managers/HttpManager.ts b/NodeApp/src/managers/HttpManager.ts index a28fc2162edd5ad3aa5082a33328d4eeb9bcf8d7..be8ac3b5326c335db571fb8562c44aadb13478aa 100644 --- a/NodeApp/src/managers/HttpManager.ts +++ b/NodeApp/src/managers/HttpManager.ts @@ -1,15 +1,14 @@ -import axios, { AxiosError, AxiosRequestConfig, AxiosRequestHeaders } from 'axios'; -import SessionManager from './SessionManager'; -import FormData from 'form-data'; -import { StatusCodes } from 'http-status-codes'; -import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig'; -import { version } from '../config/Version'; -import DojoBackendResponse from '../shared/types/Dojo/DojoBackendResponse'; -import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode'; -import boxen from 'boxen'; -import SharedConfig from '../shared/config/SharedConfig'; -import { stateConfigFile } from '../config/ConfigFiles'; -import TextStyle from '../types/TextStyle'; +import axios, { AxiosError, AxiosRequestHeaders } from 'axios'; +import SessionManager from './SessionManager'; +import FormData from 'form-data'; +import { StatusCodes } from 'http-status-codes'; +import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig'; +import { version } from '../config/Version'; +import DojoBackendResponse from '../shared/types/Dojo/DojoBackendResponse'; +import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode'; +import boxen from 'boxen'; +import { stateConfigFile } from '../config/ConfigFiles'; +import TextStyle from '../types/TextStyle'; class HttpManager { @@ -22,7 +21,6 @@ class HttpManager { private registerRequestInterceptor() { axios.interceptors.request.use(config => { - if ( config.data instanceof FormData ) { config.headers = { ...config.headers, ...config.data.getHeaders() } as AxiosRequestHeaders; } @@ -42,10 +40,6 @@ class HttpManager { config.headers['client-version'] = version; } - if ( SessionManager.gitlabCredentials.accessToken && config.url && config.url.indexOf(SharedConfig.gitlab.apiURL) !== -1 ) { - config.headers.Authorization = `Bearer ${ SessionManager.gitlabCredentials.accessToken }`; - } - return config; }); } @@ -82,45 +76,26 @@ class HttpManager { private apiAuthorizationError(error: AxiosError, isFromApi: boolean) { if ( this.handleAuthorizationCommandErrors && isFromApi && error.response ) { - switch ( error.response.status ) { - case StatusCodes.UNAUTHORIZED: - this.requestError('Session expired or does not exist. Please login again.'); - break; - case StatusCodes.FORBIDDEN: - this.requestError('Forbidden access.'); - break; - default: - this.requestError('Unknown error.'); - break; + const errorCustomCode = (error.response.data as DojoBackendResponse<unknown> | undefined)?.code ?? error.response.status; + + if ( errorCustomCode === error.response.status ) { + switch ( error.response.status ) { + case StatusCodes.UNAUTHORIZED: + this.requestError('Session expired or does not exist. Please login again.'); + break; + case StatusCodes.FORBIDDEN: + this.requestError('Forbidden access.'); + break; + default: + this.requestError('Unknown error.'); + break; + } } } else { this.handleAuthorizationCommandErrors = true; } } - private async gitlabAuthorizationError(error: AxiosError, isFromGitlab: boolean): Promise<Promise<unknown> | undefined> { - const originalConfig = Object.assign({}, error.config, { _retry: false }); - - // Try to refresh the Gitlab tokens if the request have failed with a 401 error - if ( error.response && error.response.status === StatusCodes.UNAUTHORIZED && isFromGitlab && !originalConfig?._retry ) { - originalConfig._retry = true; - - try { - await SessionManager.refreshTokens(); - - return axios(error.config as AxiosRequestConfig); - } catch ( subError ) { - if ( subError instanceof AxiosError && subError.response && subError.response.data ) { - return Promise.reject(subError.response.data); - } - - return Promise.reject(subError); - } - } - - return undefined; - } - private registerResponseInterceptor() { axios.interceptors.response.use(response => { if ( response.data && response.data.sessionToken ) { @@ -140,11 +115,8 @@ class HttpManager { return response; }, async error => { if ( error.response ) { - const isFromApi = error.response.config.url && error.response.config.url.indexOf(ClientsSharedConfig.apiURL) !== -1; - const isFromGitlab = error.response.config.url && error.response.config.url.indexOf(SharedConfig.gitlab.URL) !== -1; - await this.gitlabAuthorizationError(error, isFromGitlab); this.apiMethodNotAllowed(error, isFromApi); this.apiAuthorizationError(error, isFromApi); } else { diff --git a/NodeApp/src/managers/SessionManager.ts b/NodeApp/src/managers/SessionManager.ts index c25748cf74760c5e7ad069e74e9b8d3e92f5e21f..1f2ed42af8b02a3705caf756210a44fca0685eac 100644 --- a/NodeApp/src/managers/SessionManager.ts +++ b/NodeApp/src/managers/SessionManager.ts @@ -1,27 +1,27 @@ -import * as jwt from 'jsonwebtoken'; -import User from '../sharedByClients/models/User'; -import LocalConfigKeys from '../types/LocalConfigKeys'; -import axios, { HttpStatusCode } from 'axios'; -import HttpManager from './HttpManager'; -import ora from 'ora'; -import Permissions from '../types/Permissions'; -import ApiRoute from '../sharedByClients/types/Dojo/ApiRoute'; -import DojoBackendManager from './DojoBackendManager'; -import Config from '../config/Config'; -import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig'; -import DojoGitlabCredentials from '../sharedByClients/types/Dojo/DojoGitlabCredentials'; -import * as http from 'http'; -import EventEmitter from 'events'; -import SharedConfig from '../shared/config/SharedConfig'; -import chalk from 'chalk'; -import inquirer from 'inquirer'; -import SharedGitlabManager from '../shared/managers/SharedGitlabManager'; -import GitlabManager from './GitlabManager'; -import GitlabToken from '../shared/types/Gitlab/GitlabToken'; -import open from 'open'; -import { sessionConfigFile } from '../config/ConfigFiles'; -import TextStyle from '../types/TextStyle'; -import DojoBackendHelper from '../sharedByClients/helpers/Dojo/DojoBackendHelper'; +import * as jwt from 'jsonwebtoken'; +import User from '../sharedByClients/models/User'; +import LocalConfigKeys from '../types/LocalConfigKeys'; +import axios from 'axios'; +import HttpManager from './HttpManager'; +import ora from 'ora'; +import Permissions from '../types/Permissions'; +import ApiRoute from '../sharedByClients/types/Dojo/ApiRoute'; +import DojoBackendManager from './DojoBackendManager'; +import Config from '../config/Config'; +import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig'; +import DojoGitlabCredentials from '../sharedByClients/types/Dojo/DojoGitlabCredentials'; +import * as http from 'http'; +import EventEmitter from 'events'; +import SharedConfig from '../shared/config/SharedConfig'; +import chalk from 'chalk'; +import inquirer from 'inquirer'; +import GitlabToken from '../shared/types/Gitlab/GitlabToken'; +import open from 'open'; +import { sessionConfigFile } from '../config/ConfigFiles'; +import TextStyle from '../types/TextStyle'; +import DojoBackendHelper from '../sharedByClients/helpers/Dojo/DojoBackendHelper'; +import GitlabManager from './GitlabManager'; +import { StatusCodes } from 'http-status-codes'; class LoginServer { @@ -32,7 +32,7 @@ class LoginServer { this.server = http.createServer((req, res) => { const sendError = (error: string) => { this.events.emit('error', error); - res.writeHead(HttpStatusCode.InternalServerError, { 'Content-Type': 'text/html' }); + res.writeHead(StatusCodes.INTERNAL_SERVER_ERROR, { 'Content-Type': 'text/html' }); res.write(`<html lang="en"><body><h1 style="color: red">DojoCLI login error</h1><h3>Please look at your CLI for more informations.</h3></body></html>`); res.end(); }; @@ -40,7 +40,7 @@ class LoginServer { if ( req.url?.match(Config.login.server.route) ) { const urlParts = req.url.split('='); if ( urlParts.length > 0 ) { - res.writeHead(HttpStatusCode.Ok, { 'Content-Type': 'text/html' }); + res.writeHead(StatusCodes.OK, { 'Content-Type': 'text/html' }); res.write(`<html lang="en"><body><h1 style="color: green">DojoCLI login successful</h1><h3>You can close this window.</h3></body></html>`); res.end(); @@ -116,6 +116,10 @@ class SessionManager { set gitlabCredentials(credentials: DojoGitlabCredentials) { sessionConfigFile.setParam(LocalConfigKeys.GITLAB, credentials); + + if ( credentials.accessToken ) { + GitlabManager.setToken(credentials.accessToken); + } } private async getGitlabCodeFromHeadlessEnvironment(): Promise<string> { @@ -205,7 +209,7 @@ class SessionManager { }).start(); let gitlabTokens: GitlabToken; try { - gitlabTokens = await SharedGitlabManager.getTokens(gitlabCode); + gitlabTokens = await GitlabManager.getTokens(gitlabCode); this.gitlabCredentials = { refreshToken: gitlabTokens.refresh_token, accessToken : gitlabTokens.access_token @@ -307,6 +311,13 @@ class SessionManager { return false; } + if ( checkPermissions && checkPermissions.length === 0 ) { + ora({ + text : `Here is your permissions:`, + indent: 4 + }).start().info(); + } + return this.checkPermissions(verbose, 8, checkPermissions); } } diff --git a/NodeApp/src/shared b/NodeApp/src/shared index 75fedb26c47bb6f707725307a79a45a13e62496d..6e78095b3fe73f2c2987de1a3d3b55511335a2bf 160000 --- a/NodeApp/src/shared +++ b/NodeApp/src/shared @@ -1 +1 @@ -Subproject commit 75fedb26c47bb6f707725307a79a45a13e62496d +Subproject commit 6e78095b3fe73f2c2987de1a3d3b55511335a2bf diff --git a/NodeApp/src/sharedByClients b/NodeApp/src/sharedByClients index c4efbcfb2a50e7108e101fb673e84f87fad8e246..8ae82abcc7f11e595060b1a48f340290e7b2ebac 160000 --- a/NodeApp/src/sharedByClients +++ b/NodeApp/src/sharedByClients @@ -1 +1 @@ -Subproject commit c4efbcfb2a50e7108e101fb673e84f87fad8e246 +Subproject commit 8ae82abcc7f11e595060b1a48f340290e7b2ebac