From bd88e2304c93761ce6754985074f004a5a3c8c4b Mon Sep 17 00:00:00 2001 From: Gloria Ciavarrini Date: Thu, 15 Feb 2024 14:39:31 +0100 Subject: [PATCH] feat(orchestrator): add OpenAPI support (#1123) * Add generate-model to common Signed-off-by: Gloria Ciavarrini * Add openapi spec and /oveview definition Signed-off-by: Gloria Ciavarrini * Add generated models Signed-off-by: Gloria Ciavarrini * Export generated types Signed-off-by: Gloria Ciavarrini * Create openAPIBackend Signed-off-by: Gloria Ciavarrini * Let OpenaAPIBackend handle requests Signed-off-by: Gloria Ciavarrini * Comment api validation code block Due to the simultaneous use of both v1 and v2, validation is temporarily disabled. Enabling it results in an "operation unknown" error. This change ensures smooth operation until v1 is no longer needed. Signed-off-by: Gloria Ciavarrini * Move generated models to auto-generated dir Signed-off-by: Gloria Ciavarrini * Fix openapi.yaml header Signed-off-by: Gloria Ciavarrini * Fix: change folder for exported types Signed-off-by: Gloria Ciavarrini * Fix tsc Signed-off-by: Gloria Ciavarrini * Add generated schema into dist-type dir tsc doens't copy d.ts files. Rename the generated schema to .ts and move it inside src Signed-off-by: Gloria Ciavarrini * Script to include openapi spec at build-time. The sh script derives from notification-plugin[1] [1] https://github.com/janus-idp/backstage-plugins/blob/main/plugins/notifications-backend/scripts/openapi.sh Signed-off-by: Gloria Ciavarrini * Add openapi script in common package.json Signed-off-by: Gloria Ciavarrini * Update backend dist-dynamic Signed-off-by: Gloria Ciavarrini * Generate openapidocument.ts Using `yarn openapi` command Signed-off-by: Gloria Ciavarrini * Export openapidocument.ts Signed-off-by: Gloria Ciavarrini * Implementation of workflow overview endpoints in handlers.ts Signed-off-by: Gloria Ciavarrini * Use openapidocument instead of relative path Signed-off-by: Gloria Ciavarrini * Implement v2 getWorkflowsOverview Signed-off-by: Gloria Ciavarrini * Fix: Functions should not have too many parameters According to SonarCloud "Function 'setupInternalRoutes' has too many parameters (8). Maximum allowed is 7" Signed-off-by: Gloria Ciavarrini * Reorganize openapi files - Rename openapidocument.ts to definition.ts - Move it into autogenerated folder - Moved openapi.yaml into src to create a valid dist-type Signed-off-by: Gloria Ciavarrini * Split handlers into v1 and v2 Organize endpoint implementations based on the API versions for improved maintainability and clarity Signed-off-by: Gloria Ciavarrini * Refactor initialization of OpenAPIBackend Signed-off-by: Gloria Ciavarrini * Remove handler definiion in OpenAPIBackend Signed-off-by: Gloria Ciavarrini --------- Signed-off-by: Gloria Ciavarrini --- .../dist-dynamic/package.json | 1 + .../dist-dynamic/yarn.lock | 280 +++++++++++++----- plugins/orchestrator-backend/package.json | 1 + .../src/service/api/v1.ts | 19 ++ .../src/service/api/v2.ts | 19 ++ .../src/service/router.ts | 168 +++++++---- plugins/orchestrator-common/package.json | 3 +- .../orchestrator-common/scripts/openapi.sh | 17 ++ .../src/auto-generated/api/definition.ts | 128 ++++++++ .../src/auto-generated/api/models/schema.ts | 71 +++++ plugins/orchestrator-common/src/index.ts | 2 + .../src/openapi/openapi.yaml | 82 +++++ .../orchestrator-common/src/openapi/types.ts | 5 + 13 files changed, 653 insertions(+), 143 deletions(-) create mode 100644 plugins/orchestrator-backend/src/service/api/v1.ts create mode 100644 plugins/orchestrator-backend/src/service/api/v2.ts create mode 100755 plugins/orchestrator-common/scripts/openapi.sh create mode 100644 plugins/orchestrator-common/src/auto-generated/api/definition.ts create mode 100644 plugins/orchestrator-common/src/auto-generated/api/models/schema.ts create mode 100644 plugins/orchestrator-common/src/openapi/openapi.yaml create mode 100644 plugins/orchestrator-common/src/openapi/types.ts diff --git a/plugins/orchestrator-backend/dist-dynamic/package.json b/plugins/orchestrator-backend/dist-dynamic/package.json index cb1a0d2e01..f84787285f 100644 --- a/plugins/orchestrator-backend/dist-dynamic/package.json +++ b/plugins/orchestrator-backend/dist-dynamic/package.json @@ -58,6 +58,7 @@ "express-promise-router": "^4.1.1", "fs-extra": "^10.1.0", "json-schema": "^0.4.0", + "openapi-backend": "^5.10.5", "openapi-types": "^12.1.3", "winston": "^3.11.0", "yn": "^5.0.0", diff --git a/plugins/orchestrator-backend/dist-dynamic/yarn.lock b/plugins/orchestrator-backend/dist-dynamic/yarn.lock index 5d891476e8..98513b0ced 100644 --- a/plugins/orchestrator-backend/dist-dynamic/yarn.lock +++ b/plugins/orchestrator-backend/dist-dynamic/yarn.lock @@ -7,12 +7,23 @@ resolved "https://registry.yarnpkg.com/@0no-co/graphql.web/-/graphql.web-1.0.4.tgz#9606eb651955499525d068ce0ad8bea596286ce2" integrity sha512-W3ezhHGfO0MS1PtGloaTpg0PbaT8aZSmmaerL7idtU5F7oCI+uu25k+MsMS31BVFlp4aMkHSrNRxiD72IlK8TA== +"@apidevtools/json-schema-ref-parser@^11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.1.0.tgz#0608ed1ba47c377c6732e7185f2ea06731b58fde" + integrity sha512-g/VW9ZQEFJAOwAyUb8JFf7MLiLy2uEB4rU270rGzDwICxnxMlPy0O11KVePSgS36K1NI29gSlK84n5INGhd4Ag== + dependencies: + "@jsdevtools/ono" "^7.1.3" + "@types/json-schema" "^7.0.13" + "@types/lodash.clonedeep" "^4.5.7" + js-yaml "^4.1.0" + lodash.clonedeep "^4.5.0" + "@aws-sdk/util-utf8-browser@npm:@smithy/util-utf8@~2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.0.2.tgz#626b3e173ad137208e27ed329d6bea70f4a1a7f7" - integrity sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA== + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.1.1.tgz#690018dd28f47f014114497735e51417ea5900a6" + integrity sha512-BqTpzYEcUMDwAKr7/mVRUtHDhs6ZoXDi9NypMvMfOr/+u1NW7JgqodPDECiiLboEm6bobcPcECxzjtQh865e9A== dependencies: - "@smithy/util-buffer-from" "^2.0.0" + "@smithy/util-buffer-from" "^2.1.1" tslib "^2.5.0" "@colors/colors@1.6.0", "@colors/colors@^1.6.0": @@ -29,6 +40,11 @@ enabled "2.0.x" kuler "^2.0.0" +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + "@octokit/auth-token@^3.0.0": version "3.0.4" resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.4.tgz#70e941ba742bdd2b49bdb7393e821dea8520a3db" @@ -148,30 +164,47 @@ ajv "^8.1.0" js-yaml "^4.1.0" -"@smithy/is-array-buffer@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz#8fa9b8040651e7ba0b2f6106e636a91354ff7d34" - integrity sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug== +"@smithy/is-array-buffer@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.1.1.tgz#07b4c77ae67ed58a84400c76edd482271f9f957b" + integrity sha512-xozSQrcUinPpNPNPds4S7z/FakDTh1MZWtRP/2vQtYB/u3HYrX2UXuZs+VhaKBd6Vc7g2XPr2ZtwGBNDN6fNKQ== dependencies: tslib "^2.5.0" -"@smithy/util-buffer-from@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz#7eb75d72288b6b3001bc5f75b48b711513091deb" - integrity sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw== +"@smithy/util-buffer-from@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.1.1.tgz#f9346bf8b23c5ba6f6bdb61dd9db779441ba8d08" + integrity sha512-clhNjbyfqIv9Md2Mg6FffGVrJxw7bgK7s3Iax36xnfVj6cg0fUG7I4RH0XgXJF8bxi+saY5HR21g2UPKSxVCXg== dependencies: - "@smithy/is-array-buffer" "^2.0.0" + "@smithy/is-array-buffer" "^2.1.1" tslib "^2.5.0" +"@types/json-schema@^7.0.13": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/lodash.clonedeep@^4.5.7": + version "4.5.9" + resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz#ea48276c7cc18d080e00bb56cf965bcceb3f0fc1" + integrity sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.202" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.202.tgz#f09dbd2fb082d507178b2f2a5c7e74bd72ff98f8" + integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ== + "@types/triple-beam@^1.3.2": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== "@urql/core@^4.1.4": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@urql/core/-/core-4.2.2.tgz#c2b009373cb9084bbfa8ebc0177c854a76235b84" - integrity sha512-TP1kheq9bnrEdnVbJqh0g0ZY/wfdpPeAzjiiDK+Tm+Pbi0O1Xdu6+fUJ/wJo5QpHZzkIyya4/AecG63e6scFqQ== + version "4.2.3" + resolved "https://registry.yarnpkg.com/@urql/core/-/core-4.2.3.tgz#f956e8a33c4bd055c0c5151491843dd46d737f0f" + integrity sha512-DJ9q9+lcs5JL8DcU2J3NqsgeXYJva+1+Qt8HU94kzTPqVOIRRA7ouvy4ksUfPY+B5G2PQ+vLh+JJGyZCNXv0cg== dependencies: "@0no-co/graphql.web" "^1.0.1" wonka "^6.3.2" @@ -184,14 +217,14 @@ accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -ajv-formats@^2.1.1: +ajv-formats@^2.0.2, ajv-formats@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== dependencies: ajv "^8.0.0" -ajv@^8.0.0, ajv@^8.1.0, ajv@^8.11.0: +ajv@^8.0.0, ajv@^8.1.0, ajv@^8.11.0, ajv@^8.6.2: version "8.12.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== @@ -216,10 +249,15 @@ async@^3.2.3: resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== -available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +available-typed-arrays@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz#ac812d8ce5a6b976d738e1c45f08d0b00bc7d725" + integrity sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg== + +bath-es5@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/bath-es5/-/bath-es5-3.0.3.tgz#4e2808e8b33b4a5e3328ec1e9032f370f042193d" + integrity sha512-PdCioDToH3t84lP40kUFCKWCOCH389Dl1kbC8FGoqOwamxsmqxxnJSXdkTOsPoNHXjem4+sJ+bbNoQm5zeCqxg== before-after-hook@^2.2.0: version "2.2.3" @@ -254,14 +292,16 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" - integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6: + 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.1" - set-function-length "^1.1.1" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" cloudevents@^8.0.0: version "8.0.0" @@ -333,7 +373,7 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.5.0: +cookie@0.5.0, cookie@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== @@ -345,14 +385,14 @@ debug@2.6.9: dependencies: ms "2.0.0" -define-data-property@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" - integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== +define-data-property@^1.1.2: + 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: - get-intrinsic "^1.2.1" + es-define-property "^1.0.0" + es-errors "^1.3.0" gopd "^1.0.1" - has-property-descriptors "^1.0.0" depd@2.0.0: version "2.0.0" @@ -364,6 +404,11 @@ deprecation@^2.0.0: resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== +dereference-json-schema@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/dereference-json-schema/-/dereference-json-schema-0.2.1.tgz#fcad3c98e0116f7124b0989d39d947fa318cae09" + integrity sha512-uzJsrg225owJyRQ8FNTPHIuBOdSzIZlHhss9u6W8mp7jJldHqGuLv9cULagP/E26QVJDnjtG8U7Dw139mM1ydA== + destroy@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" @@ -384,6 +429,18 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== +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== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -499,11 +556,12 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" - integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== +get-intrinsic@^1.1.3, get-intrinsic@^1.2.3, 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" @@ -521,34 +579,34 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -has-property-descriptors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" - integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== +has-property-descriptors@^1.0.1: + 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: - get-intrinsic "^1.2.2" + 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" integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== -has-symbols@^1.0.2, has-symbols@^1.0.3: +has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== +has-tostringtag@^1.0.0, has-tostringtag@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: - has-symbols "^1.0.2" + has-symbols "^1.0.3" hasown@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" - integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + version "2.0.1" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.1.tgz#26f48f039de2c0f8d3356c223fb8d50253519faa" + integrity sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA== dependencies: function-bind "^1.1.2" @@ -621,11 +679,11 @@ is-stream@^2.0.0: integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-typed-array@^1.1.3: - version "1.1.12" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" - integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== dependencies: - which-typed-array "^1.1.11" + which-typed-array "^1.1.14" js-yaml@^4.1.0: version "4.1.0" @@ -665,11 +723,26 @@ kuler@^2.0.0: resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + lodash.flattendeep@^4.0.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" integrity sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ== +lodash.merge@^4.6.1: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash@^4.17.15, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + logform@^2.3.2, logform@^2.4.0: version "2.6.0" resolved "https://registry.yarnpkg.com/logform/-/logform-2.6.0.tgz#8c82a983f05d6eaeb2d75e3decae7a768b2bf9b5" @@ -714,6 +787,13 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mock-json-schema@^1.0.7: + version "1.1.1" + resolved "https://registry.yarnpkg.com/mock-json-schema/-/mock-json-schema-1.1.1.tgz#35cf35ae16e519986ff83c22b4a886a8fe5b9ba5" + integrity sha512-YV23vlsLP1EEOy0EviUvZTluXjLR+rhMzeayP2rcDiezj3RW01MhOSQkbQskdtg0K2fnGas5LKbSXgNjAOSX4A== + dependencies: + lodash "^4.17.21" + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -736,7 +816,7 @@ node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" -object-inspect@^1.9.0: +object-inspect@^1.13.1: version "1.13.1" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== @@ -762,7 +842,33 @@ one-time@^1.0.0: dependencies: fn.name "1.x.x" -openapi-types@^12.1.3: +openapi-backend@^5.10.5: + version "5.10.6" + resolved "https://registry.yarnpkg.com/openapi-backend/-/openapi-backend-5.10.6.tgz#e719be85d006a1379900d0ff5c8f60baecb5de21" + integrity sha512-vTjBRys/O4JIHdlRHUKZ7pxS+gwIJreAAU9dvYRFrImtPzQ5qxm5a6B8BTVT9m6I8RGGsShJv35MAc3Tu2/y/A== + dependencies: + "@apidevtools/json-schema-ref-parser" "^11.1.0" + ajv "^8.6.2" + bath-es5 "^3.0.3" + cookie "^0.5.0" + dereference-json-schema "^0.2.1" + lodash "^4.17.15" + mock-json-schema "^1.0.7" + openapi-schema-validator "^12.0.0" + openapi-types "^12.0.2" + qs "^6.9.3" + +openapi-schema-validator@^12.0.0: + version "12.1.3" + resolved "https://registry.yarnpkg.com/openapi-schema-validator/-/openapi-schema-validator-12.1.3.tgz#c9234af67b00cdbbecfdd4eb546d7006bacfe518" + integrity sha512-xTHOmxU/VQGUgo7Cm0jhwbklOKobXby+/237EG967+3TQEYJztMgX9Q5UE2taZKwyKPUq0j11dngpGjUuxz1hQ== + dependencies: + ajv "^8.1.0" + ajv-formats "^2.0.2" + lodash.merge "^4.6.1" + openapi-types "^12.1.3" + +openapi-types@^12.0.2, openapi-types@^12.1.3: version "12.1.3" resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-12.1.3.tgz#471995eb26c4b97b7bd356aacf7b91b73e777dd3" integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw== @@ -802,6 +908,13 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +qs@^6.9.3: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" @@ -875,15 +988,17 @@ serve-static@1.15.0: parseurl "~1.3.3" send "0.18.0" -set-function-length@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" - integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== +set-function-length@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.1.tgz#47cc5945f2c771e2cf261c6737cf9684a2a5e425" + integrity sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g== dependencies: - define-data-property "^1.1.1" - get-intrinsic "^1.2.1" + define-data-property "^1.1.2" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.3" gopd "^1.0.1" - has-property-descriptors "^1.0.0" + has-property-descriptors "^1.0.1" setprototypeof@1.2.0: version "1.2.0" @@ -891,13 +1006,14 @@ setprototypeof@1.2.0: integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + version "1.0.5" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.5.tgz#9a84546599b48909fb6af1211708d23b1946221b" + integrity sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ== dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" + call-bind "^1.0.6" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" simple-swizzle@^0.2.2: version "0.2.2" @@ -1022,21 +1138,21 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -which-typed-array@^1.1.11, which-typed-array@^1.1.2: - version "1.1.13" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" - integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== +which-typed-array@^1.1.14, which-typed-array@^1.1.2: + version "1.1.14" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.14.tgz#1f78a111aee1e131ca66164d8bdc3ab062c95a06" + integrity sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.4" + available-typed-arrays "^1.0.6" + call-bind "^1.0.5" for-each "^0.3.3" gopd "^1.0.1" - has-tostringtag "^1.0.0" + has-tostringtag "^1.0.1" winston-transport@^4.5.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.6.0.tgz#f1c1a665ad1b366df72199e27892721832a19e1b" - integrity sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg== + version "4.7.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.7.0.tgz#e302e6889e6ccb7f383b926df6936a5b781bd1f0" + integrity sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg== dependencies: logform "^2.3.2" readable-stream "^3.6.0" diff --git a/plugins/orchestrator-backend/package.json b/plugins/orchestrator-backend/package.json index 07b639cfb4..dee9f2d0a3 100644 --- a/plugins/orchestrator-backend/package.json +++ b/plugins/orchestrator-backend/package.json @@ -82,6 +82,7 @@ "express-promise-router": "^4.1.1", "fs-extra": "^10.1.0", "json-schema": "^0.4.0", + "openapi-backend": "^5.10.5", "openapi-types": "^12.1.3", "winston": "^3.11.0", "yn": "^5.0.0" diff --git a/plugins/orchestrator-backend/src/service/api/v1.ts b/plugins/orchestrator-backend/src/service/api/v1.ts new file mode 100644 index 0000000000..21c85ddfca --- /dev/null +++ b/plugins/orchestrator-backend/src/service/api/v1.ts @@ -0,0 +1,19 @@ +import { WorkflowOverviewListResult } from '@janus-idp/backstage-plugin-orchestrator-common'; + +import { SonataFlowService } from '../SonataFlowService'; + +export async function getWorkflowOverviewV1( + sonataFlowService: SonataFlowService, +): Promise { + const overviews = await sonataFlowService.fetchWorkflowOverviews(); + if (!overviews) { + throw new Error("Couldn't fetch workflow overviews"); + } + const result: WorkflowOverviewListResult = { + items: overviews, + limit: 0, + offset: 0, + totalCount: overviews?.length ?? 0, + }; + return result; +} diff --git a/plugins/orchestrator-backend/src/service/api/v2.ts b/plugins/orchestrator-backend/src/service/api/v2.ts new file mode 100644 index 0000000000..5149165d6a --- /dev/null +++ b/plugins/orchestrator-backend/src/service/api/v2.ts @@ -0,0 +1,19 @@ +import { WorkflowOverviewListResultDTO } from '@janus-idp/backstage-plugin-orchestrator-common'; + +import { SonataFlowService } from '../SonataFlowService'; +import { getWorkflowOverviewV1 } from './v1'; + +export async function getWorkflowOverviewV2( + sonataFlowService: SonataFlowService, +): Promise { + const overviewsV1 = await getWorkflowOverviewV1(sonataFlowService); + const result: WorkflowOverviewListResultDTO = { + overviews: overviewsV1.items, + paginationInfo: { + limit: 0, + offset: 0, + totalCount: overviewsV1.items?.length ?? 0, + }, + }; + return result; +} diff --git a/plugins/orchestrator-backend/src/service/router.ts b/plugins/orchestrator-backend/src/service/router.ts index fc5082e2f6..ae5671792a 100644 --- a/plugins/orchestrator-backend/src/service/router.ts +++ b/plugins/orchestrator-backend/src/service/router.ts @@ -5,10 +5,12 @@ import { JsonObject, JsonValue } from '@backstage/types'; import express from 'express'; import Router from 'express-promise-router'; +import { OpenAPIBackend, Request } from 'openapi-backend'; import { AssessedProcessInstance, fromWorkflowSource, + openApiDocument, ORCHESTRATOR_SERVICE_READY_TOPIC, ProcessInstance, QUERY_PARAM_ASSESSMENT_INSTANCE_ID, @@ -21,11 +23,12 @@ import { WorkflowInfo, WorkflowItem, WorkflowListResult, - WorkflowOverviewListResult, } from '@janus-idp/backstage-plugin-orchestrator-common'; import { RouterArgs } from '../routerWrapper'; import { ApiResponseBuilder } from '../types/apiResponse'; +import { getWorkflowOverviewV1 } from './api/v1'; +import { getWorkflowOverviewV2 } from './api/v2'; import { CloudEventService } from './CloudEventService'; import { WORKFLOW_DATA_KEY } from './constants'; import { DataIndexService } from './DataIndexService'; @@ -36,6 +39,14 @@ import { ScaffolderService } from './ScaffolderService'; import { SonataFlowService } from './SonataFlowService'; import { WorkflowService } from './WorkflowService'; +interface Services { + sonataFlowService: SonataFlowService; + workflowService: WorkflowService; + openApiService: OpenApiService; + jiraService: JiraService; + dataIndexService: DataIndexService; + dataInputSchemaService: DataInputSchemaService; +} export async function createBackendRouter( args: RouterArgs & { sonataFlowService: SonataFlowService; @@ -45,6 +56,9 @@ export async function createBackendRouter( const { eventBroker, config, logger, discovery, catalogApi, urlReader } = args; + const api = initOpenAPIBackend(); + await api.init(); + const router = Router(); router.use(express.json()); router.use('/workflows', express.text()); @@ -91,15 +105,16 @@ export async function createBackendRouter( await workflowService.reloadWorkflows(); - setupInternalRoutes( - router, - args.sonataFlowService, + const services: Services = { + sonataFlowService: args.sonataFlowService, workflowService, openApiService, jiraService, - args.dataIndexService, + dataIndexService: args.dataIndexService, dataInputSchemaService, - ); + }; + + setupInternalRoutes(router, api, services); setupExternalRoutes(router, discovery, scaffolderService); await eventBroker.publish({ @@ -107,50 +122,77 @@ export async function createBackendRouter( eventPayload: {}, }); + router.use((req, res, next) => { + if (!next) { + throw new Error('next is undefined'); + } + + // const validation = api.validateRequest(req as Request); + // if (!validation.valid) { + // console.log('errors: ', validation.errors); + // throw validation.errors; + // } + + api.handleRequest(req as Request, req, res, next); + }); + router.use(errorHandler()); return router; } +function initOpenAPIBackend(): OpenAPIBackend { + return new OpenAPIBackend({ + definition: openApiDocument, + strict: false, + ajvOpts: { + strict: false, + strictSchema: false, + verbose: true, + addUsedSchema: false, + }, + }); +} + // ====================================================== // Internal Backstage API calls to delegate to SonataFlow // ====================================================== function setupInternalRoutes( router: express.Router, - sonataFlowService: SonataFlowService, - workflowService: WorkflowService, - openApiService: OpenApiService, - jiraService: JiraService, - dataIndexService: DataIndexService, - dataInputSchemaService: DataInputSchemaService, + api: OpenAPIBackend, + services: Services, ) { router.get('/workflows/definitions', async (_, response) => { - const swfs = await dataIndexService.getWorkflowDefinitions(); + const swfs = await services.dataIndexService.getWorkflowDefinitions(); response.json(ApiResponseBuilder.SUCCESS_RESPONSE(swfs)); }); - router.get('/workflows/overview', async (_, res) => { - const overviews = await sonataFlowService.fetchWorkflowOverviews(); - - if (!overviews) { - res.status(500).send("Couldn't fetch workflow overviews"); - return; - } - - const result: WorkflowOverviewListResult = { - items: overviews, - limit: 0, - offset: 0, - totalCount: overviews?.length ?? 0, - }; - res.status(200).json(result); + router.get('/workflows/overview', async (_c, res) => { + await getWorkflowOverviewV1(services.sonataFlowService) + .then(result => res.status(200).json(result)) + .catch(error => { + res.status(500).send(error.message || 'Internal Server Error'); + }); }); + // v2 + api.register( + 'getWorkflowsOverview', + async (_c, _req, res: express.Response, next) => { + await getWorkflowOverviewV2(services.sonataFlowService) + .then(result => res.json(result)) + .catch(error => { + res.status(500).send(error.message || 'Internal Server Error'); + next(); + }); + }, + ); + router.get('/workflows', async (_, res) => { const definitions: WorkflowInfo[] = - await dataIndexService.getWorkflowDefinitions(); + await services.dataIndexService.getWorkflowDefinitions(); const items: WorkflowItem[] = await Promise.all( definitions.map(async info => { - const uri = await sonataFlowService.fetchWorkflowUri(info.id); + const uri = await services.sonataFlowService.fetchWorkflowUri(info.id); if (!uri) { throw new Error(`Uri is required for workflow ${info.id}`); } @@ -183,7 +225,7 @@ function setupInternalRoutes( } = req; const definition = - await sonataFlowService.fetchWorkflowDefinition(workflowId); + await services.sonataFlowService.fetchWorkflowDefinition(workflowId); if (!definition) { res @@ -192,7 +234,7 @@ function setupInternalRoutes( return; } - const uri = await sonataFlowService.fetchWorkflowUri(workflowId); + const uri = await services.sonataFlowService.fetchWorkflowUri(workflowId); if (!uri) { res.status(500).send(`Couldn't fetch workflow uri for ${workflowId}`); return; @@ -209,7 +251,8 @@ function setupInternalRoutes( params: { workflowId }, } = req; - const result = await dataIndexService.abortWorkflowInstance(workflowId); + const result = + await services.dataIndexService.abortWorkflowInstance(workflowId); if (result.error) { res.status(500).json(result.error); @@ -226,12 +269,13 @@ function setupInternalRoutes( const businessKey = extractQueryParam(req, QUERY_PARAM_BUSINESS_KEY); - const definition = await dataIndexService.getWorkflowDefinition(workflowId); + const definition = + await services.dataIndexService.getWorkflowDefinition(workflowId); const serviceUrl = definition.serviceUrl; if (!serviceUrl) { throw new Error(`ServiceURL is not defined for workflow ${workflowId}`); } - const executionResponse = await sonataFlowService.executeWorkflow({ + const executionResponse = await services.sonataFlowService.executeWorkflow({ workflowId, inputData: req.body, endpoint: serviceUrl, @@ -251,7 +295,7 @@ function setupInternalRoutes( params: { workflowId }, } = req; const overviewObj = - await sonataFlowService.fetchWorkflowOverview(workflowId); + await services.sonataFlowService.fetchWorkflowOverview(workflowId); if (!overviewObj) { res @@ -263,7 +307,7 @@ function setupInternalRoutes( }); router.get('/instances', async (_, res) => { - const instances = await dataIndexService.fetchProcessInstances(); + const instances = await services.dataIndexService.fetchProcessInstances(); if (!instances) { res.status(500).send("Couldn't fetch process instances"); @@ -283,7 +327,8 @@ function setupInternalRoutes( QUERY_PARAM_INCLUDE_ASSESSMENT, ); - const instance = await dataIndexService.fetchProcessInstance(instanceId); + const instance = + await services.dataIndexService.fetchProcessInstance(instanceId); if (!instance) { res.status(500).send(`Couldn't fetch process instance ${instanceId}`); @@ -293,7 +338,7 @@ function setupInternalRoutes( let assessedByInstance: ProcessInstance | undefined; if (!!includeAssessment && instance.businessKey) { - assessedByInstance = await dataIndexService.fetchProcessInstance( + assessedByInstance = await services.dataIndexService.fetchProcessInstance( instance.businessKey, ); } @@ -311,7 +356,8 @@ function setupInternalRoutes( params: { instanceId }, } = req; - const jobs = await dataIndexService.fetchProcessInstanceJobs(instanceId); + const jobs = + await services.dataIndexService.fetchProcessInstanceJobs(instanceId); if (!jobs) { res.status(500).send(`Couldn't fetch jobs for instance ${instanceId}`); @@ -333,21 +379,22 @@ function setupInternalRoutes( ); const workflowDefinition = - await dataIndexService.getWorkflowDefinition(workflowId); + await services.dataIndexService.getWorkflowDefinition(workflowId); const serviceUrl = workflowDefinition.serviceUrl; if (!serviceUrl) { throw new Error(`ServiceUrl is not defined for workflow ${workflowId}`); } + // workflow source const definition = - await sonataFlowService.fetchWorkflowDefinition(workflowId); + await services.sonataFlowService.fetchWorkflowDefinition(workflowId); if (!definition) { res.status(500).send(`Couldn't fetch workflow definition ${workflowId}`); return; } - const uri = await sonataFlowService.fetchWorkflowUri(workflowId); + const uri = await services.sonataFlowService.fetchWorkflowUri(workflowId); if (!uri) { res.status(500).send(`Couldn't fetch workflow uri ${workflowId}`); @@ -370,7 +417,7 @@ function setupInternalRoutes( return; } - const workflowInfo = await sonataFlowService.fetchWorkflowInfo( + const workflowInfo = await services.sonataFlowService.fetchWorkflowInfo( workflowId, serviceUrl, ); @@ -387,12 +434,14 @@ function setupInternalRoutes( return; } - const schemas = dataInputSchemaService.parseComposition( + const schemas = services.dataInputSchemaService.parseComposition( workflowInfo.inputSchema, ); const instanceVariables = instanceId - ? await dataIndexService.fetchProcessInstanceVariables(instanceId) + ? await services.dataIndexService.fetchProcessInstanceVariables( + instanceId, + ) : undefined; const instanceWorkflowData = instanceVariables?.[WORKFLOW_DATA_KEY]; @@ -400,16 +449,15 @@ function setupInternalRoutes( let readonlyKeys: string[] = []; if (instanceWorkflowData) { - initialState = dataInputSchemaService.extractInitialStateFromWorkflowData( - { + initialState = + services.dataInputSchemaService.extractInitialStateFromWorkflowData({ workflowData: instanceWorkflowData as JsonObject, schemas, - }, - ); + }); } const assessmentInstanceVariables = assessmentInstanceId - ? await dataIndexService.fetchProcessInstanceVariables( + ? await services.dataIndexService.fetchProcessInstanceVariables( assessmentInstanceId, ) : undefined; @@ -419,7 +467,7 @@ function setupInternalRoutes( if (assessmentInstanceWorkflowData) { const assessmentInstanceInitialState = - dataInputSchemaService.extractInitialStateFromWorkflowData({ + services.dataInputSchemaService.extractInitialStateFromWorkflowData({ workflowData: assessmentInstanceWorkflowData as JsonObject, schemas, }); @@ -444,14 +492,14 @@ function setupInternalRoutes( router.delete('/workflows/:workflowId', async (req, res) => { const workflowId = req.params.workflowId; - const uri = await sonataFlowService.fetchWorkflowUri(workflowId); + const uri = await services.sonataFlowService.fetchWorkflowUri(workflowId); if (!uri) { res.status(500).send(`Couldn't fetch workflow uri ${workflowId}`); return; } - await workflowService.deleteWorkflowDefinitionById(uri); + await services.workflowService.deleteWorkflowDefinitionById(uri); res.status(200).send(); }); @@ -464,8 +512,8 @@ function setupInternalRoutes( } const workflowItem = uri?.startsWith('http') - ? await workflowService.saveWorkflowDefinitionFromUrl(uri) - : await workflowService.saveWorkflowDefinition({ + ? await services.workflowService.saveWorkflowDefinitionFromUrl(uri) + : await services.workflowService.saveWorkflowDefinition({ uri, definition: fromWorkflowSource(req.body), }); @@ -473,23 +521,23 @@ function setupInternalRoutes( }); router.get('/actions/schema', async (_, res) => { - const openApi = await openApiService.generateOpenApi(); + const openApi = await services.openApiService.generateOpenApi(); res.json(openApi).status(200).send(); }); router.put('/actions/schema', async (_, res) => { - const openApi = await workflowService.saveOpenApi(); + const openApi = await services.workflowService.saveOpenApi(); res.json(openApi).status(200).send(); }); router.post('/webhook/jira', async (req, res) => { const event = req.body as JiraEvent; - await jiraService.handleEvent(event); + await services.jiraService.handleEvent(event); res.status(200).send(); }); router.get('/specs', async (_, res) => { - const specs = await workflowService.listStoredSpecs(); + const specs = await services.workflowService.listStoredSpecs(); res.status(200).json(specs); }); } diff --git a/plugins/orchestrator-common/package.json b/plugins/orchestrator-common/package.json index f2cbeb149e..8de80c4248 100644 --- a/plugins/orchestrator-common/package.json +++ b/plugins/orchestrator-common/package.json @@ -34,7 +34,8 @@ "test": "backstage-cli package test --passWithNoTests --coverage", "clean": "backstage-cli package clean", "prepack": "backstage-cli package prepack", - "postpack": "backstage-cli package postpack" + "postpack": "backstage-cli package postpack", + "openapi": "./scripts/openapi.sh" }, "dependencies": { "@backstage/types": "^1.1.1", diff --git a/plugins/orchestrator-common/scripts/openapi.sh b/plugins/orchestrator-common/scripts/openapi.sh new file mode 100755 index 0000000000..31a558936a --- /dev/null +++ b/plugins/orchestrator-common/scripts/openapi.sh @@ -0,0 +1,17 @@ +#!/bin/bash +pwd +set -ex + +# npx openapi typegen ./api/openapi.yaml > src/openapi/openapi.d.ts +npx openapi-typescript ./src/openapi/openapi.yaml -o ./src/auto-generated/api/models/schema.ts + +npx yaml2json -f ./src/openapi/openapi.yaml + +export FILE=./src/auto-generated/api/definition.ts +echo '// GENERATED FILE. DO NOT EDIT.' > ${FILE} +echo 'const OPENAPI = `' >> ${FILE} +cat ./src/openapi/openapi.json >> ${FILE} +echo '`' >> ${FILE} +echo "export const openApiDocument = JSON.parse(OPENAPI);" >> ${FILE} + +rm ./src/openapi/openapi.json \ No newline at end of file diff --git a/plugins/orchestrator-common/src/auto-generated/api/definition.ts b/plugins/orchestrator-common/src/auto-generated/api/definition.ts new file mode 100644 index 0000000000..055a4b23f2 --- /dev/null +++ b/plugins/orchestrator-common/src/auto-generated/api/definition.ts @@ -0,0 +1,128 @@ +// GENERATED FILE. DO NOT EDIT. +const OPENAPI = ` +{ + "openapi": "3.1.0", + "info": { + "title": "Orchestrator plugin", + "description": "API to interact with orchestrator plugin", + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "0.0.1" + }, + "servers": [ + { + "url": "/" + } + ], + "paths": { + "/v2/workflows/overview": { + "get": { + "operationId": "getWorkflowsOverview", + "description": "Get a list of workflow overviews", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkflowOverviewListResultDTO" + } + } + } + }, + "500": { + "description": "Error fetching workflow overviews", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error message" + } + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "WorkflowOverviewListResultDTO": { + "type": "object", + "properties": { + "overviews": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WorkflowOverviewDTO" + } + }, + "paginationInfo": { + "$ref": "#/components/schemas/PaginationInfoDTO" + } + } + }, + "WorkflowOverviewDTO": { + "type": "object", + "properties": { + "workflowId": { + "type": "string", + "description": "Workflow unique identifier", + "minLength": 1 + }, + "name": { + "type": "string", + "description": "Workflow name", + "minLength": 1 + }, + "uri": { + "type": "string" + }, + "lastTriggeredMs": { + "type": "number", + "minimum": 0 + }, + "lastRunStatus": { + "type": "string" + }, + "type": { + "type": "string", + "minimum": 0 + }, + "avgDurationMs": { + "type": "number", + "minimum": 0 + }, + "description": { + "type": "string" + } + } + }, + "PaginationInfoDTO": { + "type": "object", + "properties": { + "limit": { + "type": "number", + "minimum": 0 + }, + "offset": { + "type": "number", + "minimum": 0 + }, + "totalCount": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false + } + } + } +}`; +export const openApiDocument = JSON.parse(OPENAPI); diff --git a/plugins/orchestrator-common/src/auto-generated/api/models/schema.ts b/plugins/orchestrator-common/src/auto-generated/api/models/schema.ts new file mode 100644 index 0000000000..0c5679a293 --- /dev/null +++ b/plugins/orchestrator-common/src/auto-generated/api/models/schema.ts @@ -0,0 +1,71 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + '/v2/workflows/overview': { + /** @description Get a list of workflow overviews */ + get: operations['getWorkflowsOverview']; + }; +} + +export type webhooks = Record; + +export interface components { + schemas: { + WorkflowOverviewListResultDTO: { + overviews?: components['schemas']['WorkflowOverviewDTO'][]; + paginationInfo?: components['schemas']['PaginationInfoDTO']; + }; + WorkflowOverviewDTO: { + /** @description Workflow unique identifier */ + workflowId?: string; + /** @description Workflow name */ + name?: string; + uri?: string; + lastTriggeredMs?: number; + lastRunStatus?: string; + type?: string; + avgDurationMs?: number; + description?: string; + }; + PaginationInfoDTO: { + limit?: number; + offset?: number; + totalCount?: number; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} + +export type $defs = Record; + +export type external = Record; + +export interface operations { + /** @description Get a list of workflow overviews */ + getWorkflowsOverview: { + responses: { + /** @description Success */ + 200: { + content: { + 'application/json': components['schemas']['WorkflowOverviewListResultDTO']; + }; + }; + /** @description Error fetching workflow overviews */ + 500: { + content: { + 'application/json': { + /** @description Error message */ + message?: string; + }; + }; + }; + }; + }; +} diff --git a/plugins/orchestrator-common/src/index.ts b/plugins/orchestrator-common/src/index.ts index e667ac1072..89c3761578 100644 --- a/plugins/orchestrator-common/src/index.ts +++ b/plugins/orchestrator-common/src/index.ts @@ -1,4 +1,6 @@ export * from './types'; +export * from './openapi/types'; +export * from './auto-generated/api/definition'; export * from './constants'; export * from './models'; export * from './workflow'; diff --git a/plugins/orchestrator-common/src/openapi/openapi.yaml b/plugins/orchestrator-common/src/openapi/openapi.yaml new file mode 100644 index 0000000000..b5c3517323 --- /dev/null +++ b/plugins/orchestrator-common/src/openapi/openapi.yaml @@ -0,0 +1,82 @@ +openapi: 3.1.0 +info: + title: Orchestrator plugin + description: API to interact with orchestrator plugin + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + version: 0.0.1 +servers: + - url: / +paths: + /v2/workflows/overview: + get: + operationId: getWorkflowsOverview + description: Get a list of workflow overviews + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowOverviewListResultDTO' + '500': + description: Error fetching workflow overviews + content: + application/json: + schema: + type: object + properties: + message: + type: string + description: Error message +components: + schemas: + WorkflowOverviewListResultDTO: + type: object + properties: + overviews: + type: array + items: + $ref: '#/components/schemas/WorkflowOverviewDTO' + paginationInfo: + $ref: '#/components/schemas/PaginationInfoDTO' + WorkflowOverviewDTO: + type: object + properties: + workflowId: + type: string + description: Workflow unique identifier + minLength: 1 + name: + type: string + description: Workflow name + minLength: 1 + uri: + type: string + lastTriggeredMs: + type: number + minimum: 0 + lastRunStatus: + type: string + type: + type: string + minimum: 0 + avgDurationMs: + type: number + minimum: 0 + description: + type: string + PaginationInfoDTO: + type: object + properties: + limit: + type: number + minimum: 0 + offset: + type: number + minimum: 0 + totalCount: + type: number + minimum: 0 + additionalProperties: false diff --git a/plugins/orchestrator-common/src/openapi/types.ts b/plugins/orchestrator-common/src/openapi/types.ts new file mode 100644 index 0000000000..d51aebd49d --- /dev/null +++ b/plugins/orchestrator-common/src/openapi/types.ts @@ -0,0 +1,5 @@ +import { components } from '../auto-generated/api/models/schema'; + +export type WorkflowOverviewListResultDTO = + components['schemas']['WorkflowOverviewListResultDTO']; +export type WorkflowOverviewDTO = components['schemas']['WorkflowOverviewDTO'];