diff --git a/.changeset/stale-experts-share.md b/.changeset/stale-experts-share.md new file mode 100644 index 0000000000..0d65d34109 --- /dev/null +++ b/.changeset/stale-experts-share.md @@ -0,0 +1,8 @@ +--- +"@janus-idp/backstage-plugin-orchestrator-form-react": patch +"@janus-idp/backstage-plugin-orchestrator-form-api": patch +"@janus-idp/backstage-plugin-orchestrator-backend": patch +"@janus-idp/backstage-plugin-orchestrator": patch +--- + +backport all orchestrator commits since release 1.2 diff --git a/.sonarcloud.properties b/.sonarcloud.properties index 0991ed8e95..c2c6d14e56 100644 --- a/.sonarcloud.properties +++ b/.sonarcloud.properties @@ -1,2 +1,2 @@ # comma delimited path of files to exclude from copy/paste duplicate checking -sonar.cpd.exclusions=packages/cli/src/lib/bundler/scalprumConfig.ts \ No newline at end of file +sonar.cpd.exclusions=plugins/orchestrator-common/src/generated/**,packages/cli/src/lib/bundler/scalprumConfig.ts \ No newline at end of file diff --git a/plugins/orchestrator-backend/.eslintignore b/plugins/orchestrator-backend/.eslintignore deleted file mode 100644 index 55289f4a23..0000000000 --- a/plugins/orchestrator-backend/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -dist-dynamic -dist-scalprum diff --git a/plugins/orchestrator-backend/.lintstagedrc.json b/plugins/orchestrator-backend/.lintstagedrc.json deleted file mode 100644 index 14b2263def..0000000000 --- a/plugins/orchestrator-backend/.lintstagedrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "*": "prettier --ignore-unknown --write", - "*.{js,jsx,ts,tsx,mjs,cjs}": "backstage-cli package lint --fix" -} diff --git a/plugins/orchestrator-backend/.prettierignore b/plugins/orchestrator-backend/.prettierignore deleted file mode 100644 index fc8357d99e..0000000000 --- a/plugins/orchestrator-backend/.prettierignore +++ /dev/null @@ -1,12 +0,0 @@ -dist -dist-types -coverage -.vscode -CHANGELOG.md -generated -templates -*.hbs -renovate.json -dist-dynamic -dist-scalprum -playwright-report diff --git a/plugins/orchestrator-backend/.prettierrc.js b/plugins/orchestrator-backend/.prettierrc.js deleted file mode 100644 index 84cbac65b5..0000000000 --- a/plugins/orchestrator-backend/.prettierrc.js +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-check - -/** @type {import("@ianvs/prettier-plugin-sort-imports").PrettierConfig} */ -module.exports = { - ...require('@spotify/prettier-config'), - plugins: ['@ianvs/prettier-plugin-sort-imports'], - importOrder: [ - '^react(.*)$', - '', - '^@backstage/(.*)$', - '', - '', - '', - '^@janus-idp/(.*)$', - '', - '', - '', - '^[.]', - ], -}; diff --git a/plugins/orchestrator-backend/CHANGELOG.md b/plugins/orchestrator-backend/CHANGELOG.md index 0e17b13896..4e96fd4f6a 100644 --- a/plugins/orchestrator-backend/CHANGELOG.md +++ b/plugins/orchestrator-backend/CHANGELOG.md @@ -1,758 +1,720 @@ ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.16.100 +## 4.0.1 -### Dependencies +### Patch Changes + +- 0e6bfd3: feat: update Backstage to the latest version + + Update to Backstage 1.32.5 + +- Updated dependencies [0e6bfd3] + - @janus-idp/backstage-plugin-orchestrator-common@1.23.1 + - @janus-idp/backstage-plugin-audit-log-node@1.7.1 + - @janus-idp/backstage-plugin-rbac-common@1.12.1 + +## 4.0.0 + +### Minor Changes + +- 8244f28: chore(deps): update to backstage 1.32 + +### Patch Changes + +- Updated dependencies [8244f28] + - @janus-idp/backstage-plugin-orchestrator-common@1.23.0 + - @janus-idp/backstage-plugin-audit-log-node@1.7.0 + - @janus-idp/backstage-plugin-rbac-common@1.12.0 + +## 3.0.1 + +### Patch Changes + +- 7342e9b: chore: remove @janus-idp/cli dep and relink local packages + + This update removes `@janus-idp/cli` from all plugins, as it’s no longer necessary. Additionally, packages are now correctly linked with a specified version. + +## 3.0.0 -* **@janus-idp/backstage-plugin-rbac-common:** upgraded to 1.9.0 +### Minor Changes + +- d9551ae: feat(deps): update to backstage 1.31 + +### Patch Changes + +- d9551ae: Change local package references to a `*` +- d9551ae: pin the @janus-idp/cli package +- d9551ae: upgrade to yarn v3 +- d9551ae: Change the export-dynamic script to no longer use any flags and remove the tracking of the dist-dynamic folder +- Updated dependencies [d9551ae] +- Updated dependencies [d9551ae] +- Updated dependencies [d9551ae] + - @janus-idp/backstage-plugin-orchestrator-common@1.22.0 + - @janus-idp/backstage-plugin-rbac-common@1.11.0 + - @janus-idp/backstage-plugin-audit-log-node@1.6.0 + +* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.21.0 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.1 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.20.0 +- **@janus-idp/cli:** upgraded to 1.15.2 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.19.0 ### Dependencies -* **@janus-idp/cli:** upgraded to 1.13.1 +- **@janus-idp/backstage-plugin-audit-log-node:** upgraded to 1.5.1 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.14.0 +- **@janus-idp/cli:** upgraded to 1.15.1 -## @janus-idp/backstage-plugin-orchestrator-backend [1.17.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.17.2...@janus-idp/backstage-plugin-orchestrator-backend@1.17.3) (2024-08-06) +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.18.2 +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.18.1 ### Dependencies -* **@janus-idp/backstage-plugin-rbac-common:** upgraded to 1.8.2 +- **@janus-idp/cli:** upgraded to 1.15.0 -## @janus-idp/backstage-plugin-orchestrator-backend [1.17.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.17.1...@janus-idp/backstage-plugin-orchestrator-backend@1.17.2) (2024-08-05) +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.18.0 +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.17.3 ### Dependencies -* **@janus-idp/backstage-plugin-rbac-common:** upgraded to 1.8.1 +- **@janus-idp/cli:** upgraded to 1.14.0 +- **@janus-idp/backstage-plugin-audit-log-node:** upgraded to 1.5.0 +- **@janus-idp/backstage-plugin-rbac-common:** upgraded to 1.10.0 -## @janus-idp/backstage-plugin-orchestrator-backend [1.17.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.17.0...@janus-idp/backstage-plugin-orchestrator-backend@1.17.1) (2024-08-02) +### Dependencies +- **@janus-idp/cli:** upgraded to 1.13.2 -### Bug Fixes +### Dependencies -* **orchestrator:** remove default pagination on v2 endpoints ([#1983](https://github.com/janus-idp/backstage-plugins/issues/1983)) ([5e30274](https://github.com/janus-idp/backstage-plugins/commit/5e302748a25cbad127122407e5258576054eac3d)) +- **@janus-idp/backstage-plugin-audit-log-node:** upgraded to 1.4.1 +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.17.2 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.13.1 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.17.1 -## @janus-idp/backstage-plugin-orchestrator-backend [1.17.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.16.1...@janus-idp/backstage-plugin-orchestrator-backend@1.17.0) (2024-07-26) +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.17.0 -### Features +### Dependencies -* **deps:** update to backstage 1.29 ([#1900](https://github.com/janus-idp/backstage-plugins/issues/1900)) ([f53677f](https://github.com/janus-idp/backstage-plugins/commit/f53677fb02d6df43a9de98c43a9f101a6db76802)) -* **orchestrator:** use v2 endpoints to retrieve instances ([#1956](https://github.com/janus-idp/backstage-plugins/issues/1956)) ([537502b](https://github.com/janus-idp/backstage-plugins/commit/537502b9d2ac13f2fb3f79188422d2c6e97f41fb)) +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.16.0 +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.2 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.13.0 -* **@janus-idp/backstage-plugin-rbac-common:** upgraded to 1.8.0 +- **@janus-idp/backstage-plugin-rbac-common:** upgraded to 1.9.0 -## @janus-idp/backstage-plugin-orchestrator-backend [1.16.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.16.0...@janus-idp/backstage-plugin-orchestrator-backend@1.16.1) (2024-07-24) +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.1 -### Bug Fixes +### Dependencies -* **deps:** rollback unreleased plugins ([#1951](https://github.com/janus-idp/backstage-plugins/issues/1951)) ([8b77969](https://github.com/janus-idp/backstage-plugins/commit/8b779694f02f8125587296305276b84cdfeeaebe)) +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.0 +### Dependencies +- **@janus-idp/cli:** upgraded to 1.13.1 ### Dependencies -* **@janus-idp/backstage-plugin-rbac-common:** upgraded to 1.7.2 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.14.0 -## @janus-idp/backstage-plugin-orchestrator-backend [1.16.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.15.0...@janus-idp/backstage-plugin-orchestrator-backend@1.16.0) (2024-07-24) +## @janus-idp/backstage-plugin-orchestrator-backend [1.17.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.17.2...@janus-idp/backstage-plugin-orchestrator-backend@1.17.3) (2024-08-06) +### Dependencies -### Features +- **@janus-idp/backstage-plugin-rbac-common:** upgraded to 1.8.2 -* **deps:** update to backstage 1.28 ([#1891](https://github.com/janus-idp/backstage-plugins/issues/1891)) ([1ba1108](https://github.com/janus-idp/backstage-plugins/commit/1ba11088e0de60e90d138944267b83600dc446e5)) -* **orchestrator:** use v2 endpoints to retrieve workflow overviews ([#1892](https://github.com/janus-idp/backstage-plugins/issues/1892)) ([cca1e53](https://github.com/janus-idp/backstage-plugins/commit/cca1e53bc6b3019b1c544f2f62bed8723ebf6130)) +## @janus-idp/backstage-plugin-orchestrator-backend [1.17.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.17.1...@janus-idp/backstage-plugin-orchestrator-backend@1.17.2) (2024-08-05) + +### Dependencies +- **@janus-idp/backstage-plugin-rbac-common:** upgraded to 1.8.1 + +## @janus-idp/backstage-plugin-orchestrator-backend [1.17.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.17.0...@janus-idp/backstage-plugin-orchestrator-backend@1.17.1) (2024-08-02) ### Bug Fixes -* **orchestrator:** resolve broken dynamic plugin publish ([#1906](https://github.com/janus-idp/backstage-plugins/issues/1906)) ([5f99043](https://github.com/janus-idp/backstage-plugins/commit/5f990438ebebf8b23c0c8706852753ad0812c55a)) +- **orchestrator:** remove default pagination on v2 endpoints ([#1983](https://github.com/janus-idp/backstage-plugins/issues/1983)) ([5e30274](https://github.com/janus-idp/backstage-plugins/commit/5e302748a25cbad127122407e5258576054eac3d)) + +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.13.1 +## @janus-idp/backstage-plugin-orchestrator-backend [1.17.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.16.1...@janus-idp/backstage-plugin-orchestrator-backend@1.17.0) (2024-07-26) + +### Features + +- **deps:** update to backstage 1.29 ([#1900](https://github.com/janus-idp/backstage-plugins/issues/1900)) ([f53677f](https://github.com/janus-idp/backstage-plugins/commit/f53677fb02d6df43a9de98c43a9f101a6db76802)) +- **orchestrator:** use v2 endpoints to retrieve instances ([#1956](https://github.com/janus-idp/backstage-plugins/issues/1956)) ([537502b](https://github.com/janus-idp/backstage-plugins/commit/537502b9d2ac13f2fb3f79188422d2c6e97f41fb)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.12.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.13.0 +- **@janus-idp/backstage-plugin-rbac-common:** upgraded to 1.8.0 -## @janus-idp/backstage-plugin-orchestrator-backend [1.15.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.14.0...@janus-idp/backstage-plugin-orchestrator-backend@1.15.0) (2024-07-12) +## @janus-idp/backstage-plugin-orchestrator-backend [1.16.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.16.0...@janus-idp/backstage-plugin-orchestrator-backend@1.16.1) (2024-07-24) + +### Bug Fixes + +- **deps:** rollback unreleased plugins ([#1951](https://github.com/janus-idp/backstage-plugins/issues/1951)) ([8b77969](https://github.com/janus-idp/backstage-plugins/commit/8b779694f02f8125587296305276b84cdfeeaebe)) +### Dependencies + +- **@janus-idp/backstage-plugin-rbac-common:** upgraded to 1.7.2 + +## @janus-idp/backstage-plugin-orchestrator-backend [1.16.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.15.0...@janus-idp/backstage-plugin-orchestrator-backend@1.16.0) (2024-07-24) ### Features -* **orchestrator:** fix version ([#1886](https://github.com/janus-idp/backstage-plugins/issues/1886)) ([65c5917](https://github.com/janus-idp/backstage-plugins/commit/65c5917b8fc066a869d1a8e76d5e7b6cb4c8327c)) +- **deps:** update to backstage 1.28 ([#1891](https://github.com/janus-idp/backstage-plugins/issues/1891)) ([1ba1108](https://github.com/janus-idp/backstage-plugins/commit/1ba11088e0de60e90d138944267b83600dc446e5)) +- **orchestrator:** use v2 endpoints to retrieve workflow overviews ([#1892](https://github.com/janus-idp/backstage-plugins/issues/1892)) ([cca1e53](https://github.com/janus-idp/backstage-plugins/commit/cca1e53bc6b3019b1c544f2f62bed8723ebf6130)) +### Bug Fixes +- **orchestrator:** resolve broken dynamic plugin publish ([#1906](https://github.com/janus-idp/backstage-plugins/issues/1906)) ([5f99043](https://github.com/janus-idp/backstage-plugins/commit/5f990438ebebf8b23c0c8706852753ad0812c55a)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.11.0 - -## @janus-idp/backstage-plugin-orchestrator-backend [1.14.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.13.1...@janus-idp/backstage-plugin-orchestrator-backend@1.14.0) (2024-07-11) +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.12.0 +## @janus-idp/backstage-plugin-orchestrator-backend [1.15.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.14.0...@janus-idp/backstage-plugin-orchestrator-backend@1.15.0) (2024-07-12) ### Features -* **orchestrator:** change openapi client generator ([#1864](https://github.com/janus-idp/backstage-plugins/issues/1864)) ([d6a4f4c](https://github.com/janus-idp/backstage-plugins/commit/d6a4f4ccfedfd55356305131029fd3d8ca0ab9c5)) +- **orchestrator:** fix version ([#1886](https://github.com/janus-idp/backstage-plugins/issues/1886)) ([65c5917](https://github.com/janus-idp/backstage-plugins/commit/65c5917b8fc066a869d1a8e76d5e7b6cb4c8327c)) + +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.11.0 +## @janus-idp/backstage-plugin-orchestrator-backend [1.14.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.13.1...@janus-idp/backstage-plugin-orchestrator-backend@1.14.0) (2024-07-11) + +### Features +- **orchestrator:** change openapi client generator ([#1864](https://github.com/janus-idp/backstage-plugins/issues/1864)) ([d6a4f4c](https://github.com/janus-idp/backstage-plugins/commit/d6a4f4ccfedfd55356305131029fd3d8ca0ab9c5)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.11.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.11.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.13.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.13.0...@janus-idp/backstage-plugin-orchestrator-backend@1.13.1) (2024-07-01) - ### Bug Fixes -* **rbac:** update rbac common to fix compilation ([#1858](https://github.com/janus-idp/backstage-plugins/issues/1858)) ([48f142b](https://github.com/janus-idp/backstage-plugins/commit/48f142b447f0d1677ba3f16b2a3c8972b22d0588)) +- **rbac:** update rbac common to fix compilation ([#1858](https://github.com/janus-idp/backstage-plugins/issues/1858)) ([48f142b](https://github.com/janus-idp/backstage-plugins/commit/48f142b447f0d1677ba3f16b2a3c8972b22d0588)) ## @janus-idp/backstage-plugin-orchestrator-backend [1.13.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.12.0...@janus-idp/backstage-plugin-orchestrator-backend@1.13.0) (2024-06-28) - ### Features -* **orchestrator:** fix build failure from [#1833](https://github.com/janus-idp/backstage-plugins/issues/1833) ([#1850](https://github.com/janus-idp/backstage-plugins/issues/1850)) ([c0c73e6](https://github.com/janus-idp/backstage-plugins/commit/c0c73e638f66c03dae565614b8186938b38d7032)) -* **orchestrator:** remove unneeded orchestrator jira integration and endpoint ([#1833](https://github.com/janus-idp/backstage-plugins/issues/1833)) ([d2a76fd](https://github.com/janus-idp/backstage-plugins/commit/d2a76fd3db028f9774c821759bee5f38b7131c94)) - - +- **orchestrator:** fix build failure from [#1833](https://github.com/janus-idp/backstage-plugins/issues/1833) ([#1850](https://github.com/janus-idp/backstage-plugins/issues/1850)) ([c0c73e6](https://github.com/janus-idp/backstage-plugins/commit/c0c73e638f66c03dae565614b8186938b38d7032)) +- **orchestrator:** remove unneeded orchestrator jira integration and endpoint ([#1833](https://github.com/janus-idp/backstage-plugins/issues/1833)) ([d2a76fd](https://github.com/janus-idp/backstage-plugins/commit/d2a76fd3db028f9774c821759bee5f38b7131c94)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.10.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.10.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.12.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.11.0...@janus-idp/backstage-plugin-orchestrator-backend@1.12.0) (2024-06-26) - ### Features -* **orchestrator:** disable buttons based on permissions ([#1818](https://github.com/janus-idp/backstage-plugins/issues/1818)) ([36504b0](https://github.com/janus-idp/backstage-plugins/commit/36504b05d96dbbf0b2395dc6e5c155c21fa73bcd)) +- **orchestrator:** disable buttons based on permissions ([#1818](https://github.com/janus-idp/backstage-plugins/issues/1818)) ([36504b0](https://github.com/janus-idp/backstage-plugins/commit/36504b05d96dbbf0b2395dc6e5c155c21fa73bcd)) ## @janus-idp/backstage-plugin-orchestrator-backend [1.11.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.10.1...@janus-idp/backstage-plugin-orchestrator-backend@1.11.0) (2024-06-25) - ### Features -* **orchestrator:** add auditLog and reorganize endpoints declaration ([#1820](https://github.com/janus-idp/backstage-plugins/issues/1820)) ([00d9216](https://github.com/janus-idp/backstage-plugins/commit/00d9216ba76c13fac86933a8605102d6e1768929)) +- **orchestrator:** add auditLog and reorganize endpoints declaration ([#1820](https://github.com/janus-idp/backstage-plugins/issues/1820)) ([00d9216](https://github.com/janus-idp/backstage-plugins/commit/00d9216ba76c13fac86933a8605102d6e1768929)) ## @janus-idp/backstage-plugin-orchestrator-backend [1.10.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.10.0...@janus-idp/backstage-plugin-orchestrator-backend@1.10.1) (2024-06-19) - ### Bug Fixes -* **orchestrator:** change log level of cache messages to be debug ([#1824](https://github.com/janus-idp/backstage-plugins/issues/1824)) ([4224612](https://github.com/janus-idp/backstage-plugins/commit/422461224e31b419cd8394e2432af71ed10a986e)) - - +- **orchestrator:** change log level of cache messages to be debug ([#1824](https://github.com/janus-idp/backstage-plugins/issues/1824)) ([4224612](https://github.com/janus-idp/backstage-plugins/commit/422461224e31b419cd8394e2432af71ed10a986e)) ### Dependencies -* **@janus-idp/cli:** upgraded to 1.11.1 +- **@janus-idp/cli:** upgraded to 1.11.1 ## @janus-idp/backstage-plugin-orchestrator-backend [1.10.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.9.8...@janus-idp/backstage-plugin-orchestrator-backend@1.10.0) (2024-06-13) - ### Features -* **deps:** update to backstage 1.27 ([#1683](https://github.com/janus-idp/backstage-plugins/issues/1683)) ([a14869c](https://github.com/janus-idp/backstage-plugins/commit/a14869c3f4177049cb8d6552b36c3ffd17e7997d)) - - +- **deps:** update to backstage 1.27 ([#1683](https://github.com/janus-idp/backstage-plugins/issues/1683)) ([a14869c](https://github.com/janus-idp/backstage-plugins/commit/a14869c3f4177049cb8d6552b36c3ffd17e7997d)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.9.0 -* **@janus-idp/cli:** upgraded to 1.11.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.9.0 +- **@janus-idp/cli:** upgraded to 1.11.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.9.8](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.9.7...@janus-idp/backstage-plugin-orchestrator-backend@1.9.8) (2024-06-13) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.10.1 +- **@janus-idp/cli:** upgraded to 1.10.1 ## @janus-idp/backstage-plugin-orchestrator-backend [1.9.7](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.9.6...@janus-idp/backstage-plugin-orchestrator-backend@1.9.7) (2024-06-11) - ### Bug Fixes -* **orchestrator:** fix error handling in case data index failed to start ([#1804](https://github.com/janus-idp/backstage-plugins/issues/1804)) ([27affb7](https://github.com/janus-idp/backstage-plugins/commit/27affb7815e02127721fd854f7903dca3525dede)) +- **orchestrator:** fix error handling in case data index failed to start ([#1804](https://github.com/janus-idp/backstage-plugins/issues/1804)) ([27affb7](https://github.com/janus-idp/backstage-plugins/commit/27affb7815e02127721fd854f7903dca3525dede)) ## @janus-idp/backstage-plugin-orchestrator-backend [1.9.6](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.9.5...@janus-idp/backstage-plugin-orchestrator-backend@1.9.6) (2024-06-05) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.10.0 +- **@janus-idp/cli:** upgraded to 1.10.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.9.5](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.9.4...@janus-idp/backstage-plugin-orchestrator-backend@1.9.5) (2024-06-04) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.8.1 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.8.1 ## @janus-idp/backstage-plugin-orchestrator-backend [1.9.4](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.9.3...@janus-idp/backstage-plugin-orchestrator-backend@1.9.4) (2024-06-03) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.9.0 +- **@janus-idp/cli:** upgraded to 1.9.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.9.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.9.2...@janus-idp/backstage-plugin-orchestrator-backend@1.9.3) (2024-05-29) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.10 +- **@janus-idp/cli:** upgraded to 1.8.10 ## @janus-idp/backstage-plugin-orchestrator-backend [1.9.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.9.1...@janus-idp/backstage-plugin-orchestrator-backend@1.9.2) (2024-05-29) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.9 +- **@janus-idp/cli:** upgraded to 1.8.9 ## @janus-idp/backstage-plugin-orchestrator-backend [1.9.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.9.0...@janus-idp/backstage-plugin-orchestrator-backend@1.9.1) (2024-05-28) - ### Bug Fixes -* **orchestrator:** fixed broken workflow viewer ([#1717](https://github.com/janus-idp/backstage-plugins/issues/1717)) ([19cc79b](https://github.com/janus-idp/backstage-plugins/commit/19cc79bb9c1422556ddb9f85a2ac323186808321)) +- **orchestrator:** fixed broken workflow viewer ([#1717](https://github.com/janus-idp/backstage-plugins/issues/1717)) ([19cc79b](https://github.com/janus-idp/backstage-plugins/commit/19cc79bb9c1422556ddb9f85a2ac323186808321)) ## @janus-idp/backstage-plugin-orchestrator-backend [1.9.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.8.7...@janus-idp/backstage-plugin-orchestrator-backend@1.9.0) (2024-05-22) - ### Features -* **orchestrator:** add permissions to orchestrator plugin ([#1599](https://github.com/janus-idp/backstage-plugins/issues/1599)) ([d0a4531](https://github.com/janus-idp/backstage-plugins/commit/d0a453181e177eb1da7b1e231253b76a2d9356a8)) - +- **orchestrator:** add permissions to orchestrator plugin ([#1599](https://github.com/janus-idp/backstage-plugins/issues/1599)) ([d0a4531](https://github.com/janus-idp/backstage-plugins/commit/d0a453181e177eb1da7b1e231253b76a2d9356a8)) ### Bug Fixes -* **orchestrator:** fix the common package reference version ([#1704](https://github.com/janus-idp/backstage-plugins/issues/1704)) ([942b2a3](https://github.com/janus-idp/backstage-plugins/commit/942b2a3b6eb29c0fe88f9c98dea581309d02fded)) +- **orchestrator:** fix the common package reference version ([#1704](https://github.com/janus-idp/backstage-plugins/issues/1704)) ([942b2a3](https://github.com/janus-idp/backstage-plugins/commit/942b2a3b6eb29c0fe88f9c98dea581309d02fded)) ## @janus-idp/backstage-plugin-orchestrator-backend [1.8.7](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.8.6...@janus-idp/backstage-plugin-orchestrator-backend@1.8.7) (2024-05-21) ## @janus-idp/backstage-plugin-orchestrator-backend [1.8.6](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.8.5...@janus-idp/backstage-plugin-orchestrator-backend@1.8.6) (2024-05-20) - ### Bug Fixes -* **orchestrator:** fixes many security-related issues ([#1681](https://github.com/janus-idp/backstage-plugins/issues/1681)) ([3e801c8](https://github.com/janus-idp/backstage-plugins/commit/3e801c84015f925bdecd226a161ef81a5fc69432)) +- **orchestrator:** fixes many security-related issues ([#1681](https://github.com/janus-idp/backstage-plugins/issues/1681)) ([3e801c8](https://github.com/janus-idp/backstage-plugins/commit/3e801c84015f925bdecd226a161ef81a5fc69432)) ## @janus-idp/backstage-plugin-orchestrator-backend [1.8.5](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.8.4...@janus-idp/backstage-plugin-orchestrator-backend@1.8.5) (2024-05-16) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.7 +- **@janus-idp/cli:** upgraded to 1.8.7 ## @janus-idp/backstage-plugin-orchestrator-backend [1.8.4](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.8.3...@janus-idp/backstage-plugin-orchestrator-backend@1.8.4) (2024-05-15) - ### Documentation -* **orchestrator:** removes instructions related to the editor ([#1664](https://github.com/janus-idp/backstage-plugins/issues/1664)) ([10a75b2](https://github.com/janus-idp/backstage-plugins/commit/10a75b2706c72751bd774d6fae4332bbc527dc2b)) - - +- **orchestrator:** removes instructions related to the editor ([#1664](https://github.com/janus-idp/backstage-plugins/issues/1664)) ([10a75b2](https://github.com/janus-idp/backstage-plugins/commit/10a75b2706c72751bd774d6fae4332bbc527dc2b)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.7.2 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.7.2 ## @janus-idp/backstage-plugin-orchestrator-backend [1.8.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.8.2...@janus-idp/backstage-plugin-orchestrator-backend@1.8.3) (2024-05-15) - ### Bug Fixes -* **orchestrator:** export the `OrchestratorPlugin` accordingly ([#1644](https://github.com/janus-idp/backstage-plugins/issues/1644)) ([4a9d1f8](https://github.com/janus-idp/backstage-plugins/commit/4a9d1f821a30437e73631fac98b1aabc65473fba)) +- **orchestrator:** export the `OrchestratorPlugin` accordingly ([#1644](https://github.com/janus-idp/backstage-plugins/issues/1644)) ([4a9d1f8](https://github.com/janus-idp/backstage-plugins/commit/4a9d1f821a30437e73631fac98b1aabc65473fba)) ## @janus-idp/backstage-plugin-orchestrator-backend [1.8.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.8.1...@janus-idp/backstage-plugin-orchestrator-backend@1.8.2) (2024-05-09) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.7.1 -* **@janus-idp/cli:** upgraded to 1.8.6 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.7.1 +- **@janus-idp/cli:** upgraded to 1.8.6 ## @janus-idp/backstage-plugin-orchestrator-backend [1.8.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.8.0...@janus-idp/backstage-plugin-orchestrator-backend@1.8.1) (2024-05-09) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.7.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.7.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.8.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.7.4...@janus-idp/backstage-plugin-orchestrator-backend@1.8.0) (2024-05-06) - ### Features -* **orchestrator:** make the internal sonata podman compatible ([#1612](https://github.com/janus-idp/backstage-plugins/issues/1612)) ([e4e528e](https://github.com/janus-idp/backstage-plugins/commit/e4e528e2c10536d029ffec11953f3a1d0309b0c5)) +- **orchestrator:** make the internal sonata podman compatible ([#1612](https://github.com/janus-idp/backstage-plugins/issues/1612)) ([e4e528e](https://github.com/janus-idp/backstage-plugins/commit/e4e528e2c10536d029ffec11953f3a1d0309b0c5)) ## @janus-idp/backstage-plugin-orchestrator-backend [1.7.4](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.7.3...@janus-idp/backstage-plugin-orchestrator-backend@1.7.4) (2024-05-02) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.5 +- **@janus-idp/cli:** upgraded to 1.8.5 ## @janus-idp/backstage-plugin-orchestrator-backend [1.7.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.7.2...@janus-idp/backstage-plugin-orchestrator-backend@1.7.3) (2024-05-02) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.4 +- **@janus-idp/cli:** upgraded to 1.8.4 ## @janus-idp/backstage-plugin-orchestrator-backend [1.7.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.7.1...@janus-idp/backstage-plugin-orchestrator-backend@1.7.2) (2024-04-30) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.3 +- **@janus-idp/cli:** upgraded to 1.8.3 ## @janus-idp/backstage-plugin-orchestrator-backend [1.7.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.7.0...@janus-idp/backstage-plugin-orchestrator-backend@1.7.1) (2024-04-30) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.2 +- **@janus-idp/cli:** upgraded to 1.8.2 ## @janus-idp/backstage-plugin-orchestrator-backend [1.7.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.6.8...@janus-idp/backstage-plugin-orchestrator-backend@1.7.0) (2024-04-25) - ### Features -* **orchestrator:** add endpoint to retrigger workflow in error state ([#1343](https://github.com/janus-idp/backstage-plugins/issues/1343)) ([328d23a](https://github.com/janus-idp/backstage-plugins/commit/328d23a7992da125becc8d7775a4ebd68165f243)) - - +- **orchestrator:** add endpoint to retrigger workflow in error state ([#1343](https://github.com/janus-idp/backstage-plugins/issues/1343)) ([328d23a](https://github.com/janus-idp/backstage-plugins/commit/328d23a7992da125becc8d7775a4ebd68165f243)) ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.1 +- **@janus-idp/cli:** upgraded to 1.8.1 ## @janus-idp/backstage-plugin-orchestrator-backend [1.6.8](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.6.7...@janus-idp/backstage-plugin-orchestrator-backend@1.6.8) (2024-04-18) - ### Bug Fixes -* **orchestrator:** allows serving the editor envelope in disconnected environments ([#1450](https://github.com/janus-idp/backstage-plugins/issues/1450)) ([1e778d8](https://github.com/janus-idp/backstage-plugins/commit/1e778d88336dfec79d48ece4fd8d2a035133b70e)) - - +- **orchestrator:** allows serving the editor envelope in disconnected environments ([#1450](https://github.com/janus-idp/backstage-plugins/issues/1450)) ([1e778d8](https://github.com/janus-idp/backstage-plugins/commit/1e778d88336dfec79d48ece4fd8d2a035133b70e)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.4 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.4 ## @janus-idp/backstage-plugin-orchestrator-backend [1.6.7](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.6.6...@janus-idp/backstage-plugin-orchestrator-backend@1.6.7) (2024-04-15) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.0 +- **@janus-idp/cli:** upgraded to 1.8.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.6.6](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.6.5...@janus-idp/backstage-plugin-orchestrator-backend@1.6.6) (2024-04-09) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.10 +- **@janus-idp/cli:** upgraded to 1.7.10 ## @janus-idp/backstage-plugin-orchestrator-backend [1.6.5](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.6.4...@janus-idp/backstage-plugin-orchestrator-backend@1.6.5) (2024-04-09) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.9 +- **@janus-idp/cli:** upgraded to 1.7.9 ## @janus-idp/backstage-plugin-orchestrator-backend [1.6.4](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.6.3...@janus-idp/backstage-plugin-orchestrator-backend@1.6.4) (2024-04-05) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.3 -* **@janus-idp/cli:** upgraded to 1.7.8 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.3 +- **@janus-idp/cli:** upgraded to 1.7.8 ## @janus-idp/backstage-plugin-orchestrator-backend [1.6.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.6.2...@janus-idp/backstage-plugin-orchestrator-backend@1.6.3) (2024-04-04) - ### Bug Fixes -* **orchestrator:** add lastRunId to overview endpoints ([#1449](https://github.com/janus-idp/backstage-plugins/issues/1449)) ([cce56f7](https://github.com/janus-idp/backstage-plugins/commit/cce56f7de3acc41ecd30b1b9962d7817be69de7d)) -* **orchestrator:** only inputs inherited from the assessment workflow should be disabled ([#1436](https://github.com/janus-idp/backstage-plugins/issues/1436)) ([32d9bdf](https://github.com/janus-idp/backstage-plugins/commit/32d9bdfc38c07c4e60f0ce7670fc3813ad0d92c3)) - - +- **orchestrator:** add lastRunId to overview endpoints ([#1449](https://github.com/janus-idp/backstage-plugins/issues/1449)) ([cce56f7](https://github.com/janus-idp/backstage-plugins/commit/cce56f7de3acc41ecd30b1b9962d7817be69de7d)) +- **orchestrator:** only inputs inherited from the assessment workflow should be disabled ([#1436](https://github.com/janus-idp/backstage-plugins/issues/1436)) ([32d9bdf](https://github.com/janus-idp/backstage-plugins/commit/32d9bdfc38c07c4e60f0ce7670fc3813ad0d92c3)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.2 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.2 ## @janus-idp/backstage-plugin-orchestrator-backend [1.6.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.6.1...@janus-idp/backstage-plugin-orchestrator-backend@1.6.2) (2024-04-02) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.7 +- **@janus-idp/cli:** upgraded to 1.7.7 ## @janus-idp/backstage-plugin-orchestrator-backend [1.6.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.6.0...@janus-idp/backstage-plugin-orchestrator-backend@1.6.1) (2024-03-29) - ### Bug Fixes -* **orchestrator:** fixes v2/instances endpoint ([#1414](https://github.com/janus-idp/backstage-plugins/issues/1414)) ([88b49df](https://github.com/janus-idp/backstage-plugins/commit/88b49df35cf10e231ba69c239e873cb10e7cc25b)) - - +- **orchestrator:** fixes v2/instances endpoint ([#1414](https://github.com/janus-idp/backstage-plugins/issues/1414)) ([88b49df](https://github.com/janus-idp/backstage-plugins/commit/88b49df35cf10e231ba69c239e873cb10e7cc25b)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.1 -* **@janus-idp/cli:** upgraded to 1.7.6 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.1 +- **@janus-idp/cli:** upgraded to 1.7.6 ## @janus-idp/backstage-plugin-orchestrator-backend [1.6.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.5.3...@janus-idp/backstage-plugin-orchestrator-backend@1.6.0) (2024-03-14) - ### Features -* **orchestrator:** verify availability and cache workflow definition IDs ([#1309](https://github.com/janus-idp/backstage-plugins/issues/1309)) ([4d322f1](https://github.com/janus-idp/backstage-plugins/commit/4d322f1fc5b6f8b1afedf40cfe1b24b2edae2ac1)) - - +- **orchestrator:** verify availability and cache workflow definition IDs ([#1309](https://github.com/janus-idp/backstage-plugins/issues/1309)) ([4d322f1](https://github.com/janus-idp/backstage-plugins/commit/4d322f1fc5b6f8b1afedf40cfe1b24b2edae2ac1)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.5.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.5.2...@janus-idp/backstage-plugin-orchestrator-backend@1.5.3) (2024-03-12) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.5.1 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.5.1 ## @janus-idp/backstage-plugin-orchestrator-backend [1.5.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.5.1...@janus-idp/backstage-plugin-orchestrator-backend@1.5.2) (2024-03-11) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.5.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.5.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.5.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.5.0...@janus-idp/backstage-plugin-orchestrator-backend@1.5.1) (2024-03-11) - ### Other changes -* **orchestrator:** add unit tests for v2 endpoints ([#1300](https://github.com/janus-idp/backstage-plugins/issues/1300)) ([9a13138](https://github.com/janus-idp/backstage-plugins/commit/9a13138c61d3cc7331f739da80f020bb68dd61e5)) - - +- **orchestrator:** add unit tests for v2 endpoints ([#1300](https://github.com/janus-idp/backstage-plugins/issues/1300)) ([9a13138](https://github.com/janus-idp/backstage-plugins/commit/9a13138c61d3cc7331f739da80f020bb68dd61e5)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.4.1 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.4.1 ## @janus-idp/backstage-plugin-orchestrator-backend [1.5.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.4.12...@janus-idp/backstage-plugin-orchestrator-backend@1.5.0) (2024-03-07) - ### Features -* **orchestrator:** support pagination for /instances and /overview ([#1313](https://github.com/janus-idp/backstage-plugins/issues/1313)) ([79d5988](https://github.com/janus-idp/backstage-plugins/commit/79d598816f16c8346b6868bff4cc30d695cad518)) - - +- **orchestrator:** support pagination for /instances and /overview ([#1313](https://github.com/janus-idp/backstage-plugins/issues/1313)) ([79d5988](https://github.com/janus-idp/backstage-plugins/commit/79d598816f16c8346b6868bff4cc30d695cad518)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.4.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.4.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.4.12](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.4.11...@janus-idp/backstage-plugin-orchestrator-backend@1.4.12) (2024-03-04) - ### Bug Fixes -* **orchestrator:** increase the number of attempts to fetch the instance after execution ([#1301](https://github.com/janus-idp/backstage-plugins/issues/1301)) ([77dcce3](https://github.com/janus-idp/backstage-plugins/commit/77dcce3adceaf12b583bda5e74be69a5cc273ba1)) - - +- **orchestrator:** increase the number of attempts to fetch the instance after execution ([#1301](https://github.com/janus-idp/backstage-plugins/issues/1301)) ([77dcce3](https://github.com/janus-idp/backstage-plugins/commit/77dcce3adceaf12b583bda5e74be69a5cc273ba1)) ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.5 +- **@janus-idp/cli:** upgraded to 1.7.5 ## @janus-idp/backstage-plugin-orchestrator-backend [1.4.11](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.4.10...@janus-idp/backstage-plugin-orchestrator-backend@1.4.11) (2024-03-03) - ### Bug Fixes -* **orchestrator:** stop fetching workflow URI ([#1297](https://github.com/janus-idp/backstage-plugins/issues/1297)) ([2456a28](https://github.com/janus-idp/backstage-plugins/commit/2456a287dbff955a0916b9600e89a39511cd537a)) - - +- **orchestrator:** stop fetching workflow URI ([#1297](https://github.com/janus-idp/backstage-plugins/issues/1297)) ([2456a28](https://github.com/janus-idp/backstage-plugins/commit/2456a287dbff955a0916b9600e89a39511cd537a)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.7 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.7 ## @janus-idp/backstage-plugin-orchestrator-backend [1.4.10](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.4.9...@janus-idp/backstage-plugin-orchestrator-backend@1.4.10) (2024-02-29) - ### Bug Fixes -* **orchestrator:** refactor 500 response to use ErrorResponse object ([#1290](https://github.com/janus-idp/backstage-plugins/issues/1290)) ([2580f3d](https://github.com/janus-idp/backstage-plugins/commit/2580f3d38cecf78334964666eb7c127c21b00924)) - - +- **orchestrator:** refactor 500 response to use ErrorResponse object ([#1290](https://github.com/janus-idp/backstage-plugins/issues/1290)) ([2580f3d](https://github.com/janus-idp/backstage-plugins/commit/2580f3d38cecf78334964666eb7c127c21b00924)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.6 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.6 ## @janus-idp/backstage-plugin-orchestrator-backend [1.4.9](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.4.8...@janus-idp/backstage-plugin-orchestrator-backend@1.4.9) (2024-02-28) - ### Bug Fixes -* **orchestrator:** clean up the plugin code ([#1292](https://github.com/janus-idp/backstage-plugins/issues/1292)) ([ad27fb8](https://github.com/janus-idp/backstage-plugins/commit/ad27fb8e98913a6b80feb38ff58a7864e1953a7e)) - - +- **orchestrator:** clean up the plugin code ([#1292](https://github.com/janus-idp/backstage-plugins/issues/1292)) ([ad27fb8](https://github.com/janus-idp/backstage-plugins/commit/ad27fb8e98913a6b80feb38ff58a7864e1953a7e)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.5 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.5 ## @janus-idp/backstage-plugin-orchestrator-backend [1.4.8](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.4.7...@janus-idp/backstage-plugin-orchestrator-backend@1.4.8) (2024-02-28) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.4 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.4 ## @janus-idp/backstage-plugin-orchestrator-backend [1.4.7](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.4.6...@janus-idp/backstage-plugin-orchestrator-backend@1.4.7) (2024-02-28) - ### Bug Fixes -* **orchestrator:** handle nullable start/state properties of process instance ([#1277](https://github.com/janus-idp/backstage-plugins/issues/1277)) ([d8a43a5](https://github.com/janus-idp/backstage-plugins/commit/d8a43a5a164f83fc90d037ae3d7a355f5de543e0)) - - +- **orchestrator:** handle nullable start/state properties of process instance ([#1277](https://github.com/janus-idp/backstage-plugins/issues/1277)) ([d8a43a5](https://github.com/janus-idp/backstage-plugins/commit/d8a43a5a164f83fc90d037ae3d7a355f5de543e0)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.3 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.3 ## @janus-idp/backstage-plugin-orchestrator-backend [1.4.6](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.4.5...@janus-idp/backstage-plugin-orchestrator-backend@1.4.6) (2024-02-27) - ### Bug Fixes -* **orchestrator:** workflowId parameter wrongly parsed in getWorkflowOverviewById (v2) ([#1283](https://github.com/janus-idp/backstage-plugins/issues/1283)) ([2cd70d0](https://github.com/janus-idp/backstage-plugins/commit/2cd70d048d707a3b117c5273a1d8bc9fdc03fff7)) - - +- **orchestrator:** workflowId parameter wrongly parsed in getWorkflowOverviewById (v2) ([#1283](https://github.com/janus-idp/backstage-plugins/issues/1283)) ([2cd70d0](https://github.com/janus-idp/backstage-plugins/commit/2cd70d048d707a3b117c5273a1d8bc9fdc03fff7)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.2 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.2 ## @janus-idp/backstage-plugin-orchestrator-backend [1.4.5](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.4.4...@janus-idp/backstage-plugin-orchestrator-backend@1.4.5) (2024-02-27) - ### Bug Fixes -* **orchestrator:** warn "unknown format X ignored in schema at path Y" ([#1270](https://github.com/janus-idp/backstage-plugins/issues/1270)) ([de3c734](https://github.com/janus-idp/backstage-plugins/commit/de3c734299189b753d924c87aa9b5c9b5f94683c)), closes [/github.com/janus-idp/backstage-plugins/blob/903c7f37a1cf138ac96ef3f631f951866c2014fa/plugins/notifications-backend/src/service/router.ts#L45-L52](https://github.com/janus-idp//github.com/janus-idp/backstage-plugins/blob/903c7f37a1cf138ac96ef3f631f951866c2014fa/plugins/notifications-backend/src/service/router.ts/issues/L45-L52) - - +- **orchestrator:** warn "unknown format X ignored in schema at path Y" ([#1270](https://github.com/janus-idp/backstage-plugins/issues/1270)) ([de3c734](https://github.com/janus-idp/backstage-plugins/commit/de3c734299189b753d924c87aa9b5c9b5f94683c)), closes [/github.com/janus-idp/backstage-plugins/blob/903c7f37a1cf138ac96ef3f631f951866c2014fa/plugins/notifications-backend/src/service/router.ts#L45-L52](https://github.com/janus-idp//github.com/janus-idp/backstage-plugins/blob/903c7f37a1cf138ac96ef3f631f951866c2014fa/plugins/notifications-backend/src/service/router.ts/issues/L45-L52) ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.4 +- **@janus-idp/cli:** upgraded to 1.7.4 ## @janus-idp/backstage-plugin-orchestrator-backend [1.4.4](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.4.3...@janus-idp/backstage-plugin-orchestrator-backend@1.4.4) (2024-02-26) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.3 +- **@janus-idp/cli:** upgraded to 1.7.3 ## @janus-idp/backstage-plugin-orchestrator-backend [1.4.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.4.2...@janus-idp/backstage-plugin-orchestrator-backend@1.4.3) (2024-02-23) - ### Bug Fixes -* **orchestrator:** handle api endpoint failure ([#1254](https://github.com/janus-idp/backstage-plugins/issues/1254)) ([503de1b](https://github.com/janus-idp/backstage-plugins/commit/503de1b028e134cafb5a04045068768f30519409)) +- **orchestrator:** handle api endpoint failure ([#1254](https://github.com/janus-idp/backstage-plugins/issues/1254)) ([503de1b](https://github.com/janus-idp/backstage-plugins/commit/503de1b028e134cafb5a04045068768f30519409)) ## @janus-idp/backstage-plugin-orchestrator-backend [1.4.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.4.1...@janus-idp/backstage-plugin-orchestrator-backend@1.4.2) (2024-02-22) - ### Bug Fixes -* **orchestrator:** improvements to backend services ([#1252](https://github.com/janus-idp/backstage-plugins/issues/1252)) ([af8e072](https://github.com/janus-idp/backstage-plugins/commit/af8e072f35bc033f5111207c87711c9c0f9ff386)) +- **orchestrator:** improvements to backend services ([#1252](https://github.com/janus-idp/backstage-plugins/issues/1252)) ([af8e072](https://github.com/janus-idp/backstage-plugins/commit/af8e072f35bc033f5111207c87711c9c0f9ff386)) ## @janus-idp/backstage-plugin-orchestrator-backend [1.4.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.4.0...@janus-idp/backstage-plugin-orchestrator-backend@1.4.1) (2024-02-21) - ### Bug Fixes -* **orchestrator:** implementation of getWorkflowById (v2) ([#1233](https://github.com/janus-idp/backstage-plugins/issues/1233)) ([f9f9008](https://github.com/janus-idp/backstage-plugins/commit/f9f9008d29f244c2ae6d688d3e2dc9b65b705e5b)) -* **orchestrator:** minor improvements and fixes ([#1242](https://github.com/janus-idp/backstage-plugins/issues/1242)) ([c9ec4cb](https://github.com/janus-idp/backstage-plugins/commit/c9ec4cbe1847268e8068edc69c7937c5e133c315)) - - +- **orchestrator:** implementation of getWorkflowById (v2) ([#1233](https://github.com/janus-idp/backstage-plugins/issues/1233)) ([f9f9008](https://github.com/janus-idp/backstage-plugins/commit/f9f9008d29f244c2ae6d688d3e2dc9b65b705e5b)) +- **orchestrator:** minor improvements and fixes ([#1242](https://github.com/janus-idp/backstage-plugins/issues/1242)) ([c9ec4cb](https://github.com/janus-idp/backstage-plugins/commit/c9ec4cbe1847268e8068edc69c7937c5e133c315)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.1 -* **@janus-idp/cli:** upgraded to 1.7.2 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.1 +- **@janus-idp/cli:** upgraded to 1.7.2 ## @janus-idp/backstage-plugin-orchestrator-backend [1.4.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.3.1...@janus-idp/backstage-plugin-orchestrator-backend@1.4.0) (2024-02-20) - ### Features -* **orchestrator:** add OpenAPI v2 implementations ([#1182](https://github.com/janus-idp/backstage-plugins/issues/1182)) ([43ac2f3](https://github.com/janus-idp/backstage-plugins/commit/43ac2f3f492b5c977142a3cfd9868d5e193ceb02)) - +- **orchestrator:** add OpenAPI v2 implementations ([#1182](https://github.com/janus-idp/backstage-plugins/issues/1182)) ([43ac2f3](https://github.com/janus-idp/backstage-plugins/commit/43ac2f3f492b5c977142a3cfd9868d5e193ceb02)) ### Bug Fixes -* **orchestrator:** decommission the ProcessInstance.lastUpdate field ([#1230](https://github.com/janus-idp/backstage-plugins/issues/1230)) ([9724e27](https://github.com/janus-idp/backstage-plugins/commit/9724e27eaa84fe73d7724f28c86409681b7f79f8)) - - +- **orchestrator:** decommission the ProcessInstance.lastUpdate field ([#1230](https://github.com/janus-idp/backstage-plugins/issues/1230)) ([9724e27](https://github.com/janus-idp/backstage-plugins/commit/9724e27eaa84fe73d7724f28c86409681b7f79f8)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.3.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.3.0...@janus-idp/backstage-plugin-orchestrator-backend@1.3.1) (2024-02-16) - ### Bug Fixes -* **orchestrator:** resolve mismatch between execution data and composed schema ([#1217](https://github.com/janus-idp/backstage-plugins/issues/1217)) ([af85114](https://github.com/janus-idp/backstage-plugins/commit/af851148935e1ed083709cac145520d7551de737)) - - +- **orchestrator:** resolve mismatch between execution data and composed schema ([#1217](https://github.com/janus-idp/backstage-plugins/issues/1217)) ([af85114](https://github.com/janus-idp/backstage-plugins/commit/af851148935e1ed083709cac145520d7551de737)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.2.1 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.2.1 ## @janus-idp/backstage-plugin-orchestrator-backend [1.3.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.2.2...@janus-idp/backstage-plugin-orchestrator-backend@1.3.0) (2024-02-16) - ### Features -* **orchestrator:** add OpenAPI support ([#1123](https://github.com/janus-idp/backstage-plugins/issues/1123)) ([bd88e23](https://github.com/janus-idp/backstage-plugins/commit/bd88e2304c93761ce6754985074f004a5a3c8c4b)) - - +- **orchestrator:** add OpenAPI support ([#1123](https://github.com/janus-idp/backstage-plugins/issues/1123)) ([bd88e23](https://github.com/janus-idp/backstage-plugins/commit/bd88e2304c93761ce6754985074f004a5a3c8c4b)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.2.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.2.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.2.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.2.1...@janus-idp/backstage-plugin-orchestrator-backend@1.2.2) (2024-02-13) - ### Bug Fixes -* **orchestrator:** filter out `null` values from action input ([#1199](https://github.com/janus-idp/backstage-plugins/issues/1199)) ([55c3927](https://github.com/janus-idp/backstage-plugins/commit/55c3927fb5211e1ec78719fd38740eb29e481962)) +- **orchestrator:** filter out `null` values from action input ([#1199](https://github.com/janus-idp/backstage-plugins/issues/1199)) ([55c3927](https://github.com/janus-idp/backstage-plugins/commit/55c3927fb5211e1ec78719fd38740eb29e481962)) ## @janus-idp/backstage-plugin-orchestrator-backend [1.2.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.2.0...@janus-idp/backstage-plugin-orchestrator-backend@1.2.1) (2024-02-05) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.1 +- **@janus-idp/cli:** upgraded to 1.7.1 ## @janus-idp/backstage-plugin-orchestrator-backend [1.2.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.1.0...@janus-idp/backstage-plugin-orchestrator-backend@1.2.0) (2024-02-02) - ### Features -* **orchestrator:** add the ability to rerun workflows in a new instance ([#1141](https://github.com/janus-idp/backstage-plugins/issues/1141)) ([fe326df](https://github.com/janus-idp/backstage-plugins/commit/fe326df569caa5a9e7b7ec809c1c371a2a936010)) - +- **orchestrator:** add the ability to rerun workflows in a new instance ([#1141](https://github.com/janus-idp/backstage-plugins/issues/1141)) ([fe326df](https://github.com/janus-idp/backstage-plugins/commit/fe326df569caa5a9e7b7ec809c1c371a2a936010)) ### Bug Fixes -* add missing alpha dynamic plugin entry points ([#1161](https://github.com/janus-idp/backstage-plugins/issues/1161)) ([36e9d91](https://github.com/janus-idp/backstage-plugins/commit/36e9d910b8f534fd9db2f8210c9aa7a24560f01d)) - - +- add missing alpha dynamic plugin entry points ([#1161](https://github.com/janus-idp/backstage-plugins/issues/1161)) ([36e9d91](https://github.com/janus-idp/backstage-plugins/commit/36e9d910b8f534fd9db2f8210c9aa7a24560f01d)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.1.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.1.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.1.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.0.2...@janus-idp/backstage-plugin-orchestrator-backend@1.1.0) (2024-01-30) - ### Features -* add new backend system support for existing backend plugins that have not been migrated over yet ([#1132](https://github.com/janus-idp/backstage-plugins/issues/1132)) ([06e16fd](https://github.com/janus-idp/backstage-plugins/commit/06e16fdcf64257dd08297cb727445d9a8a23c522)) - - +- add new backend system support for existing backend plugins that have not been migrated over yet ([#1132](https://github.com/janus-idp/backstage-plugins/issues/1132)) ([06e16fd](https://github.com/janus-idp/backstage-plugins/commit/06e16fdcf64257dd08297cb727445d9a8a23c522)) ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.0 +- **@janus-idp/cli:** upgraded to 1.7.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.0.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.0.1...@janus-idp/backstage-plugin-orchestrator-backend@1.0.2) (2024-01-25) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.6.0 +- **@janus-idp/cli:** upgraded to 1.6.0 ## @janus-idp/backstage-plugin-orchestrator-backend [1.0.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator-backend@1.0.0...@janus-idp/backstage-plugin-orchestrator-backend@1.0.1) (2024-01-18) - ### Bug Fixes -* **orchestrator:** regenerate `orchestrator-backend/dist-dynamic/package.json` ([#1083](https://github.com/janus-idp/backstage-plugins/issues/1083)) ([8a8051c](https://github.com/janus-idp/backstage-plugins/commit/8a8051c5eded7bdd3e05d1532e8354709aaccb8b)) +- **orchestrator:** regenerate `orchestrator-backend/dist-dynamic/package.json` ([#1083](https://github.com/janus-idp/backstage-plugins/issues/1083)) ([8a8051c](https://github.com/janus-idp/backstage-plugins/commit/8a8051c5eded7bdd3e05d1532e8354709aaccb8b)) ## @janus-idp/backstage-plugin-orchestrator-backend 1.0.0 (2024-01-17) - ### Features -* **orchestrator:** add orchestrator plugin ([#783](https://github.com/janus-idp/backstage-plugins/issues/783)) ([cf5fe74](https://github.com/janus-idp/backstage-plugins/commit/cf5fe74db6992d9f51f5073bbcf20c8c346357a1)), closes [#28](https://github.com/janus-idp/backstage-plugins/issues/28) [#38](https://github.com/janus-idp/backstage-plugins/issues/38) [#35](https://github.com/janus-idp/backstage-plugins/issues/35) [#21](https://github.com/janus-idp/backstage-plugins/issues/21) - - +- **orchestrator:** add orchestrator plugin ([#783](https://github.com/janus-idp/backstage-plugins/issues/783)) ([cf5fe74](https://github.com/janus-idp/backstage-plugins/commit/cf5fe74db6992d9f51f5073bbcf20c8c346357a1)), closes [#28](https://github.com/janus-idp/backstage-plugins/issues/28) [#38](https://github.com/janus-idp/backstage-plugins/issues/38) [#35](https://github.com/janus-idp/backstage-plugins/issues/35) [#21](https://github.com/janus-idp/backstage-plugins/issues/21) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.0.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.0.0 diff --git a/plugins/orchestrator-backend/dev/index.ts b/plugins/orchestrator-backend/dev/index.ts index 55c5099d67..0f85011bee 100644 --- a/plugins/orchestrator-backend/dev/index.ts +++ b/plugins/orchestrator-backend/dev/index.ts @@ -1,61 +1,9 @@ -import { - createServiceBuilder, - ServerTokenManager, - UrlReader, -} from '@backstage/backend-common'; -import { DiscoveryService, LoggerService } from '@backstage/backend-plugin-api'; -import { PluginTaskScheduler } from '@backstage/backend-tasks'; -import { CatalogApi } from '@backstage/catalog-client'; -import { Config } from '@backstage/config'; -import { ServerPermissionClient } from '@backstage/plugin-permission-node'; +import { createBackend } from '@backstage/backend-defaults'; -import { Server } from 'http'; +import { orchestratorPlugin } from '../src/plugin'; -import { createRouter } from '../src/routerWrapper'; +const backend = createBackend(); -export interface ServerOptions { - port: number; - enableCors: boolean; - logger: LoggerService; - config: Config; - discovery: DiscoveryService; - catalogApi: CatalogApi; - urlReader: UrlReader; - scheduler: PluginTaskScheduler; -} +backend.add(orchestratorPlugin); -export async function startStandaloneServer( - options: ServerOptions, -): Promise { - const logger = options.logger.child({ service: 'orchestrator-backend' }); - logger.debug('Starting application server...'); - - const permissions = ServerPermissionClient.fromConfig(options.config, { - discovery: options.discovery, - tokenManager: ServerTokenManager.noop(), - }); - - const router = await createRouter({ - logger, - config: options.config, - discovery: options.discovery, - catalogApi: options.catalogApi, - urlReader: options.urlReader, - scheduler: options.scheduler, - permissions: permissions, - }); - - let service = createServiceBuilder(module) - .setPort(options.port) - .addRouter('/orchestrator', router); - if (options.enableCors) { - service = service.enableCors({ origin: 'http://localhost:3000' }); - } - - return await service.start().catch(err => { - logger.error(err); - process.exit(1); - }); -} - -module.hot?.accept(); +backend.start(); diff --git a/plugins/orchestrator-backend/dist-dynamic/package.json b/plugins/orchestrator-backend/dist-dynamic/package.json index f3732f4ca4..bc59c8d87e 100644 --- a/plugins/orchestrator-backend/dist-dynamic/package.json +++ b/plugins/orchestrator-backend/dist-dynamic/package.json @@ -4,7 +4,6 @@ "license": "Apache-2.0", "main": "./dist/index.cjs.js", "types": "src/index.ts", - "private": true, "publishConfig": { "access": "public" }, @@ -23,16 +22,12 @@ "require": "./dist/index.cjs.js", "default": "./dist/index.cjs.js" }, - "./alpha": { - "require": "./dist/alpha.cjs.js", - "default": "./dist/alpha.cjs.js" - }, "./package.json": "./package.json" }, "homepage": "https://red.ht/rhdh", "repository": { "type": "git", - "url": "git+https://github.com/janus-idp/backstage-plugins.git", + "url": "https://github.com/janus-idp/backstage-plugins", "directory": "plugins/orchestrator-backend" }, "bugs": "https://github.com/janus-idp/backstage-plugins/issues", @@ -47,8 +42,7 @@ "files": [ "app-config.janus-idp.yaml", "dist", - "static", - "alpha" + "static" ], "scripts": {}, "dependencies": { @@ -58,6 +52,7 @@ "express": "^4.18.2", "express-promise-router": "^4.1.1", "fs-extra": "^10.1.0", + "isomorphic-git": "^1.23.0", "json-schema": "^0.4.0", "moment": "^2.29.4", "openapi-backend": "^5.10.5", @@ -68,22 +63,16 @@ }, "devDependencies": {}, "peerDependencies": { - "@janus-idp/backstage-plugin-rbac-common": "1.9.0", "@janus-idp/backstage-plugin-audit-log-node": "1.4.1", - "@backstage/backend-app-api": "^0.8.0", + "@janus-idp/backstage-plugin-rbac-common": "1.9.0", "@backstage/backend-common": "^0.23.3", - "@backstage/backend-dynamic-feature-service": "^0.2.15", + "@backstage/backend-defaults": "^0.4.1", "@backstage/backend-plugin-api": "^0.7.0", "@backstage/backend-tasks": "^0.5.27", "@backstage/catalog-client": "^1.6.5", - "@backstage/config": "^1.2.0", - "@backstage/core-plugin-api": "^1.9.3", "@backstage/errors": "^1.2.4", "@backstage/integration": "^1.13.0", - "@backstage/plugin-auth-node": "^0.4.17", "@backstage/plugin-catalog-node": "^1.12.4", - "@backstage/plugin-events-backend": "^0.3.9", - "@backstage/plugin-events-node": "^0.3.8", "@backstage/plugin-permission-common": "^0.8.0", "@backstage/plugin-permission-node": "^0.8.0", "@backstage/plugin-scaffolder-backend": "^1.23.0", diff --git a/plugins/orchestrator-backend/dist-dynamic/yarn.lock b/plugins/orchestrator-backend/dist-dynamic/yarn.lock index 40681fb983..690729d693 100644 --- a/plugins/orchestrator-backend/dist-dynamic/yarn.lock +++ b/plugins/orchestrator-backend/dist-dynamic/yarn.lock @@ -3,14 +3,14 @@ "@0no-co/graphql.web@^1.0.1": - version "1.0.10" - resolved "https://registry.yarnpkg.com/@0no-co/graphql.web/-/graphql.web-1.0.10.tgz#1c124ec279b502510f8868f8c030c070353abda6" - integrity sha512-Nu/d8aLvBEGOEH/739AmEKr9Dwl6lIMTfYUwyLjtv1JK/9MdAXrrIKz50IVOcjF9TM7OBxR1uFvZ27CePk9UyQ== + version "1.0.8" + resolved "https://registry.yarnpkg.com/@0no-co/graphql.web/-/graphql.web-1.0.8.tgz#20682c7839b0b5b7728ad944a8602ca46d983e75" + integrity sha512-8BG6woLtDMvXB9Ajb/uE+Zr/U7y4qJ3upXi0JQHZmsKUJa7HjF/gFvmL2f3/mSmfZoQGRr9VoY97LCX2uaFMzA== "@apidevtools/json-schema-ref-parser@^11.1.0": - version "11.7.2" - resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.7.2.tgz#cdf3e0aded21492364a70e193b45b7cf4177f031" - integrity sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA== + version "11.7.0" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.7.0.tgz#228d72018a0e7cbee744b677eaa01a8968f302d9" + integrity sha512-pRrmXMCwnmrkS3MLgAIW5dXRzeTv6GLjkjb4HmxNnvAKXN1Nfzp4KmGADBQvlVUcqi+a5D+hfGDLLnd5NnYxog== dependencies: "@jsdevtools/ono" "^7.1.3" "@types/json-schema" "^7.0.15" @@ -100,6 +100,11 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== +async-lock@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.4.1.tgz#56b8718915a9b68b10fce2f2a9a3dddf765ef53f" + integrity sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -165,6 +170,11 @@ call-bind@^1.0.2, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +clean-git-ref@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/clean-git-ref/-/clean-git-ref-2.0.1.tgz#dcc0ca093b90e527e67adb5a5e55b1af6816dcd9" + integrity sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw== + cloudevents@^8.0.0: version "8.0.2" resolved "https://registry.yarnpkg.com/cloudevents/-/cloudevents-8.0.2.tgz#034bde4e1a0ee21a8a34b8fe837e7d98c8bbdf8d" @@ -201,16 +211,21 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" - integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== 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== +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -218,6 +233,13 @@ debug@2.6.9: dependencies: ms "2.0.0" +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" @@ -247,6 +269,11 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== +diff3@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/diff3/-/diff3-0.0.3.tgz#d4e5c3a4cdf4e5fe1211ab42e693fcb4321580fc" + integrity sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -294,16 +321,16 @@ express-promise-router@^4.1.1: methods "^1.0.0" express@^4.18.2: - version "4.21.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.21.1.tgz#9dae5dda832f16b4eec941a4e44aa89ec481b281" - integrity sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ== + version "4.21.0" + resolved "https://registry.yarnpkg.com/express/-/express-4.21.0.tgz#d57cb706d49623d4ac27833f1cbc466b668eb915" + integrity sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng== dependencies: accepts "~1.3.8" array-flatten "1.1.1" body-parser "1.20.3" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.7.1" + cookie "0.6.0" cookie-signature "1.0.6" debug "2.6.9" depd "2.0.0" @@ -336,9 +363,9 @@ fast-deep-equal@^3.1.3: integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-uri@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241" - integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw== + version "3.0.2" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.2.tgz#d78b298cf70fd3b752fd951175a3da6a7b48f024" + integrity sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row== finalhandler@1.3.1: version "1.3.1" @@ -366,9 +393,9 @@ for-each@^0.3.3: is-callable "^1.1.3" form-data@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" - integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" @@ -470,7 +497,12 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -inherits@2.0.4, inherits@^2.0.3: +ignore@^5.1.4: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -512,6 +544,23 @@ is-typed-array@^1.1.3: dependencies: which-typed-array "^1.1.14" +isomorphic-git@^1.23.0: + version "1.27.1" + resolved "https://registry.yarnpkg.com/isomorphic-git/-/isomorphic-git-1.27.1.tgz#a2752fce23a09f04baa590c41cfaf61e973405b3" + integrity sha512-X32ph5zIWfT75QAqW2l3JCIqnx9/GWd17bRRehmn3qmWc34OYbSXY6Cxv0o9bIIY+CWugoN4nQFHNA+2uYf2nA== + dependencies: + async-lock "^1.4.1" + clean-git-ref "^2.0.1" + crc-32 "^1.2.0" + diff3 "0.0.3" + ignore "^5.1.4" + minimisted "^2.0.0" + pako "^1.0.10" + pify "^4.0.1" + readable-stream "^3.4.0" + sha.js "^2.4.9" + simple-get "^4.0.1" + js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -592,6 +641,23 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +minimist@^1.2.5: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minimisted@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/minimisted/-/minimisted-2.0.1.tgz#d059fb905beecf0774bc3b308468699709805cb1" + integrity sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA== + dependencies: + minimist "^1.2.5" + 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" @@ -631,6 +697,13 @@ on-finished@2.4.1: dependencies: ee-first "1.1.1" +once@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + openapi-backend@^5.10.5: version "5.11.0" resolved "https://registry.yarnpkg.com/openapi-backend/-/openapi-backend-5.11.0.tgz#4b92d56f4d203989060ee168a95bbcf4115b30cf" @@ -662,6 +735,11 @@ openapi-types@^12.0.2, openapi-types@^12.1.3: resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-12.1.3.tgz#471995eb26c4b97b7bd356aacf7b91b73e777dd3" integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw== +pako@^1.0.10: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -672,6 +750,11 @@ path-to-regexp@0.1.10: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w== +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + possible-typed-array-names@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" @@ -717,12 +800,21 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + require-from-string@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== -safe-buffer@5.2.1: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -778,6 +870,14 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== +sha.js@^2.4.9: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + side-channel@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" @@ -788,20 +888,41 @@ side-channel@^1.0.6: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + toidentifier@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== tslib@^2.6.2: - version "2.8.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.0.tgz#d124c86c3c05a40a91e6fdea4021bd31d377971b" - integrity sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA== + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== type-is@~1.6.18: version "1.6.18" @@ -821,6 +942,11 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + util@^0.12.4: version "0.12.5" resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" @@ -863,6 +989,11 @@ wonka@^6.3.2: resolved "https://registry.yarnpkg.com/wonka/-/wonka-6.3.4.tgz#76eb9316e3d67d7febf4945202b5bdb2db534594" integrity sha512-CjpbqNtBGNAeyNS/9W6q3kSkKE52+FjIj7AkFlLr11s/VWGUu6a2CdYSdGxocIhIVjaW/zchesBQUKPVU69Cqg== +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + yn@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/yn/-/yn-5.0.0.tgz#63fc2e2e0056cf294397eed6ad4a3fbdf707f26f" diff --git a/plugins/orchestrator-backend/package.json b/plugins/orchestrator-backend/package.json index 3af32d137b..2ec07bd49b 100644 --- a/plugins/orchestrator-backend/package.json +++ b/plugins/orchestrator-backend/package.json @@ -4,7 +4,6 @@ "license": "Apache-2.0", "main": "src/index.ts", "types": "src/index.ts", - "private": true, "publishConfig": { "access": "public" }, @@ -20,14 +19,10 @@ }, "exports": { ".": "./src/index.ts", - "./alpha": "./src/alpha.ts", "./package.json": "./package.json" }, "typesVersions": { "*": { - "alpha": [ - "src/alpha.ts" - ], "package.json": [ "package.json" ] @@ -36,7 +31,7 @@ "homepage": "https://red.ht/rhdh", "repository": { "type": "git", - "url": "git+https://github.com/janus-idp/backstage-plugins.git", + "url": "https://github.com/janus-idp/backstage-plugins", "directory": "plugins/orchestrator-backend" }, "bugs": "https://github.com/janus-idp/backstage-plugins/issues", @@ -53,69 +48,60 @@ "dist", "dist-dynamic/*.*", "dist-dynamic/dist/**", - "dist-dynamic/alpha/*", "static" ], "scripts": { "start": "backstage-cli package start", "build": "backstage-cli package build", "tsc": "tsc", - "prettier:check": "prettier --ignore-unknown --check .", - "prettier:fix": "prettier --ignore-unknown --write .", - "lint:check": "backstage-cli package lint", - "lint:fix": "backstage-cli package lint --fix", + "lint": "backstage-cli package lint", "test": "backstage-cli package test --passWithNoTests --coverage", "clean": "backstage-cli package clean", "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", "postversion": "yarn run export-dynamic", - "export-dynamic": "janus-cli package export-dynamic-plugin --no-embed-as-dependencies", - "export-dynamic:clean": "janus-cli package export-dynamic-plugin --no-embed-as-dependencies --clean" + "export-dynamic": "janus-cli package export-dynamic-plugin --no-embed-as-dependencies" }, "dependencies": { - "@backstage/backend-app-api": "^0.8.0", "@backstage/backend-common": "^0.23.3", - "@backstage/backend-dynamic-feature-service": "^0.2.15", + "@backstage/backend-defaults": "^0.4.1", "@backstage/backend-plugin-api": "^0.7.0", "@backstage/backend-tasks": "^0.5.27", "@backstage/catalog-client": "^1.6.5", - "@backstage/config": "^1.2.0", - "@backstage/core-plugin-api": "^1.9.3", "@backstage/errors": "^1.2.4", "@backstage/integration": "^1.13.0", - "@backstage/plugin-auth-node": "^0.4.17", "@backstage/plugin-catalog-node": "^1.12.4", - "@backstage/plugin-events-backend": "^0.3.9", - "@backstage/plugin-events-node": "^0.3.8", "@backstage/plugin-permission-common": "^0.8.0", "@backstage/plugin-permission-node": "^0.8.0", "@backstage/plugin-scaffolder-backend": "^1.23.0", "@backstage/plugin-scaffolder-node": "^0.4.8", - "@backstage/types": "^1.1.1", - "@janus-idp/backstage-plugin-orchestrator-common": "1.16.100", + "@janus-idp/backstage-plugin-orchestrator-common": "*", "@urql/core": "^4.1.4", "ajv-formats": "^2.1.1", "cloudevents": "^8.0.0", "express": "^4.18.2", "express-promise-router": "^4.1.1", "fs-extra": "^10.1.0", + "isomorphic-git": "^1.23.0", "json-schema": "^0.4.0", "moment": "^2.29.4", "openapi-backend": "^5.10.5", "yn": "^5.0.0" }, "devDependencies": { - "prettier": "3.3.3", "@backstage/backend-test-utils": "0.4.4", "@backstage/cli": "0.26.11", "@janus-idp/cli": "1.13.3", + "@backstage/config": "^1.2.0", + "@backstage/core-plugin-api": "^1.9.3", + "@backstage/types": "^1.1.1", "@types/express": "4.17.21", "@types/fs-extra": "11.0.4", "@types/json-schema": "7.0.15" }, "peerDependencies": { - "@janus-idp/backstage-plugin-rbac-common": "1.9.0", - "@janus-idp/backstage-plugin-audit-log-node": "1.4.1" + "@janus-idp/backstage-plugin-audit-log-node": "1.4.1", + "@janus-idp/backstage-plugin-rbac-common": "1.9.0" }, "maintainers": [ "@janus-idp/maintainers-plugins", diff --git a/plugins/orchestrator-backend/src/OrchestratorPlugin.ts b/plugins/orchestrator-backend/src/OrchestratorPlugin.ts index 67b21f323b..1e5927c562 100644 --- a/plugins/orchestrator-backend/src/OrchestratorPlugin.ts +++ b/plugins/orchestrator-backend/src/OrchestratorPlugin.ts @@ -54,10 +54,6 @@ export const orchestratorPlugin = createBackendPlugin({ path: '/health', allow: 'unauthenticated', }); - httpRouter.addAuthPolicy({ - path: '/docs', - allow: 'unauthenticated', - }); }, }); }, diff --git a/plugins/orchestrator-backend/src/alpha.ts b/plugins/orchestrator-backend/src/alpha.ts deleted file mode 100644 index b49b213a08..0000000000 --- a/plugins/orchestrator-backend/src/alpha.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { orchestratorPlugin as default } from './OrchestratorPlugin'; -export { dynamicPluginInstaller } from './dynamic/alpha'; diff --git a/plugins/orchestrator-backend/src/dynamic/alpha.ts b/plugins/orchestrator-backend/src/dynamic/alpha.ts deleted file mode 100644 index 4fa66a7df4..0000000000 --- a/plugins/orchestrator-backend/src/dynamic/alpha.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { BackendDynamicPluginInstaller } from '@backstage/backend-dynamic-feature-service'; - -import { orchestratorPlugin } from '../OrchestratorPlugin'; - -export const dynamicPluginInstaller: BackendDynamicPluginInstaller = { - kind: 'new', - install: () => [orchestratorPlugin()], -}; diff --git a/plugins/orchestrator-backend/src/dynamic/index.ts b/plugins/orchestrator-backend/src/dynamic/index.ts deleted file mode 100644 index 19311fdfd6..0000000000 --- a/plugins/orchestrator-backend/src/dynamic/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { HostDiscovery } from '@backstage/backend-app-api'; -import { BackendDynamicPluginInstaller } from '@backstage/backend-dynamic-feature-service'; -import { CatalogClient } from '@backstage/catalog-client'; - -import { createRouter } from '../routerWrapper'; - -export const dynamicPluginInstaller: BackendDynamicPluginInstaller = { - kind: 'legacy', - router: { - pluginID: 'orchestrator', - createPlugin: async env => { - const catalogApi = new CatalogClient({ - discoveryApi: HostDiscovery.fromConfig(env.config), - }); - return createRouter({ - ...env, - urlReader: env.reader, - catalogApi, - }); - }, - }, -}; diff --git a/plugins/orchestrator-backend/src/helpers/queryBuilder.test.ts b/plugins/orchestrator-backend/src/helpers/queryBuilder.test.ts index bbda70ae37..7e494ac950 100644 --- a/plugins/orchestrator-backend/src/helpers/queryBuilder.test.ts +++ b/plugins/orchestrator-backend/src/helpers/queryBuilder.test.ts @@ -1,58 +1,411 @@ +import { + FieldFilterOperatorEnum, + Filter, + IntrospectionField, + TypeKind, + TypeName, +} from '@janus-idp/backstage-plugin-orchestrator-common'; + import { Pagination } from '../types/pagination'; -import { buildGraphQlQuery } from './queryBuilder'; +import { buildFilterCondition, buildGraphQlQuery } from './queryBuilder'; describe('buildGraphQlQuery', () => { - const queryBody = 'id status'; - const type = 'ProcessInstances'; - const offset = 0; - const limit = 10; - const order = 'asc'; - const sortField = 'name'; - const pagination: Pagination = { - offset, - limit, - order, - sortField, + const defaultTestParams = { + queryBody: 'id status', + type: 'ProcessInstances' as + | 'ProcessDefinitions' + | 'ProcessInstances' + | 'Jobs', + pagination: { + offset: 0, + limit: 10, + order: 'asc', + sortField: 'name', + } as Pagination | undefined, + whereClause: 'version: "1.0"', + }; + + const getPaginationString = (pagination: Pagination | undefined) => { + const paginationOrder = pagination?.order + ? pagination.order.toUpperCase() + : 'ASC'; + if (pagination) { + return `orderBy: {${pagination.sortField}: ${paginationOrder}}, pagination: {limit: ${pagination.limit}, offset: ${pagination.offset}})`; + } + return undefined; + }; + + type TestCase = { + name: string; + params: typeof defaultTestParams; + expectedResult: string; }; - const paginationString = `orderBy: {${sortField}: ${order.toUpperCase()}}, pagination: {limit: ${limit}, offset: ${offset}})`; - const whereClause = 'version: "1.0"'; + const testCases: TestCase[] = [ + { + name: 'should build a basic query without where clause and pagination', + params: { + type: defaultTestParams.type, + queryBody: defaultTestParams.queryBody, + whereClause: '', + pagination: {}, + }, + expectedResult: `{${defaultTestParams.type} {${defaultTestParams.queryBody} } }`, + }, + { + name: 'should build a query with a where clause', + params: { + type: defaultTestParams.type, + queryBody: defaultTestParams.queryBody, + whereClause: defaultTestParams.whereClause, + pagination: {}, + }, + expectedResult: `{${defaultTestParams.type} (where: {${defaultTestParams.whereClause}}) {${defaultTestParams.queryBody} } }`, + }, + { + name: 'should build a query with pagination', + params: { + type: defaultTestParams.type, + queryBody: defaultTestParams.queryBody, + whereClause: '', + pagination: defaultTestParams.pagination, + }, + expectedResult: `{${defaultTestParams.type} (${getPaginationString(defaultTestParams.pagination)} {${defaultTestParams.queryBody} } }`, + }, + { + name: 'should build a query with both where clause and pagination', + params: { + ...defaultTestParams, + }, + expectedResult: `{${defaultTestParams.type} (where: {${defaultTestParams.whereClause}}, ${getPaginationString(defaultTestParams.pagination)} {${defaultTestParams.queryBody} } }`, + }, + ]; - it('should build a basic query without where clause and pagination', () => { - const result = buildGraphQlQuery({ - type, - queryBody, + testCases.forEach(({ name, params, expectedResult }) => { + it(`${name}`, () => { + const result = buildGraphQlQuery(params); + expect(result).toBe(expectedResult); }); - expect(result).toBe(`{${type} {${queryBody} } }`); }); +}); - it('should build a query with a where clause', () => { - const result = buildGraphQlQuery({ - type, - queryBody, - whereClause, - }); - expect(result).toBe(`{${type} (where: {${whereClause}}) {${queryBody} } }`); +describe('column filters', () => { + const createStringIntrospectionField = ( + name: string, + ): IntrospectionField => ({ + name, + type: { + name: TypeName.String, + kind: TypeKind.InputObject, + ofType: null, + }, }); - it('should build a query with pagination', () => { - const result = buildGraphQlQuery({ - type, - queryBody, - pagination, - }); - expect(result).toBe(`{${type} (${paginationString} {${queryBody} } }`); + const createIdIntrospectionField = (name: string): IntrospectionField => ({ + name, + type: { + name: TypeName.Id, + kind: TypeKind.InputObject, + ofType: null, + }, }); - it('should build a query with both where clause and pagination', () => { - const result = buildGraphQlQuery({ - type, - queryBody, - whereClause, - pagination, - }); - expect(result).toBe( - `{${type} (where: {${whereClause}}, ${paginationString} {${queryBody} } }`, + const createFieldFilter = ( + field: string, + operator: FieldFilterOperatorEnum, + value: any, + ): Filter => ({ + field, + operator, + value, + }); + + type FilterTestCase = { + name: string; + introspectionFields: IntrospectionField[]; + filter: Filter | undefined; + expectedResult: string; + }; + describe('empty filter testcases', () => { + const emptyFilterTestCases: FilterTestCase[] = [ + { + name: 'returns empty string when filters are null or undefined', + introspectionFields: [], + filter: undefined, + expectedResult: '', + }, + ]; + emptyFilterTestCases.forEach( + ({ name, introspectionFields, filter, expectedResult }) => { + it(`${name}`, () => { + const result = buildFilterCondition(introspectionFields, filter); + expect(result).toBe(expectedResult); + }); + }, + ); + }); + describe('stringArgument testcases', () => { + const stringTestCases: FilterTestCase[] = [ + { + name: 'returns correct filter for single string field with equal operator', + introspectionFields: [createStringIntrospectionField('name')], + filter: createFieldFilter( + 'name', + FieldFilterOperatorEnum.Eq, + 'Hello World Workflow', + ), + expectedResult: 'name: {equal: "Hello World Workflow"}', + }, + { + name: 'returns correct filter for single string field with like operator', + introspectionFields: [createStringIntrospectionField('name')], + filter: createFieldFilter( + 'name', + FieldFilterOperatorEnum.Like, + 'Hello%', + ), + expectedResult: 'name: {like: "Hello%"}', + }, + { + name: 'returns correct filter for string field with isNull operator (true)', + introspectionFields: [createStringIntrospectionField('name')], + filter: createFieldFilter('name', FieldFilterOperatorEnum.IsNull, true), + expectedResult: 'name: {isNull: true}', + }, + { + name: 'returns correct filter for string field with isNull operator (false)', + introspectionFields: [createStringIntrospectionField('name')], + filter: createFieldFilter( + 'name', + FieldFilterOperatorEnum.IsNull, + false, + ), + expectedResult: 'name: {isNull: false}', + }, + { + name: 'returns correct filter for string field with isNull operator ("true" as string)', + introspectionFields: [createStringIntrospectionField('name')], + filter: createFieldFilter( + 'name', + FieldFilterOperatorEnum.IsNull, + 'True', + ), + expectedResult: 'name: {isNull: true}', + }, + { + name: 'returns correct filter for string field with isNull operator ("false" as string)', + introspectionFields: [createStringIntrospectionField('name')], + filter: createFieldFilter( + 'name', + FieldFilterOperatorEnum.IsNull, + 'FALSE', + ), + expectedResult: 'name: {isNull: false}', + }, + { + name: 'returns correct filter for string field with in operator (single value)', + introspectionFields: [createStringIntrospectionField('name')], + filter: createFieldFilter('name', FieldFilterOperatorEnum.In, [ + 'Test String', + ]), + expectedResult: 'name: {in: ["Test String"]}', + }, + { + name: 'returns correct filter for string field with in operator (multiple values)', + introspectionFields: [createStringIntrospectionField('name')], + filter: createFieldFilter('name', FieldFilterOperatorEnum.In, [ + 'Test String 1', + 'Test String 2', + 'Test String 3', + ]), + expectedResult: + 'name: {in: ["Test String 1", "Test String 2", "Test String 3"]}', + }, + { + name: 'returns correct OR filter for two string fields with equal operator', + introspectionFields: [ + createStringIntrospectionField('name'), + createStringIntrospectionField('processName'), + ], + filter: { + operator: 'OR', + filters: [ + createFieldFilter( + 'name', + FieldFilterOperatorEnum.Eq, + 'Hello World Workflow', + ), + createFieldFilter( + 'processName', + FieldFilterOperatorEnum.Eq, + 'Greeting workflow', + ), + ], + }, + expectedResult: + 'or: {name: {equal: "Hello World Workflow"}, processName: {equal: "Greeting workflow"}}', + }, + { + name: 'returns correct filter for string field with like and isNull operators', + introspectionFields: [createStringIntrospectionField('description')], + filter: { + operator: 'OR', + filters: [ + createFieldFilter( + 'description', + FieldFilterOperatorEnum.Like, + '%Test%', + ), + createFieldFilter( + 'description', + FieldFilterOperatorEnum.IsNull, + true, + ), + ], + }, + expectedResult: + 'or: {description: {like: "%Test%"}, description: {isNull: true}}', + }, + { + name: 'returns correct filter for string field with in, like, equal, and isNull operators', + introspectionFields: [createStringIntrospectionField('name')], + filter: { + operator: 'OR', + filters: [ + createFieldFilter('name', FieldFilterOperatorEnum.In, [ + 'Test String 1', + 'Test String 2', + ]), + createFieldFilter('name', FieldFilterOperatorEnum.Like, '%Test%'), + createFieldFilter( + 'name', + FieldFilterOperatorEnum.Eq, + 'Exact Match', + ), + createFieldFilter('name', FieldFilterOperatorEnum.IsNull, false), + ], + }, + expectedResult: + 'or: {name: {in: ["Test String 1", "Test String 2"]}, name: {like: "%Test%"}, name: {equal: "Exact Match"}, name: {isNull: false}}', + }, + { + name: 'returns correct filter for string field with in, like, equal, and isNull operators', + introspectionFields: [createStringIntrospectionField('name')], + filter: { + operator: 'AND', + filters: [ + createFieldFilter('name', FieldFilterOperatorEnum.In, [ + 'Test String 1', + 'Test String 2', + ]), + createFieldFilter('name', FieldFilterOperatorEnum.Like, '%Test%'), + createFieldFilter( + 'name', + FieldFilterOperatorEnum.Eq, + 'Exact Match', + ), + createFieldFilter('name', FieldFilterOperatorEnum.IsNull, false), + ], + }, + expectedResult: + 'and: {name: {in: ["Test String 1", "Test String 2"]}, name: {like: "%Test%"}, name: {equal: "Exact Match"}, name: {isNull: false}}', + }, + ]; + stringTestCases.forEach( + ({ name, introspectionFields, filter, expectedResult }) => { + it(`${name}`, () => { + const result = buildFilterCondition(introspectionFields, filter); + expect(result).toBe(expectedResult); + }); + }, + ); + }); + + describe('idArgument testcases', () => { + const idTestCases: FilterTestCase[] = [ + { + name: 'returns correct filter for single id field with equal operator', + introspectionFields: [createIdIntrospectionField('id')], + filter: createFieldFilter('id', FieldFilterOperatorEnum.Eq, 'idA'), + expectedResult: 'id: {equal: "idA"}', + }, + { + name: 'returns correct filter for single id field with isNull operator (false as boolean)', + introspectionFields: [createIdIntrospectionField('id')], + filter: createFieldFilter('id', FieldFilterOperatorEnum.IsNull, false), + expectedResult: 'id: {isNull: false}', + }, + { + name: 'returns correct filter for single id field with isNull operator (false as string)', + introspectionFields: [createIdIntrospectionField('id')], + filter: createFieldFilter( + 'id', + FieldFilterOperatorEnum.IsNull, + 'false', + ), + expectedResult: 'id: {isNull: false}', + }, + { + name: 'returns correct filter for single id field with IN operator', + introspectionFields: [createIdIntrospectionField('id')], + filter: createFieldFilter('id', FieldFilterOperatorEnum.In, [ + 'idA', + 'idB', + 'idC', + ]), + expectedResult: 'id: {in: ["idA", "idB", "idC"]}', + }, + { + name: 'returns correct OR filter for multiple id fields with equal, isNull, and IN operators', + introspectionFields: [ + createIdIntrospectionField('processId'), + createIdIntrospectionField('id'), + ], + filter: { + operator: 'OR', + filters: [ + createFieldFilter('id', FieldFilterOperatorEnum.Eq, 'idA'), + createFieldFilter( + 'processId', + FieldFilterOperatorEnum.IsNull, + 'True', + ), + createFieldFilter('id', 'IN', ['idA', 'idB', 'idC']), + ], + }, + expectedResult: + 'or: {id: {equal: "idA"}, processId: {isNull: true}, id: {in: ["idA", "idB", "idC"]}}', + }, + { + name: 'returns correct AND filter for multiple id fields with equal, isNull, and IN operators', + introspectionFields: [ + createIdIntrospectionField('processId'), + createIdIntrospectionField('id'), + ], + filter: { + operator: 'AND', + filters: [ + createFieldFilter('id', FieldFilterOperatorEnum.Eq, 'idA'), + createFieldFilter( + 'processId', + FieldFilterOperatorEnum.IsNull, + 'True', + ), + createFieldFilter('id', 'IN', ['idA', 'idB', 'idC']), + ], + }, + expectedResult: + 'and: {id: {equal: "idA"}, processId: {isNull: true}, id: {in: ["idA", "idB", "idC"]}}', + }, + ]; + + idTestCases.forEach( + ({ name, introspectionFields, filter, expectedResult }) => { + it(`${name}`, () => { + const result = buildFilterCondition(introspectionFields, filter); + expect(result).toBe(expectedResult); + }); + }, ); }); }); diff --git a/plugins/orchestrator-backend/src/helpers/queryBuilder.ts b/plugins/orchestrator-backend/src/helpers/queryBuilder.ts index 181a66734e..03163a93e1 100644 --- a/plugins/orchestrator-backend/src/helpers/queryBuilder.ts +++ b/plugins/orchestrator-backend/src/helpers/queryBuilder.ts @@ -1,4 +1,10 @@ -import { FilterInfo } from '@janus-idp/backstage-plugin-orchestrator-common'; +import { + FieldFilterOperatorEnum, + Filter, + IntrospectionField, + LogicalFilter, + TypeName, +} from '@janus-idp/backstage-plugin-orchestrator-common'; import { Pagination } from '../types/pagination'; @@ -53,8 +59,158 @@ function buildPaginationClause(pagination?: Pagination): string { return parts.join(', '); } -export function buildFilterCondition(filter?: FilterInfo): string { - return filter?.fieldName && filter?.operator && filter?.fieldValue - ? `${filter?.fieldName}:{ ${filter?.operator}: ${filter?.fieldValue}}` - : ''; +function isLogicalFilter(filter: Filter): filter is LogicalFilter { + return (filter as LogicalFilter).filters !== undefined; +} + +export function buildFilterCondition( + introspection: IntrospectionField[], + filters?: Filter, +): string { + if (!filters) { + return ''; + } + + if (isLogicalFilter(filters)) { + if (filters.operator) { + const subClauses = + filters.filters.map(f => buildFilterCondition(introspection, f)) ?? []; + const joinedSubClauses = `${filters.operator.toLowerCase()}: {${subClauses.join(', ')}}`; + return joinedSubClauses; + } + } + + if (!isOperatorSupported(filters.operator)) { + throw new Error(`Unsopported operator ${filters.operator}`); + } + + const fieldDef = introspection.find(f => f.name === filters.field); + if (!fieldDef) { + throw new Error(`Can't find field "${filters.field}" definition`); + } + + if (!isOperatorAllowedForField(filters.operator, fieldDef)) { + throw new Error(`Unsupported field type ${fieldDef.type.name}`); + } + + let value = filters.value; + + if (filters.operator === FieldFilterOperatorEnum.IsNull) { + return `${filters.field}: {${getGraphQLOperator(filters.operator)}: ${convertToBoolean(value)}}`; + } + + if (Array.isArray(value)) { + value = `[${value.map(v => formatValue(filters.field, v, fieldDef)).join(', ')}]`; + } else { + value = formatValue(filters.field, value, fieldDef); + } + + if ( + filters.operator === FieldFilterOperatorEnum.Eq || + filters.operator === FieldFilterOperatorEnum.Like || + filters.operator === FieldFilterOperatorEnum.In + ) { + return `${filters.field}: {${getGraphQLOperator(filters.operator)}: ${value}}`; + } + + throw new Error(`Can't build filter condition`); +} + +function isOperatorSupported(operator: FieldFilterOperatorEnum): boolean { + return ( + operator === FieldFilterOperatorEnum.Eq || + operator === FieldFilterOperatorEnum.Like || + operator === FieldFilterOperatorEnum.In || + operator === FieldFilterOperatorEnum.IsNull + ); +} + +function isFieldFilterSupported(fieldDef: IntrospectionField): boolean { + return fieldDef?.type.name === TypeName.String; +} + +function isOperatorAllowedForField( + operator: FieldFilterOperatorEnum, + fieldDef: IntrospectionField, +): boolean { + const allowedOperators: Record = { + [TypeName.String]: [ + FieldFilterOperatorEnum.In, + FieldFilterOperatorEnum.Like, + FieldFilterOperatorEnum.IsNull, + FieldFilterOperatorEnum.Eq, + ], + [TypeName.Id]: [ + FieldFilterOperatorEnum.In, + FieldFilterOperatorEnum.IsNull, + FieldFilterOperatorEnum.Eq, + ], + [TypeName.Date]: [], + [TypeName.StringArray]: [], + }; + const allowedForType = allowedOperators[fieldDef.type.name]; + return allowedForType ? allowedForType.includes(operator) : false; +} + +function convertToBoolean(value: any): boolean { + if (typeof value === 'boolean') { + return value; + } + if (typeof value === 'string') { + return value.toLowerCase() === 'true'; + } + if (typeof value === 'number') { + return value === 1; + } + return false; // Default to false for unsupported types +} + +function formatValue( + fieldName: string, + fieldValue: any, + fieldDef: IntrospectionField, +): string { + if (!isFieldFilterSupported) { + throw new Error(`Unsupported field type ${fieldDef.type.name}`); + } + + if ( + fieldDef.type.name === TypeName.String || + fieldDef.type.name === TypeName.Id + ) { + return `"${fieldValue}"`; + } + throw new Error( + `Failed to format value for ${fieldName} ${fieldValue} with type ${fieldDef.type.name}`, + ); +} + +function getGraphQLOperator(operator: FieldFilterOperatorEnum): string { + switch (operator) { + case 'EQ': + return 'equal'; + case 'LIKE': + return 'like'; + case 'IN': + return 'in'; + case 'IS_NULL': + return 'isNull'; + // case 'GT': + // // return "greaterThan" + // case 'GTE': + // return "greaterThanEqual" + // case 'LT': + // return "lessThan" + // case 'LTE': + // return "lessThanEqual" + // case 'CONTAINS': + // return "contains" + // case 'CONTAINS_ALL': + // case 'CONTAINS_ANY': + // case 'BETWEEN': + // case 'FROM': + // case 'TO': + default: + throw new Error(`Operation "${operator}" not supported`); + } } diff --git a/plugins/orchestrator-backend/src/index.ts b/plugins/orchestrator-backend/src/index.ts index e5b4102aca..b8e1645d7b 100644 --- a/plugins/orchestrator-backend/src/index.ts +++ b/plugins/orchestrator-backend/src/index.ts @@ -1,3 +1 @@ -export * from './dynamic/index'; - -export { createRouter } from './routerWrapper'; +export { orchestratorPlugin as default } from './plugin'; diff --git a/plugins/orchestrator-backend/src/plugin.ts b/plugins/orchestrator-backend/src/plugin.ts new file mode 100644 index 0000000000..f9ddc32452 --- /dev/null +++ b/plugins/orchestrator-backend/src/plugin.ts @@ -0,0 +1,44 @@ +import { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha'; + +import { createRouter } from './routerWrapper'; + +export const orchestratorPlugin = createBackendPlugin({ + pluginId: 'orchestrator', + register(env) { + env.registerInit({ + deps: { + logger: coreServices.logger, + config: coreServices.rootConfig, + discovery: coreServices.discovery, + catalogApi: catalogServiceRef, + urlReader: coreServices.urlReader, + permissions: coreServices.permissions, + scheduler: coreServices.scheduler, + auth: coreServices.auth, + httpAuth: coreServices.httpAuth, + http: coreServices.httpRouter, + }, + async init(props) { + const { http } = props; + const router = await createRouter(props); + http.use(router); + http.addAuthPolicy({ + path: '/health', + allow: 'unauthenticated', + }); + http.addAuthPolicy({ + path: '/static', + allow: 'unauthenticated', + }); + http.addAuthPolicy({ + path: '/docs', + allow: 'unauthenticated', + }); + }, + }); + }, +}); diff --git a/plugins/orchestrator-backend/src/routerWrapper/index.ts b/plugins/orchestrator-backend/src/routerWrapper/index.ts index 8e13103121..601619d4fe 100644 --- a/plugins/orchestrator-backend/src/routerWrapper/index.ts +++ b/plugins/orchestrator-backend/src/routerWrapper/index.ts @@ -1,33 +1,35 @@ -import { createLegacyAuthAdapters, UrlReader } from '@backstage/backend-common'; -import { +import type { AuthService, DiscoveryService, HttpAuthService, LoggerService, PermissionsService, + SchedulerService, + UrlReaderService, } from '@backstage/backend-plugin-api'; -import { PluginTaskScheduler } from '@backstage/backend-tasks'; -import { CatalogApi } from '@backstage/catalog-client'; -import { Config } from '@backstage/config'; +import type { CatalogApi } from '@backstage/catalog-client'; +import type { Config } from '@backstage/config'; import express from 'express'; import { DevModeService } from '../service/DevModeService'; import { createBackendRouter } from '../service/router'; -export interface RouterArgs { +export interface RouterOptions { config: Config; logger: LoggerService; discovery: DiscoveryService; catalogApi: CatalogApi; - urlReader: UrlReader; - scheduler: PluginTaskScheduler; + urlReader: UrlReaderService; + scheduler: SchedulerService; permissions: PermissionsService; - httpAuth?: HttpAuthService; - auth?: AuthService; + httpAuth: HttpAuthService; + auth: AuthService; } -export async function createRouter(args: RouterArgs): Promise { +export async function createRouter( + args: RouterOptions, +): Promise { const autoStartDevMode = args.config.getOptionalBoolean( 'orchestrator.sonataFlowService.autoStart', @@ -43,11 +45,6 @@ export async function createRouter(args: RouterArgs): Promise { } } - const { auth, httpAuth } = createLegacyAuthAdapters({ - httpAuth: args.httpAuth, - discovery: args.discovery, - auth: args.auth, - }); return await createBackendRouter({ config: args.config, logger: args.logger, @@ -56,7 +53,7 @@ export async function createRouter(args: RouterArgs): Promise { urlReader: args.urlReader, scheduler: args.scheduler, permissions: args.permissions, - httpAuth: httpAuth, - auth: auth, + httpAuth: args.httpAuth, + auth: args.auth, }); } diff --git a/plugins/orchestrator-backend/src/run.ts b/plugins/orchestrator-backend/src/run.ts deleted file mode 100644 index 997a38d5cd..0000000000 --- a/plugins/orchestrator-backend/src/run.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - getRootLogger, - HostDiscovery, - loadBackendConfig, - UrlReaders, -} from '@backstage/backend-common'; -import { TaskScheduler } from '@backstage/backend-tasks'; -import { CatalogClient } from '@backstage/catalog-client'; - -import yn from 'yn'; - -import { startStandaloneServer } from '../dev'; - -const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7007; -const enableCors = yn(process.env.PLUGIN_CORS, { default: false }); -const logger = getRootLogger(); -const config = await loadBackendConfig({ logger, argv: process.argv }); -const discovery = HostDiscovery.fromConfig(config); -const scheduler = TaskScheduler.fromConfig(config).forPlugin('orchestrator'); -const catalogApi = new CatalogClient({ - discoveryApi: HostDiscovery.fromConfig(config), -}); -const urlReader = UrlReaders.default({ logger, config }); - -startStandaloneServer({ - port, - enableCors, - logger, - config, - discovery, - catalogApi, - urlReader, - scheduler, -}).catch(err => { - logger.error(err); - process.exit(1); -}); - -process.on('SIGINT', () => { - logger.info('CTRL+C pressed; exiting.'); - process.exit(0); -}); diff --git a/plugins/orchestrator-backend/src/service/DataIndexService.test.ts b/plugins/orchestrator-backend/src/service/DataIndexService.test.ts index 20c66952da..4fceb218ea 100644 --- a/plugins/orchestrator-backend/src/service/DataIndexService.test.ts +++ b/plugins/orchestrator-backend/src/service/DataIndexService.test.ts @@ -3,14 +3,25 @@ import { LoggerService } from '@backstage/backend-plugin-api'; import { Client, OperationResult } from '@urql/core'; import { - FilterInfo, + FieldFilterOperatorEnum, + LogicalFilter, NodeInstance, ProcessInstance, + TypeKind, + TypeName, WorkflowInfo, } from '@janus-idp/backstage-plugin-orchestrator-common'; import * as graphqlUtils from '../helpers/queryBuilder'; import { Pagination } from '../types/pagination'; +import { + mockProcessDefinitionArguments, + mockProcessDefinitionIntrospection, +} from './__fixtures__/mockProcessDefinitionArgumentsData'; +import { + mockProcessInstanceArguments, + mockProcessInstanceIntrospection, +} from './__fixtures__/mockProcessInstanceArgumentsData'; import { DataIndexService } from './DataIndexService'; jest.mock('../helpers/queryBuilder', () => { @@ -37,6 +48,13 @@ const mockOperationResult = (data: T, error?: any): OperationResult => ({ stale: false, }); +const mockWfInfos: WorkflowInfo[] = [ + { + id: '9fa2a881-c932-468d-83a9-687b9f1e62a7', + nodes: [createNodeObject('A'), createNodeObject('B')], + }, +]; + const createQueryArgs = ( type: 'ProcessDefinitions' | 'ProcessInstances' | 'Jobs', queryBody: string, @@ -49,24 +67,115 @@ const createQueryArgs = ( pagination, }); +describe('initInputArgs', () => { + type MockableClient = Pick; + const createMockClient = (): jest.Mocked => ({ + query: jest.fn(), + }); + + let loggerMock: LoggerService; + let dataIndexService: DataIndexService; + let mockClient: jest.Mocked; + + beforeEach(() => { + jest.resetAllMocks(); + jest.clearAllMocks(); + // Create a new mock client for each test + mockClient = createMockClient(); + (Client as jest.MockedClass).mockImplementation( + () => mockClient as unknown as Client, + ); + + loggerMock = { + info: jest.fn(), + debug: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + child: jest.fn(), + }; + mockClient.query.mockResolvedValueOnce( + mockOperationResult(mockProcessDefinitionArguments), + ); + dataIndexService = new DataIndexService('fakeUrl', loggerMock); + }); + + it('ProcessDefinition', async () => { + const processDefinitionArguments = + await dataIndexService.initInputProcessDefinitionArgs(); + + expect(mockClient.query).toHaveBeenCalledTimes(1); + expect(mockClient.query).toHaveBeenCalledWith( + dataIndexService.graphQLArgumentQuery('ProcessDefinition'), + {}, + ); + + expect(processDefinitionArguments).toBeDefined(); + expect( + processDefinitionArguments.every( + val => !['and', 'or', 'not'].includes(val.name), + ), + ).toBe(true); + expect(processDefinitionArguments).toHaveLength(3); + expect( + processDefinitionArguments.some( + obj => + obj.name === 'id' && + obj.type.kind === TypeKind.InputObject && + obj.type.name === TypeName.String, + ), + ).toBe(true); + expect( + processDefinitionArguments.some( + obj => + obj.name === 'name' && + obj.type.kind === TypeKind.InputObject && + obj.type.name === TypeName.String, + ), + ).toBe(true); + expect( + processDefinitionArguments.some( + obj => + obj.name === 'version' && + obj.type.kind === TypeKind.InputObject && + obj.type.name === TypeName.String, + ), + ).toBe(true); + }); +}); + describe('fetchWorkflowInfos', () => { let loggerMock: LoggerService; let buildFilterConditionSpy: any; - let buildGraphQlQuerySpy: any; + let buildGraphQlQuerySpy: jest.SpyInstance; let dataIndexService: DataIndexService; let mockClient: jest.Mocked; const definitionIds = ['id1', 'id2']; - const processDefinitions = [{ id: 'def1' }, { id: 'def2' }]; const queryBody = 'id, name, version, type, endpoint, serviceUrl, source'; const pagination = { limit: 10, offset: 0, order: 'ASC', sortField: 'name' }; - const filter: FilterInfo = { - fieldName: 'foo', - operator: 'equal', - fieldValue: 'bar', + + const filterString = + 'or: {name: {equal: "Hello World Workflow"}, id: {equal: "yamlgreet"}}'; + + const helloWorldFilter = { + field: 'name', + operator: FieldFilterOperatorEnum.Eq, + value: 'Hello World Workflow', }; - const filterClause = 'foo:{ equal: bar}'; + const greetingFilter = { + field: 'id', + operator: FieldFilterOperatorEnum.Eq, + value: 'yamlgreet', + }; + + const logicalFilter: LogicalFilter = { + operator: 'OR', + filters: [helloWorldFilter, greetingFilter], + }; + beforeEach(() => { + jest.clearAllMocks(); + mockClient = { query: jest.fn(), } as any; @@ -80,52 +189,47 @@ describe('fetchWorkflowInfos', () => { warn: jest.fn(), child: jest.fn(), }; + dataIndexService = new DataIndexService('fakeUrl', loggerMock); // Set up spies on the graphql utility functions buildGraphQlQuerySpy = jest.spyOn(graphqlUtils, 'buildGraphQlQuery'); buildFilterConditionSpy = jest.spyOn(graphqlUtils, 'buildFilterCondition'); - - // Clear mocks before each test - jest.clearAllMocks(); }); it('should fetch workflow infos with no parameters', async () => { // Given - - const mockQueryResult = { ProcessDefinitions: processDefinitions }; + const mockQueryResult = { + ProcessDefinitions: mockWfInfos, + }; mockClient.query.mockResolvedValueOnce( mockOperationResult(mockQueryResult), ); - const expectedQueryArgs = createQueryArgs( - 'ProcessDefinitions', - queryBody, - '', - ); + const expectedQueryArgs = createQueryArgs('ProcessDefinitions', queryBody); // When const result = await dataIndexService.fetchWorkflowInfos({}); - // Then + expect(result).toBeDefined(); + expect(result).toBe(mockQueryResult.ProcessDefinitions); + expect(buildFilterConditionSpy).not.toHaveBeenCalled(); expect(buildGraphQlQuerySpy).toHaveBeenCalledTimes(1); expect(buildGraphQlQuerySpy).toHaveBeenCalledWith({ type: 'ProcessDefinitions', queryBody, }); - expect(buildFilterConditionSpy).toHaveBeenCalledTimes(1); - expect(buildFilterConditionSpy).toHaveBeenCalledWith(undefined); expect(mockClient.query).toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalledWith( graphqlUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); - expect(result).toBeDefined(); - expect(result).toBe(mockQueryResult.ProcessDefinitions); }); it('should fetch workflow infos with definitionIds', async () => { // Given const whereClause = `id: {in: ${JSON.stringify(definitionIds)}}`; - const mockQueryResult = { ProcessDefinitions: processDefinitions }; + const mockQueryResult = { + ProcessDefinitions: mockWfInfos, + }; mockClient.query.mockResolvedValueOnce( mockOperationResult(mockQueryResult), ); @@ -141,26 +245,27 @@ describe('fetchWorkflowInfos', () => { }); // Then + expect(result).toBeDefined(); + expect(result).toBe(mockQueryResult.ProcessDefinitions); expect(buildGraphQlQuerySpy).toHaveBeenCalledTimes(1); expect(buildGraphQlQuerySpy).toHaveBeenCalledWith({ type: 'ProcessDefinitions', queryBody, whereClause, }); - expect(buildFilterConditionSpy).toHaveBeenCalledTimes(1); - expect(buildFilterConditionSpy).toHaveBeenCalledWith(undefined); + expect(buildFilterConditionSpy).not.toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalledWith( graphqlUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); - expect(result).toBeDefined(); - expect(result).toBe(mockQueryResult.ProcessDefinitions); }); it('should fetch workflow infos with definitionIds and pagination', async () => { // Given - const mockQueryResult = { ProcessDefinitions: processDefinitions }; + const mockQueryResult = { + ProcessDefinitions: mockWfInfos, + }; mockClient.query.mockResolvedValueOnce( mockOperationResult(mockQueryResult), ); @@ -178,6 +283,8 @@ describe('fetchWorkflowInfos', () => { }); // Then + expect(result).toBeDefined(); + expect(result).toBe(mockQueryResult.ProcessDefinitions); expect(buildGraphQlQuerySpy).toHaveBeenCalledTimes(1); expect(buildGraphQlQuerySpy).toHaveBeenCalledWith({ type: 'ProcessDefinitions', @@ -185,72 +292,85 @@ describe('fetchWorkflowInfos', () => { whereClause: `id: {in: ${JSON.stringify(definitionIds)}}`, pagination, }); - expect(buildFilterConditionSpy).toHaveBeenCalledTimes(1); - expect(buildFilterConditionSpy).toHaveBeenCalledWith(undefined); expect(mockClient.query).toHaveBeenCalledTimes(1); expect(mockClient.query).toHaveBeenCalledWith( graphqlUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); - expect(result).toBeDefined(); - expect(result).toBe(mockQueryResult.ProcessDefinitions); + expect(buildFilterConditionSpy).not.toHaveBeenCalled(); }); it('should fetch workflow infos with only filter', async () => { // Given - const mockQueryResult = { ProcessDefinitions: processDefinitions }; - mockClient.query.mockResolvedValueOnce( - mockOperationResult(mockQueryResult), - ); + const mockQueryResult = { + ProcessDefinitions: mockWfInfos, + }; + mockClient.query + .mockResolvedValueOnce( + mockOperationResult(mockProcessDefinitionArguments), + ) + .mockResolvedValueOnce(mockOperationResult(mockQueryResult)); const expectedQueryArgs = createQueryArgs( 'ProcessDefinitions', queryBody, - filterClause, + filterString, ); + // When const result = await dataIndexService.fetchWorkflowInfos({ - filter: filter, + filter: logicalFilter, }); // Then + expect(result).toBeDefined(); + expect(result).toBe(mockQueryResult.ProcessDefinitions); + expect(buildGraphQlQuerySpy).toHaveBeenCalledTimes(1); expect(buildGraphQlQuerySpy).toHaveBeenCalledWith({ type: 'ProcessDefinitions', - queryBody: 'id, name, version, type, endpoint, serviceUrl, source', - whereClause: 'foo:{ equal: bar}', + queryBody, + whereClause: filterString, }); expect(buildFilterConditionSpy).toHaveBeenCalledTimes(1); - expect(buildFilterConditionSpy).toHaveBeenCalledWith(filter); - expect(mockClient.query).toHaveBeenCalledTimes(1); + expect(buildFilterConditionSpy).toHaveBeenCalledWith( + mockProcessDefinitionIntrospection, + logicalFilter, + ); + expect(mockClient.query).toHaveBeenCalledTimes(2); expect(mockClient.query).toHaveBeenCalledWith( graphqlUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); - expect(result).toBeDefined(); - expect(result).toBe(mockQueryResult.ProcessDefinitions); }); it('should fetch workflow infos with definitionIds and filter', async () => { // Given - const whereClause = `and: [{id: {in: ${JSON.stringify(definitionIds)}}}, {${filterClause}}]`; - const mockQueryResult = { ProcessDefinitions: processDefinitions }; - mockClient.query.mockResolvedValueOnce( - mockOperationResult(mockQueryResult), - ); + const whereClause = `and: [{id: {in: ${JSON.stringify(definitionIds)}}}, {${filterString}}]`; + // Given + const mockQueryResult = { + ProcessDefinitions: mockWfInfos, + }; + mockClient.query + .mockResolvedValueOnce( + mockOperationResult(mockProcessDefinitionArguments), + ) + .mockResolvedValueOnce(mockOperationResult(mockQueryResult)); const expectedQueryArgs = createQueryArgs( 'ProcessDefinitions', queryBody, whereClause, ); + // When const result = await dataIndexService.fetchWorkflowInfos({ definitionIds, - filter, + filter: logicalFilter, }); // Then + expect(buildGraphQlQuerySpy).toHaveBeenCalledTimes(1); expect(buildGraphQlQuerySpy).toHaveBeenCalledWith({ type: 'ProcessDefinitions', @@ -258,8 +378,11 @@ describe('fetchWorkflowInfos', () => { whereClause, }); expect(buildFilterConditionSpy).toHaveBeenCalledTimes(1); - expect(buildFilterConditionSpy).toHaveBeenCalledWith(filter); - expect(mockClient.query).toHaveBeenCalledTimes(1); + expect(buildFilterConditionSpy).toHaveBeenCalledWith( + mockProcessDefinitionIntrospection, + logicalFilter, + ); + expect(mockClient.query).toHaveBeenCalledTimes(2); expect(mockClient.query).toHaveBeenCalledWith( graphqlUtils.buildGraphQlQuery(expectedQueryArgs), {}, @@ -270,11 +393,16 @@ describe('fetchWorkflowInfos', () => { it('should fetch workflow infos with definitionIds, pagination, and filter', async () => { // Given - const whereClause = `and: [{id: {in: ${JSON.stringify(definitionIds)}}}, {${filterClause}}]`; - const mockQueryResult = { ProcessDefinitions: processDefinitions }; - mockClient.query.mockResolvedValueOnce( - mockOperationResult(mockQueryResult), - ); + const whereClause = `and: [{id: {in: ${JSON.stringify(definitionIds)}}}, {${filterString}}]`; + // Given + const mockQueryResult = { + ProcessDefinitions: mockWfInfos, + }; + mockClient.query + .mockResolvedValueOnce( + mockOperationResult(mockProcessDefinitionArguments), + ) + .mockResolvedValueOnce(mockOperationResult(mockQueryResult)); const expectedQueryArgs = createQueryArgs( 'ProcessDefinitions', @@ -286,11 +414,17 @@ describe('fetchWorkflowInfos', () => { const result = await dataIndexService.fetchWorkflowInfos({ definitionIds, pagination, - filter, + filter: logicalFilter, }); // Then - expect(buildGraphQlQuerySpy).toHaveBeenCalledTimes(1); + + expect(mockClient.query).toHaveBeenCalledTimes(2); + expect(mockClient.query).toHaveBeenCalledWith( + graphqlUtils.buildGraphQlQuery(expectedQueryArgs), + {}, + ); + expect(buildGraphQlQuerySpy).toHaveBeenCalledTimes(2); expect(buildGraphQlQuerySpy).toHaveBeenCalledWith({ type: 'ProcessDefinitions', queryBody, @@ -298,11 +432,9 @@ describe('fetchWorkflowInfos', () => { pagination, }); expect(buildFilterConditionSpy).toHaveBeenCalledTimes(1); - expect(buildFilterConditionSpy).toHaveBeenCalledWith(filter); - expect(mockClient.query).toHaveBeenCalledTimes(1); - expect(mockClient.query).toHaveBeenCalledWith( - graphqlUtils.buildGraphQlQuery(expectedQueryArgs), - {}, + expect(buildFilterConditionSpy).toHaveBeenCalledWith( + mockProcessDefinitionIntrospection, + logicalFilter, ); expect(result).toBeDefined(); expect(result).toBe(mockQueryResult.ProcessDefinitions); @@ -312,36 +444,52 @@ describe('fetchInstances', () => { let loggerMock: LoggerService; let buildFilterConditionSpy: any; let buildGraphQlQuerySpy: any; + let mockClient: jest.Mocked; let dataIndexService: DataIndexService; - let mockClient: jest.Mocked; - const definitionIds = ['id1', 'id2']; - const processInstances: ProcessInstance[] = [ + const definitionIds = ['id', 'name']; + const pagination = { limit: 10, offset: 0, order: 'ASC', sortField: 'name' }; + + const processIdNotNullCondition = 'processId: {isNull: false}'; + const processIdDefinitions = `processId: {in: ${JSON.stringify(definitionIds)}`; + const queryBody = + 'id, processName, processId, businessKey, state, start, end, nodes { id }, variables, parentProcessInstance {id, processName, businessKey}'; + + const mockProcessInstances: ProcessInstance[] = [ { - id: 'def1', + id: 'id', processId: 'processId1', endpoint: 'endpoint1', nodes: [createNodeObject('A'), createNodeObject('B')], }, { - id: 'def2', + id: 'name', processId: 'processId2', endpoint: 'endpoint2', - nodes: [createNodeObject('A'), createNodeObject('C')], + nodes: [createNodeObject('C'), createNodeObject('D')], }, ]; - const pagination = { limit: 10, offset: 0, order: 'ASC', sortField: 'name' }; - const filter: FilterInfo = { - fieldName: 'foo', - operator: 'equal', - fieldValue: 'bar', + + const filterString = + 'or: {processId: {equal: "processId1"}, processName: {like: "processName%"}}'; + + const procName1Filter = { + field: 'processName', + operator: FieldFilterOperatorEnum.Like, + value: 'processName%', }; - const filterClause = 'foo:{ equal: bar}'; - const processIdNotNullCondition = 'processId: {isNull: false}'; - const processIdDefinitions = `processId: {in: ${JSON.stringify(definitionIds)}`; - const queryBody = - 'id, processName, processId, businessKey, state, start, end, nodes { id }, variables, parentProcessInstance {id, processName, businessKey}'; + const procId1Filter = { + field: 'processId', + operator: FieldFilterOperatorEnum.Eq, + value: 'processId1', + }; + + const logicalFilter: LogicalFilter = { + operator: 'OR', + filters: [procId1Filter, procName1Filter], + }; + const mockQueryResult = { ProcessInstances: mockProcessInstances }; beforeEach(() => { mockClient = { @@ -349,6 +497,7 @@ describe('fetchInstances', () => { } as any; (Client as jest.Mock).mockImplementation(() => mockClient); + const wfInfo: WorkflowInfo = { id: 'wfinfo1', source: 'workflow info source', @@ -374,7 +523,6 @@ describe('fetchInstances', () => { it('should fetch instances with no parameters', async () => { // Given const whereClause = processIdNotNullCondition; - const mockQueryResult = { ProcessInstances: processInstances }; mockClient.query.mockResolvedValueOnce( mockOperationResult(mockQueryResult), ); @@ -389,28 +537,27 @@ describe('fetchInstances', () => { const result = await dataIndexService.fetchInstances({}); // Then + expect(result).toBeDefined(); + expect(result).toStrictEqual(mockQueryResult.ProcessInstances); + expect(buildGraphQlQuerySpy).toHaveBeenCalledTimes(1); expect(buildGraphQlQuerySpy).toHaveBeenCalledWith({ type: 'ProcessInstances', queryBody, whereClause, }); - expect(buildFilterConditionSpy).toHaveBeenCalledTimes(1); - expect(buildFilterConditionSpy).toHaveBeenCalledWith(undefined); + expect(buildFilterConditionSpy).not.toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalledWith( graphqlUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); - expect(result).toBeDefined(); - expect(result).toStrictEqual(mockQueryResult.ProcessInstances); }); it('should fetch instances with definitionIds', async () => { // Given const whereClause = `and: [{${processIdNotNullCondition}}, {${processIdDefinitions}}}]`; - const mockQueryResult = { ProcessInstances: processInstances }; mockClient.query.mockResolvedValueOnce( mockOperationResult(mockQueryResult), ); @@ -433,8 +580,7 @@ describe('fetchInstances', () => { whereClause, pagination: undefined, }); - expect(buildFilterConditionSpy).toHaveBeenCalledTimes(1); - expect(buildFilterConditionSpy).toHaveBeenCalledWith(undefined); + expect(buildFilterConditionSpy).not.toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalledWith( graphqlUtils.buildGraphQlQuery(expectedQueryArgs), @@ -447,7 +593,6 @@ describe('fetchInstances', () => { it('should fetch instances with definitionIds and pagination', async () => { // Given const whereClause = `and: [{${processIdNotNullCondition}}, {${processIdDefinitions}}}]`; - const mockQueryResult = { ProcessInstances: processInstances }; mockClient.query.mockResolvedValueOnce( mockOperationResult(mockQueryResult), ); @@ -466,6 +611,9 @@ describe('fetchInstances', () => { }); // Then + expect(result).toBeDefined(); + expect(result).toStrictEqual(mockQueryResult.ProcessInstances); + expect(buildGraphQlQuerySpy).toHaveBeenCalledTimes(1); expect(buildGraphQlQuerySpy).toHaveBeenCalledWith({ type: 'ProcessInstances', @@ -473,24 +621,20 @@ describe('fetchInstances', () => { whereClause, pagination, }); - expect(buildFilterConditionSpy).toHaveBeenCalledTimes(1); - expect(buildFilterConditionSpy).toHaveBeenCalledWith(undefined); + expect(buildFilterConditionSpy).not.toHaveBeenCalled(); expect(mockClient.query).toHaveBeenCalledTimes(1); expect(mockClient.query).toHaveBeenCalledWith( graphqlUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); - expect(result).toBeDefined(); - expect(result).toStrictEqual(mockQueryResult.ProcessInstances); }); it('should fetch instances with only filter', async () => { // Given - const whereClause = `and: [{${processIdNotNullCondition}}, {${filterClause}}]`; - const mockQueryResult = { ProcessInstances: processInstances }; - mockClient.query.mockResolvedValueOnce( - mockOperationResult(mockQueryResult), - ); + const whereClause = `and: [{${processIdNotNullCondition}}, {${filterString}}]`; + mockClient.query + .mockResolvedValueOnce(mockOperationResult(mockProcessInstanceArguments)) + .mockResolvedValueOnce(mockOperationResult(mockQueryResult)); const expectedQueryArgs = createQueryArgs( 'ProcessInstances', @@ -499,10 +643,12 @@ describe('fetchInstances', () => { ); // When const result = await dataIndexService.fetchInstances({ - filter: filter, + filter: logicalFilter, }); // Then + expect(result).toBeDefined(); + expect(result).toStrictEqual(mockQueryResult.ProcessInstances); expect(buildGraphQlQuerySpy).toHaveBeenCalledTimes(1); expect(buildGraphQlQuerySpy).toHaveBeenCalledWith({ type: 'ProcessInstances', @@ -510,24 +656,23 @@ describe('fetchInstances', () => { whereClause, }); expect(buildFilterConditionSpy).toHaveBeenCalledTimes(1); - expect(buildFilterConditionSpy).toHaveBeenCalledWith(filter); - expect(mockClient.query).toHaveBeenCalledTimes(1); + expect(buildFilterConditionSpy).toHaveBeenCalledWith( + mockProcessInstanceIntrospection, + logicalFilter, + ); + expect(mockClient.query).toHaveBeenCalledTimes(2); expect(mockClient.query).toHaveBeenCalledWith( graphqlUtils.buildGraphQlQuery(expectedQueryArgs), {}, ); - expect(result).toBeDefined(); - expect(result).toStrictEqual(mockQueryResult.ProcessInstances); }); it('should fetch instances with definitionIds and filter', async () => { // Given - const whereClause = `and: [{${processIdNotNullCondition}}, {${processIdDefinitions}}}, {${filterClause}}]`; - const mockQueryResult = { ProcessInstances: processInstances }; - mockClient.query.mockResolvedValueOnce( - mockOperationResult(mockQueryResult), - ); - + const whereClause = `and: [{${processIdNotNullCondition}}, {${processIdDefinitions}}}, {${filterString}}]`; + mockClient.query + .mockResolvedValueOnce(mockOperationResult(mockProcessInstanceArguments)) + .mockResolvedValueOnce(mockOperationResult(mockQueryResult)); const expectedQueryArgs = createQueryArgs( 'ProcessInstances', queryBody, @@ -536,7 +681,7 @@ describe('fetchInstances', () => { // When const result = await dataIndexService.fetchInstances({ definitionIds, - filter, + filter: logicalFilter, }); // Then @@ -547,8 +692,11 @@ describe('fetchInstances', () => { whereClause, }); expect(buildFilterConditionSpy).toHaveBeenCalledTimes(1); - expect(buildFilterConditionSpy).toHaveBeenCalledWith(filter); - expect(mockClient.query).toHaveBeenCalledTimes(1); + expect(buildFilterConditionSpy).toHaveBeenCalledWith( + mockProcessInstanceIntrospection, + logicalFilter, + ); + expect(mockClient.query).toHaveBeenCalledTimes(2); expect(mockClient.query).toHaveBeenCalledWith( graphqlUtils.buildGraphQlQuery(expectedQueryArgs), {}, @@ -559,12 +707,10 @@ describe('fetchInstances', () => { it('should fetch instances with definitionIds, pagination, and filter', async () => { // Given - const whereClause = `and: [{${processIdNotNullCondition}}, {${processIdDefinitions}}}, {${filterClause}}]`; - const mockQueryResult = { ProcessInstances: processInstances }; - mockClient.query.mockResolvedValueOnce( - mockOperationResult(mockQueryResult), - ); - + const whereClause = `and: [{${processIdNotNullCondition}}, {${processIdDefinitions}}}, {${filterString}}]`; + mockClient.query + .mockResolvedValueOnce(mockOperationResult(mockProcessInstanceArguments)) + .mockResolvedValueOnce(mockOperationResult(mockQueryResult)); const expectedQueryArgs = createQueryArgs( 'ProcessInstances', queryBody, @@ -575,7 +721,7 @@ describe('fetchInstances', () => { const result = await dataIndexService.fetchInstances({ definitionIds, pagination, - filter, + filter: logicalFilter, }); // Then @@ -587,8 +733,11 @@ describe('fetchInstances', () => { pagination, }); expect(buildFilterConditionSpy).toHaveBeenCalledTimes(1); - expect(buildFilterConditionSpy).toHaveBeenCalledWith(filter); - expect(mockClient.query).toHaveBeenCalledTimes(1); + expect(buildFilterConditionSpy).toHaveBeenCalledWith( + mockProcessInstanceIntrospection, + logicalFilter, + ); + expect(mockClient.query).toHaveBeenCalledTimes(2); expect(mockClient.query).toHaveBeenCalledWith( graphqlUtils.buildGraphQlQuery(expectedQueryArgs), {}, diff --git a/plugins/orchestrator-backend/src/service/DataIndexService.ts b/plugins/orchestrator-backend/src/service/DataIndexService.ts index b7251c9660..ac9201aa11 100644 --- a/plugins/orchestrator-backend/src/service/DataIndexService.ts +++ b/plugins/orchestrator-backend/src/service/DataIndexService.ts @@ -3,9 +3,11 @@ import { LoggerService } from '@backstage/backend-plugin-api'; import { Client, fetchExchange, gql } from '@urql/core'; import { - FilterInfo, + Filter, fromWorkflowSource, getWorkflowCategory, + IntrospectionField, + IntrospectionQuery, parseWorkflowVariables, ProcessInstance, WorkflowDefinition, @@ -21,7 +23,9 @@ import { Pagination } from '../types/pagination'; import { FETCH_PROCESS_INSTANCES_SORT_FIELD } from './constants'; export class DataIndexService { - private client: Client; + private readonly client: Client; + public processDefinitionArguments: IntrospectionField[] = []; + public processInstanceArguments: IntrospectionField[] = []; public constructor( private readonly dataIndexUrl: string, @@ -42,6 +46,111 @@ export class DataIndexService { }); } + public async initInputProcessDefinitionArgs(): Promise { + if (this.processDefinitionArguments.length === 0) { + this.processDefinitionArguments = + await this.inspectInputArgument('ProcessDefinition'); + } + return this.processDefinitionArguments; // For testing purposes + } + + public graphQLArgumentQuery(type: string): string { + return `query ${type}Argument { + __type(name: "${type}Argument") { + kind + name + inputFields { + name + type { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } + } + }`; + } + public async inspectInputArgument( + type: string, + ): Promise { + const result = await this.client.query(this.graphQLArgumentQuery(type), {}); + + this.logger.debug(`Introspection query result: ${JSON.stringify(result)}`); + + if (result?.error) { + this.logger.error(`Error executing introspection query ${result.error}`); + throw result.error; + } + + const pairs: IntrospectionField[] = []; + if (result?.data?.__type?.inputFields) { + for (const field of result.data.__type.inputFields) { + if ( + field.name !== 'and' && + field.name !== 'or' && + field.name !== 'not' + ) { + pairs.push({ + name: field.name, + type: { + name: field.type.name, + kind: field.type.kind, + ofType: field.type.ofType, + }, + }); + } + } + } + return pairs; + } + + public async getSchemaTypes(type: string): Promise { + const graphQlQuery = `query IntrospectionQuery { + __type(name: "${type}") { + name + kind + description + fields { + name + type { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } +} +`; + + const result = await this.client.query(graphQlQuery, {}); + + this.logger.debug(`Introspection query result: ${JSON.stringify(result)}`); + + if (result.error) { + this.logger.error(`Error executing introspection query ${result.error}`); + throw result.error; + } + return result as unknown as IntrospectionQuery; + } + public async abortWorkflowInstance(instanceId: string): Promise { this.logger.info(`Aborting workflow instance ${instanceId}`); const ProcessInstanceAbortMutationDocument = gql` @@ -117,7 +226,7 @@ export class DataIndexService { public async fetchWorkflowInfos(args: { definitionIds?: string[]; pagination?: Pagination; - filter?: FilterInfo; + filter?: Filter; }): Promise { this.logger.info(`fetchWorkflowInfos() called: ${this.dataIndexUrl}`); const { definitionIds, pagination, filter } = args; @@ -126,13 +235,21 @@ export class DataIndexService { definitionIds !== undefined && definitionIds.length > 0 ? `id: {in: ${JSON.stringify(definitionIds)}}` : undefined; - const filterCondition = buildFilterCondition(filter); + + const filterCondition = filter + ? buildFilterCondition( + await this.initInputProcessDefinitionArgs(), + filter, + ) + : undefined; let whereClause: string | undefined; if (definitionIds && filter) { whereClause = `and: [{${definitionIdsCondition}}, {${filterCondition}}]`; } else if (definitionIdsCondition || filterCondition) { whereClause = definitionIdsCondition ?? filterCondition; + } else { + whereClause = undefined; } const graphQlQuery = buildGraphQlQuery({ @@ -143,7 +260,6 @@ export class DataIndexService { }); this.logger.debug(`GraphQL query: ${graphQlQuery}`); const result = await this.client.query(graphQlQuery, {}); - this.logger.debug( `Get workflow definitions result: ${JSON.stringify(result)}`, ); @@ -161,7 +277,7 @@ export class DataIndexService { public async fetchInstances(args: { definitionIds?: string[]; pagination?: Pagination; - filter?: FilterInfo; + filter?: Filter; }): Promise { const { pagination, definitionIds, filter } = args; if (pagination) pagination.sortField ??= FETCH_PROCESS_INSTANCES_SORT_FIELD; @@ -170,7 +286,12 @@ export class DataIndexService { const definitionIdsCondition = definitionIds ? `processId: {in: ${JSON.stringify(definitionIds)}}` : undefined; - const filterCondition = buildFilterCondition(filter); + const filterCondition = filter + ? buildFilterCondition( + await this.inspectInputArgument('ProcessInstance'), + filter, + ) + : ''; let whereClause = ''; const conditions = []; @@ -224,15 +345,33 @@ export class DataIndexService { public async fetchInstancesTotalCount( definitionIds?: string[], + filter?: Filter, ): Promise { + const definitionIdsCondition = definitionIds + ? `processId: {in: ${JSON.stringify(definitionIds)}}` + : undefined; + this.initInputProcessDefinitionArgs(); + const filterCondition = filter + ? buildFilterCondition( + await this.inspectInputArgument('ProcessInstance'), + filter, + ) + : ''; + + let whereClause: string | undefined; + if (definitionIds && filter) { + whereClause = `and: [{${definitionIdsCondition}}, {${filterCondition}}]`; + } else if (definitionIdsCondition || filterCondition) { + whereClause = definitionIdsCondition ?? filterCondition; + } + const graphQlQuery = buildGraphQlQuery({ type: 'ProcessInstances', queryBody: 'id', - whereClause: definitionIds - ? `processId: {in: ${JSON.stringify(definitionIds)}}` - : undefined, + whereClause, }); this.logger.debug(`GraphQL query: ${graphQlQuery}`); + const result = await this.client.query(graphQlQuery, {}); if (result.error) { diff --git a/plugins/orchestrator-backend/src/service/DataInputSchemaService.test.ts b/plugins/orchestrator-backend/src/service/DataInputSchemaService.test.ts deleted file mode 100644 index a9dbacccce..0000000000 --- a/plugins/orchestrator-backend/src/service/DataInputSchemaService.test.ts +++ /dev/null @@ -1,202 +0,0 @@ -import mockComposedGreetingWorkflowData from '../../__fixtures__/mockComposedGreetingWorfklow'; -import mockGreetingWorkflowData from '../../__fixtures__/mockGreetingWorkflowData'; -import mockSpringBootWorkflowData from '../../__fixtures__/mockSpringBootWorkflowData'; -import { DataInputSchemaService } from './DataInputSchemaService'; - -const service = new DataInputSchemaService(); - -describe('workflow input schema response', () => { - it('schema with refs should return multiple steps', () => { - const response = service.getWorkflowInputSchemaResponse( - mockSpringBootWorkflowData.workflowDefinition, - mockSpringBootWorkflowData.schema, - ); - expect(response.isComposedSchema).toEqual(true); - expect(response.schemaSteps?.map(step => step.title)).toEqual([ - 'Provide information about the new component', - 'Provide information about the Java metadata', - 'Provide information about the CI method', - ]); - expect(response.schemaSteps?.map(step => step.key)).toEqual([ - 'newComponent', - 'javaMetadata', - 'ciMethod', - ]); - }); - - it('schema with two layers without refs should return a schema parse error', () => { - const response = service.getWorkflowInputSchemaResponse( - mockSpringBootWorkflowData.workflowDefinition, - { ...mockSpringBootWorkflowData.schema, $defs: undefined }, - ); - expect(response.isComposedSchema).toEqual(false); - expect(response.schemaSteps.length).toEqual(0); - expect(response.schemaParseError).toEqual( - 'schema contains invalid ref #/$defs/Provide information about the new component_0', - ); - }); - - it('none composed schema should return isComposedSchema false and one step', () => { - const response = service.getWorkflowInputSchemaResponse( - mockGreetingWorkflowData.workflowDefinition, - mockGreetingWorkflowData.schema, - ); - expect(response.isComposedSchema).toEqual(false); - expect(response.schemaSteps[0].key).toEqual('DUMMY_KEY_FOR_SINGLE_SCHEMA'); - expect(response.schemaSteps[0].title).toEqual('Data Input Schema'); - }); - - it('composed schema also without refs should return multiple steps', () => { - const response = service.getWorkflowInputSchemaResponse( - mockComposedGreetingWorkflowData.workflowDefinition, - mockComposedGreetingWorkflowData.schema, - ); - expect(response.isComposedSchema).toEqual(true); - expect(response.schemaSteps[0].key).toEqual('language'); - expect(response.schemaSteps[0].title).toEqual('Language'); - expect(response.schemaSteps[1].key).toEqual('name'); - expect(response.schemaSteps[1].title).toEqual('name'); - }); - - it('a schema without properties should return a schema parse error', () => { - const response = service.getWorkflowInputSchemaResponse( - mockComposedGreetingWorkflowData.workflowDefinition, - { title: 'A' }, - ); - expect(response.isComposedSchema).toEqual(false); - expect(response.schemaSteps.length).toEqual(0); - expect(response.schemaParseError).toEqual( - 'the provided schema does not contain valid properties', - ); - }); - - it('using initial variables should return data for each step', () => { - const response = service.getWorkflowInputSchemaResponse( - mockGreetingWorkflowData.workflowDefinition, - mockGreetingWorkflowData.schema, - mockGreetingWorkflowData.variables, - ); - expect(response.isComposedSchema).toEqual(false); - expect(response.schemaSteps[0].data).toEqual({ - language: 'Spanish', - name: 'John Doe', - }); - expect(response.schemaSteps[0].readonlyKeys).toEqual([]); - }); - - it('using initial variables on composed schema should return data for each step', () => { - const response = service.getWorkflowInputSchemaResponse( - mockComposedGreetingWorkflowData.workflowDefinition, - mockComposedGreetingWorkflowData.schema, - mockComposedGreetingWorkflowData.variables, - ); - expect(response.isComposedSchema).toEqual(true); - expect(response.schemaSteps[0].data).toEqual({ language: 'Spanish' }); - expect(response.schemaSteps[1].data).toEqual({ name: 'John Doe' }); - expect(response.schemaSteps[0].readonlyKeys).toEqual([]); - }); - - it('using initial assessment variables should return read only keys', () => { - const response = service.getWorkflowInputSchemaResponse( - mockGreetingWorkflowData.workflowDefinition, - mockGreetingWorkflowData.schema, - undefined, - { - workflowdata: { - language: 'Spanish', - name: 'John Doe', - greeting: 'hello', - waitOrError: 'Error', - }, - }, - ); - expect(response.isComposedSchema).toEqual(false); - expect(response.schemaSteps[0].data).toEqual({ - language: 'Spanish', - name: 'John Doe', - }); - expect(response.schemaSteps[0].readonlyKeys).toEqual(['language', 'name']); - }); - - it('using assessment variables on composed schema should return read only keys', () => { - const newComponentValues = { - orgName: 'org.example', - repoName: 'example', - description: 'example description', - }; - const response = service.getWorkflowInputSchemaResponse( - mockSpringBootWorkflowData.workflowDefinition, - mockSpringBootWorkflowData.schema, - undefined, - { - workflowdata: { - newComponent: newComponentValues, - }, - }, - ); - expect(response.isComposedSchema).toEqual(true); - expect(response.schemaSteps[0].data).toEqual(newComponentValues); - expect(response.schemaSteps[0].readonlyKeys).toEqual( - Object.keys(newComponentValues), - ); - }); - - it('using initial workflow and assessment variables should return read only keys', () => { - const response = service.getWorkflowInputSchemaResponse( - mockGreetingWorkflowData.workflowDefinition, - mockGreetingWorkflowData.schema, - { - workflowdata: { - name: 'John Doe', - language: 'Spanish', - }, - }, - { - workflowdata: { - name: 'John Doe', - waitOrError: 'Error', - }, - }, - ); - expect(response.isComposedSchema).toEqual(false); - expect(response.schemaSteps[0].data).toEqual({ - language: 'Spanish', - name: 'John Doe', - }); - expect(response.schemaSteps[0].readonlyKeys).toEqual(['name']); - }); - - it('using initial workflow and assessment variables on composed schema should return read only keys', () => { - const newComponentValues = { - orgName: 'org.example', - repoName: 'example', - description: 'example description', - }; - const javaMetadataValues = { - groupId: 'org.example', - artifactId: 'example', - version: '1.0.0', - }; - const response = service.getWorkflowInputSchemaResponse( - mockSpringBootWorkflowData.workflowDefinition, - mockSpringBootWorkflowData.schema, - { - workflowdata: { - newComponent: newComponentValues, - javaMetadata: javaMetadataValues, - }, - }, - { - workflowdata: { - newComponent: newComponentValues, - }, - }, - ); - expect(response.isComposedSchema).toEqual(true); - expect(response.schemaSteps[0].data).toEqual(newComponentValues); - expect(response.schemaSteps[1].data).toEqual(javaMetadataValues); - expect(response.schemaSteps[0].readonlyKeys).toEqual( - Object.keys(newComponentValues), - ); - }); -}); diff --git a/plugins/orchestrator-backend/src/service/DataInputSchemaService.ts b/plugins/orchestrator-backend/src/service/DataInputSchemaService.ts index 829c20cadb..f6ef192b62 100644 --- a/plugins/orchestrator-backend/src/service/DataInputSchemaService.ts +++ b/plugins/orchestrator-backend/src/service/DataInputSchemaService.ts @@ -1,177 +1,11 @@ -import { JsonObject } from '@backstage/types'; - -import { JSONSchema7 } from 'json-schema'; - -import { - ComposedSchema, - isComposedSchema, - isJsonObjectSchema, - JsonObjectSchema, - WorkflowDefinition, - WorkflowInputSchemaResponse, - WorkflowInputSchemaStep, -} from '@janus-idp/backstage-plugin-orchestrator-common'; +import type { JsonObject } from '@backstage/types'; import { WORKFLOW_DATA_KEY } from './constants'; -const SINGLE_SCHEMA_TITLE = 'workflow input data'; -const SINGLE_SCHEMA_KEY = 'DUMMY_KEY_FOR_SINGLE_SCHEMA'; - export class DataInputSchemaService { - public extractInitialStateFromWorkflowData( - workflowData: JsonObject, - schemaProperties: JsonObjectSchema['properties'], - ): JsonObject { - return Object.keys(schemaProperties) - .filter(k => k in workflowData) - .reduce((result, k) => { - if (!workflowData[k]) { - return result; - } - result[k] = workflowData[k]; - return result; - }, {} as JsonObject); - } - - private findReferencedSchema( - rootSchema: JSONSchema7, - ref: string, - ): JSONSchema7 { - const pathParts = ref.split('/').filter(part => !['#', ''].includes(part)); - - let current: any = rootSchema; - for (const part of pathParts) { - current = current?.[part]; - if (current === undefined) { - throw new Error(`schema contains invalid ref ${ref}`); - } - } - return current; - } - - public resolveRefs(schema: JsonObjectSchema): JsonObjectSchema { - const resolvedSchemaProperties = Object.entries(schema.properties).reduce< - JsonObjectSchema['properties'] - >( - (prev, [key, curSchema]) => ({ - ...prev, - [key]: curSchema.$ref - ? this.findReferencedSchema(schema, curSchema.$ref) - : curSchema, - }), - {}, - ); - return { - ...schema, - properties: resolvedSchemaProperties, - }; - } - - private getInputSchemaSteps( - schema: ComposedSchema, - workflowData?: JsonObject, - assessmentWorkflowData?: JsonObject, - ): WorkflowInputSchemaStep[] { - return Object.entries(schema.properties).map(([key, curSchema]) => { - const data = this.extractInnerProperty(workflowData, key); - const assessmentData = this.extractInnerProperty( - assessmentWorkflowData, - key, - ); - const assessmentInitialState = this.extractInitialStateFromWorkflowData( - assessmentData, - curSchema.properties, - ); - return { - title: curSchema.title || key, - key, - schema: curSchema, - data, - readonlyKeys: this.extractObjectKeys(assessmentInitialState), - }; - }); - } - - public getWorkflowInputSchemaResponse( - definition: WorkflowDefinition, - inputSchema: JSONSchema7, - instanceVariables?: object, - assessmentInstanceVariables?: object, - ): WorkflowInputSchemaResponse { - const instanceWorkflowData = this.extractWorkflowData(instanceVariables); - const assessmentInstanceWorkflowData = this.extractWorkflowData( - assessmentInstanceVariables, - ); - const workflowData = instanceWorkflowData ?? assessmentInstanceWorkflowData; - - const res: WorkflowInputSchemaResponse = { - definition, - isComposedSchema: false, - schemaSteps: [], - }; - if (!isJsonObjectSchema(inputSchema)) { - return { - ...res, - schemaParseError: - 'the provided schema does not contain valid properties', - }; - } - try { - const resolvedSchema = this.resolveRefs(inputSchema); - - if (isComposedSchema(resolvedSchema)) { - res.schemaSteps = this.getInputSchemaSteps( - resolvedSchema, - workflowData, - assessmentInstanceWorkflowData, - ); - res.isComposedSchema = true; - } else { - const data = workflowData - ? this.extractInitialStateFromWorkflowData( - workflowData, - resolvedSchema.properties, - ) - : {}; - const assessmentData = - assessmentInstanceWorkflowData && - this.extractInitialStateFromWorkflowData( - assessmentInstanceWorkflowData, - resolvedSchema.properties, - ); - res.schemaSteps = [ - { - schema: resolvedSchema, - title: resolvedSchema.title ?? SINGLE_SCHEMA_TITLE, - key: SINGLE_SCHEMA_KEY, - data, - readonlyKeys: this.extractObjectKeys(assessmentData), - }, - ]; - } - } catch (err) { - res.schemaParseError = - typeof err === 'object' && (err as { message: string }).message - ? (err as { message: string }).message - : 'unexpected parsing error'; - } - return res; - } - public extractWorkflowData(variables?: object): JsonObject | undefined { return variables && WORKFLOW_DATA_KEY in variables ? (variables[WORKFLOW_DATA_KEY] as JsonObject) : undefined; } - - private extractObjectKeys(obj: JsonObject | undefined): string[] { - return obj ? Object.keys(obj) : []; - } - - private extractInnerProperty( - obj: JsonObject | undefined, - key: string, - ): JsonObject { - return (obj?.[key] as JsonObject) ?? {}; - } } diff --git a/plugins/orchestrator-backend/src/service/DevModeService.ts b/plugins/orchestrator-backend/src/service/DevModeService.ts index f1a9290d7e..c1f8bb2af6 100644 --- a/plugins/orchestrator-backend/src/service/DevModeService.ts +++ b/plugins/orchestrator-backend/src/service/DevModeService.ts @@ -1,5 +1,5 @@ -import { LoggerService } from '@backstage/backend-plugin-api'; -import { Config } from '@backstage/config'; +import type { LoggerService } from '@backstage/backend-plugin-api'; +import type { Config } from '@backstage/config'; import fs from 'fs-extra'; diff --git a/plugins/orchestrator-backend/src/service/GitService.ts b/plugins/orchestrator-backend/src/service/GitService.ts index c19bc0ddcc..a99011768e 100644 --- a/plugins/orchestrator-backend/src/service/GitService.ts +++ b/plugins/orchestrator-backend/src/service/GitService.ts @@ -1,8 +1,9 @@ -import { Git } from '@backstage/backend-common'; -import { LoggerService } from '@backstage/backend-plugin-api'; -import { Config } from '@backstage/config'; +import type { LoggerService } from '@backstage/backend-plugin-api'; +import type { Config } from '@backstage/config'; import { ScmIntegrations } from '@backstage/integration'; +import { Git } from './GitWrapper'; + export class GitService { private readonly git: Git; diff --git a/plugins/orchestrator-backend/src/service/GitWrapper/git.ts b/plugins/orchestrator-backend/src/service/GitWrapper/git.ts new file mode 100644 index 0000000000..e3f4fcc7f1 --- /dev/null +++ b/plugins/orchestrator-backend/src/service/GitWrapper/git.ts @@ -0,0 +1,354 @@ +/* Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { LoggerService } from '@backstage/backend-plugin-api'; + +import fs from 'fs-extra'; +import git, { + AuthCallback, + MergeResult, + ProgressCallback, + ReadCommitResult, +} from 'isomorphic-git'; +import http from 'isomorphic-git/http/node'; + +function isAuthCallbackOptions( + options: StaticAuthOptions | AuthCallbackOptions, +): options is AuthCallbackOptions { + return 'onAuth' in options; +} + +/** + * Configure static credential for authentication + * @public + */ +export type StaticAuthOptions = { + username?: string; + password?: string; + token?: string; + logger?: LoggerService; +}; + +/** + * Configure an authentication callback that can provide credentials on demand + * @public + */ +export type AuthCallbackOptions = { + onAuth: AuthCallback; + logger?: LoggerService; +}; + +/* +provider username password +Azure 'notempty' token +Bitbucket Cloud 'x-token-auth' token +Bitbucket Server username password or token +GitHub 'x-access-token' token +GitLab 'oauth2' token +From : https://isomorphic-git.org/docs/en/onAuth with fix for GitHub +Or token provided as `token` for Bearer auth header +instead of Basic Auth (e.g., Bitbucket Server). +*/ + +/** + * A convenience wrapper around the `isomorphic-git` library. + * @public + */ + +export class Git { + private readonly headers: { + [x: string]: string; + }; + + private constructor( + private readonly config: { + onAuth: AuthCallback; + token?: string; + logger?: LoggerService; + }, + ) { + this.onAuth = config.onAuth; + + this.headers = { + 'user-agent': 'git/@isomorphic-git', + ...(config.token ? { Authorization: `Bearer ${config.token}` } : {}), + }; + } + + async add(options: { dir: string; filepath: string }): Promise { + const { dir, filepath } = options; + this.config.logger?.info(`Adding file {dir=${dir},filepath=${filepath}}`); + + return git.add({ fs, dir, filepath }); + } + + async addRemote(options: { + dir: string; + remote: string; + url: string; + force?: boolean; + }): Promise { + const { dir, url, remote, force } = options; + this.config.logger?.info( + `Creating new remote {dir=${dir},remote=${remote},url=${url}}`, + ); + return git.addRemote({ fs, dir, remote, url, force }); + } + + async deleteRemote(options: { dir: string; remote: string }): Promise { + const { dir, remote } = options; + this.config.logger?.info(`Deleting remote {dir=${dir},remote=${remote}}`); + return git.deleteRemote({ fs, dir, remote }); + } + + async checkout(options: { dir: string; ref: string }): Promise { + const { dir, ref } = options; + this.config.logger?.info(`Checking out branch {dir=${dir},ref=${ref}}`); + + return git.checkout({ fs, dir, ref }); + } + + async branch(options: { dir: string; ref: string }): Promise { + const { dir, ref } = options; + this.config.logger?.info(`Creating branch {dir=${dir},ref=${ref}`); + + return git.branch({ fs, dir, ref }); + } + + async commit(options: { + dir: string; + message: string; + author: { name: string; email: string }; + committer: { name: string; email: string }; + }): Promise { + const { dir, message, author, committer } = options; + this.config.logger?.info( + `Committing file to repo {dir=${dir},message=${message}}`, + ); + return git.commit({ fs, dir, message, author, committer }); + } + + /** https://isomorphic-git.org/docs/en/clone */ + async clone(options: { + url: string; + dir: string; + ref?: string; + depth?: number; + noCheckout?: boolean; + }): Promise { + const { url, dir, ref, depth, noCheckout } = options; + this.config.logger?.info(`Cloning repo {dir=${dir},url=${url}}`); + + try { + return await git.clone({ + fs, + http, + url, + dir, + ref, + singleBranch: true, + depth: depth ?? 1, + noCheckout, + onProgress: this.onProgressHandler(), + headers: this.headers, + onAuth: this.onAuth, + }); + } catch (ex: any) { + this.config.logger?.error(`Failed to clone repo {dir=${dir},url=${url}}`); + if (ex.data) { + throw new Error(`${ex.message} {data=${JSON.stringify(ex.data)}}`); + } + throw ex; + } + } + + /** https://isomorphic-git.org/docs/en/currentBranch */ + async currentBranch(options: { + dir: string; + fullName?: boolean; + }): Promise { + const { dir, fullName = false } = options; + return git.currentBranch({ fs, dir, fullname: fullName }) as Promise< + string | undefined + >; + } + + /** https://isomorphic-git.org/docs/en/fetch */ + async fetch(options: { + dir: string; + remote?: string; + tags?: boolean; + }): Promise { + const { dir, remote = 'origin', tags = false } = options; + this.config.logger?.info( + `Fetching remote=${remote} for repository {dir=${dir}}`, + ); + + try { + await git.fetch({ + fs, + http, + dir, + remote, + tags, + onProgress: this.onProgressHandler(), + headers: this.headers, + onAuth: this.onAuth, + }); + } catch (ex: any) { + this.config.logger?.error( + `Failed to fetch repo {dir=${dir},remote=${remote}}`, + ); + if (ex.data) { + throw new Error(`${ex.message} {data=${JSON.stringify(ex.data)}}`); + } + throw ex; + } + } + + async init(options: { dir: string; defaultBranch?: string }): Promise { + const { dir, defaultBranch = 'master' } = options; + this.config.logger?.info(`Init git repository {dir=${dir}}`); + + return git.init({ + fs, + dir, + defaultBranch, + }); + } + + /** https://isomorphic-git.org/docs/en/merge */ + async merge(options: { + dir: string; + theirs: string; + ours?: string; + author: { name: string; email: string }; + committer: { name: string; email: string }; + }): Promise { + const { dir, theirs, ours, author, committer } = options; + this.config.logger?.info( + `Merging branch '${theirs}' into '${ours}' for repository {dir=${dir}}`, + ); + + // If ours is undefined, current branch is used. + return git.merge({ + fs, + dir, + ours, + theirs, + author, + committer, + }); + } + + async push(options: { + dir: string; + remote: string; + remoteRef?: string; + force?: boolean; + }) { + const { dir, remote, remoteRef, force } = options; + this.config.logger?.info( + `Pushing directory to remote {dir=${dir},remote=${remote}}`, + ); + try { + return await git.push({ + fs, + dir, + http, + onProgress: this.onProgressHandler(), + remoteRef, + force, + headers: this.headers, + remote, + onAuth: this.onAuth, + }); + } catch (ex: any) { + this.config.logger?.error( + `Failed to push to repo {dir=${dir}, remote=${remote}}`, + ); + if (ex.data) { + throw new Error(`${ex.message} {data=${JSON.stringify(ex.data)}}`); + } + throw ex; + } + } + + /** https://isomorphic-git.org/docs/en/readCommit */ + async readCommit(options: { + dir: string; + sha: string; + }): Promise { + const { dir, sha } = options; + return git.readCommit({ fs, dir, oid: sha }); + } + + /** https://isomorphic-git.org/docs/en/remove */ + async remove(options: { dir: string; filepath: string }): Promise { + const { dir, filepath } = options; + this.config.logger?.info( + `Removing file from git index {dir=${dir},filepath=${filepath}}`, + ); + return git.remove({ fs, dir, filepath }); + } + + /** https://isomorphic-git.org/docs/en/resolveRef */ + async resolveRef(options: { dir: string; ref: string }): Promise { + const { dir, ref } = options; + return git.resolveRef({ fs, dir, ref }); + } + + /** https://isomorphic-git.org/docs/en/log */ + async log(options: { + dir: string; + ref?: string; + }): Promise { + const { dir, ref } = options; + return git.log({ + fs, + dir, + ref: ref ?? 'HEAD', + }); + } + + private readonly onAuth: AuthCallback; + + private readonly onProgressHandler = (): ProgressCallback => { + let currentPhase = ''; + + return event => { + if (currentPhase !== event.phase) { + currentPhase = event.phase; + this.config.logger?.info(event.phase); + } + const total = event.total + ? `${Math.round((event.loaded / event.total) * 100)}%` + : event.loaded; + this.config.logger?.debug(`status={${event.phase},total={${total}}}`); + }; + }; + + static readonly fromAuth = ( + options: StaticAuthOptions | AuthCallbackOptions, + ) => { + if (isAuthCallbackOptions(options)) { + const { onAuth, logger } = options; + return new Git({ onAuth, logger }); + } + + const { username, password, token, logger } = options; + return new Git({ onAuth: () => ({ username, password }), token, logger }); + }; +} diff --git a/plugins/orchestrator-backend/src/service/GitWrapper/index.ts b/plugins/orchestrator-backend/src/service/GitWrapper/index.ts new file mode 100644 index 0000000000..f6d2e5bac5 --- /dev/null +++ b/plugins/orchestrator-backend/src/service/GitWrapper/index.ts @@ -0,0 +1,4 @@ +// Plan for the future: Eventually remove that wrapper in favour of direct use of the isomorphic-git package. + +// Based on https://github.com/backstage/backstage/pull/24605/files +export * from './git'; diff --git a/plugins/orchestrator-backend/src/service/Helper.ts b/plugins/orchestrator-backend/src/service/Helper.ts index 0c10b7cbb8..471a3dadd7 100644 --- a/plugins/orchestrator-backend/src/service/Helper.ts +++ b/plugins/orchestrator-backend/src/service/Helper.ts @@ -1,5 +1,5 @@ -import { LoggerService } from '@backstage/backend-plugin-api'; -import { Config } from '@backstage/config'; +import type { LoggerService } from '@backstage/backend-plugin-api'; +import type { Config } from '@backstage/config'; import fs from 'fs-extra'; diff --git a/plugins/orchestrator-backend/src/service/OrchestratorService.test.ts b/plugins/orchestrator-backend/src/service/OrchestratorService.test.ts index 0d4cacad6b..30659c339e 100644 --- a/plugins/orchestrator-backend/src/service/OrchestratorService.test.ts +++ b/plugins/orchestrator-backend/src/service/OrchestratorService.test.ts @@ -653,129 +653,4 @@ describe('OrchestratorService', () => { expect(sonataFlowServiceMock.executeWorkflow).not.toHaveBeenCalled(); }); }); - - describe('retriggerInstanceInError', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('should execute the operation when the workflow is available', async () => { - workflowCacheServiceMock.isAvailable = jest.fn().mockReturnValue(true); - sonataFlowServiceMock.retriggerInstanceInError = jest - .fn() - .mockResolvedValue(true); - - await orchestratorService.retriggerInstanceInError({ - definitionId, - serviceUrl, - instanceId, - cacheHandler: 'skip', - }); - - expect(workflowCacheServiceMock.isAvailable).toHaveBeenCalled(); - expect(sonataFlowServiceMock.retriggerInstanceInError).toHaveBeenCalled(); - }); - - it('should skip and not execute the operation when the workflow is not available', async () => { - workflowCacheServiceMock.isAvailable = jest.fn().mockReturnValue(false); - - await orchestratorService.retriggerInstanceInError({ - definitionId, - serviceUrl, - instanceId, - cacheHandler: 'skip', - }); - - expect(workflowCacheServiceMock.isAvailable).toHaveBeenCalled(); - expect( - sonataFlowServiceMock.retriggerInstanceInError, - ).not.toHaveBeenCalled(); - }); - - it('should throw an error and not execute the operation when the workflow is not available', async () => { - workflowCacheServiceMock.isAvailable = jest - .fn() - .mockImplementation(() => { - throw new Error(); - }); - - const promise = orchestratorService.retriggerInstanceInError({ - definitionId, - serviceUrl, - instanceId, - cacheHandler: 'throw', - }); - - await expect(promise).rejects.toThrow(); - - expect(workflowCacheServiceMock.isAvailable).toHaveBeenCalled(); - expect( - sonataFlowServiceMock.retriggerInstanceInError, - ).not.toHaveBeenCalled(); - }); - }); - - describe('updateInstanceInputData', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('should execute the operation when the workflow is available', async () => { - workflowCacheServiceMock.isAvailable = jest.fn().mockReturnValue(true); - sonataFlowServiceMock.updateInstanceInputData = jest - .fn() - .mockResolvedValue(true); - - await orchestratorService.updateInstanceInputData({ - definitionId, - serviceUrl, - instanceId, - inputData, - cacheHandler: 'skip', - }); - - expect(workflowCacheServiceMock.isAvailable).toHaveBeenCalled(); - expect(sonataFlowServiceMock.updateInstanceInputData).toHaveBeenCalled(); - }); - - it('should skip and not execute the operation when the workflow is not available', async () => { - workflowCacheServiceMock.isAvailable = jest.fn().mockReturnValue(false); - - await orchestratorService.updateInstanceInputData({ - definitionId, - serviceUrl, - instanceId, - inputData, - cacheHandler: 'skip', - }); - - expect(workflowCacheServiceMock.isAvailable).toHaveBeenCalled(); - expect( - sonataFlowServiceMock.updateInstanceInputData, - ).not.toHaveBeenCalled(); - }); - - it('should throw an error and not execute the operation when the workflow is not available', async () => { - workflowCacheServiceMock.isAvailable = jest - .fn() - .mockImplementation(() => { - throw new Error(); - }); - - const promise = orchestratorService.updateInstanceInputData({ - definitionId, - serviceUrl, - instanceId, - inputData, - cacheHandler: 'throw', - }); - - await expect(promise).rejects.toThrow(); - - expect(workflowCacheServiceMock.isAvailable).toHaveBeenCalled(); - expect( - sonataFlowServiceMock.updateInstanceInputData, - ).not.toHaveBeenCalled(); - }); - }); }); diff --git a/plugins/orchestrator-backend/src/service/OrchestratorService.ts b/plugins/orchestrator-backend/src/service/OrchestratorService.ts index 35e02e385c..48b908f002 100644 --- a/plugins/orchestrator-backend/src/service/OrchestratorService.ts +++ b/plugins/orchestrator-backend/src/service/OrchestratorService.ts @@ -1,5 +1,5 @@ import { - FilterInfo, + Filter, ProcessInstance, ProcessInstanceVariables, WorkflowDefinition, @@ -54,18 +54,29 @@ export class OrchestratorService { public async fetchInstances(args: { pagination?: Pagination; - filter?: FilterInfo; + filter?: Filter; + workflowId?: string; }): Promise { + const definitionIds = args.workflowId + ? [args.workflowId] + : this.workflowCacheService.definitionIds; return await this.dataIndexService.fetchInstances({ - definitionIds: this.workflowCacheService.definitionIds, + definitionIds: definitionIds, pagination: args.pagination, filter: args.filter, }); } - public async fetchInstancesTotalCount(): Promise { + public async fetchInstancesTotalCount( + workflowId?: string, + filter?: Filter, + ): Promise { + const definitionIds = workflowId + ? [workflowId] + : this.workflowCacheService.definitionIds; return await this.dataIndexService.fetchInstancesTotalCount( - this.workflowCacheService.definitionIds, + definitionIds, + filter, ); } @@ -145,7 +156,7 @@ export class OrchestratorService { public async fetchWorkflowOverviews(args: { pagination?: Pagination; - filter?: FilterInfo; + filter?: Filter; }): Promise { return await this.sonataFlowService.fetchWorkflowOverviews({ definitionIds: this.workflowCacheService.definitionIds, @@ -184,41 +195,4 @@ export class OrchestratorService { ? await this.sonataFlowService.fetchWorkflowOverview(definitionId) : undefined; } - - public async retriggerInstanceInError(args: { - definitionId: string; - serviceUrl: string; - instanceId: string; - cacheHandler?: CacheHandler; - }): Promise { - const { definitionId, cacheHandler } = args; - - const isWorkflowAvailable = this.workflowCacheService.isAvailable( - definitionId, - cacheHandler, - ); - - return isWorkflowAvailable - ? await this.sonataFlowService.retriggerInstanceInError(args) - : false; - } - - public async updateInstanceInputData(args: { - definitionId: string; - serviceUrl: string; - instanceId: string; - inputData: ProcessInstanceVariables; - cacheHandler?: CacheHandler; - }): Promise { - const { definitionId, cacheHandler } = args; - - const isWorkflowAvailable = this.workflowCacheService.isAvailable( - definitionId, - cacheHandler, - ); - - return isWorkflowAvailable - ? await this.sonataFlowService.updateInstanceInputData(args) - : false; - } } diff --git a/plugins/orchestrator-backend/src/service/ScaffolderService.ts b/plugins/orchestrator-backend/src/service/ScaffolderService.ts index c3ddb9f918..aee81946ce 100644 --- a/plugins/orchestrator-backend/src/service/ScaffolderService.ts +++ b/plugins/orchestrator-backend/src/service/ScaffolderService.ts @@ -1,7 +1,10 @@ -import { loggerToWinstonLogger, UrlReader } from '@backstage/backend-common'; -import { LoggerService } from '@backstage/backend-plugin-api'; -import { CatalogApi } from '@backstage/catalog-client'; -import { Config } from '@backstage/config'; +import { loggerToWinstonLogger } from '@backstage/backend-common'; +import type { + LoggerService, + UrlReaderService, +} from '@backstage/backend-plugin-api'; +import type { CatalogApi } from '@backstage/catalog-client'; +import type { Config } from '@backstage/config'; import { ScmIntegrations } from '@backstage/integration'; import { createBuiltinActions, @@ -11,7 +14,7 @@ import { ActionContext, TemplateAction, } from '@backstage/plugin-scaffolder-node'; -import { JsonObject, JsonValue } from '@backstage/types'; +import type { JsonObject, JsonValue } from '@backstage/types'; import fs from 'fs-extra'; @@ -35,7 +38,7 @@ export class ScaffolderService { private readonly logger: LoggerService, private readonly config: Config, private readonly catalogApi: CatalogApi, - private readonly urlReader: UrlReader, + private readonly urlReader: UrlReaderService, ) { this.actionRegistry = new TemplateActionRegistry(); } @@ -85,10 +88,11 @@ export class ScaffolderService { ); throw err; } - const mockContext: ActionContext = { + const actionContext: ActionContext = { input: actionExecutionContext.input, workspacePath: workspacePath, // TODO: Move this to LoggerService after scaffolder-node moves to LoggerService + // https://github.com/backstage/backstage/issues/26933 logger: loggerToWinstonLogger(this.logger), logStream: this.streamLogger, createTemporaryDirectory: async () => @@ -107,7 +111,7 @@ export class ScaffolderService { return fn(); }, }; - await action.handler(mockContext); + await action.handler(actionContext); return stepOutput; } diff --git a/plugins/orchestrator-backend/src/service/SonataFlowService.test.ts b/plugins/orchestrator-backend/src/service/SonataFlowService.test.ts new file mode 100644 index 0000000000..87ed461840 --- /dev/null +++ b/plugins/orchestrator-backend/src/service/SonataFlowService.test.ts @@ -0,0 +1,355 @@ +import { LoggerService } from '@backstage/backend-plugin-api'; + +import { WorkflowExecutionResponse } from '@janus-idp/backstage-plugin-orchestrator-common'; + +import { DataIndexService } from './DataIndexService'; +import { SonataFlowService } from './SonataFlowService'; + +describe('SonataFlowService', () => { + let loggerMock: jest.Mocked; + let sonataFlowService: SonataFlowService; + + beforeAll(() => { + loggerMock = { + info: jest.fn(), + debug: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + child: jest.fn(), + }; + sonataFlowService = new SonataFlowService( + {} as DataIndexService, + loggerMock, + ); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('fetchWorkflowInfoOnService', () => { + const serviceUrl = 'http://example.com'; + const definitionId = 'workflow-123'; + const urlToFetch = 'http://example.com/management/processes/workflow-123'; + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return workflow info when the fetch response is ok', async () => { + // Given + const mockResponse: Partial = { + ok: true, + json: jest.fn().mockResolvedValue({ id: 'workflow-123' }), + }; + global.fetch = jest.fn().mockResolvedValue(mockResponse as any); + + // When + const result = await sonataFlowService.fetchWorkflowInfoOnService({ + definitionId, + serviceUrl, + }); + + // Then + expect(fetch).toHaveBeenCalledWith(urlToFetch); + expect(result).toEqual({ id: definitionId }); + expect(loggerMock.debug).toHaveBeenCalledWith( + `Fetch workflow info result: {"id":"${definitionId}"}`, + ); + }); + + it('should propagate thrown error when the fetch response is not ok', async () => { + // Given + const mockResponse: Partial = { + ok: false, + status: 500, + statusText: 'Not Found', + json: jest.fn().mockResolvedValue({ + details: 'Error details', + stack: 'Error stack trace', + }), + }; + global.fetch = jest.fn().mockResolvedValue(mockResponse as any); + + // When + let result; + try { + await sonataFlowService.fetchWorkflowInfoOnService({ + definitionId, + serviceUrl, + }); + } catch (error) { + result = error; + } + + expect(result).toBeDefined(); + }); + + it('should propagate thrown error when fetch throws an error', async () => { + // Given + global.fetch = jest.fn().mockRejectedValue(new Error('Network Error')); + + // When + let result; + try { + await sonataFlowService.fetchWorkflowInfoOnService({ + definitionId, + serviceUrl, + }); + } catch (error) { + result = error; + } + + expect(result).toBeDefined(); + }); + }); + describe('executeWorkflow', () => { + const serviceUrl = 'http://example.com/workflows'; + const definitionId = 'workflow-123'; + const urlToFetch = `${serviceUrl}/${definitionId}`; + const inputData = { var1: 'value1' }; + + const expectedFetchRequestInit = (): RequestInit => { + return { + method: 'POST', + body: JSON.stringify(inputData), + headers: { 'content-type': 'application/json' }, + }; + }; + + const setupTest = (responseConfig: { + ok: boolean; + status?: number; + statusText?: string; + json: any; + }): Partial => { + const mockResponse: Partial = { + ok: responseConfig.ok, + status: responseConfig.status || (responseConfig.ok ? 200 : 500), + statusText: responseConfig.statusText, + json: jest.fn().mockResolvedValue(responseConfig.json), + }; + global.fetch = jest.fn().mockResolvedValue(mockResponse as any); + return mockResponse; + }; + + const runErrorTest = async (): Promise< + WorkflowExecutionResponse | undefined + > => { + return await sonataFlowService.executeWorkflow({ + definitionId, + serviceUrl, + inputData, + }); + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + it('should return workflow execution response when the request is successful', async () => { + // Given + setupTest({ ok: true, json: { id: definitionId, status: 'completed' } }); + + // When + const result = await sonataFlowService.executeWorkflow({ + definitionId, + serviceUrl, + inputData: { var1: 'value1' }, + }); + + // Then + expect(fetch).toHaveBeenCalledWith( + urlToFetch, + expectedFetchRequestInit(), + ); + expect(result).toEqual({ id: definitionId, status: 'completed' }); + expect(loggerMock.debug).toHaveBeenCalledWith( + `Execute workflow result: {"id":"${definitionId}","status":"completed"}`, + ); + // Verify that all other logger methods were not called + expect(loggerMock.debug).toHaveBeenCalledTimes(1); + expect(loggerMock.info).not.toHaveBeenCalled(); + expect(loggerMock.error).not.toHaveBeenCalled(); + expect(loggerMock.warn).not.toHaveBeenCalled(); + expect(loggerMock.child).not.toHaveBeenCalled(); + }); + + it('should include businessKey in the URL if provided', async () => { + // Given + const businessKey = 'key-123'; + setupTest({ ok: true, json: { id: definitionId, status: 'completed' } }); + + // When + const result = await sonataFlowService.executeWorkflow({ + definitionId, + serviceUrl, + inputData, + businessKey, + }); + + // Then + expect(fetch).toHaveBeenCalledWith( + `${serviceUrl}/${definitionId}?businessKey=${businessKey}`, + expectedFetchRequestInit(), + ); + expect(result).toEqual({ id: definitionId, status: 'completed' }); + }); + it('should propagate thrown error when the fetch response is not ok without extra info', async () => { + // When + setupTest({ + ok: false, + status: 500, + statusText: 'Internal Server Error', + json: { details: undefined, stack: undefined }, + }); + + let result; + try { + await runErrorTest(); + } catch (error) { + result = error; + } + + expect(result).toBeDefined(); + }); + it('should propagate thrown exception when the fetch response is not ok with extra info', async () => { + // When + setupTest({ + ok: false, + json: { details: 'Error details test', stack: 'Error stacktrace test' }, + }); + + let result; + try { + await runErrorTest(); + } catch (error) { + result = error; + } + + expect(result).toBeDefined(); + }); + it('should propagate thrown error when fetch throws an error', async () => { + // Given + global.fetch = jest.fn().mockRejectedValue(new Error('Network Error')); + + // When + let result; + try { + await sonataFlowService.executeWorkflow({ + definitionId, + serviceUrl, + inputData: inputData, + }); + } catch (error) { + result = error; + } + + expect(result).toBeDefined(); + }); + }); + + describe('createPrefixFetchErrorMessage', () => { + // Constants + const TEST_URL = 'http://example.com'; + const STATUS_TEXT_BAD_REQUEST = 'Bad Request'; + const STATUS_TEXT_NOT_FOUND = 'Not Found'; + const STATUS_TEXT_INTERNAL_SERVER_ERROR = 'Internal Server Error'; + const DETAILS = 'Some error details'; + const STACK_TRACE = 'Error stack trace'; + + it('should return the correct message with all fields provided', async () => { + // Given + const mockResponseJson = { details: DETAILS, stack: STACK_TRACE }; + const mockResponse = new Response(JSON.stringify(mockResponseJson), { + status: 400, + statusText: STATUS_TEXT_BAD_REQUEST, + }); + + // When + const result = await sonataFlowService.createPrefixFetchErrorMessage( + TEST_URL, + mockResponse, + 'POST', + ); + + // Then + const expectedMessage = `Request POST ${TEST_URL} failed with: StatusCode: 400 StatusText: ${STATUS_TEXT_BAD_REQUEST}, Details: ${DETAILS}, Stack: ${STACK_TRACE}`; + expect(result).toBe(expectedMessage); + }); + + it('should return the correct message without details and stack', async () => { + // Given + const mockResponseJson = {}; + const mockResponse = new Response(JSON.stringify(mockResponseJson), { + status: 404, + statusText: STATUS_TEXT_NOT_FOUND, + }); + + // When + const result = await sonataFlowService.createPrefixFetchErrorMessage( + TEST_URL, + mockResponse, + ); + + // Then + const expectedMessage = `Request GET ${TEST_URL} failed with: StatusCode: 404 StatusText: ${STATUS_TEXT_NOT_FOUND}`; + expect(result).toBe(expectedMessage); + }); + + it('should return the correct message with only status code', async () => { + // Given + const mockResponseJson = {}; + const mockResponse = new Response(JSON.stringify(mockResponseJson), { + status: 500, + }); + + // When + const result = await sonataFlowService.createPrefixFetchErrorMessage( + TEST_URL, + mockResponse, + ); + + // Then + const expectedMessage = `Request GET ${TEST_URL} failed with: StatusCode: 500 Unexpected error`; + expect(result).toBe(expectedMessage); + }); + + it('should return the unexpected error message if no other fields are present', async () => { + // Given + const mockResponseJson = {}; + const mockResponse = new Response(JSON.stringify(mockResponseJson)); + + // When + const result = await sonataFlowService.createPrefixFetchErrorMessage( + TEST_URL, + mockResponse, + ); + + // Then + const expectedMessage = `Request GET ${TEST_URL} failed with: StatusCode: 200 Unexpected error`; + expect(result).toBe(expectedMessage); + }); + + it('should handle response with undefined JSON gracefully', async () => { + // Given + const mockResponse = new Response(undefined, { + status: 500, + statusText: STATUS_TEXT_INTERNAL_SERVER_ERROR, + }); + jest.spyOn(mockResponse, 'json').mockResolvedValue(undefined); + + // When + const result = await sonataFlowService.createPrefixFetchErrorMessage( + TEST_URL, + mockResponse, + ); + + // Then + const expectedMessage = `Request GET ${TEST_URL} failed with: StatusCode: 500 StatusText: ${STATUS_TEXT_INTERNAL_SERVER_ERROR}`; + expect(result).toBe(expectedMessage); + }); + }); +}); diff --git a/plugins/orchestrator-backend/src/service/SonataFlowService.ts b/plugins/orchestrator-backend/src/service/SonataFlowService.ts index a8536ad39c..952e08718b 100644 --- a/plugins/orchestrator-backend/src/service/SonataFlowService.ts +++ b/plugins/orchestrator-backend/src/service/SonataFlowService.ts @@ -2,7 +2,7 @@ import { LoggerService } from '@backstage/backend-plugin-api'; import { extractWorkflowFormat, - FilterInfo, + Filter, fromWorkflowSource, getWorkflowCategory, ProcessInstance, @@ -27,39 +27,26 @@ export class SonataFlowService { definitionId: string; serviceUrl: string; }): Promise { - try { - const urlToFetch = `${args.serviceUrl}/management/processes/${args.definitionId}`; - const response = await fetch(urlToFetch); - - if (response.ok) { - const json = await response.json(); - this.logger.debug( - `Fetch workflow info result: ${JSON.stringify(json)}`, - ); - return json; - } - const responseStr = JSON.stringify(response); - this.logger.error( - `Response was NOT okay when fetch(${urlToFetch}). Received response: ${responseStr}`, - ); - } catch (error) { - this.logger.error(`Error when fetching workflow info: ${error}`); - } + const urlToFetch = `${args.serviceUrl}/management/processes/${args.definitionId}`; + const response = await fetch(urlToFetch); - return undefined; + if (response.ok) { + const json = await response.json(); + this.logger.debug(`Fetch workflow info result: ${JSON.stringify(json)}`); + return json; + } + throw new Error( + await this.createPrefixFetchErrorMessage(urlToFetch, response), + ); } public async fetchWorkflowDefinition( definitionId: string, ): Promise { - try { - const source = - await this.dataIndexService.fetchWorkflowSource(definitionId); - if (source) { - return fromWorkflowSource(source); - } - } catch (error) { - this.logger.error(`Error when fetching workflow definition: ${error}`); + const source = + await this.dataIndexService.fetchWorkflowSource(definitionId); + if (source) { + return fromWorkflowSource(source); } return undefined; } @@ -67,30 +54,23 @@ export class SonataFlowService { public async fetchWorkflowOverviews(args: { definitionIds?: string[]; pagination?: Pagination; - filter?: FilterInfo; + filter?: Filter; }): Promise { const { definitionIds, pagination, filter } = args; - try { - const workflowInfos = await this.dataIndexService.fetchWorkflowInfos({ - definitionIds, - pagination, - filter, - }); - if (!workflowInfos?.length) { - return []; - } - const items = await Promise.all( - workflowInfos - .filter(info => info.source) - .map(info => this.fetchWorkflowOverviewBySource(info.source!)), - ); - return items.filter((item): item is WorkflowOverview => !!item); - } catch (error) { - this.logger.error( - `Error when fetching workflows for workflowOverview: ${error}`, - ); + const workflowInfos = await this.dataIndexService.fetchWorkflowInfos({ + definitionIds, + pagination, + filter, + }); + if (!workflowInfos?.length) { + return []; } - return undefined; + const items = await Promise.all( + workflowInfos + .filter(info => info.source) + .map(info => this.fetchWorkflowOverviewBySource(info.source!)), + ); + return items.filter((item): item is WorkflowOverview => !!item); } public async executeWorkflow(args: { @@ -99,24 +79,24 @@ export class SonataFlowService { inputData: ProcessInstanceVariables; businessKey?: string; }): Promise { - try { - const urlToFetch = args.businessKey - ? `${args.serviceUrl}/${args.definitionId}?businessKey=${args.businessKey}` - : `${args.serviceUrl}/${args.definitionId}`; - - const result = await fetch(urlToFetch, { - method: 'POST', - body: JSON.stringify(args.inputData), - headers: { 'content-type': 'application/json' }, - }); - - const json = await result.json(); + const urlToFetch = args.businessKey + ? `${args.serviceUrl}/${args.definitionId}?businessKey=${args.businessKey}` + : `${args.serviceUrl}/${args.definitionId}`; + + const response = await fetch(urlToFetch, { + method: 'POST', + body: JSON.stringify(args.inputData), + headers: { 'content-type': 'application/json' }, + }); + + if (response.ok) { + const json = await response.json(); this.logger.debug(`Execute workflow result: ${JSON.stringify(json)}`); return json; - } catch (error) { - this.logger.error(`Error when executing workflow: ${error}`); } - return undefined; + throw new Error( + `${await this.createPrefixFetchErrorMessage(urlToFetch, response, 'POST')}`, + ); } public async fetchWorkflowOverview( @@ -189,32 +169,9 @@ export class SonataFlowService { definitionId: string; serviceUrl: string; }): Promise { - try { - const urlToFetch = `${args.serviceUrl}/management/processes/${args.definitionId}`; - const response = await fetch(urlToFetch); - return response.ok; - } catch (error) { - this.logger.debug(`Error when pinging workflow service: ${error}`); - } - return false; - } - - public async retriggerInstanceInError(args: { - definitionId: string; - serviceUrl: string; - instanceId: string; - }): Promise { - const { definitionId, serviceUrl, instanceId } = args; - try { - const urlToFetch = `${serviceUrl}/management/processes/${definitionId}/instances/${instanceId}/retrigger`; - const response = await fetch(urlToFetch, { - method: 'POST', - }); - return response.ok; - } catch (error) { - this.logger.error(`Error when retriggering workflow in error: ${error}`); - } - return false; + const urlToFetch = `${args.serviceUrl}/management/processes/${args.definitionId}`; + const response = await fetch(urlToFetch); + return response.ok; } public async updateInstanceInputData(args: { @@ -224,17 +181,39 @@ export class SonataFlowService { inputData: ProcessInstanceVariables; }): Promise { const { definitionId, serviceUrl, instanceId, inputData } = args; - try { - const urlToFetch = `${serviceUrl}/${definitionId}/${instanceId}`; - const response = await fetch(urlToFetch, { - method: 'PATCH', - body: JSON.stringify(inputData), - headers: { 'content-type': 'application/json' }, - }); - return response.ok; - } catch (error) { - this.logger.error(`Error when updating instance input data: ${error}`); + const urlToFetch = `${serviceUrl}/${definitionId}/${instanceId}`; + const response = await fetch(urlToFetch, { + method: 'PATCH', + body: JSON.stringify(inputData), + headers: { 'content-type': 'application/json' }, + }); + return response.ok; + } + + public async createPrefixFetchErrorMessage( + urlToFetch: string, + response: Response, + httpMethod = 'GET', + ): Promise { + const res = await response.json(); + const errorInfo = []; + let errorMsg = `Request ${httpMethod} ${urlToFetch} failed with: StatusCode: ${response.status}`; + + if (response.statusText) { + errorInfo.push(`StatusText: ${response.statusText}`); + } + if (res?.details) { + errorInfo.push(`Details: ${res?.details}`); } - return false; + if (res?.stack) { + errorInfo.push(`Stack: ${res?.stack}`); + } + if (errorInfo.length > 0) { + errorMsg += ` ${errorInfo.join(', ')}`; + } else { + errorMsg += ' Unexpected error'; + } + + return errorMsg; } } diff --git a/plugins/orchestrator-backend/src/service/__fixtures__/mockProcessDefinitionArgumentsData.ts b/plugins/orchestrator-backend/src/service/__fixtures__/mockProcessDefinitionArgumentsData.ts new file mode 100644 index 0000000000..2cd635a93c --- /dev/null +++ b/plugins/orchestrator-backend/src/service/__fixtures__/mockProcessDefinitionArgumentsData.ts @@ -0,0 +1,105 @@ +import { + IntrospectionField, + TypeKind, + TypeName, +} from '@janus-idp/backstage-plugin-orchestrator-common'; + +export const mockProcessDefinitionArguments = { + __type: { + kind: 'INPUT_OBJECT', + name: 'ProcessDefinitionArgument', + inputFields: [ + { + name: 'and', + type: { + kind: 'LIST', + name: null, + ofType: { + kind: 'NON_NULL', + name: null, + ofType: { + kind: 'INPUT_OBJECT', + name: 'ProcessDefinitionArgument', + ofType: null, + }, + }, + }, + }, + { + name: 'or', + type: { + kind: 'LIST', + name: null, + ofType: { + kind: 'NON_NULL', + name: null, + ofType: { + kind: 'INPUT_OBJECT', + name: 'ProcessDefinitionArgument', + ofType: null, + }, + }, + }, + }, + { + name: 'not', + type: { + kind: 'INPUT_OBJECT', + name: 'ProcessDefinitionArgument', + ofType: null, + }, + }, + { + name: 'id', + type: { + kind: 'INPUT_OBJECT', + name: 'StringArgument', + ofType: null, + }, + }, + { + name: 'name', + type: { + kind: 'INPUT_OBJECT', + name: 'StringArgument', + ofType: null, + }, + }, + { + name: 'version', + type: { + kind: 'INPUT_OBJECT', + name: 'StringArgument', + ofType: null, + }, + }, + ], + }, +}; + +export const mockProcessDefinitionIntrospection: IntrospectionField[] = [ + { + name: 'id', + type: { + kind: TypeKind.InputObject, + name: TypeName.String, + ofType: null, + }, + }, + { + name: 'name', + type: { + kind: TypeKind.InputObject, + name: TypeName.String, + ofType: null, + }, + }, + { + name: 'version', + type: { + kind: TypeKind.InputObject, + name: TypeName.String, + ofType: null, + }, + }, +]; diff --git a/plugins/orchestrator-backend/src/service/__fixtures__/mockProcessInstanceArgumentsData.ts b/plugins/orchestrator-backend/src/service/__fixtures__/mockProcessInstanceArgumentsData.ts new file mode 100644 index 0000000000..089b0c6ec2 --- /dev/null +++ b/plugins/orchestrator-backend/src/service/__fixtures__/mockProcessInstanceArgumentsData.ts @@ -0,0 +1,354 @@ +import { + IntrospectionField, + TypeKind, + TypeName, +} from '@janus-idp/backstage-plugin-orchestrator-common'; + +export const mockProcessInstanceArguments = { + __type: { + kind: 'INPUT_OBJECT', + name: 'ProcessInstanceArgument', + inputFields: [ + { + name: 'and', + type: { + kind: 'LIST', + name: null, + ofType: { + kind: 'NON_NULL', + name: null, + ofType: { + kind: 'INPUT_OBJECT', + name: 'ProcessInstanceArgument', + ofType: null, + }, + }, + }, + }, + { + name: 'or', + type: { + kind: 'LIST', + name: null, + ofType: { + kind: 'NON_NULL', + name: null, + ofType: { + kind: 'INPUT_OBJECT', + name: 'ProcessInstanceArgument', + ofType: null, + }, + }, + }, + }, + { + name: 'not', + type: { + kind: 'INPUT_OBJECT', + name: 'ProcessInstanceArgument', + ofType: null, + }, + }, + { + name: 'id', + type: { + kind: 'INPUT_OBJECT', + name: 'IdArgument', + ofType: null, + }, + }, + { + name: 'processId', + type: { + kind: 'INPUT_OBJECT', + name: 'StringArgument', + ofType: null, + }, + }, + { + name: 'processName', + type: { + kind: 'INPUT_OBJECT', + name: 'StringArgument', + ofType: null, + }, + }, + { + name: 'parentProcessInstanceId', + type: { + kind: 'INPUT_OBJECT', + name: 'IdArgument', + ofType: null, + }, + }, + { + name: 'rootProcessInstanceId', + type: { + kind: 'INPUT_OBJECT', + name: 'IdArgument', + ofType: null, + }, + }, + { + name: 'rootProcessId', + type: { + kind: 'INPUT_OBJECT', + name: 'StringArgument', + ofType: null, + }, + }, + // { + // name: 'state', + // type: { + // kind: 'INPUT_OBJECT', + // name: 'ProcessInstanceStateArgument', + // ofType: null, + // }, + // }, + // { + // name: 'error', + // type: { + // kind: 'INPUT_OBJECT', + // name: 'ProcessInstanceErrorArgument', + // ofType: null, + // }, + // }, + // { + // name: 'nodes', + // type: { + // kind: 'INPUT_OBJECT', + // name: 'NodeInstanceArgument', + // ofType: null, + // }, + // }, + // { + // name: 'milestones', + // type: { + // kind: 'INPUT_OBJECT', + // name: 'MilestoneArgument', + // ofType: null, + // }, + // }, + { + name: 'endpoint', + type: { + kind: 'INPUT_OBJECT', + name: 'StringArgument', + ofType: null, + }, + }, + // { + // name: 'roles', + // type: { + // kind: 'INPUT_OBJECT', + // name: 'StringArrayArgument', + // ofType: null, + // }, + // }, + // { + // name: 'start', + // type: { + // kind: 'INPUT_OBJECT', + // name: 'DateArgument', + // ofType: null, + // }, + // }, + // { + // name: 'end', + // type: { + // kind: 'INPUT_OBJECT', + // name: 'DateArgument', + // ofType: null, + // }, + // }, + // { + // name: 'addons', + // type: { + // kind: 'INPUT_OBJECT', + // name: 'StringArrayArgument', + // ofType: null, + // }, + // }, + // { + // name: 'lastUpdate', + // type: { + // kind: 'INPUT_OBJECT', + // name: 'DateArgument', + // ofType: null, + // }, + // }, + { + name: 'businessKey', + type: { + kind: 'INPUT_OBJECT', + name: 'StringArgument', + ofType: null, + }, + }, + { + name: 'createdBy', + type: { + kind: 'INPUT_OBJECT', + name: 'StringArgument', + ofType: null, + }, + }, + { + name: 'updatedBy', + type: { + kind: 'INPUT_OBJECT', + name: 'StringArgument', + ofType: null, + }, + }, + ], + }, +}; + +export const mockProcessInstanceIntrospection: IntrospectionField[] = [ + { + name: 'id', + type: { + kind: TypeKind.InputObject, + name: TypeName.Id, + ofType: null, + }, + }, + { + name: 'processId', + type: { + kind: TypeKind.InputObject, + name: TypeName.String, + ofType: null, + }, + }, + { + name: 'processName', + type: { + kind: TypeKind.InputObject, + name: TypeName.String, + ofType: null, + }, + }, + { + name: 'parentProcessInstanceId', + type: { + kind: TypeKind.InputObject, + name: TypeName.Id, + ofType: null, + }, + }, + { + name: 'rootProcessInstanceId', + type: { + kind: TypeKind.InputObject, + name: TypeName.Id, + ofType: null, + }, + }, + { + name: 'rootProcessId', + type: { + kind: TypeKind.InputObject, + name: TypeName.String, + ofType: null, + }, + }, + + // { + // name: 'error', + // type: { + // kind: TypeKind.InputObject, + // name: 'ProcessInstanceErrorArgument', + // ofType: null, + // }, + // }, + // { + // name: 'nodes', + // type: { + // kind: TypeKind.InputObject, + // name: 'NodeInstanceArgument', + // ofType: null, + // }, + // }, + // { + // name: 'milestones', + // type: { + // kind: TypeKind.InputObject, + // name: 'MilestoneArgument', + // ofType: null, + // }, + // }, + { + name: 'endpoint', + type: { + kind: TypeKind.InputObject, + name: TypeName.String, + ofType: null, + }, + }, + // { + // name: 'roles', + // type: { + // kind: TypeKind.InputObject, + // name: TypeName.StringArray, + // ofType: null, + // }, + // }, + // { + // name: 'start', + // type: { + // kind: TypeKind.InputObject, + // name: TypeName.Date, + // ofType: null, + // }, + // }, + // { + // name: 'end', + // type: { + // kind: TypeKind.InputObject, + // name: TypeName.Date, + // ofType: null, + // }, + // }, + // { + // name: 'addons', + // type: { + // kind: TypeKind.InputObject, + // name: TypeName.StringArray, + // ofType: null, + // }, + // }, + // { + // name: 'lastUpdate', + // type: { + // kind: TypeKind.InputObject, + // name: TypeName.Date, + // ofType: null, + // }, + // }, + { + name: 'businessKey', + type: { + kind: TypeKind.InputObject, + name: TypeName.String, + ofType: null, + }, + }, + { + name: 'createdBy', + type: { + kind: TypeKind.InputObject, + name: TypeName.String, + ofType: null, + }, + }, + { + name: 'updatedBy', + type: { + kind: TypeKind.InputObject, + name: TypeName.String, + ofType: null, + }, + }, +]; diff --git a/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.test.ts b/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.test.ts index 6f298b16ab..13fece7927 100644 --- a/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.test.ts +++ b/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.test.ts @@ -57,11 +57,12 @@ describe('scenarios to verify mapToWorkflowOverviewDTO', () => { expect(result.name).toBe(overview.name); expect(result.format).toBe(overview.format); expect(result.lastTriggeredMs).toBe(overview.lastTriggeredMs); - expect(result.lastRunStatus).toBe(overview.lastRunStatus); + expect(result.lastRunStatus).toBe( + getProcessInstancesDTOFromString(overview.lastRunStatus), + ); expect(result.category).toBe('assessment'); expect(result.avgDurationMs).toBe(overview.avgDurationMs); expect(result.description).toBe(overview.description); - expect(Object.keys(result).length).toBe(8); }); }); describe('scenarios to verify mapWorkflowCategoryDTOFromString', () => { diff --git a/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.ts b/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.ts index 8b5ffa1f90..09d565f1dd 100644 --- a/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.ts +++ b/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.ts @@ -28,7 +28,16 @@ export function mapToWorkflowOverviewDTO( overview: WorkflowOverview, ): WorkflowOverviewDTO { return { - ...overview, + name: overview.name, + format: overview.format, + workflowId: overview.workflowId, + avgDurationMs: overview.avgDurationMs, + description: overview.description, + lastRunId: overview.lastRunId, + lastRunStatus: overview.lastRunStatus + ? getProcessInstancesDTOFromString(overview.lastRunStatus) + : undefined, + lastTriggeredMs: overview.lastTriggeredMs, category: mapWorkflowCategoryDTOFromString(overview.category), }; } @@ -90,7 +99,7 @@ export function getProcessInstancesDTOFromString( return 'Pending'; default: throw new Error( - 'state is not one of the values of type ProcessInstanceStatusDTO', + `state ${state} is not one of the values of type ProcessInstanceStatusDTO`, ); } } @@ -112,8 +121,17 @@ export function mapToProcessInstanceDTO( } return { - ...processInstance, + id: processInstance.id, + processId: processInstance.processId, + processName: processInstance.processName, + description: processInstance.description, + serviceUrl: processInstance.serviceUrl, + businessKey: processInstance.businessKey, + endpoint: processInstance.endpoint, + error: processInstance.error, category: mapWorkflowCategoryDTO(processInstance.category), + start: processInstance.start, + end: processInstance.end, duration: duration, // @ts-ignore workflowdata: variables?.workflowdata, diff --git a/plugins/orchestrator-backend/src/service/api/v1.test.ts b/plugins/orchestrator-backend/src/service/api/v1.test.ts deleted file mode 100644 index 0676ea5195..0000000000 --- a/plugins/orchestrator-backend/src/service/api/v1.test.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { ProcessInstance } from '@janus-idp/backstage-plugin-orchestrator-common'; - -import { OrchestratorService } from '../OrchestratorService'; -import { V1 } from './v1'; - -// Mocked data -const inputData = {}; - -// Mocked data helpers -const createInstance = (args: Partial): ProcessInstance => ({ - id: args.id || 'instanceId', - processId: args.processId || 'processId', - state: args.state || 'ACTIVE', - serviceUrl: args.serviceUrl || 'http://localhost', - endpoint: args.endpoint || 'http://localhost', - nodes: args.nodes || [], -}); - -// Mocked dependencies -const orchestratorServiceMock = {} as OrchestratorService; -orchestratorServiceMock.fetchInstance = jest.fn(); -orchestratorServiceMock.updateInstanceInputData = jest.fn(); -orchestratorServiceMock.retriggerInstanceInError = jest.fn(); - -// Target -const v1 = new V1(orchestratorServiceMock); - -describe('retriggerInstanceInError', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('should retrigger an instance in error state', async () => { - const instance = createInstance({ state: 'ERROR' }); - - orchestratorServiceMock.fetchInstance = jest - .fn() - .mockResolvedValue(instance); - orchestratorServiceMock.updateInstanceInputData = jest - .fn() - .mockResolvedValue(true); - orchestratorServiceMock.retriggerInstanceInError = jest - .fn() - .mockResolvedValue(true); - - const response = await v1.retriggerInstanceInError(instance.id, inputData); - - expect(response).toStrictEqual({ id: instance.id }); - expect(orchestratorServiceMock.fetchInstance).toHaveBeenCalled(); - expect(orchestratorServiceMock.updateInstanceInputData).toHaveBeenCalled(); - expect(orchestratorServiceMock.retriggerInstanceInError).toHaveBeenCalled(); - }); - - it('should throw an error if the instance is not found', async () => { - orchestratorServiceMock.fetchInstance = jest - .fn() - .mockResolvedValue(undefined); - - const promise = v1.retriggerInstanceInError('unknown', inputData); - - await expect(promise).rejects.toThrow(); - - expect(orchestratorServiceMock.fetchInstance).toHaveBeenCalled(); - expect( - orchestratorServiceMock.updateInstanceInputData, - ).not.toHaveBeenCalled(); - expect( - orchestratorServiceMock.retriggerInstanceInError, - ).not.toHaveBeenCalled(); - }); - - it('should throw an error if instance is not in error state', async () => { - const instance = createInstance({ state: 'ACTIVE' }); - - orchestratorServiceMock.fetchInstance = jest - .fn() - .mockResolvedValue(instance); - - const promise = v1.retriggerInstanceInError(instance.id, inputData); - - await expect(promise).rejects.toThrow(); - - expect(orchestratorServiceMock.fetchInstance).toHaveBeenCalled(); - expect( - orchestratorServiceMock.updateInstanceInputData, - ).not.toHaveBeenCalled(); - expect( - orchestratorServiceMock.retriggerInstanceInError, - ).not.toHaveBeenCalled(); - }); - - it('should throw an error if could not update the instance input data', async () => { - const instance = createInstance({ state: 'ERROR' }); - - orchestratorServiceMock.fetchInstance = jest - .fn() - .mockResolvedValue(instance); - orchestratorServiceMock.updateInstanceInputData = jest - .fn() - .mockResolvedValue(false); - - const promise = v1.retriggerInstanceInError(instance.id, inputData); - - await expect(promise).rejects.toThrow(); - - expect(orchestratorServiceMock.fetchInstance).toHaveBeenCalled(); - expect(orchestratorServiceMock.updateInstanceInputData).toHaveBeenCalled(); - expect( - orchestratorServiceMock.retriggerInstanceInError, - ).not.toHaveBeenCalled(); - }); - - it('should throw an error if could not retrigger the instance', async () => { - const instance = createInstance({ state: 'ERROR' }); - - orchestratorServiceMock.fetchInstance = jest - .fn() - .mockResolvedValue(instance); - orchestratorServiceMock.updateInstanceInputData = jest - .fn() - .mockResolvedValue(true); - orchestratorServiceMock.retriggerInstanceInError = jest - .fn() - .mockResolvedValue(false); - - const promise = v1.retriggerInstanceInError(instance.id, inputData); - - await expect(promise).rejects.toThrow(); - - expect(orchestratorServiceMock.fetchInstance).toHaveBeenCalled(); - expect(orchestratorServiceMock.updateInstanceInputData).toHaveBeenCalled(); - expect(orchestratorServiceMock.retriggerInstanceInError).toHaveBeenCalled(); - }); -}); diff --git a/plugins/orchestrator-backend/src/service/api/v1.ts b/plugins/orchestrator-backend/src/service/api/v1.ts deleted file mode 100644 index eaa4c82a70..0000000000 --- a/plugins/orchestrator-backend/src/service/api/v1.ts +++ /dev/null @@ -1,95 +0,0 @@ -import express from 'express'; - -import { - ProcessInstanceVariables, - WorkflowDefinition, - WorkflowExecutionResponse, -} from '@janus-idp/backstage-plugin-orchestrator-common'; - -import { OrchestratorService } from '../OrchestratorService'; - -export class V1 { - constructor(private readonly orchestratorService: OrchestratorService) {} - - public async getWorkflowById( - definitionId: string, - ): Promise { - const definition = await this.orchestratorService.fetchWorkflowDefinition({ - definitionId, - cacheHandler: 'throw', - }); - - if (!definition) { - throw new Error(`Couldn't fetch workflow definition for ${definitionId}`); - } - - return definition; - } - - public async getWorkflowSourceById(definitionId: string): Promise { - const source = await this.orchestratorService.fetchWorkflowSource({ - definitionId, - cacheHandler: 'throw', - }); - - if (!source) { - throw new Error(`Couldn't fetch workflow source for ${definitionId}`); - } - - return source; - } - - public async retriggerInstanceInError( - instanceId: string, - inputData: ProcessInstanceVariables, - ): Promise { - const instance = await this.orchestratorService.fetchInstance({ - instanceId, - cacheHandler: 'throw', - }); - - if (!instance?.serviceUrl) { - throw new Error(`Couldn't fetch process instance ${instanceId}`); - } - - if (instance.state !== 'ERROR') { - throw new Error( - `Can't retrigger an instance on ${instance.state} state.`, - ); - } - - const isUpdateInstanceInputDataOk = - await this.orchestratorService.updateInstanceInputData({ - definitionId: instance.processId, - instanceId, - inputData, - serviceUrl: instance.serviceUrl, - cacheHandler: 'throw', - }); - - if (!isUpdateInstanceInputDataOk) { - throw new Error(`Couldn't update instance input data for ${instanceId}`); - } - - const isRetriggerInstanceInErrorOk = - await this.orchestratorService.retriggerInstanceInError({ - definitionId: instance.processId, - instanceId, - serviceUrl: instance.serviceUrl, - cacheHandler: 'throw', - }); - - if (!isRetriggerInstanceInErrorOk) { - throw new Error(`Couldn't retrigger instance in error for ${instanceId}`); - } - - return { id: instanceId }; - } - - public extractQueryParam( - req: express.Request, - key: string, - ): string | undefined { - return req.query[key] as string | undefined; - } -} diff --git a/plugins/orchestrator-backend/src/service/api/v2.test.ts b/plugins/orchestrator-backend/src/service/api/v2.test.ts index 69dd340c5d..5ab52c5c30 100644 --- a/plugins/orchestrator-backend/src/service/api/v2.test.ts +++ b/plugins/orchestrator-backend/src/service/api/v2.test.ts @@ -3,7 +3,9 @@ import { Request } from 'express'; import { AssessedProcessInstanceDTO, ExecuteWorkflowResponseDTO, + FieldFilterOperatorEnum, ProcessInstanceListResultDTO, + SearchRequest, toWorkflowYaml, WorkflowOverview, WorkflowOverviewDTO, @@ -11,7 +13,7 @@ import { WorkflowRunStatusDTO, } from '@janus-idp/backstage-plugin-orchestrator-common'; -import { buildPagination } from '../../types/pagination'; +import { buildPagination, buildPaginationTmp } from '../../types/pagination'; import { OrchestratorService } from '../OrchestratorService'; import { mapToWorkflowOverviewDTO } from './mapping/V2Mappings'; import { @@ -23,7 +25,6 @@ import { generateTestWorkflowOverviewList, generateWorkflowDefinition, } from './test-utils'; -import { V1 } from './v1'; import { V2 } from './v2'; jest.mock('../Helper.ts', () => ({ @@ -56,7 +57,7 @@ const createMockOrchestratorService = (): OrchestratorService => { return mockOrchestratorService; }; const mockOrchestratorService = createMockOrchestratorService(); -const v2 = new V2(mockOrchestratorService, new V1(mockOrchestratorService)); +const v2 = new V2(mockOrchestratorService); describe('getWorkflowOverview', () => { beforeEach(() => { @@ -170,6 +171,76 @@ describe('getWorkflowOverview', () => { }); }); + it('filter test', async () => { + // Arrange + // category = "electronics" AND (price <= 1000 OR (brand IN ("Apple", "Samsung") AND brand like 'Apple')) + const mockRequest: SearchRequest = { + filters: { + operator: 'AND', + filters: [ + { + field: 'category', + operator: FieldFilterOperatorEnum.Eq, + value: 'electronics', + }, + { + operator: 'OR', + filters: [ + { + field: 'price', + operator: FieldFilterOperatorEnum.Lte, + value: 1000, + }, + { + operator: 'AND', + filters: [ + { + field: 'brand', + operator: FieldFilterOperatorEnum.In, + value: ['Apple', 'Samsung'], + }, + { + field: 'brand', + operator: FieldFilterOperatorEnum.Like, + value: 'Apple', + }, + ], + }, + ], + }, + ], + }, + paginationInfo: { + offset: 1, + pageSize: 50, + orderBy: 'lastUpdated', + orderDirection: 'DESC', + }, + }; + const mockOverviewsV1 = generateTestWorkflowOverviewList(100, {}); + + ( + mockOrchestratorService.fetchWorkflowOverviews as jest.Mock + ).mockResolvedValue(mockOverviewsV1.items); + + // Act + const result: WorkflowOverviewListResultDTO = await v2.getWorkflowsOverview( + buildPaginationTmp(mockRequest.paginationInfo), + ); + + // Assert + expect(result).toEqual({ + overviews: mockOverviewsV1.items.map((item: WorkflowOverview) => + mapToWorkflowOverviewDTO(item), + ), + paginationInfo: { + offset: 1, + pageSize: 50, + totalCount: mockOverviewsV1.items.length, + }, + }); + }); + it('undefined workflow overview list', async () => { // Arrange const mockRequest: any = { diff --git a/plugins/orchestrator-backend/src/service/api/v2.ts b/plugins/orchestrator-backend/src/service/api/v2.ts index 0c37873067..32bf50b3db 100644 --- a/plugins/orchestrator-backend/src/service/api/v2.ts +++ b/plugins/orchestrator-backend/src/service/api/v2.ts @@ -4,7 +4,7 @@ import { AssessedProcessInstanceDTO, ExecuteWorkflowRequestDTO, ExecuteWorkflowResponseDTO, - FilterInfo, + Filter, ProcessInstance, ProcessInstanceListResultDTO, ProcessInstanceState, @@ -26,20 +26,16 @@ import { mapToWorkflowOverviewDTO, mapToWorkflowRunStatusDTO, } from './mapping/V2Mappings'; -import { V1 } from './v1'; const FETCH_INSTANCE_MAX_ATTEMPTS = 10; const FETCH_INSTANCE_RETRY_DELAY_MS = 1000; export class V2 { - constructor( - private readonly orchestratorService: OrchestratorService, - private readonly v1: V1, - ) {} + constructor(private readonly orchestratorService: OrchestratorService) {} public async getWorkflowsOverview( pagination: Pagination, - filter?: FilterInfo, + filter?: Filter, ): Promise { const overviews = await this.orchestratorService.fetchWorkflowOverviews({ pagination, @@ -74,25 +70,37 @@ export class V2 { } public async getWorkflowById(workflowId: string): Promise { - const resultV1 = await this.v1.getWorkflowSourceById(workflowId); + const resultV1 = await this.getWorkflowSourceById(workflowId); return mapToWorkflowDTO(resultV1); } public async getWorkflowSourceById(workflowId: string): Promise { - const resultV1 = await this.v1.getWorkflowSourceById(workflowId); - return resultV1; + const source = await this.orchestratorService.fetchWorkflowSource({ + definitionId: workflowId, + cacheHandler: 'throw', + }); + + if (!source) { + throw new Error(`Couldn't fetch workflow source for ${workflowId}`); + } + + return source; } public async getInstances( pagination?: Pagination, - filter?: FilterInfo, + filter?: Filter, + workflowId?: string, ): Promise { const instances = await this.orchestratorService.fetchInstances({ pagination, filter, + workflowId, }); - const totalCount = - await this.orchestratorService.fetchInstancesTotalCount(); + const totalCount = await this.orchestratorService.fetchInstancesTotalCount( + workflowId, + filter, + ); const result: ProcessInstanceListResultDTO = { items: instances?.map(mapToProcessInstanceDTO), diff --git a/plugins/orchestrator-backend/src/service/router.ts b/plugins/orchestrator-backend/src/service/router.ts index f771642ad2..c71f624001 100644 --- a/plugins/orchestrator-backend/src/service/router.ts +++ b/plugins/orchestrator-backend/src/service/router.ts @@ -1,26 +1,22 @@ -import { - createLegacyAuthAdapters, - errorHandler, - resolvePackagePath, -} from '@backstage/backend-common'; +import { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter'; import { HttpAuthService, LoggerService, PermissionsService, + resolvePackagePath, + SchedulerService, } from '@backstage/backend-plugin-api'; -import { PluginTaskScheduler } from '@backstage/backend-tasks'; -import { Config } from '@backstage/config'; -import { DiscoveryApi } from '@backstage/core-plugin-api'; +import type { Config } from '@backstage/config'; +import type { DiscoveryApi } from '@backstage/core-plugin-api'; import { AuthorizeResult, BasicPermission, } from '@backstage/plugin-permission-common'; import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; -import { JsonObject, JsonValue } from '@backstage/types'; +import type { JsonObject, JsonValue } from '@backstage/types'; import { fullFormats } from 'ajv-formats/dist/formats'; -import express from 'express'; -import Router from 'express-promise-router'; +import express, { Router } from 'express'; import { Request as HttpRequest } from 'express-serve-static-core'; import { OpenAPIBackend, Request } from 'openapi-backend'; @@ -29,6 +25,7 @@ import { DefaultAuditLogger, } from '@janus-idp/backstage-plugin-audit-log-node'; import { + Filter, openApiDocument, orchestratorPermissions, orchestratorWorkflowExecutePermission, @@ -36,19 +33,14 @@ import { orchestratorWorkflowInstanceReadPermission, orchestratorWorkflowInstancesReadPermission, orchestratorWorkflowReadPermission, - QUERY_PARAM_ASSESSMENT_INSTANCE_ID, QUERY_PARAM_BUSINESS_KEY, QUERY_PARAM_INCLUDE_ASSESSMENT, - QUERY_PARAM_INSTANCE_ID, - WorkflowInputSchemaResponse, } from '@janus-idp/backstage-plugin-orchestrator-common'; import { UnauthorizedError } from '@janus-idp/backstage-plugin-rbac-common'; import * as pkg from '../../package.json'; -import { RouterArgs } from '../routerWrapper'; -import { buildFilter } from '../types/filters'; +import { RouterOptions } from '../routerWrapper'; import { buildPagination } from '../types/pagination'; -import { V1 } from './api/v1'; import { V2 } from './api/v2'; import { INTERNAL_SERVER_ERROR_MESSAGE } from './constants'; import { DataIndexService } from './DataIndexService'; @@ -65,7 +57,6 @@ interface PublicServices { interface RouterApi { openApiBackend: OpenAPIBackend; - v1: V1; v2: V2; } @@ -85,8 +76,8 @@ const authorize = async ( }; export async function createBackendRouter( - args: RouterArgs, -): Promise { + options: RouterOptions, +): Promise { const { config, logger, @@ -95,12 +86,9 @@ export async function createBackendRouter( urlReader, scheduler, permissions, - } = args; - const { auth, httpAuth } = createLegacyAuthAdapters({ - httpAuth: args.httpAuth, - discovery: args.discovery, - auth: args.auth, - }); + auth, + httpAuth, + } = options; const publicServices = initPublicServices(logger, config, scheduler); const routerApi = await initRouterApi(publicServices.orchestratorService); @@ -119,16 +107,6 @@ export async function createBackendRouter( router.use(permissionsIntegrationRouter); router.use('/workflows', express.text()); router.use('/static', express.static(resolvePackagePath(pkg.name, 'static'))); - router.use( - '/docs', - express.static( - resolvePackagePath( - '@janus-idp/backstage-plugin-orchestrator-common', - 'src/generated/docs', - ), - ), - ); - router.get('/health', (_, response) => { logger.info('PONG!'); response.json({ status: 'ok' }); @@ -142,7 +120,6 @@ export async function createBackendRouter( ); setupInternalRoutes( - router, publicServices, routerApi, permissions, @@ -156,17 +133,25 @@ export async function createBackendRouter( throw new Error('next is undefined'); } - routerApi.openApiBackend.handleRequest(req as Request, req, res, next); + return routerApi.openApiBackend.handleRequest( + req as Request, + req, + res, + next, + ); }); - router.use(errorHandler()); + const middleware = MiddlewareFactory.create({ logger, config }); + + router.use(middleware.error()); + return router; } function initPublicServices( logger: LoggerService, config: Config, - scheduler: PluginTaskScheduler, + scheduler: SchedulerService, ): PublicServices { const dataIndexUrl = config.getString('orchestrator.dataIndexService.url'); const dataIndexService = new DataIndexService(dataIndexUrl, logger); @@ -223,16 +208,14 @@ async function initRouterApi( }, }); await openApiBackend.init(); - const v1 = new V1(orchestratorService); - const v2 = new V2(orchestratorService, v1); - return { v1, v2, openApiBackend }; + const v2 = new V2(orchestratorService); + return { v2, openApiBackend }; } // ====================================================== // Internal Backstage API calls to delegate to SonataFlow // ====================================================== function setupInternalRoutes( - router: express.Router, services: PublicServices, routerApi: RouterApi, permissions: PermissionsService, @@ -320,57 +303,16 @@ function setupInternalRoutes( if (decision.result === AuthorizeResult.DENY) { manageDenyAuthorization(endpointName, endpoint, req); } - await routerApi.v2 - .getWorkflowsOverview(buildPagination(req), buildFilter(req)) + return routerApi.v2 + .getWorkflowsOverview(buildPagination(req), getRequestFilters(req)) .then(result => res.json(result)) .catch(error => { auditLogRequestError(error, endpointName, endpoint, req); - res - .status(500) - .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); - next(); + next(error); }); }, ); - // v1 - router.get('/workflows/:workflowId/source', async (req, res) => { - const { - params: { workflowId }, - } = req; - const endpointName = 'WorkflowsWorkflowIdSource'; - const endpoint = `/v1/workflows/${workflowId}/source`; - - auditLogger.auditLog({ - eventName: endpointName, - stage: 'start', - status: 'succeeded', - level: 'debug', - request: req, - message: `Received request to '${endpoint}' endpoint`, - }); - - const decision = await authorize( - req, - orchestratorWorkflowReadPermission, - permissions, - httpAuth, - ); - if (decision.result === AuthorizeResult.DENY) { - manageDenyAuthorization(endpointName, endpoint, req); - } - - try { - const result = await routerApi.v1.getWorkflowSourceById(workflowId); - res.status(200).contentType('text/plain').send(result); - } catch (error) { - res - .status(500) - .contentType('text/plain') - .send((error as Error)?.message || INTERNAL_SERVER_ERROR_MESSAGE); - } - }); - // v2 routerApi.openApiBackend.register( 'getWorkflowSourceById', @@ -400,14 +342,10 @@ function setupInternalRoutes( try { const result = await routerApi.v2.getWorkflowSourceById(workflowId); - res.status(200).contentType('plain/text').send(result); + res.status(200).contentType('text/plain').send(result); } catch (error) { auditLogRequestError(error, endpointName, endpoint, _req); - res - .status(500) - .contentType('plain/text') - .send((error as Error)?.message || INTERNAL_SERVER_ERROR_MESSAGE); - next(); + next(error); } }, ); @@ -415,7 +353,7 @@ function setupInternalRoutes( // v2 routerApi.openApiBackend.register( 'executeWorkflow', - async (c, req: express.Request, res: express.Response) => { + async (c, req: express.Request, res: express.Response, next) => { const workflowId = c.request.params.workflowId as string; const endpointName = 'executeWorkflow'; const endpoint = `/v2/workflows/${workflowId}/execute`; @@ -446,14 +384,12 @@ function setupInternalRoutes( const executeWorkflowRequestDTO = req.body; - await routerApi.v2 + return routerApi.v2 .executeWorkflow(executeWorkflowRequestDTO, workflowId, businessKey) .then(result => res.status(200).json(result)) - .catch((error: { message: string }) => { + .catch(error => { auditLogRequestError(error, endpointName, endpoint, req); - res - .status(500) - .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); + next(error); }); }, ); @@ -484,185 +420,20 @@ function setupInternalRoutes( if (decision.result === AuthorizeResult.DENY) { manageDenyAuthorization(endpointName, endpoint, _req); } - - await routerApi.v2 + return routerApi.v2 .getWorkflowOverviewById(workflowId) .then(result => res.json(result)) - .catch(next); + .catch(error => { + auditLogRequestError(error, endpointName, endpoint, _req); + next(error); + }); }, ); - // v1 - router.get('/workflows/:workflowId/inputSchema', async (req, res) => { - const { - params: { workflowId }, - } = req; - const endpointName = 'WorkflowsWorkflowIdInputSchema'; - const endpoint = `/v1/workflows/${workflowId}/inputSchema`; - - auditLogger.auditLog({ - eventName: endpointName, - stage: 'start', - status: 'succeeded', - level: 'debug', - request: req, - message: `Received request to '${endpoint}' endpoint`, - }); - - const decision = await authorize( - req, - orchestratorWorkflowReadPermission, - permissions, - httpAuth, - ); - if (decision.result === AuthorizeResult.DENY) { - manageDenyAuthorization(endpointName, endpoint, req); - } - - const instanceId = routerApi.v1.extractQueryParam( - req, - QUERY_PARAM_INSTANCE_ID, - ); - const assessmentInstanceId = routerApi.v1.extractQueryParam( - req, - QUERY_PARAM_ASSESSMENT_INSTANCE_ID, - ); - - const workflowDefinition = - await services.orchestratorService.fetchWorkflowInfo({ - definitionId: workflowId, - cacheHandler: 'throw', - }); - - if (!workflowDefinition) { - auditLogRequestError( - new Error(`Couldn't fetch workflow definition ${workflowId}`), - endpointName, - endpoint, - req, - ); - res.status(500).send(`Couldn't fetch workflow definition ${workflowId}`); - return; - } - const serviceUrl = workflowDefinition.serviceUrl; - if (!serviceUrl) { - auditLogRequestError( - new Error(`Service URL is not defined for workflow ${workflowId}`), - endpointName, - endpoint, - req, - ); - res - .status(500) - .send(`Service URL is not defined for workflow ${workflowId}`); - return; - } - - // workflow source - const definition = - await services.orchestratorService.fetchWorkflowDefinition({ - definitionId: workflowId, - cacheHandler: 'throw', - }); - - if (!definition) { - auditLogRequestError( - new Error( - `Couldn't fetch workflow definition of workflow source ${workflowId}`, - ), - endpointName, - endpoint, - req, - ); - res - .status(500) - .send( - `Couldn't fetch workflow definition of workflow source ${workflowId}`, - ); - return; - } - - const response: WorkflowInputSchemaResponse = { - definition, - schemaSteps: [], - isComposedSchema: false, - }; - - if (!definition.dataInputSchema) { - res.status(200).json(response); - return; - } - - const workflowInfo = - await services.orchestratorService.fetchWorkflowInfoOnService({ - definitionId: workflowId, - serviceUrl, - cacheHandler: 'throw', - }); - - if (!workflowInfo) { - auditLogRequestError( - new Error(`couldn't fetch workflow info ${workflowId}`), - endpointName, - endpoint, - req, - ); - res.status(500).send(`couldn't fetch workflow info ${workflowId}`); - return; - } - - if (!workflowInfo.inputSchema) { - auditLogRequestError( - new Error( - `failed to retreive schema ${JSON.stringify( - definition.dataInputSchema, - )}`, - ), - endpointName, - endpoint, - req, - ); - - res - .status(500) - .send( - `failed to retreive schema ${JSON.stringify( - definition.dataInputSchema, - )}`, - ); - return; - } - - const instanceVariables = instanceId - ? await services.orchestratorService.fetchInstanceVariables({ - instanceId, - cacheHandler: 'throw', - }) - : undefined; - - const assessmentInstanceVariables = assessmentInstanceId - ? await services.orchestratorService.fetchInstanceVariables({ - instanceId: assessmentInstanceId, - cacheHandler: 'throw', - }) - : undefined; - - res - .status(200) - .json( - services.dataInputSchemaService.getWorkflowInputSchemaResponse( - definition, - workflowInfo.inputSchema, - instanceVariables, - assessmentInstanceVariables, - ), - ); - }); - // v2 routerApi.openApiBackend.register( 'getWorkflowStatuses', - async (_c, _req: express.Request, res: express.Response) => { + async (_c, _req: express.Request, res: express.Response, next) => { const endpointName = 'getWorkflowStatuses'; const endpoint = '/v2/workflows/instances/statuses'; @@ -683,14 +454,12 @@ function setupInternalRoutes( if (decision.result === AuthorizeResult.DENY) { manageDenyAuthorization(endpointName, endpoint, _req); } - await routerApi.v2 + return routerApi.v2 .getWorkflowStatuses() .then(result => res.status(200).json(result)) - .catch((error: { message: string }) => { + .catch(error => { auditLogRequestError(error, endpointName, endpoint, _req); - res - .status(500) - .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); + next(error); }); }, ); @@ -698,11 +467,129 @@ function setupInternalRoutes( // v2 routerApi.openApiBackend.register( 'getWorkflowInputSchemaById', - async (c, req: express.Request, res: express.Response) => { + async (c, req: express.Request, res: express.Response, next) => { const workflowId = c.request.params.workflowId as string; const instanceId = c.request.query.instanceId as string; const endpointName = 'getWorkflowInputSchemaById'; const endpoint = `/v2/workflows/${workflowId}/inputSchema`; + try { + auditLogger.auditLog({ + eventName: endpointName, + stage: 'start', + status: 'succeeded', + level: 'debug', + request: req, + message: `Received request to '${endpoint}' endpoint`, + }); + const decision = await authorize( + req, + orchestratorWorkflowInstanceReadPermission, + permissions, + httpAuth, + ); + if (decision.result === AuthorizeResult.DENY) { + manageDenyAuthorization(endpointName, endpoint, req); + } + + const workflowDefinition = + await services.orchestratorService.fetchWorkflowInfo({ + definitionId: workflowId, + cacheHandler: 'throw', + }); + + if (!workflowDefinition) { + throw new Error( + `Failed to fetch workflow info for workflow ${workflowId}`, + ); + } + const serviceUrl = workflowDefinition.serviceUrl; + if (!serviceUrl) { + throw new Error( + `Service URL is not defined for workflow ${workflowId}`, + ); + } + + const definition = + await services.orchestratorService.fetchWorkflowDefinition({ + definitionId: workflowId, + cacheHandler: 'throw', + }); + + if (!definition) { + throw new Error( + 'Failed to fetch workflow definition for workflow ${workflowId}', + ); + } + + if (!definition.dataInputSchema) { + res.status(200).json({}); + return; + } + + const instanceVariables = instanceId + ? await services.orchestratorService.fetchInstanceVariables({ + instanceId, + cacheHandler: 'throw', + }) + : undefined; + + const workflowData = instanceVariables + ? services.dataInputSchemaService.extractWorkflowData( + instanceVariables, + ) + : undefined; + + const workflowInfo = await routerApi.v2 + .getWorkflowInputSchemaById(workflowId, serviceUrl) + .catch((error: { message: string }) => { + auditLogRequestError(error, endpointName, endpoint, req); + res.status(500).json({ + message: error.message || INTERNAL_SERVER_ERROR_MESSAGE, + }); + }); + + if ( + !workflowInfo || + !workflowInfo.inputSchema || + !workflowInfo.inputSchema.properties + ) { + res.status(200).json({}); + return; + } + + const inputSchemaProps = workflowInfo.inputSchema.properties; + let inputData; + + if (workflowData) { + inputData = Object.keys(inputSchemaProps) + .filter(k => k in workflowData) + .reduce((result, k) => { + if (!workflowData[k]) { + return result; + } + result[k] = workflowData[k]; + return result; + }, {} as JsonObject); + } + + res.status(200).json({ + inputSchema: workflowInfo.inputSchema, + data: inputData, + }); + } catch (err) { + auditLogRequestError(err, endpointName, endpoint, req); + next(err); + } + }, + ); + + // v2 + routerApi.openApiBackend.register( + 'getWorkflowInstances', + async (c, req: express.Request, res: express.Response, next) => { + const endpointName = 'getWorkflowInstances'; + const workflowId = c.request.params.workflowId as string; + const endpoint = `/v2/workflows/${workflowId}/instances`; auditLogger.auditLog({ eventName: endpointName, @@ -712,125 +599,23 @@ function setupInternalRoutes( request: req, message: `Received request to '${endpoint}' endpoint`, }); + const decision = await authorize( req, - orchestratorWorkflowInstanceReadPermission, + orchestratorWorkflowInstancesReadPermission, permissions, httpAuth, ); if (decision.result === AuthorizeResult.DENY) { manageDenyAuthorization(endpointName, endpoint, req); } - - const workflowDefinition = - await services.orchestratorService.fetchWorkflowInfo({ - definitionId: workflowId, - cacheHandler: 'throw', - }); - - if (!workflowDefinition) { - auditLogRequestError( - new Error(`Couldn't fetch workflow definition ${workflowId}`), - endpointName, - endpoint, - req, - ); - res - .status(500) - .send(`Couldn't fetch workflow definition ${workflowId}`); - return; - } - - const serviceUrl = workflowDefinition.serviceUrl; - if (!serviceUrl) { - auditLogRequestError( - new Error(`Service URL is not defined for workflow ${workflowId}`), - endpointName, - endpoint, - req, - ); - res - .status(500) - .send(`Service URL is not defined for workflow ${workflowId}`); - return; - } - - const definition = - await services.orchestratorService.fetchWorkflowDefinition({ - definitionId: workflowId, - cacheHandler: 'throw', - }); - - if (!definition) { - auditLogRequestError( - new Error( - `Couldn't fetch workflow definition of workflow source ${workflowId}`, - ), - endpointName, - endpoint, - req, - ); - res - .status(500) - .send( - `Couldn't fetch workflow definition of workflow source ${workflowId}`, - ); - return; - } - - if (!definition.dataInputSchema) { - res.status(200).json(res); - return; - } - - const instanceVariables = instanceId - ? await services.orchestratorService.fetchInstanceVariables({ - instanceId, - cacheHandler: 'throw', - }) - : undefined; - - const workflowData = instanceVariables - ? services.dataInputSchemaService.extractWorkflowData(instanceVariables) - : undefined; - - const workflowInfo = await routerApi.v2 - .getWorkflowInputSchemaById(workflowId, serviceUrl) - .catch((error: { message: string }) => { + return routerApi.v2 + .getInstances(buildPagination(req), getRequestFilters(req), workflowId) + .then(result => res.json(result)) + .catch(error => { auditLogRequestError(error, endpointName, endpoint, req); - res - .status(500) - .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); + next(error); }); - - if ( - !workflowInfo || - !workflowInfo.inputSchema || - !workflowInfo.inputSchema.properties - ) { - res.status(200); - return; - } - - const inputSchemaProps = workflowInfo.inputSchema.properties; - let inputData; - - if (workflowData) { - inputData = Object.keys(inputSchemaProps) - .filter(k => k in workflowData) - .reduce((result, k) => { - if (!workflowData[k]) { - return result; - } - result[k] = workflowData[k]; - return result; - }, {} as JsonObject); - } - - res.status(200).json({ - inputSchema: workflowInfo.inputSchema, - data: inputData, - }); }, ); @@ -839,7 +624,7 @@ function setupInternalRoutes( 'getInstances', async (_c, req: express.Request, res: express.Response, next) => { const endpointName = 'getInstances'; - const endpoint = `/v2/instances`; + const endpoint = `/v2/workflows/instances`; auditLogger.auditLog({ eventName: endpointName, @@ -859,10 +644,13 @@ function setupInternalRoutes( if (decision.result === AuthorizeResult.DENY) { manageDenyAuthorization(endpointName, endpoint, req); } - await routerApi.v2 - .getInstances(buildPagination(req), buildFilter(req)) + return routerApi.v2 + .getInstances(buildPagination(req), getRequestFilters(req)) .then(result => res.json(result)) - .catch(next); + .catch(error => { + auditLogRequestError(error, endpointName, endpoint, req); + next(error); + }); }, ); @@ -872,7 +660,7 @@ function setupInternalRoutes( async (c, _req: express.Request, res: express.Response, next) => { const instanceId = c.request.params.instanceId as string; const endpointName = 'getInstanceById'; - const endpoint = `/v2/instances/${instanceId}`; + const endpoint = `/v2/workflows/instances/${instanceId}`; auditLogger.auditLog({ eventName: endpointName, @@ -896,15 +684,12 @@ function setupInternalRoutes( c.request, QUERY_PARAM_INCLUDE_ASSESSMENT, ); - await routerApi.v2 + return routerApi.v2 .getInstanceById(instanceId, !!includeAssessment) .then(result => res.status(200).json(result)) .catch(error => { auditLogRequestError(error, endpointName, endpoint, _req); - res - .status(500) - .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); - next(); + next(error); }); }, ); @@ -914,8 +699,8 @@ function setupInternalRoutes( 'abortWorkflow', async (c, _req, res, next) => { const instanceId = c.request.params.instanceId as string; - const endpointName = 'getInstanceById'; - const endpoint = `/v2/instances/${instanceId}/abort`; + const endpointName = 'abortWorkflow'; + const endpoint = `/v2/workflows/instances/${instanceId}/abort`; auditLogger.auditLog({ eventName: endpointName, @@ -935,56 +720,15 @@ function setupInternalRoutes( if (decision.result === AuthorizeResult.DENY) { manageDenyAuthorization(endpointName, endpoint, _req); } - await routerApi.v2 + return routerApi.v2 .abortWorkflow(instanceId) .then(result => res.json(result)) .catch(error => { auditLogRequestError(error, endpointName, endpoint, _req); - res - .status(500) - .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); - next(); + next(error); }); }, ); - - // v1 - router.post('/instances/:instanceId/retrigger', async (req, res) => { - const { - params: { instanceId }, - } = req; - const endpointName = 'InstancesInstanceIdRetrigger'; - const endpoint = `/v1/instances/${instanceId}/retrigger`; - - auditLogger.auditLog({ - eventName: endpointName, - stage: 'start', - status: 'succeeded', - level: 'debug', - request: req, - message: `Received request to '${endpoint}' endpoint`, - }); - - const decision = await authorize( - req, - orchestratorWorkflowExecutePermission, - permissions, - httpAuth, - ); - if (decision.result === AuthorizeResult.DENY) { - manageDenyAuthorization(endpointName, endpoint, req); - } - - await routerApi.v1 - .retriggerInstanceInError(instanceId, req.body) - .then(result => res.status(200).json(result)) - .catch((error: { message: string }) => { - auditLogRequestError(error, endpointName, endpoint, req); - res - .status(500) - .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); - }); - }); } // ====================================================== @@ -1038,3 +782,7 @@ function setupExternalRoutes( res.status(200).json(result); }); } + +function getRequestFilters(req: HttpRequest): Filter | undefined { + return req.body.filters ? (req.body.filters as Filter) : undefined; +} diff --git a/plugins/orchestrator-backend/src/types/filters.ts b/plugins/orchestrator-backend/src/types/filters.ts deleted file mode 100644 index 07140103ed..0000000000 --- a/plugins/orchestrator-backend/src/types/filters.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Request } from 'express-serve-static-core'; - -export const Operator = { - Equal: 'equal', - In: 'in', -} as const; - -export type OperatorType = (typeof Operator)[keyof typeof Operator]; - -export interface FilterInfo { - fieldName: string; - operator: OperatorType; - fieldValue: FilterValue; -} - -export type FilterValue = boolean | number | string; - -export function buildFilter(req: Request): FilterInfo | undefined { - if (!req.body.filterInfo) { - return undefined; - } - const { fieldName, operator, fieldValue } = req.body.filterInfo as FilterInfo; - - if (fieldName && operator && fieldValue) { - return { - fieldName, - operator, - fieldValue: parseFilterValue(fieldValue), - }; - } - - return undefined; -} - -function parseFilterValue(value: FilterValue): string | number | boolean { - if (typeof value === 'boolean') return value; - if (typeof value === 'string') return `"${value}"`; - if (typeof value === 'number' && !isNaN(Number(value))) return Number(value); - return value; -} diff --git a/plugins/orchestrator-backend/src/types/pagination.ts b/plugins/orchestrator-backend/src/types/pagination.ts index 2dcb22b0b0..884c77a78a 100644 --- a/plugins/orchestrator-backend/src/types/pagination.ts +++ b/plugins/orchestrator-backend/src/types/pagination.ts @@ -40,3 +40,36 @@ export function buildPagination(req: Request): Pagination { } return pagination; } + +export function buildPaginationTmp( + paginationInfo?: PaginationInfoDTO, +): Pagination { + const pagination: Pagination = { + limit: undefined, + offset: undefined, + order: undefined, + sortField: undefined, + }; + + if (!paginationInfo) { + return pagination; + } + const { offset, pageSize, orderBy, orderDirection } = paginationInfo; + + if (!isNaN(Number(offset))) { + pagination.offset = Number(offset); + } + + if (!isNaN(Number(pageSize))) { + pagination.limit = Number(pageSize); + } + + if (orderBy) { + pagination.sortField = String(orderBy); + } + + if (orderDirection) { + pagination.order = String(orderDirection).toUpperCase(); + } + return pagination; +} diff --git a/plugins/orchestrator-common/.eslintignore b/plugins/orchestrator-common/.eslintignore deleted file mode 100644 index 55289f4a23..0000000000 --- a/plugins/orchestrator-common/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -dist-dynamic -dist-scalprum diff --git a/plugins/orchestrator-common/.lintstagedrc.json b/plugins/orchestrator-common/.lintstagedrc.json deleted file mode 100644 index 14b2263def..0000000000 --- a/plugins/orchestrator-common/.lintstagedrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "*": "prettier --ignore-unknown --write", - "*.{js,jsx,ts,tsx,mjs,cjs}": "backstage-cli package lint --fix" -} diff --git a/plugins/orchestrator-common/.prettierignore b/plugins/orchestrator-common/.prettierignore deleted file mode 100644 index fc8357d99e..0000000000 --- a/plugins/orchestrator-common/.prettierignore +++ /dev/null @@ -1,12 +0,0 @@ -dist -dist-types -coverage -.vscode -CHANGELOG.md -generated -templates -*.hbs -renovate.json -dist-dynamic -dist-scalprum -playwright-report diff --git a/plugins/orchestrator-common/.prettierrc.js b/plugins/orchestrator-common/.prettierrc.js deleted file mode 100644 index 84cbac65b5..0000000000 --- a/plugins/orchestrator-common/.prettierrc.js +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-check - -/** @type {import("@ianvs/prettier-plugin-sort-imports").PrettierConfig} */ -module.exports = { - ...require('@spotify/prettier-config'), - plugins: ['@ianvs/prettier-plugin-sort-imports'], - importOrder: [ - '^react(.*)$', - '', - '^@backstage/(.*)$', - '', - '', - '', - '^@janus-idp/(.*)$', - '', - '', - '', - '^[.]', - ], -}; diff --git a/plugins/orchestrator-common/package.json b/plugins/orchestrator-common/package.json index d2181fa980..2c53a1de21 100644 --- a/plugins/orchestrator-common/package.json +++ b/plugins/orchestrator-common/package.json @@ -24,7 +24,7 @@ "homepage": "https://red.ht/rhdh", "repository": { "type": "git", - "url": "git+https://github.com/janus-idp/backstage-plugins.git", + "url": "https://github.com/janus-idp/backstage-plugins", "directory": "plugins/orchestrator-common" }, "bugs": "https://github.com/janus-idp/backstage-plugins/issues", @@ -39,17 +39,14 @@ "files": [ "config.d.ts", "dist", - "src/generated/docs" + "src/generated/docs/html" ], "configSchema": "config.d.ts", "sideEffects": false, "scripts": { "build": "yarn openapi:check && backstage-cli package build", "tsc": "tsc", - "prettier:check": "prettier --ignore-unknown --check .", - "prettier:fix": "prettier --ignore-unknown --write .", - "lint:check": "backstage-cli package lint", - "lint:fix": "backstage-cli package lint --fix", + "lint": "backstage-cli package lint", "test": "backstage-cli package test --passWithNoTests --coverage", "clean": "backstage-cli package clean", "prepack": "backstage-cli package prepack", @@ -67,7 +64,6 @@ "axios": "^1.7.4" }, "devDependencies": { - "prettier": "3.3.3", "@backstage/cli": "0.26.11", "@openapitools/openapi-generator-cli": "2.13.4", "js-yaml-cli": "0.6.0" diff --git a/plugins/orchestrator-common/scripts/openapi.sh b/plugins/orchestrator-common/scripts/openapi.sh index adbda64950..649f9ad886 100755 --- a/plugins/orchestrator-common/scripts/openapi.sh +++ b/plugins/orchestrator-common/scripts/openapi.sh @@ -11,11 +11,13 @@ CLIENT_FOLDER="${GENERATED_FOLDER}/client" openapi_generate() { # TypeScript Client generation + rm -rf ${CLIENT_FOLDER} npx --yes @openapitools/openapi-generator-cli@v2.13.4 generate -g typescript-axios -i ${OPENAPI_SPEC_FILE} -o ${CLIENT_FOLDER} # Docs generation + rm -rf ./src/generated/docs/markdown ./src/generated/docs/html npx --yes @openapitools/openapi-generator-cli@v2.13.4 generate -g markdown -i ${OPENAPI_SPEC_FILE} -o ./src/generated/docs/markdown/ - npx --yes @openapitools/openapi-generator-cli@v2.13.4 generate -g html2 -i ${OPENAPI_SPEC_FILE} -o ./src/generated/docs + npx --yes @openapitools/openapi-generator-cli@v2.13.4 generate -g html2 -i ${OPENAPI_SPEC_FILE} -o ./src/generated/docs/html npx --yes --package=js-yaml-cli@0.6.0 -- yaml2json -f ${OPENAPI_SPEC_FILE} diff --git a/plugins/orchestrator-common/src/QueryParams.ts b/plugins/orchestrator-common/src/QueryParams.ts index 634512373a..78d7fef2e5 100644 --- a/plugins/orchestrator-common/src/QueryParams.ts +++ b/plugins/orchestrator-common/src/QueryParams.ts @@ -1,6 +1,5 @@ export const QUERY_PARAM_BUSINESS_KEY = 'businessKey' as const; export const QUERY_PARAM_INSTANCE_ID = 'instanceId' as const; -export const QUERY_PARAM_INSTANCE_STATE = 'state'; export const QUERY_PARAM_INCLUDE_ASSESSMENT = 'includeAssessment' as const; export const QUERY_PARAM_ASSESSMENT_INSTANCE_ID = 'assessmentInstanceId' as const; diff --git a/plugins/orchestrator-common/src/constants.ts b/plugins/orchestrator-common/src/constants.ts index 7b2aa0fe5c..4d8a90cbea 100644 --- a/plugins/orchestrator-common/src/constants.ts +++ b/plugins/orchestrator-common/src/constants.ts @@ -1,6 +1,6 @@ // Default values for the orchestrator plugin configuration export const DEFAULT_SONATAFLOW_CONTAINER_IMAGE = - 'quay.io/kiegroup/kogito-swf-devmode-nightly:main-2024-04-17'; + 'docker.io/apache/incubator-kie-sonataflow-devmode:latest'; export const DEFAULT_SONATAFLOW_PERSISTENCE_PATH = '/home/kogito/persistence'; export const DEFAULT_SONATAFLOW_BASE_URL = 'http://localhost'; diff --git a/plugins/orchestrator-common/src/generated/.METADATA.sha1 b/plugins/orchestrator-common/src/generated/.METADATA.sha1 index 6582f0cce6..3f69156f77 100644 --- a/plugins/orchestrator-common/src/generated/.METADATA.sha1 +++ b/plugins/orchestrator-common/src/generated/.METADATA.sha1 @@ -1 +1 @@ -4b78d7b47cd608b42ea7e3d02017f23aabdbe936 +795882d18a4731f0a4ba9225cf77929ee0ac6628 diff --git a/plugins/orchestrator-common/src/generated/api/definition.ts b/plugins/orchestrator-common/src/generated/api/definition.ts index 800a335875..898a8a4cb9 100644 --- a/plugins/orchestrator-common/src/generated/api/definition.ts +++ b/plugins/orchestrator-common/src/generated/api/definition.ts @@ -1,5 +1,5 @@ /* eslint-disable */ /* prettier-ignore */ // GENERATED FILE DO NOT EDIT. -const OPENAPI = `{"openapi":"3.1.0","info":{"title":"Orchestratorplugin","description":"APItointeractwithorchestratorplugin","license":{"name":"Apache2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"version":"0.0.1"},"servers":[{"url":"/"}],"paths":{"/v2/workflows/overview":{"post":{"operationId":"getWorkflowsOverview","description":"Getalistofworkflowoverviews","requestBody":{"required":false,"description":"Parametersforretrievingofworkflowoverviews","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOverviewsRequestParams"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowOverviewListResultDTO"}}}},"500":{"description":"Errorfetchingworkflowoverviews","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/overview":{"get":{"operationId":"getWorkflowOverviewById","description":"GetaworkflowoverviewbyID","parameters":[{"name":"workflowId","in":"path","required":true,"description":"Uniqueidentifieroftheworkflow","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowOverviewDTO"}}}},"500":{"description":"Errorfetchingworkflowoverview","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}":{"get":{"operationId":"getWorkflowById","description":"GetaworkflowbyID","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtofetch","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowDTO"}}}},"500":{"description":"Errorfetchingworkflowbyid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/source":{"get":{"operationId":"getWorkflowSourceById","description":"GetaworkflowsourcebyID","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtofetch","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"text/plain":{"schema":{"type":"string"}}}},"500":{"description":"Errorfetchingworkflowsourcebyid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/inputSchema":{"get":{"operationId":"getWorkflowInputSchemaById","description":"GetaworkflowinputschemabyID","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtofetch","required":true,"schema":{"type":"string"}},{"name":"instanceId","in":"query","description":"IDofinstance","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"500":{"description":"Errorfetchingworkflowinputschemabyid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances":{"post":{"operationId":"getInstances","summary":"Getinstances","description":"Retrieveanarrayofinstances","requestBody":{"required":false,"description":"Parametersforretrievinginstances","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetInstancesRequestParams"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessInstanceListResultDTO"}}}},"500":{"description":"Errorfetchinginstances","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances/{instanceId}":{"get":{"summary":"GetWorkflowInstancebyID","operationId":"getInstanceById","parameters":[{"name":"instanceId","in":"path","required":true,"description":"IDoftheworkflowinstance","schema":{"type":"string"}},{"name":"includeAssessment","in":"query","required":false,"description":"Whethertoincludeassessment","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Successfulresponse","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssessedProcessInstanceDTO"}}}},"500":{"description":"Errorfetchinginstance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances/statuses":{"get":{"operationId":"getWorkflowStatuses","summary":"Getworkflowstatuslist","description":"Retrieveanarrayofworkflowstatuses","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowRunStatusDTO"}}}}},"500":{"description":"Errorfetchingworkflowstatuses","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/execute":{"post":{"summary":"Executeaworkflow","operationId":"executeWorkflow","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtoexecute","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteWorkflowRequestDTO"}}}},"responses":{"200":{"description":"Successfulexecution","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteWorkflowResponseDTO"}}}},"500":{"description":"InternalServerError","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/instances/{instanceId}/abort":{"delete":{"summary":"Abortaworkflowinstance","operationId":"abortWorkflow","description":"AbortsaworkflowinstanceidentifiedbytheprovidedinstanceId.","parameters":[{"name":"instanceId","in":"path","required":true,"description":"Theidentifieroftheworkflowinstancetoabort.","schema":{"type":"string"}}],"responses":{"200":{"description":"Successfuloperation","content":{"text/plain":{"schema":{"type":"string"}}}},"500":{"description":"Errorabortingworkflow","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"ErrorResponse":{"description":"TheErrorResponseobjectrepresentsacommonstructureforhandlingerrorsinAPIresponses.Itincludesessentialinformationabouttheerror,suchastheerrormessageandadditionaloptionaldetails.","type":"object","properties":{"message":{"description":"Astringprovidingaconciseandhuman-readabledescriptionoftheencounterederror.ThisfieldisrequiredintheErrorResponseobject.","type":"string","default":"internalservererror"},"additionalInfo":{"description":"Anoptionalfieldthatcancontainadditionalinformationorcontextabouttheerror.Itprovidesflexibilityforincludingextradetailsbasedonspecificerrorscenarios.","type":"string"}},"required":["message"]},"GetInstancesRequestParams":{"type":"object","properties":{"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"},"filterInfo":{"$ref":"#/components/schemas/FilterInfo"}}},"GetOverviewsRequestParams":{"type":"object","properties":{"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"},"filterInfo":{"$ref":"#/components/schemas/FilterInfo"}}},"WorkflowOverviewListResultDTO":{"type":"object","properties":{"overviews":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowOverviewDTO"},"minItems":0},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}}},"WorkflowOverviewDTO":{"type":"object","properties":{"workflowId":{"type":"string","description":"Workflowuniqueidentifier","minLength":1},"name":{"type":"string","description":"Workflowname","minLength":1},"format":{"$ref":"#/components/schemas/WorkflowFormatDTO"},"lastRunId":{"type":"string"},"lastTriggeredMs":{"type":"number","minimum":0},"lastRunStatus":{"type":"string"},"category":{"$ref":"#/components/schemas/WorkflowCategoryDTO"},"avgDurationMs":{"type":"number","minimum":0},"description":{"type":"string"}},"required":["workflowId","format"]},"PaginationInfoDTO":{"type":"object","properties":{"pageSize":{"type":"number"},"offset":{"type":"number"},"totalCount":{"type":"number"},"orderDirection":{"enum":["ASC","DESC"]},"orderBy":{"type":"string"}},"additionalProperties":false},"FilterInfo":{"type":"object","properties":{"fieldName":{"description":"Thenameofthefieldtofilteron","type":"string"},"operator":{"$ref":"#/components/schemas/Operator"},"fieldValue":{"title":"FilterValue","oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}],"description":"Thevaluetofilterby,whichcanbeastring,number,boolean,orProcessInstanceStatusDTO"}},"required":["fieldName","operator","fieldValue"]},"Operator":{"type":"string","description":"Theoperatortouseforfiltering,suchasequalityorinclusion","enum":["equal","in"]},"WorkflowFormatDTO":{"type":"string","description":"Formatoftheworkflowdefinition","enum":["yaml","json"]},"WorkflowCategoryDTO":{"type":"string","description":"Categoryoftheworkflow","enum":["assessment","infrastructure"]},"WorkflowListResultDTO":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowDTO"}},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}},"required":["items","paginationInfo"]},"WorkflowDTO":{"type":"object","properties":{"id":{"type":"string","description":"Workflowuniqueidentifier","minLength":1},"name":{"type":"string","description":"Workflowname","minLength":1},"format":{"$ref":"#/components/schemas/WorkflowFormatDTO"},"category":{"$ref":"#/components/schemas/WorkflowCategoryDTO"},"description":{"type":"string","description":"Descriptionoftheworkflow"},"annotations":{"type":"array","items":{"type":"string"}}},"required":["id","category","format"]},"ProcessInstanceListResultDTO":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ProcessInstanceDTO"}},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}}},"AssessedProcessInstanceDTO":{"type":"object","properties":{"instance":{"$ref":"#/components/schemas/ProcessInstanceDTO"},"assessedBy":{"$ref":"#/components/schemas/ProcessInstanceDTO"}},"required":["instance"]},"ProcessInstanceDTO":{"type":"object","properties":{"id":{"type":"string"},"processId":{"type":"string"},"processName":{"type":"string"},"status":{"$ref":"#/components/schemas/ProcessInstanceStatusDTO"},"endpoint":{"type":"string"},"serviceUrl":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"},"duration":{"type":"string"},"category":{"$ref":"#/components/schemas/WorkflowCategoryDTO"},"description":{"type":"string"},"workflowdata":{"$ref":"#/components/schemas/WorkflowDataDTO"},"businessKey":{"type":"string"},"nodes":{"type":"array","items":{"$ref":"#/components/schemas/NodeInstanceDTO"}},"error":{"$ref":"#/components/schemas/ProcessInstanceErrorDTO"},"variables":{"$ref":"#/components/schemas/ProcessInstanceVariablesDTO"}},"required":["id","processId","nodes"]},"WorkflowDataDTO":{"type":"object","properties":{"workflowoptions":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowOptionsDTO"}}},"additionalProperties":true},"WorkflowOptionsDTO":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowSuggestionDTO"}},"WorkflowSuggestionDTO":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"ProcessInstanceStatusDTO":{"type":"string","description":"Statusoftheworkflowrun","enum":["Active","Error","Completed","Aborted","Suspended","Pending"]},"WorkflowRunStatusDTO":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"ExecuteWorkflowRequestDTO":{"type":"object","properties":{"inputData":{"type":"object","additionalProperties":true}},"required":["inputData"]},"ExecuteWorkflowResponseDTO":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"WorkflowProgressDTO":{"allOf":[{"$ref":"#/components/schemas/NodeInstanceDTO"},{"type":"object","properties":{"status":{"$ref":"#/components/schemas/ProcessInstanceStatusDTO"},"error":{"$ref":"#/components/schemas/ProcessInstanceErrorDTO"}}}]},"NodeInstanceDTO":{"type":"object","properties":{"__typename":{"type":"string","default":"NodeInstance","description":"Typename"},"id":{"type":"string","description":"NodeinstanceID"},"name":{"type":"string","description":"Nodename"},"type":{"type":"string","description":"Nodetype"},"enter":{"type":"string","description":"Datewhenthenodewasentered"},"exit":{"type":"string","description":"Datewhenthenodewasexited(optional)"},"definitionId":{"type":"string","description":"DefinitionID"},"nodeId":{"type":"string","description":"NodeID"}},"required":["id"]},"ProcessInstanceErrorDTO":{"type":"object","properties":{"__typename":{"type":"string","default":"ProcessInstanceError","description":"Typename"},"nodeDefinitionId":{"type":"string","description":"NodedefinitionID"},"message":{"type":"string","description":"Errormessage(optional)"}},"required":["nodeDefinitionId"]},"ProcessInstanceVariablesDTO":{"type":"object","additionalProperties":true}}}}`; +const OPENAPI = `{"openapi":"3.1.0","info":{"title":"Orchestratorplugin","description":"APItointeractwithorchestratorplugin","license":{"name":"Apache2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"version":"0.0.1"},"servers":[{"url":"/"}],"paths":{"/v2/workflows/overview":{"post":{"operationId":"getWorkflowsOverview","description":"Returnsthekeyfieldsoftheworkflowincludingdataonthelastruninstance","requestBody":{"required":false,"description":"Paginationandfilters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowOverviewListResultDTO"}}}},"500":{"description":"Errorfetchingworkflowoverviews","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/overview":{"get":{"operationId":"getWorkflowOverviewById","description":"Returnsthekeyfieldsoftheworkflowincludingdataonthelastruninstance","parameters":[{"name":"workflowId","in":"path","required":true,"description":"Uniqueidentifieroftheworkflow","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowOverviewDTO"}}}},"500":{"description":"Errorfetchingworkflowoverview","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}":{"get":{"operationId":"getWorkflowById","description":"Getfullworkflowinfo","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtofetch","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowDTO"}}}},"500":{"description":"Errorfetchingworkflowbyid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/source":{"get":{"operationId":"getWorkflowSourceById","description":"Gettheworkflow'sdefinition","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtofetch","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"text/plain":{"schema":{"type":"string"}}}},"500":{"description":"Errorfetchingworkflowsourcebyid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/inputSchema":{"get":{"operationId":"getWorkflowInputSchemaById","description":"Gettheworkflowinputschema.Itdefinestheinputfieldsoftheworkflow","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtofetch","required":true,"schema":{"type":"string"}},{"name":"instanceId","in":"query","description":"IDofinstance","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InputSchemaResponseDTO"}}}},"500":{"description":"Errorfetchingworkflowinputschemabyid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances":{"post":{"operationId":"getInstances","summary":"Getinstances","description":"Retrieveanarrayofworkflowexecutions(instances)","requestBody":{"required":false,"description":"Parametersforretrievinginstances","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetInstancesRequest"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessInstanceListResultDTO"}}}},"500":{"description":"Errorfetchinginstances","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/instances":{"post":{"operationId":"getWorkflowInstances","summary":"Getinstancesforaspecificworkflow","description":"Retrieveanarrayofworkflowexecutions(instances)forthegivenworkflow","parameters":[{"name":"workflowId","in":"path","required":true,"description":"IDoftheworkflow","schema":{"type":"string"}}],"requestBody":{"required":false,"description":"Parametersforretrievingworkflowinstances","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessInstanceListResultDTO"}}}},"500":{"description":"Errorfetchinginstances","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances/{instanceId}":{"get":{"summary":"GetWorkflowInstancebyID","description":"Getaworkflowexecution/run(instance)","operationId":"getInstanceById","parameters":[{"name":"instanceId","in":"path","required":true,"description":"IDoftheworkflowinstance","schema":{"type":"string"}},{"name":"includeAssessment","in":"query","required":false,"description":"Whethertoincludeassessment","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Successfulresponse","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssessedProcessInstanceDTO"}}}},"500":{"description":"Errorfetchinginstance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances/statuses":{"get":{"operationId":"getWorkflowStatuses","summary":"Getworkflowstatuslist","description":"Retrievearraywiththestatusofallinstances","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowRunStatusDTO"}}}}},"500":{"description":"Errorfetchingworkflowstatuses","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/{workflowId}/execute":{"post":{"summary":"Executeaworkflow","description":"Executeaworkflow","operationId":"executeWorkflow","parameters":[{"name":"workflowId","in":"path","description":"IDoftheworkflowtoexecute","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteWorkflowRequestDTO"}}}},"responses":{"200":{"description":"Successfulexecution","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteWorkflowResponseDTO"}}}},"500":{"description":"InternalServerError","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/workflows/instances/{instanceId}/abort":{"delete":{"summary":"Abortaworkflowinstance","operationId":"abortWorkflow","description":"AbortsaworkflowinstanceidentifiedbytheprovidedinstanceId.","parameters":[{"name":"instanceId","in":"path","required":true,"description":"Theidentifieroftheworkflowinstancetoabort.","schema":{"type":"string"}}],"responses":{"200":{"description":"Successfuloperation","content":{"text/plain":{"schema":{"type":"string"}}}},"500":{"description":"Errorabortingworkflow","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"ErrorResponse":{"description":"TheErrorResponseobjectrepresentsacommonstructureforhandlingerrorsinAPIresponses.Itincludesessentialinformationabouttheerror,suchastheerrormessageandadditionaloptionaldetails.","type":"object","properties":{"message":{"description":"Astringprovidingaconciseandhuman-readabledescriptionoftheencounterederror.ThisfieldisrequiredintheErrorResponseobject.","type":"string","default":"internalservererror"},"additionalInfo":{"description":"Anoptionalfieldthatcancontainadditionalinformationorcontextabouttheerror.Itprovidesflexibilityforincludingextradetailsbasedonspecificerrorscenarios.","type":"string"}},"required":["message"]},"GetInstancesRequest":{"type":"object","properties":{"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"},"filters":{"$ref":"#/components/schemas/SearchRequest"}}},"GetOverviewsRequestParams":{"type":"object","properties":{"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"},"filters":{"$ref":"#/components/schemas/SearchRequest"}}},"WorkflowOverviewListResultDTO":{"type":"object","properties":{"overviews":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowOverviewDTO"},"minItems":0},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}}},"WorkflowOverviewDTO":{"type":"object","properties":{"workflowId":{"type":"string","description":"Workflowuniqueidentifier","minLength":1},"name":{"type":"string","description":"Workflowname","minLength":1},"format":{"$ref":"#/components/schemas/WorkflowFormatDTO"},"lastRunId":{"type":"string"},"lastTriggeredMs":{"type":"number","minimum":0},"lastRunStatus":{"$ref":"#/components/schemas/ProcessInstanceStatusDTO"},"category":{"$ref":"#/components/schemas/WorkflowCategoryDTO"},"avgDurationMs":{"type":"number","minimum":0},"description":{"type":"string"}},"required":["workflowId","format"]},"PaginationInfoDTO":{"type":"object","properties":{"pageSize":{"type":"number"},"offset":{"type":"number"},"totalCount":{"type":"number"},"orderDirection":{"enum":["ASC","DESC"]},"orderBy":{"type":"string"}},"additionalProperties":false},"WorkflowFormatDTO":{"type":"string","description":"Formatoftheworkflowdefinition","enum":["yaml","json"]},"WorkflowCategoryDTO":{"type":"string","description":"Categoryoftheworkflow","enum":["assessment","infrastructure"]},"WorkflowListResultDTO":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowDTO"}},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}},"required":["items","paginationInfo"]},"WorkflowDTO":{"type":"object","properties":{"id":{"type":"string","description":"Workflowuniqueidentifier","minLength":1},"name":{"type":"string","description":"Workflowname","minLength":1},"format":{"$ref":"#/components/schemas/WorkflowFormatDTO"},"category":{"$ref":"#/components/schemas/WorkflowCategoryDTO"},"description":{"type":"string","description":"Descriptionoftheworkflow"},"annotations":{"type":"array","items":{"type":"string"}}},"required":["id","category","format"]},"ProcessInstanceListResultDTO":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ProcessInstanceDTO"}},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}}},"AssessedProcessInstanceDTO":{"type":"object","properties":{"instance":{"$ref":"#/components/schemas/ProcessInstanceDTO"},"assessedBy":{"$ref":"#/components/schemas/ProcessInstanceDTO"}},"required":["instance"]},"ProcessInstanceDTO":{"type":"object","properties":{"id":{"type":"string"},"processId":{"type":"string"},"processName":{"type":"string"},"status":{"$ref":"#/components/schemas/ProcessInstanceStatusDTO"},"endpoint":{"type":"string"},"serviceUrl":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"},"duration":{"type":"string"},"category":{"$ref":"#/components/schemas/WorkflowCategoryDTO"},"description":{"type":"string"},"workflowdata":{"$ref":"#/components/schemas/WorkflowDataDTO"},"businessKey":{"type":"string"},"nodes":{"type":"array","items":{"$ref":"#/components/schemas/NodeInstanceDTO"}},"error":{"$ref":"#/components/schemas/ProcessInstanceErrorDTO"}},"required":["id","processId","nodes"]},"WorkflowDataDTO":{"type":"object","properties":{"result":{"$ref":"#/components/schemas/WorkflowResultDTO"}},"additionalProperties":true},"WorkflowResultDTO":{"description":"Resultofaworkflowexecution","type":"object","properties":{"completedWith":{"description":"Thestateofworkflowcompletion.","type":"string","enum":["error","success"]},"message":{"description":"High-levelsummaryofthecurrentstatus,free-formtext,humanreadable.","type":"string"},"nextWorkflows":{"description":"Listofworkflowssuggestedtorunnext.Itemsatlowerindexesareofhigherpriority.","type":"array","items":{"type":"object","properties":{"id":{"description":"Workflowidentifier","type":"string"},"name":{"description":"Humanreadabletitledescribingtheworkflow.","type":"string"}},"required":["id","name"]}},"outputs":{"description":"Additionalstructuredoutputofworkflowprocessing.Thiscancontainidentifiersofcreatedresources,linkstoresources,logsorotheroutput.","type":"array","items":{"type":"object","properties":{"key":{"description":"Uniqueidentifieroftheoption.Preferablyhuman-readable.","type":"string"},"value":{"description":"Freeformvalueoftheoption.","anyOf":[{"type":"string"},{"type":"number"}]},"format":{"description":"Moredetailedtypeofthe'value'property.Defaultsto'text'.","enum":["text","number","link"]}},"required":["key","value"]}}}},"ProcessInstanceStatusDTO":{"type":"string","description":"Statusoftheworkflowrun","enum":["Active","Error","Completed","Aborted","Suspended","Pending"]},"WorkflowRunStatusDTO":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}},"ExecuteWorkflowRequestDTO":{"type":"object","properties":{"inputData":{"type":"object","additionalProperties":true}},"required":["inputData"]},"ExecuteWorkflowResponseDTO":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"WorkflowProgressDTO":{"allOf":[{"$ref":"#/components/schemas/NodeInstanceDTO"},{"type":"object","properties":{"status":{"$ref":"#/components/schemas/ProcessInstanceStatusDTO"},"error":{"$ref":"#/components/schemas/ProcessInstanceErrorDTO"}}}]},"NodeInstanceDTO":{"type":"object","properties":{"__typename":{"type":"string","default":"NodeInstance","description":"Typename"},"id":{"type":"string","description":"NodeinstanceID"},"name":{"type":"string","description":"Nodename"},"type":{"type":"string","description":"Nodetype"},"enter":{"type":"string","description":"Datewhenthenodewasentered"},"exit":{"type":"string","description":"Datewhenthenodewasexited(optional)"},"definitionId":{"type":"string","description":"DefinitionID"},"nodeId":{"type":"string","description":"NodeID"}},"required":["id"]},"ProcessInstanceErrorDTO":{"type":"object","properties":{"__typename":{"type":"string","default":"ProcessInstanceError","description":"Typename"},"nodeDefinitionId":{"type":"string","description":"NodedefinitionID"},"message":{"type":"string","description":"Errormessage(optional)"}},"required":["nodeDefinitionId"]},"SearchRequest":{"type":"object","properties":{"filters":{"$ref":"#/components/schemas/Filter"},"paginationInfo":{"$ref":"#/components/schemas/PaginationInfoDTO"}}},"Filter":{"oneOf":[{"$ref":"#/components/schemas/LogicalFilter"},{"$ref":"#/components/schemas/FieldFilter"}]},"LogicalFilter":{"type":"object","required":["operator","filters"],"properties":{"operator":{"type":"string","enum":["AND","OR","NOT"]},"filters":{"type":"array","items":{"$ref":"#/components/schemas/Filter"}}}},"FieldFilter":{"type":"object","required":["field","operator","value"],"properties":{"field":{"type":"string"},"operator":{"type":"string","enum":["EQ","GT","GTE","LT","LTE","IN","IS_NULL","CONTAINS","CONTAINS_ALL","CONTAINS_ANY","LIKE","BETWEEN","FROM","TO"]},"value":{"oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}}]}}},"InputSchemaResponseDTO":{"type":"object","properties":{"inputSchema":{"type":"object"},"data":{"type":"object"}}}}}}`; export const openApiDocument = JSON.parse(OPENAPI); diff --git a/plugins/orchestrator-common/src/generated/client/.openapi-generator/FILES b/plugins/orchestrator-common/src/generated/client/.openapi-generator/FILES index a80cd4f07b..16b445eee6 100644 --- a/plugins/orchestrator-common/src/generated/client/.openapi-generator/FILES +++ b/plugins/orchestrator-common/src/generated/client/.openapi-generator/FILES @@ -1,5 +1,6 @@ .gitignore .npmignore +.openapi-generator-ignore api.ts base.ts common.ts diff --git a/plugins/orchestrator-common/src/generated/client/api.ts b/plugins/orchestrator-common/src/generated/client/api.ts index 1ce7508549..2591512ff0 100644 --- a/plugins/orchestrator-common/src/generated/client/api.ts +++ b/plugins/orchestrator-common/src/generated/client/api.ts @@ -90,55 +90,78 @@ export interface ExecuteWorkflowResponseDTO { /** * * @export - * @interface FilterInfo + * @interface FieldFilter */ -export interface FilterInfo { +export interface FieldFilter { /** - * The name of the field to filter on + * * @type {string} - * @memberof FilterInfo + * @memberof FieldFilter */ - 'fieldName': string; + 'field': string; /** * - * @type {Operator} - * @memberof FilterInfo + * @type {string} + * @memberof FieldFilter */ - 'operator': Operator; + 'operator': FieldFilterOperatorEnum; /** * - * @type {FilterValue} - * @memberof FilterInfo + * @type {FieldFilterValue} + * @memberof FieldFilter */ - 'fieldValue': FilterValue; + 'value': FieldFilterValue; } +export const FieldFilterOperatorEnum = { + Eq: 'EQ', + Gt: 'GT', + Gte: 'GTE', + Lt: 'LT', + Lte: 'LTE', + In: 'IN', + IsNull: 'IS_NULL', + Contains: 'CONTAINS', + ContainsAll: 'CONTAINS_ALL', + ContainsAny: 'CONTAINS_ANY', + Like: 'LIKE', + Between: 'BETWEEN', + From: 'FROM', + To: 'TO' +} as const; + +export type FieldFilterOperatorEnum = typeof FieldFilterOperatorEnum[keyof typeof FieldFilterOperatorEnum]; /** - * @type FilterValue - * The value to filter by, which can be a string, number, boolean, or ProcessInstanceStatusDTO + * @type FieldFilterValue * @export */ -export type FilterValue = boolean | number | string; +export type FieldFilterValue = any | boolean | number | string; + +/** + * @type Filter + * @export + */ +export type Filter = FieldFilter | LogicalFilter; /** * * @export - * @interface GetInstancesRequestParams + * @interface GetInstancesRequest */ -export interface GetInstancesRequestParams { +export interface GetInstancesRequest { /** * * @type {PaginationInfoDTO} - * @memberof GetInstancesRequestParams + * @memberof GetInstancesRequest */ 'paginationInfo'?: PaginationInfoDTO; /** * - * @type {FilterInfo} - * @memberof GetInstancesRequestParams + * @type {SearchRequest} + * @memberof GetInstancesRequest */ - 'filterInfo'?: FilterInfo; + 'filters'?: SearchRequest; } /** * @@ -154,11 +177,58 @@ export interface GetOverviewsRequestParams { 'paginationInfo'?: PaginationInfoDTO; /** * - * @type {FilterInfo} + * @type {SearchRequest} * @memberof GetOverviewsRequestParams */ - 'filterInfo'?: FilterInfo; + 'filters'?: SearchRequest; +} +/** + * + * @export + * @interface InputSchemaResponseDTO + */ +export interface InputSchemaResponseDTO { + /** + * + * @type {object} + * @memberof InputSchemaResponseDTO + */ + 'inputSchema'?: object; + /** + * + * @type {object} + * @memberof InputSchemaResponseDTO + */ + 'data'?: object; } +/** + * + * @export + * @interface LogicalFilter + */ +export interface LogicalFilter { + /** + * + * @type {string} + * @memberof LogicalFilter + */ + 'operator': LogicalFilterOperatorEnum; + /** + * + * @type {Array} + * @memberof LogicalFilter + */ + 'filters': Array; +} + +export const LogicalFilterOperatorEnum = { + And: 'AND', + Or: 'OR', + Not: 'NOT' +} as const; + +export type LogicalFilterOperatorEnum = typeof LogicalFilterOperatorEnum[keyof typeof LogicalFilterOperatorEnum]; + /** * * @export @@ -214,20 +284,6 @@ export interface NodeInstanceDTO { */ 'nodeId'?: string; } -/** - * The operator to use for filtering, such as equality or inclusion - * @export - * @enum {string} - */ - -export const Operator = { - Equal: 'equal', - In: 'in' -} as const; - -export type Operator = typeof Operator[keyof typeof Operator]; - - /** * * @export @@ -369,12 +425,6 @@ export interface ProcessInstanceDTO { * @memberof ProcessInstanceDTO */ 'error'?: ProcessInstanceErrorDTO; - /** - * - * @type {object} - * @memberof ProcessInstanceDTO - */ - 'variables'?: object; } @@ -440,6 +490,25 @@ export const ProcessInstanceStatusDTO = { export type ProcessInstanceStatusDTO = typeof ProcessInstanceStatusDTO[keyof typeof ProcessInstanceStatusDTO]; +/** + * + * @export + * @interface SearchRequest + */ +export interface SearchRequest { + /** + * + * @type {Filter} + * @memberof SearchRequest + */ + 'filters'?: Filter; + /** + * + * @type {PaginationInfoDTO} + * @memberof SearchRequest + */ + 'paginationInfo'?: PaginationInfoDTO; +} /** * Category of the workflow * @export @@ -507,10 +576,10 @@ export interface WorkflowDTO { export interface WorkflowDataDTO { /** * - * @type {Array>} + * @type {WorkflowResultDTO} * @memberof WorkflowDataDTO */ - 'workflowoptions'?: Array>; + 'result'?: WorkflowResultDTO; } /** * Format of the workflow definition @@ -583,10 +652,10 @@ export interface WorkflowOverviewDTO { 'lastTriggeredMs'?: number; /** * - * @type {string} + * @type {ProcessInstanceStatusDTO} * @memberof WorkflowOverviewDTO */ - 'lastRunStatus'?: string; + 'lastRunStatus'?: ProcessInstanceStatusDTO; /** * * @type {WorkflowCategoryDTO} @@ -696,43 +765,123 @@ export interface WorkflowProgressDTO { } +/** + * Result of a workflow execution + * @export + * @interface WorkflowResultDTO + */ +export interface WorkflowResultDTO { + /** + * The state of workflow completion. + * @type {string} + * @memberof WorkflowResultDTO + */ + 'completedWith'?: WorkflowResultDTOCompletedWithEnum; + /** + * High-level summary of the current status, free-form text, human readable. + * @type {string} + * @memberof WorkflowResultDTO + */ + 'message'?: string; + /** + * List of workflows suggested to run next. Items at lower indexes are of higher priority. + * @type {Array} + * @memberof WorkflowResultDTO + */ + 'nextWorkflows'?: Array; + /** + * Additional structured output of workflow processing. This can contain identifiers of created resources, links to resources, logs or other output. + * @type {Array} + * @memberof WorkflowResultDTO + */ + 'outputs'?: Array; +} + +export const WorkflowResultDTOCompletedWithEnum = { + Error: 'error', + Success: 'success' +} as const; + +export type WorkflowResultDTOCompletedWithEnum = typeof WorkflowResultDTOCompletedWithEnum[keyof typeof WorkflowResultDTOCompletedWithEnum]; + /** * * @export - * @interface WorkflowRunStatusDTO + * @interface WorkflowResultDTONextWorkflowsInner */ -export interface WorkflowRunStatusDTO { +export interface WorkflowResultDTONextWorkflowsInner { /** - * + * Workflow identifier * @type {string} - * @memberof WorkflowRunStatusDTO + * @memberof WorkflowResultDTONextWorkflowsInner */ - 'key'?: string; + 'id': string; + /** + * Human readable title describing the workflow. + * @type {string} + * @memberof WorkflowResultDTONextWorkflowsInner + */ + 'name': string; +} +/** + * + * @export + * @interface WorkflowResultDTOOutputsInner + */ +export interface WorkflowResultDTOOutputsInner { + /** + * Unique identifier of the option. Preferably human-readable. + * @type {string} + * @memberof WorkflowResultDTOOutputsInner + */ + 'key': string; /** * + * @type {WorkflowResultDTOOutputsInnerValue} + * @memberof WorkflowResultDTOOutputsInner + */ + 'value': WorkflowResultDTOOutputsInnerValue; + /** + * More detailed type of the \'value\' property. Defaults to \'text\'. * @type {string} - * @memberof WorkflowRunStatusDTO + * @memberof WorkflowResultDTOOutputsInner */ - 'value'?: string; + 'format'?: WorkflowResultDTOOutputsInnerFormatEnum; +} + +export const WorkflowResultDTOOutputsInnerFormatEnum = { + Text: 'text', + Number: 'number', + Link: 'link' +} as const; + +export type WorkflowResultDTOOutputsInnerFormatEnum = typeof WorkflowResultDTOOutputsInnerFormatEnum[keyof typeof WorkflowResultDTOOutputsInnerFormatEnum]; + +/** + * Free form value of the option. + * @export + * @interface WorkflowResultDTOOutputsInnerValue + */ +export interface WorkflowResultDTOOutputsInnerValue { } /** * * @export - * @interface WorkflowSuggestionDTO + * @interface WorkflowRunStatusDTO */ -export interface WorkflowSuggestionDTO { +export interface WorkflowRunStatusDTO { /** * * @type {string} - * @memberof WorkflowSuggestionDTO + * @memberof WorkflowRunStatusDTO */ - 'id'?: string; + 'key'?: string; /** * * @type {string} - * @memberof WorkflowSuggestionDTO + * @memberof WorkflowRunStatusDTO */ - 'name'?: string; + 'value'?: string; } /** @@ -751,7 +900,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati abortWorkflow: async (instanceId: string, options: RawAxiosRequestConfig = {}): Promise => { // verify required parameter 'instanceId' is not null or undefined assertParamExists('abortWorkflow', 'instanceId', instanceId) - const localVarPath = `/v2/instances/{instanceId}/abort` + const localVarPath = `/v2/workflows/instances/{instanceId}/abort` .replace(`{${"instanceId"}}`, encodeURIComponent(String(instanceId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -776,7 +925,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati }; }, /** - * + * Execute a workflow * @summary Execute a workflow * @param {string} workflowId ID of the workflow to execute * @param {ExecuteWorkflowRequestDTO} executeWorkflowRequestDTO @@ -816,7 +965,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati }; }, /** - * + * Get a workflow execution/run (instance) * @summary Get Workflow Instance by ID * @param {string} instanceId ID of the workflow instance * @param {boolean} [includeAssessment] Whether to include assessment @@ -855,13 +1004,13 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati }; }, /** - * Retrieve an array of instances + * Retrieve an array of workflow executions (instances) * @summary Get instances - * @param {GetInstancesRequestParams} [getInstancesRequestParams] Parameters for retrieving instances + * @param {GetInstancesRequest} [getInstancesRequest] Parameters for retrieving instances * @param {*} [options] Override http request option. * @throws {RequiredError} */ - getInstances: async (getInstancesRequestParams?: GetInstancesRequestParams, options: RawAxiosRequestConfig = {}): Promise => { + getInstances: async (getInstancesRequest?: GetInstancesRequest, options: RawAxiosRequestConfig = {}): Promise => { const localVarPath = `/v2/workflows/instances`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -881,7 +1030,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(getInstancesRequestParams, localVarRequestOptions, configuration) + localVarRequestOptions.data = serializeDataIfNeeded(getInstancesRequest, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), @@ -889,7 +1038,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati }; }, /** - * Get a workflow by ID + * Get full workflow info * @param {string} workflowId ID of the workflow to fetch * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -922,7 +1071,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati }; }, /** - * Get a workflow input schema by ID + * Get the workflow input schema. It defines the input fields of the workflow * @param {string} workflowId ID of the workflow to fetch * @param {string} [instanceId] ID of instance * @param {*} [options] Override http request option. @@ -960,7 +1109,45 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati }; }, /** - * Get a workflow overview by ID + * Retrieve an array of workflow executions (instances) for the given workflow + * @summary Get instances for a specific workflow + * @param {string} workflowId ID of the workflow + * @param {SearchRequest} [searchRequest] Parameters for retrieving workflow instances + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getWorkflowInstances: async (workflowId: string, searchRequest?: SearchRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'workflowId' is not null or undefined + assertParamExists('getWorkflowInstances', 'workflowId', workflowId) + const localVarPath = `/v2/workflows/{workflowId}/instances` + .replace(`{${"workflowId"}}`, encodeURIComponent(String(workflowId))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(searchRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Returns the key fields of the workflow including data on the last run instance * @param {string} workflowId Unique identifier of the workflow * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -993,7 +1180,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati }; }, /** - * Get a workflow source by ID + * Get the workflow\'s definition * @param {string} workflowId ID of the workflow to fetch * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1026,7 +1213,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati }; }, /** - * Retrieve an array of workflow statuses + * Retrieve array with the status of all instances * @summary Get workflow status list * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1056,12 +1243,12 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati }; }, /** - * Get a list of workflow overviews - * @param {GetOverviewsRequestParams} [getOverviewsRequestParams] Parameters for retrieving of workflow overviews + * Returns the key fields of the workflow including data on the last run instance + * @param {SearchRequest} [searchRequest] Pagination and filters * @param {*} [options] Override http request option. * @throws {RequiredError} */ - getWorkflowsOverview: async (getOverviewsRequestParams?: GetOverviewsRequestParams, options: RawAxiosRequestConfig = {}): Promise => { + getWorkflowsOverview: async (searchRequest?: SearchRequest, options: RawAxiosRequestConfig = {}): Promise => { const localVarPath = `/v2/workflows/overview`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -1081,7 +1268,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(getOverviewsRequestParams, localVarRequestOptions, configuration) + localVarRequestOptions.data = serializeDataIfNeeded(searchRequest, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), @@ -1112,7 +1299,7 @@ export const DefaultApiFp = function(configuration?: Configuration) { return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * + * Execute a workflow * @summary Execute a workflow * @param {string} workflowId ID of the workflow to execute * @param {ExecuteWorkflowRequestDTO} executeWorkflowRequestDTO @@ -1126,7 +1313,7 @@ export const DefaultApiFp = function(configuration?: Configuration) { return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * + * Get a workflow execution/run (instance) * @summary Get Workflow Instance by ID * @param {string} instanceId ID of the workflow instance * @param {boolean} [includeAssessment] Whether to include assessment @@ -1140,20 +1327,20 @@ export const DefaultApiFp = function(configuration?: Configuration) { return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Retrieve an array of instances + * Retrieve an array of workflow executions (instances) * @summary Get instances - * @param {GetInstancesRequestParams} [getInstancesRequestParams] Parameters for retrieving instances + * @param {GetInstancesRequest} [getInstancesRequest] Parameters for retrieving instances * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async getInstances(getInstancesRequestParams?: GetInstancesRequestParams, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getInstances(getInstancesRequestParams, options); + async getInstances(getInstancesRequest?: GetInstancesRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getInstances(getInstancesRequest, options); const localVarOperationServerIndex = configuration?.serverIndex ?? 0; const localVarOperationServerBasePath = operationServerMap['DefaultApi.getInstances']?.[localVarOperationServerIndex]?.url; return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Get a workflow by ID + * Get full workflow info * @param {string} workflowId ID of the workflow to fetch * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1165,20 +1352,34 @@ export const DefaultApiFp = function(configuration?: Configuration) { return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Get a workflow input schema by ID + * Get the workflow input schema. It defines the input fields of the workflow * @param {string} workflowId ID of the workflow to fetch * @param {string} [instanceId] ID of instance * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async getWorkflowInputSchemaById(workflowId: string, instanceId?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + async getWorkflowInputSchemaById(workflowId: string, instanceId?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getWorkflowInputSchemaById(workflowId, instanceId, options); const localVarOperationServerIndex = configuration?.serverIndex ?? 0; const localVarOperationServerBasePath = operationServerMap['DefaultApi.getWorkflowInputSchemaById']?.[localVarOperationServerIndex]?.url; return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Get a workflow overview by ID + * Retrieve an array of workflow executions (instances) for the given workflow + * @summary Get instances for a specific workflow + * @param {string} workflowId ID of the workflow + * @param {SearchRequest} [searchRequest] Parameters for retrieving workflow instances + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getWorkflowInstances(workflowId: string, searchRequest?: SearchRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getWorkflowInstances(workflowId, searchRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['DefaultApi.getWorkflowInstances']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Returns the key fields of the workflow including data on the last run instance * @param {string} workflowId Unique identifier of the workflow * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1190,7 +1391,7 @@ export const DefaultApiFp = function(configuration?: Configuration) { return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Get a workflow source by ID + * Get the workflow\'s definition * @param {string} workflowId ID of the workflow to fetch * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1202,7 +1403,7 @@ export const DefaultApiFp = function(configuration?: Configuration) { return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Retrieve an array of workflow statuses + * Retrieve array with the status of all instances * @summary Get workflow status list * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1214,13 +1415,13 @@ export const DefaultApiFp = function(configuration?: Configuration) { return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Get a list of workflow overviews - * @param {GetOverviewsRequestParams} [getOverviewsRequestParams] Parameters for retrieving of workflow overviews + * Returns the key fields of the workflow including data on the last run instance + * @param {SearchRequest} [searchRequest] Pagination and filters * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async getWorkflowsOverview(getOverviewsRequestParams?: GetOverviewsRequestParams, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getWorkflowsOverview(getOverviewsRequestParams, options); + async getWorkflowsOverview(searchRequest?: SearchRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getWorkflowsOverview(searchRequest, options); const localVarOperationServerIndex = configuration?.serverIndex ?? 0; const localVarOperationServerBasePath = operationServerMap['DefaultApi.getWorkflowsOverview']?.[localVarOperationServerIndex]?.url; return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); @@ -1246,7 +1447,7 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa return localVarFp.abortWorkflow(instanceId, options).then((request) => request(axios, basePath)); }, /** - * + * Execute a workflow * @summary Execute a workflow * @param {string} workflowId ID of the workflow to execute * @param {ExecuteWorkflowRequestDTO} executeWorkflowRequestDTO @@ -1257,7 +1458,7 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa return localVarFp.executeWorkflow(workflowId, executeWorkflowRequestDTO, options).then((request) => request(axios, basePath)); }, /** - * + * Get a workflow execution/run (instance) * @summary Get Workflow Instance by ID * @param {string} instanceId ID of the workflow instance * @param {boolean} [includeAssessment] Whether to include assessment @@ -1268,17 +1469,17 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa return localVarFp.getInstanceById(instanceId, includeAssessment, options).then((request) => request(axios, basePath)); }, /** - * Retrieve an array of instances + * Retrieve an array of workflow executions (instances) * @summary Get instances - * @param {GetInstancesRequestParams} [getInstancesRequestParams] Parameters for retrieving instances + * @param {GetInstancesRequest} [getInstancesRequest] Parameters for retrieving instances * @param {*} [options] Override http request option. * @throws {RequiredError} */ - getInstances(getInstancesRequestParams?: GetInstancesRequestParams, options?: any): AxiosPromise { - return localVarFp.getInstances(getInstancesRequestParams, options).then((request) => request(axios, basePath)); + getInstances(getInstancesRequest?: GetInstancesRequest, options?: any): AxiosPromise { + return localVarFp.getInstances(getInstancesRequest, options).then((request) => request(axios, basePath)); }, /** - * Get a workflow by ID + * Get full workflow info * @param {string} workflowId ID of the workflow to fetch * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1287,17 +1488,28 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa return localVarFp.getWorkflowById(workflowId, options).then((request) => request(axios, basePath)); }, /** - * Get a workflow input schema by ID + * Get the workflow input schema. It defines the input fields of the workflow * @param {string} workflowId ID of the workflow to fetch * @param {string} [instanceId] ID of instance * @param {*} [options] Override http request option. * @throws {RequiredError} */ - getWorkflowInputSchemaById(workflowId: string, instanceId?: string, options?: any): AxiosPromise { + getWorkflowInputSchemaById(workflowId: string, instanceId?: string, options?: any): AxiosPromise { return localVarFp.getWorkflowInputSchemaById(workflowId, instanceId, options).then((request) => request(axios, basePath)); }, /** - * Get a workflow overview by ID + * Retrieve an array of workflow executions (instances) for the given workflow + * @summary Get instances for a specific workflow + * @param {string} workflowId ID of the workflow + * @param {SearchRequest} [searchRequest] Parameters for retrieving workflow instances + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getWorkflowInstances(workflowId: string, searchRequest?: SearchRequest, options?: any): AxiosPromise { + return localVarFp.getWorkflowInstances(workflowId, searchRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Returns the key fields of the workflow including data on the last run instance * @param {string} workflowId Unique identifier of the workflow * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1306,7 +1518,7 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa return localVarFp.getWorkflowOverviewById(workflowId, options).then((request) => request(axios, basePath)); }, /** - * Get a workflow source by ID + * Get the workflow\'s definition * @param {string} workflowId ID of the workflow to fetch * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1315,7 +1527,7 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa return localVarFp.getWorkflowSourceById(workflowId, options).then((request) => request(axios, basePath)); }, /** - * Retrieve an array of workflow statuses + * Retrieve array with the status of all instances * @summary Get workflow status list * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1324,13 +1536,13 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa return localVarFp.getWorkflowStatuses(options).then((request) => request(axios, basePath)); }, /** - * Get a list of workflow overviews - * @param {GetOverviewsRequestParams} [getOverviewsRequestParams] Parameters for retrieving of workflow overviews + * Returns the key fields of the workflow including data on the last run instance + * @param {SearchRequest} [searchRequest] Pagination and filters * @param {*} [options] Override http request option. * @throws {RequiredError} */ - getWorkflowsOverview(getOverviewsRequestParams?: GetOverviewsRequestParams, options?: any): AxiosPromise { - return localVarFp.getWorkflowsOverview(getOverviewsRequestParams, options).then((request) => request(axios, basePath)); + getWorkflowsOverview(searchRequest?: SearchRequest, options?: any): AxiosPromise { + return localVarFp.getWorkflowsOverview(searchRequest, options).then((request) => request(axios, basePath)); }, }; }; @@ -1355,7 +1567,7 @@ export class DefaultApi extends BaseAPI { } /** - * + * Execute a workflow * @summary Execute a workflow * @param {string} workflowId ID of the workflow to execute * @param {ExecuteWorkflowRequestDTO} executeWorkflowRequestDTO @@ -1368,7 +1580,7 @@ export class DefaultApi extends BaseAPI { } /** - * + * Get a workflow execution/run (instance) * @summary Get Workflow Instance by ID * @param {string} instanceId ID of the workflow instance * @param {boolean} [includeAssessment] Whether to include assessment @@ -1381,19 +1593,19 @@ export class DefaultApi extends BaseAPI { } /** - * Retrieve an array of instances + * Retrieve an array of workflow executions (instances) * @summary Get instances - * @param {GetInstancesRequestParams} [getInstancesRequestParams] Parameters for retrieving instances + * @param {GetInstancesRequest} [getInstancesRequest] Parameters for retrieving instances * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DefaultApi */ - public getInstances(getInstancesRequestParams?: GetInstancesRequestParams, options?: RawAxiosRequestConfig) { - return DefaultApiFp(this.configuration).getInstances(getInstancesRequestParams, options).then((request) => request(this.axios, this.basePath)); + public getInstances(getInstancesRequest?: GetInstancesRequest, options?: RawAxiosRequestConfig) { + return DefaultApiFp(this.configuration).getInstances(getInstancesRequest, options).then((request) => request(this.axios, this.basePath)); } /** - * Get a workflow by ID + * Get full workflow info * @param {string} workflowId ID of the workflow to fetch * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1404,7 +1616,7 @@ export class DefaultApi extends BaseAPI { } /** - * Get a workflow input schema by ID + * Get the workflow input schema. It defines the input fields of the workflow * @param {string} workflowId ID of the workflow to fetch * @param {string} [instanceId] ID of instance * @param {*} [options] Override http request option. @@ -1416,7 +1628,20 @@ export class DefaultApi extends BaseAPI { } /** - * Get a workflow overview by ID + * Retrieve an array of workflow executions (instances) for the given workflow + * @summary Get instances for a specific workflow + * @param {string} workflowId ID of the workflow + * @param {SearchRequest} [searchRequest] Parameters for retrieving workflow instances + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public getWorkflowInstances(workflowId: string, searchRequest?: SearchRequest, options?: RawAxiosRequestConfig) { + return DefaultApiFp(this.configuration).getWorkflowInstances(workflowId, searchRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Returns the key fields of the workflow including data on the last run instance * @param {string} workflowId Unique identifier of the workflow * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1427,7 +1652,7 @@ export class DefaultApi extends BaseAPI { } /** - * Get a workflow source by ID + * Get the workflow\'s definition * @param {string} workflowId ID of the workflow to fetch * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1438,7 +1663,7 @@ export class DefaultApi extends BaseAPI { } /** - * Retrieve an array of workflow statuses + * Retrieve array with the status of all instances * @summary Get workflow status list * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1449,14 +1674,14 @@ export class DefaultApi extends BaseAPI { } /** - * Get a list of workflow overviews - * @param {GetOverviewsRequestParams} [getOverviewsRequestParams] Parameters for retrieving of workflow overviews + * Returns the key fields of the workflow including data on the last run instance + * @param {SearchRequest} [searchRequest] Pagination and filters * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DefaultApi */ - public getWorkflowsOverview(getOverviewsRequestParams?: GetOverviewsRequestParams, options?: RawAxiosRequestConfig) { - return DefaultApiFp(this.configuration).getWorkflowsOverview(getOverviewsRequestParams, options).then((request) => request(this.axios, this.basePath)); + public getWorkflowsOverview(searchRequest?: SearchRequest, options?: RawAxiosRequestConfig) { + return DefaultApiFp(this.configuration).getWorkflowsOverview(searchRequest, options).then((request) => request(this.axios, this.basePath)); } } diff --git a/plugins/orchestrator-common/src/generated/docs/html/.openapi-generator-ignore b/plugins/orchestrator-common/src/generated/docs/html/.openapi-generator-ignore new file mode 100644 index 0000000000..7484ee590a --- /dev/null +++ b/plugins/orchestrator-common/src/generated/docs/html/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/plugins/orchestrator-common/src/generated/docs/html/.openapi-generator/FILES b/plugins/orchestrator-common/src/generated/docs/html/.openapi-generator/FILES new file mode 100644 index 0000000000..af3fdae9a0 --- /dev/null +++ b/plugins/orchestrator-common/src/generated/docs/html/.openapi-generator/FILES @@ -0,0 +1,2 @@ +.openapi-generator-ignore +index.html diff --git a/plugins/orchestrator-common/src/generated/docs/html/.openapi-generator/VERSION b/plugins/orchestrator-common/src/generated/docs/html/.openapi-generator/VERSION new file mode 100644 index 0000000000..8b23b8d47c --- /dev/null +++ b/plugins/orchestrator-common/src/generated/docs/html/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.3.0 \ No newline at end of file diff --git a/plugins/orchestrator-common/src/generated/docs/index.html b/plugins/orchestrator-common/src/generated/docs/html/index.html similarity index 96% rename from plugins/orchestrator-common/src/generated/docs/index.html rename to plugins/orchestrator-common/src/generated/docs/html/index.html index 0cc6657e92..fc3baae94b 100644 --- a/plugins/orchestrator-common/src/generated/docs/index.html +++ b/plugins/orchestrator-common/src/generated/docs/html/index.html @@ -882,45 +882,61 @@ } }; defs["ExecuteWorkflowResponseDTO"] = { + "required" : [ "id" ], "properties" : { "id" : { "type" : "string" } } }; - defs["FilterInfo"] = { - "required" : [ "fieldName", "fieldValue", "operator" ], + defs["FieldFilter"] = { + "required" : [ "field", "operator", "value" ], "properties" : { - "fieldName" : { - "type" : "string", - "description" : "The name of the field to filter on" + "field" : { + "type" : "string" }, "operator" : { - "$ref" : "#/components/schemas/Operator" + "type" : "string", + "enum" : [ "EQ", "GT", "GTE", "LT", "LTE", "IN", "IS_NULL", "CONTAINS", "CONTAINS_ALL", "CONTAINS_ANY", "LIKE", "BETWEEN", "FROM", "TO" ] }, - "fieldValue" : { - "$ref" : "#/components/schemas/FilterValue" + "value" : { + "$ref" : "#/components/schemas/FieldFilter_value" } } }; - defs["FilterValue"] = { - "title" : "FilterValue", - "description" : "The value to filter by, which can be a string, number, boolean, or ProcessInstanceStatusDTO", + defs["FieldFilter_value"] = { "oneOf" : [ { "type" : "string" }, { "type" : "number" }, { "type" : "boolean" + }, { + "items" : { + "oneOf" : [ { + "type" : "string" + }, { + "type" : "number" + }, { + "type" : "boolean" + } ] + } } ] }; - defs["GetInstancesRequestParams"] = { + defs["Filter"] = { + "oneOf" : [ { + "$ref" : "#/components/schemas/LogicalFilter" + }, { + "$ref" : "#/components/schemas/FieldFilter" + } ] +}; + defs["GetInstancesRequest"] = { "properties" : { "paginationInfo" : { "$ref" : "#/components/schemas/PaginationInfoDTO" }, - "filterInfo" : { - "$ref" : "#/components/schemas/FilterInfo" + "filters" : { + "$ref" : "#/components/schemas/SearchRequest" } } }; @@ -929,8 +945,33 @@ "paginationInfo" : { "$ref" : "#/components/schemas/PaginationInfoDTO" }, - "filterInfo" : { - "$ref" : "#/components/schemas/FilterInfo" + "filters" : { + "$ref" : "#/components/schemas/SearchRequest" + } + } +}; + defs["InputSchemaResponseDTO"] = { + "properties" : { + "inputSchema" : { + "type" : "object" + }, + "data" : { + "type" : "object" + } + } +}; + defs["LogicalFilter"] = { + "required" : [ "filters", "operator" ], + "properties" : { + "operator" : { + "type" : "string", + "enum" : [ "AND", "OR", "NOT" ] + }, + "filters" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Filter" + } } } }; @@ -971,11 +1012,6 @@ "description" : "Node ID" } } -}; - defs["Operator"] = { - "type" : "string", - "description" : "The operator to use for filtering, such as equality or inclusion", - "enum" : [ "equal", "in" ] }; defs["PaginationInfoDTO"] = { "properties" : { @@ -1048,9 +1084,6 @@ }, "error" : { "$ref" : "#/components/schemas/ProcessInstanceErrorDTO" - }, - "variables" : { - "$ref" : "#/components/schemas/ProcessInstanceVariablesDTO" } } }; @@ -1089,6 +1122,16 @@ "type" : "string", "description" : "Status of the workflow run", "enum" : [ "Active", "Error", "Completed", "Aborted", "Suspended", "Pending" ] +}; + defs["SearchRequest"] = { + "properties" : { + "filters" : { + "$ref" : "#/components/schemas/Filter" + }, + "paginationInfo" : { + "$ref" : "#/components/schemas/PaginationInfoDTO" + } + } }; defs["WorkflowCategoryDTO"] = { "type" : "string", @@ -1128,11 +1171,8 @@ }; defs["WorkflowDataDTO"] = { "properties" : { - "workflowoptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/WorkflowOptionsDTO" - } + "result" : { + "$ref" : "#/components/schemas/WorkflowResultDTO" } }, "additionalProperties" : false @@ -1180,7 +1220,7 @@ "type" : "number" }, "lastRunStatus" : { - "type" : "string" + "$ref" : "#/components/schemas/ProcessInstanceStatusDTO" }, "category" : { "$ref" : "#/components/schemas/WorkflowCategoryDTO" @@ -1222,22 +1262,78 @@ } } ] }; - defs["WorkflowRunStatusDTO"] = { + defs["WorkflowResultDTO"] = { + "properties" : { + "completedWith" : { + "type" : "string", + "description" : "The state of workflow completion.", + "enum" : [ "error", "success" ] + }, + "message" : { + "type" : "string", + "description" : "High-level summary of the current status, free-form text, human readable." + }, + "nextWorkflows" : { + "type" : "array", + "description" : "List of workflows suggested to run next. Items at lower indexes are of higher priority.", + "items" : { + "$ref" : "#/components/schemas/WorkflowResultDTO_nextWorkflows_inner" + } + }, + "outputs" : { + "type" : "array", + "description" : "Additional structured output of workflow processing. This can contain identifiers of created resources, links to resources, logs or other output.", + "items" : { + "$ref" : "#/components/schemas/WorkflowResultDTO_outputs_inner" + } + } + }, + "description" : "Result of a workflow execution" +}; + defs["WorkflowResultDTO_nextWorkflows_inner"] = { + "required" : [ "id", "name" ], + "properties" : { + "id" : { + "type" : "string", + "description" : "Workflow identifier" + }, + "name" : { + "type" : "string", + "description" : "Human readable title describing the workflow." + } + } +}; + defs["WorkflowResultDTO_outputs_inner"] = { + "required" : [ "key", "value" ], "properties" : { "key" : { - "type" : "string" + "type" : "string", + "description" : "Unique identifier of the option. Preferably human-readable." }, "value" : { - "type" : "string" + "$ref" : "#/components/schemas/WorkflowResultDTO_outputs_inner_value" + }, + "format" : { + "type" : "string", + "description" : "More detailed type of the 'value' property. Defaults to 'text'.", + "enum" : [ "text", "number", "link" ] } } }; - defs["WorkflowSuggestionDTO"] = { + defs["WorkflowResultDTO_outputs_inner_value"] = { + "description" : "Free form value of the option.", + "anyOf" : [ { + "type" : "string" + }, { + "type" : "number" + } ] +}; + defs["WorkflowRunStatusDTO"] = { "properties" : { - "id" : { + "key" : { "type" : "string" }, - "name" : { + "value" : { "type" : "string" } } @@ -1279,6 +1375,9 @@
  • getWorkflowInputSchemaById
  • +
  • + getWorkflowInstances +
  • getWorkflowOverviewById
  • @@ -1326,7 +1425,7 @@

    abortWorkflow

    Aborts a workflow instance identified by the provided instanceId.


    -
    /v2/instances/{instanceId}/abort
    +
    /v2/workflows/instances/{instanceId}/abort

    Usage and SDK Samples

    @@ -1350,7 +1449,7 @@

    Usage and SDK Samples

    curl -X DELETE \
      -H "Accept: text/plain,application/json" \
    - "http://localhost/v2/instances/{instanceId}/abort"
    + "http://localhost/v2/workflows/instances/{instanceId}/abort"
     
    @@ -1740,7 +1839,7 @@

    executeWorkflow

    -

    +

    Execute a workflow


    /v2/workflows/{workflowId}/execute
    @@ -2218,7 +2317,7 @@

    getInstanceById

    -

    +

    Get a workflow execution/run (instance)


    /v2/workflows/instances/{instanceId}
    @@ -2675,7 +2774,7 @@

    getInstances

    -

    Retrieve an array of instances

    +

    Retrieve an array of workflow executions (instances)


    /v2/workflows/instances
    @@ -2712,9 +2811,18 @@

    Usage and SDK Samples

    "orderBy" : "orderBy", "totalCount" : 5.637376656633329 }, - "filterInfo" : { - "fieldName" : "fieldName", - "fieldValue" : "FilterValue" + "filters" : { + "paginationInfo" : { + "offset" : 5.962133916683182, + "pageSize" : 1.4658129805029452, + "orderDirection" : "ASC", + "orderBy" : "orderBy", + "totalCount" : 5.637376656633329 + }, + "filters" : { + "filters" : [ null, null ], + "operator" : "AND" + } } }' @@ -2733,10 +2841,10 @@

    Usage and SDK Samples

    // Create an instance of the API class DefaultApi apiInstance = new DefaultApi(); - GetInstancesRequestParams getInstancesRequestParams = ; // GetInstancesRequestParams | + GetInstancesRequest getInstancesRequest = ; // GetInstancesRequest | try { - ProcessInstanceListResultDTO result = apiInstance.getInstances(getInstancesRequestParams); + ProcessInstanceListResultDTO result = apiInstance.getInstances(getInstancesRequest); System.out.println(result); } catch (ApiException e) { System.err.println("Exception when calling DefaultApi#getInstances"); @@ -2752,10 +2860,10 @@

    Usage and SDK Samples

    final api_instance = DefaultApi(); -final GetInstancesRequestParams getInstancesRequestParams = new GetInstancesRequestParams(); // GetInstancesRequestParams | +final GetInstancesRequest getInstancesRequest = new GetInstancesRequest(); // GetInstancesRequest | try { - final result = await api_instance.getInstances(getInstancesRequestParams); + final result = await api_instance.getInstances(getInstancesRequest); print(result); } catch (e) { print('Exception when calling DefaultApi->getInstances: $e\n'); @@ -2770,10 +2878,10 @@

    Usage and SDK Samples

    public class DefaultApiExample { public static void main(String[] args) { DefaultApi apiInstance = new DefaultApi(); - GetInstancesRequestParams getInstancesRequestParams = ; // GetInstancesRequestParams | + GetInstancesRequest getInstancesRequest = ; // GetInstancesRequest | try { - ProcessInstanceListResultDTO result = apiInstance.getInstances(getInstancesRequestParams); + ProcessInstanceListResultDTO result = apiInstance.getInstances(getInstancesRequest); System.out.println(result); } catch (ApiException e) { System.err.println("Exception when calling DefaultApi#getInstances"); @@ -2791,10 +2899,10 @@

    Usage and SDK Samples

    // Create an instance of the API class DefaultApi *apiInstance = [[DefaultApi alloc] init]; -GetInstancesRequestParams *getInstancesRequestParams = ; // (optional) +GetInstancesRequest *getInstancesRequest = ; // (optional) // Get instances -[apiInstance getInstancesWith:getInstancesRequestParams +[apiInstance getInstancesWith:getInstancesRequest completionHandler: ^(ProcessInstanceListResultDTO output, NSError* error) { if (output) { NSLog(@"%@", output); @@ -2812,7 +2920,7 @@

    Usage and SDK Samples

    // Create an instance of the API class var api = new OrchestratorPlugin.DefaultApi() var opts = { - 'getInstancesRequestParams': // {GetInstancesRequestParams} + 'getInstancesRequest': // {GetInstancesRequest} }; var callback = function(error, data, response) { @@ -2845,11 +2953,11 @@

    Usage and SDK Samples

    // Create an instance of the API class var apiInstance = new DefaultApi(); - var getInstancesRequestParams = new GetInstancesRequestParams(); // GetInstancesRequestParams | (optional) + var getInstancesRequest = new GetInstancesRequest(); // GetInstancesRequest | (optional) try { // Get instances - ProcessInstanceListResultDTO result = apiInstance.getInstances(getInstancesRequestParams); + ProcessInstanceListResultDTO result = apiInstance.getInstances(getInstancesRequest); Debug.WriteLine(result); } catch (Exception e) { Debug.Print("Exception when calling DefaultApi.getInstances: " + e.Message ); @@ -2866,10 +2974,10 @@

    Usage and SDK Samples

    // Create an instance of the API class $api_instance = new OpenAPITools\Client\Api\DefaultApi(); -$getInstancesRequestParams = ; // GetInstancesRequestParams | +$getInstancesRequest = ; // GetInstancesRequest | try { - $result = $api_instance->getInstances($getInstancesRequestParams); + $result = $api_instance->getInstances($getInstancesRequest); print_r($result); } catch (Exception $e) { echo 'Exception when calling DefaultApi->getInstances: ', $e->getMessage(), PHP_EOL; @@ -2884,10 +2992,10 @@

    Usage and SDK Samples

    # Create an instance of the API class my $api_instance = WWW::OPenAPIClient::DefaultApi->new(); -my $getInstancesRequestParams = WWW::OPenAPIClient::Object::GetInstancesRequestParams->new(); # GetInstancesRequestParams | +my $getInstancesRequest = WWW::OPenAPIClient::Object::GetInstancesRequest->new(); # GetInstancesRequest | eval { - my $result = $api_instance->getInstances(getInstancesRequestParams => $getInstancesRequestParams); + my $result = $api_instance->getInstances(getInstancesRequest => $getInstancesRequest); print Dumper($result); }; if ($@) { @@ -2904,11 +3012,11 @@

    Usage and SDK Samples

    # Create an instance of the API class api_instance = openapi_client.DefaultApi() -getInstancesRequestParams = # GetInstancesRequestParams | (optional) +getInstancesRequest = # GetInstancesRequest | (optional) try: # Get instances - api_response = api_instance.get_instances(getInstancesRequestParams=getInstancesRequestParams) + api_response = api_instance.get_instances(getInstancesRequest=getInstancesRequest) pprint(api_response) except ApiException as e: print("Exception when calling DefaultApi->getInstances: %s\n" % e) @@ -2918,10 +3026,10 @@

    Usage and SDK Samples

    extern crate DefaultApi;
     
     pub fn main() {
    -    let getInstancesRequestParams = ; // GetInstancesRequestParams
    +    let getInstancesRequest = ; // GetInstancesRequest
     
         let mut context = DefaultApi::Context::default();
    -    let result = client.getInstances(getInstancesRequestParams, &context).wait();
    +    let result = client.getInstances(getInstancesRequest, &context).wait();
     
         println!("{:?}", result);
     }
    @@ -2944,7 +3052,7 @@ 

    Parameters

    Name Description - getInstancesRequestParams + getInstancesRequest

    Parameters for retrieving instances

    -
    +
    @@ -3125,7 +3233,7 @@

    getWorkflowById

    -

    Get a workflow by ID

    +

    Get full workflow info


    /v2/workflows/{workflowId}
    @@ -3539,7 +3647,7 @@

    getWorkflowInputSchemaById

    -

    Get a workflow input schema by ID

    +

    Get the workflow input schema. It defines the input fields of the workflow


    /v2/workflows/{workflowId}/inputSchema
    @@ -3587,7 +3695,7 @@

    Usage and SDK Samples

    String instanceId = instanceId_example; // String | ID of instance try { - Object result = apiInstance.getWorkflowInputSchemaById(workflowId, instanceId); + InputSchemaResponseDTO result = apiInstance.getWorkflowInputSchemaById(workflowId, instanceId); System.out.println(result); } catch (ApiException e) { System.err.println("Exception when calling DefaultApi#getWorkflowInputSchemaById"); @@ -3626,7 +3734,7 @@

    Usage and SDK Samples

    String instanceId = instanceId_example; // String | ID of instance try { - Object result = apiInstance.getWorkflowInputSchemaById(workflowId, instanceId); + InputSchemaResponseDTO result = apiInstance.getWorkflowInputSchemaById(workflowId, instanceId); System.out.println(result); } catch (ApiException e) { System.err.println("Exception when calling DefaultApi#getWorkflowInputSchemaById"); @@ -3649,7 +3757,7 @@

    Usage and SDK Samples

    [apiInstance getWorkflowInputSchemaByIdWith:workflowId instanceId:instanceId - completionHandler: ^(Object output, NSError* error) { + completionHandler: ^(InputSchemaResponseDTO output, NSError* error) { if (output) { NSLog(@"%@", output); } @@ -3704,7 +3812,7 @@

    Usage and SDK Samples

    var instanceId = instanceId_example; // String | ID of instance (optional) (default to null) try { - Object result = apiInstance.getWorkflowInputSchemaById(workflowId, instanceId); + InputSchemaResponseDTO result = apiInstance.getWorkflowInputSchemaById(workflowId, instanceId); Debug.WriteLine(result); } catch (Exception e) { Debug.Print("Exception when calling DefaultApi.getWorkflowInputSchemaById: " + e.Message ); @@ -3984,6 +4092,497 @@


    +
    +
    +
    +

    getWorkflowInstances

    +

    Get instances for a specific workflow

    +
    +
    +
    +

    +

    Retrieve an array of workflow executions (instances) for the given workflow

    +

    +
    +
    /v2/workflows/{workflowId}/instances
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X POST \
    + -H "Accept: application/json" \
    + -H "Content-Type: application/json" \
    + "http://localhost/v2/workflows/{workflowId}/instances" \
    + -d '{
    +  "paginationInfo" : {
    +    "offset" : 5.962133916683182,
    +    "pageSize" : 1.4658129805029452,
    +    "orderDirection" : "ASC",
    +    "orderBy" : "orderBy",
    +    "totalCount" : 5.637376656633329
    +  },
    +  "filters" : {
    +    "filters" : [ null, null ],
    +    "operator" : "AND"
    +  }
    +}'
    +
    +
    +
    +
    import org.openapitools.client.*;
    +import org.openapitools.client.auth.*;
    +import org.openapitools.client.model.*;
    +import org.openapitools.client.api.DefaultApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class DefaultApiExample {
    +    public static void main(String[] args) {
    +
    +        // Create an instance of the API class
    +        DefaultApi apiInstance = new DefaultApi();
    +        String workflowId = workflowId_example; // String | ID of the workflow
    +        SearchRequest searchRequest = ; // SearchRequest | 
    +
    +        try {
    +            ProcessInstanceListResultDTO result = apiInstance.getWorkflowInstances(workflowId, searchRequest);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DefaultApi#getWorkflowInstances");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    +
    + +
    +
    import 'package:openapi/api.dart';
    +
    +final api_instance = DefaultApi();
    +
    +final String workflowId = new String(); // String | ID of the workflow
    +final SearchRequest searchRequest = new SearchRequest(); // SearchRequest | 
    +
    +try {
    +    final result = await api_instance.getWorkflowInstances(workflowId, searchRequest);
    +    print(result);
    +} catch (e) {
    +    print('Exception when calling DefaultApi->getWorkflowInstances: $e\n');
    +}
    +
    +
    +
    + +
    +
    import org.openapitools.client.api.DefaultApi;
    +
    +public class DefaultApiExample {
    +    public static void main(String[] args) {
    +        DefaultApi apiInstance = new DefaultApi();
    +        String workflowId = workflowId_example; // String | ID of the workflow
    +        SearchRequest searchRequest = ; // SearchRequest | 
    +
    +        try {
    +            ProcessInstanceListResultDTO result = apiInstance.getWorkflowInstances(workflowId, searchRequest);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DefaultApi#getWorkflowInstances");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    
    +
    +// Create an instance of the API class
    +DefaultApi *apiInstance = [[DefaultApi alloc] init];
    +String *workflowId = workflowId_example; // ID of the workflow (default to null)
    +SearchRequest *searchRequest = ; //  (optional)
    +
    +// Get instances for a specific workflow
    +[apiInstance getWorkflowInstancesWith:workflowId
    +    searchRequest:searchRequest
    +              completionHandler: ^(ProcessInstanceListResultDTO output, NSError* error) {
    +    if (output) {
    +        NSLog(@"%@", output);
    +    }
    +    if (error) {
    +        NSLog(@"Error: %@", error);
    +    }
    +}];
    +
    +
    + +
    +
    var OrchestratorPlugin = require('orchestrator_plugin');
    +
    +// Create an instance of the API class
    +var api = new OrchestratorPlugin.DefaultApi()
    +var workflowId = workflowId_example; // {String} ID of the workflow
    +var opts = {
    +  'searchRequest':  // {SearchRequest} 
    +};
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.getWorkflowInstances(workflowId, opts, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using Org.OpenAPITools.Api;
    +using Org.OpenAPITools.Client;
    +using Org.OpenAPITools.Model;
    +
    +namespace Example
    +{
    +    public class getWorkflowInstancesExample
    +    {
    +        public void main()
    +        {
    +
    +            // Create an instance of the API class
    +            var apiInstance = new DefaultApi();
    +            var workflowId = workflowId_example;  // String | ID of the workflow (default to null)
    +            var searchRequest = new SearchRequest(); // SearchRequest |  (optional) 
    +
    +            try {
    +                // Get instances for a specific workflow
    +                ProcessInstanceListResultDTO result = apiInstance.getWorkflowInstances(workflowId, searchRequest);
    +                Debug.WriteLine(result);
    +            } catch (Exception e) {
    +                Debug.Print("Exception when calling DefaultApi.getWorkflowInstances: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +// Create an instance of the API class
    +$api_instance = new OpenAPITools\Client\Api\DefaultApi();
    +$workflowId = workflowId_example; // String | ID of the workflow
    +$searchRequest = ; // SearchRequest | 
    +
    +try {
    +    $result = $api_instance->getWorkflowInstances($workflowId, $searchRequest);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling DefaultApi->getWorkflowInstances: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use WWW::OPenAPIClient::Configuration;
    +use WWW::OPenAPIClient::DefaultApi;
    +
    +# Create an instance of the API class
    +my $api_instance = WWW::OPenAPIClient::DefaultApi->new();
    +my $workflowId = workflowId_example; # String | ID of the workflow
    +my $searchRequest = WWW::OPenAPIClient::Object::SearchRequest->new(); # SearchRequest | 
    +
    +eval {
    +    my $result = $api_instance->getWorkflowInstances(workflowId => $workflowId, searchRequest => $searchRequest);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling DefaultApi->getWorkflowInstances: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import openapi_client
    +from openapi_client.rest import ApiException
    +from pprint import pprint
    +
    +# Create an instance of the API class
    +api_instance = openapi_client.DefaultApi()
    +workflowId = workflowId_example # String | ID of the workflow (default to null)
    +searchRequest =  # SearchRequest |  (optional)
    +
    +try:
    +    # Get instances for a specific workflow
    +    api_response = api_instance.get_workflow_instances(workflowId, searchRequest=searchRequest)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling DefaultApi->getWorkflowInstances: %s\n" % e)
    +
    + +
    +
    extern crate DefaultApi;
    +
    +pub fn main() {
    +    let workflowId = workflowId_example; // String
    +    let searchRequest = ; // SearchRequest
    +
    +    let mut context = DefaultApi::Context::default();
    +    let result = client.getWorkflowInstances(workflowId, searchRequest, &context).wait();
    +
    +    println!("{:?}", result);
    +}
    +
    +
    +
    + +

    Scopes

    + + +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + +
    NameDescription
    workflowId* + + +
    +
    +
    + + String + + +
    +ID of the workflow +
    +
    +
    + Required +
    +
    +
    +
    + + +
    Body parameters
    + + + + + + + + + +
    NameDescription
    searchRequest +

    Parameters for retrieving workflow instances

    + +
    +
    + + + +

    Responses

    +

    +

    + + + + + + +
    +
    +
    + +
    + +
    +
    +

    +

    + + + + + + +
    +
    +
    + +
    + +
    +
    +
    +
    +
    @@ -3993,7 +4592,7 @@

    getWorkflowOverviewById

    -

    Get a workflow overview by ID

    +

    Returns the key fields of the workflow including data on the last run instance


    /v2/workflows/{workflowId}/overview
    @@ -4407,7 +5006,7 @@

    getWorkflowSourceById

    -

    Get a workflow source by ID

    +

    Get the workflow's definition


    /v2/workflows/{workflowId}/source
    @@ -4821,7 +5420,7 @@

    getWorkflowStatuses

    -

    Retrieve an array of workflow statuses

    +

    Retrieve array with the status of all instances


    /v2/workflows/instances/statuses
    @@ -5197,7 +5796,7 @@

    getWorkflowsOverview

    -

    Get a list of workflow overviews

    +

    Returns the key fields of the workflow including data on the last run instance


    /v2/workflows/overview
    @@ -5234,9 +5833,9 @@

    Usage and SDK Samples

    "orderBy" : "orderBy", "totalCount" : 5.637376656633329 }, - "filterInfo" : { - "fieldName" : "fieldName", - "fieldValue" : "FilterValue" + "filters" : { + "filters" : [ null, null ], + "operator" : "AND" } }'
    @@ -5255,10 +5854,10 @@

    Usage and SDK Samples

    // Create an instance of the API class DefaultApi apiInstance = new DefaultApi(); - GetOverviewsRequestParams getOverviewsRequestParams = ; // GetOverviewsRequestParams | + SearchRequest searchRequest = ; // SearchRequest | try { - WorkflowOverviewListResultDTO result = apiInstance.getWorkflowsOverview(getOverviewsRequestParams); + WorkflowOverviewListResultDTO result = apiInstance.getWorkflowsOverview(searchRequest); System.out.println(result); } catch (ApiException e) { System.err.println("Exception when calling DefaultApi#getWorkflowsOverview"); @@ -5274,10 +5873,10 @@

    Usage and SDK Samples

    final api_instance = DefaultApi(); -final GetOverviewsRequestParams getOverviewsRequestParams = new GetOverviewsRequestParams(); // GetOverviewsRequestParams | +final SearchRequest searchRequest = new SearchRequest(); // SearchRequest | try { - final result = await api_instance.getWorkflowsOverview(getOverviewsRequestParams); + final result = await api_instance.getWorkflowsOverview(searchRequest); print(result); } catch (e) { print('Exception when calling DefaultApi->getWorkflowsOverview: $e\n'); @@ -5292,10 +5891,10 @@

    Usage and SDK Samples

    public class DefaultApiExample { public static void main(String[] args) { DefaultApi apiInstance = new DefaultApi(); - GetOverviewsRequestParams getOverviewsRequestParams = ; // GetOverviewsRequestParams | + SearchRequest searchRequest = ; // SearchRequest | try { - WorkflowOverviewListResultDTO result = apiInstance.getWorkflowsOverview(getOverviewsRequestParams); + WorkflowOverviewListResultDTO result = apiInstance.getWorkflowsOverview(searchRequest); System.out.println(result); } catch (ApiException e) { System.err.println("Exception when calling DefaultApi#getWorkflowsOverview"); @@ -5313,9 +5912,9 @@

    Usage and SDK Samples

    // Create an instance of the API class DefaultApi *apiInstance = [[DefaultApi alloc] init]; -GetOverviewsRequestParams *getOverviewsRequestParams = ; // (optional) +SearchRequest *searchRequest = ; // (optional) -[apiInstance getWorkflowsOverviewWith:getOverviewsRequestParams +[apiInstance getWorkflowsOverviewWith:searchRequest completionHandler: ^(WorkflowOverviewListResultDTO output, NSError* error) { if (output) { NSLog(@"%@", output); @@ -5333,7 +5932,7 @@

    Usage and SDK Samples

    // Create an instance of the API class var api = new OrchestratorPlugin.DefaultApi() var opts = { - 'getOverviewsRequestParams': // {GetOverviewsRequestParams} + 'searchRequest': // {SearchRequest} }; var callback = function(error, data, response) { @@ -5366,10 +5965,10 @@

    Usage and SDK Samples

    // Create an instance of the API class var apiInstance = new DefaultApi(); - var getOverviewsRequestParams = new GetOverviewsRequestParams(); // GetOverviewsRequestParams | (optional) + var searchRequest = new SearchRequest(); // SearchRequest | (optional) try { - WorkflowOverviewListResultDTO result = apiInstance.getWorkflowsOverview(getOverviewsRequestParams); + WorkflowOverviewListResultDTO result = apiInstance.getWorkflowsOverview(searchRequest); Debug.WriteLine(result); } catch (Exception e) { Debug.Print("Exception when calling DefaultApi.getWorkflowsOverview: " + e.Message ); @@ -5386,10 +5985,10 @@

    Usage and SDK Samples

    // Create an instance of the API class $api_instance = new OpenAPITools\Client\Api\DefaultApi(); -$getOverviewsRequestParams = ; // GetOverviewsRequestParams | +$searchRequest = ; // SearchRequest | try { - $result = $api_instance->getWorkflowsOverview($getOverviewsRequestParams); + $result = $api_instance->getWorkflowsOverview($searchRequest); print_r($result); } catch (Exception $e) { echo 'Exception when calling DefaultApi->getWorkflowsOverview: ', $e->getMessage(), PHP_EOL; @@ -5404,10 +6003,10 @@

    Usage and SDK Samples

    # Create an instance of the API class my $api_instance = WWW::OPenAPIClient::DefaultApi->new(); -my $getOverviewsRequestParams = WWW::OPenAPIClient::Object::GetOverviewsRequestParams->new(); # GetOverviewsRequestParams | +my $searchRequest = WWW::OPenAPIClient::Object::SearchRequest->new(); # SearchRequest | eval { - my $result = $api_instance->getWorkflowsOverview(getOverviewsRequestParams => $getOverviewsRequestParams); + my $result = $api_instance->getWorkflowsOverview(searchRequest => $searchRequest); print Dumper($result); }; if ($@) { @@ -5424,10 +6023,10 @@

    Usage and SDK Samples

    # Create an instance of the API class api_instance = openapi_client.DefaultApi() -getOverviewsRequestParams = # GetOverviewsRequestParams | (optional) +searchRequest = # SearchRequest | (optional) try: - api_response = api_instance.get_workflows_overview(getOverviewsRequestParams=getOverviewsRequestParams) + api_response = api_instance.get_workflows_overview(searchRequest=searchRequest) pprint(api_response) except ApiException as e: print("Exception when calling DefaultApi->getWorkflowsOverview: %s\n" % e) @@ -5437,10 +6036,10 @@

    Usage and SDK Samples

    extern crate DefaultApi;
     
     pub fn main() {
    -    let getOverviewsRequestParams = ; // GetOverviewsRequestParams
    +    let searchRequest = ; // SearchRequest
     
         let mut context = DefaultApi::Context::default();
    -    let result = client.getWorkflowsOverview(getOverviewsRequestParams, &context).wait();
    +    let result = client.getWorkflowsOverview(searchRequest, &context).wait();
     
         println!("{:?}", result);
     }
    @@ -5463,17 +6062,17 @@ 

    Parameters

    Name Description - getOverviewsRequestParams + searchRequest -

    Parameters for retrieving of workflow overviews

    +

    Pagination and filters

    -
    +
    diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/.openapi-generator/FILES b/plugins/orchestrator-common/src/generated/docs/markdown/.openapi-generator/FILES index 4cc0fb6f01..e774366e59 100644 --- a/plugins/orchestrator-common/src/generated/docs/markdown/.openapi-generator/FILES +++ b/plugins/orchestrator-common/src/generated/docs/markdown/.openapi-generator/FILES @@ -1,19 +1,23 @@ +.openapi-generator-ignore Apis/DefaultApi.md Models/AssessedProcessInstanceDTO.md Models/ErrorResponse.md Models/ExecuteWorkflowRequestDTO.md Models/ExecuteWorkflowResponseDTO.md -Models/FilterInfo.md -Models/FilterValue.md -Models/GetInstancesRequestParams.md +Models/FieldFilter.md +Models/FieldFilter_value.md +Models/Filter.md +Models/GetInstancesRequest.md Models/GetOverviewsRequestParams.md +Models/InputSchemaResponseDTO.md +Models/LogicalFilter.md Models/NodeInstanceDTO.md -Models/Operator.md Models/PaginationInfoDTO.md Models/ProcessInstanceDTO.md Models/ProcessInstanceErrorDTO.md Models/ProcessInstanceListResultDTO.md Models/ProcessInstanceStatusDTO.md +Models/SearchRequest.md Models/WorkflowCategoryDTO.md Models/WorkflowDTO.md Models/WorkflowDataDTO.md @@ -22,6 +26,9 @@ Models/WorkflowListResultDTO.md Models/WorkflowOverviewDTO.md Models/WorkflowOverviewListResultDTO.md Models/WorkflowProgressDTO.md +Models/WorkflowResultDTO.md +Models/WorkflowResultDTO_nextWorkflows_inner.md +Models/WorkflowResultDTO_outputs_inner.md +Models/WorkflowResultDTO_outputs_inner_value.md Models/WorkflowRunStatusDTO.md -Models/WorkflowSuggestionDTO.md README.md diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Apis/DefaultApi.md b/plugins/orchestrator-common/src/generated/docs/markdown/Apis/DefaultApi.md index beacef1d1f..65ebb811fe 100644 --- a/plugins/orchestrator-common/src/generated/docs/markdown/Apis/DefaultApi.md +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Apis/DefaultApi.md @@ -4,12 +4,13 @@ All URIs are relative to *http://localhost* | Method | HTTP request | Description | |------------- | ------------- | -------------| -| [**abortWorkflow**](DefaultApi.md#abortWorkflow) | **DELETE** /v2/instances/{instanceId}/abort | Abort a workflow instance | +| [**abortWorkflow**](DefaultApi.md#abortWorkflow) | **DELETE** /v2/workflows/instances/{instanceId}/abort | Abort a workflow instance | | [**executeWorkflow**](DefaultApi.md#executeWorkflow) | **POST** /v2/workflows/{workflowId}/execute | Execute a workflow | | [**getInstanceById**](DefaultApi.md#getInstanceById) | **GET** /v2/workflows/instances/{instanceId} | Get Workflow Instance by ID | | [**getInstances**](DefaultApi.md#getInstances) | **POST** /v2/workflows/instances | Get instances | | [**getWorkflowById**](DefaultApi.md#getWorkflowById) | **GET** /v2/workflows/{workflowId} | | | [**getWorkflowInputSchemaById**](DefaultApi.md#getWorkflowInputSchemaById) | **GET** /v2/workflows/{workflowId}/inputSchema | | +| [**getWorkflowInstances**](DefaultApi.md#getWorkflowInstances) | **POST** /v2/workflows/{workflowId}/instances | Get instances for a specific workflow | | [**getWorkflowOverviewById**](DefaultApi.md#getWorkflowOverviewById) | **GET** /v2/workflows/{workflowId}/overview | | | [**getWorkflowSourceById**](DefaultApi.md#getWorkflowSourceById) | **GET** /v2/workflows/{workflowId}/source | | | [**getWorkflowStatuses**](DefaultApi.md#getWorkflowStatuses) | **GET** /v2/workflows/instances/statuses | Get workflow status list | @@ -49,6 +50,8 @@ No authorization required Execute a workflow + Execute a workflow + ### Parameters |Name | Type | Description | Notes | @@ -75,6 +78,8 @@ No authorization required Get Workflow Instance by ID + Get a workflow execution/run (instance) + ### Parameters |Name | Type | Description | Notes | @@ -97,17 +102,17 @@ No authorization required # **getInstances** -> ProcessInstanceListResultDTO getInstances(GetInstancesRequestParams) +> ProcessInstanceListResultDTO getInstances(GetInstancesRequest) Get instances - Retrieve an array of instances + Retrieve an array of workflow executions (instances) ### Parameters |Name | Type | Description | Notes | |------------- | ------------- | ------------- | -------------| -| **GetInstancesRequestParams** | [**GetInstancesRequestParams**](../Models/GetInstancesRequestParams.md)| Parameters for retrieving instances | [optional] | +| **GetInstancesRequest** | [**GetInstancesRequest**](../Models/GetInstancesRequest.md)| Parameters for retrieving instances | [optional] | ### Return type @@ -128,7 +133,7 @@ No authorization required - Get a workflow by ID + Get full workflow info ### Parameters @@ -151,11 +156,11 @@ No authorization required # **getWorkflowInputSchemaById** -> Object getWorkflowInputSchemaById(workflowId, instanceId) +> InputSchemaResponseDTO getWorkflowInputSchemaById(workflowId, instanceId) - Get a workflow input schema by ID + Get the workflow input schema. It defines the input fields of the workflow ### Parameters @@ -166,7 +171,7 @@ No authorization required ### Return type -**Object** +[**InputSchemaResponseDTO**](../Models/InputSchemaResponseDTO.md) ### Authorization @@ -177,13 +182,41 @@ No authorization required - **Content-Type**: Not defined - **Accept**: application/json + +# **getWorkflowInstances** +> ProcessInstanceListResultDTO getWorkflowInstances(workflowId, SearchRequest) + +Get instances for a specific workflow + + Retrieve an array of workflow executions (instances) for the given workflow + +### Parameters + +|Name | Type | Description | Notes | +|------------- | ------------- | ------------- | -------------| +| **workflowId** | **String**| ID of the workflow | [default to null] | +| **SearchRequest** | [**SearchRequest**](../Models/SearchRequest.md)| Parameters for retrieving workflow instances | [optional] | + +### Return type + +[**ProcessInstanceListResultDTO**](../Models/ProcessInstanceListResultDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/json + # **getWorkflowOverviewById** > WorkflowOverviewDTO getWorkflowOverviewById(workflowId) - Get a workflow overview by ID + Returns the key fields of the workflow including data on the last run instance ### Parameters @@ -210,7 +243,7 @@ No authorization required - Get a workflow source by ID + Get the workflow's definition ### Parameters @@ -237,7 +270,7 @@ No authorization required Get workflow status list - Retrieve an array of workflow statuses + Retrieve array with the status of all instances ### Parameters This endpoint does not need any parameter. @@ -257,17 +290,17 @@ No authorization required # **getWorkflowsOverview** -> WorkflowOverviewListResultDTO getWorkflowsOverview(GetOverviewsRequestParams) +> WorkflowOverviewListResultDTO getWorkflowsOverview(SearchRequest) - Get a list of workflow overviews + Returns the key fields of the workflow including data on the last run instance ### Parameters |Name | Type | Description | Notes | |------------- | ------------- | ------------- | -------------| -| **GetOverviewsRequestParams** | [**GetOverviewsRequestParams**](../Models/GetOverviewsRequestParams.md)| Parameters for retrieving of workflow overviews | [optional] | +| **SearchRequest** | [**SearchRequest**](../Models/SearchRequest.md)| Pagination and filters | [optional] | ### Return type diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/FieldFilter.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/FieldFilter.md new file mode 100644 index 0000000000..e0ce280fd0 --- /dev/null +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/FieldFilter.md @@ -0,0 +1,11 @@ +# FieldFilter +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +| **field** | **String** | | [default to null] | +| **operator** | **String** | | [default to null] | +| **value** | [**FieldFilter_value**](FieldFilter_value.md) | | [default to null] | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/Operator.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/FieldFilter_value.md similarity index 93% rename from plugins/orchestrator-common/src/generated/docs/markdown/Models/Operator.md rename to plugins/orchestrator-common/src/generated/docs/markdown/Models/FieldFilter_value.md index 8f74240114..7d9890ad04 100644 --- a/plugins/orchestrator-common/src/generated/docs/markdown/Models/Operator.md +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/FieldFilter_value.md @@ -1,4 +1,4 @@ -# Operator +# FieldFilter_value ## Properties | Name | Type | Description | Notes | diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/Filter.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/Filter.md new file mode 100644 index 0000000000..cd24e64b56 --- /dev/null +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/Filter.md @@ -0,0 +1,12 @@ +# Filter +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +| **operator** | [**oas_any_type_not_mapped**](AnyType.md) | | [default to null] | +| **filters** | [**oas_any_type_not_mapped**](.md) | | [default to null] | +| **field** | [**oas_any_type_not_mapped**](.md) | | [default to null] | +| **value** | [**FieldFilter_value**](FieldFilter_value.md) | | [default to null] | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/FilterInfo.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/FilterInfo.md deleted file mode 100644 index e0ec7c01b5..0000000000 --- a/plugins/orchestrator-common/src/generated/docs/markdown/Models/FilterInfo.md +++ /dev/null @@ -1,11 +0,0 @@ -# FilterInfo -## Properties - -| Name | Type | Description | Notes | -|------------ | ------------- | ------------- | -------------| -| **fieldName** | **String** | The name of the field to filter on | [default to null] | -| **operator** | [**Operator**](Operator.md) | | [default to null] | -| **fieldValue** | [**FilterValue**](FilterValue.md) | | [default to null] | - -[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) - diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/GetInstancesRequestParams.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/GetInstancesRequest.md similarity index 77% rename from plugins/orchestrator-common/src/generated/docs/markdown/Models/GetInstancesRequestParams.md rename to plugins/orchestrator-common/src/generated/docs/markdown/Models/GetInstancesRequest.md index 1bc17ee82f..ccd82afad9 100644 --- a/plugins/orchestrator-common/src/generated/docs/markdown/Models/GetInstancesRequestParams.md +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/GetInstancesRequest.md @@ -1,10 +1,10 @@ -# GetInstancesRequestParams +# GetInstancesRequest ## Properties | Name | Type | Description | Notes | |------------ | ------------- | ------------- | -------------| | **paginationInfo** | [**PaginationInfoDTO**](PaginationInfoDTO.md) | | [optional] [default to null] | -| **filterInfo** | [**FilterInfo**](FilterInfo.md) | | [optional] [default to null] | +| **filters** | [**SearchRequest**](SearchRequest.md) | | [optional] [default to null] | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/GetOverviewsRequestParams.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/GetOverviewsRequestParams.md index 2f39412457..4765b2587d 100644 --- a/plugins/orchestrator-common/src/generated/docs/markdown/Models/GetOverviewsRequestParams.md +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/GetOverviewsRequestParams.md @@ -4,7 +4,7 @@ | Name | Type | Description | Notes | |------------ | ------------- | ------------- | -------------| | **paginationInfo** | [**PaginationInfoDTO**](PaginationInfoDTO.md) | | [optional] [default to null] | -| **filterInfo** | [**FilterInfo**](FilterInfo.md) | | [optional] [default to null] | +| **filters** | [**SearchRequest**](SearchRequest.md) | | [optional] [default to null] | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/InputSchemaResponseDTO.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/InputSchemaResponseDTO.md new file mode 100644 index 0000000000..c9309f5dda --- /dev/null +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/InputSchemaResponseDTO.md @@ -0,0 +1,10 @@ +# InputSchemaResponseDTO +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +| **inputSchema** | [**Object**](.md) | | [optional] [default to null] | +| **data** | [**Object**](.md) | | [optional] [default to null] | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowSuggestionDTO.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/LogicalFilter.md similarity index 66% rename from plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowSuggestionDTO.md rename to plugins/orchestrator-common/src/generated/docs/markdown/Models/LogicalFilter.md index c389a840ba..d9aab1e83d 100644 --- a/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowSuggestionDTO.md +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/LogicalFilter.md @@ -1,10 +1,10 @@ -# WorkflowSuggestionDTO +# LogicalFilter ## Properties | Name | Type | Description | Notes | |------------ | ------------- | ------------- | -------------| -| **id** | **String** | | [optional] [default to null] | -| **name** | **String** | | [optional] [default to null] | +| **operator** | **String** | | [default to null] | +| **filters** | [**List**](Filter.md) | | [default to null] | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/ProcessInstanceDTO.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/ProcessInstanceDTO.md index b7ce0dc8f2..e09ee6158b 100644 --- a/plugins/orchestrator-common/src/generated/docs/markdown/Models/ProcessInstanceDTO.md +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/ProcessInstanceDTO.md @@ -18,7 +18,6 @@ | **businessKey** | **String** | | [optional] [default to null] | | **nodes** | [**List**](NodeInstanceDTO.md) | | [default to null] | | **error** | [**ProcessInstanceErrorDTO**](ProcessInstanceErrorDTO.md) | | [optional] [default to null] | -| **variables** | [**Object**](.md) | | [optional] [default to null] | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/SearchRequest.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/SearchRequest.md new file mode 100644 index 0000000000..086b292471 --- /dev/null +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/SearchRequest.md @@ -0,0 +1,10 @@ +# SearchRequest +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +| **filters** | [**Filter**](Filter.md) | | [optional] [default to null] | +| **paginationInfo** | [**PaginationInfoDTO**](PaginationInfoDTO.md) | | [optional] [default to null] | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowDataDTO.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowDataDTO.md index 997300385e..c4a00febb4 100644 --- a/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowDataDTO.md +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowDataDTO.md @@ -3,7 +3,7 @@ | Name | Type | Description | Notes | |------------ | ------------- | ------------- | -------------| -| **workflowoptions** | [**List**](array.md) | | [optional] [default to null] | +| **result** | [**WorkflowResultDTO**](WorkflowResultDTO.md) | | [optional] [default to null] | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowOverviewDTO.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowOverviewDTO.md index 5399b9dd39..26ee81655c 100644 --- a/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowOverviewDTO.md +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowOverviewDTO.md @@ -8,7 +8,7 @@ | **format** | [**WorkflowFormatDTO**](WorkflowFormatDTO.md) | | [default to null] | | **lastRunId** | **String** | | [optional] [default to null] | | **lastTriggeredMs** | **BigDecimal** | | [optional] [default to null] | -| **lastRunStatus** | **String** | | [optional] [default to null] | +| **lastRunStatus** | [**ProcessInstanceStatusDTO**](ProcessInstanceStatusDTO.md) | | [optional] [default to null] | | **category** | [**WorkflowCategoryDTO**](WorkflowCategoryDTO.md) | | [optional] [default to null] | | **avgDurationMs** | **BigDecimal** | | [optional] [default to null] | | **description** | **String** | | [optional] [default to null] | diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowResultDTO.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowResultDTO.md new file mode 100644 index 0000000000..aa14267f56 --- /dev/null +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowResultDTO.md @@ -0,0 +1,12 @@ +# WorkflowResultDTO +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +| **completedWith** | **String** | The state of workflow completion. | [optional] [default to null] | +| **message** | **String** | High-level summary of the current status, free-form text, human readable. | [optional] [default to null] | +| **nextWorkflows** | [**List**](WorkflowResultDTO_nextWorkflows_inner.md) | List of workflows suggested to run next. Items at lower indexes are of higher priority. | [optional] [default to null] | +| **outputs** | [**List**](WorkflowResultDTO_outputs_inner.md) | Additional structured output of workflow processing. This can contain identifiers of created resources, links to resources, logs or other output. | [optional] [default to null] | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowResultDTO_nextWorkflows_inner.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowResultDTO_nextWorkflows_inner.md new file mode 100644 index 0000000000..40db1d4d0a --- /dev/null +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowResultDTO_nextWorkflows_inner.md @@ -0,0 +1,10 @@ +# WorkflowResultDTO_nextWorkflows_inner +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +| **id** | **String** | Workflow identifier | [default to null] | +| **name** | **String** | Human readable title describing the workflow. | [default to null] | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowResultDTO_outputs_inner.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowResultDTO_outputs_inner.md new file mode 100644 index 0000000000..b4b62ad013 --- /dev/null +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowResultDTO_outputs_inner.md @@ -0,0 +1,11 @@ +# WorkflowResultDTO_outputs_inner +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +| **key** | **String** | Unique identifier of the option. Preferably human-readable. | [default to null] | +| **value** | [**WorkflowResultDTO_outputs_inner_value**](WorkflowResultDTO_outputs_inner_value.md) | | [default to null] | +| **format** | **String** | More detailed type of the 'value' property. Defaults to 'text'. | [optional] [default to null] | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/Models/FilterValue.md b/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowResultDTO_outputs_inner_value.md similarity index 87% rename from plugins/orchestrator-common/src/generated/docs/markdown/Models/FilterValue.md rename to plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowResultDTO_outputs_inner_value.md index 7a15c0bf3d..9238c5915a 100644 --- a/plugins/orchestrator-common/src/generated/docs/markdown/Models/FilterValue.md +++ b/plugins/orchestrator-common/src/generated/docs/markdown/Models/WorkflowResultDTO_outputs_inner_value.md @@ -1,4 +1,4 @@ -# FilterValue +# WorkflowResultDTO_outputs_inner_value ## Properties | Name | Type | Description | Notes | diff --git a/plugins/orchestrator-common/src/generated/docs/markdown/README.md b/plugins/orchestrator-common/src/generated/docs/markdown/README.md index beb4b6aa48..36ed22d6a3 100644 --- a/plugins/orchestrator-common/src/generated/docs/markdown/README.md +++ b/plugins/orchestrator-common/src/generated/docs/markdown/README.md @@ -7,16 +7,17 @@ All URIs are relative to *http://localhost* | Class | Method | HTTP request | Description | |------------ | ------------- | ------------- | -------------| -| *DefaultApi* | [**abortWorkflow**](Apis/DefaultApi.md#abortworkflow) | **DELETE** /v2/instances/{instanceId}/abort | Abort a workflow instance | +| *DefaultApi* | [**abortWorkflow**](Apis/DefaultApi.md#abortworkflow) | **DELETE** /v2/workflows/instances/{instanceId}/abort | Abort a workflow instance | *DefaultApi* | [**executeWorkflow**](Apis/DefaultApi.md#executeworkflow) | **POST** /v2/workflows/{workflowId}/execute | Execute a workflow | *DefaultApi* | [**getInstanceById**](Apis/DefaultApi.md#getinstancebyid) | **GET** /v2/workflows/instances/{instanceId} | Get Workflow Instance by ID | *DefaultApi* | [**getInstances**](Apis/DefaultApi.md#getinstances) | **POST** /v2/workflows/instances | Get instances | -*DefaultApi* | [**getWorkflowById**](Apis/DefaultApi.md#getworkflowbyid) | **GET** /v2/workflows/{workflowId} | Get a workflow by ID | -*DefaultApi* | [**getWorkflowInputSchemaById**](Apis/DefaultApi.md#getworkflowinputschemabyid) | **GET** /v2/workflows/{workflowId}/inputSchema | Get a workflow input schema by ID | -*DefaultApi* | [**getWorkflowOverviewById**](Apis/DefaultApi.md#getworkflowoverviewbyid) | **GET** /v2/workflows/{workflowId}/overview | Get a workflow overview by ID | -*DefaultApi* | [**getWorkflowSourceById**](Apis/DefaultApi.md#getworkflowsourcebyid) | **GET** /v2/workflows/{workflowId}/source | Get a workflow source by ID | +*DefaultApi* | [**getWorkflowById**](Apis/DefaultApi.md#getworkflowbyid) | **GET** /v2/workflows/{workflowId} | Get full workflow info | +*DefaultApi* | [**getWorkflowInputSchemaById**](Apis/DefaultApi.md#getworkflowinputschemabyid) | **GET** /v2/workflows/{workflowId}/inputSchema | Get the workflow input schema. It defines the input fields of the workflow | +*DefaultApi* | [**getWorkflowInstances**](Apis/DefaultApi.md#getworkflowinstances) | **POST** /v2/workflows/{workflowId}/instances | Get instances for a specific workflow | +*DefaultApi* | [**getWorkflowOverviewById**](Apis/DefaultApi.md#getworkflowoverviewbyid) | **GET** /v2/workflows/{workflowId}/overview | Returns the key fields of the workflow including data on the last run instance | +*DefaultApi* | [**getWorkflowSourceById**](Apis/DefaultApi.md#getworkflowsourcebyid) | **GET** /v2/workflows/{workflowId}/source | Get the workflow's definition | *DefaultApi* | [**getWorkflowStatuses**](Apis/DefaultApi.md#getworkflowstatuses) | **GET** /v2/workflows/instances/statuses | Get workflow status list | -*DefaultApi* | [**getWorkflowsOverview**](Apis/DefaultApi.md#getworkflowsoverview) | **POST** /v2/workflows/overview | Get a list of workflow overviews | +*DefaultApi* | [**getWorkflowsOverview**](Apis/DefaultApi.md#getworkflowsoverview) | **POST** /v2/workflows/overview | Returns the key fields of the workflow including data on the last run instance | @@ -26,17 +27,20 @@ All URIs are relative to *http://localhost* - [ErrorResponse](./Models/ErrorResponse.md) - [ExecuteWorkflowRequestDTO](./Models/ExecuteWorkflowRequestDTO.md) - [ExecuteWorkflowResponseDTO](./Models/ExecuteWorkflowResponseDTO.md) - - [FilterInfo](./Models/FilterInfo.md) - - [FilterValue](./Models/FilterValue.md) - - [GetInstancesRequestParams](./Models/GetInstancesRequestParams.md) + - [FieldFilter](./Models/FieldFilter.md) + - [FieldFilter_value](./Models/FieldFilter_value.md) + - [Filter](./Models/Filter.md) + - [GetInstancesRequest](./Models/GetInstancesRequest.md) - [GetOverviewsRequestParams](./Models/GetOverviewsRequestParams.md) + - [InputSchemaResponseDTO](./Models/InputSchemaResponseDTO.md) + - [LogicalFilter](./Models/LogicalFilter.md) - [NodeInstanceDTO](./Models/NodeInstanceDTO.md) - - [Operator](./Models/Operator.md) - [PaginationInfoDTO](./Models/PaginationInfoDTO.md) - [ProcessInstanceDTO](./Models/ProcessInstanceDTO.md) - [ProcessInstanceErrorDTO](./Models/ProcessInstanceErrorDTO.md) - [ProcessInstanceListResultDTO](./Models/ProcessInstanceListResultDTO.md) - [ProcessInstanceStatusDTO](./Models/ProcessInstanceStatusDTO.md) + - [SearchRequest](./Models/SearchRequest.md) - [WorkflowCategoryDTO](./Models/WorkflowCategoryDTO.md) - [WorkflowDTO](./Models/WorkflowDTO.md) - [WorkflowDataDTO](./Models/WorkflowDataDTO.md) @@ -45,8 +49,11 @@ All URIs are relative to *http://localhost* - [WorkflowOverviewDTO](./Models/WorkflowOverviewDTO.md) - [WorkflowOverviewListResultDTO](./Models/WorkflowOverviewListResultDTO.md) - [WorkflowProgressDTO](./Models/WorkflowProgressDTO.md) + - [WorkflowResultDTO](./Models/WorkflowResultDTO.md) + - [WorkflowResultDTO_nextWorkflows_inner](./Models/WorkflowResultDTO_nextWorkflows_inner.md) + - [WorkflowResultDTO_outputs_inner](./Models/WorkflowResultDTO_outputs_inner.md) + - [WorkflowResultDTO_outputs_inner_value](./Models/WorkflowResultDTO_outputs_inner_value.md) - [WorkflowRunStatusDTO](./Models/WorkflowRunStatusDTO.md) - - [WorkflowSuggestionDTO](./Models/WorkflowSuggestionDTO.md) diff --git a/plugins/orchestrator-common/src/models.ts b/plugins/orchestrator-common/src/models.ts index 63044764f5..600341fa4d 100644 --- a/plugins/orchestrator-common/src/models.ts +++ b/plugins/orchestrator-common/src/models.ts @@ -85,3 +85,35 @@ export interface ProcessInstance { category?: WorkflowCategory; description?: WorkflowDefinition['description']; } +export interface IntrospectionQuery { + __type: IntrospectionType | null; +} + +export interface IntrospectionType { + name: string; + kind: TypeKind; + description: string | null; + fields: IntrospectionField[] | null; +} + +export interface IntrospectionField { + name: string; + type: IntrospectionTypeRef; +} + +export interface IntrospectionTypeRef { + kind: TypeKind; + name: TypeName; + ofType: IntrospectionTypeRef | null; +} + +export enum TypeKind { + InputObject = 'INPUT_OBJECT', +} + +export enum TypeName { + Id = 'IdArgument', + String = 'StringArgument', + StringArray = 'StringArrayArgument', + Date = 'DateArgument', +} diff --git a/plugins/orchestrator-common/src/openapi/openapi.yaml b/plugins/orchestrator-common/src/openapi/openapi.yaml index 9b94e67e38..e566bba68a 100644 --- a/plugins/orchestrator-common/src/openapi/openapi.yaml +++ b/plugins/orchestrator-common/src/openapi/openapi.yaml @@ -12,14 +12,14 @@ paths: /v2/workflows/overview: post: operationId: getWorkflowsOverview - description: Get a list of workflow overviews + description: Returns the key fields of the workflow including data on the last run instance requestBody: required: false - description: Parameters for retrieving of workflow overviews + description: Pagination and filters content: application/json: schema: - $ref: '#/components/schemas/GetOverviewsRequestParams' + $ref: '#/components/schemas/SearchRequest' responses: '200': description: Success @@ -36,7 +36,7 @@ paths: /v2/workflows/{workflowId}/overview: get: operationId: getWorkflowOverviewById - description: Get a workflow overview by ID + description: Returns the key fields of the workflow including data on the last run instance parameters: - name: workflowId in: path @@ -60,7 +60,7 @@ paths: /v2/workflows/{workflowId}: get: operationId: getWorkflowById - description: Get a workflow by ID + description: Get full workflow info parameters: - name: workflowId in: path @@ -84,7 +84,7 @@ paths: /v2/workflows/{workflowId}/source: get: operationId: getWorkflowSourceById - description: Get a workflow source by ID + description: Get the workflow's definition parameters: - name: workflowId in: path @@ -108,7 +108,7 @@ paths: /v2/workflows/{workflowId}/inputSchema: get: operationId: getWorkflowInputSchemaById - description: Get a workflow input schema by ID + description: Get the workflow input schema. It defines the input fields of the workflow parameters: - name: workflowId in: path @@ -127,7 +127,7 @@ paths: content: application/json: schema: - type: object + $ref: '#/components/schemas/InputSchemaResponseDTO' '500': description: Error fetching workflow input schema by id content: @@ -138,14 +138,46 @@ paths: post: operationId: getInstances summary: Get instances - description: Retrieve an array of instances + description: Retrieve an array of workflow executions (instances) requestBody: required: false description: Parameters for retrieving instances content: application/json: schema: - $ref: '#/components/schemas/GetInstancesRequestParams' + $ref: '#/components/schemas/GetInstancesRequest' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/ProcessInstanceListResultDTO' + '500': + description: Error fetching instances + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + /v2/workflows/{workflowId}/instances: + post: + operationId: getWorkflowInstances + summary: Get instances for a specific workflow + description: Retrieve an array of workflow executions (instances) for the given workflow + parameters: + - name: workflowId + in: path + required: true + description: ID of the workflow + schema: + type: string + requestBody: + required: false + description: Parameters for retrieving workflow instances + content: + application/json: + schema: + $ref: '#/components/schemas/SearchRequest' responses: '200': description: Success @@ -162,6 +194,7 @@ paths: /v2/workflows/instances/{instanceId}: get: summary: Get Workflow Instance by ID + description: Get a workflow execution/run (instance) operationId: getInstanceById parameters: - name: instanceId @@ -194,7 +227,7 @@ paths: get: operationId: getWorkflowStatuses summary: Get workflow status list - description: Retrieve an array of workflow statuses + description: Retrieve array with the status of all instances responses: '200': description: Success @@ -213,6 +246,7 @@ paths: /v2/workflows/{workflowId}/execute: post: summary: Execute a workflow + description: Execute a workflow operationId: executeWorkflow parameters: - name: workflowId @@ -240,7 +274,7 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - /v2/instances/{instanceId}/abort: + /v2/workflows/instances/{instanceId}/abort: delete: summary: Abort a workflow instance operationId: abortWorkflow @@ -286,20 +320,20 @@ components: type: string required: - message - GetInstancesRequestParams: + GetInstancesRequest: type: object properties: paginationInfo: $ref: '#/components/schemas/PaginationInfoDTO' - filterInfo: - $ref: '#/components/schemas/FilterInfo' + filters: + $ref: '#/components/schemas/SearchRequest' GetOverviewsRequestParams: type: object properties: paginationInfo: $ref: '#/components/schemas/PaginationInfoDTO' - filterInfo: - $ref: '#/components/schemas/FilterInfo' + filters: + $ref: '#/components/schemas/SearchRequest' WorkflowOverviewListResultDTO: type: object properties: @@ -329,7 +363,7 @@ components: type: number minimum: 0 lastRunStatus: - type: string + $ref: '#/components/schemas/ProcessInstanceStatusDTO' category: $ref: '#/components/schemas/WorkflowCategoryDTO' avgDurationMs: @@ -356,31 +390,6 @@ components: orderBy: type: string additionalProperties: false - FilterInfo: - type: object - properties: - fieldName: - description: The name of the field to filter on - type: string - operator: - $ref: '#/components/schemas/Operator' - fieldValue: - title: FilterValue - oneOf: - - type: string - - type: number - - type: boolean - description: The value to filter by, which can be a string, number, boolean, or ProcessInstanceStatusDTO - required: - - fieldName - - operator - - fieldValue - Operator: - type: string - description: The operator to use for filtering, such as equality or inclusion - enum: - - equal - - in WorkflowFormatDTO: type: string description: Format of the workflow definition @@ -484,8 +493,6 @@ components: $ref: '#/components/schemas/NodeInstanceDTO' error: $ref: '#/components/schemas/ProcessInstanceErrorDTO' - variables: - $ref: '#/components/schemas/ProcessInstanceVariablesDTO' required: - id - processId @@ -493,22 +500,61 @@ components: WorkflowDataDTO: type: object properties: - workflowoptions: - type: array - items: - $ref: '#/components/schemas/WorkflowOptionsDTO' + result: + $ref: '#/components/schemas/WorkflowResultDTO' additionalProperties: true - WorkflowOptionsDTO: - type: array - items: - $ref: '#/components/schemas/WorkflowSuggestionDTO' - WorkflowSuggestionDTO: + WorkflowResultDTO: + # Based on https://github.com/parodos-dev/serverless-workflows/blob/main/shared/schemas/workflow-result-schema.json + description: Result of a workflow execution type: object properties: - id: + completedWith: + description: The state of workflow completion. type: string - name: + enum: + - error + - success + message: + description: High-level summary of the current status, free-form text, human readable. type: string + nextWorkflows: + description: List of workflows suggested to run next. Items at lower indexes are of higher priority. + type: array + items: + type: object + properties: + id: + description: Workflow identifier + type: string + name: + description: Human readable title describing the workflow. + type: string + required: + - id + - name + outputs: + description: Additional structured output of workflow processing. This can contain identifiers of created resources, links to resources, logs or other output. + type: array + items: + type: object + properties: + key: + description: Unique identifier of the option. Preferably human-readable. + type: string + value: + description: Free form value of the option. + anyOf: + - type: string + - type: number + format: + description: More detailed type of the 'value' property. Defaults to 'text'. + enum: + - text + - number + - link + required: + - key + - value ProcessInstanceStatusDTO: type: string description: Status of the workflow run @@ -595,6 +641,74 @@ components: description: Error message (optional) required: - nodeDefinitionId - ProcessInstanceVariablesDTO: + SearchRequest: type: object - additionalProperties: true + properties: + filters: + $ref: '#/components/schemas/Filter' + paginationInfo: + $ref: '#/components/schemas/PaginationInfoDTO' + Filter: + oneOf: + - $ref: '#/components/schemas/LogicalFilter' + - $ref: '#/components/schemas/FieldFilter' + LogicalFilter: + type: object + required: + - operator + - filters + properties: + operator: + type: string + enum: [AND, OR, NOT] + filters: + type: array + items: + $ref: '#/components/schemas/Filter' + + FieldFilter: + type: object + required: + - field + - operator + - value + properties: + field: + type: string + operator: + type: string + enum: + [ + EQ, + GT, + GTE, + LT, + LTE, + IN, + IS_NULL, + CONTAINS, + CONTAINS_ALL, + CONTAINS_ANY, + LIKE, + BETWEEN, + FROM, + TO, + ] + value: + oneOf: + - type: string + - type: number + - type: boolean + - type: array + items: + oneOf: + - type: string + - type: number + - type: boolean + InputSchemaResponseDTO: + type: object + properties: + inputSchema: + type: object + data: + type: object diff --git a/plugins/orchestrator-common/src/types.ts b/plugins/orchestrator-common/src/types.ts index 4902074da3..53bf9b9671 100644 --- a/plugins/orchestrator-common/src/types.ts +++ b/plugins/orchestrator-common/src/types.ts @@ -76,13 +76,6 @@ export const isComposedSchema = ( curSchema => !isJsonObjectSchema(curSchema), ).length === 0; -export interface WorkflowInputSchemaResponse { - definition: WorkflowDefinition; - schemaSteps: WorkflowInputSchemaStep[]; - isComposedSchema: boolean; - schemaParseError?: string; -} - export interface WorkflowExecutionResponse { id: string; } diff --git a/plugins/orchestrator-form-api/.eslintignore b/plugins/orchestrator-form-api/.eslintignore deleted file mode 100644 index 55289f4a23..0000000000 --- a/plugins/orchestrator-form-api/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -dist-dynamic -dist-scalprum diff --git a/plugins/orchestrator-form-api/.lintstagedrc.json b/plugins/orchestrator-form-api/.lintstagedrc.json deleted file mode 100644 index 14b2263def..0000000000 --- a/plugins/orchestrator-form-api/.lintstagedrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "*": "prettier --ignore-unknown --write", - "*.{js,jsx,ts,tsx,mjs,cjs}": "backstage-cli package lint --fix" -} diff --git a/plugins/orchestrator-form-api/.prettierignore b/plugins/orchestrator-form-api/.prettierignore deleted file mode 100644 index fc8357d99e..0000000000 --- a/plugins/orchestrator-form-api/.prettierignore +++ /dev/null @@ -1,12 +0,0 @@ -dist -dist-types -coverage -.vscode -CHANGELOG.md -generated -templates -*.hbs -renovate.json -dist-dynamic -dist-scalprum -playwright-report diff --git a/plugins/orchestrator-form-api/.prettierrc.js b/plugins/orchestrator-form-api/.prettierrc.js deleted file mode 100644 index 84cbac65b5..0000000000 --- a/plugins/orchestrator-form-api/.prettierrc.js +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-check - -/** @type {import("@ianvs/prettier-plugin-sort-imports").PrettierConfig} */ -module.exports = { - ...require('@spotify/prettier-config'), - plugins: ['@ianvs/prettier-plugin-sort-imports'], - importOrder: [ - '^react(.*)$', - '', - '^@backstage/(.*)$', - '', - '', - '', - '^@janus-idp/(.*)$', - '', - '', - '', - '^[.]', - ], -}; diff --git a/plugins/orchestrator-form-api/.versionhistory.md b/plugins/orchestrator-form-api/.versionhistory.md new file mode 100644 index 0000000000..b17e69831a --- /dev/null +++ b/plugins/orchestrator-form-api/.versionhistory.md @@ -0,0 +1 @@ +- Bumped to 1.1.0 in main branch for next release 1.3.0 diff --git a/plugins/orchestrator-form-api/README.md b/plugins/orchestrator-form-api/README.md index 6877bc7b85..460eae527f 100644 --- a/plugins/orchestrator-form-api/README.md +++ b/plugins/orchestrator-form-api/README.md @@ -2,24 +2,34 @@ ### Overview -This library offers the flexibility to completely override all [properties](https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props) of the `react-jsonschema-form` workflow execution form component. It allows customers to provide a custom decorator for the form component, which can be defined by implementing a dynamic frontend plugin. This decorator enables users to: +This library offers the flexibility to override a selected list of [properties](https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props) of the `react-jsonschema-form` workflow execution form component. It allows customers to provide a custom decorator for the form component in a backstage plugin. This decorator enables users to: -- **Dynamic Validations:** Override the `extraErrors` property to implement dynamic validation logic. +- **Custom Validations:** Two types of custom validations can be added on top of the JSON schema validation provided by default: + - Synchronous validation through the `customValidate` property + - Asynchronous validation through the `getExtraErrors` property. Handles validations that require backend calls. - **Custom Components:** Replace default components by overriding the `widgets` property. -- **Correlated Field Values:** Implement complex inter-field dependencies by overriding the `onChange` property. -- **Additional Customizations:** Make other necessary adjustments by overriding additional properties. +- **Correlated Field Values:** Implement complex inter-field dependencies by overriding the `onChange` and the `formData` properties. The decorator will be provided through a factory method that leverages a [Backstage utility API](https://backstage.io/docs/api/utility-apis) offered by the orchestrator. ### Interface Provided in this package ```typescript +export type FormDecoratorProps = Pick< + FormProps, + 'formData' | 'formContext' | 'widgets' | 'onChange' | 'customValidate' +> & { + getExtraErrors?: ( + formData: JsonObject, + ) => Promise> | undefined; +}; + export type FormDecorator = ( - FormComponent: React.ComponentType>, + FormComponent: React.ComponentType, ) => React.ComponentType; export interface FormExtensionsApi { - getFormDecorator(): FormDecorator; + getFormDecorator(schema: JSONSchema7): FormDecorator; } ``` @@ -27,11 +37,9 @@ export interface FormExtensionsApi { ```typescript class CustomFormExtensionsApi implements FormExtensionsApi { - getFormDecorator() { - return (FormComponent: React.ComponentType>) => { - const widgets = { - color1: ColorWidget - }; + getFormDecorator(schema: JSONSchema7) { + return (FormComponent: React.ComponentType>) => { + const widgets = {CountryWidget}; return () => ; }; } @@ -41,20 +49,17 @@ class CustomFormExtensionsApi implements FormExtensionsApi { ### Plugin Creation Example ```typescript +export const formApiFactory = createApiFactory({ + api: orchestratorFormApiRef, + deps: {}, + factory() { + return new CustomApi(); + }, +}); + export const testFactoryPlugin = createPlugin({ id: 'testfactory', - routes: { - root: rootRouteRef, - }, - apis: [ - createApiFactory({ - api: formExtensionsApiRef, - deps: {}, - factory() { - return new CustomApi(); - }, - }), - ], + apis: [formApiFactory], }); ``` @@ -67,21 +72,35 @@ export const testFactoryPlugin = createPlugin({ "properties": { "name": { "type": "string", - "title": "Product Name" + "title": "Name" }, - "color": { + "country": { "type": "string", - "title": "Product Color", - "description": "The color of the product", - "ui:widget": "color1" + "title": "Country", + "description": "Country of residence", + "ui:widget": "CountryWidget" } }, - "required": ["name", "color"] + "required": ["name", "country"] } ``` +### dynamic plugin configuration + +add the following to app-config.local.yaml for integrating the dynamic plugin. + +```yaml +dynamicPlugins: + frontend: + backstage-plugin-testfactory: + apiFactories: + - importName: formApiFactory +``` + ### Additional Details The workflow execution schema adheres to the [json-schema](https://json-schema.org/) format, which allows for extending the schema with custom properties beyond the official specification. This flexibility enables the inclusion of additional [UiSchema](https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/uiSchema/) fields directly within the schema, as demonstrated in the example above. -The orchestrator plugin handles the separation of UI schema fields from the main schema. By default, it also organizes the form into wizard steps based on an additional hierarchical structure within the JSON schema. This behavior is built into the orchestrator plugin but can be customized or overridden using the provided decorator. +The orchestrator plugin handles the separation of UI schema fields from the main schema. It also organizes the form into wizard steps based on an additional hierarchical structure within the JSON schema. + +Full plugin example is available [here](https://github.com/parodos-dev/extended-form-example-plugin). diff --git a/plugins/orchestrator-form-api/package.json b/plugins/orchestrator-form-api/package.json index 5398bef1c7..8b7bc24f8c 100644 --- a/plugins/orchestrator-form-api/package.json +++ b/plugins/orchestrator-form-api/package.json @@ -5,7 +5,6 @@ "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", - "private": true, "publishConfig": { "access": "public", "main": "dist/index.esm.js", @@ -17,25 +16,22 @@ "sideEffects": false, "scripts": { "build": "backstage-cli package build", - "lint:check": "backstage-cli package lint", - "lint:fix": "backstage-cli package lint --fix", + "lint": "backstage-cli package lint", "clean": "backstage-cli package clean", "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", - "tsc": "tsc", - "prettier:check": "prettier --ignore-unknown --check .", - "prettier:fix": "prettier --ignore-unknown --write ." + "tsc": "tsc" }, "dependencies": { "@backstage/core-plugin-api": "^1.9.3", - "@backstage/types": "^1.1.1" + "@backstage/types": "^1.1.1", + "@rjsf/core": "5.18.5", + "@rjsf/utils": "5.18.5" }, "peerDependencies": { - "react": "^16.13.1 || ^17.0.0 || ^18.0.0", - "@rjsf/core": "^5.17.1" + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" }, "devDependencies": { - "prettier": "3.3.3", "@backstage/cli": "0.26.11", "@types/json-schema": "^7.0.15" }, diff --git a/plugins/orchestrator-form-api/src/api.ts b/plugins/orchestrator-form-api/src/api.ts index e284a94242..c3ba1c2030 100644 --- a/plugins/orchestrator-form-api/src/api.ts +++ b/plugins/orchestrator-form-api/src/api.ts @@ -2,17 +2,46 @@ import { createApiRef } from '@backstage/core-plugin-api'; import { JsonObject } from '@backstage/types'; import { FormProps } from '@rjsf/core'; -// eslint-disable-next-line @backstage/no-undeclared-imports -import { JSONSchema7 } from 'json-schema'; +import { ErrorSchema, UiSchema } from '@rjsf/utils'; +import type { JSONSchema7 } from 'json-schema'; + +/** + * Type definition for properties passed to a form decorator component. + * This interface extends selected fields from `FormProps` provided by `react-jsonschema-form`, + * with additional custom functionality. + * + * @see {@link https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props|RJSF Form Props Documentation} + * + * Core properties include: + * - formData: The form's current data + * - formContext: Contextual data shared across form components + * - widgets: Custom widget components for form fields + * - onChange: Handler for form data changes + * - customValidate: Custom validation function + * + * Additional properties: + * - getExtraErrors: Async function to fetch additional validation errors. + * This replaces the static 'extraErrors' prop from react-jsonschema-form, which can't be used as is, since onSubmit isn't exposed. + * The orchestrator form component will call getExtraErrors when running onSubmit. + */ +export type FormDecoratorProps = Pick< + FormProps, + 'formData' | 'formContext' | 'widgets' | 'onChange' | 'customValidate' +> & { + getExtraErrors?: ( + formData: JsonObject, + ) => Promise> | undefined; +}; export type OrchestratorFormDecorator = ( - FormComponent: React.ComponentType< - Partial> - >, + FormComponent: React.ComponentType, ) => React.ComponentType; export interface OrchestratorFormApi { - getFormDecorator(): OrchestratorFormDecorator; + getFormDecorator( + schema: JSONSchema7, + uiSchema: UiSchema, + ): OrchestratorFormDecorator; } export const orchestratorFormApiRef = createApiRef({ diff --git a/plugins/orchestrator-form-api/src/index.ts b/plugins/orchestrator-form-api/src/index.ts index d1f668b0e3..50d3e263c6 100644 --- a/plugins/orchestrator-form-api/src/index.ts +++ b/plugins/orchestrator-form-api/src/index.ts @@ -1,3 +1,4 @@ export { orchestratorFormApiRef } from './api'; export type { OrchestratorFormApi } from './api'; export type { OrchestratorFormDecorator } from './api'; +export type { FormDecoratorProps } from './api'; diff --git a/plugins/orchestrator-form-react/.eslintignore b/plugins/orchestrator-form-react/.eslintignore deleted file mode 100644 index 55289f4a23..0000000000 --- a/plugins/orchestrator-form-react/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -dist-dynamic -dist-scalprum diff --git a/plugins/orchestrator-form-react/.lintstagedrc.json b/plugins/orchestrator-form-react/.lintstagedrc.json deleted file mode 100644 index 14b2263def..0000000000 --- a/plugins/orchestrator-form-react/.lintstagedrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "*": "prettier --ignore-unknown --write", - "*.{js,jsx,ts,tsx,mjs,cjs}": "backstage-cli package lint --fix" -} diff --git a/plugins/orchestrator-form-react/.prettierignore b/plugins/orchestrator-form-react/.prettierignore deleted file mode 100644 index fc8357d99e..0000000000 --- a/plugins/orchestrator-form-react/.prettierignore +++ /dev/null @@ -1,12 +0,0 @@ -dist -dist-types -coverage -.vscode -CHANGELOG.md -generated -templates -*.hbs -renovate.json -dist-dynamic -dist-scalprum -playwright-report diff --git a/plugins/orchestrator-form-react/.prettierrc.js b/plugins/orchestrator-form-react/.prettierrc.js deleted file mode 100644 index 84cbac65b5..0000000000 --- a/plugins/orchestrator-form-react/.prettierrc.js +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-check - -/** @type {import("@ianvs/prettier-plugin-sort-imports").PrettierConfig} */ -module.exports = { - ...require('@spotify/prettier-config'), - plugins: ['@ianvs/prettier-plugin-sort-imports'], - importOrder: [ - '^react(.*)$', - '', - '^@backstage/(.*)$', - '', - '', - '', - '^@janus-idp/(.*)$', - '', - '', - '', - '^[.]', - ], -}; diff --git a/plugins/orchestrator-form-react/.versionhistory.md b/plugins/orchestrator-form-react/.versionhistory.md new file mode 100644 index 0000000000..b17e69831a --- /dev/null +++ b/plugins/orchestrator-form-react/.versionhistory.md @@ -0,0 +1 @@ +- Bumped to 1.1.0 in main branch for next release 1.3.0 diff --git a/plugins/orchestrator-form-react/CHANGELOG.md b/plugins/orchestrator-form-react/CHANGELOG.md index 0f1ebc5a94..db12bf0598 100644 --- a/plugins/orchestrator-form-react/CHANGELOG.md +++ b/plugins/orchestrator-form-react/CHANGELOG.md @@ -1,20 +1,125 @@ ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.16.100 -* **@janus-idp/backstage-plugin-orchestrator-form-api:** upgraded to 1.0.100 +## 1.4.1 + +### Patch Changes + +- 0e6bfd3: feat: update Backstage to the latest version + + Update to Backstage 1.32.5 + +- 67f466a: Resolved the following issues: + + 1. enabled validation using customValidate, and replaced extraErrors with getExtraErrors, since extraErrors is supposed to be populated when running onSubmit, and that isn't exposed to the user. Added busy handling while calling getExtraErrors. + 2. moved FormComponent to a separate component, to avoid buggy behavior and code smells with component generated in a different component. + 3. update formData on each change instead of when moving to next step, to avoid data being cleared. + 4. fix bug in validator - it only worked in first step, because of issue in @rjsf form + 5. removed unnecessary package json-schema that was used just for lint error, and fixed the root cause of lint error when importing types from @types/json-schema + +- Updated dependencies [0e6bfd3] +- Updated dependencies [67f466a] + - @janus-idp/backstage-plugin-orchestrator-form-api@1.4.1 + +## 1.4.0 + +### Minor Changes + +- 8244f28: chore(deps): update to backstage 1.32 + +### Patch Changes + +- Updated dependencies [8244f28] + - @janus-idp/backstage-plugin-orchestrator-form-api@1.4.0 + +## 1.3.1 + +### Patch Changes + +- 7342e9b: chore: remove @janus-idp/cli dep and relink local packages + + This update removes `@janus-idp/cli` from all plugins, as it’s no longer necessary. Additionally, packages are now correctly linked with a specified version. + +## 1.3.0 + +### Minor Changes + +- d9551ae: feat(deps): update to backstage 1.31 + +### Patch Changes + +- d9551ae: Change local package references to a `*` +- d9551ae: upgrade to yarn v3 +- Updated dependencies [d9551ae] +- Updated dependencies [d9551ae] + - @janus-idp/backstage-plugin-orchestrator-form-api@1.3.0 + +* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.21.0 + +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.20.0 + +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.19.0 + +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.18.2 + +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.18.1 + +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.18.0 + +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.17.3 + +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-form-api:** upgraded to 1.1.0 + +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.17.2 + +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.17.1 + +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.17.0 + +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.17.0 + +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.0.0 +- **@janus-idp/backstage-plugin-orchestrator-form-api:** upgraded to 1.0.0 + +### Dependencies + +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.16.0 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.16.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.2 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.1 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.1 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.0 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-form-api:** upgraded to 1.0.1 +- **@janus-idp/backstage-plugin-orchestrator-form-api:** upgraded to 1.0.1 diff --git a/plugins/orchestrator-form-react/package.json b/plugins/orchestrator-form-react/package.json index 93d237890e..1242133d43 100644 --- a/plugins/orchestrator-form-react/package.json +++ b/plugins/orchestrator-form-react/package.json @@ -5,47 +5,46 @@ "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", - "private": true, "publishConfig": { "access": "public", "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, "backstage": { - "role": "web-library" + "role": "web-library", + "pluginId": "orchestrator-form", + "pluginPackages": [ + "@janus-idp/backstage-plugin-orchestrator-form-react" + ] }, "sideEffects": false, "scripts": { "start": "backstage-cli package start", "build": "backstage-cli package build", - "lint:check": "backstage-cli package lint", - "lint:fix": "backstage-cli package lint --fix", + "lint": "backstage-cli package lint", "test": "backstage-cli package test --passWithNoTests --coverage", "clean": "backstage-cli package clean", "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", - "tsc": "tsc", - "prettier:check": "prettier --ignore-unknown --check .", - "prettier:fix": "prettier --ignore-unknown --write ." + "tsc": "tsc" }, "dependencies": { "@backstage/core-components": "^0.14.9", "@backstage/core-plugin-api": "^1.9.3", "@backstage/types": "^1.1.1", - "@janus-idp/backstage-plugin-orchestrator-common": "1.16.100", "@janus-idp/backstage-plugin-orchestrator-form-api": "1.0.100", - "json-schema": "^0.4.0" + "@material-ui/core": "^4.12.4", + "@rjsf/core": "5.18.5", + "@rjsf/material-ui": "5.18.5", + "@rjsf/utils": "5.18.5", + "@rjsf/validator-ajv8": "5.18.5", + "json-schema-library": "^9.0.0", + "lodash": "^4.17.21" }, "peerDependencies": { - "@material-ui/core": "^4.12.4", - "@rjsf/core": "^5.17.1", - "@rjsf/material-ui": "^5.17.1", - "@rjsf/utils": "^5.17.1", - "@rjsf/validator-ajv8": "^5.17.1", "react": "^16.13.1 || ^17.0.0 || ^18.0.0" }, "devDependencies": { - "prettier": "3.3.3", "@backstage/cli": "0.26.11", "@types/json-schema": "^7.0.15" }, diff --git a/plugins/orchestrator-form-react/src/DefaultFormApi.tsx b/plugins/orchestrator-form-react/src/DefaultFormApi.tsx index b3dd08b0a8..518cf88ce5 100644 --- a/plugins/orchestrator-form-react/src/DefaultFormApi.tsx +++ b/plugins/orchestrator-form-react/src/DefaultFormApi.tsx @@ -1,22 +1,17 @@ import React from 'react'; -import { JsonObject } from '@backstage/types'; - -import { FormProps } from '@rjsf/core'; -import { JSONSchema7 } from 'json-schema'; +import type { JSONSchema7 } from 'json-schema'; import { + FormDecoratorProps, OrchestratorFormApi, OrchestratorFormDecorator, } from '@janus-idp/backstage-plugin-orchestrator-form-api'; class DefaultFormApi implements OrchestratorFormApi { - getFormDecorator(): OrchestratorFormDecorator { - return ( - FormComponent: React.ComponentType< - Partial> - >, - ) => FormComponent; + getFormDecorator(_schema: JSONSchema7): OrchestratorFormDecorator { + return (FormComponent: React.ComponentType) => + FormComponent; } } diff --git a/plugins/orchestrator-form-react/src/components/OrchestratorForm.tsx b/plugins/orchestrator-form-react/src/components/OrchestratorForm.tsx index 82aa403b90..a689511425 100644 --- a/plugins/orchestrator-form-react/src/components/OrchestratorForm.tsx +++ b/plugins/orchestrator-form-react/src/components/OrchestratorForm.tsx @@ -1,207 +1,133 @@ -import React from 'react'; +import React, { Fragment } from 'react'; -import { Content, StructuredMetadataTable } from '@backstage/core-components'; -import { useApiHolder } from '@backstage/core-plugin-api'; import { JsonObject } from '@backstage/types'; -import { - Box, - Button, - Paper, - Step, - StepContent, - StepLabel, - Stepper, - Typography, -} from '@material-ui/core'; -import { FormProps, withTheme } from '@rjsf/core'; -import { Theme as MuiTheme } from '@rjsf/material-ui'; -import { RJSFSchema, UiSchema } from '@rjsf/utils'; -import validator from '@rjsf/validator-ajv8'; +import { UiSchema } from '@rjsf/utils'; +import type { JSONSchema7 } from 'json-schema'; -import { WorkflowInputSchemaStep } from '@janus-idp/backstage-plugin-orchestrator-common'; -import { orchestratorFormApiRef } from '@janus-idp/backstage-plugin-orchestrator-form-api'; +import generateUiSchema from '../utils/generateUiSchema'; +import { StepperContextProvider } from '../utils/StepperContext'; +import OrchestratorFormStepper, { + OrchestratorFormStep, + OrchestratorFormToolbar, +} from './OrchestratorFormStepper'; +import OrchestratorFormWrapper from './OrchestratorFormWrapper'; +import ReviewStep from './ReviewStep'; -import { defaultFormExtensionsApi } from '../DefaultFormApi'; -import SubmitButton from './SubmitButton'; - -const Form = withTheme(MuiTheme); - -const getCombinedData = ( - steps: WorkflowInputSchemaStep[], - isComposedSchema: boolean, -): JsonObject => { - if (!isComposedSchema) { - return steps[0].data; - } - return steps.reduce( - (prev, { key, data }) => ({ ...prev, [key]: data }), - {}, +const getNumSteps = (schema: JSONSchema7): number | undefined => { + if (schema.type !== 'object' || !schema.properties) return undefined; + const isMultiStep = Object.values(schema.properties).every( + prop => (prop as JSONSchema7).type === 'object', ); + return isMultiStep ? Object.keys(schema.properties).length : undefined; }; -const ReviewStep = ({ - busy, - steps, - isComposedSchema, - handleBack, - handleReset, - handleExecute, +const SingleStepForm = ({ + schema, + formData, + onChange, + uiSchema, }: { - busy: boolean; - steps: WorkflowInputSchemaStep[]; - isComposedSchema: boolean; - handleBack: () => void; - handleReset: () => void; - handleExecute: (getParameters: () => JsonObject) => Promise; + schema: JSONSchema7; + formData: JsonObject; + onChange: (formData: JsonObject) => void; + uiSchema: UiSchema; }) => { - const displayData: JsonObject = React.useMemo(() => { - if (!isComposedSchema) { - return steps[0].data; - } - return steps.reduce( - (prev, { title, data }) => ({ ...prev, [title]: data }), - {}, - ); - }, [steps, isComposedSchema]); - return ( - - - Review and run - - - - - - handleExecute(() => getCombinedData(steps, isComposedSchema)) - } - submitting={busy} - focusOnMount - > - Run - - - - ); + const steps = React.useMemo(() => { + return [ + { + title: schema.title || 'Inputs', + key: 'schema', + content: ( + + + + ), + }, + ]; + }, [schema, formData, onChange, uiSchema]); + return ; }; -const FormWrapper = ({ - step, - onSubmit, - children, -}: Pick, 'onSubmit' | 'children'> & { - step: WorkflowInputSchemaStep; -}) => { - const formApi = - useApiHolder().get(orchestratorFormApiRef) || defaultFormExtensionsApi; - const withFormExtensions = formApi.getFormDecorator(); - const firstKey = Object.keys(step.schema.properties ?? {})[0]; - const uiSchema = React.useMemo(() => { - const res: UiSchema = firstKey - ? { [firstKey]: { 'ui:autofocus': 'true' } } - : {}; - for (const key of step.readonlyKeys) { - res[key] = { 'ui:disabled': 'true' }; - } - return res; - }, [firstKey, step.readonlyKeys]); - - const schema = { ...step.schema, title: '' }; // the title is in the step - - const FormComponent = (props: Partial) => { - return ( -
    - {children} -
    - ); - }; - const NewComponent = withFormExtensions(FormComponent); - return ; +type OrchestratorFormProps = { + schema: JSONSchema7; + isExecuting: boolean; + handleExecute: (parameters: JsonObject) => Promise; + data?: JsonObject; + isDataReadonly?: boolean; }; const OrchestratorForm = ({ - isComposedSchema, - steps: inputSteps, + schema, handleExecute, isExecuting, - onReset, -}: { - isComposedSchema: boolean; - steps: WorkflowInputSchemaStep[]; - handleExecute: (getParameters: () => JsonObject) => Promise; - isExecuting: boolean; - onReset: () => void; -}) => { - const [activeStep, setActiveStep] = React.useState(0); - const handleBack = () => setActiveStep(activeStep - 1); + data, + isDataReadonly, +}: OrchestratorFormProps) => { + const [formData, setFormData] = React.useState(data || {}); + const numStepsInMultiStepSchema = React.useMemo( + () => getNumSteps(schema), + [schema], + ); + const isMultiStep = numStepsInMultiStepSchema !== undefined; + + const _handleExecute = React.useCallback(() => { + handleExecute(formData || {}); + }, [formData, handleExecute]); + + const onChange = React.useCallback( + (_formData: JsonObject) => { + setFormData(_formData); + }, + [setFormData], + ); + + const uiSchema = React.useMemo>(() => { + return generateUiSchema( + schema, + isMultiStep, + isDataReadonly ? data : undefined, + ); + }, [schema, isMultiStep, isDataReadonly, data]); + + const reviewStep = React.useMemo( + () => ( + + ), + [formData, schema, isExecuting, _handleExecute], + ); - const [steps, setSteps] = React.useState([...inputSteps]); return ( - <> - - {steps?.map((step, index) => ( - - - - {step.title} - - - - { - const newStep: WorkflowInputSchemaStep = { - ...step, - data: e.formData ?? {}, - }; - const newSteps = [...steps]; - newSteps.splice(index, 1, newStep); - setSteps(newSteps); - setActiveStep(activeStep + 1); - }} - > - - - - - - ))} - - {activeStep === steps.length && ( - { - onReset(); - setSteps([...inputSteps]); - setActiveStep(0); - }} - handleExecute={handleExecute} - busy={isExecuting} + + {isMultiStep ? ( + + + // it is required to pass the fragment so rjsf won't generate a Submit button + ) : ( + )} - + ); }; diff --git a/plugins/orchestrator-form-react/src/components/OrchestratorFormStepper.tsx b/plugins/orchestrator-form-react/src/components/OrchestratorFormStepper.tsx new file mode 100644 index 0000000000..c877c9d661 --- /dev/null +++ b/plugins/orchestrator-form-react/src/components/OrchestratorFormStepper.tsx @@ -0,0 +1,109 @@ +import React from 'react'; + +import { + Button, + makeStyles, + Step, + StepLabel, + Stepper, + Typography, +} from '@material-ui/core'; + +import { useStepperContext } from '../utils/StepperContext'; +import SubmitButton from './SubmitButton'; + +const useStyles = makeStyles(theme => ({ + // Hotfix: this should be fixed in the theme + step: { + '& form': { + '& .field-array > div > div': { + outline: 'inherit !important', + padding: 'inherit !important', + backgroundColor: 'inherit !important', + + '& div > div > div > div': { + // unfortunately there are no better CSS selectors + backgroundColor: 'inherit !important', + }, + }, + }, + }, + regularButton: { + // hotifx for https://issues.redhat.com/browse/FLPATH-1825 + backgroundColor: 'inherit !important', + }, + footer: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'right', + marginTop: theme.spacing(2), + }, + formWrapper: { + padding: theme.spacing(2), + }, +})); + +export type OrchestratorFormStep = { + content: React.ReactNode; + title: string; + key: string; +}; + +const OrchestratorFormStepper = ({ + steps, +}: { + steps: OrchestratorFormStep[]; +}) => { + const { activeStep, reviewStep } = useStepperContext(); + const stepsWithReview = [ + ...steps, + { content: reviewStep, title: 'Review', key: 'review' }, + ]; + const styles = useStyles(); + return ( + <> + + {stepsWithReview?.map((step, index) => ( + + + + {step.title} + + + + ))} + +
    + {stepsWithReview[activeStep].content} +
    + + ); +}; + +export const OrchestratorFormToolbar = () => { + const { activeStep, handleBack, isValidating } = useStepperContext(); + const styles = useStyles(); + return ( +
    + + Next +
    + ); +}; + +export default OrchestratorFormStepper; diff --git a/plugins/orchestrator-form-react/src/components/OrchestratorFormWrapper.tsx b/plugins/orchestrator-form-react/src/components/OrchestratorFormWrapper.tsx new file mode 100644 index 0000000000..1cc025ae59 --- /dev/null +++ b/plugins/orchestrator-form-react/src/components/OrchestratorFormWrapper.tsx @@ -0,0 +1,153 @@ +import React from 'react'; + +import { ErrorPanel } from '@backstage/core-components'; +import { useApiHolder } from '@backstage/core-plugin-api'; +import { JsonObject } from '@backstage/types'; + +import { Grid } from '@material-ui/core'; +import { withTheme } from '@rjsf/core'; +import { Theme as MuiTheme } from '@rjsf/material-ui'; +import { ErrorSchema, UiSchema } from '@rjsf/utils'; +import type { JSONSchema7 } from 'json-schema'; +import omit from 'lodash/omit'; + +import { + FormDecoratorProps, + orchestratorFormApiRef, +} from '@janus-idp/backstage-plugin-orchestrator-form-api'; + +import { defaultFormExtensionsApi } from '../DefaultFormApi'; +import { useStepperContext } from '../utils/StepperContext'; +import useValidator from '../utils/useValidator'; +import StepperObjectField from './StepperObjectField'; + +const MuiForm = withTheme(MuiTheme); + +type OrchestratorFormWrapperProps = { + schema: JSONSchema7; + numStepsInMultiStepSchema?: number; + children: React.ReactNode; + formData: JsonObject; + onChange: (formData: JsonObject) => void; + uiSchema: UiSchema; +}; + +const WrapperFormPropsContext = + React.createContext(null); + +const useWrapperFormPropsContext = (): OrchestratorFormWrapperProps => { + const context = React.useContext(WrapperFormPropsContext); + if (context === null) { + throw new Error('OrchestratorFormWrapperProps not provided'); + } + return context; +}; + +const FormComponent = (decoratorProps: FormDecoratorProps) => { + const props = useWrapperFormPropsContext(); + const { + numStepsInMultiStepSchema, + uiSchema, + schema, + onChange, + formData, + children, + } = props; + const [extraErrors, setExtraErrors] = React.useState< + ErrorSchema | undefined + >(); + const isMultiStep = numStepsInMultiStepSchema !== undefined; + const { handleNext, activeStep, handleValidateStarted, handleValidateEnded } = + useStepperContext(); + const [validationError, setValidationError] = React.useState< + Error | undefined + >(); + const validator = useValidator(isMultiStep); + const getActiveKey = () => { + if (!isMultiStep) { + return undefined; + } + return Object.keys(schema.properties || {})[activeStep]; + }; + + const onSubmit = async (_formData: JsonObject) => { + setExtraErrors(undefined); + let _extraErrors: ErrorSchema | undefined = undefined; + let _validationError: Error | undefined = undefined; + if (decoratorProps.getExtraErrors) { + try { + handleValidateStarted(); + _extraErrors = await decoratorProps.getExtraErrors(formData); + const activeKey = getActiveKey(); + setExtraErrors( + activeKey && _extraErrors?.[activeKey] + ? (_extraErrors[activeKey] as ErrorSchema) + : _extraErrors, + ); + } catch (err) { + _validationError = err as Error; + } finally { + handleValidateEnded(); + } + } + setValidationError(_validationError); + if ( + (!_extraErrors || Object.keys(_extraErrors).length === 0) && + !_validationError && + activeStep < (numStepsInMultiStepSchema || 1) + ) { + handleNext(); + } + }; + + return ( + + {validationError && ( + + + + )} + + onSubmit(e.formData || {})} + onChange={e => { + onChange(e.formData || {}); + if (decoratorProps.onChange) { + decoratorProps.onChange(e); + } + }} + > + {children} + + + + ); +}; + +const OrchestratorFormWrapper = ({ + schema, + uiSchema, + ...props +}: OrchestratorFormWrapperProps) => { + const formApi = + useApiHolder().get(orchestratorFormApiRef) || defaultFormExtensionsApi; + const NewComponent = React.useMemo(() => { + const formDecorator = formApi.getFormDecorator(schema, uiSchema); + return formDecorator(FormComponent); + }, [schema, formApi, uiSchema]); + return ( + + + + ); +}; + +export default OrchestratorFormWrapper; diff --git a/plugins/orchestrator-form-react/src/components/ReviewStep.tsx b/plugins/orchestrator-form-react/src/components/ReviewStep.tsx new file mode 100644 index 0000000000..2a59b59ea6 --- /dev/null +++ b/plugins/orchestrator-form-react/src/components/ReviewStep.tsx @@ -0,0 +1,60 @@ +import React from 'react'; + +import { Content, StructuredMetadataTable } from '@backstage/core-components'; +import { JsonObject } from '@backstage/types'; + +import { Box, Button, makeStyles, Paper } from '@material-ui/core'; +import type { JSONSchema7 } from 'json-schema'; + +import generateReviewTableData from '../utils/generateReviewTableData'; +import { useStepperContext } from '../utils/StepperContext'; +import SubmitButton from './SubmitButton'; + +const useStyles = makeStyles(theme => ({ + footer: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'right', + marginTop: theme.spacing(2), + }, +})); + +const ReviewStep = ({ + busy, + schema, + data, + handleExecute, +}: { + busy: boolean; + schema: JSONSchema7; + data: JsonObject; + handleExecute: () => void; +}) => { + const styles = useStyles(); + const { handleBack } = useStepperContext(); + const displayData = React.useMemo(() => { + return generateReviewTableData(schema, data); + }, [schema, data]); + return ( + + + + +
    + + + Run + +
    +
    +
    + ); +}; + +export default ReviewStep; diff --git a/plugins/orchestrator-form-react/src/components/StepperObjectField.tsx b/plugins/orchestrator-form-react/src/components/StepperObjectField.tsx new file mode 100644 index 0000000000..005b0b4117 --- /dev/null +++ b/plugins/orchestrator-form-react/src/components/StepperObjectField.tsx @@ -0,0 +1,69 @@ +import React from 'react'; + +import { JsonObject } from '@backstage/types'; + +import ObjectField from '@rjsf/core/lib/components/fields/ObjectField'; +import { ErrorSchema, FieldProps, IdSchema } from '@rjsf/utils'; +import type { JSONSchema7 } from 'json-schema'; + +import OrchestratorFormStepper, { + OrchestratorFormStep, + OrchestratorFormToolbar, +} from './OrchestratorFormStepper'; + +const StepperObjectField = ({ + formData, + schema, + uiSchema, + onChange, + registry, + idSchema, + errorSchema, + ...props +}: FieldProps) => { + if (schema.properties === undefined) { + throw new Error( + "Stepper object field is not supported for schema that doesn't contain properties", + ); + } + const steps = Object.entries(schema.properties).reduce< + OrchestratorFormStep[] + >((prev, [key, subSchema]) => { + if (typeof subSchema === 'boolean') { + return prev; + } + return [ + ...prev, + { + content: ( + <> + + {...props} + schema={{ ...subSchema, title: '' }} // the title is in the step + uiSchema={uiSchema?.[key] || {}} + formData={(formData?.[key] as JsonObject) || {}} + onChange={data => { + onChange({ ...formData, [key]: data }); + }} + idSchema={idSchema[key] as IdSchema} + registry={{ + ...registry, + fields: { + ...registry.fields, + ObjectField: ObjectField, // undo override of objectfield + }, + }} + errorSchema={errorSchema?.[key] as ErrorSchema} + /> + + + ), + title: subSchema.title || key, + key, + }, + ]; + }, []); + return ; +}; + +export default StepperObjectField; diff --git a/plugins/orchestrator-form-react/src/components/SubmitButton.tsx b/plugins/orchestrator-form-react/src/components/SubmitButton.tsx index 1008df8ff6..55ffd8d3b6 100644 --- a/plugins/orchestrator-form-react/src/components/SubmitButton.tsx +++ b/plugins/orchestrator-form-react/src/components/SubmitButton.tsx @@ -9,7 +9,7 @@ const SubmitButton = ({ focusOnMount, }: { submitting: boolean; - handleClick: () => void; + handleClick?: () => void; children: React.ReactNode; focusOnMount?: boolean; }) => { @@ -28,6 +28,9 @@ const SubmitButton = ({ disabled={submitting} type="submit" startIcon={submitting ? : null} + // work around for using react 18 with material 4 causes button to crash when pressing enter, see https://github.com/mui/material-ui/issues/30953 + // this will be resolved when upgrading to material 5 + disableRipple > {children} diff --git a/plugins/orchestrator-form-react/src/components/useStyles.ts b/plugins/orchestrator-form-react/src/components/useStyles.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/orchestrator-form-react/src/utils/StepperContext.tsx b/plugins/orchestrator-form-react/src/utils/StepperContext.tsx new file mode 100644 index 0000000000..9672db0272 --- /dev/null +++ b/plugins/orchestrator-form-react/src/utils/StepperContext.tsx @@ -0,0 +1,46 @@ +import React from 'react'; + +export type StepperContext = { + activeStep: number; + handleNext: () => void; + handleBack: () => void; + reviewStep: React.ReactNode; + isValidating: boolean; + handleValidateStarted: () => void; + handleValidateEnded: () => void; +}; + +const context = React.createContext(null); + +export const useStepperContext = (): StepperContext => { + const multiStepFormContext = React.useContext(context); + if (!multiStepFormContext) { + throw new Error('Context StepperContext is not defined'); + } + return multiStepFormContext; +}; + +export const StepperContextProvider = ({ + children, + reviewStep, +}: { + children: React.ReactNode; + reviewStep: React.ReactNode; +}) => { + const [activeStep, setActiveStep] = React.useState(0); + const [isValidating, setIsValidating] = React.useState(false); + const contextData = React.useMemo(() => { + return { + activeStep, + handleNext: () => { + setActiveStep(curActiveStep => curActiveStep + 1); + }, + handleBack: () => setActiveStep(curActiveStep => curActiveStep - 1), + reviewStep, + isValidating, + handleValidateStarted: () => setIsValidating(true), + handleValidateEnded: () => setIsValidating(false), + }; + }, [setActiveStep, activeStep, reviewStep, isValidating, setIsValidating]); + return {children}; +}; diff --git a/plugins/orchestrator-form-react/src/utils/generateReviewTableData.test.ts b/plugins/orchestrator-form-react/src/utils/generateReviewTableData.test.ts new file mode 100644 index 0000000000..4e40419de1 --- /dev/null +++ b/plugins/orchestrator-form-react/src/utils/generateReviewTableData.test.ts @@ -0,0 +1,154 @@ +import type { JSONSchema7 } from 'json-schema'; + +import generateReviewTableData from './generateReviewTableData'; + +describe('mapSchemaToData', () => { + it('should map schema titles to data values correctly', () => { + const schema: JSONSchema7 = { + type: 'object', + title: 'Person', + properties: { + firstName: { + type: 'string', + title: 'First Name', + }, + lastName: { + type: 'string', + title: 'Last Name', + }, + age: { + type: 'number', + title: 'Age', + }, + address: { + type: 'object', + title: 'Address', + properties: { + street: { + type: 'string', + title: 'Street', + }, + city: { + type: 'string', + title: 'City', + }, + }, + }, + }, + }; + + const data = { + firstName: 'John', + lastName: 'Doe', + age: 30, + address: { + street: '123 Main St', + city: 'Somewhere', + }, + }; + + const expectedResult = { + 'First Name': 'John', + 'Last Name': 'Doe', + Age: 30, + Address: { + Street: '123 Main St', + City: 'Somewhere', + }, + }; + + const result = generateReviewTableData(schema, data); + expect(result).toEqual(expectedResult); + }); + + it('should map schema titles to data values with arrays correctly', () => { + const schema: JSONSchema7 = { + type: 'object', + title: 'Person', + properties: { + firstName: { + type: 'string', + title: 'First Name', + }, + hobbies: { + type: 'array', + title: 'Hobbies', + items: { + type: 'string', + }, + }, + }, + }; + + const data = { + firstName: 'Jane', + hobbies: ['reading', 'hiking'], + }; + + const expectedResult = { + 'First Name': 'Jane', + Hobbies: ['reading', 'hiking'], + }; + + const result = generateReviewTableData(schema, data); + expect(result).toEqual(expectedResult); + }); + + it('should map schema titles to data values with complex nesting correctly', () => { + const schema: JSONSchema7 = { + type: 'object', + properties: { + person: { + type: 'object', + title: 'Person', + properties: { + name: { + type: 'string', + title: 'Name', + }, + addresses: { + type: 'array', + title: 'Addresses', + items: { + type: 'object', + properties: { + street: { type: 'string' }, + city: { type: 'string' }, + }, + }, + }, + }, + }, + }, + }; + + const data = { + person: { + name: 'John', + addresses: [ + { street: '123 A St', city: 'City A' }, + { street: '456 B St', city: 'City B' }, + ], + }, + }; + + const expectedResult = { + Person: { + Name: 'John', + Addresses: [ + { + street: '123 A St', + city: 'City A', + }, + { + street: '456 B St', + city: 'City B', + }, + ], + }, + }; + + const result = generateReviewTableData(schema, data); + expect(result).toEqual(expectedResult); + }); +}); diff --git a/plugins/orchestrator-form-react/src/utils/generateReviewTableData.ts b/plugins/orchestrator-form-react/src/utils/generateReviewTableData.ts new file mode 100644 index 0000000000..3fdec0c2ee --- /dev/null +++ b/plugins/orchestrator-form-react/src/utils/generateReviewTableData.ts @@ -0,0 +1,61 @@ +// import { JsonObject, JsonValue } from '@backstage/types'; +// import { JSONSchema7 } from 'json-schema'; +import { JsonObject, JsonValue } from '@backstage/types'; + +import type { JSONSchema7 } from 'json-schema'; +import { JsonSchema, Draft07 as JSONSchema } from 'json-schema-library'; + +export function isJsonObject(value?: JsonValue): value is JsonObject { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +export function processSchema( + key: string, + value: JsonValue | undefined, + schema: JSONSchema7, + formState: JsonObject, +): JsonObject { + const parsedSchema = new JSONSchema(schema); + const definitionInSchema = + key === '' + ? (schema as JsonSchema) + : parsedSchema.getSchema({ + pointer: `#/${key}`, + data: formState, + }); + + const name = definitionInSchema?.title ?? key; + if (definitionInSchema) { + if (definitionInSchema['ui:widget'] === 'password') { + return { [name]: '******' }; + } + + if (isJsonObject(value)) { + // Recurse nested objects + const nestedValue = Object.entries(value).reduce( + (prev, [nestedKey, _nestedValue]) => { + const curKey = key ? `${key}/${nestedKey}` : nestedKey; + return { + ...prev, + ...processSchema(curKey, _nestedValue, schema, formState), + }; + }, + {}, + ); + return { [name]: nestedValue }; + } + } + + return { [name]: value }; +} + +function generateReviewTableData( + schema: JSONSchema7, + data: JsonObject, +): JsonObject { + schema.title = ''; + const result = processSchema('', data, schema, data); + return result[''] as JsonObject; +} + +export default generateReviewTableData; diff --git a/plugins/orchestrator-form-react/src/utils/generateUiSchema.test.ts b/plugins/orchestrator-form-react/src/utils/generateUiSchema.test.ts new file mode 100644 index 0000000000..8af4f85b32 --- /dev/null +++ b/plugins/orchestrator-form-react/src/utils/generateUiSchema.test.ts @@ -0,0 +1,516 @@ +import { UiSchema } from '@rjsf/utils'; +import type { JSONSchema7 } from 'json-schema'; + +import generateUiSchema from './generateUiSchema'; + +describe('extract ui schema', () => { + it('if has properties ui: should create ui schema with properties', () => { + const expected = { + name: { 'ui:validationType': 'product', 'ui:autofocus': true }, + color: { 'ui:widget': 'color1', 'ui:validationType': 'color' }, + }; + const mixedSchema: JSONSchema7 = { + title: 'Product', + type: 'object', + properties: { + name: { + type: 'string', + title: 'Product Name', + 'ui:validationType': 'product', + }, + color: { + type: 'string', + title: 'Product Color', + description: 'The color of the product', + 'ui:widget': 'color1', + 'ui:validationType': 'color', + }, + }, + required: ['name', 'color'], + } as JSONSchema7; + const uiSchema = generateUiSchema(mixedSchema, false); + expect(uiSchema).toEqual(expected); + }); + + it('if no properties ui: should create ui schema just with auto focus', () => { + const mixedSchema: JSONSchema7 = { + title: 'Product', + type: 'object', + properties: { + name: { + type: 'string', + title: 'Product Name', + }, + color: { + type: 'string', + title: 'Product Color', + description: 'The color of the product', + }, + }, + required: ['name', 'color'], + } as JSONSchema7; + const uiSchema = generateUiSchema(mixedSchema, false); + expect(uiSchema).toEqual({ name: { 'ui:autofocus': true } }); + }); + + it('should extract from array', () => { + const mixedSchema = { + title: 'A list of tasks', + type: 'object', + required: ['title'], + properties: { + title: { + type: 'string', + title: 'Task list title', + }, + tasks: { + type: 'array', + title: 'Tasks', + items: { + type: 'object', + required: ['title'], + properties: { + title: { + type: 'string', + title: 'Title', + description: 'A sample title', + }, + details: { + type: 'string', + title: 'Task details', + description: 'Enter the task details', + 'ui:widget': 'textarea', + }, + done: { + type: 'boolean', + title: 'Done?', + default: false, + }, + }, + }, + }, + }, + } as JSONSchema7; + const expected = { + title: { + 'ui:autofocus': true, + }, + tasks: { + items: { + details: { + 'ui:widget': 'textarea', + }, + }, + }, + } as UiSchema; + const uiSchema = generateUiSchema(mixedSchema, false); + expect(uiSchema).toEqual(expected); + }); + + it('should extract from array with fixed number of items', () => { + const mixedSchema = { + type: 'object', + properties: { + fixedItemsList: { + type: 'array', + title: 'A list of fixed items', + items: [ + { + title: 'A string value', + type: 'string', + default: 'lorem ipsum', + 'ui:widget': 'textarea', + }, + { + title: 'a boolean value', + type: 'boolean', + }, + ], + additionalItems: { + title: 'Additional item', + type: 'number', + }, + }, + }, + } as JSONSchema7; + const expected = { + fixedItemsList: { + items: [ + { + 'ui:widget': 'textarea', + }, + ], + 'ui:autofocus': true, + }, + } as JSONSchema7; + + const uiSchema = generateUiSchema(mixedSchema, false); + expect(uiSchema).toEqual(expected); + }); + + it('should handle anyOf', () => { + const schemaWithAnyOf = { + title: 'A selection of items', + type: 'object', + properties: { + selectedItem: { + anyOf: [ + { type: 'number', title: 'Number item' }, + { type: 'boolean', title: 'Boolean item' }, + { type: 'string', title: 'Color', 'ui:widget': 'color' }, + ], + }, + }, + } as JSONSchema7; + + const expected = { + selectedItem: { + anyOf: [{}, {}, { 'ui:widget': 'color' }], + 'ui:autofocus': true, + }, + }; + + const uiSchema = generateUiSchema(schemaWithAnyOf, false); + expect(uiSchema).toEqual(expected); + }); + + it('should handle oneOf', () => { + const schemaWithAnyOf = { + title: 'A selection of items', + type: 'object', + properties: { + selectedItem: { + oneOf: [ + { type: 'string', title: 'Color', 'ui:widget': 'color' }, + { type: 'number', title: 'Number item' }, + { type: 'boolean', title: 'Boolean item' }, + ], + }, + }, + } as JSONSchema7; + + const expected = { + selectedItem: { + oneOf: [{ 'ui:widget': 'color' }], + 'ui:autofocus': true, + }, + }; + + const uiSchema = generateUiSchema(schemaWithAnyOf, false); + expect(uiSchema).toEqual(expected); + }); + + it('should handle allOf', () => { + const schemaWithAnyOf = { + title: 'A selection of items', + type: 'object', + properties: { + selectedItem: { + allOf: [ + { type: 'string', title: 'Color', 'ui:widget': 'color' }, + { type: 'number', title: 'Number item' }, + { type: 'boolean', title: 'Boolean item' }, + ], + }, + }, + } as JSONSchema7; + + const expected = { + selectedItem: { + allOf: [{ 'ui:widget': 'color' }], + 'ui:autofocus': true, + }, + }; + + const uiSchema = generateUiSchema(schemaWithAnyOf, false); + expect(uiSchema).toEqual(expected); + }); + + it('should handle referenced schemas', () => { + const refSchema = { + title: 'A referenced schema', + type: 'object', + properties: { + user: { + $ref: '#/definitions/User', + }, + }, + definitions: { + User: { + type: 'object', + properties: { + firstName: { + type: 'string', + title: 'First name', + 'ui:widget': 'textarea', + 'ui:autofocus': true, + }, + lastName: { type: 'string', title: 'Last name' }, + }, + }, + }, + } as JSONSchema7; + + const expected = { + user: { + firstName: { 'ui:autofocus': true, 'ui:widget': 'textarea' }, + }, + }; + + const uiSchema = generateUiSchema(refSchema, true); + expect(uiSchema).toEqual(expected); + }); + + it('should handle schemas with multiple hierarchies', () => { + const complexSchema = { + title: 'Complex schema with multiple hierarchies', + type: 'object', + properties: { + person: { + type: 'object', + properties: { + name: { type: 'string', title: 'Name' }, + password: { + type: 'string', + title: 'Name', + 'ui:widget': 'password', + }, + address: { + type: 'object', + properties: { + street: { + type: 'string', + title: 'Street', + 'ui:widget': 'textarea', + }, + city: { + type: 'string', + title: 'City', + 'ui:widget': 'textarea', + }, + }, + }, + }, + }, + }, + } as JSONSchema7; + + const expected = { + person: { + name: { 'ui:autofocus': true }, + password: { 'ui:widget': 'password' }, + address: { + street: { 'ui:widget': 'textarea' }, + city: { 'ui:widget': 'textarea' }, + }, + }, + }; + + const uiSchema = generateUiSchema(complexSchema, true); + expect(uiSchema).toEqual(expected); + }); + + it('should handle if/then/else schema with ui:widget: "textarea"', () => { + const schemaWithIfThenElse = { + title: 'Conditional Schema', + type: 'object', + properties: { + age: { type: 'number', title: 'Age', 'ui:autofocus': true }, + }, + if: { + properties: { age: { minimum: 18 } }, + }, + then: { + properties: { + canVote: { + type: 'boolean', + title: 'Can vote?', + 'ui:description': 'can vote', + }, + }, + }, + else: { + properties: { + needsConsent: { + type: 'boolean', + title: 'Needs parental consent?', + 'ui:description': 'needs consent', + }, + }, + }, + } as JSONSchema7; + + const expected = { + age: { 'ui:autofocus': true }, + canVote: { 'ui:description': 'can vote' }, + needsConsent: { 'ui:description': 'needs consent' }, + }; + + const uiSchema = generateUiSchema(schemaWithIfThenElse, false); + expect(uiSchema).toEqual(expected); + }); + + it('should handle a complex schema with various ui: properties and $ref, including readonly data', () => { + const complexSchema = { + title: 'Complex Schema Example', + type: 'object', + properties: { + userInfo: { + type: 'object', + properties: { + name: { + type: 'string', + title: 'Name', + 'ui:autofocus': true, + 'ui:widget': 'text', + 'ui:placeholder': 'Enter your name', + 'ui:description': 'Full legal name', + }, + age: { + type: 'number', + title: 'Age', + 'ui:widget': 'updown', + 'ui:help': 'Enter your age in years', + }, + address: { + $ref: '#/definitions/Address', + }, + }, + }, + tasks: { + type: 'array', + title: 'Tasks', + items: { + type: 'object', + required: ['title'], + properties: { + title: { + type: 'string', + title: 'Title', + 'ui:widget': 'color', + 'ui:description': 'Color-coded task title', + }, + details: { + type: 'string', + title: 'Task details', + 'ui:widget': 'textarea', + 'ui:placeholder': 'Describe the task in detail', + }, + done: { + type: 'boolean', + title: 'Done?', + 'ui:widget': 'checkbox', + }, + }, + }, + }, + preferences: { + type: 'object', + properties: { + notifications: { + anyOf: [ + { + type: 'boolean', + title: 'Receive Notifications', + 'ui:widget': 'radio', + 'ui:options': { inline: true }, + }, + { + type: 'string', + title: 'Notification Email', + 'ui:widget': 'email', + 'ui:placeholder': 'you@example.com', + }, + ], + }, + }, + }, + }, + definitions: { + Address: { + type: 'object', + properties: { + street: { + type: 'string', + title: 'Street', + 'ui:widget': 'textarea', + 'ui:placeholder': '123 Main St', + }, + city: { + type: 'string', + title: 'City', + 'ui:widget': 'select', + 'ui:emptyValue': 'Select a city', + }, + }, + }, + }, + } as unknown as JSONSchema7; + + const expected = { + userInfo: { + name: { + 'ui:autofocus': true, + 'ui:widget': 'text', + 'ui:placeholder': 'Enter your name', + 'ui:description': 'Full legal name', + }, + age: { + 'ui:widget': 'updown', + 'ui:help': 'Enter your age in years', + }, + address: { + street: { + 'ui:widget': 'textarea', + 'ui:placeholder': '123 Main St', + }, + city: { + 'ui:widget': 'select', + 'ui:emptyValue': 'Select a city', + }, + }, + }, + tasks: { + items: { + title: { + 'ui:widget': 'color', + 'ui:description': 'Color-coded task title', + }, + details: { + 'ui:widget': 'textarea', + 'ui:placeholder': 'Describe the task in detail', + }, + done: { + 'ui:widget': 'checkbox', + }, + 'ui:readonly': true, + }, + }, + preferences: { + notifications: { + anyOf: [ + { + 'ui:widget': 'radio', + 'ui:options': { inline: true }, + }, + { + 'ui:widget': 'email', + 'ui:placeholder': 'you@example.com', + }, + ], + }, + }, + }; + + const uiSchema = generateUiSchema(complexSchema, true, { + tasks: { + items: { + title: 'purple', + details: 'abc', + done: true, + }, + }, + }); + expect(uiSchema).toEqual(expected); + }); +}); diff --git a/plugins/orchestrator-form-react/src/utils/generateUiSchema.ts b/plugins/orchestrator-form-react/src/utils/generateUiSchema.ts new file mode 100644 index 0000000000..be2a637069 --- /dev/null +++ b/plugins/orchestrator-form-react/src/utils/generateUiSchema.ts @@ -0,0 +1,223 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import { JsonObject } from '@backstage/types'; + +import { UiSchema } from '@rjsf/utils'; +import type { JSONSchema7, JSONSchema7Definition } from 'json-schema'; +import get from 'lodash/get'; +import set from 'lodash/set'; + +/** + * Extracts the uiSchema from a mixed JSON Schema that includes + * both standard JSON Schema properties and react-json-schema-form specific + * UI Schema properties (prefixed with 'ui:'). The function does not modify + * the original JSON Schema. + * + * @param mixedSchema - The JSON Schema that contains both standard and UI Schema properties. + * @returns An object representing the uiSchema. + */ + +const getSchemaDefinition = (ref: string, rootSchema: JSONSchema7) => { + const path = ref.replace(/^#\//, '').replace(/\//g, '.'); + return get(rootSchema, path); +}; + +const getStringAfterDot = (input: string) => + input.startsWith('.') ? input.slice(1) : input; + +function replaceSparseArrayElementsdWithEmptyObject(value: any): any { + /* handle cases where ui: properties exists for some of the itmes in the array, for example: + { + "selectedItem": { + "anyOf": [ + , + , + { + "ui:widget": "color", + }, + ], + } + the function will return + { + "selectedItem": { + "anyOf": [ + {}, + {}, + { + "ui:widget": "color", + }, + ], + } + */ + if (Array.isArray(value)) { + return [...value].map(item => { + return item ? replaceSparseArrayElementsdWithEmptyObject(item) : {}; + }); + } else if (value && typeof value === 'object') { + return Object.keys(value).reduce( + (acc, key) => { + acc[key] = replaceSparseArrayElementsdWithEmptyObject(value[key]); + return acc; + }, + {} as Record, + ); + } + return value; +} + +function extractUiSchema(mixedSchema: JSONSchema7): UiSchema { + const rootSchema = mixedSchema; + const result = {}; + + const processObjectProperties = ( + properties: { + [key: string]: JSONSchema7Definition; + }, + path: string, + ) => { + for (const [key, value] of Object.entries(properties)) { + // eslint-disable-next-line @typescript-eslint/no-use-before-define + processObject(value, `${path}.${key}`); + } + }; + + const processObject = (curSchema: JSONSchema7Definition, path: string) => { + if (typeof curSchema === 'boolean') { + return; + } + if (curSchema.$ref) { + processObject(getSchemaDefinition(curSchema.$ref, rootSchema), path); + } else if (curSchema.properties) { + processObjectProperties(curSchema.properties, path); + } else if (curSchema.items) { + processArraySchema(curSchema, path); + } else { + processLeafSchema(curSchema, path); + } + processComposedSchema(curSchema, path); + }; + + const processLeafSchema = ( + leafSchema: JSONSchema7Definition, + path: string, + ) => { + for (const [subSchemaKey, value] of Object.entries(leafSchema)) { + if (subSchemaKey.startsWith('ui:')) { + set(result, getStringAfterDot(`${path}.${subSchemaKey}`), value); + } + } + }; + + const processArrayItems = (items: JSONSchema7Definition[], path: string) => { + for (let i = 0; i < items.length; ++i) { + processObject(items[i], `${path}[${i}]`); + } + }; + + const processArraySchema = (schema: JSONSchema7, path: string) => { + if (Array.isArray(schema.items)) { + processArrayItems(schema.items, `${path}.items`); + } else if (typeof schema.items === 'object') { + processObject(schema.items, `${path}.items`); + } + if (schema.additionalItems && typeof schema.additionalItems === 'object') { + processObject(schema.additionalItems, `${path}.additinalItems`); + } + }; + + const processComposedSchema = (curSchema: JSONSchema7, path: string) => { + if (curSchema.anyOf) { + processArrayItems(curSchema.anyOf, `${path}.anyOf`); + } else if (curSchema.oneOf) { + processArrayItems(curSchema.oneOf, `${path}.oneOf`); + } else if (curSchema.allOf) { + processArrayItems(curSchema.allOf, `${path}.allOf`); + } else if (curSchema.then) { + processObject(curSchema.then, `${path}`); + if (curSchema.else) { + processObject(curSchema.else, `${path}`); + } + } + }; + + processObject(mixedSchema, ''); + return replaceSparseArrayElementsdWithEmptyObject(result); +} + +const addReadonly = ( + data: JsonObject, + uiSchema: UiSchema, + isMultiStep: boolean, +) => { + // make inputs that came from assessment instance variables readonly + if (!isMultiStep) { + for (const key of Object.keys(data)) { + uiSchema[key] = { + ...uiSchema[key], + 'ui:readonly': true, + }; + } + return; + } + for (const [stepKey, stepValue] of Object.entries(data)) { + uiSchema[stepKey] = { + ...uiSchema[stepKey], + }; + for (const key of Object.keys(stepValue as JsonObject)) { + uiSchema[stepKey][key] = { + ...uiSchema[stepKey][key], + 'ui:readonly': true, + }; + } + } +}; + +const addFocusOnFirstElement = ( + schema: JSONSchema7, + uiSchema: UiSchema, + isMultiStep: boolean, +) => { + if (!schema.properties) { + return; + } + if (!isMultiStep) { + const firstKey = Object.keys(schema.properties)[0]; + uiSchema[firstKey] = { + ...uiSchema[firstKey], + 'ui:autofocus': true, + }; + } + for (const [stepKey, subSchema] of Object.entries(schema.properties)) { + if (typeof subSchema !== 'object') { + return; + } + const _subSchema = subSchema.$ref + ? getSchemaDefinition(subSchema.$ref, schema) + : subSchema; + if (!_subSchema.properties) { + return; + } + const subSchemaFirstKey = Object.keys(_subSchema.properties)[0]; + uiSchema[stepKey] = { + ...uiSchema[stepKey], + [subSchemaFirstKey]: { + ...uiSchema[stepKey]?.[subSchemaFirstKey], + 'ui:autofocus': true, + }, + }; + } +}; + +const generateUiSchema = ( + schema: JSONSchema7, + isMultiStep: boolean, + readonlyData?: JsonObject, +): UiSchema => { + const uiSchema = extractUiSchema(schema); + if (readonlyData) { + addReadonly(readonlyData, uiSchema, isMultiStep); + } + addFocusOnFirstElement(schema, uiSchema, isMultiStep); + return uiSchema; +}; + +export default generateUiSchema; diff --git a/plugins/orchestrator-form-react/src/utils/useValidator.ts b/plugins/orchestrator-form-react/src/utils/useValidator.ts new file mode 100644 index 0000000000..1abc52e39c --- /dev/null +++ b/plugins/orchestrator-form-react/src/utils/useValidator.ts @@ -0,0 +1,87 @@ +import { JsonObject } from '@backstage/types'; + +import { + createErrorHandler, + CustomValidator, + ErrorSchema, + RJSFValidationError, + unwrapErrorHandler, + ValidationData, + validationDataMerge, + ValidatorType, +} from '@rjsf/utils'; +import validatorAjv from '@rjsf/validator-ajv8'; +import _validator from '@rjsf/validator-ajv8/lib/validator'; +import type { JSONSchema7 } from 'json-schema'; + +import { useStepperContext } from './StepperContext'; + +// add the activeStep to the validator to force rjsf form to rerender when activeStep changes. This doesn't happen because it assumes function are equal. +// see https://github.com/rjsf-team/react-jsonschema-form/blob/v5.18.5/packages/utils/src/deepEquals.ts#L12 + +export type ValidatorTypeForceRender = ValidatorType< + JsonObject, + JSONSchema7 +> & { + activeStep: number; +}; + +const useValidator = (isMultiStepSchema: boolean) => { + const { activeStep } = useStepperContext(); + const validator: ValidatorTypeForceRender = { + activeStep, + validateFormData: ( + formData: JsonObject, + _schema: JSONSchema7, + customValidate: CustomValidator, + ): ValidationData => { + let validationData = validatorAjv.validateFormData(formData, _schema); + + if (customValidate) { + const errorHandler = customValidate( + formData, + createErrorHandler(formData), + ); + const userErrorSchema = unwrapErrorHandler(errorHandler); + validationData = validationDataMerge( + validationData, + userErrorSchema, + ); + } + + if (!isMultiStepSchema) { + return validationData; + } + + const activeKey = Object.keys(_schema.properties || {})[activeStep]; + return { + errors: validationData.errors.filter(err => + err.property?.startsWith(`.${activeKey}.`), + ), + errorSchema: validationData.errorSchema[activeKey] || {}, + }; + }, + + toErrorList: ( + errorSchema?: ErrorSchema, + fieldPath?: string[], + ): RJSFValidationError[] => { + return validatorAjv.toErrorList(errorSchema, fieldPath); + }, + + isValid: ( + _schema: JSONSchema7, + formData: JsonObject | undefined, + rootSchema: JSONSchema7, + ) => { + return validatorAjv.isValid(_schema, formData, rootSchema); + }, + + rawValidation: (_schema: JSONSchema7, formData?: JsonObject) => + validatorAjv.rawValidation(_schema, formData), + }; + + return validator; +}; + +export default useValidator; diff --git a/plugins/orchestrator-swf-editor-envelope/.eslintignore b/plugins/orchestrator-swf-editor-envelope/.eslintignore deleted file mode 100644 index 55289f4a23..0000000000 --- a/plugins/orchestrator-swf-editor-envelope/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -dist-dynamic -dist-scalprum diff --git a/plugins/orchestrator-swf-editor-envelope/.lintstagedrc.json b/plugins/orchestrator-swf-editor-envelope/.lintstagedrc.json deleted file mode 100644 index 14b2263def..0000000000 --- a/plugins/orchestrator-swf-editor-envelope/.lintstagedrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "*": "prettier --ignore-unknown --write", - "*.{js,jsx,ts,tsx,mjs,cjs}": "backstage-cli package lint --fix" -} diff --git a/plugins/orchestrator-swf-editor-envelope/.prettierignore b/plugins/orchestrator-swf-editor-envelope/.prettierignore deleted file mode 100644 index fc8357d99e..0000000000 --- a/plugins/orchestrator-swf-editor-envelope/.prettierignore +++ /dev/null @@ -1,12 +0,0 @@ -dist -dist-types -coverage -.vscode -CHANGELOG.md -generated -templates -*.hbs -renovate.json -dist-dynamic -dist-scalprum -playwright-report diff --git a/plugins/orchestrator-swf-editor-envelope/.prettierrc.js b/plugins/orchestrator-swf-editor-envelope/.prettierrc.js deleted file mode 100644 index 84cbac65b5..0000000000 --- a/plugins/orchestrator-swf-editor-envelope/.prettierrc.js +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-check - -/** @type {import("@ianvs/prettier-plugin-sort-imports").PrettierConfig} */ -module.exports = { - ...require('@spotify/prettier-config'), - plugins: ['@ianvs/prettier-plugin-sort-imports'], - importOrder: [ - '^react(.*)$', - '', - '^@backstage/(.*)$', - '', - '', - '', - '^@janus-idp/(.*)$', - '', - '', - '', - '^[.]', - ], -}; diff --git a/plugins/orchestrator-swf-editor-envelope/package.json b/plugins/orchestrator-swf-editor-envelope/package.json index 43f26b5b9a..ef7ad0c5e3 100644 --- a/plugins/orchestrator-swf-editor-envelope/package.json +++ b/plugins/orchestrator-swf-editor-envelope/package.json @@ -25,14 +25,10 @@ "build": "scripts/build", "postbuild": "scripts/postbuild copy", "clean": "backstage-cli package clean", - "lint:check": "backstage-cli package lint", - "lint:fix": "backstage-cli package lint --fix", + "lint": "backstage-cli package lint", "postpack": "backstage-cli package postpack", "prepack": "backstage-cli package prepack", - "test": "backstage-cli package test --passWithNoTests --coverage", - "tsc": "tsc", - "prettier:check": "prettier --ignore-unknown --check .", - "prettier:fix": "prettier --ignore-unknown --write ." + "test": "backstage-cli package test --passWithNoTests --coverage" }, "dependencies": { "@kie-tools-core/editor": "^0.32.0", @@ -43,7 +39,6 @@ "@kie-tools/serverless-workflow-text-editor": "^0.32.0" }, "devDependencies": { - "prettier": "3.3.3", "@backstage/cli": "0.26.11", "clean-webpack-plugin": "4.0.0", "css-loader": "6.11.0", diff --git a/plugins/orchestrator/.eslintignore b/plugins/orchestrator/.eslintignore deleted file mode 100644 index 55289f4a23..0000000000 --- a/plugins/orchestrator/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -dist-dynamic -dist-scalprum diff --git a/plugins/orchestrator/.lintstagedrc.json b/plugins/orchestrator/.lintstagedrc.json deleted file mode 100644 index 14b2263def..0000000000 --- a/plugins/orchestrator/.lintstagedrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "*": "prettier --ignore-unknown --write", - "*.{js,jsx,ts,tsx,mjs,cjs}": "backstage-cli package lint --fix" -} diff --git a/plugins/orchestrator/.prettierignore b/plugins/orchestrator/.prettierignore deleted file mode 100644 index fc8357d99e..0000000000 --- a/plugins/orchestrator/.prettierignore +++ /dev/null @@ -1,12 +0,0 @@ -dist -dist-types -coverage -.vscode -CHANGELOG.md -generated -templates -*.hbs -renovate.json -dist-dynamic -dist-scalprum -playwright-report diff --git a/plugins/orchestrator/.prettierrc.js b/plugins/orchestrator/.prettierrc.js deleted file mode 100644 index 84cbac65b5..0000000000 --- a/plugins/orchestrator/.prettierrc.js +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-check - -/** @type {import("@ianvs/prettier-plugin-sort-imports").PrettierConfig} */ -module.exports = { - ...require('@spotify/prettier-config'), - plugins: ['@ianvs/prettier-plugin-sort-imports'], - importOrder: [ - '^react(.*)$', - '', - '^@backstage/(.*)$', - '', - '', - '', - '^@janus-idp/(.*)$', - '', - '', - '', - '^[.]', - ], -}; diff --git a/plugins/orchestrator/CHANGELOG.md b/plugins/orchestrator/CHANGELOG.md index 54fba7fdf8..13fce5376c 100644 --- a/plugins/orchestrator/CHANGELOG.md +++ b/plugins/orchestrator/CHANGELOG.md @@ -1,813 +1,797 @@ ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.16.100 -* **@janus-idp/backstage-plugin-orchestrator-form-api:** upgraded to 1.0.100 -* **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.0.100 +## 2.3.2 + +### Patch Changes + +- 76674da: Fixes issue when WorkflowResult panel fails on malformed provided result. + +## 2.3.1 + +### Patch Changes + +- 0e6bfd3: feat: update Backstage to the latest version + + Update to Backstage 1.32.5 + +- Updated dependencies [0e6bfd3] +- Updated dependencies [67f466a] + - @janus-idp/backstage-plugin-orchestrator-form-react@1.4.1 + - @janus-idp/backstage-plugin-orchestrator-form-api@1.4.1 + - @janus-idp/backstage-plugin-orchestrator-common@1.23.1 + +## 2.3.0 + +### Minor Changes + +- 8244f28: chore(deps): update to backstage 1.32 + +### Patch Changes + +- Updated dependencies [8244f28] + - @janus-idp/backstage-plugin-orchestrator-form-react@1.4.0 + - @janus-idp/backstage-plugin-orchestrator-form-api@1.4.0 + - @janus-idp/backstage-plugin-orchestrator-common@1.23.0 + +## 2.2.1 + +### Patch Changes + +- 7342e9b: chore: remove @janus-idp/cli dep and relink local packages + + This update removes `@janus-idp/cli` from all plugins, as it’s no longer necessary. Additionally, packages are now correctly linked with a specified version. + +- Updated dependencies [7342e9b] + - @janus-idp/backstage-plugin-orchestrator-form-react@1.3.1 + +## 2.2.0 + +### Minor Changes + +- d9551ae: feat(deps): update to backstage 1.31 + +### Patch Changes + +- d9551ae: Change local package references to a `*` +- d9551ae: pin the @janus-idp/cli package +- d9551ae: upgrade to yarn v3 +- Updated dependencies [d9551ae] +- Updated dependencies [d9551ae] +- Updated dependencies [d9551ae] +- Updated dependencies [d9551ae] + - @janus-idp/backstage-plugin-orchestrator-form-react@1.3.0 + - @janus-idp/backstage-plugin-orchestrator-common@1.22.0 + - @janus-idp/backstage-plugin-orchestrator-form-api@1.3.0 + +* **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.2.1 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.0.4 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.21.0 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.2.0 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.1 -* **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.0.3 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.21.0 +- **@janus-idp/backstage-plugin-orchestrator-form-api:** upgraded to 1.2.0 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.2.0 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.0 -* **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.0.2 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.20.0 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.1.6 +- **@janus-idp/cli:** upgraded to 1.15.2 ### Dependencies -* **@janus-idp/cli:** upgraded to 1.13.1 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.19.0 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.1.5 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-form-api:** upgraded to 1.0.1 -* **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.0.1 +- **@janus-idp/cli:** upgraded to 1.15.1 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.14.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.18.2 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.1.4 -## @janus-idp/backstage-plugin-orchestrator [1.18.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.18.0...@janus-idp/backstage-plugin-orchestrator@1.18.1) (2024-08-02) +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.18.1 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.1.3 +### Dependencies + +- **@janus-idp/cli:** upgraded to 1.15.0 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.13.1 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.18.0 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.1.2 -## @janus-idp/backstage-plugin-orchestrator [1.18.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.17.0...@janus-idp/backstage-plugin-orchestrator@1.18.0) (2024-07-26) +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.17.3 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.1.1 -### Features +### Dependencies -* **deps:** update to backstage 1.29 ([#1900](https://github.com/janus-idp/backstage-plugins/issues/1900)) ([f53677f](https://github.com/janus-idp/backstage-plugins/commit/f53677fb02d6df43a9de98c43a9f101a6db76802)) -* **orchestrator:** use v2 endpoints to retrieve instances ([#1956](https://github.com/janus-idp/backstage-plugins/issues/1956)) ([537502b](https://github.com/janus-idp/backstage-plugins/commit/537502b9d2ac13f2fb3f79188422d2c6e97f41fb)) +- **@janus-idp/backstage-plugin-orchestrator-form-api:** upgraded to 1.1.0 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.1.0 +- **@janus-idp/cli:** upgraded to 1.14.0 +### Dependencies +- **@janus-idp/cli:** upgraded to 1.13.2 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.13.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.17.2 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.0.10 -## @janus-idp/backstage-plugin-orchestrator [1.17.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.16.1...@janus-idp/backstage-plugin-orchestrator@1.17.0) (2024-07-24) +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.17.1 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.0.9 -### Features +### Dependencies -* **deps:** update to backstage 1.28 ([#1891](https://github.com/janus-idp/backstage-plugins/issues/1891)) ([1ba1108](https://github.com/janus-idp/backstage-plugins/commit/1ba11088e0de60e90d138944267b83600dc446e5)) -* **orchestrator:** use v2 endpoints to retrieve workflow overviews ([#1892](https://github.com/janus-idp/backstage-plugins/issues/1892)) ([cca1e53](https://github.com/janus-idp/backstage-plugins/commit/cca1e53bc6b3019b1c544f2f62bed8723ebf6130)) +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.17.0 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.0.8 +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.16.0 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.0.6 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.12.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.2 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.0.5 -## @janus-idp/backstage-plugin-orchestrator [1.16.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.16.0...@janus-idp/backstage-plugin-orchestrator@1.16.1) (2024-07-11) +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.0.4 -### Bug Fixes +### Dependencies -* **orchestrator:** returned scrolling bars to instance page cards ([#1880](https://github.com/janus-idp/backstage-plugins/issues/1880)) ([08545da](https://github.com/janus-idp/backstage-plugins/commit/08545daabd02a7ba6f9f12dedf237afbff1cd67a)) +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.1 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.0.3 -## @janus-idp/backstage-plugin-orchestrator [1.16.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.15.0...@janus-idp/backstage-plugin-orchestrator@1.16.0) (2024-06-28) +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.15.0 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.0.2 -### Features +### Dependencies -* **orchestrator:** remove unneeded orchestrator jira integration and endpoint ([#1833](https://github.com/janus-idp/backstage-plugins/issues/1833)) ([d2a76fd](https://github.com/janus-idp/backstage-plugins/commit/d2a76fd3db028f9774c821759bee5f38b7131c94)) +- **@janus-idp/cli:** upgraded to 1.13.1 +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-form-api:** upgraded to 1.0.1 +- **@janus-idp/backstage-plugin-orchestrator-form-react:** upgraded to 1.0.1 ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.10.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.14.0 -## @janus-idp/backstage-plugin-orchestrator [1.15.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.14.1...@janus-idp/backstage-plugin-orchestrator@1.15.0) (2024-06-26) +## @janus-idp/backstage-plugin-orchestrator [1.18.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.18.0...@janus-idp/backstage-plugin-orchestrator@1.18.1) (2024-08-02) +### Dependencies -### Features +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.13.1 -* **orchestrator:** disable buttons based on permissions ([#1818](https://github.com/janus-idp/backstage-plugins/issues/1818)) ([36504b0](https://github.com/janus-idp/backstage-plugins/commit/36504b05d96dbbf0b2395dc6e5c155c21fa73bcd)) +## @janus-idp/backstage-plugin-orchestrator [1.18.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.17.0...@janus-idp/backstage-plugin-orchestrator@1.18.0) (2024-07-26) -## @janus-idp/backstage-plugin-orchestrator [1.14.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.14.0...@janus-idp/backstage-plugin-orchestrator@1.14.1) (2024-06-19) +### Features +- **deps:** update to backstage 1.29 ([#1900](https://github.com/janus-idp/backstage-plugins/issues/1900)) ([f53677f](https://github.com/janus-idp/backstage-plugins/commit/f53677fb02d6df43a9de98c43a9f101a6db76802)) +- **orchestrator:** use v2 endpoints to retrieve instances ([#1956](https://github.com/janus-idp/backstage-plugins/issues/1956)) ([537502b](https://github.com/janus-idp/backstage-plugins/commit/537502b9d2ac13f2fb3f79188422d2c6e97f41fb)) -### Bug Fixes +### Dependencies -* **matomo:** add default export for new backend system ([#1822](https://github.com/janus-idp/backstage-plugins/issues/1822)) ([5e72920](https://github.com/janus-idp/backstage-plugins/commit/5e72920209589535d503bb28e77f54175a0bd946)) +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.13.0 +## @janus-idp/backstage-plugin-orchestrator [1.17.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.16.1...@janus-idp/backstage-plugin-orchestrator@1.17.0) (2024-07-24) + +### Features +- **deps:** update to backstage 1.28 ([#1891](https://github.com/janus-idp/backstage-plugins/issues/1891)) ([1ba1108](https://github.com/janus-idp/backstage-plugins/commit/1ba11088e0de60e90d138944267b83600dc446e5)) +- **orchestrator:** use v2 endpoints to retrieve workflow overviews ([#1892](https://github.com/janus-idp/backstage-plugins/issues/1892)) ([cca1e53](https://github.com/janus-idp/backstage-plugins/commit/cca1e53bc6b3019b1c544f2f62bed8723ebf6130)) ### Dependencies -* **@janus-idp/cli:** upgraded to 1.11.1 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.12.0 -## @janus-idp/backstage-plugin-orchestrator [1.14.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.13.7...@janus-idp/backstage-plugin-orchestrator@1.14.0) (2024-06-13) +## @janus-idp/backstage-plugin-orchestrator [1.16.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.16.0...@janus-idp/backstage-plugin-orchestrator@1.16.1) (2024-07-11) +### Bug Fixes -### Features +- **orchestrator:** returned scrolling bars to instance page cards ([#1880](https://github.com/janus-idp/backstage-plugins/issues/1880)) ([08545da](https://github.com/janus-idp/backstage-plugins/commit/08545daabd02a7ba6f9f12dedf237afbff1cd67a)) -* **deps:** update to backstage 1.27 ([#1683](https://github.com/janus-idp/backstage-plugins/issues/1683)) ([a14869c](https://github.com/janus-idp/backstage-plugins/commit/a14869c3f4177049cb8d6552b36c3ffd17e7997d)) +## @janus-idp/backstage-plugin-orchestrator [1.16.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.15.0...@janus-idp/backstage-plugin-orchestrator@1.16.0) (2024-06-28) +### Features +- **orchestrator:** remove unneeded orchestrator jira integration and endpoint ([#1833](https://github.com/janus-idp/backstage-plugins/issues/1833)) ([d2a76fd](https://github.com/janus-idp/backstage-plugins/commit/d2a76fd3db028f9774c821759bee5f38b7131c94)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.9.0 -* **@janus-idp/cli:** upgraded to 1.11.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.10.0 -## @janus-idp/backstage-plugin-orchestrator [1.13.7](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.13.6...@janus-idp/backstage-plugin-orchestrator@1.13.7) (2024-06-13) +## @janus-idp/backstage-plugin-orchestrator [1.15.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.14.1...@janus-idp/backstage-plugin-orchestrator@1.15.0) (2024-06-26) + +### Features + +- **orchestrator:** disable buttons based on permissions ([#1818](https://github.com/janus-idp/backstage-plugins/issues/1818)) ([36504b0](https://github.com/janus-idp/backstage-plugins/commit/36504b05d96dbbf0b2395dc6e5c155c21fa73bcd)) +## @janus-idp/backstage-plugin-orchestrator [1.14.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.14.0...@janus-idp/backstage-plugin-orchestrator@1.14.1) (2024-06-19) + +### Bug Fixes +- **matomo:** add default export for new backend system ([#1822](https://github.com/janus-idp/backstage-plugins/issues/1822)) ([5e72920](https://github.com/janus-idp/backstage-plugins/commit/5e72920209589535d503bb28e77f54175a0bd946)) ### Dependencies -* **@janus-idp/cli:** upgraded to 1.10.1 +- **@janus-idp/cli:** upgraded to 1.11.1 -## @janus-idp/backstage-plugin-orchestrator [1.13.6](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.13.5...@janus-idp/backstage-plugin-orchestrator@1.13.6) (2024-06-05) +## @janus-idp/backstage-plugin-orchestrator [1.14.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.13.7...@janus-idp/backstage-plugin-orchestrator@1.14.0) (2024-06-13) +### Features +- **deps:** update to backstage 1.27 ([#1683](https://github.com/janus-idp/backstage-plugins/issues/1683)) ([a14869c](https://github.com/janus-idp/backstage-plugins/commit/a14869c3f4177049cb8d6552b36c3ffd17e7997d)) ### Dependencies -* **@janus-idp/cli:** upgraded to 1.10.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.9.0 +- **@janus-idp/cli:** upgraded to 1.11.0 -## @janus-idp/backstage-plugin-orchestrator [1.13.5](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.13.4...@janus-idp/backstage-plugin-orchestrator@1.13.5) (2024-06-04) +## @janus-idp/backstage-plugin-orchestrator [1.13.7](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.13.6...@janus-idp/backstage-plugin-orchestrator@1.13.7) (2024-06-13) + +### Dependencies +- **@janus-idp/cli:** upgraded to 1.10.1 +## @janus-idp/backstage-plugin-orchestrator [1.13.6](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.13.5...@janus-idp/backstage-plugin-orchestrator@1.13.6) (2024-06-05) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.8.1 +- **@janus-idp/cli:** upgraded to 1.10.0 -## @janus-idp/backstage-plugin-orchestrator [1.13.4](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.13.3...@janus-idp/backstage-plugin-orchestrator@1.13.4) (2024-06-03) +## @janus-idp/backstage-plugin-orchestrator [1.13.5](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.13.4...@janus-idp/backstage-plugin-orchestrator@1.13.5) (2024-06-04) +### Dependencies +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.8.1 + +## @janus-idp/backstage-plugin-orchestrator [1.13.4](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.13.3...@janus-idp/backstage-plugin-orchestrator@1.13.4) (2024-06-03) ### Dependencies -* **@janus-idp/cli:** upgraded to 1.9.0 +- **@janus-idp/cli:** upgraded to 1.9.0 ## @janus-idp/backstage-plugin-orchestrator [1.13.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.13.2...@janus-idp/backstage-plugin-orchestrator@1.13.3) (2024-05-31) ## @janus-idp/backstage-plugin-orchestrator [1.13.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.13.1...@janus-idp/backstage-plugin-orchestrator@1.13.2) (2024-05-29) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.10 +- **@janus-idp/cli:** upgraded to 1.8.10 ## @janus-idp/backstage-plugin-orchestrator [1.13.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.13.0...@janus-idp/backstage-plugin-orchestrator@1.13.1) (2024-05-29) - ### Bug Fixes -* **orchestrator:** upgrade to mui v5 ([#1727](https://github.com/janus-idp/backstage-plugins/issues/1727)) ([8b935dc](https://github.com/janus-idp/backstage-plugins/commit/8b935dc3c85fbe4030564301820d946effa78426)) - - +- **orchestrator:** upgrade to mui v5 ([#1727](https://github.com/janus-idp/backstage-plugins/issues/1727)) ([8b935dc](https://github.com/janus-idp/backstage-plugins/commit/8b935dc3c85fbe4030564301820d946effa78426)) ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.9 +- **@janus-idp/cli:** upgraded to 1.8.9 ## @janus-idp/backstage-plugin-orchestrator [1.13.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.12.6...@janus-idp/backstage-plugin-orchestrator@1.13.0) (2024-05-28) - ### Features -* **orchestrator:** add permissions to orchestrator plugin ([#1599](https://github.com/janus-idp/backstage-plugins/issues/1599)) ([d0a4531](https://github.com/janus-idp/backstage-plugins/commit/d0a453181e177eb1da7b1e231253b76a2d9356a8)) -* **orchestrator:** label a Workflow assessment result as recommended ([#1705](https://github.com/janus-idp/backstage-plugins/issues/1705)) ([7e24e86](https://github.com/janus-idp/backstage-plugins/commit/7e24e86eb3094fa00b22aa77f79fb0e04dbf86f7)) - +- **orchestrator:** add permissions to orchestrator plugin ([#1599](https://github.com/janus-idp/backstage-plugins/issues/1599)) ([d0a4531](https://github.com/janus-idp/backstage-plugins/commit/d0a453181e177eb1da7b1e231253b76a2d9356a8)) +- **orchestrator:** label a Workflow assessment result as recommended ([#1705](https://github.com/janus-idp/backstage-plugins/issues/1705)) ([7e24e86](https://github.com/janus-idp/backstage-plugins/commit/7e24e86eb3094fa00b22aa77f79fb0e04dbf86f7)) ### Bug Fixes -* **deps:** update dependency monaco-editor to ^0.49.0 ([#1690](https://github.com/janus-idp/backstage-plugins/issues/1690)) ([34308a3](https://github.com/janus-idp/backstage-plugins/commit/34308a3ba669666ab2ddd61b2ac0073edd98f8ce)) -* **orchestrator:** bump `rjsf` dependencies ([#1715](https://github.com/janus-idp/backstage-plugins/issues/1715)) ([ea31cdb](https://github.com/janus-idp/backstage-plugins/commit/ea31cdbd7cb0a8842119f6d5d5dbd689e31040aa)) -* **orchestrator:** fix the common package reference version ([#1704](https://github.com/janus-idp/backstage-plugins/issues/1704)) ([942b2a3](https://github.com/janus-idp/backstage-plugins/commit/942b2a3b6eb29c0fe88f9c98dea581309d02fded)) +- **deps:** update dependency monaco-editor to ^0.49.0 ([#1690](https://github.com/janus-idp/backstage-plugins/issues/1690)) ([34308a3](https://github.com/janus-idp/backstage-plugins/commit/34308a3ba669666ab2ddd61b2ac0073edd98f8ce)) +- **orchestrator:** bump `rjsf` dependencies ([#1715](https://github.com/janus-idp/backstage-plugins/issues/1715)) ([ea31cdb](https://github.com/janus-idp/backstage-plugins/commit/ea31cdbd7cb0a8842119f6d5d5dbd689e31040aa)) +- **orchestrator:** fix the common package reference version ([#1704](https://github.com/janus-idp/backstage-plugins/issues/1704)) ([942b2a3](https://github.com/janus-idp/backstage-plugins/commit/942b2a3b6eb29c0fe88f9c98dea581309d02fded)) ## @janus-idp/backstage-plugin-orchestrator [1.12.6](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.12.5...@janus-idp/backstage-plugin-orchestrator@1.12.6) (2024-05-21) ## @janus-idp/backstage-plugin-orchestrator [1.12.5](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.12.4...@janus-idp/backstage-plugin-orchestrator@1.12.5) (2024-05-20) - ### Bug Fixes -* **orchestrator:** fixes many security-related issues ([#1681](https://github.com/janus-idp/backstage-plugins/issues/1681)) ([3e801c8](https://github.com/janus-idp/backstage-plugins/commit/3e801c84015f925bdecd226a161ef81a5fc69432)) +- **orchestrator:** fixes many security-related issues ([#1681](https://github.com/janus-idp/backstage-plugins/issues/1681)) ([3e801c8](https://github.com/janus-idp/backstage-plugins/commit/3e801c84015f925bdecd226a161ef81a5fc69432)) ## @janus-idp/backstage-plugin-orchestrator [1.12.4](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.12.3...@janus-idp/backstage-plugin-orchestrator@1.12.4) (2024-05-16) - ### Bug Fixes -* **orchestrator:** remove the need of react dev dependencies ([#1650](https://github.com/janus-idp/backstage-plugins/issues/1650)) ([5e60875](https://github.com/janus-idp/backstage-plugins/commit/5e60875932b906fd40e282d53b277a0f29efc67f)) +- **orchestrator:** remove the need of react dev dependencies ([#1650](https://github.com/janus-idp/backstage-plugins/issues/1650)) ([5e60875](https://github.com/janus-idp/backstage-plugins/commit/5e60875932b906fd40e282d53b277a0f29efc67f)) ## @janus-idp/backstage-plugin-orchestrator [1.12.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.12.2...@janus-idp/backstage-plugin-orchestrator@1.12.3) (2024-05-16) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.7 +- **@janus-idp/cli:** upgraded to 1.8.7 ## @janus-idp/backstage-plugin-orchestrator [1.12.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.12.1...@janus-idp/backstage-plugin-orchestrator@1.12.2) (2024-05-15) - ### Documentation -* **orchestrator:** removes instructions related to the editor ([#1664](https://github.com/janus-idp/backstage-plugins/issues/1664)) ([10a75b2](https://github.com/janus-idp/backstage-plugins/commit/10a75b2706c72751bd774d6fae4332bbc527dc2b)) - - +- **orchestrator:** removes instructions related to the editor ([#1664](https://github.com/janus-idp/backstage-plugins/issues/1664)) ([10a75b2](https://github.com/janus-idp/backstage-plugins/commit/10a75b2706c72751bd774d6fae4332bbc527dc2b)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.7.2 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.7.2 ## @janus-idp/backstage-plugin-orchestrator [1.12.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.12.0...@janus-idp/backstage-plugin-orchestrator@1.12.1) (2024-05-15) - ### Bug Fixes -* **orchestrator:** export the `OrchestratorPlugin` accordingly ([#1644](https://github.com/janus-idp/backstage-plugins/issues/1644)) ([4a9d1f8](https://github.com/janus-idp/backstage-plugins/commit/4a9d1f821a30437e73631fac98b1aabc65473fba)) - +- **orchestrator:** export the `OrchestratorPlugin` accordingly ([#1644](https://github.com/janus-idp/backstage-plugins/issues/1644)) ([4a9d1f8](https://github.com/janus-idp/backstage-plugins/commit/4a9d1f821a30437e73631fac98b1aabc65473fba)) ### Other changes -* **orchestrator:** add OrchestratorClient unit tests ([#1640](https://github.com/janus-idp/backstage-plugins/issues/1640)) ([2a2dc55](https://github.com/janus-idp/backstage-plugins/commit/2a2dc5581aa04b20bdf973ecb8310d179d6fd1a5)) +- **orchestrator:** add OrchestratorClient unit tests ([#1640](https://github.com/janus-idp/backstage-plugins/issues/1640)) ([2a2dc55](https://github.com/janus-idp/backstage-plugins/commit/2a2dc5581aa04b20bdf973ecb8310d179d6fd1a5)) ## @janus-idp/backstage-plugin-orchestrator [1.12.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.11.2...@janus-idp/backstage-plugin-orchestrator@1.12.0) (2024-05-14) - ### Features -* **deps:** use RHDH themes in the backstage app and dev pages ([#1480](https://github.com/janus-idp/backstage-plugins/issues/1480)) ([8263bf0](https://github.com/janus-idp/backstage-plugins/commit/8263bf099736cbb0d0f2316082d338ba81fa6927)) +- **deps:** use RHDH themes in the backstage app and dev pages ([#1480](https://github.com/janus-idp/backstage-plugins/issues/1480)) ([8263bf0](https://github.com/janus-idp/backstage-plugins/commit/8263bf099736cbb0d0f2316082d338ba81fa6927)) ## @janus-idp/backstage-plugin-orchestrator [1.11.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.11.1...@janus-idp/backstage-plugin-orchestrator@1.11.2) (2024-05-13) - ### Bug Fixes -* **orchestrator:** typos mentioning OpenShift ([#1639](https://github.com/janus-idp/backstage-plugins/issues/1639)) ([7ff4c75](https://github.com/janus-idp/backstage-plugins/commit/7ff4c754f73681e1a596d56721972af8872f3211)) +- **orchestrator:** typos mentioning OpenShift ([#1639](https://github.com/janus-idp/backstage-plugins/issues/1639)) ([7ff4c75](https://github.com/janus-idp/backstage-plugins/commit/7ff4c754f73681e1a596d56721972af8872f3211)) ## @janus-idp/backstage-plugin-orchestrator [1.11.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.11.0...@janus-idp/backstage-plugin-orchestrator@1.11.1) (2024-05-09) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.7.1 -* **@janus-idp/cli:** upgraded to 1.8.6 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.7.1 +- **@janus-idp/cli:** upgraded to 1.8.6 ## @janus-idp/backstage-plugin-orchestrator [1.11.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.10.6...@janus-idp/backstage-plugin-orchestrator@1.11.0) (2024-05-09) - ### Features -* **orchestrator:** add ability to re-trigger workflow in error state ([#1624](https://github.com/janus-idp/backstage-plugins/issues/1624)) ([8709a37](https://github.com/janus-idp/backstage-plugins/commit/8709a37d08c2eafc22f10bd2a41f0a105768222d)) - - +- **orchestrator:** add ability to re-trigger workflow in error state ([#1624](https://github.com/janus-idp/backstage-plugins/issues/1624)) ([8709a37](https://github.com/janus-idp/backstage-plugins/commit/8709a37d08c2eafc22f10bd2a41f0a105768222d)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.7.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.7.0 ## @janus-idp/backstage-plugin-orchestrator [1.10.6](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.10.5...@janus-idp/backstage-plugin-orchestrator@1.10.6) (2024-05-06) - ### Bug Fixes -* **orchestrator:** disabled MUI table thirdSortClick ([#1614](https://github.com/janus-idp/backstage-plugins/issues/1614)) ([5e541bd](https://github.com/janus-idp/backstage-plugins/commit/5e541bd217500c83bd8d9eb94cf060805ef4b8a9)) +- **orchestrator:** disabled MUI table thirdSortClick ([#1614](https://github.com/janus-idp/backstage-plugins/issues/1614)) ([5e541bd](https://github.com/janus-idp/backstage-plugins/commit/5e541bd217500c83bd8d9eb94cf060805ef4b8a9)) ## @janus-idp/backstage-plugin-orchestrator [1.10.5](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.10.4...@janus-idp/backstage-plugin-orchestrator@1.10.5) (2024-05-02) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.5 +- **@janus-idp/cli:** upgraded to 1.8.5 ## @janus-idp/backstage-plugin-orchestrator [1.10.4](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.10.3...@janus-idp/backstage-plugin-orchestrator@1.10.4) (2024-05-02) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.4 +- **@janus-idp/cli:** upgraded to 1.8.4 ## @janus-idp/backstage-plugin-orchestrator [1.10.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.10.2...@janus-idp/backstage-plugin-orchestrator@1.10.3) (2024-05-02) - ### Bug Fixes -* **orchestrator:** disable sorting ID column in workflow runs table ([#1595](https://github.com/janus-idp/backstage-plugins/issues/1595)) ([4d4875e](https://github.com/janus-idp/backstage-plugins/commit/4d4875eb4f91a3a3464b1ecbdcf647e9f1b84be5)) +- **orchestrator:** disable sorting ID column in workflow runs table ([#1595](https://github.com/janus-idp/backstage-plugins/issues/1595)) ([4d4875e](https://github.com/janus-idp/backstage-plugins/commit/4d4875eb4f91a3a3464b1ecbdcf647e9f1b84be5)) ## @janus-idp/backstage-plugin-orchestrator [1.10.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.10.1...@janus-idp/backstage-plugin-orchestrator@1.10.2) (2024-04-30) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.3 +- **@janus-idp/cli:** upgraded to 1.8.3 ## @janus-idp/backstage-plugin-orchestrator [1.10.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.10.0...@janus-idp/backstage-plugin-orchestrator@1.10.1) (2024-04-30) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.2 +- **@janus-idp/cli:** upgraded to 1.8.2 ## @janus-idp/backstage-plugin-orchestrator [1.10.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.9.4...@janus-idp/backstage-plugin-orchestrator@1.10.0) (2024-04-25) - ### Features -* **orchestrator:** add endpoint to retrigger workflow in error state ([#1343](https://github.com/janus-idp/backstage-plugins/issues/1343)) ([328d23a](https://github.com/janus-idp/backstage-plugins/commit/328d23a7992da125becc8d7775a4ebd68165f243)) - - +- **orchestrator:** add endpoint to retrigger workflow in error state ([#1343](https://github.com/janus-idp/backstage-plugins/issues/1343)) ([328d23a](https://github.com/janus-idp/backstage-plugins/commit/328d23a7992da125becc8d7775a4ebd68165f243)) ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.1 +- **@janus-idp/cli:** upgraded to 1.8.1 ## @janus-idp/backstage-plugin-orchestrator [1.9.4](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.9.3...@janus-idp/backstage-plugin-orchestrator@1.9.4) (2024-04-18) - ### Bug Fixes -* **orchestrator:** allows serving the editor envelope in disconnected environments ([#1450](https://github.com/janus-idp/backstage-plugins/issues/1450)) ([1e778d8](https://github.com/janus-idp/backstage-plugins/commit/1e778d88336dfec79d48ece4fd8d2a035133b70e)) - +- **orchestrator:** allows serving the editor envelope in disconnected environments ([#1450](https://github.com/janus-idp/backstage-plugins/issues/1450)) ([1e778d8](https://github.com/janus-idp/backstage-plugins/commit/1e778d88336dfec79d48ece4fd8d2a035133b70e)) ### Documentation -* **orchestrator:** fix quick start urls to private repo and make image urls raw ([#1521](https://github.com/janus-idp/backstage-plugins/issues/1521)) ([eefd264](https://github.com/janus-idp/backstage-plugins/commit/eefd2642b0dd3a2d6eb26eaf229c97a280adf07c)) - - +- **orchestrator:** fix quick start urls to private repo and make image urls raw ([#1521](https://github.com/janus-idp/backstage-plugins/issues/1521)) ([eefd264](https://github.com/janus-idp/backstage-plugins/commit/eefd2642b0dd3a2d6eb26eaf229c97a280adf07c)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.4 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.4 ## @janus-idp/backstage-plugin-orchestrator [1.9.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.9.2...@janus-idp/backstage-plugin-orchestrator@1.9.3) (2024-04-16) - ### Bug Fixes -* fix typo in orchestrator documentation ([#1508](https://github.com/janus-idp/backstage-plugins/issues/1508)) ([bfa360a](https://github.com/janus-idp/backstage-plugins/commit/bfa360af97b5daf1902c267cd682e51cb6d71c83)) +- fix typo in orchestrator documentation ([#1508](https://github.com/janus-idp/backstage-plugins/issues/1508)) ([bfa360a](https://github.com/janus-idp/backstage-plugins/commit/bfa360af97b5daf1902c267cd682e51cb6d71c83)) ## @janus-idp/backstage-plugin-orchestrator [1.9.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.9.1...@janus-idp/backstage-plugin-orchestrator@1.9.2) (2024-04-15) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.8.0 +- **@janus-idp/cli:** upgraded to 1.8.0 ## @janus-idp/backstage-plugin-orchestrator [1.9.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.9.0...@janus-idp/backstage-plugin-orchestrator@1.9.1) (2024-04-15) - ### Documentation -* **orchestrator:** add a quickstart for users ([#1499](https://github.com/janus-idp/backstage-plugins/issues/1499)) ([28fe8da](https://github.com/janus-idp/backstage-plugins/commit/28fe8da644350facb4c414f1bd5ff48ba4801b24)) +- **orchestrator:** add a quickstart for users ([#1499](https://github.com/janus-idp/backstage-plugins/issues/1499)) ([28fe8da](https://github.com/janus-idp/backstage-plugins/commit/28fe8da644350facb4c414f1bd5ff48ba4801b24)) ## @janus-idp/backstage-plugin-orchestrator [1.9.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.8.9...@janus-idp/backstage-plugin-orchestrator@1.9.0) (2024-04-10) - ### Features -* **orchestrator:** make workflow last run status as link to the workflow last run details page ([#1488](https://github.com/janus-idp/backstage-plugins/issues/1488)) ([fc2f94e](https://github.com/janus-idp/backstage-plugins/commit/fc2f94ed4ff2cb0795ba3b65eeea57eae3a8640c)) +- **orchestrator:** make workflow last run status as link to the workflow last run details page ([#1488](https://github.com/janus-idp/backstage-plugins/issues/1488)) ([fc2f94e](https://github.com/janus-idp/backstage-plugins/commit/fc2f94ed4ff2cb0795ba3b65eeea57eae3a8640c)) ## @janus-idp/backstage-plugin-orchestrator [1.8.9](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.8.8...@janus-idp/backstage-plugin-orchestrator@1.8.9) (2024-04-09) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.10 +- **@janus-idp/cli:** upgraded to 1.7.10 ## @janus-idp/backstage-plugin-orchestrator [1.8.8](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.8.7...@janus-idp/backstage-plugin-orchestrator@1.8.8) (2024-04-09) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.9 +- **@janus-idp/cli:** upgraded to 1.7.9 ## @janus-idp/backstage-plugin-orchestrator [1.8.7](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.8.6...@janus-idp/backstage-plugin-orchestrator@1.8.7) (2024-04-05) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.3 -* **@janus-idp/cli:** upgraded to 1.7.8 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.3 +- **@janus-idp/cli:** upgraded to 1.7.8 ## @janus-idp/backstage-plugin-orchestrator [1.8.6](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.8.5...@janus-idp/backstage-plugin-orchestrator@1.8.6) (2024-04-04) - ### Documentation -* **orchestrator:** add OpenAPI doc ([#1441](https://github.com/janus-idp/backstage-plugins/issues/1441)) ([f6275e2](https://github.com/janus-idp/backstage-plugins/commit/f6275e2b37f467e65c267f951db8c413a69eb923)) - - +- **orchestrator:** add OpenAPI doc ([#1441](https://github.com/janus-idp/backstage-plugins/issues/1441)) ([f6275e2](https://github.com/janus-idp/backstage-plugins/commit/f6275e2b37f467e65c267f951db8c413a69eb923)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.2 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.2 ## @janus-idp/backstage-plugin-orchestrator [1.8.5](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.8.4...@janus-idp/backstage-plugin-orchestrator@1.8.5) (2024-04-02) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.7 +- **@janus-idp/cli:** upgraded to 1.7.7 ## @janus-idp/backstage-plugin-orchestrator [1.8.4](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.8.3...@janus-idp/backstage-plugin-orchestrator@1.8.4) (2024-03-29) - ### Bug Fixes -* **orchestrator:** fixes v2/instances endpoint ([#1414](https://github.com/janus-idp/backstage-plugins/issues/1414)) ([88b49df](https://github.com/janus-idp/backstage-plugins/commit/88b49df35cf10e231ba69c239e873cb10e7cc25b)) - - +- **orchestrator:** fixes v2/instances endpoint ([#1414](https://github.com/janus-idp/backstage-plugins/issues/1414)) ([88b49df](https://github.com/janus-idp/backstage-plugins/commit/88b49df35cf10e231ba69c239e873cb10e7cc25b)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.1 -* **@janus-idp/cli:** upgraded to 1.7.6 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.1 +- **@janus-idp/cli:** upgraded to 1.7.6 ## @janus-idp/backstage-plugin-orchestrator [1.8.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.8.2...@janus-idp/backstage-plugin-orchestrator@1.8.3) (2024-03-26) - ### Bug Fixes -* **orchestrator:** remove error on Reset workflow ([#1393](https://github.com/janus-idp/backstage-plugins/issues/1393)) ([6ce210d](https://github.com/janus-idp/backstage-plugins/commit/6ce210dfb3ac82a887985057ea234cf8b6065068)) +- **orchestrator:** remove error on Reset workflow ([#1393](https://github.com/janus-idp/backstage-plugins/issues/1393)) ([6ce210d](https://github.com/janus-idp/backstage-plugins/commit/6ce210dfb3ac82a887985057ea234cf8b6065068)) ## @janus-idp/backstage-plugin-orchestrator [1.8.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.8.1...@janus-idp/backstage-plugin-orchestrator@1.8.2) (2024-03-17) - ### Bug Fixes -* **orchestrator:** fix dropdown look ([#1344](https://github.com/janus-idp/backstage-plugins/issues/1344)) ([9284299](https://github.com/janus-idp/backstage-plugins/commit/9284299710f4d498deb098a94a2be57e6d7516a6)) +- **orchestrator:** fix dropdown look ([#1344](https://github.com/janus-idp/backstage-plugins/issues/1344)) ([9284299](https://github.com/janus-idp/backstage-plugins/commit/9284299710f4d498deb098a94a2be57e6d7516a6)) ## @janus-idp/backstage-plugin-orchestrator [1.8.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.8.0...@janus-idp/backstage-plugin-orchestrator@1.8.1) (2024-03-14) - ### Bug Fixes -* **orchestrator:** update the installation instructions ([#1336](https://github.com/janus-idp/backstage-plugins/issues/1336)) ([d77e388](https://github.com/janus-idp/backstage-plugins/commit/d77e3887ee838a0d4ce075ab976203f13f2037c8)) +- **orchestrator:** update the installation instructions ([#1336](https://github.com/janus-idp/backstage-plugins/issues/1336)) ([d77e388](https://github.com/janus-idp/backstage-plugins/commit/d77e3887ee838a0d4ce075ab976203f13f2037c8)) ## @janus-idp/backstage-plugin-orchestrator [1.8.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.7.8...@janus-idp/backstage-plugin-orchestrator@1.8.0) (2024-03-14) - ### Features -* **orchestrator:** verify availability and cache workflow definition IDs ([#1309](https://github.com/janus-idp/backstage-plugins/issues/1309)) ([4d322f1](https://github.com/janus-idp/backstage-plugins/commit/4d322f1fc5b6f8b1afedf40cfe1b24b2edae2ac1)) - - +- **orchestrator:** verify availability and cache workflow definition IDs ([#1309](https://github.com/janus-idp/backstage-plugins/issues/1309)) ([4d322f1](https://github.com/janus-idp/backstage-plugins/commit/4d322f1fc5b6f8b1afedf40cfe1b24b2edae2ac1)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.6.0 ## @janus-idp/backstage-plugin-orchestrator [1.7.8](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.7.7...@janus-idp/backstage-plugin-orchestrator@1.7.8) (2024-03-12) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.5.1 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.5.1 ## @janus-idp/backstage-plugin-orchestrator [1.7.7](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.7.6...@janus-idp/backstage-plugin-orchestrator@1.7.7) (2024-03-11) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.5.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.5.0 ## @janus-idp/backstage-plugin-orchestrator [1.7.6](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.7.5...@janus-idp/backstage-plugin-orchestrator@1.7.6) (2024-03-11) - ### Other changes -* **orchestrator:** add unit tests for v2 endpoints ([#1300](https://github.com/janus-idp/backstage-plugins/issues/1300)) ([9a13138](https://github.com/janus-idp/backstage-plugins/commit/9a13138c61d3cc7331f739da80f020bb68dd61e5)) - - +- **orchestrator:** add unit tests for v2 endpoints ([#1300](https://github.com/janus-idp/backstage-plugins/issues/1300)) ([9a13138](https://github.com/janus-idp/backstage-plugins/commit/9a13138c61d3cc7331f739da80f020bb68dd61e5)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.4.1 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.4.1 ## @janus-idp/backstage-plugin-orchestrator [1.7.5](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.7.4...@janus-idp/backstage-plugin-orchestrator@1.7.5) (2024-03-07) - ### Bug Fixes -* **orchestraotr:** resolved grey background appears in actions column in workflows table ([#1317](https://github.com/janus-idp/backstage-plugins/issues/1317)) ([cd7b4e7](https://github.com/janus-idp/backstage-plugins/commit/cd7b4e7267c804c75b4bccf927b48c32f7943ed6)) +- **orchestraotr:** resolved grey background appears in actions column in workflows table ([#1317](https://github.com/janus-idp/backstage-plugins/issues/1317)) ([cd7b4e7](https://github.com/janus-idp/backstage-plugins/commit/cd7b4e7267c804c75b4bccf927b48c32f7943ed6)) ## @janus-idp/backstage-plugin-orchestrator [1.7.4](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.7.3...@janus-idp/backstage-plugin-orchestrator@1.7.4) (2024-03-07) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.4.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.4.0 ## @janus-idp/backstage-plugin-orchestrator [1.7.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.7.2...@janus-idp/backstage-plugin-orchestrator@1.7.3) (2024-03-07) - ### Bug Fixes -* **orchestrator:** fix abort button and rerun button disable issue ([#1311](https://github.com/janus-idp/backstage-plugins/issues/1311)) ([0c98279](https://github.com/janus-idp/backstage-plugins/commit/0c982798872f2cb1a3b9fef7ab15850474cb03a7)) +- **orchestrator:** fix abort button and rerun button disable issue ([#1311](https://github.com/janus-idp/backstage-plugins/issues/1311)) ([0c98279](https://github.com/janus-idp/backstage-plugins/commit/0c982798872f2cb1a3b9fef7ab15850474cb03a7)) ## @janus-idp/backstage-plugin-orchestrator [1.7.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.7.1...@janus-idp/backstage-plugin-orchestrator@1.7.2) (2024-03-04) - ### Bug Fixes -* **orchestrator:** walk around the state field is empty issue when fetch instance ([#1299](https://github.com/janus-idp/backstage-plugins/issues/1299)) ([e5c33c0](https://github.com/janus-idp/backstage-plugins/commit/e5c33c06fc66a6ff393365282f825c5fdc4713c9)) - - +- **orchestrator:** walk around the state field is empty issue when fetch instance ([#1299](https://github.com/janus-idp/backstage-plugins/issues/1299)) ([e5c33c0](https://github.com/janus-idp/backstage-plugins/commit/e5c33c06fc66a6ff393365282f825c5fdc4713c9)) ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.5 +- **@janus-idp/cli:** upgraded to 1.7.5 ## @janus-idp/backstage-plugin-orchestrator [1.7.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.7.0...@janus-idp/backstage-plugin-orchestrator@1.7.1) (2024-03-03) - ### Bug Fixes -* **orchestrator:** stop fetching workflow URI ([#1297](https://github.com/janus-idp/backstage-plugins/issues/1297)) ([2456a28](https://github.com/janus-idp/backstage-plugins/commit/2456a287dbff955a0916b9600e89a39511cd537a)) - - +- **orchestrator:** stop fetching workflow URI ([#1297](https://github.com/janus-idp/backstage-plugins/issues/1297)) ([2456a28](https://github.com/janus-idp/backstage-plugins/commit/2456a287dbff955a0916b9600e89a39511cd537a)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.7 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.7 ## @janus-idp/backstage-plugin-orchestrator [1.7.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.6.7...@janus-idp/backstage-plugin-orchestrator@1.7.0) (2024-03-03) - ### Features -* **orchestrator:** display a description modal before triggering infra-wfs that resulted from an assessment wf ([#1284](https://github.com/janus-idp/backstage-plugins/issues/1284)) ([ec293c9](https://github.com/janus-idp/backstage-plugins/commit/ec293c9e79efd77873e17d07b1511ad9fdda8842)) +- **orchestrator:** display a description modal before triggering infra-wfs that resulted from an assessment wf ([#1284](https://github.com/janus-idp/backstage-plugins/issues/1284)) ([ec293c9](https://github.com/janus-idp/backstage-plugins/commit/ec293c9e79efd77873e17d07b1511ad9fdda8842)) ## @janus-idp/backstage-plugin-orchestrator [1.6.7](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.6.6...@janus-idp/backstage-plugin-orchestrator@1.6.7) (2024-02-29) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.6 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.6 ## @janus-idp/backstage-plugin-orchestrator [1.6.6](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.6.5...@janus-idp/backstage-plugin-orchestrator@1.6.6) (2024-02-28) - ### Bug Fixes -* **orchestrator:** clean up the plugin code ([#1292](https://github.com/janus-idp/backstage-plugins/issues/1292)) ([ad27fb8](https://github.com/janus-idp/backstage-plugins/commit/ad27fb8e98913a6b80feb38ff58a7864e1953a7e)) - - +- **orchestrator:** clean up the plugin code ([#1292](https://github.com/janus-idp/backstage-plugins/issues/1292)) ([ad27fb8](https://github.com/janus-idp/backstage-plugins/commit/ad27fb8e98913a6b80feb38ff58a7864e1953a7e)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.5 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.5 ## @janus-idp/backstage-plugin-orchestrator [1.6.5](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.6.4...@janus-idp/backstage-plugin-orchestrator@1.6.5) (2024-02-28) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.4 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.4 ## @janus-idp/backstage-plugin-orchestrator [1.6.4](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.6.3...@janus-idp/backstage-plugin-orchestrator@1.6.4) (2024-02-28) - ### Bug Fixes -* **orchestrator:** handle nullable start/state properties of process instance ([#1277](https://github.com/janus-idp/backstage-plugins/issues/1277)) ([d8a43a5](https://github.com/janus-idp/backstage-plugins/commit/d8a43a5a164f83fc90d037ae3d7a355f5de543e0)) - - +- **orchestrator:** handle nullable start/state properties of process instance ([#1277](https://github.com/janus-idp/backstage-plugins/issues/1277)) ([d8a43a5](https://github.com/janus-idp/backstage-plugins/commit/d8a43a5a164f83fc90d037ae3d7a355f5de543e0)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.3 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.3 ## @janus-idp/backstage-plugin-orchestrator [1.6.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.6.2...@janus-idp/backstage-plugin-orchestrator@1.6.3) (2024-02-27) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.2 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.2 ## @janus-idp/backstage-plugin-orchestrator [1.6.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.6.1...@janus-idp/backstage-plugin-orchestrator@1.6.2) (2024-02-27) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.4 +- **@janus-idp/cli:** upgraded to 1.7.4 ## @janus-idp/backstage-plugin-orchestrator [1.6.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.6.0...@janus-idp/backstage-plugin-orchestrator@1.6.1) (2024-02-26) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.3 +- **@janus-idp/cli:** upgraded to 1.7.3 ## @janus-idp/backstage-plugin-orchestrator [1.6.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.5.2...@janus-idp/backstage-plugin-orchestrator@1.6.0) (2024-02-22) - ### Features -* **orchestrator:** display a alert dialog when the user fails to abort a running workflow ([#1239](https://github.com/janus-idp/backstage-plugins/issues/1239)) ([44cb11b](https://github.com/janus-idp/backstage-plugins/commit/44cb11b80739f772f4caa4c2834287eec162b826)) +- **orchestrator:** display a alert dialog when the user fails to abort a running workflow ([#1239](https://github.com/janus-idp/backstage-plugins/issues/1239)) ([44cb11b](https://github.com/janus-idp/backstage-plugins/commit/44cb11b80739f772f4caa4c2834287eec162b826)) ## @janus-idp/backstage-plugin-orchestrator [1.5.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.5.1...@janus-idp/backstage-plugin-orchestrator@1.5.2) (2024-02-21) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.1 -* **@janus-idp/cli:** upgraded to 1.7.2 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.1 +- **@janus-idp/cli:** upgraded to 1.7.2 ## @janus-idp/backstage-plugin-orchestrator [1.5.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.5.0...@janus-idp/backstage-plugin-orchestrator@1.5.1) (2024-02-20) - ### Bug Fixes -* **orchestrator:** decommission the ProcessInstance.lastUpdate field ([#1230](https://github.com/janus-idp/backstage-plugins/issues/1230)) ([9724e27](https://github.com/janus-idp/backstage-plugins/commit/9724e27eaa84fe73d7724f28c86409681b7f79f8)) - - +- **orchestrator:** decommission the ProcessInstance.lastUpdate field ([#1230](https://github.com/janus-idp/backstage-plugins/issues/1230)) ([9724e27](https://github.com/janus-idp/backstage-plugins/commit/9724e27eaa84fe73d7724f28c86409681b7f79f8)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.3.0 ## @janus-idp/backstage-plugin-orchestrator [1.5.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.4.3...@janus-idp/backstage-plugin-orchestrator@1.5.0) (2024-02-18) - ### Features -* **orchestrator:** display a confirmation dialog before the user aborts a running workflow ([#1215](https://github.com/janus-idp/backstage-plugins/issues/1215)) ([1453cf8](https://github.com/janus-idp/backstage-plugins/commit/1453cf8d42b14372c1a5c1973510450d24ae4b5a)) +- **orchestrator:** display a confirmation dialog before the user aborts a running workflow ([#1215](https://github.com/janus-idp/backstage-plugins/issues/1215)) ([1453cf8](https://github.com/janus-idp/backstage-plugins/commit/1453cf8d42b14372c1a5c1973510450d24ae4b5a)) ## @janus-idp/backstage-plugin-orchestrator [1.4.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.4.2...@janus-idp/backstage-plugin-orchestrator@1.4.3) (2024-02-16) - ### Bug Fixes -* **orchestrator:** resolve mismatch between execution data and composed schema ([#1217](https://github.com/janus-idp/backstage-plugins/issues/1217)) ([af85114](https://github.com/janus-idp/backstage-plugins/commit/af851148935e1ed083709cac145520d7551de737)) - - +- **orchestrator:** resolve mismatch between execution data and composed schema ([#1217](https://github.com/janus-idp/backstage-plugins/issues/1217)) ([af85114](https://github.com/janus-idp/backstage-plugins/commit/af851148935e1ed083709cac145520d7551de737)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.2.1 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.2.1 ## @janus-idp/backstage-plugin-orchestrator [1.4.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.4.1...@janus-idp/backstage-plugin-orchestrator@1.4.2) (2024-02-16) - - ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.2.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.2.0 ## @janus-idp/backstage-plugin-orchestrator [1.4.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.4.0...@janus-idp/backstage-plugin-orchestrator@1.4.1) (2024-02-14) - ### Bug Fixes -* **orchestrator:** the instance details card content is cropped ([#1196](https://github.com/janus-idp/backstage-plugins/issues/1196)) ([eb45070](https://github.com/janus-idp/backstage-plugins/commit/eb450709e8e34972386f4e34ee842208e323a3fb)) +- **orchestrator:** the instance details card content is cropped ([#1196](https://github.com/janus-idp/backstage-plugins/issues/1196)) ([eb45070](https://github.com/janus-idp/backstage-plugins/commit/eb450709e8e34972386f4e34ee842208e323a3fb)) ## @janus-idp/backstage-plugin-orchestrator [1.4.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.3.3...@janus-idp/backstage-plugin-orchestrator@1.4.0) (2024-02-12) - ### Features -* build Information dialog component to show confirmation or alert ([#1176](https://github.com/janus-idp/backstage-plugins/issues/1176)) ([ee8cc1d](https://github.com/janus-idp/backstage-plugins/commit/ee8cc1dad2f10d698b8fb7e19ef0f9abe3b6c6c7)) +- build Information dialog component to show confirmation or alert ([#1176](https://github.com/janus-idp/backstage-plugins/issues/1176)) ([ee8cc1d](https://github.com/janus-idp/backstage-plugins/commit/ee8cc1dad2f10d698b8fb7e19ef0f9abe3b6c6c7)) ## @janus-idp/backstage-plugin-orchestrator [1.3.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.3.2...@janus-idp/backstage-plugin-orchestrator@1.3.3) (2024-02-08) - ### Bug Fixes -* **orchestrator:** resolve inconsistency with workflow run average duration format ([#1191](https://github.com/janus-idp/backstage-plugins/issues/1191)) ([0d82e90](https://github.com/janus-idp/backstage-plugins/commit/0d82e90a15fc8e90a4855188586986235394e3d3)) +- **orchestrator:** resolve inconsistency with workflow run average duration format ([#1191](https://github.com/janus-idp/backstage-plugins/issues/1191)) ([0d82e90](https://github.com/janus-idp/backstage-plugins/commit/0d82e90a15fc8e90a4855188586986235394e3d3)) ## @janus-idp/backstage-plugin-orchestrator [1.3.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.3.1...@janus-idp/backstage-plugin-orchestrator@1.3.2) (2024-02-07) - ### Bug Fixes -* **orchestrator:** removes the divider from the workflow definition card ([#1181](https://github.com/janus-idp/backstage-plugins/issues/1181)) ([c2fe940](https://github.com/janus-idp/backstage-plugins/commit/c2fe940fa395842c705f1371872791fdbd77095c)) +- **orchestrator:** removes the divider from the workflow definition card ([#1181](https://github.com/janus-idp/backstage-plugins/issues/1181)) ([c2fe940](https://github.com/janus-idp/backstage-plugins/commit/c2fe940fa395842c705f1371872791fdbd77095c)) ## @janus-idp/backstage-plugin-orchestrator [1.3.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.3.0...@janus-idp/backstage-plugin-orchestrator@1.3.1) (2024-02-05) - - ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.1 +- **@janus-idp/cli:** upgraded to 1.7.1 ## @janus-idp/backstage-plugin-orchestrator [1.3.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.2.0...@janus-idp/backstage-plugin-orchestrator@1.3.0) (2024-02-02) - ### Features -* **orchestrator:** add the ability to rerun workflows in a new instance ([#1141](https://github.com/janus-idp/backstage-plugins/issues/1141)) ([fe326df](https://github.com/janus-idp/backstage-plugins/commit/fe326df569caa5a9e7b7ec809c1c371a2a936010)) - - +- **orchestrator:** add the ability to rerun workflows in a new instance ([#1141](https://github.com/janus-idp/backstage-plugins/issues/1141)) ([fe326df](https://github.com/janus-idp/backstage-plugins/commit/fe326df569caa5a9e7b7ec809c1c371a2a936010)) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.1.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.1.0 ## @janus-idp/backstage-plugin-orchestrator [1.2.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.1.3...@janus-idp/backstage-plugin-orchestrator@1.2.0) (2024-01-30) - ### Features -* add new backend system support for existing backend plugins that have not been migrated over yet ([#1132](https://github.com/janus-idp/backstage-plugins/issues/1132)) ([06e16fd](https://github.com/janus-idp/backstage-plugins/commit/06e16fdcf64257dd08297cb727445d9a8a23c522)) - - +- add new backend system support for existing backend plugins that have not been migrated over yet ([#1132](https://github.com/janus-idp/backstage-plugins/issues/1132)) ([06e16fd](https://github.com/janus-idp/backstage-plugins/commit/06e16fdcf64257dd08297cb727445d9a8a23c522)) ### Dependencies -* **@janus-idp/cli:** upgraded to 1.7.0 +- **@janus-idp/cli:** upgraded to 1.7.0 ## @janus-idp/backstage-plugin-orchestrator [1.1.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.1.2...@janus-idp/backstage-plugin-orchestrator@1.1.3) (2024-01-30) - ### Bug Fixes -* **orchestrator:** resolve bug in workflow instance page assessed by link ([#1142](https://github.com/janus-idp/backstage-plugins/issues/1142)) ([48724f8](https://github.com/janus-idp/backstage-plugins/commit/48724f8d90ec9927ed07382061bce78171ccb1b2)) +- **orchestrator:** resolve bug in workflow instance page assessed by link ([#1142](https://github.com/janus-idp/backstage-plugins/issues/1142)) ([48724f8](https://github.com/janus-idp/backstage-plugins/commit/48724f8d90ec9927ed07382061bce78171ccb1b2)) ## @janus-idp/backstage-plugin-orchestrator [1.1.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.1.1...@janus-idp/backstage-plugin-orchestrator@1.1.2) (2024-01-29) - ### Bug Fixes -* **orchestrator:** fixes sorting by name in the workflows list ([#1135](https://github.com/janus-idp/backstage-plugins/issues/1135)) ([2a023e1](https://github.com/janus-idp/backstage-plugins/commit/2a023e156a69ca3cf102ba9a77f076e3289b60b4)) -* **orchestrator:** fixes sorting workflow runs ([#1136](https://github.com/janus-idp/backstage-plugins/issues/1136)) ([7c3d0f6](https://github.com/janus-idp/backstage-plugins/commit/7c3d0f62abf861faae82d84cf1d25213d1791dc5)) +- **orchestrator:** fixes sorting by name in the workflows list ([#1135](https://github.com/janus-idp/backstage-plugins/issues/1135)) ([2a023e1](https://github.com/janus-idp/backstage-plugins/commit/2a023e156a69ca3cf102ba9a77f076e3289b60b4)) +- **orchestrator:** fixes sorting workflow runs ([#1136](https://github.com/janus-idp/backstage-plugins/issues/1136)) ([7c3d0f6](https://github.com/janus-idp/backstage-plugins/commit/7c3d0f62abf861faae82d84cf1d25213d1791dc5)) ## @janus-idp/backstage-plugin-orchestrator [1.1.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.1.0...@janus-idp/backstage-plugin-orchestrator@1.1.1) (2024-01-25) - ### Bug Fixes -* **orchestrator:** set default workflow runs table size to 20 ([#1127](https://github.com/janus-idp/backstage-plugins/issues/1127)) ([c5e14fd](https://github.com/janus-idp/backstage-plugins/commit/c5e14fd8e343df7d8c6db7f539fbbd2747e7792e)) - +- **orchestrator:** set default workflow runs table size to 20 ([#1127](https://github.com/janus-idp/backstage-plugins/issues/1127)) ([c5e14fd](https://github.com/janus-idp/backstage-plugins/commit/c5e14fd8e343df7d8c6db7f539fbbd2747e7792e)) ### Documentation -* **orchestrator:** adds a section about deploying as a dynamic plugins ([#1125](https://github.com/janus-idp/backstage-plugins/issues/1125)) ([eaff621](https://github.com/janus-idp/backstage-plugins/commit/eaff621cf39ab76909446616230de48512714187)) +- **orchestrator:** adds a section about deploying as a dynamic plugins ([#1125](https://github.com/janus-idp/backstage-plugins/issues/1125)) ([eaff621](https://github.com/janus-idp/backstage-plugins/commit/eaff621cf39ab76909446616230de48512714187)) ## @janus-idp/backstage-plugin-orchestrator [1.1.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.0.2...@janus-idp/backstage-plugin-orchestrator@1.1.0) (2024-01-25) - ### Features -* **orchestrator:** add auto refresh to workflow instance list and details pages ([#1081](https://github.com/janus-idp/backstage-plugins/issues/1081)) ([fc30645](https://github.com/janus-idp/backstage-plugins/commit/fc30645ff740e914708a20f1fa1e2e118f771433)) - - +- **orchestrator:** add auto refresh to workflow instance list and details pages ([#1081](https://github.com/janus-idp/backstage-plugins/issues/1081)) ([fc30645](https://github.com/janus-idp/backstage-plugins/commit/fc30645ff740e914708a20f1fa1e2e118f771433)) ### Dependencies -* **@janus-idp/cli:** upgraded to 1.6.0 +- **@janus-idp/cli:** upgraded to 1.6.0 ## @janus-idp/backstage-plugin-orchestrator [1.0.2](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.0.1...@janus-idp/backstage-plugin-orchestrator@1.0.2) (2024-01-24) - ### Bug Fixes -* **orchestrator:** do not show duration when ProcessInstance.end time is n/a ([#1112](https://github.com/janus-idp/backstage-plugins/issues/1112)) ([75e6bbe](https://github.com/janus-idp/backstage-plugins/commit/75e6bbe8737742494817112b8da0fc50be5ff245)) +- **orchestrator:** do not show duration when ProcessInstance.end time is n/a ([#1112](https://github.com/janus-idp/backstage-plugins/issues/1112)) ([75e6bbe](https://github.com/janus-idp/backstage-plugins/commit/75e6bbe8737742494817112b8da0fc50be5ff245)) ## @janus-idp/backstage-plugin-orchestrator [1.0.1](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-orchestrator@1.0.0...@janus-idp/backstage-plugin-orchestrator@1.0.1) (2024-01-18) - ### Bug Fixes -* **orchestrator:** update the navigation bar icon according to UX ([#1078](https://github.com/janus-idp/backstage-plugins/issues/1078)) ([da3d8fc](https://github.com/janus-idp/backstage-plugins/commit/da3d8fc7a33f01729ead1d515d16ebefc47326c3)) +- **orchestrator:** update the navigation bar icon according to UX ([#1078](https://github.com/janus-idp/backstage-plugins/issues/1078)) ([da3d8fc](https://github.com/janus-idp/backstage-plugins/commit/da3d8fc7a33f01729ead1d515d16ebefc47326c3)) ## @janus-idp/backstage-plugin-orchestrator 1.0.0 (2024-01-17) - ### Features -* **orchestrator:** add orchestrator plugin ([#783](https://github.com/janus-idp/backstage-plugins/issues/783)) ([cf5fe74](https://github.com/janus-idp/backstage-plugins/commit/cf5fe74db6992d9f51f5073bbcf20c8c346357a1)), closes [#28](https://github.com/janus-idp/backstage-plugins/issues/28) [#38](https://github.com/janus-idp/backstage-plugins/issues/38) [#35](https://github.com/janus-idp/backstage-plugins/issues/35) [#21](https://github.com/janus-idp/backstage-plugins/issues/21) - - +- **orchestrator:** add orchestrator plugin ([#783](https://github.com/janus-idp/backstage-plugins/issues/783)) ([cf5fe74](https://github.com/janus-idp/backstage-plugins/commit/cf5fe74db6992d9f51f5073bbcf20c8c346357a1)), closes [#28](https://github.com/janus-idp/backstage-plugins/issues/28) [#38](https://github.com/janus-idp/backstage-plugins/issues/38) [#35](https://github.com/janus-idp/backstage-plugins/issues/35) [#21](https://github.com/janus-idp/backstage-plugins/issues/21) ### Dependencies -* **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.0.0 +- **@janus-idp/backstage-plugin-orchestrator-common:** upgraded to 1.0.0 diff --git a/plugins/orchestrator/README.md b/plugins/orchestrator/README.md index 74a0287409..0ed9f9b264 100644 --- a/plugins/orchestrator/README.md +++ b/plugins/orchestrator/README.md @@ -86,61 +86,7 @@ orchestrator: For more information about the configuration options, including other optional properties, see the [config.d.ts](../orchestrator-common/config.d.ts) file. -#### Setting up the Orchestrator backend package for the legacy backend - -1. Install the Orchestrator backend plugin using the following command: - - ```console - yarn workspace backend add @janus-idp/backstage-plugin-orchestrator-backend - ``` - -1. Create a new plugin instance in `packages/backend/src/plugins/orchestrator.ts` file: - - ```ts title="packages/backend/src/plugins/orchestrator.ts" - import { Router } from 'express'; - - import { createRouter } from '@janus-idp/backstage-plugin-orchestrator-backend'; - - import { PluginEnvironment } from '../types'; - - export default async function createPlugin( - env: PluginEnvironment, - ): Promise { - return await createRouter({ - config: env.config, - logger: env.logger, - discovery: env.discovery, - catalogApi: env.catalogApi, - urlReader: env.reader, - scheduler: env.scheduler, - }); - } - ``` - -1. Import and plug the new instance into `packages/backend/src/index.ts` file: - - ```ts title="packages/backend/src/index.ts" - /* highlight-add-next-line */ - import orchestrator from './plugins/orchestrator'; - - async function main() { - // ... - const createEnv = makeCreateEnv(config); - // ... - /* highlight-add-next-line */ - const orchestratorEnv = useHotMemoize(module, () => - createEnv('orchestrator'), - ); - // ... - const apiRouter = Router(); - // ... - /* highlight-add-next-line */ - apiRouter.use('/orchestrator', await orchestrator(orchestratorEnv)); - // ... - } - ``` - -#### Setting up the Orchestrator backend package for the new backend +#### Setting up the Orchestrator backend package 1. Install the Orchestrator backend plugin using the following command: @@ -154,9 +100,7 @@ For more information about the configuration options, including other optional p const backend = createBackend(); /* highlight-add-next-line */ - backend.add( - import('@janus-idp/backstage-plugin-orchestrator-backend/alpha'), - ); + backend.add(import('@janus-idp/backstage-plugin-orchestrator-backend')); backend.start(); ``` diff --git a/plugins/orchestrator/package.json b/plugins/orchestrator/package.json index 186d2e5b2c..60e0be77b8 100644 --- a/plugins/orchestrator/package.json +++ b/plugins/orchestrator/package.json @@ -4,7 +4,6 @@ "license": "Apache-2.0", "main": "src/index.ts", "types": "src/index.ts", - "private": true, "publishConfig": { "access": "public", "main": "dist/index.esm.js", @@ -23,7 +22,7 @@ "homepage": "https://red.ht/rhdh", "repository": { "type": "git", - "url": "git+https://github.com/janus-idp/backstage-plugins.git", + "url": "https://github.com/janus-idp/backstage-plugins", "directory": "plugins/orchestrator" }, "bugs": "https://github.com/janus-idp/backstage-plugins/issues", @@ -45,12 +44,8 @@ "start": "backstage-cli package start", "build": "backstage-cli package build", "export-dynamic": "janus-cli package export-dynamic-plugin --in-place", - "export-dynamic:clean": "janus-cli package export-dynamic-plugin --in-place --clean", "tsc": "tsc", - "prettier:check": "prettier --ignore-unknown --check .", - "prettier:fix": "prettier --ignore-unknown --write .", - "lint:check": "backstage-cli package lint", - "lint:fix": "backstage-cli package lint --fix", + "lint": "backstage-cli package lint", "test": "backstage-cli package test --passWithNoTests --coverage", "clean": "backstage-cli package clean", "prepack": "backstage-cli package prepack", @@ -84,7 +79,6 @@ "vscode-languageserver-types": "^3.16.0" }, "devDependencies": { - "prettier": "3.3.3", "@backstage/cli": "0.26.11", "@backstage/dev-utils": "1.0.36", "@backstage/test-utils": "1.5.9", diff --git a/plugins/orchestrator/src/__fixtures__/fakeLongProcessInstanceList.ts b/plugins/orchestrator/src/__fixtures__/fakeLongProcessInstanceList.ts deleted file mode 100644 index 0421b5b194..0000000000 --- a/plugins/orchestrator/src/__fixtures__/fakeLongProcessInstanceList.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { - ProcessInstance, - ProcessInstanceState, - WorkflowCategory, -} from '@janus-idp/backstage-plugin-orchestrator-common'; - -import { fakeWorkflowOverviewList } from './fakeWorkflowOverviewList'; - -const createValuesGenerator = (counter: number, size: number) => { - const baseDate = new Date('2024-02-01'); - const DAY = 24 * 60 * 60 * 1000; // 24 hours in milliseconds - const startDate = new Date(baseDate.getTime() - 30 * DAY); // 30 days ago - const endDate = new Date(); - - const getNextEnumValue = (enumerator: object) => { - const values = Object.values(enumerator); - const index = counter % values.length; - return values[index]; - }; - - const getNextDate = (): Date => { - const startMillis = startDate.getTime(); - const endMillis = endDate.getTime(); - const millis = - startMillis + ((endMillis - startMillis) / size) * (counter % size); // Assuming 5 states - return new Date(millis); - }; - - return { - getNextEnumValue, - getNextDate, - }; -}; - -export const generateFakeProcessInstances = ( - listSize: number, -): ProcessInstance[] => { - const instances: ProcessInstance[] = []; - - for (let i = 0; i < listSize; i++) { - const valuesGenerator = createValuesGenerator(i, listSize); - - const randomState = valuesGenerator.getNextEnumValue(ProcessInstanceState); - const randomCategory = valuesGenerator.getNextEnumValue(WorkflowCategory); - - const overviews = fakeWorkflowOverviewList.overviews ?? []; - instances.push({ - id: `12f767c1-9002-43af-9515-62a72d0eaf${i}`, - processId: overviews[i % overviews.length].workflowId ?? 'FAKE_ID', // This will be fixed in next PRs - state: randomState, - endpoint: 'enpoint/foo', - start: valuesGenerator.getNextDate().toISOString(), - nodes: [], - variables: {}, - isOpen: false, - isSelected: false, - category: randomCategory, - description: `test description ${i}`, - }); - } - - return instances; -}; diff --git a/plugins/orchestrator/src/__fixtures__/fakeNodeInstances.ts b/plugins/orchestrator/src/__fixtures__/fakeNodeInstances.ts deleted file mode 100644 index c2cd405001..0000000000 --- a/plugins/orchestrator/src/__fixtures__/fakeNodeInstances.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { NodeInstance } from '@janus-idp/backstage-plugin-orchestrator-common'; - -export const fakeNodeInstances: NodeInstance[] = [ - { - id: '5e33c387-1235-41cd-a7af-6d9e11c4381d', - nodeId: '2', - definitionId: '_jbpm-unique-352', - type: 'EndNode', - name: 'End', - enter: '2024-01-07T11:02:44.264Z', - exit: '2024-01-07T11:02:44.264Z', - }, - { - id: 'b1602034-979e-49ce-99f6-b4222f799f6d', - nodeId: '6', - definitionId: '_jbpm-unique-356', - type: 'CompositeContextNode', - name: 'GreetPerson', - enter: '2024-01-07T11:02:39.204Z', - exit: '2024-01-07T11:02:44.264Z', - }, - { - id: '57d01c6a-7dc7-4c85-86f3-db46d148bd15', - nodeId: '12', - definitionId: '_jbpm-unique-361', - type: 'EndNode', - name: 'EmbeddedEnd', - enter: '2024-01-07T11:02:44.263Z', - exit: '2024-01-07T11:02:44.263Z', - }, - { - id: 'dbb08e15-13e7-47db-b8a9-0924cc288885', - nodeId: '10', - definitionId: '_jbpm-unique-359', - type: 'ActionNode', - name: 'Script', - enter: '2024-01-07T11:02:44.262Z', - exit: '2024-01-07T11:02:44.263Z', - }, - { - id: '4b5cf2ef-32e3-4720-952a-90158cb8259d', - nodeId: '9', - definitionId: '_jbpm-unique-358', - type: 'ActionNode', - name: 'greetFunction', - enter: '2024-01-07T11:02:44.261Z', - exit: '2024-01-07T11:02:44.262Z', - }, - { - id: '54b25b60-b03a-4c76-bd69-648ce73ab3cf', - nodeId: '11', - definitionId: '11', - type: 'TimerNode', - name: 'TimerNode_11', - enter: '2024-01-07T11:02:39.205Z', - exit: '2024-01-07T11:02:44.26Z', - }, - { - id: 'dac8b5a1-d918-4e01-b538-58ca440e9f05', - nodeId: '7', - definitionId: '_jbpm-unique-357', - type: 'StartNode', - name: 'EmbeddedStart', - enter: '2024-01-07T11:02:39.204Z', - exit: '2024-01-07T11:02:39.205Z', - }, - { - id: 'c52c291b-b9c8-4fa1-a586-a0ed9531feb0', - nodeId: '14', - definitionId: '_jbpm-unique-363', - type: 'Join', - name: 'Join-GreetPerson', - enter: '2024-01-07T11:02:39.203Z', - exit: '2024-01-07T11:02:39.203Z', - }, - { - id: 'f5991057-3f73-493b-9be2-14a5069db99c', - nodeId: '4', - definitionId: '_jbpm-unique-354', - type: 'ActionNode', - name: 'GreetInEnglish', - enter: '2024-01-07T11:02:39.197Z', - exit: '2024-01-07T11:02:39.203Z', - }, - { - id: 'd44679a4-ae8b-4481-b65b-2310da26af47', - nodeId: '13', - definitionId: '_jbpm-unique-362', - type: 'Join', - name: 'Join-GreetInEnglish', - enter: '2024-01-07T11:02:39.196Z', - exit: '2024-01-07T11:02:39.197Z', - }, - { - id: '31ee04b6-ec89-4d69-9e48-f520a353abc3', - nodeId: '3', - definitionId: '3', - type: 'Split', - name: 'ChooseOnLanguage', - enter: '2024-01-07T11:02:39.167Z', - exit: '2024-01-07T11:02:39.195Z', - }, - { - id: 'f571878c-c59f-4f59-8759-9be8bb51a68e', - nodeId: '1', - definitionId: '_jbpm-unique-351', - type: 'StartNode', - name: 'Start', - enter: '2024-01-07T11:02:39.165Z', - exit: '2024-01-07T11:02:39.166Z', - }, -]; diff --git a/plugins/orchestrator/src/__fixtures__/fakeProcessInstance.ts b/plugins/orchestrator/src/__fixtures__/fakeProcessInstance.ts deleted file mode 100644 index f83ec0897f..0000000000 --- a/plugins/orchestrator/src/__fixtures__/fakeProcessInstance.ts +++ /dev/null @@ -1,104 +0,0 @@ -import moment from 'moment'; - -import { - ProcessInstance, - ProcessInstanceState, - WorkflowCategory, -} from '@janus-idp/backstage-plugin-orchestrator-common'; - -import { fakeWorkflowOverviewList } from './fakeWorkflowOverviewList'; - -let id = 10; -const baseDate = '2023-11-16T10:50:34.346Z'; -export const fakeProcessInstance1: ProcessInstance = { - id: `12f767c1-9002-43af-9515-62a72d0eaf${id++}`, - processName: fakeWorkflowOverviewList.overviews?.[0].name, - processId: - fakeWorkflowOverviewList.overviews?.[0].workflowId ?? 'FAKE_PROCESS_ID', // This will be fixed in next PRs - state: ProcessInstanceState.Error, - start: baseDate, - end: moment(baseDate).add(13, 'hours').toISOString(), - nodes: [], - endpoint: 'enpoint/foo', - serviceUrl: 'service/bar', - source: 'my-source', - category: WorkflowCategory.INFRASTRUCTURE, - description: 'test description 1', - variables: { - foo: 'bar', - workflowdata: { - workflowOptions: { - 'my-category': { - id: 'next-workflow-1', - name: 'Next Workflow One', - }, - 'my-secod-category': [ - { - id: 'next-workflow-20', - name: 'Next Workflow Twenty', - }, - { - id: 'next-workflow-21', - name: 'Next Workflow Twenty One', - }, - ], - }, - }, - }, -}; - -export const fakeCompletedInstance: ProcessInstance = { - id: `12f767c1-9002-43af-9515-62a72d0eaf${id++}`, - processName: fakeWorkflowOverviewList.overviews?.[1].name, - processId: - fakeWorkflowOverviewList.overviews?.[1].workflowId ?? 'FAKE_PROCESS_ID', // This will be fixed in next PRs, - state: ProcessInstanceState.Completed, - start: moment(baseDate).add(1, 'hour').toISOString(), - end: moment(baseDate).add(1, 'day').toISOString(), - nodes: [], - variables: {}, - endpoint: 'enpoint/foo', - serviceUrl: 'service/bar', - source: 'my-source', - category: WorkflowCategory.ASSESSMENT, - description: 'test description 2', -}; - -export const fakeActiveInstance: ProcessInstance = { - id: `12f767c1-9002-43af-9515-62a72d0eaf${id++}`, - processName: fakeWorkflowOverviewList.overviews?.[2].name, - processId: - fakeWorkflowOverviewList.overviews?.[2].workflowId ?? 'FAKE_PROCESS_ID', // This will be fixed in next PRs, - state: ProcessInstanceState.Active, - start: moment(baseDate).add(2, 'hours').toISOString(), - nodes: [], - variables: {}, - endpoint: 'enpoint/foo', - serviceUrl: 'service/bar', - source: 'my-source', - category: WorkflowCategory.INFRASTRUCTURE, - description: 'test description 3', -}; - -export const fakeProcessInstance4: ProcessInstance = { - id: `12f767c1-9002-43af-9515-62a72d0eaf${id++}`, - processName: fakeWorkflowOverviewList.overviews?.[3].name, - processId: - fakeWorkflowOverviewList.overviews?.[3].workflowId ?? 'FAKE_PROCESS_ID', // This will be fixed in next PRs, - state: ProcessInstanceState.Suspended, - start: baseDate, - nodes: [], - variables: {}, - endpoint: 'enpoint/foo', - serviceUrl: 'service/bar', - source: 'my-source', - category: WorkflowCategory.INFRASTRUCTURE, - description: 'test description 4', -}; - -export const fakeProcessInstances = [ - fakeProcessInstance1, - fakeCompletedInstance, - fakeActiveInstance, - fakeProcessInstance4, -]; diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaDifferentTypes.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaDifferentTypes.ts deleted file mode 100644 index f2f63abb4d..0000000000 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaDifferentTypes.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { WorkflowInputSchemaResponse } from '@janus-idp/backstage-plugin-orchestrator-common'; - -export const fakeDataInputSchemaDifferentTypes: WorkflowInputSchemaResponse = { - definition: { - id: 'yamlgreet', - version: '1.0', - specVersion: '0.8', - name: 'Greeting workflow', - description: 'YAML based greeting workflow', - dataInputSchema: 'schemas/yamlgreet__main_schema.json', - start: 'ChooseOnLanguage', - functions: [ - { - name: 'greetFunction', - type: 'custom', - operation: 'sysout', - }, - ], - states: [ - { - name: 'ChooseOnLanguage', - type: 'switch', - dataConditions: [ - { - condition: '${ .language == "English" }', - transition: 'GreetInEnglish', - }, - { - condition: '${ .language == "Spanish" }', - transition: 'GreetInSpanish', - }, - ], - defaultCondition: { - transition: 'GreetInEnglish', - }, - }, - { - name: 'GreetInEnglish', - type: 'inject', - data: { - greeting: 'Hello from YAML Workflow, ', - }, - transition: 'GreetPerson', - }, - { - name: 'GreetInSpanish', - type: 'inject', - data: { - greeting: 'Saludos desde YAML Workflow, ', - }, - transition: 'GreetPerson', - }, - { - name: 'GreetPerson', - type: 'operation', - actions: [ - { - name: 'greetAction', - functionRef: { - refName: 'greetFunction', - arguments: { - message: '.greeting+.name', - }, - }, - }, - ], - end: { - terminate: true, - }, - }, - ], - }, - isComposedSchema: true, - schemaSteps: [ - { - title: 'Boolean field', - schema: { - type: 'object', - properties: { - default: { - type: 'boolean', - title: 'checkbox (default)', - description: 'This is the checkbox-description', - }, - }, - }, - key: 'booleanfield', - readonlyKeys: [], - data: {}, - }, - { - title: 'String formats', - schema: { - type: 'object', - - properties: { - email: { - type: 'string', - format: 'email', - }, - uri: { - type: 'string', - format: 'uri', - }, - }, - }, - data: {}, - readonlyKeys: [], - key: 'string-formats', - }, - { - readonlyKeys: [], - key: 'select', - title: 'Select', - data: {}, - schema: { - title: 'Select', - type: 'object', - properties: { - select: { - title: 'Select widget with options', - type: 'string', - enum: ['pizza', 'pasta', 'canaloni', 'ravioli'], - }, - }, - }, - }, - { - readonlyKeys: [], - key: 'dateandtime', - title: 'Date and time', - data: {}, - schema: { - type: 'object', - properties: { - datetime: { - type: 'string', - format: 'date-time', - }, - date: { - type: 'string', - format: 'date', - }, - time: { - type: 'string', - format: 'time', - }, - }, - }, - }, - { - key: 'array', - title: 'Array', - schema: { - type: 'object', - required: ['title'], - properties: { - title: { - type: 'string', - title: 'Task list title', - }, - tasks: { - type: 'array', - title: 'Tasks', - items: { - type: 'object', - required: ['title'], - properties: { - title: { - type: 'string', - title: 'Title', - description: 'A sample title', - }, - details: { - type: 'string', - title: 'Task details', - description: 'Enter the task details', - }, - done: { - type: 'boolean', - title: 'Done?', - default: false, - }, - }, - }, - }, - }, - }, - data: { title: 'my task list' }, - readonlyKeys: ['title'], - }, - ], -}; diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStep.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStep.ts deleted file mode 100644 index 7397f78a0b..0000000000 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStep.ts +++ /dev/null @@ -1,330 +0,0 @@ -import { WorkflowInputSchemaResponse } from '@janus-idp/backstage-plugin-orchestrator-common'; - -export const fakeDataInputSchemaMultiStepResponse: WorkflowInputSchemaResponse = - { - definition: { - id: 'ansible-job-template', - version: '1.0', - specVersion: '0.8', - name: 'Ansible Job Template', - description: - 'Define an Ansible Job Template within Ansible Automation Platform', - dataInputSchema: 'schemas/ansible-job-template__main-schema.json', - functions: [ - { - name: 'runActionFetchTemplate', - operation: 'specs/actions-openapi.json#fetch:template', - }, - { - name: 'runActionGitHubRepoPush', - operation: 'specs/actions-openapi.json#github:repo:push', - }, - { - name: 'runActionCatalogRegister', - operation: 'specs/actions-openapi.json#catalog:register', - }, - { - name: 'fs:delete', - operation: 'specs/actions-openapi.json#fs:delete', - }, - { - name: 'sysout', - type: 'custom', - operation: 'sysout', - }, - ], - errors: [ - { - name: 'Error on Action', - code: 'java.lang.RuntimeException', - }, - ], - start: 'Code and Catalog generation', - states: [ - { - name: 'Code and Catalog generation', - type: 'parallel', - branches: [ - { - name: 'Generating the Ansible Job component', - actions: [ - { - name: 'Run Template Fetch Action', - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - id: '$WORKFLOW.instanceId', - url: 'https://github.com/janus-idp/software-templates/tree/main/templates/github/launch-ansible-job/skeleton', - values: { - name: '${.ansibleJobDefinition.name}', - jobTemplate: '${.ansibleJobDefinition.jobTemplate}', - component_id: '${.ansibleJobDefinition.name}', - namespace: '${.ansibleJobDefinition.namespace}', - // deepcode ignore HardcodedNonCryptoSecret: False positive - connection_secret: - '${.ansibleJobDefinition.connectionSecret}', - description: '${.ansibleJobDefinition.description}', - extra_vars: '${.ansibleJobDefinition.extraVars}', - }, - }, - }, - }, - ], - }, - { - name: 'Generating the Catalog Info Component', - actions: [ - { - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - id: '$WORKFLOW.instanceId', - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/catalog-info', - values: { - githubOrg: '${.repositoryInfo.githubOrg}', - repoName: '${.repositoryInfo.repoName}', - owner: '${.repositoryInfo.owner}', - applicationType: 'api', - description: '${.ansibleJobDefinition.description}', - }, - }, - }, - }, - ], - }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', - }, - ], - compensatedBy: 'Clear Code and Catalog generation', - transition: 'Publishing to the Source Code Repository', - }, - { - name: 'Publishing to the Source Code Repository', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Publish Github', - functionRef: { - refName: 'runActionGitHubRepoPush', - arguments: { - id: '$WORKFLOW.instanceId', - title: '.ansibleJobDefinition.name + "-job"', - description: 'Workflow Action', - repoUrl: - '"github.com?owner=" + .repositoryInfo.githubOrg + "&repo=" + .repositoryInfo.repoName', - defaultBranch: 'main', - gitCommitMessage: 'Initial commit', - protectDefaultBranch: false, - protectEnforceAdmins: false, - }, - }, - actionDataFilter: { - results: '.actionPublishResult', - }, - }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', - }, - ], - compensatedBy: 'Remove Source Code Repository', - transition: 'Registering the Catalog Info Component', - }, - { - name: 'Registering the Catalog Info Component', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Catalog Register Action', - functionRef: { - refName: 'runActionCatalogRegister', - arguments: { - id: '$WORKFLOW.instanceId', - repoContentsUrl: '.actionPublishResult.repoContentsUrl', - catalogInfoPath: '"/catalog-info.yaml"', - }, - }, - actionDataFilter: { - toStateData: '.actionCatalogRegisterResult', - }, - }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', - }, - ], - compensatedBy: 'Remove Catalog Info Component', - end: true, - }, - { - name: 'Clear Code and Catalog generation', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Clear FS Action', - functionRef: { - refName: 'fs:delete', - arguments: { - files: ['./'], - }, - }, - }, - ], - }, - { - name: 'Remove Source Code Repository', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Remove Source Code Repository', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Remove Source Code Repository', - }, - }, - }, - ], - }, - { - name: 'Remove Catalog Info Component', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Remove Catalog Info Component', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Remove Catalog Info Component', - }, - }, - }, - ], - }, - { - name: 'Handle Error', - type: 'operation', - actions: [ - { - name: 'Error Action', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Error on workflow, triggering compensations', - }, - }, - }, - ], - end: { - compensate: true, - }, - }, - ], - }, - isComposedSchema: true, - schemaSteps: [ - { - title: 'Provide information about the GitHub location', - key: 'repositoryInfo', - schema: { - $id: 'classpath:/schemas/ansible-job-template__ref-schema__GitHub_Repository_Info.json', - title: 'Provide information about the GitHub location', - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'object', - properties: { - githubOrg: { - title: 'Organization Name', - description: 'GitHub organization name', - type: 'string', - }, - owner: { - title: 'Owner', - description: 'An entity from the catalog', - type: 'string', - }, - repoName: { - title: 'Repository Name', - description: 'GitHub repository name', - type: 'string', - }, - system: { - title: 'System', - description: 'An entity from the catalog', - type: 'string', - default: 'system:janus-idp', - }, - }, - required: ['githubOrg', 'owner', 'repoName', 'system'], - }, - data: {}, - readonlyKeys: [], - }, - { - title: 'Ansible Job Definition', - key: 'ansibleJobDefinition', - schema: { - $id: 'classpath:/schemas/ansible-job-template__ref-schema__Ansible_Job_Definition.json', - title: 'Ansible Job Definition', - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'object', - properties: { - name: { - title: 'Name of the Ansible Job', - description: 'A unique name for the Ansible Job', - type: 'string', - }, - jobTemplate: { - title: 'Name of the Job Template to launch', - description: 'Specify a job template to launch', - type: 'string', - }, - description: { - title: 'Description', - description: 'Provide a description of the Job to be launched', - type: 'string', - }, - namespace: { - title: 'Namespace', - description: 'Specify the namespace to launch the job', - type: 'string', - default: 'aap', - }, - connectionSecret: { - title: 'Connection Secret', - description: 'Specify the connection secret to use for the job', - type: 'string', - default: 'aapaccess', - }, - extraVars: { - title: 'Extra Vars', - description: 'Specify any extra vars to pass to the job', - type: 'string', - default: '{}', - }, - }, - required: [ - 'name', - 'jobTemplate', - 'description', - 'namespace', - 'connectionSecret', - ], - }, - data: {}, - readonlyKeys: [], - }, - ], - }; diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStepInitialState.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStepInitialState.ts deleted file mode 100644 index f4b0a93fa0..0000000000 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStepInitialState.ts +++ /dev/null @@ -1,575 +0,0 @@ -import { WorkflowInputSchemaResponse } from '@janus-idp/backstage-plugin-orchestrator-common'; - -export const fakeDataInputSchemaMultiStepInitialStateResponse: WorkflowInputSchemaResponse = - { - definition: { - id: 'quarkus-backend', - version: '1.0', - specVersion: '0.8', - name: 'Quarkus Backend application', - description: - 'Create a starter Quarkus backend application with a CI pipeline', - dataInputSchema: 'schemas/quarkus-backend__main-schema.json', - functions: [ - { - name: 'runActionFetchTemplate', - operation: 'specs/actions-openapi.json#fetch:template', - }, - { - name: 'runActionPublishGithub', - operation: 'specs/actions-openapi.json#publish:github', - }, - { - name: 'runActionCatalogRegister', - operation: 'specs/actions-openapi.json#catalog:register', - }, - { - name: 'fs:delete', - operation: 'specs/actions-openapi.json#fs:delete', - }, - { - name: 'sysout', - type: 'custom', - operation: 'sysout', - }, - ], - errors: [ - { - name: 'Error on Action', - code: 'java.lang.RuntimeException', - }, - ], - start: 'Generating the Source Code Component', - states: [ - { - name: 'Generating the Source Code Component', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Fetch Template Action - Source Code', - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/templates/github/quarkus-backend/skeleton', - values: { - orgName: '.orgName', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - port: '.port', - ci: '.ci', - sourceControl: 'github.com', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - version: '.version', - }, - }, - }, - actionDataFilter: { - toStateData: '.actionFetchTemplateSourceCodeResult', - }, - }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', - }, - ], - compensatedBy: 'Clear File System - Source Code', - transition: 'Generating the CI Component', - }, - { - name: 'Generating the CI Component', - type: 'switch', - dataConditions: [ - { - condition: '${ .ci == "github" }', - transition: 'Generating the CI Component - GitHub', - }, - { - condition: '${ .ci == "tekton" }', - transition: 'Generating the CI Component - Tekton', - }, - ], - defaultCondition: { - transition: 'Generating the CI Component - GitHub', - }, - }, - { - name: 'Generating the CI Component - GitHub', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Run Template Fetch Action - CI - GitHub', - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/github-actions', - copyWithoutTemplating: ['".github/workflows/"'], - values: { - orgName: '.orgName', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - port: '.port', - ci: '.ci', - sourceControl: 'github.com', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - version: '.version', - }, - }, - }, - actionDataFilter: { - toStateData: '.actionTemplateFetchCIResult', - }, - }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', - }, - ], - compensatedBy: 'Clear File System - CI', - transition: 'Generating the Catalog Info Component', - }, - { - name: 'Generating the CI Component - Tekton', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Run Template Fetch Action - CI - Tekton', - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/tekton', - copyWithoutTemplating: ['".github/workflows/"'], - values: { - orgName: '.orgName', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - imageUrl: '.imageUrl', - imageRepository: '.imageRepository', - imageBuilder: 's2i-java', - port: '.port', - ci: '.ci', - sourceControl: 'github.com', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - version: '.version', - }, - }, - }, - actionDataFilter: { - toStateData: '.actionTemplateFetchCIResult', - }, - }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', - }, - ], - compensatedBy: 'Clear File System - CI', - transition: 'Generating the Catalog Info Component', - }, - { - name: 'Generating the Catalog Info Component', - type: 'operation', - actions: [ - { - name: 'Fetch Template Action - Catalog Info', - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/catalog-info', - values: { - orgName: '.orgName', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - imageUrl: 'imageUrl', - imageRepository: '.imageRepository', - imageBuilder: 's2i-go', - port: '.port', - ci: '.ci', - sourceControl: 'github.com', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - version: '.version', - }, - }, - }, - actionDataFilter: { - toStateData: '.actionFetchTemplateCatalogInfoResult', - }, - }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', - }, - ], - compensatedBy: 'Clear File System - Catalog', - transition: 'Publishing to the Source Code Repository', - }, - { - name: 'Publishing to the Source Code Repository', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Publish Github', - functionRef: { - refName: 'runActionPublishGithub', - arguments: { - allowedHosts: ['"github.com"'], - description: 'Workflow Action', - repoUrl: - '"github.com?owner=" + .orgName + "&repo=" + .repoName', - defaultBranch: 'main', - gitCommitMessage: 'Initial commit', - allowAutoMerge: true, - allowRebaseMerge: true, - }, - }, - actionDataFilter: { - toStateData: '.actionPublishResult', - }, - }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', - }, - ], - compensatedBy: 'Remove Source Code Repository', - transition: 'Registering the Catalog Info Component', - }, - { - name: 'Registering the Catalog Info Component', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Catalog Register Action', - functionRef: { - refName: 'runActionCatalogRegister', - arguments: { - repoContentsUrl: '.actionPublishResult.repoContentsUrl', - catalogInfoPath: '"/catalog-info.yaml"', - }, - }, - actionDataFilter: { - toStateData: '.actionCatalogRegisterResult', - }, - }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', - }, - ], - compensatedBy: 'Remove Catalog Info Component', - end: true, - }, - { - name: 'Handle Error', - type: 'operation', - actions: [ - { - name: 'Error Action', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Error on workflow, triggering compensations', - }, - }, - }, - ], - end: { - compensate: true, - }, - }, - { - name: 'Clear File System - Source Code', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Clear FS Action', - functionRef: { - refName: 'fs:delete', - arguments: { - files: ['./'], - }, - }, - }, - ], - }, - { - name: 'Clear File System - CI', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Clear FS Action', - functionRef: { - refName: 'fs:delete', - arguments: { - files: ['./'], - }, - }, - }, - ], - }, - { - name: 'Clear File System - Catalog', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Clear FS Action', - functionRef: { - refName: 'fs:delete', - arguments: { - files: ['./'], - }, - }, - }, - ], - }, - { - name: 'Remove Source Code Repository', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Remove Source Code Repository', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Remove Source Code Repository', - }, - }, - }, - ], - }, - { - name: 'Remove Catalog Info Component', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Remove Catalog Info Component', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Remove Catalog Info Component', - }, - }, - }, - ], - }, - ], - }, - schemaSteps: [ - { - key: 'newcomponent', - readonlyKeys: ['system'], - title: 'Provide information about the new component', - data: { - system: 'system', - }, - schema: { - $id: 'classpath:/schemas/quarkus-backend__ref-schema__New_Component.json', - title: 'Provide information about the new component', - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'object', - properties: { - orgName: { - title: 'Organization Name', - description: 'Organization name', - type: 'string', - }, - repoName: { - title: 'Repository Name', - description: 'Repository name', - type: 'string', - }, - description: { - title: 'Description', - description: 'Help others understand what this component is for', - type: 'string', - }, - owner: { - title: 'Owner', - description: 'An entity from the catalog', - type: 'string', - }, - system: { - title: 'System', - description: 'An entity from the catalog', - type: 'string', - }, - port: { - title: 'Port', - description: 'Override the port exposed for the application', - type: 'number', - default: 8080, - }, - }, - required: ['orgName', 'repoName', 'owner', 'system', 'port'], - }, - }, - { - schema: { - $id: 'classpath:/schemas/quarkus-backend__ref-schema__Java_Metadata.json', - title: 'Provide information about the Java metadata', - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'object', - properties: { - groupId: { - title: 'Group ID', - description: 'Maven Group ID eg (io.janus)', - type: 'string', - default: 'io.janus', - }, - artifactId: { - title: 'Artifact ID', - description: 'Maven Artifact ID', - type: 'string', - default: 'quarkusapp', - }, - javaPackageName: { - title: 'Java Package Namespace', - description: - 'Name for the Java Package (ensure to use the / character as this is used for folder structure) should match Group ID and Artifact ID', - type: 'string', - default: 'io/janus/quarkusapp', - }, - version: { - title: 'Version', - description: 'Maven Artifact Version', - type: 'string', - default: '1.0.0-SNAPSHOT', - }, - }, - required: ['groupId', 'artifactId', 'javaPackageName', 'version'], - }, - title: 'Provide information about the Java metadata', - key: 'javametadata', - data: {}, - readonlyKeys: [], - }, - { - schema: { - $id: 'classpath:/schemas/quarkus-backend__ref-schema__CI_Method.json', - title: 'Provide information about the CI method', - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'object', - properties: { - ci: { - title: 'CI Method', - type: 'string', - default: 'github', - oneOf: [ - { - const: 'github', - title: 'GitHub Action', - }, - { - const: 'tekton', - title: 'Tekton', - }, - ], - }, - }, - allOf: [ - { - if: { - properties: { - ci: { - const: 'github', - }, - }, - }, - }, - { - if: { - properties: { - ci: { - const: 'tekton', - }, - }, - }, - then: { - properties: { - imageRepository: { - title: 'Image Registry', - description: 'The registry to use', - type: 'string', - default: 'quay.io', - oneOf: [ - { - const: 'quay.io', - title: 'Quay', - }, - { - const: - 'image-registry.openshift-image-registry.svc:5000', - title: 'Internal OpenShift Registry', - }, - ], - }, - imageUrl: { - title: 'Image URL', - description: - 'The Quay.io or OpenShift Image URL //', - type: 'string', - }, - namespace: { - title: 'Namespace', - description: 'The namespace for deploying resources', - type: 'string', - }, - }, - required: ['namespace', 'imageUrl', 'imageRepository'], - }, - }, - ], - }, - data: { ci: 'tekton' }, - key: 'ci', - title: 'Ci', - readonlyKeys: [], - }, - ], - isComposedSchema: true, - }; diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDefinition.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowDefinition.ts deleted file mode 100644 index 98d37f597f..0000000000 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDefinition.ts +++ /dev/null @@ -1,244 +0,0 @@ -import { WorkflowDefinition } from '@janus-idp/backstage-plugin-orchestrator-common'; - -export const fakeWorkflowDefinition: WorkflowDefinition = { - id: 'quarkus-backend-workflow-ci-switch', - version: '1.0', - specVersion: '0.8', - name: '[WF] Create a starter Quarkus Backend application with a CI pipeline - CI Switch', - description: - '[WF] Create a starter Quarkus Backend application with a CI pipeline - CI Switch', - functions: [ - { - name: 'runActionTemplateFetch', - operation: 'specs/actions-openapi.json#fetch:template', - }, - { - name: 'runActionPublishGithub', - operation: 'specs/actions-openapi.json#publish:github', - }, - { - name: 'runActionCatalogRegister', - operation: 'specs/actions-openapi.json#catalog:register', - }, - ], - start: 'Generating the Source Code Component', - states: [ - { - name: 'Generating the Source Code Component', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Fetch Template Action - Source Code', - functionRef: { - refName: 'runActionTemplateFetch', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/templates/github/quarkus-backend/skeleton', - values: { - githubOrg: '.githubOrg', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - imageUrl: 'imageUrl', - imageBuilder: '.imageBuilder', - imageRepository: '.imageRepository', - port: '.port', - ci: '.ci', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - }, - }, - }, - actionDataFilter: { - toStateData: '.actionFetchTemplateSourceCodeResult', - }, - }, - ], - transition: 'Generating the CI Component', - }, - { - name: 'Generating the CI Component', - type: 'switch', - dataConditions: [ - { - condition: '${ .ci == "github" }', - transition: 'Generating the CI Component - GitHub', - }, - { - condition: '${ .ci == "tekton" }', - transition: 'Generating the CI Component - Tekton', - }, - ], - defaultCondition: { - transition: 'Generating the CI Component - GitHub', - }, - }, - { - name: 'Generating the CI Component - GitHub', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Run Template Fetch Action - CI - GitHub', - functionRef: { - refName: 'runActionTemplateFetch', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/github-actions', - copyWithoutTemplating: ['".github/workflows/"'], - values: { - githubOrg: '.githubOrg', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - imageUrl: 'imageUrl', - imageBuilder: '.imageBuilder', - imageRepository: '.imageRepository', - port: '.port', - ci: '.ci', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - }, - }, - }, - actionDataFilter: { - toStateData: '.actionTemplateFetchCIResult', - }, - }, - ], - transition: 'Generating the Catalog Info Component', - }, - { - name: 'Generating the CI Component - Tekton', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Run Template Fetch Action - CI - Tekton', - functionRef: { - refName: 'runActionTemplateFetch', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/tekton', - copyWithoutTemplating: ['".tekton/workflows/"'], - values: { - githubOrg: '.githubOrg', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - imageUrl: 'imageUrl', - imageBuilder: '.imageBuilder', - imageRepository: '.imageRepository', - port: '.port', - ci: '.ci', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - }, - }, - }, - actionDataFilter: { - toStateData: '.actionTemplateFetchCIResult', - }, - }, - ], - transition: 'Generating the Catalog Info Component', - }, - { - name: 'Generating the Catalog Info Component', - type: 'operation', - actions: [ - { - name: 'Fetch Template Action - Catalog Info', - functionRef: { - refName: 'runActionTemplateFetch', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/catalog-info', - values: { - githubOrg: '.githubOrg', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - imageUrl: 'imageUrl', - imageBuilder: '.imageBuilder', - imageRepository: '.imageRepository', - port: '.port', - ci: '.ci', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - }, - }, - }, - actionDataFilter: { - toStateData: '.actionFetchTemplateCatalogInfoResult', - }, - }, - ], - transition: 'Publishing to the Source Code Repository', - }, - { - name: 'Publishing to the Source Code Repository', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Publish Github Action', - functionRef: { - refName: 'runActionPublishGithub', - arguments: { - allowedHosts: ['"github.com"'], - description: 'Workflow Action', - repoUrl: - '"github.com?owner=" + .githubOrg + "&repo=" + .repoName', - defaultBranch: '.defaultBranch', - gitCommitMessage: '.gitCommitMessage', - allowAutoMerge: true, - allowRebaseMerge: true, - }, - }, - actionDataFilter: { - toStateData: '.actionPublishResult', - }, - }, - ], - transition: 'Registering the Catalog Info Component', - }, - { - name: 'Registering the Catalog Info Component', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Catalog Register Action', - functionRef: { - refName: 'runActionCatalogRegister', - arguments: { - repoContentsUrl: '.actionPublishResult.repoContentsUrl', - catalogInfoPath: '"/catalog-info.yaml"', - }, - }, - actionDataFilter: { - toStateData: '.actionCatalogRegisterResult', - }, - }, - ], - end: true, - }, - ], - dataInputSchema: - 'schemas/quarkus-backend-workflow-ci-switch__main_schema.json', - annotations: ['workflow-type/ci'], -}; diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowInputSchemaResponse.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowInputSchemaResponse.ts deleted file mode 100644 index f65aef0a42..0000000000 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowInputSchemaResponse.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { WorkflowInputSchemaResponse } from '@janus-idp/backstage-plugin-orchestrator-common'; - -export const fakeDataInputSchemaResponse: WorkflowInputSchemaResponse = { - definition: { - id: 'yamlgreet', - version: '1.0', - specVersion: '0.8', - name: 'Greeting workflow', - description: 'YAML based greeting workflow', - dataInputSchema: 'schemas/yamlgreet__main_schema.json', - start: 'ChooseOnLanguage', - functions: [ - { - name: 'greetFunction', - type: 'custom', - operation: 'sysout', - }, - ], - states: [ - { - name: 'ChooseOnLanguage', - type: 'switch', - dataConditions: [ - { - condition: '${ .language == "English" }', - transition: 'GreetInEnglish', - }, - { - condition: '${ .language == "Spanish" }', - transition: 'GreetInSpanish', - }, - ], - defaultCondition: { - transition: 'GreetInEnglish', - }, - }, - { - name: 'GreetInEnglish', - type: 'inject', - data: { - greeting: 'Hello from YAML Workflow, ', - }, - transition: 'GreetPerson', - }, - { - name: 'GreetInSpanish', - type: 'inject', - data: { - greeting: 'Saludos desde YAML Workflow, ', - }, - transition: 'GreetPerson', - }, - { - name: 'GreetPerson', - type: 'operation', - actions: [ - { - name: 'greetAction', - functionRef: { - refName: 'greetFunction', - arguments: { - message: '.greeting+.name', - }, - }, - }, - ], - end: { - terminate: true, - }, - }, - ], - }, - schemaSteps: [ - { - readonlyKeys: [], - data: {}, - key: 'yamlgreet', - title: 'yamlgreet: Additional input data', - schema: { - $id: 'classpath:/schemas/yamlgreet__sub_schema__Additional_input_data.json', - title: 'yamlgreet: Additional input data', - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'object', - required: ['language'], - properties: { - language: { - title: 'language', - type: 'string', - pattern: 'Spanish|English', - description: 'Extracted from the Workflow definition', - default: 'English', - }, - }, - }, - }, - ], - isComposedSchema: false, -}; diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowOverview.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowOverview.ts deleted file mode 100644 index c0382106d7..0000000000 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowOverview.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { WorkflowOverview } from '@janus-idp/backstage-plugin-orchestrator-common'; - -export const fakeWorkflowOverview: WorkflowOverview = { - workflowId: 'quarkus-backend-workflow-ci-switch', - name: '[WF] Create a starter Quarkus Backend application with a CI pipeline - CI Switch', - lastTriggeredMs: 1697276096000, - lastRunStatus: 'COMPLETED', - category: 'ci', - avgDurationMs: 150000, - description: - 'Create a starter Quarkus Backend application with a CI pipeline', - format: 'yaml', -}; diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowOverviewList.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowOverviewList.ts deleted file mode 100644 index 3e9d3908af..0000000000 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowOverviewList.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { - PaginationInfoDTOOrderDirectionEnum, - WorkflowCategoryDTO, - WorkflowOverviewListResultDTO, -} from '@janus-idp/backstage-plugin-orchestrator-common'; - -export const fakeWorkflowOverviewList: WorkflowOverviewListResultDTO = { - overviews: [ - { - workflowId: 'quarkus-backend-workflow-ci-switch', - name: '[WF] Create a starter Quarkus Backend application with a CI pipeline - CI Switch', - format: 'yaml', - lastTriggeredMs: 1701765793, - category: WorkflowCategoryDTO.Infrastructure, - avgDurationMs: 5000, - lastRunStatus: 'COMPLETED', - description: - '[WF] Create a starter Quarkus Backend application with a CI pipeline - CI Switch', - }, - { - workflowId: 'orchestrator-ansible-job-parallel-error-handler', - name: '[WF] Ansible Job - Parallel/ERROR', - format: 'yaml', - lastTriggeredMs: 1701765793, - category: WorkflowCategoryDTO.Infrastructure, - avgDurationMs: 5000, - lastRunStatus: 'SUSPENDED', - description: - '[WF] Launch an Ansible Job within Ansible Automation Platform - ERROR Handling', - }, - { - workflowId: 'orchestrator-ansible-job', - name: '[WF] Ansible Job', - format: 'yaml', - lastTriggeredMs: 1701765793, - category: WorkflowCategoryDTO.Infrastructure, - avgDurationMs: 5000, - lastRunStatus: 'ERROR', - description: - '[WF] Launch an Ansible Job within Ansible Automation Platform', - }, - { - workflowId: 'quarkus-backend-workflow-extended', - name: '[WF] Create a Quarkus Backend application with a CI pipeline - Extended', - format: 'yaml', - lastTriggeredMs: 1701765793, - category: WorkflowCategoryDTO.Infrastructure, - avgDurationMs: 5000, - lastRunStatus: 'SUSPENDED', - description: - '[WF] Create a starter Quarkus Backend application with a CI pipeline - Extended', - }, - { - workflowId: 'workflow_actions', - name: 'Workflow name', - format: 'yaml', - lastTriggeredMs: 1701765793, - category: WorkflowCategoryDTO.Infrastructure, - avgDurationMs: 5000, - lastRunStatus: 'COMPLETED', - description: 'Workflow description', - }, - { - workflowId: 'yamlgreet', - name: 'Greeting workflow', - format: 'yaml', - lastTriggeredMs: 1701765793, - category: WorkflowCategoryDTO.Infrastructure, - avgDurationMs: 5000, - lastRunStatus: 'ERROR', - description: 'YAML based greeting workflow', - }, - ], - paginationInfo: { - offset: 1, - pageSize: 10, - orderDirection: PaginationInfoDTOOrderDirectionEnum.Asc, - totalCount: 9, - }, -}; diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowRunDetails.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowRunDetails.ts deleted file mode 100644 index 1bbd127769..0000000000 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowRunDetails.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { WorkflowRunDetail } from '../components/WorkflowRunDetail'; - -export const fakeWorkflowRunDetail: WorkflowRunDetail = { - id: '1691fab4-45ce-44d8-840d-5573dfe809b8', - name: 'Assessment Workflow', - workflowId: 'assessment', - started: '2/13/2024, 10:58:26 AM', - duration: 'a few seconds', - category: 'assessment', - status: 'COMPLETED', - description: 'Simple workflow to simulate assessment', -}; diff --git a/plugins/orchestrator/src/__fixtures__/veryLongString.ts b/plugins/orchestrator/src/__fixtures__/veryLongString.ts deleted file mode 100644 index 7753177aa5..0000000000 --- a/plugins/orchestrator/src/__fixtures__/veryLongString.ts +++ /dev/null @@ -1,61 +0,0 @@ -export const veryLongString = ` -2 cups all-purpose flour -1 1/2 teaspoons baking powder -1/2 teaspoon baking soda -1/2 teaspoon salt -1 teaspoon ground cinnamon -1/2 teaspoon ground nutmeg -1/2 cup unsalted butter, softened -1 cup granulated sugar -1/2 cup brown sugar, packed -2 large eggs -2 teaspoons vanilla extract -1/2 cup sour cream -2 cups apples, peeled and diced (such as Granny Smith) -1/2 cup chopped nuts (optional) -Powdered sugar for dusting (optional) -Instructions: - -Preheat your oven to 350°F (175°C). Grease and flour a 9x13-inch baking pan. -In a medium bowl, whisk together the flour, baking powder, baking soda, salt, cinnamon, and nutmeg. Set aside. -In a large bowl, cream together the softened butter, granulated sugar, and brown sugar until light and fluffy. -Beat in the eggs one at a time, then stir in the vanilla extract. -Gradually add the dry ingredients to the wet ingredients, mixing until just combined. -Fold in the sour cream, followed by the diced apples and nuts (if using). -Spread the batter evenly in the prepared baking pan. -Bake for 40-45 minutes or until a toothpick inserted into the center comes out clean. -Allow the cake to cool in the pan for 10 minutes, then transfer it to a wire rack to cool completely. -Optionally, dust the cooled cake with powdered sugar before serving. -Enjoy your delicious homemade apple cake! - - -Ingredients: - -2 cups all-purpose flour -1 1/2 teaspoons baking powder -1/2 teaspoon baking soda -1/2 teaspoon salt -1 teaspoon ground cinnamon -1/2 teaspoon ground nutmeg -1/2 cup unsalted butter, softened -1 cup granulated sugar -1/2 cup brown sugar, packed -2 large eggs -2 teaspoons vanilla extract -1/2 cup sour cream -2 cups apples, peeled and diced (such as Granny Smith) -1/2 cup chopped nuts (optional) -Powdered sugar for dusting (optional) -Instructions: - -Preheat your oven to 350°F (175°C). Grease and flour a 9x13-inch baking pan. -In a medium bowl, whisk together the flour, baking powder, baking soda, salt, cinnamon, and nutmeg. Set aside. -In a large bowl, cream together the softened butter, granulated sugar, and brown sugar until light and fluffy. -Beat in the eggs one at a time, then stir in the vanilla extract. -Gradually add the dry ingredients to the wet ingredients, mixing until just combined. -Fold in the sour cream, followed by the diced apples and nuts (if using). -Spread the batter evenly in the prepared baking pan. -Bake for 40-45 minutes or until a toothpick inserted into the center comes out clean. -Allow the cake to cool in the pan for 10 minutes, then transfer it to a wire rack to cool completely. -Optionally, dust the cooled cake with powdered sugar before serving. -Enjoy your delicious homemade apple cake!`; diff --git a/plugins/orchestrator/src/api/MockOrchestratorClient.ts b/plugins/orchestrator/src/api/MockOrchestratorClient.ts deleted file mode 100644 index 700771e8ae..0000000000 --- a/plugins/orchestrator/src/api/MockOrchestratorClient.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { JsonObject } from '@backstage/types'; - -import { AxiosResponse } from 'axios'; - -import { - AssessedProcessInstanceDTO, - ExecuteWorkflowResponseDTO, - ProcessInstanceListResultDTO, - WorkflowDefinition, - WorkflowExecutionResponse, - WorkflowInputSchemaResponse, - WorkflowOverviewDTO, - WorkflowOverviewListResultDTO, -} from '@janus-idp/backstage-plugin-orchestrator-common'; - -import { hasOwnProp, isNonNullable } from '../utils/TypeGuards'; -import { OrchestratorApi } from './api'; - -export interface MockOrchestratorApiData { - executeWorkflowResponse: () => ReturnType; - getInstanceResponse: () => ReturnType; - retriggerInstanceInErrorResponse: () => ReturnType< - OrchestratorApi['retriggerInstanceInError'] - >; - abortWorkflowInstanceResponse: () => ReturnType< - OrchestratorApi['abortWorkflowInstance'] - >; - listInstancesResponse: ReturnType; - getWorkflowDefinitionResponse: ReturnType< - OrchestratorApi['getWorkflowDefinition'] - >; - getWorkflowSourceResponse: ReturnType; - getWorkflowDataInputSchemaResponse: ReturnType< - OrchestratorApi['getWorkflowDataInputSchema'] - >; - listWorkflowOverviewsResponse: ReturnType< - OrchestratorApi['listWorkflowOverviews'] - >; - getWorkflowOverviewResponse: ReturnType< - OrchestratorApi['getWorkflowOverview'] - >; -} - -export class MockOrchestratorClient implements OrchestratorApi { - private _mockData: Partial; - - constructor(mockData: Partial = {}) { - this._mockData = mockData; - } - - executeWorkflow(_args: { - workflowId: string; - parameters: JsonObject; - }): Promise> { - if ( - !hasOwnProp(this._mockData, 'executeWorkflowResponse') || - !isNonNullable(this._mockData.executeWorkflowResponse) - ) { - throw new Error(`[executeWorkflow]: No mock data available`); - } - - return this._mockData.executeWorkflowResponse(); - } - - getInstance( - _instanceId: string, - _includeAssessment: boolean, - ): Promise> { - if ( - !hasOwnProp(this._mockData, 'getInstanceResponse') || - !isNonNullable(this._mockData.getInstanceResponse) - ) { - throw new Error(`[getInstance]: No mock data available`); - } - - return Promise.resolve(this._mockData.getInstanceResponse()); - } - - listInstances(): Promise> { - if ( - !hasOwnProp(this._mockData, 'listInstancesResponse') || - !isNonNullable(this._mockData.listInstancesResponse) - ) { - throw new Error(`[listInstances]: No mock data available`); - } - - return Promise.resolve(this._mockData.listInstancesResponse); - } - - getWorkflowSource(_workflowId: string): Promise { - if ( - !hasOwnProp(this._mockData, 'getWorkflowSourceResponse') || - !isNonNullable(this._mockData.getWorkflowSourceResponse) - ) { - throw new Error(`[getWorkflowSource]: No mock data available`); - } - - return Promise.resolve(this._mockData.getWorkflowSourceResponse); - } - - getWorkflowDefinition(_workflowId: string): Promise { - if ( - !hasOwnProp(this._mockData, 'getWorkflowDefinitionResponse') || - !isNonNullable(this._mockData.getWorkflowDefinitionResponse) - ) { - throw new Error(`[getWorkflowDefinition]: No mock data available`); - } - - return Promise.resolve(this._mockData.getWorkflowDefinitionResponse); - } - - getWorkflowDataInputSchema(_args: { - workflowId: string; - instanceId?: string; - assessmentInstanceId?: string; - }): Promise { - if ( - !hasOwnProp(this._mockData, 'getWorkflowDataInputSchemaResponse') || - !isNonNullable(this._mockData.getWorkflowDataInputSchemaResponse) - ) { - throw new Error(`[getWorkflowDataInputSchema]: No mock data available`); - } - - return Promise.resolve(this._mockData.getWorkflowDataInputSchemaResponse); - } - - listWorkflowOverviews(): Promise< - AxiosResponse - > { - if ( - !hasOwnProp(this._mockData, 'listWorkflowOverviewsResponse') || - !isNonNullable(this._mockData.listWorkflowOverviewsResponse) - ) { - throw new Error(`[listWorkflowOverviews]: No mock data available`); - } - - return Promise.resolve(this._mockData.listWorkflowOverviewsResponse); - } - - getWorkflowOverview(): Promise> { - if ( - !hasOwnProp(this._mockData, 'getWorkflowOverviewResponse') || - !isNonNullable(this._mockData.getWorkflowOverviewResponse) - ) { - throw new Error(`[getWorkflowOverview]: No mock data available`); - } - - return Promise.resolve(this._mockData.getWorkflowOverviewResponse); - } - - abortWorkflowInstance(_instanceId: string): Promise> { - if ( - !hasOwnProp(this._mockData, 'abortWorkflowInstanceResponse') || - !isNonNullable(this._mockData.abortWorkflowInstanceResponse) - ) { - throw new Error(`[abortWorkflowInstance]: No mock data available`); - } - - return this._mockData.abortWorkflowInstanceResponse(); - } - - retriggerInstanceInError(_args: { - instanceId: string; - inputData: JsonObject; - }): Promise { - if ( - !hasOwnProp(this._mockData, 'retriggerInstanceInErrorResponse') || - !isNonNullable(this._mockData.retriggerInstanceInErrorResponse) - ) { - throw new Error(`[retriggerInstanceInError]: No mock data available`); - } - - return this._mockData.retriggerInstanceInErrorResponse(); - } -} diff --git a/plugins/orchestrator/src/api/OrchestratorClient.test.ts b/plugins/orchestrator/src/api/OrchestratorClient.test.ts index 4c70cc4bdf..3d1fae7126 100644 --- a/plugins/orchestrator/src/api/OrchestratorClient.test.ts +++ b/plugins/orchestrator/src/api/OrchestratorClient.test.ts @@ -197,7 +197,7 @@ describe('OrchestratorClient', () => { expect(result.data).toEqual(instanceId); expect(axios.request).toHaveBeenCalledTimes(1); expect(axios.request).toHaveBeenCalledWith({ - ...getAxiosTestRequest(`/v2/instances/${instanceId}/abort`), + ...getAxiosTestRequest(`/v2/workflows/instances/${instanceId}/abort`), method: 'DELETE', headers: { ...defaultAuthHeaders, @@ -270,35 +270,54 @@ describe('OrchestratorClient', () => { // Given const workflowId = 'workflow123'; const mockWorkflowSource = 'test workflow source'; + const responseConfigOptions = getDefaultTestRequestConfig(); + responseConfigOptions.responseType = 'text'; + const mockResponse: AxiosResponse = { + data: mockWorkflowSource, + status: 200, + statusText: 'OK', + headers: {} as RawAxiosResponseHeaders, + config: {} as InternalAxiosRequestConfig, + }; + // Mock axios request to simulate a successful response + jest.spyOn(axios, 'request').mockResolvedValueOnce(mockResponse); - // Mock fetch to simulate a successful response - (global.fetch as jest.Mock).mockResolvedValueOnce({ - ok: true, - text: jest.fn().mockResolvedValue(mockWorkflowSource), - }); + // Spy DefaultApi + const getSourceSpy = jest.spyOn( + DefaultApi.prototype, + 'getWorkflowSourceById', + ); // When const result = await orchestratorClient.getWorkflowSource(workflowId); // Then - expect(fetch).toHaveBeenCalledWith( - `${baseUrl}/workflows/${workflowId}/source`, - { headers: defaultAuthHeaders }, + expect(result).toBeDefined(); + expect(result.data).toEqual(mockWorkflowSource); + expect(axios.request).toHaveBeenCalledTimes(1); + expect(axios.request).toHaveBeenCalledWith({ + ...getAxiosTestRequest(`/v2/workflows/${workflowId}/source`), + method: 'GET', + headers: { + ...defaultAuthHeaders, + }, + responseType: 'text', + }); + expect(getSourceSpy).toHaveBeenCalledTimes(1); + expect(getSourceSpy).toHaveBeenCalledWith( + workflowId, + responseConfigOptions, ); - expect(result).toEqual(mockWorkflowSource); }); it('should throw a ResponseError when fetching the workflow source fails', async () => { // Given const workflowId = 'workflow123'; - // Mock fetch to simulate a failed response - (global.fetch as jest.Mock).mockResolvedValueOnce({ - ok: false, - status: 404, - statusText: 'Not Found', - }); - + // Mock fetch to simulate a failure + axios.request = jest + .fn() + .mockRejectedValueOnce(new Error('Simulated error')); // When const promise = orchestratorClient.getWorkflowSource(workflowId); @@ -517,56 +536,6 @@ describe('OrchestratorClient', () => { await expect(promise).rejects.toThrow(); }); }); - describe('getWorkflowDataInputSchema', () => { - it('should return workflow input schema when successful', async () => { - // Given - const workflowId = 'workflow123'; - const instanceId = 'instance123'; - const assessmentInstanceId = 'assessment123'; - const mockInputSchema = { id: 'schemaId', name: 'schemaName' }; - - // Mock fetch to simulate a successful response - (global.fetch as jest.Mock).mockResolvedValueOnce({ - ok: true, - json: jest.fn().mockResolvedValue(mockInputSchema), - }); - - // When - const result = await orchestratorClient.getWorkflowDataInputSchema({ - workflowId, - instanceId, - assessmentInstanceId, - }); - - // Then - const expectedEndpoint = `${baseUrl}/workflows/${workflowId}/inputSchema?instanceId=${instanceId}&assessmentInstanceId=${assessmentInstanceId}`; - - expect(fetch).toHaveBeenCalledWith(expectedEndpoint, { - headers: defaultAuthHeaders, - }); - expect(result).toEqual(mockInputSchema); - }); - - it('should throw a ResponseError when fetching the workflow input schema fails', async () => { - // Given - const workflowId = 'workflow123'; - - // Mock fetch to simulate a failed response - (global.fetch as jest.Mock).mockResolvedValueOnce({ - ok: false, - status: 500, - statusText: 'Internal Server Error', - }); - - // When - const promise = orchestratorClient.getWorkflowDataInputSchema({ - workflowId, - }); - - // Then - await expect(promise).rejects.toThrow(); - }); - }); describe('getWorkflowOverview', () => { it('should return workflow overview when successful', async () => { // Given @@ -628,61 +597,6 @@ describe('OrchestratorClient', () => { await expect(promise).rejects.toThrow(); }); }); - describe('retriggerInstanceInError', () => { - it('should retrigger instance when successful', async () => { - // Given - const instanceId = 'instance123'; - const inputData = { key: 'value' }; - const mockResponse = { id: 'newInstanceId', status: 'running' }; - - // Mock fetch to simulate a successful response - (global.fetch as jest.Mock).mockResolvedValueOnce({ - ok: true, - json: jest.fn().mockResolvedValue(mockResponse), - }); - - // When - const result = await orchestratorClient.retriggerInstanceInError({ - instanceId, - inputData, - }); - - // Then - const expectedUrlToFetch = `${baseUrl}/instances/${instanceId}/retrigger`; - expect(fetch).toHaveBeenCalledWith(expectedUrlToFetch, { - method: 'POST', - body: JSON.stringify(inputData), - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${mockToken}`, - }, - }); - expect(result).toEqual(mockResponse); - }); - - it('should throw a ResponseError when retriggering instance fails', async () => { - // Given - const instanceId = 'instance123'; - const inputData = { key: 'value' }; - - // Mock fetch to simulate a failed response - (global.fetch as jest.Mock).mockResolvedValueOnce({ - ok: false, - status: 500, - statusText: 'Internal Server Error', - }); - - // When - const promise = orchestratorClient.retriggerInstanceInError({ - instanceId, - inputData, - }); - - // Then - await expect(promise).rejects.toThrow(); - }); - }); - function getDefaultTestRequestConfig(): AxiosRequestConfig { return { baseURL: baseUrl, diff --git a/plugins/orchestrator/src/api/OrchestratorClient.ts b/plugins/orchestrator/src/api/OrchestratorClient.ts index 782375c254..d84e51e985 100644 --- a/plugins/orchestrator/src/api/OrchestratorClient.ts +++ b/plugins/orchestrator/src/api/OrchestratorClient.ts @@ -1,11 +1,11 @@ import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; -import { ResponseError } from '@backstage/errors'; -import { JsonObject } from '@backstage/types'; +import type { JsonObject } from '@backstage/types'; import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, + isAxiosError, RawAxiosRequestHeaders, } from 'axios'; @@ -14,21 +14,30 @@ import { Configuration, DefaultApi, ExecuteWorkflowResponseDTO, - FilterInfo, + Filter, + GetInstancesRequest, + InputSchemaResponseDTO, PaginationInfoDTO, ProcessInstanceListResultDTO, - QUERY_PARAM_ASSESSMENT_INSTANCE_ID, - QUERY_PARAM_INSTANCE_ID, WorkflowDefinition, - WorkflowExecutionResponse, - WorkflowInputSchemaResponse, WorkflowOverviewDTO, WorkflowOverviewListResultDTO, } from '@janus-idp/backstage-plugin-orchestrator-common'; -import { buildUrl } from '../utils/UrlUtils'; import { OrchestratorApi } from './api'; +const getError = (err: unknown): Error => { + if ( + isAxiosError<{ error: { message: string; name: string } }>(err) && + err.response?.data?.error?.message + ) { + const error = new Error(err.response?.data?.error?.message); + error.name = err.response?.data?.error?.name || 'Error'; + return error; + } + return err as Error; +}; + export interface OrchestratorClientOptions { discoveryApi: DiscoveryApi; identityApi: IdentityApi; @@ -82,11 +91,15 @@ export class OrchestratorClient implements OrchestratorApi { const defaultApi = await this.getDefaultAPI(); const reqConfigOption: AxiosRequestConfig = await this.getDefaultReqConfig(); - return await defaultApi.executeWorkflow( - args.workflowId, - { inputData: args.parameters }, - reqConfigOption, - ); + try { + return await defaultApi.executeWorkflow( + args.workflowId, + { inputData: args.parameters }, + reqConfigOption, + ); + } catch (err) { + throw getError(err); + } } async abortWorkflowInstance( @@ -95,44 +108,67 @@ export class OrchestratorClient implements OrchestratorApi { const defaultApi = await this.getDefaultAPI(); const reqConfigOption: AxiosRequestConfig = await this.getDefaultReqConfig(); - return await defaultApi.abortWorkflow(instanceId, reqConfigOption); + try { + return await defaultApi.abortWorkflow(instanceId, reqConfigOption); + } catch (err) { + throw getError(err); + } } async getWorkflowDefinition(workflowId: string): Promise { const baseUrl = await this.getBaseUrl(); - return await this.fetcher(`${baseUrl}/workflows/${workflowId}`).then(r => - r.json(), - ); + try { + return await this.fetcher(`${baseUrl}/workflows/${workflowId}`).then(r => + r.json(), + ); + } catch (err) { + throw getError(err); + } } - async getWorkflowSource(workflowId: string): Promise { - const baseUrl = await this.getBaseUrl(); - return await this.fetcher(`${baseUrl}/workflows/${workflowId}/source`).then( - r => r.text(), - ); + async getWorkflowSource(workflowId: string): Promise> { + const defaultApi = await this.getDefaultAPI(); + const reqConfigOption: AxiosRequestConfig = + await this.getDefaultReqConfig(); + reqConfigOption.responseType = 'text'; + try { + return await defaultApi.getWorkflowSourceById( + workflowId, + reqConfigOption, + ); + } catch (err) { + throw getError(err); + } } async listWorkflowOverviews( paginationInfo?: PaginationInfoDTO, - filterInfo?: FilterInfo, + filters?: Filter, ): Promise> { const defaultApi = await this.getDefaultAPI(); const reqConfigOption: AxiosRequestConfig = await this.getDefaultReqConfig(); - return await defaultApi.getWorkflowsOverview( - { paginationInfo, filterInfo }, - reqConfigOption, - ); + try { + return await defaultApi.getWorkflowsOverview( + { paginationInfo, filters }, + reqConfigOption, + ); + } catch (err) { + throw getError(err); + } } - async listInstances(args: { - paginationInfo?: PaginationInfoDTO; - filterInfo?: FilterInfo; - }): Promise> { + async listInstances( + args: GetInstancesRequest, + ): Promise> { const defaultApi = await this.getDefaultAPI(); const reqConfigOption: AxiosRequestConfig = await this.getDefaultReqConfig(); - return await defaultApi.getInstances(args, reqConfigOption); + try { + return await defaultApi.getInstances(args, reqConfigOption); + } catch (err) { + throw getError(err); + } } async getInstance( @@ -148,23 +184,27 @@ export class OrchestratorClient implements OrchestratorApi { includeAssessment, reqConfigOption, ); - } catch (error: any) { - throw new Error(error); + } catch (err) { + throw getError(err); } } - async getWorkflowDataInputSchema(args: { - workflowId: string; - instanceId?: string; - assessmentInstanceId?: string; - }): Promise { - const baseUrl = await this.getBaseUrl(); - const endpoint = `${baseUrl}/workflows/${args.workflowId}/inputSchema`; - const urlToFetch = buildUrl(endpoint, { - [QUERY_PARAM_INSTANCE_ID]: args.instanceId, - [QUERY_PARAM_ASSESSMENT_INSTANCE_ID]: args.assessmentInstanceId, - }); - return await this.fetcher(urlToFetch).then(r => r.json()); + async getWorkflowDataInputSchema( + workflowId: string, + instanceId?: string, + ): Promise> { + const defaultApi = await this.getDefaultAPI(); + const reqConfigOption: AxiosRequestConfig = + await this.getDefaultReqConfig(); + try { + return await defaultApi.getWorkflowInputSchemaById( + workflowId, + instanceId, + reqConfigOption, + ); + } catch (err) { + throw getError(err); + } } async getWorkflowOverview( @@ -173,23 +213,14 @@ export class OrchestratorClient implements OrchestratorApi { const defaultApi = await this.getDefaultAPI(); const reqConfigOption: AxiosRequestConfig = await this.getDefaultReqConfig(); - return await defaultApi.getWorkflowOverviewById( - workflowId, - reqConfigOption, - ); - } - - async retriggerInstanceInError(args: { - instanceId: string; - inputData: JsonObject; - }): Promise { - const baseUrl = await this.getBaseUrl(); - const urlToFetch = `${baseUrl}/instances/${args.instanceId}/retrigger`; - return await this.fetcher(urlToFetch, { - method: 'POST', - body: JSON.stringify(args.inputData), - headers: { 'Content-Type': 'application/json' }, - }).then(r => r.json()); + try { + return await defaultApi.getWorkflowOverviewById( + workflowId, + reqConfigOption, + ); + } catch (err) { + throw getError(err); + } } /** fetcher is convenience fetch wrapper that includes authentication @@ -205,9 +236,6 @@ export class OrchestratorClient implements OrchestratorApi { ...(idToken && { Authorization: `Bearer ${idToken}` }), }; const response = await fetch(url, r); - if (!response.ok) { - throw await ResponseError.fromResponse(response); - } return response; } diff --git a/plugins/orchestrator/src/api/api.ts b/plugins/orchestrator/src/api/api.ts index 73f344d079..12bd21a602 100644 --- a/plugins/orchestrator/src/api/api.ts +++ b/plugins/orchestrator/src/api/api.ts @@ -6,12 +6,10 @@ import { AxiosResponse } from 'axios'; import { AssessedProcessInstanceDTO, ExecuteWorkflowResponseDTO, - FilterInfo, - PaginationInfoDTO, + GetInstancesRequest, + InputSchemaResponseDTO, ProcessInstanceListResultDTO, WorkflowDefinition, - WorkflowExecutionResponse, - WorkflowInputSchemaResponse, WorkflowOverviewDTO, WorkflowOverviewListResultDTO, } from '@janus-idp/backstage-plugin-orchestrator-common'; @@ -25,25 +23,19 @@ export interface OrchestratorApi { businessKey?: string; }): Promise>; - retriggerInstanceInError(args: { - instanceId: string; - inputData: JsonObject; - }): Promise; - getWorkflowDefinition(workflowId: string): Promise; - getWorkflowSource(workflowId: string): Promise; + getWorkflowSource(workflowId: string): Promise>; getInstance( instanceId: string, includeAssessment: boolean, ): Promise>; - getWorkflowDataInputSchema(args: { - workflowId: string; - instanceId?: string; - assessmentInstanceId?: string; - }): Promise; + getWorkflowDataInputSchema( + workflowId: string, + instanceId?: string, + ): Promise>; getWorkflowOverview( workflowId: string, @@ -53,10 +45,9 @@ export interface OrchestratorApi { AxiosResponse >; - listInstances(args?: { - paginationInfo?: PaginationInfoDTO; - filterInfo?: FilterInfo; - }): Promise>; + listInstances( + args?: GetInstancesRequest, + ): Promise>; } export const orchestratorApiRef = createApiRef({ diff --git a/plugins/orchestrator/src/components/ExecuteWorkflowPage/ExecuteWorkflowPage.tsx b/plugins/orchestrator/src/components/ExecuteWorkflowPage/ExecuteWorkflowPage.tsx index fa568ed0b6..0aaf450316 100644 --- a/plugins/orchestrator/src/components/ExecuteWorkflowPage/ExecuteWorkflowPage.tsx +++ b/plugins/orchestrator/src/components/ExecuteWorkflowPage/ExecuteWorkflowPage.tsx @@ -18,10 +18,9 @@ import { JsonObject } from '@backstage/types'; import { Grid } from '@material-ui/core'; import { + InputSchemaResponseDTO, QUERY_PARAM_ASSESSMENT_INSTANCE_ID, QUERY_PARAM_INSTANCE_ID, - QUERY_PARAM_INSTANCE_STATE, - WorkflowInputSchemaResponse, } from '@janus-idp/backstage-plugin-orchestrator-common'; import { OrchestratorForm } from '@janus-idp/backstage-plugin-orchestrator-form-react'; @@ -43,35 +42,33 @@ export const ExecuteWorkflowPage = () => { const [assessmentInstanceId] = useQueryParamState( QUERY_PARAM_ASSESSMENT_INSTANCE_ID, ); - const [instanceState] = useQueryParamState( - QUERY_PARAM_INSTANCE_STATE, - ); const navigate = useNavigate(); const instanceLink = useRouteRef(workflowInstanceRouteRef); const { - value: schemaResponse, + value, loading, error: responseError, - } = useAsync( - async (): Promise => - await orchestratorApi.getWorkflowDataInputSchema({ - workflowId, - instanceId, - assessmentInstanceId, - }), - [orchestratorApi, workflowId], - ); + } = useAsync(async (): Promise => { + const res = await orchestratorApi.getWorkflowDataInputSchema( + workflowId, + assessmentInstanceId || instanceId, + ); + return res.data; + }, [orchestratorApi, workflowId]); + const schema = value?.inputSchema; + const data = value?.data; + const { + value: workflowName, + loading: workflowNameLoading, + error: workflowNameError, + } = useAsync(async (): Promise => { + const res = await orchestratorApi.getWorkflowOverview(workflowId); + return res.data.name || ''; + }, [orchestratorApi, workflowId]); const handleExecute = useCallback( - async (getParameters: () => JsonObject) => { + async (parameters: JsonObject) => { setUpdateError(undefined); - let parameters: JsonObject = {}; - try { - parameters = getParameters(); - } catch (err) { - setUpdateError(getErrorObject(err)); - return; - } try { setIsExecuting(true); const response = await orchestratorApi.executeWorkflow({ @@ -89,57 +86,13 @@ export const ExecuteWorkflowPage = () => { [orchestratorApi, workflowId, navigate, instanceLink, assessmentInstanceId], ); - const isErrorState = React.useMemo( - () => instanceState === 'ERROR', - [instanceState], - ); - - const handleRetrigger = useCallback( - async (getParameters: () => JsonObject) => { - setUpdateError(undefined); - let parameters: JsonObject = {}; - try { - parameters = getParameters(); - } catch (err) { - setUpdateError(getErrorObject(err)); - return; - } - if (instanceId) { - try { - setIsExecuting(true); - const response = await orchestratorApi.retriggerInstanceInError({ - instanceId, - inputData: parameters, - }); - navigate(instanceLink({ instanceId: response.id })); - } catch (err) { - setUpdateError(getErrorObject(err)); - } finally { - setIsExecuting(false); - } - } - }, - [orchestratorApi, instanceId, navigate, instanceLink], - ); - - const onReset = useCallback(() => { - setUpdateError(undefined); - }, [setUpdateError]); - + const error = responseError || workflowNameError; let pageContent; - if (loading) { + if (loading || workflowNameLoading) { pageContent = ; - } else if (responseError) { - pageContent = ; - } else if (!schemaResponse) { - pageContent = ( - - ); + } else if (error) { + pageContent = ; } else { pageContent = ( @@ -148,30 +101,19 @@ export const ExecuteWorkflowPage = () => { )} - {schemaResponse.schemaParseError && ( - - - - )} - {schemaResponse.schemaSteps.length > 0 ? ( + {!!schema ? ( ) : ( )} @@ -183,8 +125,8 @@ export const ExecuteWorkflowPage = () => { return ( diff --git a/plugins/orchestrator/src/components/ExecuteWorkflowPage/JsonTextAreaForm.tsx b/plugins/orchestrator/src/components/ExecuteWorkflowPage/JsonTextAreaForm.tsx index ea9e617dcc..3d6ea75d17 100644 --- a/plugins/orchestrator/src/components/ExecuteWorkflowPage/JsonTextAreaForm.tsx +++ b/plugins/orchestrator/src/components/ExecuteWorkflowPage/JsonTextAreaForm.tsx @@ -15,7 +15,7 @@ const JsonTextAreaForm = ({ handleExecute, }: { isExecuting: boolean; - handleExecute: (getParameters: () => JsonObject) => Promise; + handleExecute: (parameters: JsonObject) => Promise; }) => { const [jsonText, setJsonText] = React.useState(DEFAULT_VALUE); const theme = useTheme(); @@ -56,7 +56,7 @@ const JsonTextAreaForm = ({ handleExecute(getParameters)} + handleClick={() => handleExecute(getParameters())} > Run diff --git a/plugins/orchestrator/src/components/WorkflowDescriptionModal.tsx b/plugins/orchestrator/src/components/WorkflowDescriptionModal.tsx index 0a84d7aea0..913fb19ba0 100644 --- a/plugins/orchestrator/src/components/WorkflowDescriptionModal.tsx +++ b/plugins/orchestrator/src/components/WorkflowDescriptionModal.tsx @@ -18,6 +18,10 @@ import { WorkflowOverviewDTO } from '@janus-idp/backstage-plugin-orchestrator-co export type WorkflowDescriptionModalProps = { workflow: WorkflowOverviewDTO; + workflowError?: { + itemId: string; + error: any; + }; runWorkflowLink: string; open: boolean; onClose?: () => void; @@ -37,7 +41,13 @@ export const RefForwardingWorkflowDescriptionModal: ForwardRefRenderFunction< ParentComponentRef, WorkflowDescriptionModalProps > = (props, forwardedRef): JSX.Element | null => { - const { workflow, open = false, onClose, runWorkflowLink } = props; + const { + workflow, + open = false, + onClose, + runWorkflowLink, + workflowError, + } = props; const classes = useStyles(); const navigate = useNavigate(); @@ -47,6 +57,27 @@ export const RefForwardingWorkflowDescriptionModal: ForwardRefRenderFunction< } }; + let content; + if (workflowError) { + content = ( + +

    + Failed to load details for the workflow ID: + {workflowError.itemId} +

    + {workflowError.error.message &&

    {workflowError.error.message}

    } +
    + ); + } else if (workflow.description) { + content = {workflow.description}; + } else { + content = ( + +

    Are you sure you want to run this workflow?

    +
    + ); + } + return ( onClose} @@ -67,17 +98,15 @@ export const RefForwardingWorkflowDescriptionModal: ForwardRefRenderFunction< - - {workflow.description ? ( - {workflow.description} - ) : ( - -

    Are you sure you want to run this workflow?

    -
    - )} -
    + + {content} - - -
    - )} - {canAbort && ( - - + + - - - )} - {canRerun && ( - - + + + + + + - - - )} + Rerun + + +
    diff --git a/plugins/orchestrator/src/components/WorkflowInstancePageContent.tsx b/plugins/orchestrator/src/components/WorkflowInstancePageContent.tsx index 10d0657a38..b13113b8bf 100644 --- a/plugins/orchestrator/src/components/WorkflowInstancePageContent.tsx +++ b/plugins/orchestrator/src/components/WorkflowInstancePageContent.tsx @@ -1,32 +1,20 @@ import React from 'react'; -import { Content, InfoCard, Link } from '@backstage/core-components'; -import { - PathParams, - RouteFunc, - useApi, - useRouteRef, -} from '@backstage/core-plugin-api'; +import { Content, InfoCard } from '@backstage/core-components'; -import { Chip, Grid, makeStyles } from '@material-ui/core'; +import { Grid, makeStyles } from '@material-ui/core'; import moment from 'moment'; import { AssessedProcessInstanceDTO, - parseWorkflowVariables, ProcessInstanceDTO, - QUERY_PARAM_ASSESSMENT_INSTANCE_ID, - WorkflowOverviewDTO, } from '@janus-idp/backstage-plugin-orchestrator-common'; -import { orchestratorApiRef } from '../api'; import { VALUE_UNAVAILABLE } from '../constants'; -import { executeWorkflowRouteRef } from '../routes'; -import { buildUrl } from '../utils/UrlUtils'; -import { WorkflowDescriptionModal } from './WorkflowDescriptionModal'; import { EditorViewKind, WorkflowEditor } from './WorkflowEditor'; import { WorkflowProgress } from './WorkflowProgress'; -import { WorkflowRunDetail, WorkflowSuggestion } from './WorkflowRunDetail'; +import { WorkflowResult } from './WorkflowResult'; +import { WorkflowRunDetail } from './WorkflowRunDetail'; import { WorkflowRunDetails } from './WorkflowRunDetails'; import { WorkflowVariablesViewer } from './WorkflowVariablesViewer'; @@ -42,10 +30,6 @@ export const mapProcessInstanceToDetails = ( } const started = start?.toDate().toLocaleString() ?? VALUE_UNAVAILABLE; - const variables = parseWorkflowVariables(instance?.variables); - const nextWorkflowSuggestions: WorkflowRunDetail['nextWorkflowSuggestions'] = - // @ts-ignore - variables?.workflowdata?.workflowOptions; return { id: instance.id, @@ -56,7 +40,6 @@ export const mapProcessInstanceToDetails = ( category: instance.category, status: instance.status, description: instance.description, - nextWorkflowSuggestions, businessKey: instance.businessKey, }; }; @@ -66,9 +49,12 @@ const useStyles = makeStyles(_ => ({ height: '20rem', }, middleRowCard: { - height: 'calc(2 * 20rem)', + height: '20rem', + overflow: 'auto', + wordBreak: 'break-word', }, bottomRowCard: { + minHeight: '40rem', height: '100%', }, autoOverflow: { overflow: 'auto' }, @@ -80,106 +66,32 @@ const useStyles = makeStyles(_ => ({ recommendedLabel: { margin: '0 0.25rem' }, })); -const getNextWorkflows = ( - details: WorkflowRunDetail, - executeWorkflowLink: RouteFunc< - PathParams<'/v2/workflows/:workflowId/execute'> - >, -) => { - const nextWorkflows: { - title: string; - link: string; - id: string; - isRecommended: boolean; - }[] = []; - - if (details.nextWorkflowSuggestions) { - Object.entries(details.nextWorkflowSuggestions).forEach(([_, value]) => { - const nextWorkflowSuggestions: WorkflowSuggestion[] = Array.isArray(value) - ? value - : [value]; - nextWorkflowSuggestions.forEach(nextWorkflowSuggestion => { - // Produce flat structure - const routeUrl = executeWorkflowLink({ - workflowId: nextWorkflowSuggestion.id, - }); - const urlToNavigate = buildUrl(routeUrl, { - [QUERY_PARAM_ASSESSMENT_INSTANCE_ID]: details.id, - }); - nextWorkflows.push({ - title: nextWorkflowSuggestion.name, - link: urlToNavigate, - id: nextWorkflowSuggestion.id, - isRecommended: - ( - details.nextWorkflowSuggestions - ?.currentVersion as WorkflowSuggestion - )?.id === nextWorkflowSuggestion.id, - }); - }); - }); - } - - return nextWorkflows; -}; - export const WorkflowInstancePageContent: React.FC<{ assessedInstance: AssessedProcessInstanceDTO; }> = ({ assessedInstance }) => { const styles = useStyles(); - const executeWorkflowLink = useRouteRef(executeWorkflowRouteRef); + const details = React.useMemo( () => mapProcessInstanceToDetails(assessedInstance.instance), [assessedInstance.instance], ); - const orchestratorApi = useApi(orchestratorApiRef); - const [ - currentOpenedWorkflowDescriptionModalID, - setCurrentOpenedWorkflowDescriptionModalID, - ] = React.useState(''); - const [currentWorkflow, setCurrentWorkflow] = React.useState( - {} as WorkflowOverviewDTO, - ); - - const openWorkflowDescriptionModal = (itemId: string) => { - if (itemId) { - orchestratorApi - .getWorkflowOverview(itemId) - .then( - workflow => { - setCurrentWorkflow(workflow.data); - }, - error => { - throw new Error(error); - }, - ) - .catch(error => { - throw new Error(error); - }); - setCurrentOpenedWorkflowDescriptionModalID(itemId); + const workflowdata = assessedInstance.instance?.workflowdata; + let instanceVariables; + if (workflowdata) { + instanceVariables = { + /* Since we are about to remove just the top-level property, shallow copy of the object is sufficient */ + ...workflowdata, + }; + if (instanceVariables.hasOwnProperty('result')) { + delete instanceVariables.result; } - }; - - const closeWorkflowDescriptionModal = () => { - setCurrentOpenedWorkflowDescriptionModalID(''); - setCurrentWorkflow({} as WorkflowOverviewDTO); - }; - - const nextWorkflows = React.useMemo( - () => getNextWorkflows(details, executeWorkflowLink), - [details, executeWorkflowLink], - ); - - const instanceVariables = React.useMemo( - () => parseWorkflowVariables(assessedInstance.instance.variables), - [assessedInstance], - ); + } return ( - + - {nextWorkflows.length === 0 ? ( + {instanceVariables && ( - ) : ( - - {nextWorkflows.map(item => ( - -
    - { - openWorkflowDescriptionModal(item.id); - }} - > - {item.title} - - {item.isRecommended ? ( - - ) : null} -
    - -
    - ))} -
    + )} + {!instanceVariables && ( +
    The workflow instance has no variables
    )}
    + + + - - {nextWorkflows.length > 0 ? ( - - - - - - ) : null}
    ); diff --git a/plugins/orchestrator/src/components/WorkflowInstanceStatusIndicator.tsx b/plugins/orchestrator/src/components/WorkflowInstanceStatusIndicator.tsx index 5cfa31cdc6..0bde12c694 100644 --- a/plugins/orchestrator/src/components/WorkflowInstanceStatusIndicator.tsx +++ b/plugins/orchestrator/src/components/WorkflowInstanceStatusIndicator.tsx @@ -8,6 +8,7 @@ import DotIcon from '@material-ui/icons/FiberManualRecord'; import { capitalize, ProcessInstanceStatusDTO, + WorkflowResultDTOCompletedWithEnum, } from '@janus-idp/backstage-plugin-orchestrator-common'; import { VALUE_UNAVAILABLE } from '../constants'; @@ -17,26 +18,40 @@ import { workflowInstanceRouteRef } from '../routes'; export const WorkflowInstanceStatusIndicator = ({ status, lastRunId, + completedWith, }: { status?: ProcessInstanceStatusDTO; lastRunId?: string; + completedWith?: WorkflowResultDTOCompletedWithEnum; }) => { - const iconColor = useWorkflowInstanceStateColors(status); + const iconColor = useWorkflowInstanceStateColors( + status === ProcessInstanceStatusDTO.Completed && completedWith === 'error' + ? ProcessInstanceStatusDTO.Error + : status, + ); const workflowInstanceLink = useRouteRef(workflowInstanceRouteRef); if (!status) { return VALUE_UNAVAILABLE; } + let statusText: string = status; + if ( + status === ProcessInstanceStatusDTO.Completed && + completedWith === 'error' + ) { + statusText = 'Completed with error'; + } + return ( <> {' '} {lastRunId ? ( - {capitalize(status)} + {capitalize(statusText)} ) : ( - <>{capitalize(status)} + <>{capitalize(statusText)} )} ); diff --git a/plugins/orchestrator/src/components/WorkflowResult.tsx b/plugins/orchestrator/src/components/WorkflowResult.tsx new file mode 100644 index 0000000000..1ba6c904da --- /dev/null +++ b/plugins/orchestrator/src/components/WorkflowResult.tsx @@ -0,0 +1,299 @@ +import React from 'react'; + +import { InfoCard, Link } from '@backstage/core-components'; +import { RouteFunc, useApi, useRouteRef } from '@backstage/core-plugin-api'; +import { AboutField } from '@backstage/plugin-catalog'; + +import { + CircularProgress, + Grid, + List, + ListItem, + makeStyles, +} from '@material-ui/core'; +import DotIcon from '@material-ui/icons/FiberManualRecord'; + +import { + AssessedProcessInstanceDTO, + ProcessInstanceErrorDTO, + ProcessInstanceStatusDTO, + QUERY_PARAM_ASSESSMENT_INSTANCE_ID, + WorkflowOverviewDTO, + WorkflowResultDTO, + WorkflowResultDTOCompletedWithEnum, +} from '@janus-idp/backstage-plugin-orchestrator-common'; + +import { orchestratorApiRef } from '../api'; +import { VALUE_UNAVAILABLE } from '../constants'; +import { executeWorkflowRouteRef } from '../routes'; +import { buildUrl } from '../utils/UrlUtils'; +import { + WorkflowDescriptionModal, + WorkflowDescriptionModalProps, +} from './WorkflowDescriptionModal'; + +const useStyles = makeStyles(theme => ({ + outputGrid: { + '& h2': { + textTransform: 'none', + fontSize: 'small', + }, + }, + links: { + padding: '0px', + }, + errorIcon: { + color: theme.palette.error.main, + }, +})); + +const finalStates: ProcessInstanceStatusDTO[] = [ + ProcessInstanceStatusDTO.Error, + ProcessInstanceStatusDTO.Completed, + ProcessInstanceStatusDTO.Aborted, + ProcessInstanceStatusDTO.Suspended, +]; + +const ResultMessage = ({ + status, + error, + resultMessage, + completedWith, +}: { + status?: ProcessInstanceStatusDTO; + error?: ProcessInstanceErrorDTO; + resultMessage?: WorkflowResultDTO['message']; + completedWith: WorkflowResultDTO['completedWith']; +}) => { + const styles = useStyles(); + + const errorMessage = error?.message || error?.toString(); + let noResult = <>; + if (!resultMessage && !errorMessage) { + if (status && finalStates.includes(status)) { + noResult = ( + The workflow provided no additional info about the status. + ); + } else { + noResult = ( + + +  The workflow has not yet provided additional info about the + status. + + ); + } + } + + return ( + <> + {resultMessage && ( + + {completedWith === WorkflowResultDTOCompletedWithEnum.Error && ( + <> + +   + + )} + {resultMessage} + + )} + {errorMessage && {errorMessage}} + {noResult} + + ); +}; + +const NextWorkflows = ({ + instanceId, + nextWorkflows, +}: { + instanceId: string; + nextWorkflows: WorkflowResultDTO['nextWorkflows']; +}) => { + const styles = useStyles(); + + const orchestratorApi = useApi(orchestratorApiRef); + const executeWorkflowLink: RouteFunc<{ workflowId: string }> = useRouteRef( + executeWorkflowRouteRef, + ); + + const [ + currentOpenedWorkflowDescriptionModalID, + setCurrentOpenedWorkflowDescriptionModalID, + ] = React.useState(''); + + const [currentWorkflow, setCurrentWorkflow] = React.useState( + {} as WorkflowOverviewDTO, + ); + const [workflowError, setWorkflowError] = + React.useState(); + + const runWorkflowLink = React.useMemo( + () => + buildUrl( + executeWorkflowLink({ + workflowId: currentOpenedWorkflowDescriptionModalID, + }), + { + [QUERY_PARAM_ASSESSMENT_INSTANCE_ID]: instanceId, + }, + ), + [currentOpenedWorkflowDescriptionModalID, executeWorkflowLink, instanceId], + ); + + const openWorkflowDescriptionModal = React.useCallback( + (itemId: string) => { + if (itemId) { + orchestratorApi + .getWorkflowOverview(itemId) + .then(workflow => { + setCurrentWorkflow(workflow.data); + }) + .catch(error => { + setWorkflowError({ itemId, error }); + }); + setCurrentOpenedWorkflowDescriptionModalID(itemId); + } + }, + [orchestratorApi], + ); + + const closeWorkflowDescriptionModal = React.useCallback(() => { + setCurrentOpenedWorkflowDescriptionModalID(''); + setCurrentWorkflow({} as WorkflowOverviewDTO); + }, []); + + if (!nextWorkflows?.length) { + return null; + } + + const sectionLabel = + nextWorkflows.length === 1 + ? 'Suggested next workflow' + : 'Suggested next workflows'; + + return ( + + + + {nextWorkflows.map(item => ( + + { + openWorkflowDescriptionModal(item.id); + }} + > + {item.name} + + + ))} + + + + + ); +}; + +const WorkflowOutputs = ({ + outputs, +}: { + outputs: WorkflowResultDTO['outputs']; +}) => { + const styles = useStyles(); + + if (!outputs?.length) { + return null; + } + + const links = outputs?.filter(item => item.format === 'link'); + const nonLinks = outputs?.filter(item => item.format !== 'link'); + + return ( + <> + {nonLinks.map(item => { + let value = item.value || VALUE_UNAVAILABLE; + if (typeof value !== 'string') { + // This is a workaround for malformed returned data. It should not happen if the sender does WorkflowResult validation properly. + if (typeof value === 'object') { + value = `Object: ${JSON.stringify(value)}`; + } else { + value = 'Unexpected type'; + } + } + + return ( + + + + ); + })} + + {links?.length > 0 && ( + + + + {links + .filter( + item => + item.value && item.key && typeof item.value === 'string', + ) + .map(item => { + return ( + + {item.key} + + ); + })} + + + + )} + + ); +}; + +export const WorkflowResult: React.FC<{ + assessedInstance: AssessedProcessInstanceDTO; + className: string; +}> = ({ assessedInstance, className }) => { + const instance = assessedInstance.instance; + const result = instance.workflowdata?.result; + + return ( + + } + divider={false} + className={className} + > + + + + + + ); +}; + +WorkflowResult.displayName = 'WorkflowResult'; diff --git a/plugins/orchestrator/src/components/WorkflowRunDetail.ts b/plugins/orchestrator/src/components/WorkflowRunDetail.ts index d4b7f2ca6b..80c70bc012 100644 --- a/plugins/orchestrator/src/components/WorkflowRunDetail.ts +++ b/plugins/orchestrator/src/components/WorkflowRunDetail.ts @@ -13,7 +13,4 @@ export type WorkflowRunDetail = { category?: string; description?: string; businessKey?: string; - nextWorkflowSuggestions?: { - [key: string]: WorkflowSuggestion | WorkflowSuggestion[]; - }; }; diff --git a/plugins/orchestrator/src/components/WorkflowRunDetails.tsx b/plugins/orchestrator/src/components/WorkflowRunDetails.tsx index e02328149d..34d3a5982e 100644 --- a/plugins/orchestrator/src/components/WorkflowRunDetails.tsx +++ b/plugins/orchestrator/src/components/WorkflowRunDetails.tsx @@ -10,6 +10,7 @@ import { capitalize, ProcessInstanceDTO, ProcessInstanceStatusDTO, + WorkflowResultDTOCompletedWithEnum, } from '@janus-idp/backstage-plugin-orchestrator-common'; import { VALUE_UNAVAILABLE } from '../constants'; @@ -20,6 +21,7 @@ import { WorkflowRunDetail } from './WorkflowRunDetail'; type WorkflowDetailsCardProps = { assessedBy?: ProcessInstanceDTO; details: WorkflowRunDetail; + completedWith?: WorkflowResultDTOCompletedWithEnum; }; const useStyles = makeStyles({ @@ -32,6 +34,7 @@ const useStyles = makeStyles({ export const WorkflowRunDetails: React.FC = ({ assessedBy, details, + completedWith, }) => { const styles = useStyles(); const workflowInstanceLink = useRouteRef(workflowInstanceRouteRef); @@ -51,6 +54,7 @@ export const WorkflowRunDetails: React.FC = ({ diff --git a/plugins/orchestrator/src/dataFormatters/WorkflowOverviewFormatter.test.ts b/plugins/orchestrator/src/dataFormatters/WorkflowOverviewFormatter.test.ts index d399991df4..337ccb94c4 100644 --- a/plugins/orchestrator/src/dataFormatters/WorkflowOverviewFormatter.test.ts +++ b/plugins/orchestrator/src/dataFormatters/WorkflowOverviewFormatter.test.ts @@ -1,4 +1,5 @@ import { + ProcessInstanceStatusDTO, WorkflowCategoryDTO, WorkflowOverviewDTO, } from '@janus-idp/backstage-plugin-orchestrator-common'; @@ -14,7 +15,7 @@ describe('WorkflowOverviewAdapter', () => { workflowId: '123', name: 'Sample Workflow', lastTriggeredMs: 1697276096000, - lastRunStatus: 'COMPLETED', + lastRunStatus: ProcessInstanceStatusDTO.Completed, category: WorkflowCategoryDTO.Infrastructure, avgDurationMs: 150000, description: 'Sample description', diff --git a/plugins/orchestrator/src/hooks/useWorkflowInstanceStatusColors.ts b/plugins/orchestrator/src/hooks/useWorkflowInstanceStatusColors.ts index 6f7b984fe0..7358b81c3d 100644 --- a/plugins/orchestrator/src/hooks/useWorkflowInstanceStatusColors.ts +++ b/plugins/orchestrator/src/hooks/useWorkflowInstanceStatusColors.ts @@ -2,33 +2,33 @@ import { makeStyles } from '@material-ui/core'; import { ProcessInstanceStatusDTO } from '@janus-idp/backstage-plugin-orchestrator-common'; +const useStyles = makeStyles( + theme => + ({ + [ProcessInstanceStatusDTO.Active]: { + color: theme.palette.primary.main, + }, + [ProcessInstanceStatusDTO.Completed]: { + color: theme.palette.success.main, + }, + [ProcessInstanceStatusDTO.Suspended]: { + color: theme.palette.warning.main, + }, + [ProcessInstanceStatusDTO.Aborted]: { + color: theme.palette.error.main, + }, + [ProcessInstanceStatusDTO.Error]: { + color: theme.palette.error.main, + }, + [ProcessInstanceStatusDTO.Pending]: { + color: theme.palette.grey[500], + }, + }) as const, +); + export const useWorkflowInstanceStateColors = ( value?: ProcessInstanceStatusDTO, ) => { - const useStyles = makeStyles( - theme => - ({ - [ProcessInstanceStatusDTO.Active]: { - color: theme.palette.primary.main, - }, - [ProcessInstanceStatusDTO.Completed]: { - color: theme.palette.success.main, - }, - [ProcessInstanceStatusDTO.Suspended]: { - color: theme.palette.warning.main, - }, - [ProcessInstanceStatusDTO.Aborted]: { - color: theme.palette.error.main, - }, - [ProcessInstanceStatusDTO.Error]: { - color: theme.palette.error.main, - }, - [ProcessInstanceStatusDTO.Pending]: { - color: theme.palette.grey[500], - }, - }) as const, - ); - const styles = useStyles(); return value ? styles[value] : undefined; };