Merge branch 'main' into update-workflow-json

This commit is contained in:
mike cullerton 2022-11-18 09:02:29 -05:00
commit 3c04dd8325
15 changed files with 487 additions and 298 deletions

View File

@ -806,18 +806,12 @@ def process_instance_list(
ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore
).paginate(page=page, per_page=per_page, error_out=False) ).paginate(page=page, per_page=per_page, error_out=False)
# TODO need to look into test failures when the results from result_dict is results = list(
# used instead of the process instances map(
ProcessInstanceService.serialize_flat_with_task_data,
# substitution_variables = request.args.to_dict() process_instances.items,
# result_dict = process_instance_report.generate_report( )
# process_instances.items, substitution_variables )
# )
# results = result_dict["results"]
# report_metadata = result_dict["report_metadata"]
results = process_instances.items
report_metadata = process_instance_report.report_metadata report_metadata = process_instance_report.report_metadata
response_json = { response_json = {

View File

@ -315,3 +315,22 @@ class ProcessInstanceService:
) )
return task return task
@staticmethod
def serialize_flat_with_task_data(
process_instance: ProcessInstanceModel,
) -> dict[str, Any]:
"""serialize_flat_with_task_data."""
results = {}
try:
original_status = process_instance.status
processor = ProcessInstanceProcessor(process_instance)
process_instance.data = processor.get_current_data()
results = process_instance.serialized_flat
# this process seems to mutate the status of the process_instance which
# can result in different results than expected from process_instance_list,
# so set the status back to the expected value
results["status"] = original_status
except ApiError:
results = process_instance.serialized
return results

View File

@ -24,7 +24,6 @@
"@rjsf/mui": "^5.0.0-beta.13", "@rjsf/mui": "^5.0.0-beta.13",
"@rjsf/utils": "^5.0.0-beta.13", "@rjsf/utils": "^5.0.0-beta.13",
"@rjsf/validator-ajv6": "^5.0.0-beta.13", "@rjsf/validator-ajv6": "^5.0.0-beta.13",
"@rjsf/validator-ajv8": "^5.0.0-beta.13",
"@tanstack/react-table": "^8.2.2", "@tanstack/react-table": "^8.2.2",
"@testing-library/jest-dom": "^5.16.4", "@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0", "@testing-library/react": "^13.3.0",
@ -4950,23 +4949,6 @@
"@rjsf/utils": "^5.0.0-beta.1" "@rjsf/utils": "^5.0.0-beta.1"
} }
}, },
"node_modules/@rjsf/validator-ajv8": {
"version": "5.0.0-beta.13",
"resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.0.0-beta.13.tgz",
"integrity": "sha512-/hrYbiwgCvEqw1Z7YZTWvd+ZAiX5vSN0WAI2hJTJTqKuCTcIH0fqNDCaOg3FBR38BL7seZrUmibIUcPU66iJ1w==",
"dependencies": {
"ajv-formats": "^2.1.1",
"ajv8": "npm:ajv@^8.11.0",
"lodash": "^4.17.15",
"lodash-es": "^4.17.15"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
"@rjsf/utils": "^5.0.0-beta.12"
}
},
"node_modules/@rollup/plugin-babel": { "node_modules/@rollup/plugin-babel": {
"version": "5.3.1", "version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@ -6840,27 +6822,6 @@
"ajv": "^6.9.1" "ajv": "^6.9.1"
} }
}, },
"node_modules/ajv8": {
"name": "ajv",
"version": "8.11.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz",
"integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv8/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"node_modules/ansi-align": { "node_modules/ansi-align": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
@ -19562,9 +19523,9 @@
} }
}, },
"node_modules/loader-utils": { "node_modules/loader-utils": {
"version": "2.0.2", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"dependencies": { "dependencies": {
"big.js": "^5.2.2", "big.js": "^5.2.2",
"emojis-list": "^3.0.0", "emojis-list": "^3.0.0",
@ -23934,9 +23895,9 @@
} }
}, },
"node_modules/react-dev-utils/node_modules/loader-utils": { "node_modules/react-dev-utils/node_modules/loader-utils": {
"version": "3.2.0", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz",
"integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==", "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==",
"engines": { "engines": {
"node": ">= 12.13.0" "node": ">= 12.13.0"
} }
@ -25031,25 +24992,14 @@
} }
}, },
"node_modules/recursive-readdir": { "node_modules/recursive-readdir": {
"version": "2.2.2", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz",
"integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==",
"dependencies": { "dependencies": {
"minimatch": "3.0.4" "minimatch": "^3.0.5"
}, },
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=6.0.0"
}
},
"node_modules/recursive-readdir/node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
} }
}, },
"node_modules/redent": { "node_modules/redent": {
@ -26103,9 +26053,9 @@
} }
}, },
"node_modules/serve": { "node_modules/serve": {
"version": "14.0.1", "version": "14.1.1",
"resolved": "https://registry.npmjs.org/serve/-/serve-14.0.1.tgz", "resolved": "https://registry.npmjs.org/serve/-/serve-14.1.1.tgz",
"integrity": "sha512-tNGwxl27FwA8TbmMQqN0jTaSx8/trL532qZsJHX1VdiEIjjtMJHCs7AFS6OvtC7cTHOvmjXqt5yczejU6CV2Xg==", "integrity": "sha512-7RhRDEirZ7Qyee4QWhBHO9qRtjIGsIPGecDDPzNzlOsjDiZWcq36GS8FioVJAuJPVJBBDTsGp33WWOO4B9A82g==",
"dependencies": { "dependencies": {
"@zeit/schemas": "2.21.0", "@zeit/schemas": "2.21.0",
"ajv": "8.11.0", "ajv": "8.11.0",
@ -26116,7 +26066,7 @@
"clipboardy": "3.0.0", "clipboardy": "3.0.0",
"compression": "1.7.4", "compression": "1.7.4",
"is-port-reachable": "4.0.0", "is-port-reachable": "4.0.0",
"serve-handler": "6.1.3", "serve-handler": "6.1.5",
"update-check": "1.5.4" "update-check": "1.5.4"
}, },
"bin": { "bin": {
@ -26127,15 +26077,15 @@
} }
}, },
"node_modules/serve-handler": { "node_modules/serve-handler": {
"version": "6.1.3", "version": "6.1.5",
"resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz", "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz",
"integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==", "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==",
"dependencies": { "dependencies": {
"bytes": "3.0.0", "bytes": "3.0.0",
"content-disposition": "0.5.2", "content-disposition": "0.5.2",
"fast-url-parser": "1.1.3", "fast-url-parser": "1.1.3",
"mime-types": "2.1.18", "mime-types": "2.1.18",
"minimatch": "3.0.4", "minimatch": "3.1.2",
"path-is-inside": "1.0.2", "path-is-inside": "1.0.2",
"path-to-regexp": "2.2.1", "path-to-regexp": "2.2.1",
"range-parser": "1.2.0" "range-parser": "1.2.0"
@ -26160,17 +26110,6 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/serve-handler/node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/serve-index": { "node_modules/serve-index": {
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
@ -30730,9 +30669,9 @@
} }
}, },
"node_modules/webpack/node_modules/loader-utils": { "node_modules/webpack/node_modules/loader-utils": {
"version": "1.4.0", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
"dependencies": { "dependencies": {
"big.js": "^5.2.2", "big.js": "^5.2.2",
"emojis-list": "^3.0.0", "emojis-list": "^3.0.0",
@ -34883,17 +34822,6 @@
"lodash-es": "^4.17.15" "lodash-es": "^4.17.15"
} }
}, },
"@rjsf/validator-ajv8": {
"version": "5.0.0-beta.13",
"resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.0.0-beta.13.tgz",
"integrity": "sha512-/hrYbiwgCvEqw1Z7YZTWvd+ZAiX5vSN0WAI2hJTJTqKuCTcIH0fqNDCaOg3FBR38BL7seZrUmibIUcPU66iJ1w==",
"requires": {
"ajv-formats": "^2.1.1",
"ajv8": "npm:ajv@^8.11.0",
"lodash": "^4.17.15",
"lodash-es": "^4.17.15"
}
},
"@rollup/plugin-babel": { "@rollup/plugin-babel": {
"version": "5.3.1", "version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@ -36367,24 +36295,6 @@
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"requires": {} "requires": {}
}, },
"ajv8": {
"version": "npm:ajv@8.11.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz",
"integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==",
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"dependencies": {
"json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
}
}
},
"ansi-align": { "ansi-align": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
@ -45991,9 +45901,9 @@
"integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw=="
}, },
"loader-utils": { "loader-utils": {
"version": "2.0.2", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"requires": { "requires": {
"big.js": "^5.2.2", "big.js": "^5.2.2",
"emojis-list": "^3.0.0", "emojis-list": "^3.0.0",
@ -48988,9 +48898,9 @@
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
}, },
"loader-utils": { "loader-utils": {
"version": "3.2.0", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz",
"integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==" "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw=="
}, },
"supports-color": { "supports-color": {
"version": "7.2.0", "version": "7.2.0",
@ -49765,21 +49675,11 @@
} }
}, },
"recursive-readdir": { "recursive-readdir": {
"version": "2.2.2", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz",
"integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==",
"requires": { "requires": {
"minimatch": "3.0.4" "minimatch": "^3.0.5"
},
"dependencies": {
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
}
}
} }
}, },
"redent": { "redent": {
@ -50593,9 +50493,9 @@
} }
}, },
"serve": { "serve": {
"version": "14.0.1", "version": "14.1.1",
"resolved": "https://registry.npmjs.org/serve/-/serve-14.0.1.tgz", "resolved": "https://registry.npmjs.org/serve/-/serve-14.1.1.tgz",
"integrity": "sha512-tNGwxl27FwA8TbmMQqN0jTaSx8/trL532qZsJHX1VdiEIjjtMJHCs7AFS6OvtC7cTHOvmjXqt5yczejU6CV2Xg==", "integrity": "sha512-7RhRDEirZ7Qyee4QWhBHO9qRtjIGsIPGecDDPzNzlOsjDiZWcq36GS8FioVJAuJPVJBBDTsGp33WWOO4B9A82g==",
"requires": { "requires": {
"@zeit/schemas": "2.21.0", "@zeit/schemas": "2.21.0",
"ajv": "8.11.0", "ajv": "8.11.0",
@ -50606,7 +50506,7 @@
"clipboardy": "3.0.0", "clipboardy": "3.0.0",
"compression": "1.7.4", "compression": "1.7.4",
"is-port-reachable": "4.0.0", "is-port-reachable": "4.0.0",
"serve-handler": "6.1.3", "serve-handler": "6.1.5",
"update-check": "1.5.4" "update-check": "1.5.4"
}, },
"dependencies": { "dependencies": {
@ -50634,15 +50534,15 @@
} }
}, },
"serve-handler": { "serve-handler": {
"version": "6.1.3", "version": "6.1.5",
"resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz", "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz",
"integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==", "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==",
"requires": { "requires": {
"bytes": "3.0.0", "bytes": "3.0.0",
"content-disposition": "0.5.2", "content-disposition": "0.5.2",
"fast-url-parser": "1.1.3", "fast-url-parser": "1.1.3",
"mime-types": "2.1.18", "mime-types": "2.1.18",
"minimatch": "3.0.4", "minimatch": "3.1.2",
"path-is-inside": "1.0.2", "path-is-inside": "1.0.2",
"path-to-regexp": "2.2.1", "path-to-regexp": "2.2.1",
"range-parser": "1.2.0" "range-parser": "1.2.0"
@ -50660,14 +50560,6 @@
"requires": { "requires": {
"mime-db": "~1.33.0" "mime-db": "~1.33.0"
} }
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
}
} }
} }
}, },
@ -54022,9 +53914,9 @@
} }
}, },
"loader-utils": { "loader-utils": {
"version": "1.4.0", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
"requires": { "requires": {
"big.js": "^5.2.2", "big.js": "^5.2.2",
"emojis-list": "^3.0.0", "emojis-list": "^3.0.0",

View File

@ -17,9 +17,13 @@ import {
import { Logout, Login } from '@carbon/icons-react'; import { Logout, Login } from '@carbon/icons-react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { Can } from '@casl/react';
// @ts-expect-error TS(2307) FIXME: Cannot find module '../logo.svg' or its correspond... Remove this comment to see the full error message // @ts-expect-error TS(2307) FIXME: Cannot find module '../logo.svg' or its correspond... Remove this comment to see the full error message
import logo from '../logo.svg'; import logo from '../logo.svg';
import UserService from '../services/UserService'; import UserService from '../services/UserService';
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
import { PermissionsToCheck } from '../interfaces';
import { usePermissionFetcher } from '../hooks/PermissionService';
// for ref: https://react-bootstrap.github.io/components/navbar/ // for ref: https://react-bootstrap.github.io/components/navbar/
export default function NavigationBar() { export default function NavigationBar() {
@ -34,6 +38,14 @@ export default function NavigationBar() {
const location = useLocation(); const location = useLocation();
const [activeKey, setActiveKey] = useState<string>(''); const [activeKey, setActiveKey] = useState<string>('');
const { targetUris } = useUriListForPermissions();
const permissionRequestData: PermissionsToCheck = {
[targetUris.authenticationListPath]: ['GET'],
[targetUris.messageInstanceListPath]: ['GET'],
[targetUris.secretListPath]: ['GET'],
};
const { ability } = usePermissionFetcher(permissionRequestData);
useEffect(() => { useEffect(() => {
let newActiveKey = '/admin/process-groups'; let newActiveKey = '/admin/process-groups';
if (location.pathname.match(/^\/admin\/messages\b/)) { if (location.pathname.match(/^\/admin\/messages\b/)) {
@ -84,6 +96,42 @@ export default function NavigationBar() {
); );
}; };
const configurationElement = () => {
return (
<Can
I="GET"
a={targetUris.authenticationListPath}
ability={ability}
passThrough
>
{(authenticationAllowed: boolean) => {
return (
<Can
I="GET"
a={targetUris.secretListPath}
ability={ability}
passThrough
>
{(secretAllowed: boolean) => {
if (secretAllowed || authenticationAllowed) {
return (
<HeaderMenuItem
href="/admin/configuration"
isCurrentPage={isActivePage('/admin/configuration')}
>
Configuration
</HeaderMenuItem>
);
}
return null;
}}
</Can>
);
}}
</Can>
);
};
const headerMenuItems = () => { const headerMenuItems = () => {
return ( return (
<> <>
@ -103,18 +151,15 @@ export default function NavigationBar() {
> >
Process Instances Process Instances
</HeaderMenuItem> </HeaderMenuItem>
<HeaderMenuItem <Can I="GET" a={targetUris.messageInstanceListPath} ability={ability}>
href="/admin/messages" <HeaderMenuItem
isCurrentPage={isActivePage('/admin/messages')} href="/admin/messages"
> isCurrentPage={isActivePage('/admin/messages')}
Messages >
</HeaderMenuItem> Messages
<HeaderMenuItem </HeaderMenuItem>
href="/admin/configuration" </Can>
isCurrentPage={isActivePage('/admin/configuration')} {configurationElement()}
>
Configuration
</HeaderMenuItem>
<HeaderMenuItem <HeaderMenuItem
href="/admin/process-instances/reports" href="/admin/process-instances/reports"
isCurrentPage={isActivePage('/admin/process-instances/reports')} isCurrentPage={isActivePage('/admin/process-instances/reports')}
@ -125,7 +170,7 @@ export default function NavigationBar() {
); );
}; };
if (activeKey) { if (activeKey && ability) {
return ( return (
<HeaderContainer <HeaderContainer
render={({ isSideNavExpanded, onClickSideNavExpand }: any) => ( render={({ isSideNavExpanded, onClickSideNavExpand }: any) => (

View File

@ -20,12 +20,16 @@ import {
TableHeader, TableHeader,
TableHead, TableHead,
TableRow, TableRow,
TimePicker,
// @ts-ignore // @ts-ignore
} from '@carbon/react'; } from '@carbon/react';
import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config'; import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config';
import { import {
convertDateStringToSeconds, convertDateAndTimeStringsToSeconds,
convertSecondsToFormattedDate, convertDateObjectToFormattedHoursMinutes,
convertSecondsToFormattedDateString,
convertSecondsToFormattedDateTime,
convertSecondsToFormattedTimeHoursMinutes,
getPageInfoFromSearchParams, getPageInfoFromSearchParams,
getProcessModelFullIdentifierFromSearchParams, getProcessModelFullIdentifierFromSearchParams,
modifyProcessModelPath, modifyProcessModelPath,
@ -49,6 +53,10 @@ type OwnProps = {
perPageOptions?: number[]; perPageOptions?: number[];
}; };
interface dateParameters {
[key: string]: ((..._args: any[]) => any)[];
}
export default function ProcessInstanceListTable({ export default function ProcessInstanceListTable({
filtersEnabled = true, filtersEnabled = true,
processModelFullIdentifier, processModelFullIdentifier,
@ -66,11 +74,20 @@ export default function ProcessInstanceListTable({
const oneHourInSeconds = 3600; const oneHourInSeconds = 3600;
const oneMonthInSeconds = oneHourInSeconds * 24 * 30; const oneMonthInSeconds = oneHourInSeconds * 24 * 30;
const [startFrom, setStartFrom] = useState<string>(''); const [startFromDate, setStartFromDate] = useState<string>('');
const [startTo, setStartTo] = useState<string>(''); const [startToDate, setStartToDate] = useState<string>('');
const [endFrom, setEndFrom] = useState<string>(''); const [endFromDate, setEndFromDate] = useState<string>('');
const [endTo, setEndTo] = useState<string>(''); const [endToDate, setEndToDate] = useState<string>('');
const [startFromTime, setStartFromTime] = useState<string>('');
const [startToTime, setStartToTime] = useState<string>('');
const [endFromTime, setEndFromTime] = useState<string>('');
const [endToTime, setEndToTime] = useState<string>('');
const [showFilterOptions, setShowFilterOptions] = useState<boolean>(false); const [showFilterOptions, setShowFilterOptions] = useState<boolean>(false);
const [startFromTimeInvalid, setStartFromTimeInvalid] =
useState<boolean>(false);
const [startToTimeInvalid, setStartToTimeInvalid] = useState<boolean>(false);
const [endFromTimeInvalid, setEndFromTimeInvalid] = useState<boolean>(false);
const [endToTimeInvalid, setEndToTimeInvalid] = useState<boolean>(false);
const setErrorMessage = (useContext as any)(ErrorContext)[1]; const setErrorMessage = (useContext as any)(ErrorContext)[1];
@ -86,14 +103,23 @@ export default function ProcessInstanceListTable({
const [processModelSelection, setProcessModelSelection] = const [processModelSelection, setProcessModelSelection] =
useState<ProcessModel | null>(null); useState<ProcessModel | null>(null);
const parametersToAlwaysFilterBy = useMemo(() => { const dateParametersToAlwaysFilterBy: dateParameters = useMemo(() => {
return { return {
start_from: setStartFrom, start_from: [setStartFromDate, setStartFromTime],
start_to: setStartTo, start_to: [setStartToDate, setStartToTime],
end_from: setEndFrom, end_from: [setEndFromDate, setEndFromTime],
end_to: setEndTo, end_to: [setEndToDate, setEndToTime],
}; };
}, [setStartFrom, setStartTo, setEndFrom, setEndTo]); }, [
setStartFromDate,
setStartFromTime,
setStartToDate,
setStartToTime,
setEndFromDate,
setEndFromTime,
setEndToDate,
setEndToTime,
]);
const parametersToGetFromSearchParams = useMemo(() => { const parametersToGetFromSearchParams = useMemo(() => {
return { return {
@ -130,19 +156,27 @@ export default function ProcessInstanceListTable({
queryParamString += `&user_filter=${userAppliedFilter}`; queryParamString += `&user_filter=${userAppliedFilter}`;
} }
Object.keys(parametersToAlwaysFilterBy).forEach((paramName: string) => { Object.keys(dateParametersToAlwaysFilterBy).forEach(
// @ts-expect-error TS(7053) FIXME: (paramName: string) => {
const functionToCall = parametersToAlwaysFilterBy[paramName]; const dateFunctionToCall =
const searchParamValue = searchParams.get(paramName); dateParametersToAlwaysFilterBy[paramName][0];
if (searchParamValue) { const timeFunctionToCall =
queryParamString += `&${paramName}=${searchParamValue}`; dateParametersToAlwaysFilterBy[paramName][1];
const dateString = convertSecondsToFormattedDate( const searchParamValue = searchParams.get(paramName);
searchParamValue as any if (searchParamValue) {
); queryParamString += `&${paramName}=${searchParamValue}`;
functionToCall(dateString); const dateString = convertSecondsToFormattedDateString(
setShowFilterOptions(true); searchParamValue as any
);
dateFunctionToCall(dateString);
const timeString = convertSecondsToFormattedTimeHoursMinutes(
searchParamValue as any
);
timeFunctionToCall(timeString);
setShowFilterOptions(true);
}
} }
}); );
Object.keys(parametersToGetFromSearchParams).forEach( Object.keys(parametersToGetFromSearchParams).forEach(
(paramName: string) => { (paramName: string) => {
@ -211,7 +245,7 @@ export default function ProcessInstanceListTable({
params, params,
oneMonthInSeconds, oneMonthInSeconds,
oneHourInSeconds, oneHourInSeconds,
parametersToAlwaysFilterBy, dateParametersToAlwaysFilterBy,
parametersToGetFromSearchParams, parametersToGetFromSearchParams,
filtersEnabled, filtersEnabled,
paginationQueryParamPrefix, paginationQueryParamPrefix,
@ -219,16 +253,25 @@ export default function ProcessInstanceListTable({
perPageOptions, perPageOptions,
]); ]);
// This sets the filter data using the saved reports returned from the initial instance_list query.
// This could probably be merged into the main useEffect but it works here now.
useEffect(() => { useEffect(() => {
const filters = processInstanceFilters as any; const filters = processInstanceFilters as any;
Object.keys(parametersToAlwaysFilterBy).forEach((paramName: string) => { Object.keys(dateParametersToAlwaysFilterBy).forEach((paramName: string) => {
// @ts-expect-error TS(7053) FIXME: const dateFunctionToCall = dateParametersToAlwaysFilterBy[paramName][0];
const functionToCall = parametersToAlwaysFilterBy[paramName]; const timeFunctionToCall = dateParametersToAlwaysFilterBy[paramName][1];
const paramValue = filters[paramName]; const paramValue = filters[paramName];
functionToCall(''); dateFunctionToCall('');
timeFunctionToCall('');
if (paramValue) { if (paramValue) {
const dateString = convertSecondsToFormattedDate(paramValue as any); const dateString = convertSecondsToFormattedDateString(
functionToCall(dateString); paramValue as any
);
dateFunctionToCall(dateString);
const timeString = convertSecondsToFormattedTimeHoursMinutes(
paramValue as any
);
timeFunctionToCall(timeString);
setShowFilterOptions(true); setShowFilterOptions(true);
} }
}); });
@ -253,7 +296,7 @@ export default function ProcessInstanceListTable({
setProcessStatusSelection(processStatusSelectedArray); setProcessStatusSelection(processStatusSelectedArray);
}, [ }, [
processInstanceFilters, processInstanceFilters,
parametersToAlwaysFilterBy, dateParametersToAlwaysFilterBy,
parametersToGetFromSearchParams, parametersToGetFromSearchParams,
processModelAvailableItems, processModelAvailableItems,
]); ]);
@ -285,10 +328,22 @@ export default function ProcessInstanceListTable({
); );
let queryParamString = `per_page=${perPage}&page=${page}&user_filter=true`; let queryParamString = `per_page=${perPage}&page=${page}&user_filter=true`;
const startFromSeconds = convertDateStringToSeconds(startFrom); const startFromSeconds = convertDateAndTimeStringsToSeconds(
const endFromSeconds = convertDateStringToSeconds(endFrom); startFromDate,
const startToSeconds = convertDateStringToSeconds(startTo); startFromTime || '00:00:00'
const endToSeconds = convertDateStringToSeconds(endTo); );
const startToSeconds = convertDateAndTimeStringsToSeconds(
startToDate,
startToTime || '00:00:00'
);
const endFromSeconds = convertDateAndTimeStringsToSeconds(
endFromDate,
endFromTime || '00:00:00'
);
const endToSeconds = convertDateAndTimeStringsToSeconds(
endToDate,
endToTime || '00:00:00'
);
if (isTrueComparison(startFromSeconds, '>', startToSeconds)) { if (isTrueComparison(startFromSeconds, '>', startToSeconds)) {
setErrorMessage({ setErrorMessage({
message: '"Start date from" cannot be after "start date to"', message: '"Start date from" cannot be after "start date to"',
@ -342,24 +397,50 @@ export default function ProcessInstanceListTable({
labelString: any, labelString: any,
name: any, name: any,
initialDate: any, initialDate: any,
onChangeFunction: any initialTime: string,
onChangeDateFunction: any,
onChangeTimeFunction: any,
timeInvalid: boolean,
setTimeInvalid: any
) => { ) => {
return ( return (
<DatePicker dateFormat={DATE_FORMAT_CARBON} datePickerType="single"> <>
<DatePickerInput <DatePicker dateFormat={DATE_FORMAT_CARBON} datePickerType="single">
id={`date-picker-${name}`} <DatePickerInput
placeholder={DATE_FORMAT} id={`date-picker-${name}`}
labelText={labelString} placeholder={DATE_FORMAT}
type="text" labelText={labelString}
size="md" type="text"
autocomplete="off" size="md"
allowInput={false} autocomplete="off"
onChange={(dateChangeEvent: any) => { allowInput={false}
onChangeFunction(dateChangeEvent.srcElement.value); onChange={(dateChangeEvent: any) => {
if (!initialDate && !initialTime) {
onChangeTimeFunction(
convertDateObjectToFormattedHoursMinutes(new Date())
);
}
onChangeDateFunction(dateChangeEvent.srcElement.value);
}}
value={initialDate}
/>
</DatePicker>
<TimePicker
invalid={timeInvalid}
id="time-picker"
labelText="Select a time"
pattern="^([01]\d|2[0-3]):?([0-5]\d)$"
value={initialTime}
onChange={(event: any) => {
if (event.srcElement.validity.valid) {
setTimeInvalid(false);
} else {
setTimeInvalid(true);
}
onChangeTimeFunction(event.srcElement.value);
}} }}
value={initialDate}
/> />
</DatePicker> </>
); );
}; };
@ -386,10 +467,14 @@ export default function ProcessInstanceListTable({
const clearFilters = () => { const clearFilters = () => {
setProcessModelSelection(null); setProcessModelSelection(null);
setProcessStatusSelection([]); setProcessStatusSelection([]);
setStartFrom(''); setStartFromDate('');
setStartTo(''); setStartFromTime('');
setEndFrom(''); setStartToDate('');
setEndTo(''); setStartToTime('');
setEndFromDate('');
setEndFromTime('');
setEndToDate('');
setEndToTime('');
}; };
const filterOptions = () => { const filterOptions = () => {
@ -415,18 +500,49 @@ export default function ProcessInstanceListTable({
{dateComponent( {dateComponent(
'Start date from', 'Start date from',
'start-from', 'start-from',
startFrom, startFromDate,
setStartFrom startFromTime,
setStartFromDate,
setStartFromTime,
startFromTimeInvalid,
setStartFromTimeInvalid
)} )}
</Column> </Column>
<Column md={4}> <Column md={4}>
{dateComponent('Start date to', 'start-to', startTo, setStartTo)} {dateComponent(
'Start date to',
'start-to',
startToDate,
startToTime,
setStartToDate,
setStartToTime,
startToTimeInvalid,
setStartToTimeInvalid
)}
</Column> </Column>
<Column md={4}> <Column md={4}>
{dateComponent('End date from', 'end-from', endFrom, setEndFrom)} {dateComponent(
'End date from',
'end-from',
endFromDate,
endFromTime,
setEndFromDate,
setEndFromTime,
endFromTimeInvalid,
setEndFromTimeInvalid
)}
</Column> </Column>
<Column md={4}> <Column md={4}>
{dateComponent('End date to', 'end-to', endTo, setEndTo)} {dateComponent(
'End date to',
'end-to',
endToDate,
endToTime,
setEndToDate,
setEndToTime,
endToTimeInvalid,
setEndToTimeInvalid
)}
</Column> </Column>
</Grid> </Grid>
<Grid fullWidth className="with-bottom-margin"> <Grid fullWidth className="with-bottom-margin">
@ -455,7 +571,7 @@ export default function ProcessInstanceListTable({
const buildTable = () => { const buildTable = () => {
const headerLabels: Record<string, string> = { const headerLabels: Record<string, string> = {
id: 'Process Instance Id', id: 'Id',
process_model_identifier: 'Process Model', process_model_identifier: 'Process Model',
start_in_seconds: 'Start Time', start_in_seconds: 'Start Time',
end_in_seconds: 'End Time', end_in_seconds: 'End Time',
@ -493,7 +609,7 @@ export default function ProcessInstanceListTable({
); );
}; };
const formatSecondsForDisplay = (_row: any, seconds: any) => { const formatSecondsForDisplay = (_row: any, seconds: any) => {
return convertSecondsToFormattedDate(seconds) || '-'; return convertSecondsToFormattedDateTime(seconds) || '-';
}; };
const defaultFormatter = (_row: any, value: any) => { const defaultFormatter = (_row: any, value: any) => {
return value; return value;
@ -530,7 +646,12 @@ export default function ProcessInstanceListTable({
<TableHead> <TableHead>
<TableRow> <TableRow>
{headers.map((header: any) => ( {headers.map((header: any) => (
<TableHeader key={header}>{header}</TableHeader> <TableHeader
key={header}
title={header === 'Id' ? 'Process Instance Id' : null}
>
{header}
</TableHeader>
))} ))}
</TableRow> </TableRow>
</TableHead> </TableHead>

View File

@ -18,5 +18,6 @@ export const PROCESS_STATUSES = [
// with time: yyyy-MM-dd HH:mm:ss // with time: yyyy-MM-dd HH:mm:ss
export const DATE_TIME_FORMAT = 'yyyy-MM-dd HH:mm:ss'; export const DATE_TIME_FORMAT = 'yyyy-MM-dd HH:mm:ss';
export const TIME_FORMAT_HOURS_MINUTES = 'HH:mm';
export const DATE_FORMAT = 'yyyy-MM-dd'; export const DATE_FORMAT = 'yyyy-MM-dd';
export const DATE_FORMAT_CARBON = 'Y-m-d'; export const DATE_FORMAT_CARBON = 'Y-m-d';

View File

@ -1,4 +1,4 @@
import { convertSecondsToFormattedDate, slugifyString } from './helpers'; import { convertSecondsToFormattedDateString, slugifyString } from './helpers';
test('it can slugify a string', () => { test('it can slugify a string', () => {
expect(slugifyString('hello---world_ and then Some such-')).toEqual( expect(slugifyString('hello---world_ and then Some such-')).toEqual(
@ -7,6 +7,6 @@ test('it can slugify a string', () => {
}); });
test('it can keep the correct date when converting seconds to date', () => { test('it can keep the correct date when converting seconds to date', () => {
const dateString = convertSecondsToFormattedDate(1666325400); const dateString = convertSecondsToFormattedDateString(1666325400);
expect(dateString).toEqual('2022-10-21'); expect(dateString).toEqual('2022-10-21');
}); });

View File

@ -1,5 +1,9 @@
import { format } from 'date-fns'; import { format } from 'date-fns';
import { DATE_TIME_FORMAT, DATE_FORMAT } from './config'; import {
DATE_TIME_FORMAT,
DATE_FORMAT,
TIME_FORMAT_HOURS_MINUTES,
} from './config';
import { import {
DEFAULT_PER_PAGE, DEFAULT_PER_PAGE,
DEFAULT_PAGE, DEFAULT_PAGE,
@ -42,27 +46,72 @@ export const convertDateToSeconds = (
return null; return null;
}; };
export const convertDateObjectToFormattedString = (dateObject: Date) => {
if (dateObject) {
return format(dateObject, DATE_FORMAT);
}
return null;
};
export const convertDateAndTimeStringsToDate = (
dateString: string,
timeString: string
) => {
if (dateString && timeString) {
return new Date(`${dateString}T${timeString}`);
}
return null;
};
export const convertDateAndTimeStringsToSeconds = (
dateString: string,
timeString: string
) => {
const dateObject = convertDateAndTimeStringsToDate(dateString, timeString);
if (dateObject) {
return convertDateToSeconds(dateObject);
}
return null;
};
export const convertStringToDate = (dateString: string) => { export const convertStringToDate = (dateString: string) => {
if (dateString) { return convertDateAndTimeStringsToSeconds(dateString, '00:10:00');
// add midnight time to the date so it c uses the correct date };
// after converting to timezone
return new Date(`${dateString}T00:10:00`); export const convertSecondsToDateObject = (seconds: number) => {
if (seconds) {
return new Date(seconds * 1000);
} }
return null; return null;
}; };
export const convertSecondsToFormattedDateTime = (seconds: number) => { export const convertSecondsToFormattedDateTime = (seconds: number) => {
if (seconds) { const dateObject = convertSecondsToDateObject(seconds);
const dateObject = new Date(seconds * 1000); if (dateObject) {
return format(dateObject, DATE_TIME_FORMAT); return format(dateObject, DATE_TIME_FORMAT);
} }
return null; return null;
}; };
export const convertSecondsToFormattedDate = (seconds: number) => { export const convertDateObjectToFormattedHoursMinutes = (dateObject: Date) => {
if (seconds) { if (dateObject) {
const dateObject = new Date(seconds * 1000); return format(dateObject, TIME_FORMAT_HOURS_MINUTES);
return format(dateObject, DATE_FORMAT); }
return null;
};
export const convertSecondsToFormattedTimeHoursMinutes = (seconds: number) => {
const dateObject = convertSecondsToDateObject(seconds);
if (dateObject) {
return convertDateObjectToFormattedHoursMinutes(dateObject);
}
return null;
};
export const convertSecondsToFormattedDateString = (seconds: number) => {
const dateObject = convertSecondsToDateObject(seconds);
if (dateObject) {
return convertDateObjectToFormattedString(dateObject);
} }
return null; return null;
}; };
@ -148,3 +197,16 @@ export const getGroupFromModifiedModelId = (modifiedId: string) => {
export const splitProcessModelId = (processModelId: string) => { export const splitProcessModelId = (processModelId: string) => {
return processModelId.split('/'); return processModelId.split('/');
}; };
export const refreshAtInterval = (
interval: number,
timeout: number,
func: Function
) => {
const intervalRef = setInterval(() => func(), interval * 1000);
const timeoutRef = setTimeout(
() => clearInterval(intervalRef),
timeout * 1000
);
return [intervalRef, timeoutRef];
};

View File

@ -3,6 +3,7 @@ import { useParams } from 'react-router-dom';
export const useUriListForPermissions = () => { export const useUriListForPermissions = () => {
const params = useParams(); const params = useParams();
const targetUris = { const targetUris = {
authenticationListPath: `/v1.0/authentications`,
messageInstanceListPath: '/v1.0/messages', messageInstanceListPath: '/v1.0/messages',
processGroupListPath: '/v1.0/process-groups', processGroupListPath: '/v1.0/process-groups',
processGroupShowPath: `/v1.0/process-groups/${params.process_group_id}`, processGroupShowPath: `/v1.0/process-groups/${params.process_group_id}`,
@ -12,6 +13,7 @@ export const useUriListForPermissions = () => {
processModelFileCreatePath: `/v1.0/process-models/${params.process_model_id}/files`, processModelFileCreatePath: `/v1.0/process-models/${params.process_model_id}/files`,
processModelFileShowPath: `/v1.0/process-models/${params.process_model_id}/files/${params.file_name}`, processModelFileShowPath: `/v1.0/process-models/${params.process_model_id}/files/${params.file_name}`,
processModelShowPath: `/v1.0/process-models/${params.process_model_id}`, processModelShowPath: `/v1.0/process-models/${params.process_model_id}`,
secretListPath: `/v1.0/secrets`,
}; };
return { targetUris }; return { targetUris };

View File

@ -2,11 +2,15 @@ import { useContext, useEffect, useState } from 'react';
import { Route, Routes, useLocation, useNavigate } from 'react-router-dom'; import { Route, Routes, useLocation, useNavigate } from 'react-router-dom';
// @ts-ignore // @ts-ignore
import { Tabs, TabList, Tab } from '@carbon/react'; import { Tabs, TabList, Tab } from '@carbon/react';
import { Can } from '@casl/react';
import ErrorContext from '../contexts/ErrorContext'; import ErrorContext from '../contexts/ErrorContext';
import SecretList from './SecretList'; import SecretList from './SecretList';
import SecretNew from './SecretNew'; import SecretNew from './SecretNew';
import SecretShow from './SecretShow'; import SecretShow from './SecretShow';
import AuthenticationList from './AuthenticationList'; import AuthenticationList from './AuthenticationList';
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
import { PermissionsToCheck } from '../interfaces';
import { usePermissionFetcher } from '../hooks/PermissionService';
export default function Configuration() { export default function Configuration() {
const location = useLocation(); const location = useLocation();
@ -14,6 +18,13 @@ export default function Configuration() {
const [selectedTabIndex, setSelectedTabIndex] = useState<number>(0); const [selectedTabIndex, setSelectedTabIndex] = useState<number>(0);
const navigate = useNavigate(); const navigate = useNavigate();
const { targetUris } = useUriListForPermissions();
const permissionRequestData: PermissionsToCheck = {
[targetUris.authenticationListPath]: ['GET'],
[targetUris.secretListPath]: ['GET'],
};
const { ability } = usePermissionFetcher(permissionRequestData);
useEffect(() => { useEffect(() => {
setErrorMessage(null); setErrorMessage(null);
let newSelectedTabIndex = 0; let newSelectedTabIndex = 0;
@ -27,12 +38,18 @@ export default function Configuration() {
<> <>
<Tabs selectedIndex={selectedTabIndex}> <Tabs selectedIndex={selectedTabIndex}>
<TabList aria-label="List of tabs"> <TabList aria-label="List of tabs">
<Tab onClick={() => navigate('/admin/configuration/secrets')}> <Can I="GET" a={targetUris.secretListPath} ability={ability}>
Secrets <Tab onClick={() => navigate('/admin/configuration/secrets')}>
</Tab> Secrets
<Tab onClick={() => navigate('/admin/configuration/authentications')}> </Tab>
Authentications </Can>
</Tab> <Can I="GET" a={targetUris.authenticationListPath} ability={ability}>
<Tab
onClick={() => navigate('/admin/configuration/authentications')}
>
Authentications
</Tab>
</Can>
</TabList> </TabList>
</Tabs> </Tabs>
<br /> <br />

View File

@ -5,7 +5,7 @@ import { Link, useParams, useSearchParams } from 'react-router-dom';
import PaginationForTable from '../components/PaginationForTable'; import PaginationForTable from '../components/PaginationForTable';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import { import {
convertSecondsToFormattedDate, convertSecondsToFormattedDateString,
getPageInfoFromSearchParams, getPageInfoFromSearchParams,
modifyProcessModelPath, modifyProcessModelPath,
unModifyProcessModelPath, unModifyProcessModelPath,
@ -68,7 +68,9 @@ export default function MessageInstanceList() {
<td>{rowToUse.failure_cause || '-'}</td> <td>{rowToUse.failure_cause || '-'}</td>
<td>{rowToUse.status}</td> <td>{rowToUse.status}</td>
<td> <td>
{convertSecondsToFormattedDate(rowToUse.created_at_in_seconds)} {convertSecondsToFormattedDateString(
rowToUse.created_at_in_seconds
)}
</td> </td>
</tr> </tr>
); );

View File

@ -6,11 +6,14 @@ import PaginationForTable from '../components/PaginationForTable';
import { import {
getPageInfoFromSearchParams, getPageInfoFromSearchParams,
modifyProcessModelPath, modifyProcessModelPath,
refreshAtInterval,
} from '../helpers'; } from '../helpers';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
import { PaginationObject, RecentProcessModel } from '../interfaces'; import { PaginationObject, RecentProcessModel } from '../interfaces';
const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5; const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5;
const REFRESH_INTERVAL = 10;
const REFRESH_TIMEOUT = 600;
export default function MyTasks() { export default function MyTasks() {
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
@ -18,18 +21,23 @@ export default function MyTasks() {
const [pagination, setPagination] = useState<PaginationObject | null>(null); const [pagination, setPagination] = useState<PaginationObject | null>(null);
useEffect(() => { useEffect(() => {
const { page, perPage } = getPageInfoFromSearchParams( const getTasks = () => {
searchParams, const { page, perPage } = getPageInfoFromSearchParams(
PER_PAGE_FOR_TASKS_ON_HOME_PAGE searchParams,
); PER_PAGE_FOR_TASKS_ON_HOME_PAGE
const setTasksFromResult = (result: any) => { );
setTasks(result.results); const setTasksFromResult = (result: any) => {
setPagination(result.pagination); setTasks(result.results);
setPagination(result.pagination);
};
HttpService.makeCallToBackend({
path: `/tasks?per_page=${perPage}&page=${page}`,
successCallback: setTasksFromResult,
});
}; };
HttpService.makeCallToBackend({
path: `/tasks?per_page=${perPage}&page=${page}`, getTasks();
successCallback: setTasksFromResult, refreshAtInterval(REFRESH_INTERVAL, REFRESH_TIMEOUT, getTasks);
});
}, [searchParams]); }, [searchParams]);
let recentProcessModels: RecentProcessModel[] = []; let recentProcessModels: RecentProcessModel[] = [];

View File

@ -6,9 +6,9 @@ import PaginationForTable from '../components/PaginationForTable';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import { import {
getPageInfoFromSearchParams, getPageInfoFromSearchParams,
convertSecondsToFormattedDate,
modifyProcessModelPath, modifyProcessModelPath,
unModifyProcessModelPath, unModifyProcessModelPath,
convertSecondsToFormattedDateTime,
} from '../helpers'; } from '../helpers';
import HttpService from '../services/HttpService'; import HttpService from '../services/HttpService';
@ -49,7 +49,7 @@ export default function ProcessInstanceLogList() {
data-qa="process-instance-show-link" data-qa="process-instance-show-link"
to={`/admin/process-models/${modifiedProcessModelId}/process-instances/${rowToUse.process_instance_id}/${rowToUse.spiff_step}`} to={`/admin/process-models/${modifiedProcessModelId}/process-instances/${rowToUse.process_instance_id}/${rowToUse.spiff_step}`}
> >
{convertSecondsToFormattedDate(rowToUse.timestamp)} {convertSecondsToFormattedDateTime(rowToUse.timestamp)}
</Link> </Link>
</td> </td>
</tr> </tr>

View File

@ -1,5 +1,5 @@
import { useContext, useEffect, useState } from 'react'; import { useContext, useEffect, useState } from 'react';
import { Link, useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
// FIXME: npm install @rjsf/validator-ajv8 and use it as soon as // FIXME: npm install @rjsf/validator-ajv8 and use it as soon as
// rawErrors is fixed. // rawErrors is fixed.
@ -9,8 +9,14 @@ import { Link, useNavigate, useParams } from 'react-router-dom';
// https://github.com/rjsf-team/react-jsonschema-form/blob/main/docs/api-reference/uiSchema.md talks about rawErrors // https://github.com/rjsf-team/react-jsonschema-form/blob/main/docs/api-reference/uiSchema.md talks about rawErrors
import validator from '@rjsf/validator-ajv6'; import validator from '@rjsf/validator-ajv6';
// @ts-ignore import {
import { Button, Stack } from '@carbon/react'; TabList,
Tab,
Tabs,
Grid,
Column,
// @ts-ignore
} from '@carbon/react';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm'; import remarkGfm from 'remark-gfm';
@ -73,39 +79,52 @@ export default function TaskShow() {
const buildTaskNavigation = () => { const buildTaskNavigation = () => {
let userTasksElement; let userTasksElement;
let selectedTabIndex = 0;
if (userTasks) { if (userTasks) {
userTasksElement = (userTasks as any).map(function getUserTasksElement( userTasksElement = (userTasks as any).map(function getUserTasksElement(
userTask: any userTask: any,
index: number
) { ) {
const taskUrl = `/tasks/${params.process_instance_id}/${userTask.id}`; const taskUrl = `/tasks/${params.process_instance_id}/${userTask.id}`;
if (userTask.id === params.task_id) { if (userTask.id === params.task_id) {
return <span>{userTask.name}</span>; selectedTabIndex = index;
return <Tab selected>{userTask.title}</Tab>;
} }
if (userTask.state === 'COMPLETED') { if (userTask.state === 'COMPLETED') {
return ( return (
<Link to={taskUrl} data-qa={`form-nav-${userTask.name}`}> <Tab
{userTask.name} onClick={() => navigate(taskUrl)}
</Link> data-qa={`form-nav-${userTask.name}`}
>
{userTask.title}
</Tab>
); );
} }
if (userTask.state === 'FUTURE') { if (userTask.state === 'FUTURE') {
return <span style={{ color: 'red' }}>{userTask.name}</span>; return <Tab disabled>{userTask.title}</Tab>;
} }
if (userTask.state === 'READY') { if (userTask.state === 'READY') {
return ( return (
<Link to={taskUrl} data-qa={`form-nav-${userTask.name}`}> <Tab
{userTask.name} - Current onClick={() => navigate(taskUrl)}
</Link> data-qa={`form-nav-${userTask.name}`}
>
{userTask.title}
</Tab>
); );
} }
return null; return null;
}); });
} }
return ( return (
<Stack orientation="horizontal" gap={3}> <Tabs
<Button href="/tasks">Go Back To List</Button> title="Steps in this process instance involving people"
{userTasksElement} selectedIndex={selectedTabIndex}
</Stack> >
<TabList aria-label="List of tabs" contained>
{userTasksElement}
</TabList>
</Tabs>
); );
}; };
@ -149,15 +168,19 @@ export default function TaskShow() {
} }
return ( return (
<Form <Grid fullWidth condensed>
formData={taskData} <Column md={5} lg={8} sm={4}>
onSubmit={handleFormSubmit} <Form
schema={jsonSchema} formData={taskData}
uiSchema={formUiSchema} onSubmit={handleFormSubmit}
validator={validator} schema={jsonSchema}
> uiSchema={formUiSchema}
{reactFragmentToHideSubmitButton} validator={validator}
</Form> >
{reactFragmentToHideSubmitButton}
</Form>
</Column>
</Grid>
); );
}; };
@ -175,7 +198,7 @@ export default function TaskShow() {
); );
}; };
if (task) { if (task && userTasks) {
const taskToUse = task as any; const taskToUse = task as any;
let statusString = ''; let statusString = '';
if (taskToUse.state !== 'READY') { if (taskToUse.state !== 'READY') {

View File

@ -82,6 +82,9 @@ export default function BaseInputTemplate<
} else if (schema && schema.title) { } else if (schema && schema.title) {
labelToUse = schema.title; labelToUse = schema.title;
} }
if (required) {
labelToUse = `${labelToUse}*`;
}
let invalid = false; let invalid = false;
let errorMessageForField = null; let errorMessageForField = null;