From 1ac719158810654c4c26b67affa1b133bfe2adbc Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 31 Oct 2022 14:50:19 -0400 Subject: [PATCH 01/46] added carbon react package w/ burnettk --- spiffworkflow-frontend/package-lock.json | 577 +++++++++++++++++- spiffworkflow-frontend/package.json | 5 + spiffworkflow-frontend/src/index.scss | 2 + spiffworkflow-frontend/src/index.tsx | 1 + .../src/routes/HomePage.tsx | 3 +- 5 files changed, 581 insertions(+), 7 deletions(-) create mode 100644 spiffworkflow-frontend/src/index.scss diff --git a/spiffworkflow-frontend/package-lock.json b/spiffworkflow-frontend/package-lock.json index 738cea50..7eb07fb0 100644 --- a/spiffworkflow-frontend/package-lock.json +++ b/spiffworkflow-frontend/package-lock.json @@ -12,6 +12,7 @@ "@babel/plugin-transform-react-jsx": "^7.18.6", "@babel/preset-react": "^7.18.6", "@bpmn-io/properties-panel": "^0.19.0", + "@carbon/react": "^1.16.0", "@ginkgo-bioworks/react-json-schema-form-builder": "^2.9.0", "@monaco-editor/react": "^4.4.5", "@rjsf/core": "^4.2.0", @@ -2094,6 +2095,138 @@ "resolved": "https://registry.npmjs.org/@camunda/zeebe-element-templates-json-schema/-/zeebe-element-templates-json-schema-0.5.0.tgz", "integrity": "sha512-BVHVl4cuK9LxL1eDSdWs8AzuZd981/+CPkw7xlwcB1Xkn6Di8E2iRbDUCBhOIqkahjJYq957nVtbM6jlqXX5qw==" }, + "node_modules/@carbon/colors": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/@carbon/colors/-/colors-11.7.0.tgz", + "integrity": "sha512-2B57vtirYTpxGg7p9yIO+s4cwL5QB8ogGQB5Pz+zdaHoVxrLsGlxAg28CQgk7yxw7doqST79bRVYS6FaJq+qJw==" + }, + "node_modules/@carbon/feature-flags": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@carbon/feature-flags/-/feature-flags-0.9.0.tgz", + "integrity": "sha512-uNCRkxsNwLdJVNwQ5S5vrLAm4yawWzhFBwyP9EgXVmRzJH85Aim+miC+QNjRXntJDYhZYDuLABTeb3VD692ypA==" + }, + "node_modules/@carbon/grid": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/@carbon/grid/-/grid-11.7.0.tgz", + "integrity": "sha512-OCDXQNSPVOoBQN08Qn5wo2WpKRGRm/kEBo6ZTl2NoDCl21mcYJZ0IDbKPTukixJB1sUNDUlnrpwMoaznPj48dw==", + "dependencies": { + "@carbon/layout": "^11.7.0" + } + }, + "node_modules/@carbon/icon-helpers": { + "version": "10.34.0", + "resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.34.0.tgz", + "integrity": "sha512-Ov9EBc1tR/DDrMI0pN1drj2jb27ISmYFBLdDji+aivVJkLPy8R/jikJOsOBgIq2kUjQJYNN199k2acHKjZdYIg==" + }, + "node_modules/@carbon/icons-react": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/@carbon/icons-react/-/icons-react-11.10.0.tgz", + "integrity": "sha512-807RWTfbvVzmsDg2DJ4FjwYNbJSgkrEd1Ui8I07YheJVb3sbYGGZMG7aCS0qXVlrQOhB2hggtxSW1w9NksUXNA==", + "hasInstallScript": true, + "dependencies": { + "@carbon/icon-helpers": "^10.34.0", + "@carbon/telemetry": "0.1.0", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": ">=16" + } + }, + "node_modules/@carbon/layout": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/@carbon/layout/-/layout-11.7.0.tgz", + "integrity": "sha512-p4YQvW8U5Go0Tz1PZZgllGSPmoq8xBB5PHByuHiAjzwGclxPsBmY6Ea7tftINFW8VlcjDcampyT8VfZXhP2lFg==" + }, + "node_modules/@carbon/motion": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/@carbon/motion/-/motion-11.5.0.tgz", + "integrity": "sha512-5QEALh+xZzICdgVLanSpiDfBTErzVgEze/xUKs7ZdSbd6p1FaDKDGvCmj9RCsaz/CMVHIWo65MshIglSWX5Xvw==" + }, + "node_modules/@carbon/react": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@carbon/react/-/react-1.16.0.tgz", + "integrity": "sha512-kVeL/iyVqN2tfcoE1xliIm4w2ex9L4m/b8KuGo7ZuqBmRzANQdfSYGfj11KU0TXT1CbOVFrqsT/aBxApsP5IDg==", + "hasInstallScript": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@carbon/feature-flags": "^0.9.0", + "@carbon/icons-react": "^11.10.0", + "@carbon/layout": "^11.7.0", + "@carbon/styles": "^1.16.0", + "@carbon/telemetry": "0.1.0", + "classnames": "2.3.2", + "copy-to-clipboard": "^3.3.1", + "downshift": "5.2.1", + "flatpickr": "4.6.9", + "invariant": "^2.2.3", + "lodash.debounce": "^4.0.8", + "lodash.findlast": "^4.5.0", + "lodash.isequal": "^4.5.0", + "lodash.omit": "^4.5.0", + "lodash.throttle": "^4.1.1", + "prop-types": "^15.7.2", + "react-is": "^17.0.2", + "use-resize-observer": "^6.0.0", + "wicg-inert": "^3.1.1", + "window-or-global": "^1.0.1" + }, + "peerDependencies": { + "react": "^16.8.6 || ^17.0.1", + "react-dom": "^16.8.6 || ^17.0.1", + "sass": "^1.33.0" + } + }, + "node_modules/@carbon/react/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/@carbon/styles": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@carbon/styles/-/styles-1.16.0.tgz", + "integrity": "sha512-LSjRw2Ws8rWI1a96KYUuX10jG+rpSn68dHlZhhDq+RJWsMGpjFhKUxPoTiJli2hHXyxey6rXV0hfr7oBJ0ud7w==", + "dependencies": { + "@carbon/colors": "^11.7.0", + "@carbon/feature-flags": "^0.9.0", + "@carbon/grid": "^11.7.0", + "@carbon/layout": "^11.7.0", + "@carbon/motion": "^11.5.0", + "@carbon/themes": "^11.11.0", + "@carbon/type": "^11.11.0", + "@ibm/plex": "6.0.0-next.6" + }, + "peerDependencies": { + "sass": "^1.33.0" + } + }, + "node_modules/@carbon/telemetry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@carbon/telemetry/-/telemetry-0.1.0.tgz", + "integrity": "sha512-kNWt0bkgPwGW0i5h7HFuljbKRXPvIhsKbB+1tEURAYLXoJg9iJLF1eGvWN5iVoFCS2zje4GR3OGOsvvKVe7Hlg==", + "bin": { + "carbon-telemetry": "bin/carbon-telemetry.js" + } + }, + "node_modules/@carbon/themes": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@carbon/themes/-/themes-11.11.0.tgz", + "integrity": "sha512-EhLizr2oMqJXNubI2zWw09qcSPLZdWoBOQ6uNcjHzpXgoGNMwbVJE/qGMy/ivr+EOs6Fe0z5T0u97v+ZhSmRhg==", + "dependencies": { + "@carbon/colors": "^11.7.0", + "@carbon/layout": "^11.7.0", + "@carbon/type": "^11.11.0", + "color": "^4.0.0" + } + }, + "node_modules/@carbon/type": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@carbon/type/-/type-11.11.0.tgz", + "integrity": "sha512-eX6z8BibP1En1xBm2wUd01Nzk0Tm1jftR2QSD4JBn4xhnkGR824gpcbLTAIMGx9/Mr3R65Enbam3uFO0OOaH8g==", + "dependencies": { + "@carbon/grid": "^11.7.0", + "@carbon/layout": "^11.7.0" + } + }, "node_modules/@codemirror/autocomplete": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.1.0.tgz", @@ -2847,6 +2980,14 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "node_modules/@ibm/plex": { + "version": "6.0.0-next.6", + "resolved": "https://registry.npmjs.org/@ibm/plex/-/plex-6.0.0-next.6.tgz", + "integrity": "sha512-B3uGruTn2rS5gweynLmfSe7yCawSRsJguJJQHVQiqf4rh2RNgJFu8YLE2Zd/JHV0ZXoVMOslcXP2k3hMkxKEyA==", + "engines": { + "node": ">=14" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -7939,9 +8080,9 @@ } }, "node_modules/classnames": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", - "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" }, "node_modules/clean-css": { "version": "5.3.1", @@ -8137,6 +8278,18 @@ "node": ">=0.10.0" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -8150,6 +8303,31 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -8414,6 +8592,14 @@ "node": ">=0.10.0" } }, + "node_modules/copy-to-clipboard": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz", + "integrity": "sha512-Vme1Z6RUDzrb6xAI7EZlVZ5uvOk2F//GaxKUxajDqm9LhOVM1inxNAD2vy+UZDYsd0uyA9s7b3/FVZPSxqrCfg==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, "node_modules/core_d": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/core_d/-/core_d-5.0.1.tgz", @@ -9800,6 +9986,25 @@ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" }, + "node_modules/downshift": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/downshift/-/downshift-5.2.1.tgz", + "integrity": "sha512-uHX2OLbWthLR8QbR8NCI8OmjvvJxq8+PrA95KblFd9JyB1zVZh1HnszzsWMMCnMuH6IvsUtVfF5HY7XfijJ2dw==", + "dependencies": { + "@babel/runtime": "^7.9.1", + "compute-scroll-into-view": "^1.0.13", + "prop-types": "^15.7.2", + "react-is": "^16.13.1" + }, + "peerDependencies": { + "react": ">=0.14.9" + } + }, + "node_modules/downshift/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -11524,6 +11729,11 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/flatpickr": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.9.tgz", + "integrity": "sha512-F0azNNi8foVWKSF+8X+ZJzz8r9sE1G4hl06RyceIaLvyltKvDl6vqk9Lm/6AUUCi5HWaIjiUbk7UpeE/fOXOpw==" + }, "node_modules/flatted": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", @@ -12722,6 +12932,12 @@ "url": "https://opencollective.com/immer" } }, + "node_modules/immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "peer": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -18165,6 +18381,16 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.findlast": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.findlast/-/lodash.findlast-4.6.0.tgz", + "integrity": "sha512-+OGwb1FVKjhc2aIEQ9vKqNDW1a0/HaCLr0iCIK10jfVif3dBE0nhQD0jOZNZLh7zOlmFUTrk+vt85eXoH4vKuA==" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -18175,6 +18401,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -18186,6 +18417,11 @@ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -23844,6 +24080,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -24172,6 +24413,23 @@ "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" }, + "node_modules/sass": { + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.55.0.tgz", + "integrity": "sha512-Pk+PMy7OGLs9WaxZGJMn7S96dvlyVBwwtToX895WmCpAOr5YiJYEUJfiJidMuKb613z2xNWcXCHEuOvjZbqC6A==", + "peer": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -24642,6 +24900,19 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -26126,6 +26397,11 @@ "ret": "~0.1.10" } }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -27671,6 +27947,18 @@ "react": "^16.8.0 || ^17.0.0" } }, + "node_modules/use-resize-observer": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-6.1.0.tgz", + "integrity": "sha512-SiPcWHiIQ1CnHmb6PxbYtygqiZXR0U9dNkkbpX9VYnlstUwF8+QqpUTrzh13pjPwcjMVGR+QIC+nvF5ujfFNng==", + "dependencies": { + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", @@ -28791,6 +29079,11 @@ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, + "node_modules/wicg-inert": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/wicg-inert/-/wicg-inert-3.1.2.tgz", + "integrity": "sha512-Ba9tGNYxXwaqKEi9sJJvPMKuo063umUPsHN0JJsjrs2j8KDSzkWLMZGZ+MH1Jf1Fq4OWZ5HsESJID6nRza2ang==" + }, "node_modules/widest-line": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", @@ -28846,6 +29139,11 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/window-or-global": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/window-or-global/-/window-or-global-1.0.1.tgz", + "integrity": "sha512-tE12J/NenOv4xdVobD+AD3fT06T4KNqnzRhkv5nBIu7K+pvOH2oLCEgYP+i+5mF2jtI6FEADheOdZkA8YWET9w==" + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -30704,6 +31002,124 @@ "resolved": "https://registry.npmjs.org/@camunda/zeebe-element-templates-json-schema/-/zeebe-element-templates-json-schema-0.5.0.tgz", "integrity": "sha512-BVHVl4cuK9LxL1eDSdWs8AzuZd981/+CPkw7xlwcB1Xkn6Di8E2iRbDUCBhOIqkahjJYq957nVtbM6jlqXX5qw==" }, + "@carbon/colors": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/@carbon/colors/-/colors-11.7.0.tgz", + "integrity": "sha512-2B57vtirYTpxGg7p9yIO+s4cwL5QB8ogGQB5Pz+zdaHoVxrLsGlxAg28CQgk7yxw7doqST79bRVYS6FaJq+qJw==" + }, + "@carbon/feature-flags": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@carbon/feature-flags/-/feature-flags-0.9.0.tgz", + "integrity": "sha512-uNCRkxsNwLdJVNwQ5S5vrLAm4yawWzhFBwyP9EgXVmRzJH85Aim+miC+QNjRXntJDYhZYDuLABTeb3VD692ypA==" + }, + "@carbon/grid": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/@carbon/grid/-/grid-11.7.0.tgz", + "integrity": "sha512-OCDXQNSPVOoBQN08Qn5wo2WpKRGRm/kEBo6ZTl2NoDCl21mcYJZ0IDbKPTukixJB1sUNDUlnrpwMoaznPj48dw==", + "requires": { + "@carbon/layout": "^11.7.0" + } + }, + "@carbon/icon-helpers": { + "version": "10.34.0", + "resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.34.0.tgz", + "integrity": "sha512-Ov9EBc1tR/DDrMI0pN1drj2jb27ISmYFBLdDji+aivVJkLPy8R/jikJOsOBgIq2kUjQJYNN199k2acHKjZdYIg==" + }, + "@carbon/icons-react": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/@carbon/icons-react/-/icons-react-11.10.0.tgz", + "integrity": "sha512-807RWTfbvVzmsDg2DJ4FjwYNbJSgkrEd1Ui8I07YheJVb3sbYGGZMG7aCS0qXVlrQOhB2hggtxSW1w9NksUXNA==", + "requires": { + "@carbon/icon-helpers": "^10.34.0", + "@carbon/telemetry": "0.1.0", + "prop-types": "^15.7.2" + } + }, + "@carbon/layout": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/@carbon/layout/-/layout-11.7.0.tgz", + "integrity": "sha512-p4YQvW8U5Go0Tz1PZZgllGSPmoq8xBB5PHByuHiAjzwGclxPsBmY6Ea7tftINFW8VlcjDcampyT8VfZXhP2lFg==" + }, + "@carbon/motion": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/@carbon/motion/-/motion-11.5.0.tgz", + "integrity": "sha512-5QEALh+xZzICdgVLanSpiDfBTErzVgEze/xUKs7ZdSbd6p1FaDKDGvCmj9RCsaz/CMVHIWo65MshIglSWX5Xvw==" + }, + "@carbon/react": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@carbon/react/-/react-1.16.0.tgz", + "integrity": "sha512-kVeL/iyVqN2tfcoE1xliIm4w2ex9L4m/b8KuGo7ZuqBmRzANQdfSYGfj11KU0TXT1CbOVFrqsT/aBxApsP5IDg==", + "requires": { + "@babel/runtime": "^7.18.3", + "@carbon/feature-flags": "^0.9.0", + "@carbon/icons-react": "^11.10.0", + "@carbon/layout": "^11.7.0", + "@carbon/styles": "^1.16.0", + "@carbon/telemetry": "0.1.0", + "classnames": "2.3.2", + "copy-to-clipboard": "^3.3.1", + "downshift": "5.2.1", + "flatpickr": "4.6.9", + "invariant": "^2.2.3", + "lodash.debounce": "^4.0.8", + "lodash.findlast": "^4.5.0", + "lodash.isequal": "^4.5.0", + "lodash.omit": "^4.5.0", + "lodash.throttle": "^4.1.1", + "prop-types": "^15.7.2", + "react-is": "^17.0.2", + "use-resize-observer": "^6.0.0", + "wicg-inert": "^3.1.1", + "window-or-global": "^1.0.1" + }, + "dependencies": { + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "@carbon/styles": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@carbon/styles/-/styles-1.16.0.tgz", + "integrity": "sha512-LSjRw2Ws8rWI1a96KYUuX10jG+rpSn68dHlZhhDq+RJWsMGpjFhKUxPoTiJli2hHXyxey6rXV0hfr7oBJ0ud7w==", + "requires": { + "@carbon/colors": "^11.7.0", + "@carbon/feature-flags": "^0.9.0", + "@carbon/grid": "^11.7.0", + "@carbon/layout": "^11.7.0", + "@carbon/motion": "^11.5.0", + "@carbon/themes": "^11.11.0", + "@carbon/type": "^11.11.0", + "@ibm/plex": "6.0.0-next.6" + } + }, + "@carbon/telemetry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@carbon/telemetry/-/telemetry-0.1.0.tgz", + "integrity": "sha512-kNWt0bkgPwGW0i5h7HFuljbKRXPvIhsKbB+1tEURAYLXoJg9iJLF1eGvWN5iVoFCS2zje4GR3OGOsvvKVe7Hlg==" + }, + "@carbon/themes": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@carbon/themes/-/themes-11.11.0.tgz", + "integrity": "sha512-EhLizr2oMqJXNubI2zWw09qcSPLZdWoBOQ6uNcjHzpXgoGNMwbVJE/qGMy/ivr+EOs6Fe0z5T0u97v+ZhSmRhg==", + "requires": { + "@carbon/colors": "^11.7.0", + "@carbon/layout": "^11.7.0", + "@carbon/type": "^11.11.0", + "color": "^4.0.0" + } + }, + "@carbon/type": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@carbon/type/-/type-11.11.0.tgz", + "integrity": "sha512-eX6z8BibP1En1xBm2wUd01Nzk0Tm1jftR2QSD4JBn4xhnkGR824gpcbLTAIMGx9/Mr3R65Enbam3uFO0OOaH8g==", + "requires": { + "@carbon/grid": "^11.7.0", + "@carbon/layout": "^11.7.0" + } + }, "@codemirror/autocomplete": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.1.0.tgz", @@ -31226,6 +31642,11 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "@ibm/plex": { + "version": "6.0.0-next.6", + "resolved": "https://registry.npmjs.org/@ibm/plex/-/plex-6.0.0-next.6.tgz", + "integrity": "sha512-B3uGruTn2rS5gweynLmfSe7yCawSRsJguJJQHVQiqf4rh2RNgJFu8YLE2Zd/JHV0ZXoVMOslcXP2k3hMkxKEyA==" + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -35077,9 +35498,9 @@ } }, "classnames": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", - "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" }, "clean-css": { "version": "5.3.1", @@ -35217,6 +35638,30 @@ "object-visit": "^1.0.0" } }, + "color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "requires": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "dependencies": { + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -35230,6 +35675,15 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -35455,6 +35909,14 @@ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==" }, + "copy-to-clipboard": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz", + "integrity": "sha512-Vme1Z6RUDzrb6xAI7EZlVZ5uvOk2F//GaxKUxajDqm9LhOVM1inxNAD2vy+UZDYsd0uyA9s7b3/FVZPSxqrCfg==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, "core_d": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/core_d/-/core_d-5.0.1.tgz", @@ -36554,6 +37016,24 @@ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" }, + "downshift": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/downshift/-/downshift-5.2.1.tgz", + "integrity": "sha512-uHX2OLbWthLR8QbR8NCI8OmjvvJxq8+PrA95KblFd9JyB1zVZh1HnszzsWMMCnMuH6IvsUtVfF5HY7XfijJ2dw==", + "requires": { + "@babel/runtime": "^7.9.1", + "compute-scroll-into-view": "^1.0.13", + "prop-types": "^15.7.2", + "react-is": "^16.13.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, "duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -37859,6 +38339,11 @@ "rimraf": "^3.0.2" } }, + "flatpickr": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.9.tgz", + "integrity": "sha512-F0azNNi8foVWKSF+8X+ZJzz8r9sE1G4hl06RyceIaLvyltKvDl6vqk9Lm/6AUUCi5HWaIjiUbk7UpeE/fOXOpw==" + }, "flatted": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", @@ -38715,6 +39200,12 @@ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz", "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==" }, + "immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "peer": true + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -42838,6 +43329,16 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "lodash.findlast": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.findlast/-/lodash.findlast-4.6.0.tgz", + "integrity": "sha512-+OGwb1FVKjhc2aIEQ9vKqNDW1a0/HaCLr0iCIK10jfVif3dBE0nhQD0jOZNZLh7zOlmFUTrk+vt85eXoH4vKuA==" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -42848,6 +43349,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" + }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -42859,6 +43365,11 @@ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" }, + "lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -46772,6 +47283,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -47005,6 +47521,17 @@ "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" }, + "sass": { + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.55.0.tgz", + "integrity": "sha512-Pk+PMy7OGLs9WaxZGJMn7S96dvlyVBwwtToX895WmCpAOr5YiJYEUJfiJidMuKb613z2xNWcXCHEuOvjZbqC6A==", + "peer": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -47403,6 +47930,21 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + } + } + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -48573,6 +49115,11 @@ "is-number": "^7.0.0" } }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -49742,6 +50289,14 @@ "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==", "requires": {} }, + "use-resize-observer": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-6.1.0.tgz", + "integrity": "sha512-SiPcWHiIQ1CnHmb6PxbYtygqiZXR0U9dNkkbpX9VYnlstUwF8+QqpUTrzh13pjPwcjMVGR+QIC+nvF5ujfFNng==", + "requires": { + "resize-observer-polyfill": "^1.5.1" + } + }, "util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", @@ -50618,6 +51173,11 @@ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, + "wicg-inert": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/wicg-inert/-/wicg-inert-3.1.2.tgz", + "integrity": "sha512-Ba9tGNYxXwaqKEi9sJJvPMKuo063umUPsHN0JJsjrs2j8KDSzkWLMZGZ+MH1Jf1Fq4OWZ5HsESJID6nRza2ang==" + }, "widest-line": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", @@ -50651,6 +51211,11 @@ } } }, + "window-or-global": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/window-or-global/-/window-or-global-1.0.1.tgz", + "integrity": "sha512-tE12J/NenOv4xdVobD+AD3fT06T4KNqnzRhkv5nBIu7K+pvOH2oLCEgYP+i+5mF2jtI6FEADheOdZkA8YWET9w==" + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/spiffworkflow-frontend/package.json b/spiffworkflow-frontend/package.json index 2ce2d7a1..c48ffca0 100644 --- a/spiffworkflow-frontend/package.json +++ b/spiffworkflow-frontend/package.json @@ -7,6 +7,7 @@ "@babel/plugin-transform-react-jsx": "^7.18.6", "@babel/preset-react": "^7.18.6", "@bpmn-io/properties-panel": "^0.19.0", + "@carbon/react": "^1.16.0", "@ginkgo-bioworks/react-json-schema-form-builder": "^2.9.0", "@monaco-editor/react": "^4.4.5", "@rjsf/core": "^4.2.0", @@ -57,6 +58,10 @@ "react": "^18.2.0", "bootstrap": "^5.2.0-beta1" }, + "@carbon/react": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, "dmn-js-properties-panel": { "@bpmn-io/properties-panel": "^0.19.0" } diff --git a/spiffworkflow-frontend/src/index.scss b/spiffworkflow-frontend/src/index.scss new file mode 100644 index 00000000..61228e3b --- /dev/null +++ b/spiffworkflow-frontend/src/index.scss @@ -0,0 +1,2 @@ +@use '@carbon/react'; + diff --git a/spiffworkflow-frontend/src/index.tsx b/spiffworkflow-frontend/src/index.tsx index ebd40415..a2fab5c0 100644 --- a/spiffworkflow-frontend/src/index.tsx +++ b/spiffworkflow-frontend/src/index.tsx @@ -4,6 +4,7 @@ import App from './App'; import 'bootstrap/dist/css/bootstrap.css'; import './index.css'; +import './index.scss'; import reportWebVitals from './reportWebVitals'; import UserService from './services/UserService'; diff --git a/spiffworkflow-frontend/src/routes/HomePage.tsx b/spiffworkflow-frontend/src/routes/HomePage.tsx index 4d348ed2..da39e205 100644 --- a/spiffworkflow-frontend/src/routes/HomePage.tsx +++ b/spiffworkflow-frontend/src/routes/HomePage.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react'; -import { Button, Table } from 'react-bootstrap'; +// @ts-ignore +import { Button, Table } from '@carbon/react'; import { Link, useSearchParams } from 'react-router-dom'; import PaginationForTable from '../components/PaginationForTable'; import { getPageInfoFromSearchParams } from '../helpers'; From 32716b29806a9328490462e28c9a3e3aad3738ce Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 31 Oct 2022 15:09:21 -0400 Subject: [PATCH 02/46] many things have been switched to carbon and the home page loads w/ burnettk --- spiffworkflow-frontend/src/App.tsx | 7 +- .../src/components/ButtonWithConfirmation.tsx | 3 +- .../src/components/NavigationBar.tsx | 4 +- .../src/components/PaginationForTable.tsx | 274 +++++++++--------- .../src/routes/AuthenticationList.tsx | 3 +- .../src/routes/MessageInstanceList.tsx | 3 +- .../src/routes/ProcessGroupEdit.tsx | 3 +- .../src/routes/ProcessGroupList.tsx | 4 +- .../src/routes/ProcessGroupShow.tsx | 3 +- .../src/routes/ProcessInstanceList.tsx | 4 +- .../src/routes/ProcessInstanceLogList.tsx | 3 +- .../src/routes/ProcessInstanceReportList.tsx | 3 +- .../src/routes/ProcessInstanceReportShow.tsx | 3 +- .../src/routes/ProcessInstanceShow.tsx | 3 +- .../src/routes/ProcessModelEdit.tsx | 3 +- .../src/routes/ProcessModelEditDiagram.tsx | 3 +- .../src/routes/ProcessModelShow.tsx | 3 +- .../src/routes/ReactFormEditor.tsx | 3 +- .../src/routes/SecretList.tsx | 3 +- .../src/routes/SecretNew.tsx | 3 +- .../src/routes/SecretShow.tsx | 3 +- .../src/routes/TaskShow.tsx | 3 +- 22 files changed, 185 insertions(+), 159 deletions(-) diff --git a/spiffworkflow-frontend/src/App.tsx b/spiffworkflow-frontend/src/App.tsx index 0444f042..0684465e 100644 --- a/spiffworkflow-frontend/src/App.tsx +++ b/spiffworkflow-frontend/src/App.tsx @@ -1,5 +1,6 @@ import { useMemo, useState } from 'react'; -import { Container } from 'react-bootstrap'; +// @ts-ignore +// import { Container } from '@carbon/react'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; import ErrorContext from './contexts/ErrorContext'; @@ -48,7 +49,7 @@ export default function App() { return ( - +
{errorTag} @@ -70,7 +71,7 @@ export default function App() { - +
); } diff --git a/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx b/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx index 6956dadc..663bae1e 100644 --- a/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx +++ b/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; -import { Button, Modal } from 'react-bootstrap'; +// @ts-ignore +import { Button, Modal } from '@carbon/react'; type OwnProps = { description?: string; diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index 9b070a55..2f70ff0c 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -1,4 +1,6 @@ -import { Button, Navbar, Nav, Container } from 'react-bootstrap'; +// @ts-ignore +import { Button } from '@carbon/react'; +import { Navbar, Nav, Container } from 'react-bootstrap'; // @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 UserService from '../services/UserService'; diff --git a/spiffworkflow-frontend/src/components/PaginationForTable.tsx b/spiffworkflow-frontend/src/components/PaginationForTable.tsx index 2f648f52..c1cde1ab 100644 --- a/spiffworkflow-frontend/src/components/PaginationForTable.tsx +++ b/spiffworkflow-frontend/src/components/PaginationForTable.tsx @@ -1,7 +1,8 @@ import React from 'react'; import { Link } from 'react-router-dom'; -import { Dropdown, Stack } from 'react-bootstrap'; +// @ts-ignore +import { Dropdown, Stack } from '@carbon/react'; export const DEFAULT_PER_PAGE = 50; export const DEFAULT_PAGE = 1; @@ -29,139 +30,140 @@ export default function PaginationForTable({ }: OwnProps) { const PER_PAGE_OPTIONS = [2, 10, 50, 100]; - const buildPerPageDropdown = () => { - const perPageDropdownRows = (perPageOptions || PER_PAGE_OPTIONS).map( - (perPageOption) => { - if (perPageOption === perPage) { - return ( - - {perPageOption} - - ); - } - return ( - - {perPageOption} - - ); - } - ); - return ( - - - - Number to show: {perPage} - - - {perPageDropdownRows} - - - ); - }; - - const buildPaginationNav = () => { - let previousPageTag = null; - if (page === 1) { - previousPageTag = ( -
  • - - « - -
  • - ); - } else { - previousPageTag = ( -
  • - - « - -
  • - ); - } - - let nextPageTag = null; - if (page >= pagination.pages) { - nextPageTag = ( -
  • - - » - -
  • - ); - } else { - nextPageTag = ( -
  • - - » - -
  • - ); - } - - let startingNumber = (page - 1) * perPage + 1; - let endingNumber = page * perPage; - if (endingNumber > pagination.total) { - endingNumber = pagination.total; - } - if (startingNumber > pagination.total) { - startingNumber = pagination.total; - } - - return ( - -

    - {startingNumber}-{endingNumber} of{' '} - {pagination.total} -

    - -
    - ); - }; - - return ( -
    - {buildPaginationNav()} - {tableToDisplay} - {buildPerPageDropdown()} -
    - ); + // const buildPerPageDropdown = () => { + // const perPageDropdownRows = (perPageOptions || PER_PAGE_OPTIONS).map( + // (perPageOption) => { + // if (perPageOption === perPage) { + // return ( + // + // {perPageOption} + // + // ); + // } + // return ( + // + // {perPageOption} + // + // ); + // } + // ); + // return ( + // + // + // + // Number to show: {perPage} + // + // + // {perPageDropdownRows} + // + // + // ); + // }; + // + // const buildPaginationNav = () => { + // let previousPageTag = null; + // if (page === 1) { + // previousPageTag = ( + //
  • + // + // « + // + //
  • + // ); + // } else { + // previousPageTag = ( + //
  • + // + // « + // + //
  • + // ); + // } + // + // let nextPageTag = null; + // if (page >= pagination.pages) { + // nextPageTag = ( + //
  • + // + // » + // + //
  • + // ); + // } else { + // nextPageTag = ( + //
  • + // + // » + // + //
  • + // ); + // } + // + // let startingNumber = (page - 1) * perPage + 1; + // let endingNumber = page * perPage; + // if (endingNumber > pagination.total) { + // endingNumber = pagination.total; + // } + // if (startingNumber > pagination.total) { + // startingNumber = pagination.total; + // } + // + // return ( + // + //

    + // {startingNumber}-{endingNumber} of{' '} + // {pagination.total} + //

    + // + //
    + // ); + // }; + // + // return ( + //
    + // {buildPaginationNav()} + // {tableToDisplay} + // {buildPerPageDropdown()} + //
    + // ); + return null; } diff --git a/spiffworkflow-frontend/src/routes/AuthenticationList.tsx b/spiffworkflow-frontend/src/routes/AuthenticationList.tsx index 6be0f054..a249aa27 100644 --- a/spiffworkflow-frontend/src/routes/AuthenticationList.tsx +++ b/spiffworkflow-frontend/src/routes/AuthenticationList.tsx @@ -1,5 +1,6 @@ import { useContext, useEffect, useState } from 'react'; -import { Table } from 'react-bootstrap'; +// @ts-ignore +import { Table } from '@carbon/react'; import ErrorContext from '../contexts/ErrorContext'; import { AuthenticationItem } from '../interfaces'; import HttpService from '../services/HttpService'; diff --git a/spiffworkflow-frontend/src/routes/MessageInstanceList.tsx b/spiffworkflow-frontend/src/routes/MessageInstanceList.tsx index 90c74c29..6bc0a57d 100644 --- a/spiffworkflow-frontend/src/routes/MessageInstanceList.tsx +++ b/spiffworkflow-frontend/src/routes/MessageInstanceList.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react'; -import { Table } from 'react-bootstrap'; +// @ts-ignore +import { Table } from '@carbon/react'; import { Link, useParams, useSearchParams } from 'react-router-dom'; import PaginationForTable from '../components/PaginationForTable'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; diff --git a/spiffworkflow-frontend/src/routes/ProcessGroupEdit.tsx b/spiffworkflow-frontend/src/routes/ProcessGroupEdit.tsx index 83379999..05f75fb1 100644 --- a/spiffworkflow-frontend/src/routes/ProcessGroupEdit.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessGroupEdit.tsx @@ -1,6 +1,7 @@ import { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; -import { Button, Stack } from 'react-bootstrap'; +// @ts-ignore +import { Button, Stack } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import HttpService from '../services/HttpService'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; diff --git a/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx b/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx index 3c1be15c..29dc3882 100644 --- a/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx @@ -1,6 +1,8 @@ import { useEffect, useState } from 'react'; import { Link, useNavigate, useSearchParams } from 'react-router-dom'; -import { Button, Form, InputGroup, Table } from 'react-bootstrap'; +// @ts-ignore +import { Button, Form, Table } from '@carbon/react'; +import { InputGroup } from 'react-bootstrap'; import { Typeahead } from 'react-bootstrap-typeahead'; import { Option } from 'react-bootstrap-typeahead/types/types'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; diff --git a/spiffworkflow-frontend/src/routes/ProcessGroupShow.tsx b/spiffworkflow-frontend/src/routes/ProcessGroupShow.tsx index 522058c3..02625a8f 100644 --- a/spiffworkflow-frontend/src/routes/ProcessGroupShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessGroupShow.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; import { Link, useSearchParams, useParams } from 'react-router-dom'; -import { Button, Table, Stack } from 'react-bootstrap'; +// @ts-ignore +import { Button, Table, Stack } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import PaginationForTable from '../components/PaginationForTable'; import HttpService from '../services/HttpService'; diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx index a2e2aca2..32bf92ba 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx @@ -6,7 +6,9 @@ import { useSearchParams, } from 'react-router-dom'; -import { Button, Table, Stack, Form, InputGroup } from 'react-bootstrap'; +// @ts-ignore +import { Button, Table, Stack, Form } from '@carbon/react'; +import { InputGroup } from 'react-bootstrap'; // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'reac... Remove this comment to see the full error message import DatePicker from 'react-datepicker'; import { Typeahead } from 'react-bootstrap-typeahead'; diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx index f2d297f0..43a3ef1d 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react'; -import { Table } from 'react-bootstrap'; +// @ts-ignore +import { Table } from '@carbon/react'; import { useParams, useSearchParams } from 'react-router-dom'; import PaginationForTable from '../components/PaginationForTable'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceReportList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceReportList.tsx index 2d129837..4422b174 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceReportList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceReportList.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react'; -import { Button, Table } from 'react-bootstrap'; +// @ts-ignore +import { Button, Table } from '@carbon/react'; import { useParams, Link } from 'react-router-dom'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import HttpService from '../services/HttpService'; diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceReportShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceReportShow.tsx index a006d4b9..1d26e57f 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceReportShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceReportShow.tsx @@ -1,7 +1,8 @@ import { useEffect, useState } from 'react'; import { useParams, useSearchParams } from 'react-router-dom'; -import { Button, Table } from 'react-bootstrap'; +// @ts-ignore +import { Button, Table } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import PaginationForTable from '../components/PaginationForTable'; diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index f5336df8..0d97e608 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -1,7 +1,8 @@ import { useContext, useEffect, useState } from 'react'; import Editor from '@monaco-editor/react'; import { useParams, useNavigate, Link } from 'react-router-dom'; -import { Button, Modal, Stack } from 'react-bootstrap'; +// @ts-ignore +import { Button, Modal, Stack } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import HttpService from '../services/HttpService'; import ReactDiagramEditor from '../components/ReactDiagramEditor'; diff --git a/spiffworkflow-frontend/src/routes/ProcessModelEdit.tsx b/spiffworkflow-frontend/src/routes/ProcessModelEdit.tsx index 4567d14a..45648b1f 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelEdit.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelEdit.tsx @@ -1,6 +1,7 @@ import { useState, useEffect, useContext } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; -import { Button, Stack } from 'react-bootstrap'; +// @ts-ignore +import { Button, Stack } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import HttpService from '../services/HttpService'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; diff --git a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx index 2382fce7..aa1eafc6 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx @@ -1,6 +1,7 @@ import { useContext, useEffect, useRef, useState } from 'react'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; -import { Button, Modal, Stack } from 'react-bootstrap'; +// @ts-ignore +import { Button, Modal, Stack } from '@carbon/react'; import Container from 'react-bootstrap/Container'; import Row from 'react-bootstrap/Row'; import Col from 'react-bootstrap/Col'; diff --git a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx index 63217d2d..b12aee95 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx @@ -1,6 +1,7 @@ import { useContext, useEffect, useState } from 'react'; import { Link, useParams } from 'react-router-dom'; -import { Button, Stack } from 'react-bootstrap'; +// @ts-ignore +import { Button, Stack } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import FileInput from '../components/FileInput'; import HttpService from '../services/HttpService'; diff --git a/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx b/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx index 40dd2d89..2b92ea19 100644 --- a/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx +++ b/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx @@ -1,7 +1,8 @@ import { useEffect, useState } from 'react'; import Editor from '@monaco-editor/react'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; -import { Button, Modal } from 'react-bootstrap'; +// @ts-ignore +import { Button, Modal } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import HttpService from '../services/HttpService'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; diff --git a/spiffworkflow-frontend/src/routes/SecretList.tsx b/spiffworkflow-frontend/src/routes/SecretList.tsx index 2b9e756a..7d165c01 100644 --- a/spiffworkflow-frontend/src/routes/SecretList.tsx +++ b/spiffworkflow-frontend/src/routes/SecretList.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; import { Link, useSearchParams } from 'react-router-dom'; -import { Button, Table } from 'react-bootstrap'; +// @ts-ignore +import { Button, Table } from '@carbon/react'; import { MdDelete } from 'react-icons/md'; import PaginationForTable from '../components/PaginationForTable'; import HttpService from '../services/HttpService'; diff --git a/spiffworkflow-frontend/src/routes/SecretNew.tsx b/spiffworkflow-frontend/src/routes/SecretNew.tsx index 7a697a98..e1f34e89 100644 --- a/spiffworkflow-frontend/src/routes/SecretNew.tsx +++ b/spiffworkflow-frontend/src/routes/SecretNew.tsx @@ -1,6 +1,7 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Stack } from 'react-bootstrap'; +// @ts-ignore +import { Stack } from '@carbon/react'; import Button from 'react-bootstrap/Button'; import Form from 'react-bootstrap/Form'; import HttpService from '../services/HttpService'; diff --git a/spiffworkflow-frontend/src/routes/SecretShow.tsx b/spiffworkflow-frontend/src/routes/SecretShow.tsx index 707f0d14..2ee75e19 100644 --- a/spiffworkflow-frontend/src/routes/SecretShow.tsx +++ b/spiffworkflow-frontend/src/routes/SecretShow.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; -import { Stack, Table, Button } from 'react-bootstrap'; +// @ts-ignore +import { Stack, Table, Button } from '@carbon/react'; import HttpService from '../services/HttpService'; import { Secret } from '../interfaces'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx index 28fc8582..97bb5d3c 100644 --- a/spiffworkflow-frontend/src/routes/TaskShow.tsx +++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx @@ -1,7 +1,8 @@ import { useContext, useEffect, useState } from 'react'; import { Link, useNavigate, useParams } from 'react-router-dom'; import Form from '@rjsf/core'; -import { Button, Stack } from 'react-bootstrap'; +// @ts-ignore +import { Button, Stack } from '@carbon/react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; From 3f8559e837a7970b34298b2d2c27589f4307a00f Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 31 Oct 2022 16:03:14 -0400 Subject: [PATCH 03/46] some more changes... navigation bar is broken due to header container w/ burnettk --- spiffworkflow-frontend/src/App.tsx | 20 +- .../src/components/NavigationBar.tsx | 204 +++++++++++++----- spiffworkflow-frontend/src/index.css | 4 +- .../src/routes/ProcessModelEditDiagram.tsx | 8 +- 4 files changed, 161 insertions(+), 75 deletions(-) diff --git a/spiffworkflow-frontend/src/App.tsx b/spiffworkflow-frontend/src/App.tsx index 0684465e..4a078c21 100644 --- a/spiffworkflow-frontend/src/App.tsx +++ b/spiffworkflow-frontend/src/App.tsx @@ -1,6 +1,6 @@ import { useMemo, useState } from 'react'; // @ts-ignore -// import { Container } from '@carbon/react'; +import { Content } from '@carbon/react'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; import ErrorContext from './contexts/ErrorContext'; @@ -46,14 +46,14 @@ export default function App() { ); } + // return ( - -
    - {errorTag} - - - + + + + {errorTag} +
    } /> @@ -69,9 +69,9 @@ export default function App() { />
    -
    -
    -
    + + +
    ); } diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index 2f70ff0c..606a2118 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -1,72 +1,158 @@ -// @ts-ignore -import { Button } from '@carbon/react'; -import { Navbar, Nav, Container } from 'react-bootstrap'; +import { + Header, + Theme, + HeaderName, + HeaderContainer, + HeaderNavigation, + HeaderMenuItem, + HeaderMenu, + // @ts-ignore +} from '@carbon/react'; +import { useEffect, useState } from 'react'; +import { Navbar, Nav } from 'react-bootstrap'; +import { useLocation } from 'react-router-dom'; // @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 UserService from '../services/UserService'; // for ref: https://react-bootstrap.github.io/components/navbar/ export default function NavigationBar() { - const navElements = null; + // const navElements = null; + // + // const handleLogout = () => { + // UserService.doLogout(); + // }; + // + // const handleLogin = () => { + // UserService.doLogin(); + // }; + // + // const loginLink = () => { + // if (!UserService.isLoggedIn()) { + // return ( + // + // + // + // + // + // ); + // } + // return null; + // }; + // + // const logoutLink = () => { + // if (UserService.isLoggedIn()) { + // return ( + // + // + // Signed in as: {UserService.getUsername()} + // + // + // + // + // + // ); + // } + // return null; + // }; - const handleLogout = () => { - UserService.doLogout(); - }; + const location = useLocation(); + const [activeKey, setActiveKey] = useState(''); - const handleLogin = () => { - UserService.doLogin(); - }; - - const loginLink = () => { - if (!UserService.isLoggedIn()) { - return ( - - - - - - ); + useEffect(() => { + let newActiveKey = '/admin/process-groups'; + if (location.pathname.match(/^\/admin\/messages\b/)) { + newActiveKey = '/admin/messages'; + } else if (location.pathname.match(/^\/admin\/process-instances\b/)) { + newActiveKey = '/admin/process-instances'; + } else if (location.pathname.match(/^\/admin\/secrets\b/)) { + newActiveKey = '/admin/secrets'; + } else if (location.pathname.match(/^\/admin\/authentications\b/)) { + newActiveKey = '/admin/authentications'; + } else if (location.pathname === '/') { + newActiveKey = '/'; + } else if (location.pathname.match(/^\/tasks\b/)) { + newActiveKey = '/'; } - return null; + setActiveKey(newActiveKey); + }, [location]); + + const isActivePage = (menuItemPath: string) => { + return activeKey === menuItemPath; }; - const logoutLink = () => { - if (UserService.isLoggedIn()) { - return ( - - - Signed in as: {UserService.getUsername()} - - - - - - ); - } - return null; - }; + // return ( + // + // + // + // + // logo + // + // + // + // + // + // {loginLink()} + // {logoutLink()} + // + // + // + // ); - return ( - - - - logo - - - - - - {loginLink()} - {logoutLink()} - - - ); + // + //
    + // + // [Platform] + // + // + // Link 1 + // Link 2 + // Link 3 + // + // Sub-link 1 + // Sub-link 2 + // Sub-link 3 + // + // + //
    + //
    + if (activeKey) { + return ( + + +
    + + logo + + + + Home + + + Processes + + + Process Instances + + +
    +
    +
    + ); + } + return null; } diff --git a/spiffworkflow-frontend/src/index.css b/spiffworkflow-frontend/src/index.css index fd156c23..32a712d5 100644 --- a/spiffworkflow-frontend/src/index.css +++ b/spiffworkflow-frontend/src/index.css @@ -20,8 +20,8 @@ span.bjs-crumb { } .app-logo { - height: 35%; - width: 35%; + height: 90%; + width: 90%; margin-top: 1em; margin-bottom: 1em; } diff --git a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx index aa1eafc6..9ca0754a 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx @@ -1,8 +1,8 @@ import { useContext, useEffect, useRef, useState } from 'react'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; // @ts-ignore -import { Button, Modal, Stack } from '@carbon/react'; -import Container from 'react-bootstrap/Container'; +import { Button, Modal, Stack, Content } from '@carbon/react'; +// import Container from 'react-bootstrap/Container'; import Row from 'react-bootstrap/Row'; import Col from 'react-bootstrap/Col'; @@ -463,7 +463,7 @@ export default function ProcessModelEditDiagram() { } return (
    - + + // + // + // + //
    + //
    + // + // ); + // return ( + // + // + // Hello1 + // + // + // Hello2 + // + // + // ); + // return ( + // + // + // + // Span 25% + // + // + // Span 25% + // + // + // Span 25% + // + // + // Span 25% + // + // + // + // ); return ( -
    -
    -
    -
    - - {processModelSearch()} - -
    - - {dateComponent( - 'Start Range: ', - 'start-from', - startFrom, - setStartFrom - )} - {dateComponent('-', 'start-till', startTill, setStartTill)} - -
    - - {dateComponent( - 'End Range: \u00A0\u00A0', - 'end-from', - endFrom, - setEndFrom - )} - {dateComponent('-', 'end-till', endTill, setEndTill)} - -
    - - {processStatusSearch()} - - - - -
    -
    -
    -
    -
    + <> + + + { + if (processModel) { + return `${processModel.process_group_id}/${ + processModel.id + } (${truncateString(processModel.display_name, 20)})`; + } + return null; + }} + shouldFilterItem={shouldFilterProcessModel} + placeholder="Process Model" + titleText="Process model" + /> + + {processStatusSearch()} + + + + + + + + + + + + + + + ); }; @@ -444,14 +561,6 @@ export default function ProcessInstanceList() { <> {processInstanceTitleElement()} {filterOptions()} - ); } From b31afd11684d5cb6b7b05e8c5cb050d97b3a7bac Mon Sep 17 00:00:00 2001 From: jasquat Date: Tue, 1 Nov 2022 17:00:59 -0400 Subject: [PATCH 05/46] filter is working for status and process model w/ burnettk --- .../src/routes/ProcessInstanceList.tsx | 204 +++++++----------- 1 file changed, 74 insertions(+), 130 deletions(-) diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx index 498ca916..b6f87262 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx @@ -24,7 +24,6 @@ import { InputGroup } from 'react-bootstrap'; // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'reac... Remove this comment to see the full error message import DatePicker from 'react-datepicker'; import { Typeahead } from 'react-bootstrap-typeahead'; -import { Option } from 'react-bootstrap-typeahead/types/types'; import { PROCESS_STATUSES, DATE_FORMAT } from '../config'; import { convertDateToSeconds, @@ -61,10 +60,11 @@ export default function ProcessInstanceList() { const setErrorMessage = (useContext as any)(ErrorContext)[1]; - const [processStatusSelectionOptions, setProcessStatusSelectionOptions] = - useState([]); + const [processStatusAllOptions, setProcessStatusAllOptions] = useState( + [] + ); const [processStatusSelection, setProcessStatusSelection] = useState< - Option[] + string[] >([]); const [processModelAvailableItems, setProcessModelAvailableItems] = useState< ProcessModel[] @@ -72,9 +72,8 @@ export default function ProcessInstanceList() { const [processModelFilteredItems, setProcessModelFilteredItems] = useState< ProcessModel[] >([]); - const [processModelSelection, setProcessModelSelection] = useState( - [] - ); + const [processModelSelection, setProcessModelSelection] = + useState(null); const parametersToAlwaysFilterBy = useMemo(() => { return { @@ -138,25 +137,25 @@ export default function ProcessInstanceList() { const label = `${item.process_group_id}/${item.id}`; Object.assign(item, { label }); if (label === processModelFullIdentifier) { - setProcessModelSelection([item]); + setProcessModelSelection(item); } return item; }); setProcessModelAvailableItems(selectionArray); setProcessModelFilteredItems(selectionArray); - const processStatusSelectedArray: Option[] = []; - const processStatusSelectionArray = PROCESS_STATUSES.map( + const processStatusSelectedArray: string[] = []; + const processStatusAllOptionsArray = PROCESS_STATUSES.map( (processStatusOption: any) => { const regex = new RegExp(`\\b${processStatusOption}\\b`); if ((searchParams.get('process_status') || '').match(regex)) { - processStatusSelectedArray.push({ label: processStatusOption }); + processStatusSelectedArray.push(processStatusOption); } - return { label: processStatusOption }; + return processStatusOption; } ); setProcessStatusSelection(processStatusSelectedArray); - setProcessStatusSelectionOptions(processStatusSelectionArray); + setProcessStatusAllOptions(processStatusAllOptionsArray); getProcessInstances(); } @@ -191,7 +190,7 @@ export default function ProcessInstanceList() { } }; - const handleFilter = (event: any) => { + const applyFilter = (event: any) => { event.preventDefault(); const { page, perPage } = getPageInfoFromSearchParams(searchParams); let queryParamString = `per_page=${perPage}&page=${page}`; @@ -226,16 +225,11 @@ export default function ProcessInstanceList() { queryParamString += `&end_till=${endTill}`; } if (processStatusSelection.length > 0) { - const processStatusSelectionString = processStatusSelection.map( - (pss: any) => { - return pss.label; - } - ); - queryParamString += `&process_status=${processStatusSelectionString}`; + queryParamString += `&process_status=${processStatusSelection}`; } - if (processModelSelection.length > 0) { - const currentProcessModel: any = processModelSelection[0]; - queryParamString += `&process_group_identifier=${currentProcessModel.process_group_id}&process_model_identifier=${currentProcessModel.id}`; + + if (processModelSelection) { + queryParamString += `&process_group_identifier=${processModelSelection.process_group_id}&process_model_identifier=${processModelSelection.id}`; } setErrorMessage(null); @@ -294,65 +288,6 @@ export default function ProcessInstanceList() { return queryParamString; }; - const processModelSearch = () => { - return ( - - - - Process Model:{' '} - - - - - ); - }; - - const processStatusSearch = () => { - // return ( - // - // - // - // Process Status:{' '} - // - // - // - // - // ); - return ( - { - return item.label || ''; - }} - selectionFeedback="top-after-reopen" - /> - ); - }; const shouldFilterProcessModel = (options: any) => { const processModel: ProcessModel = options.item; const { inputValue } = options; @@ -363,6 +298,54 @@ export default function ProcessInstanceList() { ); }; + const processModelSearch = () => { + return ( + + setProcessModelSelection(selection.selectedItem) + } + id="process-model-select" + items={processModelFilteredItems} + itemToString={(processModel: ProcessModel) => { + if (processModel) { + return `${processModel.process_group_id}/${ + processModel.id + } (${truncateString(processModel.display_name, 20)})`; + } + return null; + }} + shouldFilterItem={shouldFilterProcessModel} + placeholder="Choose a process model" + titleText="Process model" + selectedItem={processModelSelection} + /> + ); + }; + + const processStatusSearch = () => { + return ( + { + setProcessStatusSelection(selection.selectedItems); + }} + itemToString={(item: any) => { + return item || ''; + }} + selectionFeedback="top-after-reopen" + selectedItems={processStatusSelection} + /> + ); + }; + + const clearFilters = () => { + setProcessModelSelection(null); + setProcessStatusSelection([]); + }; + const filterOptions = () => { const { page, perPage } = getPageInfoFromSearchParams(searchParams); // return ( @@ -408,64 +391,25 @@ export default function ProcessInstanceList() { //
    // // ); - // return ( - // - // - // Hello1 - // - // - // Hello2 - // - // - // ); - // return ( - // - // - // - // Span 25% - // - // - // Span 25% - // - // - // Span 25% - // - // - // Span 25% - // - // - // - // ); return ( <> - - { - if (processModel) { - return `${processModel.process_group_id}/${ - processModel.id - } (${truncateString(processModel.display_name, 20)})`; - } - return null; - }} - shouldFilterItem={shouldFilterProcessModel} - placeholder="Process Model" - titleText="Process model" - /> - + {processModelSearch()} {processStatusSearch()} - - + From bc965ae939a4eb592a7bd263f444243d1328f394 Mon Sep 17 00:00:00 2001 From: jasquat Date: Tue, 1 Nov 2022 17:30:55 -0400 Subject: [PATCH 06/46] a simple date picker w/ burnettk --- spiffworkflow-frontend/src/config.tsx | 2 +- .../src/routes/ProcessInstanceList.tsx | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/spiffworkflow-frontend/src/config.tsx b/spiffworkflow-frontend/src/config.tsx index 94519ba8..573e96bf 100644 --- a/spiffworkflow-frontend/src/config.tsx +++ b/spiffworkflow-frontend/src/config.tsx @@ -16,4 +16,4 @@ export const PROCESS_STATUSES = [ 'suspended', ]; -export const DATE_FORMAT = 'yyyy-MM-dd HH:mm:ss'; +export const DATE_FORMAT = 'Y-m-d'; diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx index b6f87262..214ab936 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx @@ -9,6 +9,8 @@ import { import { Button, ButtonSet, + DatePicker, + DatePickerInput, Table, Stack, Form, @@ -21,8 +23,6 @@ import { // @ts-ignore } from '@carbon/react'; import { InputGroup } from 'react-bootstrap'; -// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'reac... Remove this comment to see the full error message -import DatePicker from 'react-datepicker'; import { Typeahead } from 'react-bootstrap-typeahead'; import { PROCESS_STATUSES, DATE_FORMAT } from '../config'; import { @@ -397,6 +397,20 @@ export default function ProcessInstanceList() { {processModelSearch()} {processStatusSearch()} + + + {' '} + + + + + From fdec3e1e1250529469a983da09cf405ca44bb06c Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Wed, 2 Nov 2022 11:35:10 -0400 Subject: [PATCH 07/46] SpiffWorkflow cold start improvements (#13) --- .../SpiffWorkflow/bpmn/PythonScriptEngine.py | 15 +----- SpiffWorkflow/SpiffWorkflow/specs/Celery.py | 12 ++--- SpiffWorkflow/doc/bpmn/advanced.rst | 27 +++++------ SpiffWorkflow/requirements.txt | 2 - SpiffWorkflow/setup.py | 2 +- .../bpmn/NITimerDurationBoundaryTest.py | 12 +++-- .../bpmn/PythonScriptEngineTest.py | 21 -------- .../SpiffWorkflow/bpmn/TooManyLoopsTest.py | 18 +++++-- .../bpmn/data/timer-date-start.bpmn | 4 +- .../bpmn/events/TimerCycleStartTest.py | 6 ++- .../bpmn/events/TimerCycleTest.py | 6 ++- .../bpmn/events/TimerDateTest.py | 12 +++-- .../events/TimerDurationBoundaryOnTaskTest.py | 7 ++- .../bpmn/events/TimerDurationTest.py | 9 +++- .../ExternalMessageBoundaryEventTest.py | 2 +- .../camunda/MessageBoundaryEventTest.py | 9 +++- .../dmn/python_engine/PythonDecisionRunner.py | 4 +- spiffworkflow-backend/poetry.lock | 48 ++++++++++++++----- spiffworkflow-backend/pyproject.toml | 4 ++ .../routes/process_api_blueprint.py | 4 +- .../scripts/get_frontend_url.py | 3 +- .../services/process_instance_processor.py | 33 +++++++------ 22 files changed, 146 insertions(+), 114 deletions(-) diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/PythonScriptEngine.py b/SpiffWorkflow/SpiffWorkflow/bpmn/PythonScriptEngine.py index 17772992..5d45ceb0 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/PythonScriptEngine.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/PythonScriptEngine.py @@ -3,23 +3,10 @@ import ast import copy import sys import traceback -import datetime - -import dateparser -import pytz from SpiffWorkflow.bpmn.exceptions import WorkflowTaskExecException from ..operators import Operator -# Would love to get rid of this altogether, as it rightly belongs in the -# backend, but leaving it here because that's the path of least resistance. -DEFAULT_GLOBALS = { - 'timedelta': datetime.timedelta, - 'datetime': datetime, - 'dateparser': dateparser, - 'pytz': pytz, -} - # Copyright (C) 2020 Kelly McDonald # @@ -112,7 +99,7 @@ class PythonScriptEngine(object): def __init__(self, default_globals=None, scripting_additions=None): - self.globals = default_globals or DEFAULT_GLOBALS + self.globals = default_globals or {} self.globals.update(scripting_additions or {}) self.error_tasks = {} diff --git a/SpiffWorkflow/SpiffWorkflow/specs/Celery.py b/SpiffWorkflow/SpiffWorkflow/specs/Celery.py index 35288d24..85c4d134 100644 --- a/SpiffWorkflow/SpiffWorkflow/specs/Celery.py +++ b/SpiffWorkflow/SpiffWorkflow/specs/Celery.py @@ -25,13 +25,6 @@ from .base import TaskSpec from ..operators import valueof, Attrib, PathAttrib from ..util.deep_merge import DeepMerge -try: - from celery.app import default_app -except ImportError: - have_celery = False -else: - have_celery = True - logger = logging.getLogger('spiff') @@ -111,7 +104,10 @@ class Celery(TaskSpec): :type kwargs: dict :param kwargs: kwargs to pass to celery task. """ - if not have_celery: + + try: + from celery.app import default_app + except ImportError: raise Exception("Unable to import python-celery imports.") assert wf_spec is not None assert name is not None diff --git a/SpiffWorkflow/doc/bpmn/advanced.rst b/SpiffWorkflow/doc/bpmn/advanced.rst index af96317d..d6442b87 100644 --- a/SpiffWorkflow/doc/bpmn/advanced.rst +++ b/SpiffWorkflow/doc/bpmn/advanced.rst @@ -74,7 +74,7 @@ Serialization .. warning:: - Serialization Changed in Version 1.1.7. + Serialization Changed in Version 1.1.7. Support for pre-1.1.7 serialization will be dropped in a future release. The old serialization method still works but it is deprecated. To migrate your system to the new version, see "Migrating between @@ -85,8 +85,8 @@ setting. This may not always be the case, we may be executing the workflow in th may have a user request a web page where we open a specific workflow that we may be in the middle of, do one step of that workflow and then the user may be back in a few minutes, or maybe a few hours depending on the application. -The :code:`BpmnWorkflowSerializer` class contains a serializer for a workflow containing only standard BPMN Tasks. -Since we are using custom task classes (the Camunda :code:`UserTask` and the DMN :code:`BusinessRuleTask`), +The :code:`BpmnWorkflowSerializer` class contains a serializer for a workflow containing only standard BPMN Tasks. +Since we are using custom task classes (the Camunda :code:`UserTask` and the DMN :code:`BusinessRuleTask`), we'll need to supply serializers for those task specs as well. Strictly speaking, these are not serializers per se: they actually convert the tasks into dictionaries of @@ -138,7 +138,7 @@ two components: - a data converter (which handles workflow and task data). The default workflow spec converter likely to meet your needs, either on its own, or with the inclusion of -:code:`UserTask` and :code:`BusinessRuleTask` in the :code:`camnuda` or :code:`spiff` and :code:`dmn` subpackages +:code:`UserTask` and :code:`BusinessRuleTask` in the :code:`camnuda` or :code:`spiff` and :code:`dmn` subpackages of this library, and all you'll need to do is add them to the list of task converters, as we did above. However, he default data converter is very simple, adding only JSON-serializable conversions of :code:`datetime` @@ -180,7 +180,7 @@ If you have written any custom task specs, you'll need to implement task spec co Task Spec converters are also based on the :code:`DictionaryConverter`. You should be able to use the `BpmnTaskSpecConverter `_ -as a basis for your custom specs. It provides some methods for extracting attributes from Spiff base classes as well as +as a basis for your custom specs. It provides some methods for extracting attributes from Spiff base classes as well as standard BPNN attributes from tasks that inherit from :code:`BMPNSpecMixin`. The `Camunda User Task Converter `_ @@ -221,7 +221,7 @@ serialize the workflow in the new format: new_json = serializer.serialize_json(workflow) However, if you use custom tasks or data serialization, you'll also need to specify workflow spec or data -serializers, as in the examples in the previous section, before you'll be able to serialize with the new serializer. +serializers, as in the examples in the previous section, before you'll be able to serialize with the new serializer. The code would then look more like this: .. code:: python @@ -244,7 +244,7 @@ The code would then look more like this: new_json = serializer.serialize_json(workflow) Because the serializer is highly customizable, we've made it possible for you to manage your own versions of the -serialization. You can do this by passing a version number into the serializer, which will be embedded in the +serialization. You can do this by passing a version number into the serializer, which will be embedded in the json of all workflows. This allow you to modify the serialization and customize it over time, and still manage the different forms as you make adjustments without leaving people behind. @@ -253,11 +253,11 @@ Versioned Serializer As we make changes to Spiff, we may change the serialization format. For example, in 1.1.8, we changed how subprocesses were handled interally in BPMN workflows and updated how they are serialized. If you have -not overridden our version number with one of your own, the serializer will transform the 1.0 format to the +not overridden our version number with one of your own, the serializer will transform the 1.0 format to the new 1.1 format. If you've overridden the serializer version, you may need to incorporate our serialization changes with -your own. You can find our conversions in +your own. You can find our conversions in `version_migrations.py `_ Custom Script Engines @@ -277,14 +277,9 @@ We'll cover a simple extension of custom script engine here. There is also an e a similar engine based on `RestrictedPython `_ included alongside this example. -The default script engine imports the following objects: +The default script engine does not import any objects. -- :code:`timedelta` -- :code:`datetime` -- :code:`dateparser` -- :code:`pytz` - -You could add other functions or classes from the standard python modules or any code you've +You could add functions or classes from the standard python modules or any code you've implemented yourself. Your global environment can be passed in using the `default_globals` argument when initializing the script engine. In our RestrictedPython example, we use their `safe_globals` which prevents users from executing some potentially unsafe operations. diff --git a/SpiffWorkflow/requirements.txt b/SpiffWorkflow/requirements.txt index 63b4abb4..bf928ff6 100644 --- a/SpiffWorkflow/requirements.txt +++ b/SpiffWorkflow/requirements.txt @@ -3,6 +3,4 @@ celery==5.2.3 coverage lxml -dateparser -pytz . diff --git a/SpiffWorkflow/setup.py b/SpiffWorkflow/setup.py index 15522250..ad4c2163 100644 --- a/SpiffWorkflow/setup.py +++ b/SpiffWorkflow/setup.py @@ -22,7 +22,7 @@ setup(name='SpiffWorkflow', license='lGPLv2', packages=find_packages(exclude=['tests', 'tests.*']), package_data={'SpiffWorkflow.bpmn.parser.schema': ['*.xsd']}, - install_requires=['configparser', 'lxml', 'celery', 'dateparser', 'pytz', + install_requires=['configparser', 'lxml', 'celery', # required for python 3.7 - https://stackoverflow.com/a/73932581 'importlib-metadata<5.0; python_version <= "3.7"'], keywords='spiff workflow bpmn engine', diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/NITimerDurationBoundaryTest.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/NITimerDurationBoundaryTest.py index f7cef40b..97553a6d 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/NITimerDurationBoundaryTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/NITimerDurationBoundaryTest.py @@ -5,6 +5,7 @@ import datetime import time from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.workflow import BpmnWorkflow +from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase __author__ = 'kellym' @@ -15,11 +16,12 @@ class NITimerDurationTest(BpmnWorkflowTestCase): Non-Interrupting Timer boundary test """ def setUp(self): + self.script_engine = PythonScriptEngine(default_globals={"timedelta": datetime.timedelta}) spec, subprocesses = self.load_workflow_spec('timer-non-interrupt-boundary.bpmn', 'NonInterruptTimer') - self.workflow = BpmnWorkflow(spec, subprocesses) + self.workflow = BpmnWorkflow(spec, subprocesses, script_engine=self.script_engine) def load_spec(self): - return + return def testRunThroughHappy(self): self.actual_test(save_restore=False) @@ -28,7 +30,7 @@ class NITimerDurationTest(BpmnWorkflowTestCase): self.actual_test(save_restore=True) def actual_test(self,save_restore = False): - + ready_tasks = self.workflow.get_tasks(TaskState.READY) self.assertEqual(1, len(ready_tasks)) self.workflow.complete_task_from_id(ready_tasks[0].id) @@ -47,7 +49,9 @@ class NITimerDurationTest(BpmnWorkflowTestCase): ready_tasks = self.workflow.get_tasks(TaskState.READY) if len(ready_tasks) > 1: break - if save_restore: self.save_restore() + if save_restore: + self.save_restore() + self.workflow.script_engine = self.script_engine #self.assertEqual(1, len(self.workflow.get_tasks(Task.WAITING))) time.sleep(0.1) self.workflow.complete_task_from_id(ready_tasks[0].id) diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/PythonScriptEngineTest.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/PythonScriptEngineTest.py index d5c8ec32..05ac0df0 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/PythonScriptEngineTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/PythonScriptEngineTest.py @@ -24,32 +24,11 @@ class PythonScriptEngineTest(BpmnWorkflowTestCase): workflow.do_engine_steps() self.task = workflow.last_task - def testDateTimeExpressions(self): - """Basically, assure that we can use datime, dateutils, and pytz""" - script = """ -# Create Current Date as UTC -now_utc = datetime.datetime.now(datetime.timezone.utc) -# Create Current Date at EST -now_est = now_utc.astimezone(pytz.timezone('US/Eastern')) - -# Format a date from a date String in UTC -datestr = "2021-09-23 16:11:00 -0000" # 12 pm EST, 4pm UTC -dt = dateparser.parse(datestr) -localtime = dt.astimezone(pytz.timezone('US/Eastern')) -localtime_str = localtime.strftime("%Y-%m-%d %H:%M:%S") - """ - self.expressionEngine.execute(self.task, script) - self.assertEqual(self.task.data['now_utc'].utcoffset().days, 0) - self.assertEqual(self.task.data['now_est'].tzinfo.zone, "US/Eastern") - self.assertEqual(self.task.data['localtime_str'], "2021-09-23 12:11:00") - self.assertTrue(True) - def testFunctionsAndGlobalsAreRemoved(self): self.assertIn('testvar', self.task.data) self.assertIn('testvar2', self.task.data) self.assertIn('sample', self.task.data) self.assertNotIn('my_function', self.task.data) - self.assertNotIn('datetime', self.task.data) def suite(): return unittest.TestLoader().loadTestsFromTestCase(PythonScriptEngineTest) diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/TooManyLoopsTest.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/TooManyLoopsTest.py index a1018e0b..072d9375 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/TooManyLoopsTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/TooManyLoopsTest.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import datetime import unittest from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine @@ -8,6 +9,15 @@ from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase __author__ = 'sartography' +class CustomScriptEngine(PythonScriptEngine): + """This is a custom script processor that can be easily injected into Spiff Workflow. + It will execute python code read in from the bpmn. It will also make any scripts in the + scripts directory available for execution. """ + def __init__(self): + augment_methods = { + 'timedelta': datetime.timedelta, + } + super().__init__(scripting_additions=augment_methods) class TooManyLoopsTest(BpmnWorkflowTestCase): @@ -23,7 +33,7 @@ class TooManyLoopsTest(BpmnWorkflowTestCase): def actual_test(self,save_restore = False): spec, subprocesses = self.load_workflow_spec('too_many_loops*.bpmn', 'loops') - self.workflow = BpmnWorkflow(spec, subprocesses, script_engine=PythonScriptEngine()) + self.workflow = BpmnWorkflow(spec, subprocesses, script_engine=CustomScriptEngine()) counter = 0 data = {} while not self.workflow.is_completed(): @@ -34,6 +44,7 @@ class TooManyLoopsTest(BpmnWorkflowTestCase): counter += 1 # There is a 10 millisecond wait task. if save_restore: self.save_restore() + self.workflow.script_engine = CustomScriptEngine() self.assertEqual(20, self.workflow.last_task.data['counter']) def test_with_sub_process(self): @@ -41,7 +52,7 @@ class TooManyLoopsTest(BpmnWorkflowTestCase): # right after a sub-process. So assuring this is fixed. counter = 0 spec, subprocesses = self.load_workflow_spec('too_many_loops_sub_process.bpmn', 'loops_sub') - self.workflow = BpmnWorkflow(spec, subprocesses, script_engine=PythonScriptEngine()) + self.workflow = BpmnWorkflow(spec, subprocesses, script_engine=CustomScriptEngine()) data = {} while not self.workflow.is_completed(): self.workflow.do_engine_steps() @@ -57,7 +68,7 @@ class TooManyLoopsTest(BpmnWorkflowTestCase): def test_with_two_call_activities(self): spec, subprocess = self.load_workflow_spec('sub_in_loop*.bpmn', 'main') - self.workflow = BpmnWorkflow(spec, subprocess) + self.workflow = BpmnWorkflow(spec, subprocess, script_engine=CustomScriptEngine()) self.workflow.do_engine_steps() for loop in range(3): ready = self.workflow.get_ready_user_tasks() @@ -66,6 +77,7 @@ class TooManyLoopsTest(BpmnWorkflowTestCase): self.workflow.refresh_waiting_tasks() self.workflow.do_engine_steps() self.save_restore() + self.workflow.script_engine = CustomScriptEngine() def suite(): return unittest.TestLoader().loadTestsFromTestCase(TooManyLoopsTest) diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/data/timer-date-start.bpmn b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/data/timer-date-start.bpmn index 7bead0fa..46f907b9 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/data/timer-date-start.bpmn +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/data/timer-date-start.bpmn @@ -7,8 +7,8 @@ Flow_1i73q45 Flow_00e79cz - futuredate = dateparser.parse('in 1 second') - timedelta(seconds=.95) -futuredate2 = dateparser.parse('September 1 2021 at 10am EDT') + futuredate = datetime.now() + timedelta(0, 1) - timedelta(seconds=.95) +futuredate2 = datetime.strptime('2021-09-01 10:00','%Y-%m-%d %H:%M') diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerCycleStartTest.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerCycleStartTest.py index e4e8741c..98de249c 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerCycleStartTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerCycleStartTest.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import datetime import unittest import time @@ -21,7 +22,10 @@ class CustomScriptEngine(PythonScriptEngine): It will execute python code read in from the bpmn. It will also make any scripts in the scripts directory available for execution. """ def __init__(self): - augment_methods = {'custom_function': my_custom_function} + augment_methods = { + 'custom_function': my_custom_function, + 'timedelta': datetime.timedelta, + } super().__init__(scripting_additions=augment_methods) diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerCycleTest.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerCycleTest.py index 8b9ad0f5..56b52730 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerCycleTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerCycleTest.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import datetime import unittest import time @@ -21,7 +22,10 @@ class CustomScriptEngine(PythonScriptEngine): It will execute python code read in from the bpmn. It will also make any scripts in the scripts directory available for execution. """ def __init__(self): - augment_methods = {'custom_function': my_custom_function} + augment_methods = { + 'custom_function': my_custom_function, + 'timedelta': datetime.timedelta, + } super().__init__(scripting_additions=augment_methods) diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDateTest.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDateTest.py index a82698b7..d6157ecd 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDateTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDateTest.py @@ -3,10 +3,10 @@ import unittest import datetime import time -import pytz from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.workflow import BpmnWorkflow +from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase __author__ = 'kellym' @@ -15,8 +15,12 @@ __author__ = 'kellym' class TimerDateTest(BpmnWorkflowTestCase): def setUp(self): + self.script_engine = PythonScriptEngine(default_globals={ + "datetime": datetime.datetime, + "timedelta": datetime.timedelta, + }) self.spec, self.subprocesses = self.load_workflow_spec('timer-date-start.bpmn', 'date_timer') - self.workflow = BpmnWorkflow(self.spec, self.subprocesses) + self.workflow = BpmnWorkflow(self.spec, self.subprocesses, script_engine=self.script_engine) def testRunThroughHappy(self): self.actual_test(save_restore=False) @@ -42,6 +46,7 @@ class TimerDateTest(BpmnWorkflowTestCase): break if save_restore: self.save_restore() + self.workflow.script_engine = self.script_engine waiting_tasks = self.workflow.get_tasks(TaskState.WAITING) @@ -50,8 +55,7 @@ class TimerDateTest(BpmnWorkflowTestCase): loopcount = loopcount +1 endtime = datetime.datetime.now() self.workflow.do_engine_steps() - tz = pytz.timezone('US/Eastern') - testdate = tz.localize(datetime.datetime.strptime('2021-09-01 10:00','%Y-%m-%d %H:%M')) + testdate = datetime.datetime.strptime('2021-09-01 10:00','%Y-%m-%d %H:%M') self.assertEqual(self.workflow.last_task.data['futuredate2'],testdate) self.assertTrue('completed' in self.workflow.last_task.data) self.assertTrue(self.workflow.last_task.data['completed']) diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py index ec743b54..e87de251 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py @@ -3,10 +3,12 @@ import unittest import datetime import time +from datetime import timedelta from SpiffWorkflow.bpmn.specs.events import EndEvent from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.workflow import BpmnWorkflow +from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase __author__ = 'kellym' @@ -14,8 +16,9 @@ __author__ = 'kellym' class TimerDurationTest(BpmnWorkflowTestCase): def setUp(self): + self.script_engine = PythonScriptEngine(default_globals={"timedelta": timedelta}) self.spec, self.subprocesses = self.load_workflow_spec('boundary_timer_on_task.bpmn', 'test_timer') - self.workflow = BpmnWorkflow(self.spec, self.subprocesses) + self.workflow = BpmnWorkflow(self.spec, self.subprocesses, script_engine=self.script_engine) def testRunThroughHappy(self): self.actual_test(save_restore=False) @@ -43,9 +46,11 @@ class TimerDurationTest(BpmnWorkflowTestCase): starttime = datetime.datetime.now() self.workflow = BpmnWorkflow(self.spec) + self.workflow.script_engine = self.script_engine self.workflow.do_engine_steps() if save_restore: self.save_restore() + self.workflow.script_engine = self.script_engine time.sleep(0.1) self.workflow.refresh_waiting_tasks() self.workflow.do_engine_steps() diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDurationTest.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDurationTest.py index 24b27eea..8026483d 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDurationTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDurationTest.py @@ -3,8 +3,10 @@ import unittest import datetime import time +from datetime import timedelta from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.workflow import BpmnWorkflow +from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase __author__ = 'kellym' @@ -13,8 +15,9 @@ __author__ = 'kellym' class TimerDurationTest(BpmnWorkflowTestCase): def setUp(self): + self.script_engine = PythonScriptEngine(default_globals={"timedelta": timedelta}) self.spec, self.subprocesses = self.load_workflow_spec('timer.bpmn', 'timer') - self.workflow = BpmnWorkflow(self.spec, self.subprocesses) + self.workflow = BpmnWorkflow(self.spec, self.subprocesses, script_engine=self.script_engine) def testRunThroughHappy(self): self.actual_test(save_restore=False) @@ -40,7 +43,9 @@ class TimerDurationTest(BpmnWorkflowTestCase): while loopcount < 10: if len(self.workflow.get_tasks(TaskState.READY)) >= 1: break - if save_restore: self.save_restore() + if save_restore: + self.save_restore() + self.workflow.script_engine = self.script_engine self.assertEqual(1, len(self.workflow.get_tasks(TaskState.WAITING))) time.sleep(0.1) self.workflow.refresh_waiting_tasks() diff --git a/SpiffWorkflow/tests/SpiffWorkflow/camunda/ExternalMessageBoundaryEventTest.py b/SpiffWorkflow/tests/SpiffWorkflow/camunda/ExternalMessageBoundaryEventTest.py index 7dd2b8df..e7d5d13c 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/camunda/ExternalMessageBoundaryEventTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/camunda/ExternalMessageBoundaryEventTest.py @@ -24,7 +24,7 @@ class ExternalMessageBoundaryTest(BaseTestCase): def actual_test(self,save_restore = False): - + self.workflow.do_engine_steps() ready_tasks = self.workflow.get_tasks(TaskState.READY) self.assertEqual(1, len(ready_tasks),'Expected to have only one ready task') diff --git a/SpiffWorkflow/tests/SpiffWorkflow/camunda/MessageBoundaryEventTest.py b/SpiffWorkflow/tests/SpiffWorkflow/camunda/MessageBoundaryEventTest.py index 03606c23..ce830b3a 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/camunda/MessageBoundaryEventTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/camunda/MessageBoundaryEventTest.py @@ -3,8 +3,10 @@ import unittest import time +from datetime import timedelta from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.workflow import BpmnWorkflow +from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine from .BaseTestCase import BaseTestCase __author__ = 'kellym' @@ -13,8 +15,9 @@ __author__ = 'kellym' class MessageBoundaryTest(BaseTestCase): def setUp(self): + self.script_engine = PythonScriptEngine(default_globals={"timedelta": timedelta}) self.spec, self.subprocesses = self.load_workflow_spec('MessageBoundary.bpmn', 'Process_1kjyavs') - self.workflow = BpmnWorkflow(self.spec, self.subprocesses) + self.workflow = BpmnWorkflow(self.spec, self.subprocesses, script_engine=self.script_engine) def testRunThroughHappy(self): self.actual_test(save_restore=False) @@ -41,7 +44,9 @@ class MessageBoundaryTest(BaseTestCase): self.workflow.do_engine_steps() time.sleep(.01) self.workflow.refresh_waiting_tasks() - if save_restore: self.save_restore() + if save_restore: + self.save_restore() + self.workflow.script_engine = self.script_engine ready_tasks = self.workflow.get_tasks(TaskState.READY) time.sleep(.01) self.workflow.refresh_waiting_tasks() diff --git a/SpiffWorkflow/tests/SpiffWorkflow/dmn/python_engine/PythonDecisionRunner.py b/SpiffWorkflow/tests/SpiffWorkflow/dmn/python_engine/PythonDecisionRunner.py index 7abeb88a..c3ef77ce 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/dmn/python_engine/PythonDecisionRunner.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/dmn/python_engine/PythonDecisionRunner.py @@ -1,3 +1,4 @@ +import datetime from decimal import Decimal from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine @@ -7,4 +8,5 @@ from ..DecisionRunner import DecisionRunner class PythonDecisionRunner(DecisionRunner): def __init__(self, filename): - super().__init__(PythonScriptEngine(scripting_additions={'Decimal': Decimal}), filename, 'python_engine') \ No newline at end of file + scripting_additions={'Decimal': Decimal, 'datetime': datetime} + super().__init__(PythonScriptEngine(scripting_additions=scripting_additions), filename, 'python_engine') diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock index 0fd209b3..06b2b562 100644 --- a/spiffworkflow-backend/poetry.lock +++ b/spiffworkflow-backend/poetry.lock @@ -410,7 +410,7 @@ python-versions = ">=3.6,<4.0" [[package]] name = "dateparser" -version = "1.1.1" +version = "1.1.2" description = "Date parsing library designed to parse dates from HTML pages" category = "main" optional = false @@ -639,7 +639,7 @@ werkzeug = "*" type = "git" url = "https://github.com/sartography/flask-bpmn" reference = "main" -resolved_reference = "cedc5253add81a18a274f2cd3289fe36bb138f8b" +resolved_reference = "191f0f32798720c9ce1e5307732c90ac26433298" [[package]] name = "Flask-Cors" @@ -820,7 +820,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "importlib-metadata" -version = "5.0.0" +version = "4.13.0" description = "Read metadata from Python packages" category = "main" optional = false @@ -1441,7 +1441,7 @@ docs = ["Sphinx (>=5.0.2,<6.0.0)", "alabaster (>=0.7.12,<0.8.0)", "commonmark (> [[package]] name = "pytz" -version = "2022.5" +version = "2022.6" description = "World timezone definitions, modern and historical" category = "main" optional = false @@ -1873,7 +1873,7 @@ pytz = "*" type = "git" url = "https://github.com/sartography/SpiffWorkflow" reference = "main" -resolved_reference = "2d3bd00854ab483e823c4b386430abc9267f536b" +resolved_reference = "5cdb881edc4621502bfd61ce67565cf1148199f0" [[package]] name = "SQLAlchemy" @@ -2000,6 +2000,14 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "types-dateparser" +version = "1.1.4.1" +description = "Typing stubs for dateparser" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "types-Flask" version = "1.1.6" @@ -2248,7 +2256,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "1.1" python-versions = ">=3.9,<3.11" -content-hash = "bfb51ebc4ef76d4a74f670f44dc4d7ca7e91874b096f56521c2776f1837f6a63" +content-hash = "995be3a9a60b515b281f017ff32ff27a52ca178b1980611b348dccac6afb6b89" [metadata.files] alabaster = [ @@ -2454,8 +2462,8 @@ darglint = [ {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, ] dateparser = [ - {file = "dateparser-1.1.1-py2.py3-none-any.whl", hash = "sha256:9600874312ff28a41f96ec7ccdc73be1d1c44435719da47fea3339d55ff5a628"}, - {file = "dateparser-1.1.1.tar.gz", hash = "sha256:038196b1f12c7397e38aad3d61588833257f6f552baa63a1499e6987fa8d42d9"}, + {file = "dateparser-1.1.2-py2.py3-none-any.whl", hash = "sha256:d31659dc806a7d88e2b510b2c74f68b525ae531f145c62a57a99bd616b7f90cf"}, + {file = "dateparser-1.1.2.tar.gz", hash = "sha256:3821bf191f95b2658c4abd91571c09821ce7a2bc179bf6cefd8b4515c3ccf9ef"}, ] distlib = [ {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, @@ -2613,7 +2621,6 @@ greenlet = [ {file = "greenlet-1.1.3.post0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a954002064ee919b444b19c1185e8cce307a1f20600f47d6f4b6d336972c809"}, {file = "greenlet-1.1.3.post0-cp39-cp39-win32.whl", hash = "sha256:2ccdc818cc106cc238ff7eba0d71b9c77be868fdca31d6c3b1347a54c9b187b2"}, {file = "greenlet-1.1.3.post0-cp39-cp39-win_amd64.whl", hash = "sha256:91a84faf718e6f8b888ca63d0b2d6d185c8e2a198d2a7322d75c303e7097c8b7"}, - {file = "greenlet-1.1.3.post0.tar.gz", hash = "sha256:f5e09dc5c6e1796969fd4b775ea1417d70e49a5df29aaa8e5d10675d9e11872c"}, ] gunicorn = [ {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, @@ -2632,8 +2639,8 @@ imagesize = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] importlib-metadata = [ - {file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"}, - {file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"}, + {file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"}, + {file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"}, ] inflection = [ {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, @@ -3051,7 +3058,18 @@ py = [ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] pyasn1 = [ + {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, + {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, + {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, + {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, + {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, + {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, + {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, + {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, + {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, + {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, + {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, ] pycodestyle = [ @@ -3130,8 +3148,8 @@ python-keycloak = [ {file = "python_keycloak-2.6.0-py3-none-any.whl", hash = "sha256:a1ce102b978beb56d385319b3ca20992b915c2c12d15a2d0c23f1104882f3fb6"}, ] pytz = [ - {file = "pytz-2022.5-py2.py3-none-any.whl", hash = "sha256:335ab46900b1465e714b4fda4963d87363264eb662aab5e65da039c25f1f5b22"}, - {file = "pytz-2022.5.tar.gz", hash = "sha256:c4d88f472f54d615e9cd582a5004d1e5f624854a6a27a6211591c251f22a6914"}, + {file = "pytz-2022.6-py2.py3-none-any.whl", hash = "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427"}, + {file = "pytz-2022.6.tar.gz", hash = "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"}, ] pytz-deprecation-shim = [ {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, @@ -3539,6 +3557,10 @@ types-click = [ {file = "types-click-7.1.8.tar.gz", hash = "sha256:b6604968be6401dc516311ca50708a0a28baa7a0cb840efd7412f0dbbff4e092"}, {file = "types_click-7.1.8-py3-none-any.whl", hash = "sha256:8cb030a669e2e927461be9827375f83c16b8178c365852c060a34e24871e7e81"}, ] +types-dateparser = [ + {file = "types-dateparser-1.1.4.1.tar.gz", hash = "sha256:0f76578bbae15c8b8701b5efd94db98a97ce0a27aedfe6f14a531170de6db97d"}, + {file = "types_dateparser-1.1.4.1-py3-none-any.whl", hash = "sha256:dd7b2343bb06225c0e358533609b66a8edfb95e5426d8f658664e7d0f27dea68"}, +] types-Flask = [ {file = "types-Flask-1.1.6.tar.gz", hash = "sha256:aac777b3abfff9436e6b01f6d08171cf23ea6e5be71cbf773aaabb1c5763e9cf"}, {file = "types_Flask-1.1.6-py3-none-any.whl", hash = "sha256:6ab8a9a5e258b76539d652f6341408867298550b19b81f0e41e916825fc39087"}, diff --git a/spiffworkflow-backend/pyproject.toml b/spiffworkflow-backend/pyproject.toml index a4741447..7f2d09a4 100644 --- a/spiffworkflow-backend/pyproject.toml +++ b/spiffworkflow-backend/pyproject.toml @@ -28,6 +28,7 @@ flask-migrate = "*" flask-restful = "*" werkzeug = "*" SpiffWorkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "main"} +#SpiffWorkflow = {develop = true, path = "../SpiffWorkflow" } sentry-sdk = "^1.10" sphinx-autoapi = "^2.0" flask-bpmn = {git = "https://github.com/sartography/flask-bpmn", rev = "main"} @@ -68,6 +69,9 @@ types-pytz = "^2022.1.1" # for now use my fork sqlalchemy-stubs = { git = "https://github.com/burnettk/sqlalchemy-stubs.git", rev = "scoped-session-delete" } simplejson = "^3.17.6" +pytz = "^2022.6" +dateparser = "^1.1.2" +types-dateparser = "^1.1.4.1" [tool.poetry.dev-dependencies] diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index f2ffec20..f0a93fd9 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1071,7 +1071,9 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response task.form_ui_schema = ui_form_contents if task.properties and task.data and "instructionsForEndUser" in task.properties: - print(f"task.properties['instructionsForEndUser']: {task.properties['instructionsForEndUser']}") + print( + f"task.properties['instructionsForEndUser']: {task.properties['instructionsForEndUser']}" + ) if task.properties["instructionsForEndUser"]: task.properties["instructionsForEndUser"] = render_jinja_template( task.properties["instructionsForEndUser"], task.data diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_frontend_url.py b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_frontend_url.py index 40dc482c..9490df95 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_frontend_url.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_frontend_url.py @@ -1,5 +1,6 @@ """Get_env.""" from typing import Any + from flask import current_app from spiffworkflow_backend.models.script_attributes_context import ( @@ -22,4 +23,4 @@ class GetFrontendUrl(Script): **kwargs: Any ) -> Any: """Run.""" - return current_app.config['SPIFFWORKFLOW_FRONTEND_URL'] + return current_app.config["SPIFFWORKFLOW_FRONTEND_URL"] diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index d82d8063..b499081f 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -7,6 +7,7 @@ import os import re import time from datetime import datetime +from datetime import timedelta from typing import Any from typing import Callable from typing import Dict @@ -17,6 +18,8 @@ from typing import Tuple from typing import TypedDict from typing import Union +import dateparser +import pytz from flask import current_app from flask_bpmn.api.api_error import ApiError from flask_bpmn.models.db import db @@ -25,7 +28,6 @@ from RestrictedPython import safe_globals # type: ignore from SpiffWorkflow.bpmn.exceptions import WorkflowTaskExecException # type: ignore from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException # type: ignore from SpiffWorkflow.bpmn.PythonScriptEngine import Box # type: ignore -from SpiffWorkflow.bpmn.PythonScriptEngine import DEFAULT_GLOBALS from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine from SpiffWorkflow.bpmn.serializer import BpmnWorkflowSerializer # type: ignore from SpiffWorkflow.bpmn.specs.BpmnProcessSpec import BpmnProcessSpec # type: ignore @@ -98,19 +100,6 @@ def _import(name: str, glbls: Dict[str, Any], *args: Any) -> None: raise ImportError(f"Import not allowed: {name}", name=name) -DEFAULT_GLOBALS.update( - { - "datetime": datetime, - "time": time, - "decimal": decimal, - "_strptime": _strptime, - } -) -# This will overwrite the standard builtins -DEFAULT_GLOBALS.update(safe_globals) -DEFAULT_GLOBALS["__builtins__"]["__import__"] = _import - - class PotentialOwnerIdList(TypedDict): """PotentialOwnerIdList.""" @@ -143,7 +132,21 @@ class CustomBpmnScriptEngine(PythonScriptEngine): # type: ignore def __init__(self) -> None: """__init__.""" - super().__init__(default_globals=DEFAULT_GLOBALS) + default_globals = { + "timedelta": timedelta, + "datetime": datetime, + "dateparser": dateparser, + "pytz": pytz, + "time": time, + "decimal": decimal, + "_strptime": _strptime, + } + + # This will overwrite the standard builtins + default_globals.update(safe_globals) + default_globals["__builtins__"]["__import__"] = _import + + super().__init__(default_globals=default_globals) def __get_augment_methods(self, task: SpiffTask) -> Dict[str, Callable]: """__get_augment_methods.""" From 62a9d67be8354dec2ccc6941ccb3021c53b33530 Mon Sep 17 00:00:00 2001 From: Jon Herron Date: Wed, 2 Nov 2022 11:36:30 -0400 Subject: [PATCH 08/46] Squashed 'flask-bpmn/' changes from ed0559dd9..191f0f327 191f0f327 Merge pull request #159 from sartography/dependabot/pip/furo-2022.9.29 a638983b9 Merge pull request #158 from sartography/dependabot/pip/pyupgrade-3.2.0 b15c1b290 Bump furo from 2022.6.21 to 2022.9.29 efd02ba7c Bump pyupgrade from 3.1.0 to 3.2.0 git-subtree-dir: flask-bpmn git-subtree-split: 191f0f32798720c9ce1e5307732c90ac26433298 --- poetry.lock | 18 +++++++++--------- pyproject.toml | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/poetry.lock b/poetry.lock index bf863b37..7a63efc6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -667,7 +667,7 @@ SQLAlchemy = ">=0.8.0" [[package]] name = "furo" -version = "2022.6.21" +version = "2022.9.29" description = "A clean customisable Sphinx documentation theme." category = "dev" optional = false @@ -675,7 +675,7 @@ python-versions = ">=3.7" [package.dependencies] beautifulsoup4 = "*" -pygments = "*" +pygments = ">=2.7" sphinx = ">=4.0,<6.0" sphinx-basic-ng = "*" @@ -1206,7 +1206,7 @@ tzdata = {version = "*", markers = "python_version >= \"3.6\""} [[package]] name = "pyupgrade" -version = "3.1.0" +version = "3.2.0" description = "A tool to automatically upgrade syntax for newer versions." category = "dev" optional = false @@ -1578,7 +1578,7 @@ pytz = "*" type = "git" url = "https://github.com/sartography/SpiffWorkflow" reference = "main" -resolved_reference = "2d3bd00854ab483e823c4b386430abc9267f536b" +resolved_reference = "5cdb881edc4621502bfd61ce67565cf1148199f0" [[package]] name = "sqlalchemy" @@ -1853,7 +1853,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>= [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "f5c0fcc30ff491c23da05e4d24c2dc9c66f43a2dfde028345f9dffd5e91f3f0a" +content-hash = "7d1d5e13f2546566277c6f0b5935753c89804db2abb7a1e76498b582f40f9a01" [metadata.files] alabaster = [ @@ -2191,8 +2191,8 @@ flask-sqlalchemy = [ {file = "Flask_SQLAlchemy-2.5.1-py2.py3-none-any.whl", hash = "sha256:f12c3d4cc5cc7fdcc148b9527ea05671718c3ea45d50c7e732cceb33f574b390"}, ] furo = [ - {file = "furo-2022.6.21-py3-none-any.whl", hash = "sha256:061b68e323345e27fcba024cf33a1e77f3dfd8d9987410be822749a706e2add6"}, - {file = "furo-2022.6.21.tar.gz", hash = "sha256:9aa983b7488a4601d13113884bfb7254502c8729942e073a0acb87a5512af223"}, + {file = "furo-2022.9.29-py3-none-any.whl", hash = "sha256:559ee17999c0f52728481dcf6b1b0cf8c9743e68c5e3a18cb45a7992747869a9"}, + {file = "furo-2022.9.29.tar.gz", hash = "sha256:d4238145629c623609c2deb5384f8d036e2a1ee2a101d64b67b4348112470dbd"}, ] gitdb = [ {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, @@ -2608,8 +2608,8 @@ pytz-deprecation-shim = [ {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"}, ] pyupgrade = [ - {file = "pyupgrade-3.1.0-py2.py3-none-any.whl", hash = "sha256:77c6101a710be3e24804891e43388cedbee617258e93b09c8c5e58de08617758"}, - {file = "pyupgrade-3.1.0.tar.gz", hash = "sha256:7a8d393d85e15e0e2753e90b7b2e173b9d29dfd71e61f93d93e985b242627ed3"}, + {file = "pyupgrade-3.2.0-py2.py3-none-any.whl", hash = "sha256:2aa6c40e49ea5a350e6e45b8c7847b1741aef274a35d4f0b2bf91731ec8ab796"}, + {file = "pyupgrade-3.2.0.tar.gz", hash = "sha256:70e1ac1e6b34a90fb21f5cada1907ef035b12dfc1d9f13cefd367acf3b530310"}, ] pyyaml = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, diff --git a/pyproject.toml b/pyproject.toml index a5a2ff3f..352cd2dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,7 +61,7 @@ reorder-python-imports = "^3.9.0" pre-commit-hooks = "^4.3.0" sphinx-click = "^4.3.0" Pygments = "^2.13.0" -pyupgrade = "^3.1.0" +pyupgrade = "^3.2.0" furo = ">=2021.11.12" MonkeyType = "^22.2.0" From c3cdca16b8e8fe3b85dc8983b40aacdfc93f01e9 Mon Sep 17 00:00:00 2001 From: burnettk Date: Wed, 2 Nov 2022 12:19:52 -0400 Subject: [PATCH 09/46] Squashed 'SpiffWorkflow/' changes from 5cdb881ed..a6392d190 a6392d190 SpiffWorkflow cold start improvements (#13) git-subtree-dir: SpiffWorkflow git-subtree-split: a6392d19061f623394f5705fb78af23673d3940d --- SpiffWorkflow/bpmn/PythonScriptEngine.py | 15 +---------- SpiffWorkflow/specs/Celery.py | 12 +++------ doc/bpmn/advanced.rst | 27 ++++++++----------- requirements.txt | 2 -- setup.py | 2 +- .../bpmn/NITimerDurationBoundaryTest.py | 12 ++++++--- .../bpmn/PythonScriptEngineTest.py | 21 --------------- tests/SpiffWorkflow/bpmn/TooManyLoopsTest.py | 18 ++++++++++--- .../bpmn/data/timer-date-start.bpmn | 4 +-- .../bpmn/events/TimerCycleStartTest.py | 6 ++++- .../bpmn/events/TimerCycleTest.py | 6 ++++- .../bpmn/events/TimerDateTest.py | 12 ++++++--- .../events/TimerDurationBoundaryOnTaskTest.py | 7 ++++- .../bpmn/events/TimerDurationTest.py | 9 +++++-- .../ExternalMessageBoundaryEventTest.py | 2 +- .../camunda/MessageBoundaryEventTest.py | 9 +++++-- .../dmn/python_engine/PythonDecisionRunner.py | 4 ++- 17 files changed, 84 insertions(+), 84 deletions(-) diff --git a/SpiffWorkflow/bpmn/PythonScriptEngine.py b/SpiffWorkflow/bpmn/PythonScriptEngine.py index 17772992..5d45ceb0 100644 --- a/SpiffWorkflow/bpmn/PythonScriptEngine.py +++ b/SpiffWorkflow/bpmn/PythonScriptEngine.py @@ -3,23 +3,10 @@ import ast import copy import sys import traceback -import datetime - -import dateparser -import pytz from SpiffWorkflow.bpmn.exceptions import WorkflowTaskExecException from ..operators import Operator -# Would love to get rid of this altogether, as it rightly belongs in the -# backend, but leaving it here because that's the path of least resistance. -DEFAULT_GLOBALS = { - 'timedelta': datetime.timedelta, - 'datetime': datetime, - 'dateparser': dateparser, - 'pytz': pytz, -} - # Copyright (C) 2020 Kelly McDonald # @@ -112,7 +99,7 @@ class PythonScriptEngine(object): def __init__(self, default_globals=None, scripting_additions=None): - self.globals = default_globals or DEFAULT_GLOBALS + self.globals = default_globals or {} self.globals.update(scripting_additions or {}) self.error_tasks = {} diff --git a/SpiffWorkflow/specs/Celery.py b/SpiffWorkflow/specs/Celery.py index 35288d24..85c4d134 100644 --- a/SpiffWorkflow/specs/Celery.py +++ b/SpiffWorkflow/specs/Celery.py @@ -25,13 +25,6 @@ from .base import TaskSpec from ..operators import valueof, Attrib, PathAttrib from ..util.deep_merge import DeepMerge -try: - from celery.app import default_app -except ImportError: - have_celery = False -else: - have_celery = True - logger = logging.getLogger('spiff') @@ -111,7 +104,10 @@ class Celery(TaskSpec): :type kwargs: dict :param kwargs: kwargs to pass to celery task. """ - if not have_celery: + + try: + from celery.app import default_app + except ImportError: raise Exception("Unable to import python-celery imports.") assert wf_spec is not None assert name is not None diff --git a/doc/bpmn/advanced.rst b/doc/bpmn/advanced.rst index af96317d..d6442b87 100644 --- a/doc/bpmn/advanced.rst +++ b/doc/bpmn/advanced.rst @@ -74,7 +74,7 @@ Serialization .. warning:: - Serialization Changed in Version 1.1.7. + Serialization Changed in Version 1.1.7. Support for pre-1.1.7 serialization will be dropped in a future release. The old serialization method still works but it is deprecated. To migrate your system to the new version, see "Migrating between @@ -85,8 +85,8 @@ setting. This may not always be the case, we may be executing the workflow in th may have a user request a web page where we open a specific workflow that we may be in the middle of, do one step of that workflow and then the user may be back in a few minutes, or maybe a few hours depending on the application. -The :code:`BpmnWorkflowSerializer` class contains a serializer for a workflow containing only standard BPMN Tasks. -Since we are using custom task classes (the Camunda :code:`UserTask` and the DMN :code:`BusinessRuleTask`), +The :code:`BpmnWorkflowSerializer` class contains a serializer for a workflow containing only standard BPMN Tasks. +Since we are using custom task classes (the Camunda :code:`UserTask` and the DMN :code:`BusinessRuleTask`), we'll need to supply serializers for those task specs as well. Strictly speaking, these are not serializers per se: they actually convert the tasks into dictionaries of @@ -138,7 +138,7 @@ two components: - a data converter (which handles workflow and task data). The default workflow spec converter likely to meet your needs, either on its own, or with the inclusion of -:code:`UserTask` and :code:`BusinessRuleTask` in the :code:`camnuda` or :code:`spiff` and :code:`dmn` subpackages +:code:`UserTask` and :code:`BusinessRuleTask` in the :code:`camnuda` or :code:`spiff` and :code:`dmn` subpackages of this library, and all you'll need to do is add them to the list of task converters, as we did above. However, he default data converter is very simple, adding only JSON-serializable conversions of :code:`datetime` @@ -180,7 +180,7 @@ If you have written any custom task specs, you'll need to implement task spec co Task Spec converters are also based on the :code:`DictionaryConverter`. You should be able to use the `BpmnTaskSpecConverter `_ -as a basis for your custom specs. It provides some methods for extracting attributes from Spiff base classes as well as +as a basis for your custom specs. It provides some methods for extracting attributes from Spiff base classes as well as standard BPNN attributes from tasks that inherit from :code:`BMPNSpecMixin`. The `Camunda User Task Converter `_ @@ -221,7 +221,7 @@ serialize the workflow in the new format: new_json = serializer.serialize_json(workflow) However, if you use custom tasks or data serialization, you'll also need to specify workflow spec or data -serializers, as in the examples in the previous section, before you'll be able to serialize with the new serializer. +serializers, as in the examples in the previous section, before you'll be able to serialize with the new serializer. The code would then look more like this: .. code:: python @@ -244,7 +244,7 @@ The code would then look more like this: new_json = serializer.serialize_json(workflow) Because the serializer is highly customizable, we've made it possible for you to manage your own versions of the -serialization. You can do this by passing a version number into the serializer, which will be embedded in the +serialization. You can do this by passing a version number into the serializer, which will be embedded in the json of all workflows. This allow you to modify the serialization and customize it over time, and still manage the different forms as you make adjustments without leaving people behind. @@ -253,11 +253,11 @@ Versioned Serializer As we make changes to Spiff, we may change the serialization format. For example, in 1.1.8, we changed how subprocesses were handled interally in BPMN workflows and updated how they are serialized. If you have -not overridden our version number with one of your own, the serializer will transform the 1.0 format to the +not overridden our version number with one of your own, the serializer will transform the 1.0 format to the new 1.1 format. If you've overridden the serializer version, you may need to incorporate our serialization changes with -your own. You can find our conversions in +your own. You can find our conversions in `version_migrations.py `_ Custom Script Engines @@ -277,14 +277,9 @@ We'll cover a simple extension of custom script engine here. There is also an e a similar engine based on `RestrictedPython `_ included alongside this example. -The default script engine imports the following objects: +The default script engine does not import any objects. -- :code:`timedelta` -- :code:`datetime` -- :code:`dateparser` -- :code:`pytz` - -You could add other functions or classes from the standard python modules or any code you've +You could add functions or classes from the standard python modules or any code you've implemented yourself. Your global environment can be passed in using the `default_globals` argument when initializing the script engine. In our RestrictedPython example, we use their `safe_globals` which prevents users from executing some potentially unsafe operations. diff --git a/requirements.txt b/requirements.txt index 63b4abb4..bf928ff6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,4 @@ celery==5.2.3 coverage lxml -dateparser -pytz . diff --git a/setup.py b/setup.py index 15522250..ad4c2163 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ setup(name='SpiffWorkflow', license='lGPLv2', packages=find_packages(exclude=['tests', 'tests.*']), package_data={'SpiffWorkflow.bpmn.parser.schema': ['*.xsd']}, - install_requires=['configparser', 'lxml', 'celery', 'dateparser', 'pytz', + install_requires=['configparser', 'lxml', 'celery', # required for python 3.7 - https://stackoverflow.com/a/73932581 'importlib-metadata<5.0; python_version <= "3.7"'], keywords='spiff workflow bpmn engine', diff --git a/tests/SpiffWorkflow/bpmn/NITimerDurationBoundaryTest.py b/tests/SpiffWorkflow/bpmn/NITimerDurationBoundaryTest.py index f7cef40b..97553a6d 100644 --- a/tests/SpiffWorkflow/bpmn/NITimerDurationBoundaryTest.py +++ b/tests/SpiffWorkflow/bpmn/NITimerDurationBoundaryTest.py @@ -5,6 +5,7 @@ import datetime import time from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.workflow import BpmnWorkflow +from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase __author__ = 'kellym' @@ -15,11 +16,12 @@ class NITimerDurationTest(BpmnWorkflowTestCase): Non-Interrupting Timer boundary test """ def setUp(self): + self.script_engine = PythonScriptEngine(default_globals={"timedelta": datetime.timedelta}) spec, subprocesses = self.load_workflow_spec('timer-non-interrupt-boundary.bpmn', 'NonInterruptTimer') - self.workflow = BpmnWorkflow(spec, subprocesses) + self.workflow = BpmnWorkflow(spec, subprocesses, script_engine=self.script_engine) def load_spec(self): - return + return def testRunThroughHappy(self): self.actual_test(save_restore=False) @@ -28,7 +30,7 @@ class NITimerDurationTest(BpmnWorkflowTestCase): self.actual_test(save_restore=True) def actual_test(self,save_restore = False): - + ready_tasks = self.workflow.get_tasks(TaskState.READY) self.assertEqual(1, len(ready_tasks)) self.workflow.complete_task_from_id(ready_tasks[0].id) @@ -47,7 +49,9 @@ class NITimerDurationTest(BpmnWorkflowTestCase): ready_tasks = self.workflow.get_tasks(TaskState.READY) if len(ready_tasks) > 1: break - if save_restore: self.save_restore() + if save_restore: + self.save_restore() + self.workflow.script_engine = self.script_engine #self.assertEqual(1, len(self.workflow.get_tasks(Task.WAITING))) time.sleep(0.1) self.workflow.complete_task_from_id(ready_tasks[0].id) diff --git a/tests/SpiffWorkflow/bpmn/PythonScriptEngineTest.py b/tests/SpiffWorkflow/bpmn/PythonScriptEngineTest.py index d5c8ec32..05ac0df0 100644 --- a/tests/SpiffWorkflow/bpmn/PythonScriptEngineTest.py +++ b/tests/SpiffWorkflow/bpmn/PythonScriptEngineTest.py @@ -24,32 +24,11 @@ class PythonScriptEngineTest(BpmnWorkflowTestCase): workflow.do_engine_steps() self.task = workflow.last_task - def testDateTimeExpressions(self): - """Basically, assure that we can use datime, dateutils, and pytz""" - script = """ -# Create Current Date as UTC -now_utc = datetime.datetime.now(datetime.timezone.utc) -# Create Current Date at EST -now_est = now_utc.astimezone(pytz.timezone('US/Eastern')) - -# Format a date from a date String in UTC -datestr = "2021-09-23 16:11:00 -0000" # 12 pm EST, 4pm UTC -dt = dateparser.parse(datestr) -localtime = dt.astimezone(pytz.timezone('US/Eastern')) -localtime_str = localtime.strftime("%Y-%m-%d %H:%M:%S") - """ - self.expressionEngine.execute(self.task, script) - self.assertEqual(self.task.data['now_utc'].utcoffset().days, 0) - self.assertEqual(self.task.data['now_est'].tzinfo.zone, "US/Eastern") - self.assertEqual(self.task.data['localtime_str'], "2021-09-23 12:11:00") - self.assertTrue(True) - def testFunctionsAndGlobalsAreRemoved(self): self.assertIn('testvar', self.task.data) self.assertIn('testvar2', self.task.data) self.assertIn('sample', self.task.data) self.assertNotIn('my_function', self.task.data) - self.assertNotIn('datetime', self.task.data) def suite(): return unittest.TestLoader().loadTestsFromTestCase(PythonScriptEngineTest) diff --git a/tests/SpiffWorkflow/bpmn/TooManyLoopsTest.py b/tests/SpiffWorkflow/bpmn/TooManyLoopsTest.py index a1018e0b..072d9375 100644 --- a/tests/SpiffWorkflow/bpmn/TooManyLoopsTest.py +++ b/tests/SpiffWorkflow/bpmn/TooManyLoopsTest.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import datetime import unittest from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine @@ -8,6 +9,15 @@ from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase __author__ = 'sartography' +class CustomScriptEngine(PythonScriptEngine): + """This is a custom script processor that can be easily injected into Spiff Workflow. + It will execute python code read in from the bpmn. It will also make any scripts in the + scripts directory available for execution. """ + def __init__(self): + augment_methods = { + 'timedelta': datetime.timedelta, + } + super().__init__(scripting_additions=augment_methods) class TooManyLoopsTest(BpmnWorkflowTestCase): @@ -23,7 +33,7 @@ class TooManyLoopsTest(BpmnWorkflowTestCase): def actual_test(self,save_restore = False): spec, subprocesses = self.load_workflow_spec('too_many_loops*.bpmn', 'loops') - self.workflow = BpmnWorkflow(spec, subprocesses, script_engine=PythonScriptEngine()) + self.workflow = BpmnWorkflow(spec, subprocesses, script_engine=CustomScriptEngine()) counter = 0 data = {} while not self.workflow.is_completed(): @@ -34,6 +44,7 @@ class TooManyLoopsTest(BpmnWorkflowTestCase): counter += 1 # There is a 10 millisecond wait task. if save_restore: self.save_restore() + self.workflow.script_engine = CustomScriptEngine() self.assertEqual(20, self.workflow.last_task.data['counter']) def test_with_sub_process(self): @@ -41,7 +52,7 @@ class TooManyLoopsTest(BpmnWorkflowTestCase): # right after a sub-process. So assuring this is fixed. counter = 0 spec, subprocesses = self.load_workflow_spec('too_many_loops_sub_process.bpmn', 'loops_sub') - self.workflow = BpmnWorkflow(spec, subprocesses, script_engine=PythonScriptEngine()) + self.workflow = BpmnWorkflow(spec, subprocesses, script_engine=CustomScriptEngine()) data = {} while not self.workflow.is_completed(): self.workflow.do_engine_steps() @@ -57,7 +68,7 @@ class TooManyLoopsTest(BpmnWorkflowTestCase): def test_with_two_call_activities(self): spec, subprocess = self.load_workflow_spec('sub_in_loop*.bpmn', 'main') - self.workflow = BpmnWorkflow(spec, subprocess) + self.workflow = BpmnWorkflow(spec, subprocess, script_engine=CustomScriptEngine()) self.workflow.do_engine_steps() for loop in range(3): ready = self.workflow.get_ready_user_tasks() @@ -66,6 +77,7 @@ class TooManyLoopsTest(BpmnWorkflowTestCase): self.workflow.refresh_waiting_tasks() self.workflow.do_engine_steps() self.save_restore() + self.workflow.script_engine = CustomScriptEngine() def suite(): return unittest.TestLoader().loadTestsFromTestCase(TooManyLoopsTest) diff --git a/tests/SpiffWorkflow/bpmn/data/timer-date-start.bpmn b/tests/SpiffWorkflow/bpmn/data/timer-date-start.bpmn index 7bead0fa..46f907b9 100644 --- a/tests/SpiffWorkflow/bpmn/data/timer-date-start.bpmn +++ b/tests/SpiffWorkflow/bpmn/data/timer-date-start.bpmn @@ -7,8 +7,8 @@ Flow_1i73q45 Flow_00e79cz - futuredate = dateparser.parse('in 1 second') - timedelta(seconds=.95) -futuredate2 = dateparser.parse('September 1 2021 at 10am EDT') + futuredate = datetime.now() + timedelta(0, 1) - timedelta(seconds=.95) +futuredate2 = datetime.strptime('2021-09-01 10:00','%Y-%m-%d %H:%M') diff --git a/tests/SpiffWorkflow/bpmn/events/TimerCycleStartTest.py b/tests/SpiffWorkflow/bpmn/events/TimerCycleStartTest.py index e4e8741c..98de249c 100644 --- a/tests/SpiffWorkflow/bpmn/events/TimerCycleStartTest.py +++ b/tests/SpiffWorkflow/bpmn/events/TimerCycleStartTest.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import datetime import unittest import time @@ -21,7 +22,10 @@ class CustomScriptEngine(PythonScriptEngine): It will execute python code read in from the bpmn. It will also make any scripts in the scripts directory available for execution. """ def __init__(self): - augment_methods = {'custom_function': my_custom_function} + augment_methods = { + 'custom_function': my_custom_function, + 'timedelta': datetime.timedelta, + } super().__init__(scripting_additions=augment_methods) diff --git a/tests/SpiffWorkflow/bpmn/events/TimerCycleTest.py b/tests/SpiffWorkflow/bpmn/events/TimerCycleTest.py index 8b9ad0f5..56b52730 100644 --- a/tests/SpiffWorkflow/bpmn/events/TimerCycleTest.py +++ b/tests/SpiffWorkflow/bpmn/events/TimerCycleTest.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import datetime import unittest import time @@ -21,7 +22,10 @@ class CustomScriptEngine(PythonScriptEngine): It will execute python code read in from the bpmn. It will also make any scripts in the scripts directory available for execution. """ def __init__(self): - augment_methods = {'custom_function': my_custom_function} + augment_methods = { + 'custom_function': my_custom_function, + 'timedelta': datetime.timedelta, + } super().__init__(scripting_additions=augment_methods) diff --git a/tests/SpiffWorkflow/bpmn/events/TimerDateTest.py b/tests/SpiffWorkflow/bpmn/events/TimerDateTest.py index a82698b7..d6157ecd 100644 --- a/tests/SpiffWorkflow/bpmn/events/TimerDateTest.py +++ b/tests/SpiffWorkflow/bpmn/events/TimerDateTest.py @@ -3,10 +3,10 @@ import unittest import datetime import time -import pytz from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.workflow import BpmnWorkflow +from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase __author__ = 'kellym' @@ -15,8 +15,12 @@ __author__ = 'kellym' class TimerDateTest(BpmnWorkflowTestCase): def setUp(self): + self.script_engine = PythonScriptEngine(default_globals={ + "datetime": datetime.datetime, + "timedelta": datetime.timedelta, + }) self.spec, self.subprocesses = self.load_workflow_spec('timer-date-start.bpmn', 'date_timer') - self.workflow = BpmnWorkflow(self.spec, self.subprocesses) + self.workflow = BpmnWorkflow(self.spec, self.subprocesses, script_engine=self.script_engine) def testRunThroughHappy(self): self.actual_test(save_restore=False) @@ -42,6 +46,7 @@ class TimerDateTest(BpmnWorkflowTestCase): break if save_restore: self.save_restore() + self.workflow.script_engine = self.script_engine waiting_tasks = self.workflow.get_tasks(TaskState.WAITING) @@ -50,8 +55,7 @@ class TimerDateTest(BpmnWorkflowTestCase): loopcount = loopcount +1 endtime = datetime.datetime.now() self.workflow.do_engine_steps() - tz = pytz.timezone('US/Eastern') - testdate = tz.localize(datetime.datetime.strptime('2021-09-01 10:00','%Y-%m-%d %H:%M')) + testdate = datetime.datetime.strptime('2021-09-01 10:00','%Y-%m-%d %H:%M') self.assertEqual(self.workflow.last_task.data['futuredate2'],testdate) self.assertTrue('completed' in self.workflow.last_task.data) self.assertTrue(self.workflow.last_task.data['completed']) diff --git a/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py b/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py index ec743b54..e87de251 100644 --- a/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py +++ b/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py @@ -3,10 +3,12 @@ import unittest import datetime import time +from datetime import timedelta from SpiffWorkflow.bpmn.specs.events import EndEvent from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.workflow import BpmnWorkflow +from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase __author__ = 'kellym' @@ -14,8 +16,9 @@ __author__ = 'kellym' class TimerDurationTest(BpmnWorkflowTestCase): def setUp(self): + self.script_engine = PythonScriptEngine(default_globals={"timedelta": timedelta}) self.spec, self.subprocesses = self.load_workflow_spec('boundary_timer_on_task.bpmn', 'test_timer') - self.workflow = BpmnWorkflow(self.spec, self.subprocesses) + self.workflow = BpmnWorkflow(self.spec, self.subprocesses, script_engine=self.script_engine) def testRunThroughHappy(self): self.actual_test(save_restore=False) @@ -43,9 +46,11 @@ class TimerDurationTest(BpmnWorkflowTestCase): starttime = datetime.datetime.now() self.workflow = BpmnWorkflow(self.spec) + self.workflow.script_engine = self.script_engine self.workflow.do_engine_steps() if save_restore: self.save_restore() + self.workflow.script_engine = self.script_engine time.sleep(0.1) self.workflow.refresh_waiting_tasks() self.workflow.do_engine_steps() diff --git a/tests/SpiffWorkflow/bpmn/events/TimerDurationTest.py b/tests/SpiffWorkflow/bpmn/events/TimerDurationTest.py index 24b27eea..8026483d 100644 --- a/tests/SpiffWorkflow/bpmn/events/TimerDurationTest.py +++ b/tests/SpiffWorkflow/bpmn/events/TimerDurationTest.py @@ -3,8 +3,10 @@ import unittest import datetime import time +from datetime import timedelta from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.workflow import BpmnWorkflow +from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase __author__ = 'kellym' @@ -13,8 +15,9 @@ __author__ = 'kellym' class TimerDurationTest(BpmnWorkflowTestCase): def setUp(self): + self.script_engine = PythonScriptEngine(default_globals={"timedelta": timedelta}) self.spec, self.subprocesses = self.load_workflow_spec('timer.bpmn', 'timer') - self.workflow = BpmnWorkflow(self.spec, self.subprocesses) + self.workflow = BpmnWorkflow(self.spec, self.subprocesses, script_engine=self.script_engine) def testRunThroughHappy(self): self.actual_test(save_restore=False) @@ -40,7 +43,9 @@ class TimerDurationTest(BpmnWorkflowTestCase): while loopcount < 10: if len(self.workflow.get_tasks(TaskState.READY)) >= 1: break - if save_restore: self.save_restore() + if save_restore: + self.save_restore() + self.workflow.script_engine = self.script_engine self.assertEqual(1, len(self.workflow.get_tasks(TaskState.WAITING))) time.sleep(0.1) self.workflow.refresh_waiting_tasks() diff --git a/tests/SpiffWorkflow/camunda/ExternalMessageBoundaryEventTest.py b/tests/SpiffWorkflow/camunda/ExternalMessageBoundaryEventTest.py index 7dd2b8df..e7d5d13c 100644 --- a/tests/SpiffWorkflow/camunda/ExternalMessageBoundaryEventTest.py +++ b/tests/SpiffWorkflow/camunda/ExternalMessageBoundaryEventTest.py @@ -24,7 +24,7 @@ class ExternalMessageBoundaryTest(BaseTestCase): def actual_test(self,save_restore = False): - + self.workflow.do_engine_steps() ready_tasks = self.workflow.get_tasks(TaskState.READY) self.assertEqual(1, len(ready_tasks),'Expected to have only one ready task') diff --git a/tests/SpiffWorkflow/camunda/MessageBoundaryEventTest.py b/tests/SpiffWorkflow/camunda/MessageBoundaryEventTest.py index 03606c23..ce830b3a 100644 --- a/tests/SpiffWorkflow/camunda/MessageBoundaryEventTest.py +++ b/tests/SpiffWorkflow/camunda/MessageBoundaryEventTest.py @@ -3,8 +3,10 @@ import unittest import time +from datetime import timedelta from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.workflow import BpmnWorkflow +from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine from .BaseTestCase import BaseTestCase __author__ = 'kellym' @@ -13,8 +15,9 @@ __author__ = 'kellym' class MessageBoundaryTest(BaseTestCase): def setUp(self): + self.script_engine = PythonScriptEngine(default_globals={"timedelta": timedelta}) self.spec, self.subprocesses = self.load_workflow_spec('MessageBoundary.bpmn', 'Process_1kjyavs') - self.workflow = BpmnWorkflow(self.spec, self.subprocesses) + self.workflow = BpmnWorkflow(self.spec, self.subprocesses, script_engine=self.script_engine) def testRunThroughHappy(self): self.actual_test(save_restore=False) @@ -41,7 +44,9 @@ class MessageBoundaryTest(BaseTestCase): self.workflow.do_engine_steps() time.sleep(.01) self.workflow.refresh_waiting_tasks() - if save_restore: self.save_restore() + if save_restore: + self.save_restore() + self.workflow.script_engine = self.script_engine ready_tasks = self.workflow.get_tasks(TaskState.READY) time.sleep(.01) self.workflow.refresh_waiting_tasks() diff --git a/tests/SpiffWorkflow/dmn/python_engine/PythonDecisionRunner.py b/tests/SpiffWorkflow/dmn/python_engine/PythonDecisionRunner.py index 7abeb88a..c3ef77ce 100644 --- a/tests/SpiffWorkflow/dmn/python_engine/PythonDecisionRunner.py +++ b/tests/SpiffWorkflow/dmn/python_engine/PythonDecisionRunner.py @@ -1,3 +1,4 @@ +import datetime from decimal import Decimal from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine @@ -7,4 +8,5 @@ from ..DecisionRunner import DecisionRunner class PythonDecisionRunner(DecisionRunner): def __init__(self, filename): - super().__init__(PythonScriptEngine(scripting_additions={'Decimal': Decimal}), filename, 'python_engine') \ No newline at end of file + scripting_additions={'Decimal': Decimal, 'datetime': datetime} + super().__init__(PythonScriptEngine(scripting_additions=scripting_additions), filename, 'python_engine') From f143cde0ab65e1ebbd0c32f1062f9e91fcbba3b7 Mon Sep 17 00:00:00 2001 From: burnettk Date: Wed, 2 Nov 2022 12:19:53 -0400 Subject: [PATCH 10/46] Squashed 'spiffworkflow-backend/' changes from 9259cb4f0..400fa583f 400fa583f SpiffWorkflow cold start improvements (#13) git-subtree-dir: spiffworkflow-backend git-subtree-split: 400fa583f3269675d8a86dd37b0b3d8561501e9c --- poetry.lock | 48 ++++++++++++++----- pyproject.toml | 4 ++ .../routes/process_api_blueprint.py | 4 +- .../scripts/get_frontend_url.py | 3 +- .../services/process_instance_processor.py | 33 +++++++------ 5 files changed, 62 insertions(+), 30 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0fd209b3..06b2b562 100644 --- a/poetry.lock +++ b/poetry.lock @@ -410,7 +410,7 @@ python-versions = ">=3.6,<4.0" [[package]] name = "dateparser" -version = "1.1.1" +version = "1.1.2" description = "Date parsing library designed to parse dates from HTML pages" category = "main" optional = false @@ -639,7 +639,7 @@ werkzeug = "*" type = "git" url = "https://github.com/sartography/flask-bpmn" reference = "main" -resolved_reference = "cedc5253add81a18a274f2cd3289fe36bb138f8b" +resolved_reference = "191f0f32798720c9ce1e5307732c90ac26433298" [[package]] name = "Flask-Cors" @@ -820,7 +820,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "importlib-metadata" -version = "5.0.0" +version = "4.13.0" description = "Read metadata from Python packages" category = "main" optional = false @@ -1441,7 +1441,7 @@ docs = ["Sphinx (>=5.0.2,<6.0.0)", "alabaster (>=0.7.12,<0.8.0)", "commonmark (> [[package]] name = "pytz" -version = "2022.5" +version = "2022.6" description = "World timezone definitions, modern and historical" category = "main" optional = false @@ -1873,7 +1873,7 @@ pytz = "*" type = "git" url = "https://github.com/sartography/SpiffWorkflow" reference = "main" -resolved_reference = "2d3bd00854ab483e823c4b386430abc9267f536b" +resolved_reference = "5cdb881edc4621502bfd61ce67565cf1148199f0" [[package]] name = "SQLAlchemy" @@ -2000,6 +2000,14 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "types-dateparser" +version = "1.1.4.1" +description = "Typing stubs for dateparser" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "types-Flask" version = "1.1.6" @@ -2248,7 +2256,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "1.1" python-versions = ">=3.9,<3.11" -content-hash = "bfb51ebc4ef76d4a74f670f44dc4d7ca7e91874b096f56521c2776f1837f6a63" +content-hash = "995be3a9a60b515b281f017ff32ff27a52ca178b1980611b348dccac6afb6b89" [metadata.files] alabaster = [ @@ -2454,8 +2462,8 @@ darglint = [ {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, ] dateparser = [ - {file = "dateparser-1.1.1-py2.py3-none-any.whl", hash = "sha256:9600874312ff28a41f96ec7ccdc73be1d1c44435719da47fea3339d55ff5a628"}, - {file = "dateparser-1.1.1.tar.gz", hash = "sha256:038196b1f12c7397e38aad3d61588833257f6f552baa63a1499e6987fa8d42d9"}, + {file = "dateparser-1.1.2-py2.py3-none-any.whl", hash = "sha256:d31659dc806a7d88e2b510b2c74f68b525ae531f145c62a57a99bd616b7f90cf"}, + {file = "dateparser-1.1.2.tar.gz", hash = "sha256:3821bf191f95b2658c4abd91571c09821ce7a2bc179bf6cefd8b4515c3ccf9ef"}, ] distlib = [ {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, @@ -2613,7 +2621,6 @@ greenlet = [ {file = "greenlet-1.1.3.post0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a954002064ee919b444b19c1185e8cce307a1f20600f47d6f4b6d336972c809"}, {file = "greenlet-1.1.3.post0-cp39-cp39-win32.whl", hash = "sha256:2ccdc818cc106cc238ff7eba0d71b9c77be868fdca31d6c3b1347a54c9b187b2"}, {file = "greenlet-1.1.3.post0-cp39-cp39-win_amd64.whl", hash = "sha256:91a84faf718e6f8b888ca63d0b2d6d185c8e2a198d2a7322d75c303e7097c8b7"}, - {file = "greenlet-1.1.3.post0.tar.gz", hash = "sha256:f5e09dc5c6e1796969fd4b775ea1417d70e49a5df29aaa8e5d10675d9e11872c"}, ] gunicorn = [ {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, @@ -2632,8 +2639,8 @@ imagesize = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] importlib-metadata = [ - {file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"}, - {file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"}, + {file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"}, + {file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"}, ] inflection = [ {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, @@ -3051,7 +3058,18 @@ py = [ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] pyasn1 = [ + {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, + {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, + {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, + {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, + {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, + {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, + {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, + {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, + {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, + {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, + {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, ] pycodestyle = [ @@ -3130,8 +3148,8 @@ python-keycloak = [ {file = "python_keycloak-2.6.0-py3-none-any.whl", hash = "sha256:a1ce102b978beb56d385319b3ca20992b915c2c12d15a2d0c23f1104882f3fb6"}, ] pytz = [ - {file = "pytz-2022.5-py2.py3-none-any.whl", hash = "sha256:335ab46900b1465e714b4fda4963d87363264eb662aab5e65da039c25f1f5b22"}, - {file = "pytz-2022.5.tar.gz", hash = "sha256:c4d88f472f54d615e9cd582a5004d1e5f624854a6a27a6211591c251f22a6914"}, + {file = "pytz-2022.6-py2.py3-none-any.whl", hash = "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427"}, + {file = "pytz-2022.6.tar.gz", hash = "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"}, ] pytz-deprecation-shim = [ {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, @@ -3539,6 +3557,10 @@ types-click = [ {file = "types-click-7.1.8.tar.gz", hash = "sha256:b6604968be6401dc516311ca50708a0a28baa7a0cb840efd7412f0dbbff4e092"}, {file = "types_click-7.1.8-py3-none-any.whl", hash = "sha256:8cb030a669e2e927461be9827375f83c16b8178c365852c060a34e24871e7e81"}, ] +types-dateparser = [ + {file = "types-dateparser-1.1.4.1.tar.gz", hash = "sha256:0f76578bbae15c8b8701b5efd94db98a97ce0a27aedfe6f14a531170de6db97d"}, + {file = "types_dateparser-1.1.4.1-py3-none-any.whl", hash = "sha256:dd7b2343bb06225c0e358533609b66a8edfb95e5426d8f658664e7d0f27dea68"}, +] types-Flask = [ {file = "types-Flask-1.1.6.tar.gz", hash = "sha256:aac777b3abfff9436e6b01f6d08171cf23ea6e5be71cbf773aaabb1c5763e9cf"}, {file = "types_Flask-1.1.6-py3-none-any.whl", hash = "sha256:6ab8a9a5e258b76539d652f6341408867298550b19b81f0e41e916825fc39087"}, diff --git a/pyproject.toml b/pyproject.toml index a4741447..7f2d09a4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ flask-migrate = "*" flask-restful = "*" werkzeug = "*" SpiffWorkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "main"} +#SpiffWorkflow = {develop = true, path = "../SpiffWorkflow" } sentry-sdk = "^1.10" sphinx-autoapi = "^2.0" flask-bpmn = {git = "https://github.com/sartography/flask-bpmn", rev = "main"} @@ -68,6 +69,9 @@ types-pytz = "^2022.1.1" # for now use my fork sqlalchemy-stubs = { git = "https://github.com/burnettk/sqlalchemy-stubs.git", rev = "scoped-session-delete" } simplejson = "^3.17.6" +pytz = "^2022.6" +dateparser = "^1.1.2" +types-dateparser = "^1.1.4.1" [tool.poetry.dev-dependencies] diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index f2ffec20..f0a93fd9 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1071,7 +1071,9 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response task.form_ui_schema = ui_form_contents if task.properties and task.data and "instructionsForEndUser" in task.properties: - print(f"task.properties['instructionsForEndUser']: {task.properties['instructionsForEndUser']}") + print( + f"task.properties['instructionsForEndUser']: {task.properties['instructionsForEndUser']}" + ) if task.properties["instructionsForEndUser"]: task.properties["instructionsForEndUser"] = render_jinja_template( task.properties["instructionsForEndUser"], task.data diff --git a/src/spiffworkflow_backend/scripts/get_frontend_url.py b/src/spiffworkflow_backend/scripts/get_frontend_url.py index 40dc482c..9490df95 100644 --- a/src/spiffworkflow_backend/scripts/get_frontend_url.py +++ b/src/spiffworkflow_backend/scripts/get_frontend_url.py @@ -1,5 +1,6 @@ """Get_env.""" from typing import Any + from flask import current_app from spiffworkflow_backend.models.script_attributes_context import ( @@ -22,4 +23,4 @@ class GetFrontendUrl(Script): **kwargs: Any ) -> Any: """Run.""" - return current_app.config['SPIFFWORKFLOW_FRONTEND_URL'] + return current_app.config["SPIFFWORKFLOW_FRONTEND_URL"] diff --git a/src/spiffworkflow_backend/services/process_instance_processor.py b/src/spiffworkflow_backend/services/process_instance_processor.py index d82d8063..b499081f 100644 --- a/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/src/spiffworkflow_backend/services/process_instance_processor.py @@ -7,6 +7,7 @@ import os import re import time from datetime import datetime +from datetime import timedelta from typing import Any from typing import Callable from typing import Dict @@ -17,6 +18,8 @@ from typing import Tuple from typing import TypedDict from typing import Union +import dateparser +import pytz from flask import current_app from flask_bpmn.api.api_error import ApiError from flask_bpmn.models.db import db @@ -25,7 +28,6 @@ from RestrictedPython import safe_globals # type: ignore from SpiffWorkflow.bpmn.exceptions import WorkflowTaskExecException # type: ignore from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException # type: ignore from SpiffWorkflow.bpmn.PythonScriptEngine import Box # type: ignore -from SpiffWorkflow.bpmn.PythonScriptEngine import DEFAULT_GLOBALS from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine from SpiffWorkflow.bpmn.serializer import BpmnWorkflowSerializer # type: ignore from SpiffWorkflow.bpmn.specs.BpmnProcessSpec import BpmnProcessSpec # type: ignore @@ -98,19 +100,6 @@ def _import(name: str, glbls: Dict[str, Any], *args: Any) -> None: raise ImportError(f"Import not allowed: {name}", name=name) -DEFAULT_GLOBALS.update( - { - "datetime": datetime, - "time": time, - "decimal": decimal, - "_strptime": _strptime, - } -) -# This will overwrite the standard builtins -DEFAULT_GLOBALS.update(safe_globals) -DEFAULT_GLOBALS["__builtins__"]["__import__"] = _import - - class PotentialOwnerIdList(TypedDict): """PotentialOwnerIdList.""" @@ -143,7 +132,21 @@ class CustomBpmnScriptEngine(PythonScriptEngine): # type: ignore def __init__(self) -> None: """__init__.""" - super().__init__(default_globals=DEFAULT_GLOBALS) + default_globals = { + "timedelta": timedelta, + "datetime": datetime, + "dateparser": dateparser, + "pytz": pytz, + "time": time, + "decimal": decimal, + "_strptime": _strptime, + } + + # This will overwrite the standard builtins + default_globals.update(safe_globals) + default_globals["__builtins__"]["__import__"] = _import + + super().__init__(default_globals=default_globals) def __get_augment_methods(self, task: SpiffTask) -> Dict[str, Callable]: """__get_augment_methods.""" From d32b5d8e0068aea81f2cd87bebff7111da19b403 Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 2 Nov 2022 12:42:49 -0400 Subject: [PATCH 11/46] add date ranges for process instances search w/ burnettk --- .../src/spiffworkflow_backend/api.yml | 4 +- .../routes/process_api_blueprint.py | 12 +- spiffworkflow-frontend/src/config.tsx | 4 +- spiffworkflow-frontend/src/helpers.tsx | 25 ++- .../src/routes/ProcessInstanceList.tsx | 191 ++++++++---------- 5 files changed, 110 insertions(+), 126 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index 614d4f26..de10f598 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -429,7 +429,7 @@ paths: description: For filtering - beginning of start window - in seconds since epoch schema: type: integer - - name: start_till + - name: start_to in: query required: false description: For filtering - end of start window - in seconds since epoch @@ -441,7 +441,7 @@ paths: description: For filtering - beginning of end window - in seconds since epoch schema: type: integer - - name: end_till + - name: end_to in: query required: false description: For filtering - end of end window - in seconds since epoch diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index f2ffec20..012cb52f 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -651,9 +651,9 @@ def process_instance_list( page: int = 1, per_page: int = 100, start_from: Optional[int] = None, - start_till: Optional[int] = None, + start_to: Optional[int] = None, end_from: Optional[int] = None, - end_till: Optional[int] = None, + end_to: Optional[int] = None, process_status: Optional[str] = None, ) -> flask.wrappers.Response: """Process_instance_list.""" @@ -684,17 +684,17 @@ def process_instance_list( process_instance_query = process_instance_query.filter( ProcessInstanceModel.start_in_seconds >= start_from ) - if start_till is not None: + if start_to is not None: process_instance_query = process_instance_query.filter( - ProcessInstanceModel.start_in_seconds <= start_till + ProcessInstanceModel.start_in_seconds <= start_to ) if end_from is not None: process_instance_query = process_instance_query.filter( ProcessInstanceModel.end_in_seconds >= end_from ) - if end_till is not None: + if end_to is not None: process_instance_query = process_instance_query.filter( - ProcessInstanceModel.end_in_seconds <= end_till + ProcessInstanceModel.end_in_seconds <= end_to ) if process_status is not None: process_status_array = process_status.split(",") diff --git a/spiffworkflow-frontend/src/config.tsx b/spiffworkflow-frontend/src/config.tsx index 573e96bf..c9e94eb0 100644 --- a/spiffworkflow-frontend/src/config.tsx +++ b/spiffworkflow-frontend/src/config.tsx @@ -16,4 +16,6 @@ export const PROCESS_STATUSES = [ 'suspended', ]; -export const DATE_FORMAT = 'Y-m-d'; +// with time: yyyy-MM-dd HH:mm:ss +export const DATE_FORMAT = 'yyyy-MM-dd'; +export const DATE_FORMAT_CARBON = 'Y-m-d'; diff --git a/spiffworkflow-frontend/src/helpers.tsx b/spiffworkflow-frontend/src/helpers.tsx index e9bb100a..6b27fb4c 100644 --- a/spiffworkflow-frontend/src/helpers.tsx +++ b/spiffworkflow-frontend/src/helpers.tsx @@ -20,7 +20,10 @@ export const capitalizeFirstLetter = (string: any) => { return string.charAt(0).toUpperCase() + string.slice(1); }; -export const convertDateToSeconds = (date: any, onChangeFunction: any) => { +export const convertDateToSeconds = ( + date: any, + onChangeFunction: any = null +) => { let dateInSeconds = date; if (date !== null) { let dateInMilliseconds = date; @@ -39,14 +42,26 @@ export const convertDateToSeconds = (date: any, onChangeFunction: any) => { return null; }; -export const convertSecondsToFormattedDate = (seconds: number) => { - if (seconds) { - const startDate = new Date(seconds * 1000); - return format(startDate, DATE_FORMAT); +export const convertStringToDate = (dateString: string) => { + if (dateString) { + return new Date(dateString); } return null; }; +export const convertSecondsToFormattedDate = (seconds: number) => { + if (seconds) { + const dateObject = new Date(seconds * 1000); + return format(dateObject, DATE_FORMAT); + } + return null; +}; + +export const convertDateStringToSeconds = (dateString: string) => { + const dateObject = convertStringToDate(dateString); + return convertDateToSeconds(dateObject); +}; + export const objectIsEmpty = (obj: object) => { return Object.keys(obj).length === 0; }; diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx index 214ab936..63ee7a2f 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx @@ -24,9 +24,9 @@ import { } from '@carbon/react'; import { InputGroup } from 'react-bootstrap'; import { Typeahead } from 'react-bootstrap-typeahead'; -import { PROCESS_STATUSES, DATE_FORMAT } from '../config'; +import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config'; import { - convertDateToSeconds, + convertDateStringToSeconds, convertSecondsToFormattedDate, getPageInfoFromSearchParams, getProcessModelFullIdentifierFromSearchParams, @@ -53,10 +53,10 @@ export default function ProcessInstanceList() { const oneHourInSeconds = 3600; const oneMonthInSeconds = oneHourInSeconds * 24 * 30; - const [startFrom, setStartFrom] = useState(null); - const [startTill, setStartTill] = useState(null); - const [endFrom, setEndFrom] = useState(null); - const [endTill, setEndTill] = useState(null); + const [startFrom, setStartFrom] = useState(''); + const [startTo, setStartTo] = useState(''); + const [endFrom, setEndFrom] = useState(''); + const [endTo, setEndTo] = useState(''); const setErrorMessage = (useContext as any)(ErrorContext)[1]; @@ -69,20 +69,17 @@ export default function ProcessInstanceList() { const [processModelAvailableItems, setProcessModelAvailableItems] = useState< ProcessModel[] >([]); - const [processModelFilteredItems, setProcessModelFilteredItems] = useState< - ProcessModel[] - >([]); const [processModelSelection, setProcessModelSelection] = useState(null); const parametersToAlwaysFilterBy = useMemo(() => { return { start_from: setStartFrom, - start_till: setStartTill, + start_to: setStartTo, end_from: setEndFrom, - end_till: setEndTill, + end_to: setEndTo, }; - }, [setStartFrom, setStartTill, setEndFrom, setEndTill]); + }, [setStartFrom, setStartTo, setEndFrom, setEndTo]); const parametersToGetFromSearchParams = useMemo(() => { return { @@ -109,7 +106,10 @@ export default function ProcessInstanceList() { const searchParamValue = searchParams.get(paramName); if (searchParamValue) { queryParamString += `&${paramName}=${searchParamValue}`; - functionToCall(searchParamValue); + const dateString = convertSecondsToFormattedDate( + searchParamValue as any + ); + functionToCall(dateString); } }); @@ -142,7 +142,6 @@ export default function ProcessInstanceList() { return item; }); setProcessModelAvailableItems(selectionArray); - setProcessModelFilteredItems(selectionArray); const processStatusSelectedArray: string[] = []; const processStatusAllOptionsArray = PROCESS_STATUSES.map( @@ -195,34 +194,46 @@ export default function ProcessInstanceList() { const { page, perPage } = getPageInfoFromSearchParams(searchParams); let queryParamString = `per_page=${perPage}&page=${page}`; - if (isTrueComparison(startFrom, '>', startTill)) { - setErrorMessage({ message: 'startFrom cannot be after startTill' }); + const startFromSeconds = convertDateStringToSeconds(startFrom); + const endFromSeconds = convertDateStringToSeconds(endFrom); + const startToSeconds = convertDateStringToSeconds(startTo); + const endToSeconds = convertDateStringToSeconds(endTo); + if (isTrueComparison(startFromSeconds, '>', startToSeconds)) { + setErrorMessage({ + message: '"Start date from" cannot be after "start date to"', + }); return; } - if (isTrueComparison(endFrom, '>', endTill)) { - setErrorMessage({ message: 'endFrom cannot be after endTill' }); + if (isTrueComparison(endFromSeconds, '>', endToSeconds)) { + setErrorMessage({ + message: '"End date from" cannot be after "end date to"', + }); return; } - if (isTrueComparison(startFrom, '>', endFrom)) { - setErrorMessage({ message: 'startFrom cannot be after endFrom' }); + if (isTrueComparison(startFromSeconds, '>', endFromSeconds)) { + setErrorMessage({ + message: '"Start date from" cannot be after "end date from"', + }); return; } - if (isTrueComparison(startTill, '>', endTill)) { - setErrorMessage({ message: 'startTill cannot be after endTill' }); + if (isTrueComparison(startToSeconds, '>', endToSeconds)) { + setErrorMessage({ + message: '"Start date to" cannot be after "end date to"', + }); return; } - if (startFrom) { - queryParamString += `&start_from=${startFrom}`; + if (startFromSeconds) { + queryParamString += `&start_from=${startFromSeconds}`; } - if (startTill) { - queryParamString += `&start_till=${startTill}`; + if (startToSeconds) { + queryParamString += `&start_to=${startToSeconds}`; } - if (endFrom) { - queryParamString += `&end_from=${endFrom}`; + if (endFromSeconds) { + queryParamString += `&end_from=${endFromSeconds}`; } - if (endTill) { - queryParamString += `&end_till=${endTill}`; + if (endToSeconds) { + queryParamString += `&end_to=${endToSeconds}`; } if (processStatusSelection.length > 0) { queryParamString += `&process_status=${processStatusSelection}`; @@ -242,30 +253,22 @@ export default function ProcessInstanceList() { initialDate: any, onChangeFunction: any ) => { - let selectedDate = null; - if (initialDate) { - selectedDate = new Date(initialDate * 1000); - } return ( - - - - - {labelString} - {'\u00A0'} - - - convertDateToSeconds(date, onChangeFunction) - } - showTimeSelect - dateFormat={DATE_FORMAT} - /> - - - + + { + onChangeFunction(dateChangeEvent.srcElement.value); + }} + value={initialDate} + /> + ); }; @@ -291,10 +294,8 @@ export default function ProcessInstanceList() { const shouldFilterProcessModel = (options: any) => { const processModel: ProcessModel = options.item; const { inputValue } = options; - return ( - processModel.process_group_id.match(inputValue) || - processModel.id.match(inputValue) || - processModel.display_name.match(inputValue) + return `${processModel.process_group_id}/${processModel.id} (${processModel.display_name})`.includes( + inputValue ); }; @@ -305,7 +306,7 @@ export default function ProcessInstanceList() { setProcessModelSelection(selection.selectedItem) } id="process-model-select" - items={processModelFilteredItems} + items={processModelAvailableItems} itemToString={(processModel: ProcessModel) => { if (processModel) { return `${processModel.process_group_id}/${ @@ -344,53 +345,14 @@ export default function ProcessInstanceList() { const clearFilters = () => { setProcessModelSelection(null); setProcessStatusSelection([]); + setStartFrom(''); + setStartTo(''); + setEndFrom(''); + setEndTo(''); }; const filterOptions = () => { const { page, perPage } = getPageInfoFromSearchParams(searchParams); - // return ( - //
    - //
    - //
    - //
    - // - // {processModelSearch()} - // - //
    - // - // {dateComponent( - // 'Start Range: ', - // 'start-from', - // startFrom, - // setStartFrom - // )} - // {dateComponent('-', 'start-till', startTill, setStartTill)} - // - //
    - // - // {dateComponent( - // 'End Range: \u00A0\u00A0', - // 'end-from', - // endFrom, - // setEndFrom - // )} - // {dateComponent('-', 'end-till', endTill, setEndTill)} - // - //
    - // - // {processStatusSearch()} - // - // - // - // - //
    - //
    - //
    - //
    - //
    - // ); return ( <> @@ -398,17 +360,22 @@ export default function ProcessInstanceList() { {processStatusSearch()} - - {' '} - - - + + {dateComponent( + 'Start date from', + 'start-from', + startFrom, + setStartFrom + )} + + + {dateComponent('Start date to', 'start-to', startTo, setStartTo)} + + + {dateComponent('End date from', 'end-from', endFrom, setEndFrom)} + + + {dateComponent('End date to', 'end-to', endTo, setEndTo)} From 95d9dbf036f09b62054c04a64d5c7a9e3002173c Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 2 Nov 2022 14:17:16 -0400 Subject: [PATCH 12/46] Squashed 'spiffworkflow-backend/' changes from 400fa583f..dba09086b dba09086b Merge pull request #164 from sartography/feature/remove_unused_tables 6baaf092b fixed group test w/ burnettk d55298517 update SpiffWorkflow w/ burnettk 10fee6d84 Merge remote-tracking branch 'origin/main' into feature/remove_unused_tables a2d4604ca pyl passes w/ burnettk b7aee0549 removed tables for file, admin_session, task_event, and data_store w/ burnettk git-subtree-dir: spiffworkflow-backend git-subtree-split: dba09086ba6e2ef090998a22a086a633e6c13694 --- .../{3bd6b0b1b8ae_.py => bdd1d64689db_.py} | 71 +--------- poetry.lock | 17 +-- .../load_database_models.py | 3 - .../models/data_store.py | 31 ---- src/spiffworkflow_backend/models/file.py | 30 ---- .../models/process_instance.py | 1 - .../models/task_event.py | 100 ------------- src/spiffworkflow_backend/models/user.py | 9 -- .../routes/process_api_blueprint.py | 3 - .../services/process_instance_processor.py | 57 +------- .../services/process_instance_service.py | 133 ------------------ .../services/user_service.py | 133 +----------------- .../get_group_members/get_group_members.bpmn | 4 +- .../helpers/base_test.py | 19 --- .../integration/test_process_api.py | 44 ------ 15 files changed, 15 insertions(+), 640 deletions(-) rename migrations/versions/{3bd6b0b1b8ae_.py => bdd1d64689db_.py} (83%) delete mode 100644 src/spiffworkflow_backend/models/data_store.py delete mode 100644 src/spiffworkflow_backend/models/task_event.py diff --git a/migrations/versions/3bd6b0b1b8ae_.py b/migrations/versions/bdd1d64689db_.py similarity index 83% rename from migrations/versions/3bd6b0b1b8ae_.py rename to migrations/versions/bdd1d64689db_.py index 80c47958..55566149 100644 --- a/migrations/versions/3bd6b0b1b8ae_.py +++ b/migrations/versions/bdd1d64689db_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: 3bd6b0b1b8ae +Revision ID: bdd1d64689db Revises: -Create Date: 2022-10-25 12:31:50.177599 +Create Date: 2022-11-02 11:31:50.606843 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '3bd6b0b1b8ae' +revision = 'bdd1d64689db' down_revision = None branch_labels = None depends_on = None @@ -18,13 +18,6 @@ depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.create_table('admin_session', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('token', sa.String(length=50), nullable=True), - sa.Column('admin_impersonate_uid', sa.String(length=50), nullable=True), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('token') - ) op.create_table('bpmn_process_id_lookup', sa.Column('id', sa.Integer(), nullable=False), sa.Column('bpmn_process_identifier', sa.String(length=255), nullable=True), @@ -183,25 +176,6 @@ def upgrade(): sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('task_id', 'process_instance_id', name='active_task_unique') ) - op.create_table('file', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('name', sa.String(length=50), nullable=False), - sa.Column('type', sa.String(length=50), nullable=False), - sa.Column('content_type', sa.String(length=50), nullable=False), - sa.Column('process_instance_id', sa.Integer(), nullable=True), - sa.Column('task_spec', sa.String(length=50), nullable=True), - sa.Column('irb_doc_code', sa.String(length=50), nullable=False), - sa.Column('md5_hash', sa.String(length=50), nullable=False), - sa.Column('data', sa.LargeBinary(), nullable=True), - sa.Column('size', sa.Integer(), nullable=True), - sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True), - sa.Column('created_at_in_seconds', sa.Integer(), nullable=True), - sa.Column('user_uid', sa.String(length=50), nullable=True), - sa.Column('archived', sa.Boolean(), nullable=True), - sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), - sa.ForeignKeyConstraint(['user_uid'], ['user.uid'], ), - sa.PrimaryKeyConstraint('id') - ) op.create_table('message_correlation', sa.Column('id', sa.Integer(), nullable=False), sa.Column('process_instance_id', sa.Integer(), nullable=False), @@ -259,28 +233,6 @@ def upgrade(): sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), sa.PrimaryKeyConstraint('id') ) - op.create_table('task_event', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('user_id', sa.Integer(), nullable=False), - sa.Column('process_instance_id', sa.Integer(), nullable=False), - sa.Column('spec_version', sa.String(length=50), nullable=True), - sa.Column('action', sa.String(length=50), nullable=True), - sa.Column('task_id', sa.String(length=50), nullable=True), - sa.Column('task_name', sa.String(length=50), nullable=True), - sa.Column('task_title', sa.String(length=50), nullable=True), - sa.Column('task_type', sa.String(length=50), nullable=True), - sa.Column('task_state', sa.String(length=50), nullable=True), - sa.Column('task_lane', sa.String(length=50), nullable=True), - sa.Column('form_data', sa.JSON(), nullable=True), - sa.Column('mi_type', sa.String(length=50), nullable=True), - sa.Column('mi_count', sa.Integer(), nullable=True), - sa.Column('mi_index', sa.Integer(), nullable=True), - sa.Column('process_name', sa.String(length=50), nullable=True), - sa.Column('date', sa.DateTime(timezone=True), nullable=True), - sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), - sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), - sa.PrimaryKeyConstraint('id') - ) op.create_table('active_task_user', sa.Column('id', sa.Integer(), nullable=False), sa.Column('active_task_id', sa.Integer(), nullable=False), @@ -292,19 +244,6 @@ def upgrade(): ) op.create_index(op.f('ix_active_task_user_active_task_id'), 'active_task_user', ['active_task_id'], unique=False) op.create_index(op.f('ix_active_task_user_user_id'), 'active_task_user', ['user_id'], unique=False) - op.create_table('data_store', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True), - sa.Column('key', sa.String(length=50), nullable=False), - sa.Column('process_instance_id', sa.Integer(), nullable=True), - sa.Column('task_spec', sa.String(length=50), nullable=True), - sa.Column('spec_id', sa.String(length=50), nullable=True), - sa.Column('user_id', sa.String(length=50), nullable=True), - sa.Column('file_id', sa.Integer(), nullable=True), - sa.Column('value', sa.String(length=50), nullable=True), - sa.ForeignKeyConstraint(['file_id'], ['file.id'], ), - sa.PrimaryKeyConstraint('id') - ) op.create_table('message_correlation_message_instance', sa.Column('id', sa.Integer(), nullable=False), sa.Column('message_instance_id', sa.Integer(), nullable=False), @@ -324,11 +263,9 @@ def downgrade(): op.drop_index(op.f('ix_message_correlation_message_instance_message_instance_id'), table_name='message_correlation_message_instance') op.drop_index(op.f('ix_message_correlation_message_instance_message_correlation_id'), table_name='message_correlation_message_instance') op.drop_table('message_correlation_message_instance') - op.drop_table('data_store') op.drop_index(op.f('ix_active_task_user_user_id'), table_name='active_task_user') op.drop_index(op.f('ix_active_task_user_active_task_id'), table_name='active_task_user') op.drop_table('active_task_user') - op.drop_table('task_event') op.drop_table('spiff_logging') op.drop_table('permission_assignment') op.drop_table('message_instance') @@ -337,7 +274,6 @@ def downgrade(): op.drop_index(op.f('ix_message_correlation_name'), table_name='message_correlation') op.drop_index(op.f('ix_message_correlation_message_correlation_property_id'), table_name='message_correlation') op.drop_table('message_correlation') - op.drop_table('file') op.drop_table('active_task') op.drop_table('user_group_assignment') op.drop_table('secret') @@ -363,5 +299,4 @@ def downgrade(): op.drop_table('group') op.drop_index(op.f('ix_bpmn_process_id_lookup_bpmn_process_identifier'), table_name='bpmn_process_id_lookup') op.drop_table('bpmn_process_id_lookup') - op.drop_table('admin_session') # ### end Alembic commands ### diff --git a/poetry.lock b/poetry.lock index 06b2b562..c474c696 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1873,7 +1873,7 @@ pytz = "*" type = "git" url = "https://github.com/sartography/SpiffWorkflow" reference = "main" -resolved_reference = "5cdb881edc4621502bfd61ce67565cf1148199f0" +resolved_reference = "a6392d19061f623394f5705fb78af23673d3940d" [[package]] name = "SQLAlchemy" @@ -2621,6 +2621,7 @@ greenlet = [ {file = "greenlet-1.1.3.post0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a954002064ee919b444b19c1185e8cce307a1f20600f47d6f4b6d336972c809"}, {file = "greenlet-1.1.3.post0-cp39-cp39-win32.whl", hash = "sha256:2ccdc818cc106cc238ff7eba0d71b9c77be868fdca31d6c3b1347a54c9b187b2"}, {file = "greenlet-1.1.3.post0-cp39-cp39-win_amd64.whl", hash = "sha256:91a84faf718e6f8b888ca63d0b2d6d185c8e2a198d2a7322d75c303e7097c8b7"}, + {file = "greenlet-1.1.3.post0.tar.gz", hash = "sha256:f5e09dc5c6e1796969fd4b775ea1417d70e49a5df29aaa8e5d10675d9e11872c"}, ] gunicorn = [ {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, @@ -2945,7 +2946,10 @@ orjson = [ {file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b68a42a31f8429728183c21fb440c21de1b62e5378d0d73f280e2d894ef8942e"}, {file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ff13410ddbdda5d4197a4a4c09969cb78c722a67550f0a63c02c07aadc624833"}, {file = "orjson-3.8.0-cp310-none-win_amd64.whl", hash = "sha256:2d81e6e56bbea44be0222fb53f7b255b4e7426290516771592738ca01dbd053b"}, + {file = "orjson-3.8.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:200eae21c33f1f8b02a11f5d88d76950cd6fd986d88f1afe497a8ae2627c49aa"}, + {file = "orjson-3.8.0-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:9529990f3eab54b976d327360aa1ff244a4b12cb5e4c5b3712fcdd96e8fe56d4"}, {file = "orjson-3.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e2defd9527651ad39ec20ae03c812adf47ef7662bdd6bc07dabb10888d70dc62"}, + {file = "orjson-3.8.0-cp311-none-win_amd64.whl", hash = "sha256:b21c7af0ff6228ca7105f54f0800636eb49201133e15ddb80ac20c1ce973ef07"}, {file = "orjson-3.8.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9e6ac22cec72d5b39035b566e4b86c74b84866f12b5b0b6541506a080fb67d6d"}, {file = "orjson-3.8.0-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:e2f4a5542f50e3d336a18cb224fc757245ca66b1fd0b70b5dd4471b8ff5f2b0e"}, {file = "orjson-3.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1418feeb8b698b9224b1f024555895169d481604d5d884498c1838d7412794c"}, @@ -3058,18 +3062,7 @@ py = [ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] pyasn1 = [ - {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, - {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, - {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, - {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, - {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, - {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, - {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, - {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, - {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, - {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, - {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, ] pycodestyle = [ diff --git a/src/spiffworkflow_backend/load_database_models.py b/src/spiffworkflow_backend/load_database_models.py index 12b33a7e..7283b19b 100644 --- a/src/spiffworkflow_backend/load_database_models.py +++ b/src/spiffworkflow_backend/load_database_models.py @@ -21,8 +21,6 @@ from spiffworkflow_backend.models.active_task import ActiveTaskModel # noqa: F4 from spiffworkflow_backend.models.bpmn_process_id_lookup import ( BpmnProcessIdLookup, ) # noqa: F401 -from spiffworkflow_backend.models.data_store import DataStoreModel # noqa: F401 -from spiffworkflow_backend.models.file import FileModel # noqa: F401 from spiffworkflow_backend.models.message_correlation_property import ( MessageCorrelationPropertyModel, ) # noqa: F401 @@ -48,7 +46,6 @@ from spiffworkflow_backend.models.process_instance_report import ( from spiffworkflow_backend.models.refresh_token import RefreshTokenModel # noqa: F401 from spiffworkflow_backend.models.secret_model import SecretModel # noqa: F401 from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel # noqa: F401 -from spiffworkflow_backend.models.task_event import TaskEventModel # noqa: F401 from spiffworkflow_backend.models.user import UserModel # noqa: F401 from spiffworkflow_backend.models.group import GroupModel # noqa: F401 diff --git a/src/spiffworkflow_backend/models/data_store.py b/src/spiffworkflow_backend/models/data_store.py deleted file mode 100644 index abbfcf34..00000000 --- a/src/spiffworkflow_backend/models/data_store.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Data_store.""" -from flask_bpmn.models.db import db -from flask_bpmn.models.db import SpiffworkflowBaseDBModel -from flask_marshmallow.sqla import SQLAlchemyAutoSchema # type: ignore - - -class DataStoreModel(SpiffworkflowBaseDBModel): - """DataStoreModel.""" - - __tablename__ = "data_store" - id = db.Column(db.Integer, primary_key=True) - updated_at_in_seconds = db.Column(db.Integer) - key = db.Column(db.String(50), nullable=False) - process_instance_id = db.Column(db.Integer) - task_spec = db.Column(db.String(50)) - spec_id = db.Column(db.String(50)) - user_id = db.Column(db.String(50), nullable=True) - file_id = db.Column(db.Integer, db.ForeignKey("file.id"), nullable=True) - value = db.Column(db.String(50)) - - -class DataStoreSchema(SQLAlchemyAutoSchema): # type: ignore - """DataStoreSchema.""" - - class Meta: - """Meta.""" - - model = DataStoreModel - load_instance = True - include_fk = True - sqla_session = db.session diff --git a/src/spiffworkflow_backend/models/file.py b/src/spiffworkflow_backend/models/file.py index eb49b873..0c844766 100644 --- a/src/spiffworkflow_backend/models/file.py +++ b/src/spiffworkflow_backend/models/file.py @@ -4,40 +4,10 @@ from dataclasses import field from datetime import datetime from typing import Optional -from flask_bpmn.models.db import db -from flask_bpmn.models.db import SpiffworkflowBaseDBModel from marshmallow import INCLUDE from marshmallow import Schema -from sqlalchemy.orm import deferred -from sqlalchemy.orm import relationship from spiffworkflow_backend.helpers.spiff_enum import SpiffEnum -from spiffworkflow_backend.models.data_store import DataStoreModel - - -class FileModel(SpiffworkflowBaseDBModel): - """FileModel.""" - - __tablename__ = "file" - id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(50), nullable=False) - type = db.Column(db.String(50), nullable=False) - content_type = db.Column(db.String(50), nullable=False) - process_instance_id = db.Column( - db.Integer, db.ForeignKey("process_instance.id"), nullable=True - ) - task_spec = db.Column(db.String(50), nullable=True) - irb_doc_code = db.Column( - db.String(50), nullable=False - ) # Code reference to the documents.xlsx reference file. - data_stores = relationship(DataStoreModel, cascade="all,delete", backref="file") - md5_hash = db.Column(db.String(50), unique=False, nullable=False) - data = deferred(db.Column(db.LargeBinary)) # type: ignore - size = db.Column(db.Integer, default=0) - updated_at_in_seconds = db.Column(db.Integer) - created_at_in_seconds = db.Column(db.Integer) - user_uid = db.Column(db.String(50), db.ForeignKey("user.uid"), nullable=True) - archived = db.Column(db.Boolean, default=False) class FileType(SpiffEnum): diff --git a/src/spiffworkflow_backend/models/process_instance.py b/src/spiffworkflow_backend/models/process_instance.py index d1d117c8..1c2098e9 100644 --- a/src/spiffworkflow_backend/models/process_instance.py +++ b/src/spiffworkflow_backend/models/process_instance.py @@ -78,7 +78,6 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): process_initiator = relationship("UserModel") active_tasks = relationship("ActiveTaskModel", cascade="delete") # type: ignore - task_events = relationship("TaskEventModel", cascade="delete") # type: ignore spiff_logs = relationship("SpiffLoggingModel", cascade="delete") # type: ignore message_instances = relationship("MessageInstanceModel", cascade="delete") # type: ignore message_correlations = relationship("MessageCorrelationModel", cascade="delete") # type: ignore diff --git a/src/spiffworkflow_backend/models/task_event.py b/src/spiffworkflow_backend/models/task_event.py deleted file mode 100644 index 5bb668b4..00000000 --- a/src/spiffworkflow_backend/models/task_event.py +++ /dev/null @@ -1,100 +0,0 @@ -"""Task_event.""" -from __future__ import annotations - -import enum -from typing import TYPE_CHECKING - -from flask_bpmn.models.db import db -from flask_bpmn.models.db import SpiffworkflowBaseDBModel -from marshmallow import fields -from marshmallow import INCLUDE -from marshmallow import Schema -from sqlalchemy import func - - -if TYPE_CHECKING: - from spiffworkflow_backend.models.process_instance import ( - ProcessInstanceModel, - ) # noqa: F401 - - -class TaskAction(enum.Enum): - """TaskAction.""" - - COMPLETE = "COMPLETE" - TOKEN_RESET = "TOKEN_RESET" # noqa: S105 - HARD_RESET = "HARD_RESET" - SOFT_RESET = "SOFT_RESET" - ASSIGNMENT = "ASSIGNMENT" # Whenever the lane changes between tasks we assign the task to specific user. - - -class TaskEventModel(SpiffworkflowBaseDBModel): - """TaskEventModel.""" - - __tablename__ = "task_event" - id = db.Column(db.Integer, primary_key=True) - user_id = db.Column( - db.Integer, db.ForeignKey("user.id"), nullable=False - ) # In some cases the unique user id may not exist in the db yet. - process_instance_id = db.Column( - db.Integer, db.ForeignKey("process_instance.id"), nullable=False - ) - spec_version = db.Column(db.String(50)) - action = db.Column(db.String(50)) - task_id = db.Column(db.String(50)) - task_name = db.Column(db.String(50)) - task_title = db.Column(db.String(50)) - task_type = db.Column(db.String(50)) - task_state = db.Column(db.String(50)) - task_lane = db.Column(db.String(50)) - form_data = db.Column( - db.JSON - ) # And form data submitted when the task was completed. - mi_type = db.Column(db.String(50)) - mi_count = db.Column(db.Integer) - mi_index = db.Column(db.Integer) - process_name = db.Column(db.String(50)) - date = db.Column(db.DateTime(timezone=True), default=func.now()) - - -class TaskEvent: - """TaskEvent.""" - - def __init__(self, model: TaskEventModel, process_instance: ProcessInstanceModel): - """__init__.""" - self.id = model.id - self.process_instance = process_instance - self.user_id = model.user_id - self.action = model.action - self.task_id = model.task_id - self.task_title = model.task_title - self.task_name = model.task_name - self.task_type = model.task_type - self.task_state = model.task_state - self.task_lane = model.task_lane - self.date = model.date - - -class TaskEventSchema(Schema): - """TaskEventSchema.""" - - process_instance = fields.Nested("ProcessInstanceMetadataSchema", dump_only=True) - task_lane = fields.String(allow_none=True, required=False) - - class Meta: - """Meta.""" - - model = TaskEvent - additional = [ - "id", - "user_id", - "action", - "task_id", - "task_title", - "task_name", - "task_type", - "task_state", - "task_lane", - "date", - ] - unknown = INCLUDE diff --git a/src/spiffworkflow_backend/models/user.py b/src/spiffworkflow_backend/models/user.py index 22cdfb69..c33a72e7 100644 --- a/src/spiffworkflow_backend/models/user.py +++ b/src/spiffworkflow_backend/models/user.py @@ -112,12 +112,3 @@ class UserModelSchema(Schema): id = marshmallow.fields.String(required=True) username = marshmallow.fields.String(required=True) - - -class AdminSessionModel(SpiffworkflowBaseDBModel): - """AdminSessionModel.""" - - __tablename__ = "admin_session" - id = db.Column(db.Integer, primary_key=True) - token = db.Column(db.String(50), unique=True) - admin_impersonate_uid = db.Column(db.String(50)) diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index f0a93fd9..0523ad25 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -424,7 +424,6 @@ def process_instance_run( task=task, ) from e processor.save() - ProcessInstanceService.update_task_assignments(processor) if not current_app.config["RUN_BACKGROUND_SCHEDULER"]: MessageService.process_message_instances() @@ -1123,8 +1122,6 @@ def task_submit( # last_index = next_task.task_info()["mi_index"] # next_task = processor.next_task() - ProcessInstanceService.update_task_assignments(processor) - next_active_task_assigned_to_me = ( ActiveTaskModel.query.filter_by(process_instance_id=process_instance_id) .order_by(asc(ActiveTaskModel.id)) # type: ignore diff --git a/src/spiffworkflow_backend/services/process_instance_processor.py b/src/spiffworkflow_backend/services/process_instance_processor.py index b499081f..fecde1b9 100644 --- a/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/src/spiffworkflow_backend/services/process_instance_processor.py @@ -79,8 +79,6 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.script_attributes_context import ( ScriptAttributesContext, ) -from spiffworkflow_backend.models.task_event import TaskAction -from spiffworkflow_backend.models.task_event import TaskEventModel from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user import UserModelSchema from spiffworkflow_backend.scripts.script import Script @@ -419,7 +417,7 @@ class ProcessInstanceProcessor: """Add_user_info_to_process_instance.""" current_user = None if UserService.has_user(): - current_user = UserService.current_user(allow_admin_impersonate=True) + current_user = UserService.current_user() # fall back to initiator if g.user is not set # this is for background processes when there will not be a user @@ -433,59 +431,6 @@ class ProcessInstanceProcessor: for task in tasks: task.data["current_user"] = current_user_data - @staticmethod - def reset( - process_instance_model: ProcessInstanceModel, clear_data: bool = False - ) -> None: - """Resets the process_instance back to an unstarted state - where nothing has happened yet. - - If clear_data is set to false, then the information - previously used in forms will be re-populated when the form is re- - displayed, and any files that were updated will remain in place, otherwise - files will also be cleared out. - """ - # Try to execute a cancel notify - try: - bpmn_process_instance = ( - ProcessInstanceProcessor.__get_bpmn_process_instance( - process_instance_model - ) - ) - ProcessInstanceProcessor.__cancel_notify(bpmn_process_instance) - except Exception as e: - db.session.rollback() # in case the above left the database with a bad transaction - current_app.logger.error( - "Unable to send a cancel notify for process_instance %s during a reset." - " Continuing with the reset anyway so we don't get in an unresolvable" - " state. An %s error occured with the following information: %s" - % (process_instance_model.id, e.__class__.__name__, str(e)) - ) - process_instance_model.bpmn_json = None - process_instance_model.status = ProcessInstanceStatus.not_started.value - - # clear out any task assignments - db.session.query(TaskEventModel).filter( - TaskEventModel.process_instance_id == process_instance_model.id - ).filter(TaskEventModel.action == TaskAction.ASSIGNMENT.value).delete() - - if clear_data: - # Clear out data in previous task events - task_events = ( - db.session.query(TaskEventModel) - .filter(TaskEventModel.process_instance_id == process_instance_model.id) - .all() - ) - for task_event in task_events: - task_event.form_data = {} - db.session.add(task_event) - # Remove any uploaded files. - - # TODO: grab UserFileService - # files = FileModel.query.filter(FileModel.process_instance_id == process_instance_model.id).all() - # for file in files: - # UserFileService().delete_file(file.id) - db.session.commit() - @staticmethod def get_bpmn_process_instance_from_workflow_spec( spec: BpmnProcessSpec, diff --git a/src/spiffworkflow_backend/services/process_instance_service.py b/src/spiffworkflow_backend/services/process_instance_service.py index f3d080a8..062d0ef7 100644 --- a/src/spiffworkflow_backend/services/process_instance_service.py +++ b/src/spiffworkflow_backend/services/process_instance_service.py @@ -1,7 +1,6 @@ """Process_instance_service.""" import time from typing import Any -from typing import Dict from typing import List from typing import Optional @@ -9,15 +8,12 @@ from flask import current_app from flask_bpmn.api.api_error import ApiError from flask_bpmn.models.db import db from SpiffWorkflow.task import Task as SpiffTask # type: ignore -from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore from spiffworkflow_backend.models.process_instance import ProcessInstanceApi from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus from spiffworkflow_backend.models.task import MultiInstanceType from spiffworkflow_backend.models.task import Task -from spiffworkflow_backend.models.task_event import TaskAction -from spiffworkflow_backend.models.task_event import TaskEventModel from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.git_service import GitService @@ -108,70 +104,9 @@ class ProcessInstanceService: is_review=is_review_value, title=title_value, ) - next_task_trying_again = next_task - if ( - not next_task - ): # The Next Task can be requested to be a certain task, useful for parallel tasks. - # This may or may not work, sometimes there is no next task to complete. - next_task_trying_again = processor.next_task() - - if next_task_trying_again is not None: - previous_form_data = ProcessInstanceService.get_previously_submitted_data( - processor.process_instance_model.id, next_task_trying_again - ) - # DeepMerge.merge(next_task_trying_again.data, previous_form_data) - next_task_trying_again.data = DeepMerge.merge( - previous_form_data, next_task_trying_again.data - ) - - process_instance_api.next_task = ( - ProcessInstanceService.spiff_task_to_api_task( - next_task_trying_again, add_docs_and_forms=True - ) - ) - # TODO: Hack for now, until we decide how to implment forms - process_instance_api.next_task.form = None - - # Update the state of the task to locked if the current user does not own the task. - # user_uids = WorkflowService.get_users_assigned_to_task(processor, next_task) - # if not UserService.in_list(user_uids, allow_admin_impersonate=True): - # workflow_api.next_task.state = WorkflowService.TASK_STATE_LOCKED return process_instance_api - @staticmethod - def get_previously_submitted_data( - process_instance_id: int, spiff_task: SpiffTask - ) -> Dict[Any, Any]: - """If the user has completed this task previously, find the form data for the last submission.""" - query = ( - db.session.query(TaskEventModel) - .filter_by(process_instance_id=process_instance_id) - .filter_by(task_name=spiff_task.task_spec.name) - .filter_by(action=TaskAction.COMPLETE.value) - ) - - if ( - hasattr(spiff_task, "internal_data") - and "runtimes" in spiff_task.internal_data - ): - query = query.filter_by(mi_index=spiff_task.internal_data["runtimes"]) - - latest_event = query.order_by(TaskEventModel.date.desc()).first() - if latest_event: - if latest_event.form_data is not None: - return latest_event.form_data # type: ignore - else: - missing_form_error = ( - f"We have lost data for workflow {process_instance_id}, " - f"task {spiff_task.task_spec.name}, it is not in the task event model, " - f"and it should be." - ) - current_app.logger.exception("missing_form_data", missing_form_error) - return {} - else: - return {} - def get_process_instance(self, process_instance_id: int) -> Any: """Get_process_instance.""" result = ( @@ -181,30 +116,6 @@ class ProcessInstanceService: ) return result - @staticmethod - def update_task_assignments(processor: ProcessInstanceProcessor) -> None: - """For every upcoming user task, log a task action that connects the assigned user(s) to that task. - - All existing assignment actions for this workflow are removed from the database, - so that only the current valid actions are available. update_task_assignments - should be called whenever progress is made on a workflow. - """ - db.session.query(TaskEventModel).filter( - TaskEventModel.process_instance_id == processor.process_instance_model.id - ).filter(TaskEventModel.action == TaskAction.ASSIGNMENT.value).delete() - db.session.commit() - - tasks = processor.get_current_user_tasks() - for task in tasks: - user_ids = ProcessInstanceService.get_users_assigned_to_task( - processor, task - ) - - for user_id in user_ids: - ProcessInstanceService().log_task_action( - user_id, processor, task, TaskAction.ASSIGNMENT.value - ) - @staticmethod def get_users_assigned_to_task( processor: ProcessInstanceProcessor, spiff_task: SpiffTask @@ -279,52 +190,8 @@ class ProcessInstanceService: spiff_task.update_data(dot_dct) # ProcessInstanceService.post_process_form(spiff_task) # some properties may update the data store. processor.complete_task(spiff_task) - # Log the action before doing the engine steps, as doing so could effect the state of the task - # the workflow could wrap around in the ngine steps, and the task could jump from being completed to - # another state. What we are logging here is the completion. - ProcessInstanceService.log_task_action( - user.id, processor, spiff_task, TaskAction.COMPLETE.value - ) processor.do_engine_steps(save=True) - @staticmethod - def log_task_action( - user_id: int, - processor: ProcessInstanceProcessor, - spiff_task: SpiffTask, - action: str, - ) -> None: - """Log_task_action.""" - task = ProcessInstanceService.spiff_task_to_api_task(spiff_task) - form_data = ProcessInstanceService.extract_form_data( - spiff_task.data, spiff_task - ) - multi_instance_type_value = "" - if task.multi_instance_type: - multi_instance_type_value = task.multi_instance_type.value - - task_event = TaskEventModel( - # study_id=processor.workflow_model.study_id, - user_id=user_id, - process_instance_id=processor.process_instance_model.id, - # workflow_spec_id=processor.workflow_model.workflow_spec_id, - action=action, - task_id=str(task.id), - task_name=task.name, - task_title=task.title, - task_type=str(task.type), - task_state=task.state, - task_lane=task.lane, - form_data=form_data, - mi_type=multi_instance_type_value, # Some tasks have a repeat behavior. - mi_count=task.multi_instance_count, # This is the number of times the task could repeat. - mi_index=task.multi_instance_index, # And the index of the currently repeating task. - process_name=task.process_name, - # date=datetime.utcnow(), <=== For future reference, NEVER do this. Let the database set the time. - ) - db.session.add(task_event) - db.session.commit() - @staticmethod def extract_form_data(latest_data: dict, task: SpiffTask) -> dict: """Extracts data from the latest_data that is directly related to the form that is being submitted.""" diff --git a/src/spiffworkflow_backend/services/user_service.py b/src/spiffworkflow_backend/services/user_service.py index d4749e01..0e8e65c2 100644 --- a/src/spiffworkflow_backend/services/user_service.py +++ b/src/spiffworkflow_backend/services/user_service.py @@ -11,7 +11,6 @@ from spiffworkflow_backend.models.active_task import ActiveTaskModel from spiffworkflow_backend.models.active_task_user import ActiveTaskUserModel from spiffworkflow_backend.models.group import GroupModel from spiffworkflow_backend.models.principal import PrincipalModel -from spiffworkflow_backend.models.user import AdminSessionModel from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user_group_assignment import UserGroupAssignmentModel @@ -103,27 +102,6 @@ class UserService: """Has_user.""" return "token" in g and bool(g.token) and "user" in g and bool(g.user) - # Returns true if the current user is an admin. - @staticmethod - def user_is_admin() -> bool: - """User_is_admin.""" - return UserService.has_user() and g.user.is_admin() - - # Returns true if the current admin user is impersonating another user. - @staticmethod - def admin_is_impersonating() -> bool: - """Admin_is_impersonating.""" - if UserService.user_is_admin(): - admin_session = UserService.get_admin_session() - return admin_session is not None - - else: - raise ApiError( - "unauthorized", - "You do not have permissions to do this.", - status_code=403, - ) - # Returns true if the given user uid is different from the current user's uid. @staticmethod def is_different_user(uid: str) -> bool: @@ -131,84 +109,16 @@ class UserService: return UserService.has_user() and uid is not None and uid is not g.user.uid @staticmethod - def current_user(allow_admin_impersonate: bool = False) -> Any: + def current_user() -> Any: """Current_user.""" if not UserService.has_user(): raise ApiError( "logged_out", "You are no longer logged in.", status_code=401 ) - - # Admins can pretend to be different users and act on a user's behalf in - # some circumstances. - if ( - UserService.user_is_admin() - and allow_admin_impersonate - and UserService.admin_is_impersonating() - ): - return UserService.get_admin_session_user() - else: - return g.user - - # Admins can pretend to be different users and act on a user's behalf in some circumstances. - # This method allows an admin user to start impersonating another user with the given uid. - # Stops impersonating if the uid is None or invalid. - @staticmethod - def start_impersonating(uid: Optional[str] = None) -> None: - """Start_impersonating.""" - if not UserService.has_user(): - raise ApiError( - "logged_out", "You are no longer logged in.", status_code=401 - ) - - if not UserService.user_is_admin(): - raise ApiError( - "unauthorized", - "You do not have permissions to do this.", - status_code=403, - ) - - if uid is None: - raise ApiError("invalid_uid", "Please provide a valid user uid.") - - if UserService.is_different_user(uid): - # Impersonate the user if the given uid is valid. - impersonate_user = ( - db.session.query(UserModel).filter(UserModel.uid == uid).first() - ) - - if impersonate_user is not None: - g.impersonate_user = impersonate_user - - # Store the uid and user session token. - db.session.query(AdminSessionModel).filter( - AdminSessionModel.token == g.token - ).delete() - db.session.add( - AdminSessionModel(token=g.token, admin_impersonate_uid=uid) - ) - db.session.commit() - else: - raise ApiError("invalid_uid", "The uid provided is not valid.") + return g.user @staticmethod - def stop_impersonating() -> None: - """Stop_impersonating.""" - if not UserService.has_user(): - raise ApiError( - "logged_out", "You are no longer logged in.", status_code=401 - ) - - # Clear out the current impersonating user. - if "impersonate_user" in g: - del g.impersonate_user - - admin_session = UserService.get_admin_session() - if admin_session: - db.session.delete(admin_session) - db.session.commit() - - @staticmethod - def in_list(uids: list[str], allow_admin_impersonate: bool = False) -> bool: + def in_list(uids: list[str]) -> bool: """Returns true if the current user's id is in the given list of ids. False if there is no user, or the user is not in the list. @@ -216,46 +126,11 @@ class UserService: if ( UserService.has_user() ): # If someone is logged in, lock tasks that don't belong to them. - user = UserService.current_user(allow_admin_impersonate) + user = UserService.current_user() if user.uid in uids: return True return False - @staticmethod - def get_admin_session() -> Any: - """Get_admin_session.""" - if UserService.user_is_admin(): - return ( - db.session.query(AdminSessionModel) - .filter(AdminSessionModel.token == g.token) - .first() - ) - else: - raise ApiError( - "unauthorized", - "You do not have permissions to do this.", - status_code=403, - ) - - @staticmethod - def get_admin_session_user() -> Any: - """Get_admin_session_user.""" - if UserService.user_is_admin(): - admin_session = UserService.get_admin_session() - - if admin_session is not None: - return ( - db.session.query(UserModel) - .filter(UserModel.uid == admin_session.admin_impersonate_uid) - .first() - ) - else: - raise ApiError( - "unauthorized", - "You do not have permissions to do this.", - status_code=403, - ) - @staticmethod def get_principal_by_user_id(user_id: int) -> PrincipalModel: """Get_principal_by_user_id.""" diff --git a/tests/data/get_group_members/get_group_members.bpmn b/tests/data/get_group_members/get_group_members.bpmn index 23f6df07..b6e849ba 100644 --- a/tests/data/get_group_members/get_group_members.bpmn +++ b/tests/data/get_group_members/get_group_members.bpmn @@ -11,13 +11,13 @@ Flow_1j4jzft Flow_10xyk22 - members_a = get_group_members("GroupA") + members_a = get_group_members("groupA") Flow_10xyk22 Flow_01xr2ac - members_b = get_group_members("GroupB") + members_b = get_group_members("groupB") diff --git a/tests/spiffworkflow_backend/helpers/base_test.py b/tests/spiffworkflow_backend/helpers/base_test.py index b7d2c5d7..a573e8a3 100644 --- a/tests/spiffworkflow_backend/helpers/base_test.py +++ b/tests/spiffworkflow_backend/helpers/base_test.py @@ -275,25 +275,6 @@ class BaseTest: user: UserModel, _redirect_url: str = "http://some/frontend/url" ) -> Dict[str, str]: """Logged_in_headers.""" - # if user is None: - # uid = 'test_user' - # user_info = {'uid': 'test_user'} - # else: - # uid = user.uid - # user_info = {'uid': user.uid} - - # query_string = user_info_to_query_string(user_info, redirect_url) - # rv = self.app.get("/v1.0/login%s" % query_string, follow_redirects=False) - # self.assertTrue(rv.status_code == 302) - # self.assertTrue(str.startswith(rv.location, redirect_url)) - # - # user_model = session.query(UserModel).filter_by(uid=uid).first() - # self.assertIsNotNone(user_model.ldap_info.display_name) - # self.assertEqual(user_model.uid, uid) - # self.assertTrue('user' in g, 'User should be in Flask globals') - # user = UserService.current_user(allow_admin_impersonate=True) - # self.assertEqual(uid, user.uid, 'Logged in user should match given user uid') - return dict(Authorization="Bearer " + user.encode_auth_token()) def get_test_data_file_contents( diff --git a/tests/spiffworkflow_backend/integration/test_process_api.py b/tests/spiffworkflow_backend/integration/test_process_api.py index 2d79b652..9a923b97 100644 --- a/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/tests/spiffworkflow_backend/integration/test_process_api.py @@ -25,7 +25,6 @@ from spiffworkflow_backend.models.process_instance_report import ( ) from spiffworkflow_backend.models.process_model import NotificationType from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema -from spiffworkflow_backend.models.task_event import TaskEventModel from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.file_system_service import FileSystemService @@ -1088,16 +1087,7 @@ class TestProcessApi(BaseTest): f"/v1.0/process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}/run", headers=self.logged_in_headers(with_super_admin_user), ) - assert response.json is not None - task_events = ( - db.session.query(TaskEventModel) - .filter(TaskEventModel.process_instance_id == process_instance_id) - .all() - ) - assert len(task_events) == 1 - task_event = task_events[0] - assert task_event.user_id == with_super_admin_user.id delete_response = client.delete( f"/v1.0/process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}", @@ -1105,40 +1095,6 @@ class TestProcessApi(BaseTest): ) assert delete_response.status_code == 200 - def test_process_instance_run_user_task_creates_task_event( - self, - app: Flask, - client: FlaskClient, - with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel, - ) -> None: - """Test_process_instance_run_user_task.""" - process_group_id = "my_process_group" - process_model_id = "user_task" - - headers = self.logged_in_headers(with_super_admin_user) - response = self.create_process_instance( - client, process_group_id, process_model_id, headers - ) - assert response.json is not None - process_instance_id = response.json["id"] - - response = client.post( - f"/v1.0/process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}/run", - headers=self.logged_in_headers(with_super_admin_user), - ) - - assert response.json is not None - task_events = ( - db.session.query(TaskEventModel) - .filter(TaskEventModel.process_instance_id == process_instance_id) - .all() - ) - assert len(task_events) == 1 - task_event = task_events[0] - assert task_event.user_id == with_super_admin_user.id - # TODO: When user tasks work, we need to add some more assertions for action, task_state, etc. - def test_task_show( self, app: Flask, From 76fac5fb6dbfc83a617232395844b6b4d3c45ac5 Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Wed, 2 Nov 2022 14:34:59 -0400 Subject: [PATCH 13/46] From the logs, allow viewing a diagram in a previous state (#15) Co-authored-by: Elizabeth Esswein --- .../{bdd1d64689db_.py => b1647eff45c9_.py} | 20 +++++- .../src/spiffworkflow_backend/api.yml | 6 ++ .../load_database_models.py | 3 + .../models/process_instance.py | 2 + .../models/spiff_logging.py | 1 + .../models/spiff_step_details.py | 23 +++++++ .../routes/process_api_blueprint.py | 17 ++++- .../services/logging_service.py | 8 +++ .../services/process_instance_processor.py | 62 ++++++++++++++++--- .../unit/test_spiff_logging.py | 1 + .../src/routes/AdminRoutes.tsx | 4 ++ .../src/routes/ProcessInstanceLogList.tsx | 11 +++- .../src/routes/ProcessInstanceShow.tsx | 14 +++-- 13 files changed, 155 insertions(+), 17 deletions(-) rename spiffworkflow-backend/migrations/versions/{bdd1d64689db_.py => b1647eff45c9_.py} (95%) create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_step_details.py diff --git a/spiffworkflow-backend/migrations/versions/bdd1d64689db_.py b/spiffworkflow-backend/migrations/versions/b1647eff45c9_.py similarity index 95% rename from spiffworkflow-backend/migrations/versions/bdd1d64689db_.py rename to spiffworkflow-backend/migrations/versions/b1647eff45c9_.py index 55566149..d6ff25e3 100644 --- a/spiffworkflow-backend/migrations/versions/bdd1d64689db_.py +++ b/spiffworkflow-backend/migrations/versions/b1647eff45c9_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: bdd1d64689db +Revision ID: b1647eff45c9 Revises: -Create Date: 2022-11-02 11:31:50.606843 +Create Date: 2022-11-02 14:25:09.992800 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'bdd1d64689db' +revision = 'b1647eff45c9' down_revision = None branch_labels = None depends_on = None @@ -106,6 +106,7 @@ def upgrade(): sa.Column('status', sa.String(length=50), nullable=True), sa.Column('bpmn_version_control_type', sa.String(length=50), nullable=True), sa.Column('bpmn_version_control_identifier', sa.String(length=255), nullable=True), + sa.Column('spiff_step', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['process_initiator_id'], ['user.id'], ), sa.PrimaryKeyConstraint('id') ) @@ -229,10 +230,22 @@ def upgrade(): sa.Column('timestamp', sa.DECIMAL(precision=17, scale=6), nullable=False), sa.Column('message', sa.String(length=255), nullable=True), sa.Column('current_user_id', sa.Integer(), nullable=True), + sa.Column('spiff_step', sa.Integer(), nullable=False), sa.ForeignKeyConstraint(['current_user_id'], ['user.id'], ), sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), sa.PrimaryKeyConstraint('id') ) + op.create_table('spiff_step_details', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('process_instance_id', sa.Integer(), nullable=False), + sa.Column('spiff_step', sa.Integer(), nullable=False), + sa.Column('task_json', sa.JSON(), nullable=False), + sa.Column('timestamp', sa.DECIMAL(precision=17, scale=6), nullable=False), + sa.Column('completed_by_user_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['completed_by_user_id'], ['user.id'], ), + sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), + sa.PrimaryKeyConstraint('id') + ) op.create_table('active_task_user', sa.Column('id', sa.Integer(), nullable=False), sa.Column('active_task_id', sa.Integer(), nullable=False), @@ -266,6 +279,7 @@ def downgrade(): op.drop_index(op.f('ix_active_task_user_user_id'), table_name='active_task_user') op.drop_index(op.f('ix_active_task_user_active_task_id'), table_name='active_task_user') op.drop_table('active_task_user') + op.drop_table('spiff_step_details') op.drop_table('spiff_logging') op.drop_table('permission_assignment') op.drop_table('message_instance') diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index 614d4f26..0f0a49c2 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -1000,6 +1000,12 @@ paths: description: If true, this wil return all tasks associated with the process instance and not just user tasks. schema: type: boolean + - name: spiff_step + in: query + required: false + description: If set will return the tasks as they were during a specific step of execution. + schema: + type: integer get: tags: - Process Instances diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py b/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py index 7283b19b..14dcac0d 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py @@ -46,6 +46,9 @@ from spiffworkflow_backend.models.process_instance_report import ( from spiffworkflow_backend.models.refresh_token import RefreshTokenModel # noqa: F401 from spiffworkflow_backend.models.secret_model import SecretModel # noqa: F401 from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel # noqa: F401 +from spiffworkflow_backend.models.spiff_step_details import ( + SpiffStepDetailsModel, +) # noqa: F401 from spiffworkflow_backend.models.user import UserModel # noqa: F401 from spiffworkflow_backend.models.group import GroupModel # noqa: F401 diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py index 1c2098e9..0e4112d6 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py @@ -81,6 +81,7 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): spiff_logs = relationship("SpiffLoggingModel", cascade="delete") # type: ignore message_instances = relationship("MessageInstanceModel", cascade="delete") # type: ignore message_correlations = relationship("MessageCorrelationModel", cascade="delete") # type: ignore + spiff_step_details = relationship("SpiffStepDetailsModel", cascade="delete") # type: ignore bpmn_json: str | None = deferred(db.Column(db.JSON)) # type: ignore start_in_seconds: int | None = db.Column(db.Integer) @@ -92,6 +93,7 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): bpmn_xml_file_contents: bytes | None = None bpmn_version_control_type: str = db.Column(db.String(50)) bpmn_version_control_identifier: str = db.Column(db.String(255)) + spiff_step: int = db.Column(db.Integer) @property def serialized(self) -> dict[str, Any]: diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_logging.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_logging.py index a655ec51..58f13cd4 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_logging.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_logging.py @@ -25,3 +25,4 @@ class SpiffLoggingModel(SpiffworkflowBaseDBModel): timestamp: float = db.Column(db.DECIMAL(17, 6), nullable=False) message: Optional[str] = db.Column(db.String(255), nullable=True) current_user_id: int = db.Column(ForeignKey(UserModel.id), nullable=True) + spiff_step: int = db.Column(db.Integer, nullable=False) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_step_details.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_step_details.py new file mode 100644 index 00000000..1706c2e9 --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_step_details.py @@ -0,0 +1,23 @@ +"""Spiff_step_details.""" +from dataclasses import dataclass + +from flask_bpmn.models.db import db +from flask_bpmn.models.db import SpiffworkflowBaseDBModel +from sqlalchemy import ForeignKey +from sqlalchemy.orm import deferred + +from spiffworkflow_backend.models.process_instance import ProcessInstanceModel +from spiffworkflow_backend.models.user import UserModel + + +@dataclass +class SpiffStepDetailsModel(SpiffworkflowBaseDBModel): + """SpiffStepDetailsModel.""" + + __tablename__ = "spiff_step_details" + id: int = db.Column(db.Integer, primary_key=True) + process_instance_id: int = db.Column(ForeignKey(ProcessInstanceModel.id), nullable=False) # type: ignore + spiff_step: int = db.Column(db.Integer, nullable=False) + task_json: str | None = deferred(db.Column(db.JSON, nullable=False)) # type: ignore + timestamp: float = db.Column(db.DECIMAL(17, 6), nullable=False) + completed_by_user_id: int = db.Column(ForeignKey(UserModel.id), nullable=True) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index 0523ad25..41cd9d99 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -56,6 +56,7 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema from spiffworkflow_backend.models.secret_model import SecretModel from spiffworkflow_backend.models.secret_model import SecretModelSchema from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel +from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.routes.user import verify_token from spiffworkflow_backend.services.authorization_service import AuthorizationService @@ -954,10 +955,23 @@ def task_list_my_tasks(page: int = 1, per_page: int = 100) -> flask.wrappers.Res def process_instance_task_list( - process_instance_id: int, all_tasks: bool = False + process_instance_id: int, all_tasks: bool = False, spiff_step: int = 0 ) -> flask.wrappers.Response: """Process_instance_task_list.""" process_instance = find_process_instance_by_id_or_raise(process_instance_id) + + if spiff_step > 0: + step_detail = ( + db.session.query(SpiffStepDetailsModel) + .filter( + SpiffStepDetailsModel.process_instance_id == process_instance.id, + SpiffStepDetailsModel.spiff_step == spiff_step, + ) + .first() + ) + if step_detail is not None: + process_instance.bpmn_json = json.dumps(step_detail.task_json) + processor = ProcessInstanceProcessor(process_instance) spiff_tasks = None @@ -1233,6 +1247,7 @@ def script_unit_test_run( """Script_unit_test_run.""" # FIXME: We should probably clear this somewhere else but this works current_app.config["THREAD_LOCAL_DATA"].process_instance_id = None + current_app.config["THREAD_LOCAL_DATA"].spiff_step = None python_script = _get_required_parameter_or_raise("python_script", body) input_json = _get_required_parameter_or_raise("input_json", body) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py index ce30e8b9..13f66e00 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py @@ -108,6 +108,8 @@ class SpiffFilter(logging.Filter): if hasattr(tld, "process_instance_id"): process_instance_id = tld.process_instance_id setattr(record, "process_instance_id", process_instance_id) # noqa: B010 + if hasattr(tld, "spiff_step"): + setattr(record, "spiff_step", tld.spiff_step) # noqa: 8010 if hasattr(g, "user") and g.user: setattr(record, "current_user_id", g.user.id) # noqa: B010 return True @@ -204,6 +206,11 @@ class DBHandler(logging.Handler): timestamp = record.created message = record.msg if hasattr(record, "msg") else None current_user_id = record.current_user_id if hasattr(record, "current_user_id") else None # type: ignore + spiff_step = ( + record.spiff_step # type: ignore + if hasattr(record, "spiff_step") and record.spiff_step is not None # type: ignore + else 1 + ) spiff_log = SpiffLoggingModel( process_instance_id=record.process_instance_id, # type: ignore bpmn_process_identifier=bpmn_process_identifier, @@ -214,6 +221,7 @@ class DBHandler(logging.Handler): message=message, timestamp=timestamp, current_user_id=current_user_id, + spiff_step=spiff_step, ) db.session.add(spiff_log) db.session.commit() diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index fecde1b9..aa94704c 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -79,6 +79,7 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.script_attributes_context import ( ScriptAttributesContext, ) +from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user import UserModelSchema from spiffworkflow_backend.scripts.script import Script @@ -276,9 +277,9 @@ class ProcessInstanceProcessor: self, process_instance_model: ProcessInstanceModel, validate_only: bool = False ) -> None: """Create a Workflow Processor based on the serialized information available in the process_instance model.""" - current_app.config[ - "THREAD_LOCAL_DATA" - ].process_instance_id = process_instance_model.id + tld = current_app.config["THREAD_LOCAL_DATA"] + tld.process_instance_id = process_instance_model.id + tld.spiff_step = process_instance_model.spiff_step # we want this to be the fully qualified path to the process model including all group subcomponents current_app.config["THREAD_LOCAL_DATA"].process_model_identifier = ( @@ -411,10 +412,8 @@ class ProcessInstanceProcessor: bpmn_process_spec, subprocesses ) - def add_user_info_to_process_instance( - self, bpmn_process_instance: BpmnWorkflow - ) -> None: - """Add_user_info_to_process_instance.""" + def current_user(self) -> Any: + """Current_user.""" current_user = None if UserService.has_user(): current_user = UserService.current_user() @@ -425,6 +424,14 @@ class ProcessInstanceProcessor: elif self.process_instance_model.process_initiator_id: current_user = self.process_instance_model.process_initiator + return current_user + + def add_user_info_to_process_instance( + self, bpmn_process_instance: BpmnWorkflow + ) -> None: + """Add_user_info_to_process_instance.""" + current_user = self.current_user() + if current_user: current_user_data = UserModelSchema().dump(current_user) tasks = bpmn_process_instance.get_tasks(TaskState.READY) @@ -542,9 +549,32 @@ class ProcessInstanceProcessor: "lane_assignment_id": lane_assignment_id, } + def save_spiff_step_details(self, bpmn_json: Optional[str]) -> None: + """SaveSpiffStepDetails.""" + if bpmn_json is None: + return + wf_json = json.loads(bpmn_json) + task_json = "{}" + if "tasks" in wf_json: + task_json = json.dumps(wf_json["tasks"]) + + # TODO want to just save the tasks, something wasn't immediately working + # so after the flow works with the full wf_json revisit this + task_json = wf_json + details_model = SpiffStepDetailsModel( + process_instance_id=self.process_instance_model.id, + spiff_step=self.process_instance_model.spiff_step or 1, + task_json=task_json, + timestamp=round(time.time()), + completed_by_user_id=self.current_user().id, + ) + db.session.add(details_model) + db.session.commit() + def save(self) -> None: """Saves the current state of this processor to the database.""" self.process_instance_model.bpmn_json = self.serialize() + complete_states = [TaskState.CANCELLED, TaskState.COMPLETED] user_tasks = list(self.get_all_user_tasks()) self.process_instance_model.status = self.get_status().value @@ -930,8 +960,19 @@ class ProcessInstanceProcessor: db.session.commit() + def increment_spiff_step(self) -> None: + """Spiff_step++.""" + spiff_step = self.process_instance_model.spiff_step or 0 + spiff_step += 1 + self.process_instance_model.spiff_step = spiff_step + current_app.config["THREAD_LOCAL_DATA"].spiff_step = spiff_step + db.session.add(self.process_instance_model) + db.session.commit() + def do_engine_steps(self, exit_at: None = None, save: bool = False) -> None: """Do_engine_steps.""" + self.increment_spiff_step() + try: self.bpmn_process_instance.refresh_waiting_tasks() self.bpmn_process_instance.do_engine_steps(exit_at=exit_at) @@ -944,6 +985,10 @@ class ProcessInstanceProcessor: finally: if save: self.save() + bpmn_json = self.process_instance_model.bpmn_json + else: + bpmn_json = self.serialize() + self.save_spiff_step_details(bpmn_json) def cancel_notify(self) -> None: """Cancel_notify.""" @@ -1054,7 +1099,10 @@ class ProcessInstanceProcessor: def complete_task(self, task: SpiffTask) -> None: """Complete_task.""" + self.increment_spiff_step() self.bpmn_process_instance.complete_task_from_id(task.id) + bpmn_json = self.serialize() + self.save_spiff_step_details(bpmn_json) def get_data(self) -> dict[str, Any]: """Get_data.""" diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spiff_logging.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spiff_logging.py index c4a5984f..d8680b71 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spiff_logging.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spiff_logging.py @@ -36,6 +36,7 @@ class TestSpiffLogging(BaseTest): bpmn_task_identifier=bpmn_task_identifier, message=message, timestamp=timestamp, + spiff_step=1, ) assert spiff_log.timestamp == timestamp diff --git a/spiffworkflow-frontend/src/routes/AdminRoutes.tsx b/spiffworkflow-frontend/src/routes/AdminRoutes.tsx index c3e39e16..776a5f34 100644 --- a/spiffworkflow-frontend/src/routes/AdminRoutes.tsx +++ b/spiffworkflow-frontend/src/routes/AdminRoutes.tsx @@ -76,6 +76,10 @@ export default function AdminRoutes() { path="process-models/:process_group_id/:process_model_id/process-instances/:process_instance_id" element={} /> + } + /> } diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx index f2d297f0..83e56ac5 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; import { Table } from 'react-bootstrap'; -import { useParams, useSearchParams } from 'react-router-dom'; +import { useParams, useSearchParams, Link } from 'react-router-dom'; import PaginationForTable from '../components/PaginationForTable'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import { @@ -39,7 +39,14 @@ export default function ProcessInstanceLogList() { {rowToUse.bpmn_task_name} {rowToUse.bpmn_task_type} {rowToUse.username} - {convertSecondsToFormattedDate(rowToUse.timestamp)} + + + {convertSecondsToFormattedDate(rowToUse.timestamp)} + + ); }); diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index f5336df8..010a7f48 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -32,10 +32,16 @@ export default function ProcessInstanceShow() { path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/${params.process_instance_id}`, successCallback: setProcessInstance, }); - HttpService.makeCallToBackend({ - path: `/process-instance/${params.process_instance_id}/tasks?all_tasks=true`, - successCallback: setTasks, - }); + if (typeof params.spiff_step === 'undefined') + HttpService.makeCallToBackend({ + path: `/process-instance/${params.process_instance_id}/tasks?all_tasks=true`, + successCallback: setTasks, + }); + else + HttpService.makeCallToBackend({ + path: `/process-instance/${params.process_instance_id}/tasks?all_tasks=true&spiff_step=${params.spiff_step}`, + successCallback: setTasks, + }); }, [params]); const deleteProcessInstance = () => { From 5fc23bdb07444bd7252119aa27723f7b66e385ee Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 2 Nov 2022 14:54:02 -0400 Subject: [PATCH 14/46] finished process instance list filter w/ burnettk --- spiffworkflow-frontend/package-lock.json | 1 + spiffworkflow-frontend/package.json | 1 + .../src/routes/HomePage.tsx | 23 ++++++--- .../src/routes/ProcessInstanceList.tsx | 49 ++++++++++++++----- 4 files changed, 55 insertions(+), 19 deletions(-) diff --git a/spiffworkflow-frontend/package-lock.json b/spiffworkflow-frontend/package-lock.json index faba49ba..a72fc406 100644 --- a/spiffworkflow-frontend/package-lock.json +++ b/spiffworkflow-frontend/package-lock.json @@ -11,6 +11,7 @@ "@babel/core": "^7.18.10", "@babel/plugin-transform-react-jsx": "^7.18.6", "@babel/preset-react": "^7.18.6", + "@carbon/icons-react": "^11.10.0", "@carbon/react": "^1.16.0", "@carbon/styles": "^1.16.0", "@ginkgo-bioworks/react-json-schema-form-builder": "^2.9.0", diff --git a/spiffworkflow-frontend/package.json b/spiffworkflow-frontend/package.json index c3364a12..c9127d7c 100644 --- a/spiffworkflow-frontend/package.json +++ b/spiffworkflow-frontend/package.json @@ -6,6 +6,7 @@ "@babel/core": "^7.18.10", "@babel/plugin-transform-react-jsx": "^7.18.6", "@babel/preset-react": "^7.18.6", + "@carbon/icons-react": "^11.10.0", "@carbon/react": "^1.16.0", "@carbon/styles": "^1.16.0", "@ginkgo-bioworks/react-json-schema-form-builder": "^2.9.0", diff --git a/spiffworkflow-frontend/src/routes/HomePage.tsx b/spiffworkflow-frontend/src/routes/HomePage.tsx index a66ded5c..2cfc0078 100644 --- a/spiffworkflow-frontend/src/routes/HomePage.tsx +++ b/spiffworkflow-frontend/src/routes/HomePage.tsx @@ -5,14 +5,14 @@ import { Link, useSearchParams } from 'react-router-dom'; import PaginationForTable from '../components/PaginationForTable'; import { getPageInfoFromSearchParams } from '../helpers'; import HttpService from '../services/HttpService'; -import { RecentProcessModel } from '../interfaces'; +import { PaginationObject, RecentProcessModel } from '../interfaces'; const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5; export default function HomePage() { const [searchParams] = useSearchParams(); const [tasks, setTasks] = useState([]); - const [pagination, setPagination] = useState(null); + const [pagination, setPagination] = useState(null); useEffect(() => { const { page, perPage } = getPageInfoFromSearchParams( @@ -126,10 +126,10 @@ export default function HomePage() { ); }; - const relevantProcessModelSection = - recentProcessModels.length > 0 && buildRecentProcessModelSection(); - - if (pagination) { + const tasksWaitingForMeComponent = () => { + if (pagination && pagination.total < 1) { + return null; + } const { page, perPage } = getPageInfoFromSearchParams( searchParams, PER_PAGE_FOR_TASKS_ON_HOME_PAGE @@ -145,6 +145,17 @@ export default function HomePage() { tableToDisplay={buildTable()} path="/tasks" /> + + ); + }; + + const relevantProcessModelSection = + recentProcessModels.length > 0 && buildRecentProcessModelSection(); + + if (pagination) { + return ( + <> + {tasksWaitingForMeComponent()} {relevantProcessModelSection} ); diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx index 63ee7a2f..5be2c3c7 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx @@ -6,6 +6,8 @@ import { useSearchParams, } from 'react-router-dom'; +// @ts-ignore +import { Filter } from '@carbon/icons-react'; import { Button, ButtonSet, @@ -57,6 +59,7 @@ export default function ProcessInstanceList() { const [startTo, setStartTo] = useState(''); const [endFrom, setEndFrom] = useState(''); const [endTo, setEndTo] = useState(''); + const [showFilterOptions, setShowFilterOptions] = useState(false); const setErrorMessage = (useContext as any)(ErrorContext)[1]; @@ -352,7 +355,9 @@ export default function ProcessInstanceList() { }; const filterOptions = () => { - const { page, perPage } = getPageInfoFromSearchParams(searchParams); + if (!showFilterOptions) { + return null; + } return ( <> @@ -394,18 +399,6 @@ export default function ProcessInstanceList() { - - - - - ); }; @@ -480,12 +473,42 @@ export default function ProcessInstanceList() { ); }; + const toggleShowFilterOptions = () => { + setShowFilterOptions(!showFilterOptions); + }; + if (pagination) { const { page, perPage } = getPageInfoFromSearchParams(searchParams); return ( <> {processInstanceTitleElement()} + + + + - // - // - // ); - // } - // return null; - // }; - // - // const logoutLink = () => { - // if (UserService.isLoggedIn()) { - // return ( - // - // - // Signed in as: {UserService.getUsername()} - // - // - // - // - // - // ); - // } - // return null; - // }; + const handleLogout = () => { + UserService.doLogout(); + }; + + const handleLogin = () => { + UserService.doLogin(); + }; const location = useLocation(); const [activeKey, setActiveKey] = useState(''); @@ -87,6 +53,24 @@ export default function NavigationBar() { return activeKey === menuItemPath; }; + const loginAndLogoutAction = () => { + if (UserService.isLoggedIn()) { + return ( + <> + {UserService.getUsername()} + + + + + ); + } + return ( + + + + ); + }; + if (activeKey) { return (
    @@ -112,6 +96,7 @@ export default function NavigationBar() { Process Instances + {loginAndLogoutAction()}
    From d25ac513960db4e34db4b0842444b20f9d110b9b Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 2 Nov 2022 17:16:41 -0400 Subject: [PATCH 17/46] added search bar on process groups page w/ burnettk --- spiffworkflow-frontend/src/App.tsx | 2 - .../src/components/NavigationBar.tsx | 2 - .../src/components/PaginationForTable.tsx | 5 +- .../src/components/ProcessModelSearch.tsx | 45 ++++++++++++++++ spiffworkflow-frontend/src/index.css | 4 +- spiffworkflow-frontend/src/interfaces.ts | 4 ++ .../src/routes/ProcessGroupList.tsx | 40 +++++---------- .../src/routes/ProcessInstanceList.tsx | 51 ++++--------------- 8 files changed, 75 insertions(+), 78 deletions(-) create mode 100644 spiffworkflow-frontend/src/components/ProcessModelSearch.tsx diff --git a/spiffworkflow-frontend/src/App.tsx b/spiffworkflow-frontend/src/App.tsx index 84b99483..186ac513 100644 --- a/spiffworkflow-frontend/src/App.tsx +++ b/spiffworkflow-frontend/src/App.tsx @@ -10,7 +10,6 @@ import HomePage from './routes/HomePage'; import TaskShow from './routes/TaskShow'; import ErrorBoundary from './components/ErrorBoundary'; import AdminRoutes from './routes/AdminRoutes'; -import SubNavigation from './components/SubNavigation'; import { ErrorForDisplay } from './interfaces'; export default function App() { @@ -46,7 +45,6 @@ export default function App() { ); } - // return ( diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index cb3b5271..a51ee779 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -1,11 +1,9 @@ import { Header, Theme, - Button, HeaderName, HeaderNavigation, HeaderMenuItem, - HeaderMenu, HeaderGlobalAction, HeaderGlobalBar, // @ts-ignore diff --git a/spiffworkflow-frontend/src/components/PaginationForTable.tsx b/spiffworkflow-frontend/src/components/PaginationForTable.tsx index 44edff63..d9d94b84 100644 --- a/spiffworkflow-frontend/src/components/PaginationForTable.tsx +++ b/spiffworkflow-frontend/src/components/PaginationForTable.tsx @@ -1,8 +1,7 @@ -import React from 'react'; -import { Link, useNavigate } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; // @ts-ignore -import { Dropdown, Stack, Pagination } from '@carbon/react'; +import { Pagination } from '@carbon/react'; import { PaginationObject } from '../interfaces'; export const DEFAULT_PER_PAGE = 50; diff --git a/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx b/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx new file mode 100644 index 00000000..679831a5 --- /dev/null +++ b/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx @@ -0,0 +1,45 @@ +import { + ComboBox, + // @ts-ignore +} from '@carbon/react'; +import { truncateString } from '../helpers'; +import { ProcessModel } from '../interfaces'; + +type OwnProps = { + onChange: (..._args: any[]) => any; + processModels: ProcessModel[]; + selectedItem?: ProcessModel | null; +}; + +export default function ProcessModelSearch({ + processModels, + selectedItem, + onChange, +}: OwnProps) { + const shouldFilterProcessModel = (options: any) => { + const processModel: ProcessModel = options.item; + const { inputValue } = options; + return `${processModel.process_group_id}/${processModel.id} (${processModel.display_name})`.includes( + inputValue + ); + }; + return ( + { + if (processModel) { + return `${processModel.process_group_id}/${ + processModel.id + } (${truncateString(processModel.display_name, 20)})`; + } + return null; + }} + shouldFilterItem={shouldFilterProcessModel} + placeholder="Choose a process model" + titleText="Process model" + selectedItem={selectedItem} + /> + ); +} diff --git a/spiffworkflow-frontend/src/index.css b/spiffworkflow-frontend/src/index.css index 763200df..c51398c4 100644 --- a/spiffworkflow-frontend/src/index.css +++ b/spiffworkflow-frontend/src/index.css @@ -20,8 +20,8 @@ span.bjs-crumb { } .app-logo { - height: 90%; - width: 90%; + height: 85%; + width: 85%; margin-top: 1em; margin-bottom: 1em; } diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts index 16e1491b..08c8e4fa 100644 --- a/spiffworkflow-frontend/src/interfaces.ts +++ b/spiffworkflow-frontend/src/interfaces.ts @@ -47,3 +47,7 @@ export interface PaginationObject { total: number; pages: number; } + +export interface CarbonComboBoxSelection { + selectedItem: ProcessModel; +} diff --git a/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx b/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx index 29dc3882..f9881def 100644 --- a/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx @@ -9,7 +9,8 @@ import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import PaginationForTable from '../components/PaginationForTable'; import HttpService from '../services/HttpService'; import { getPageInfoFromSearchParams } from '../helpers'; -import { ProcessModel } from '../interfaces'; +import { CarbonComboBoxSelection, ProcessModel } from '../interfaces'; +import ProcessModelSearch from '../components/ProcessModelSearch'; // Example process group json // {'process_group_id': 'sure', 'display_name': 'Test Workflows', 'id': 'test_process_group'} @@ -19,8 +20,9 @@ export default function ProcessGroupList() { const [processGroups, setProcessGroups] = useState([]); const [pagination, setPagination] = useState(null); - const [processModeleSelectionOptions, setProcessModelSelectionOptions] = - useState([]); + const [processModelAvailableItems, setProcessModelAvailableItems] = useState( + [] + ); useEffect(() => { const setProcessGroupsFromResult = (result: any) => { @@ -33,7 +35,7 @@ export default function ProcessGroupList() { Object.assign(item, { label }); return item; }); - setProcessModelSelectionOptions(selectionArray); + setProcessModelAvailableItems(selectionArray); }; const { page, perPage } = getPageInfoFromSearchParams(searchParams); @@ -99,35 +101,17 @@ export default function ProcessGroupList() { }; const processModelSearchArea = () => { - const processModelSearchOnChange = (selected: Option[]) => { - const processModel = selected[0] as ProcessModel; + const processModelSearchOnChange = (selection: CarbonComboBoxSelection) => { + const processModel = selection.selectedItem; navigate( `/admin/process-models/${processModel.process_group_id}/${processModel.id}` ); }; return ( -
    -

    Search

    - - - - Process Model:{' '} - - - - -
    + ); }; diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx index 5be2c3c7..10ac2c0b 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx @@ -14,25 +14,17 @@ import { DatePicker, DatePickerInput, Table, - Stack, - Form, - ComboBox, Grid, Column, - FlexGrid, - Row, MultiSelect, // @ts-ignore } from '@carbon/react'; -import { InputGroup } from 'react-bootstrap'; -import { Typeahead } from 'react-bootstrap-typeahead'; import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config'; import { convertDateStringToSeconds, convertSecondsToFormattedDate, getPageInfoFromSearchParams, getProcessModelFullIdentifierFromSearchParams, - truncateString, } from '../helpers'; import PaginationForTable from '../components/PaginationForTable'; @@ -44,6 +36,7 @@ import HttpService from '../services/HttpService'; import 'react-bootstrap-typeahead/css/Typeahead.css'; import 'react-bootstrap-typeahead/css/Typeahead.bs5.css'; import { PaginationObject, ProcessModel } from '../interfaces'; +import ProcessModelSearch from '../components/ProcessModelSearch'; export default function ProcessInstanceList() { const params = useParams(); @@ -294,38 +287,6 @@ export default function ProcessInstanceList() { return queryParamString; }; - const shouldFilterProcessModel = (options: any) => { - const processModel: ProcessModel = options.item; - const { inputValue } = options; - return `${processModel.process_group_id}/${processModel.id} (${processModel.display_name})`.includes( - inputValue - ); - }; - - const processModelSearch = () => { - return ( - - setProcessModelSelection(selection.selectedItem) - } - id="process-model-select" - items={processModelAvailableItems} - itemToString={(processModel: ProcessModel) => { - if (processModel) { - return `${processModel.process_group_id}/${ - processModel.id - } (${truncateString(processModel.display_name, 20)})`; - } - return null; - }} - shouldFilterItem={shouldFilterProcessModel} - placeholder="Choose a process model" - titleText="Process model" - selectedItem={processModelSelection} - /> - ); - }; - const processStatusSearch = () => { return ( - {processModelSearch()} + + + setProcessModelSelection(selection.selectedItem) + } + processModels={processModelAvailableItems} + selectedItem={processModelSelection} + /> + {processStatusSearch()} From 04172f2c10dce36a9492f82027958e5c05a11e40 Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Thu, 3 Nov 2022 11:11:32 -0400 Subject: [PATCH 18/46] Fix FutureWarning in SpiffWorkflow (#16) --- SpiffWorkflow/SpiffWorkflow/bpmn/parser/node_parser.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/parser/node_parser.py b/SpiffWorkflow/SpiffWorkflow/bpmn/parser/node_parser.py index b76ccfbb..0b1c8ed4 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/parser/node_parser.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/parser/node_parser.py @@ -35,7 +35,8 @@ class NodeParser: return expression.text if expression is not None else None def parse_documentation(self, sequence_flow=None): - documentation_node = first(self._xpath(sequence_flow or self.node, './/bpmn:documentation')) + node = sequence_flow if sequence_flow is not None else self.node + documentation_node = first(self._xpath(node, './/bpmn:documentation')) return None if documentation_node is None else documentation_node.text def parse_incoming_data_references(self): From 55cb9f4fd07b3a72af1cb029655eaae1bc266525 Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Thu, 3 Nov 2022 11:12:00 -0400 Subject: [PATCH 19/46] Clear the remaining __init__.py imports in SpiffWorkflow (#14) --- .../SpiffWorkflow/bpmn/parser/BpmnParser.py | 6 +- .../SpiffWorkflow/bpmn/parser/TaskParser.py | 3 +- .../bpmn/parser/event_parsers.py | 2 +- .../serializer/CompactWorkflowSerializer.py | 2 +- .../SpiffWorkflow/bpmn/serializer/__init__.py | 3 - .../bpmn/serializer/bpmn_converters.py | 6 +- .../bpmn/serializer/task_spec_converters.py | 6 +- .../SpiffWorkflow/bpmn/specs/BpmnSpecMixin.py | 2 +- .../bpmn/specs/ExclusiveGateway.py | 2 +- .../SpiffWorkflow/bpmn/specs/ManualTask.py | 4 +- .../bpmn/specs/MultiInstanceTask.py | 2 +- .../SpiffWorkflow/bpmn/specs/NoneTask.py | 2 +- .../bpmn/specs/SubWorkflowTask.py | 2 +- .../bpmn/specs/events/__init__.py | 5 -- .../camunda/parser/CamundaParser.py | 4 +- .../camunda/serializer/__init__.py | 2 - .../serializer/task_spec_converters.py | 4 +- .../SpiffWorkflow/dmn/serializer/__init__.py | 1 - .../dmn/specs/BusinessRuleTask.py | 2 +- .../SpiffWorkflow/serializer/base.py | 65 +++++++++++++++++++ .../SpiffWorkflow/serializer/dict.py | 29 +++++++-- .../SpiffWorkflow/serializer/prettyxml.py | 26 +++----- SpiffWorkflow/SpiffWorkflow/serializer/xml.py | 46 +++++++------ .../SpiffWorkflow/signavio/parser/__init__.py | 1 - .../SpiffWorkflow/specs/SubWorkflow.py | 4 +- .../SpiffWorkflow/specs/ThreadMerge.py | 2 +- .../SpiffWorkflow/specs/WorkflowSpec.py | 4 +- SpiffWorkflow/SpiffWorkflow/specs/__init__.py | 31 --------- .../SpiffWorkflow/spiff/parser/__init__.py | 1 - .../SpiffWorkflow/spiff/parser/process.py | 11 +++- .../spiff/serializer/__init__.py | 4 -- .../spiff/serializer/task_spec_converters.py | 13 +++- .../SpiffWorkflow/spiff/specs/__init__.py | 6 -- .../spiff/specs/events/__init__.py | 1 - SpiffWorkflow/SpiffWorkflow/workflow.py | 4 +- SpiffWorkflow/doc/non-bpmn/tutorial/start.py | 2 +- .../tests/SpiffWorkflow/PatternTest.py | 2 +- .../SpiffWorkflow/PersistSmallWorkflowTest.py | 4 +- SpiffWorkflow/tests/SpiffWorkflow/TaskTest.py | 3 +- .../tests/SpiffWorkflow/WorkflowTest.py | 4 +- .../tests/SpiffWorkflow/bpmn/ApprovalsTest.py | 2 +- .../SpiffWorkflow/bpmn/BpmnLoaderForTests.py | 2 +- .../bpmn/BpmnWorkflowSerializerTest.py | 2 +- .../bpmn/BpmnWorkflowTestCase.py | 2 +- .../bpmn/ProcessDependencyTest.py | 2 +- .../bpmn/events/MultipleEventsTest.py | 2 +- .../events/TimerDurationBoundaryOnTaskTest.py | 2 +- .../SpiffWorkflow/camunda/BaseTestCase.py | 6 +- .../camunda/specs/UserTaskSpecTest.py | 3 +- .../SpiffWorkflow/data/spiff/workflow1.py | 7 +- .../SpiffWorkflow/serializer/baseTest.py | 2 +- .../tests/SpiffWorkflow/specs/CeleryTest.py | 3 +- .../tests/SpiffWorkflow/specs/ExecuteTest.py | 2 +- .../tests/SpiffWorkflow/specs/JoinTest.py | 2 +- .../tests/SpiffWorkflow/specs/MergeTest.py | 4 +- .../SpiffWorkflow/specs/SubWorkflowTest.py | 2 +- .../tests/SpiffWorkflow/specs/TaskSpecTest.py | 6 +- .../SpiffWorkflow/specs/TransformTest.py | 3 +- .../SpiffWorkflow/specs/WorkflowSpecTest.py | 3 +- .../tests/SpiffWorkflow/spiff/BaseTestCase.py | 6 +- .../services/process_instance_processor.py | 46 +++++++------ 61 files changed, 253 insertions(+), 179 deletions(-) diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/parser/BpmnParser.py b/SpiffWorkflow/SpiffWorkflow/bpmn/parser/BpmnParser.py index c6ed81bb..e6783e05 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/parser/BpmnParser.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/parser/BpmnParser.py @@ -27,8 +27,10 @@ from SpiffWorkflow.bpmn.specs.events.event_definitions import NoneEventDefinitio from .ValidationException import ValidationException from ..specs.BpmnProcessSpec import BpmnProcessSpec -from ..specs.events import StartEvent, EndEvent, BoundaryEvent, IntermediateCatchEvent, IntermediateThrowEvent -from ..specs.events import SendTask, ReceiveTask +from ..specs.events.EndEvent import EndEvent +from ..specs.events.StartEvent import StartEvent +from ..specs.events.IntermediateEvent import BoundaryEvent, IntermediateCatchEvent, IntermediateThrowEvent +from ..specs.events.IntermediateEvent import SendTask, ReceiveTask from ..specs.SubWorkflowTask import CallActivity, SubWorkflowTask, TransactionSubprocess from ..specs.ExclusiveGateway import ExclusiveGateway from ..specs.InclusiveGateway import InclusiveGateway diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/parser/TaskParser.py b/SpiffWorkflow/SpiffWorkflow/bpmn/parser/TaskParser.py index afbd1972..5291b162 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/parser/TaskParser.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/parser/TaskParser.py @@ -23,7 +23,8 @@ from .ValidationException import ValidationException from ..specs.NoneTask import NoneTask from ..specs.ScriptTask import ScriptTask from ..specs.UserTask import UserTask -from ..specs.events import _BoundaryEventParent, CancelEventDefinition +from ..specs.events.IntermediateEvent import _BoundaryEventParent +from ..specs.events.event_definitions import CancelEventDefinition from ..specs.MultiInstanceTask import getDynamicMIClass from ..specs.SubWorkflowTask import CallActivity, TransactionSubprocess, SubWorkflowTask from ..specs.ExclusiveGateway import ExclusiveGateway diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/parser/event_parsers.py b/SpiffWorkflow/SpiffWorkflow/bpmn/parser/event_parsers.py index d007fde8..07cef338 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/parser/event_parsers.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/parser/event_parsers.py @@ -5,7 +5,7 @@ from SpiffWorkflow.bpmn.specs.events.event_definitions import CorrelationPropert from .ValidationException import ValidationException from .TaskParser import TaskParser from .util import first, one -from ..specs.events import (TimerEventDefinition, MessageEventDefinition, +from ..specs.events.event_definitions import (TimerEventDefinition, MessageEventDefinition, ErrorEventDefinition, EscalationEventDefinition, SignalEventDefinition, CancelEventDefinition, CycleTimerEventDefinition, diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/CompactWorkflowSerializer.py b/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/CompactWorkflowSerializer.py index ac38dc6b..9ed543fb 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/CompactWorkflowSerializer.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/CompactWorkflowSerializer.py @@ -23,7 +23,7 @@ from builtins import object from collections import deque import json from ...task import TaskState -from ...specs import SubWorkflow +from ...specs.Subworkflow import SubWorkflow from ...serializer.base import Serializer from ..workflow import BpmnWorkflow diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/__init__.py b/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/__init__.py index 507b6257..9a31a407 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/__init__.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/__init__.py @@ -15,6 +15,3 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA - -from .workflow import BpmnWorkflowSerializer -from .bpmn_converters import BpmnDataConverter \ No newline at end of file diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/bpmn_converters.py b/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/bpmn_converters.py index 99666b34..1e274c21 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/bpmn_converters.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/bpmn_converters.py @@ -7,9 +7,9 @@ from SpiffWorkflow.bpmn.specs.BpmnProcessSpec import BpmnDataSpecification from .dictionary import DictionaryConverter -from ..specs.events import SignalEventDefinition, MessageEventDefinition, NoneEventDefinition -from ..specs.events import TimerEventDefinition, CycleTimerEventDefinition, TerminateEventDefinition -from ..specs.events import ErrorEventDefinition, EscalationEventDefinition, CancelEventDefinition +from ..specs.events.event_definitions import SignalEventDefinition, MessageEventDefinition, NoneEventDefinition +from ..specs.events.event_definitions import TimerEventDefinition, CycleTimerEventDefinition, TerminateEventDefinition +from ..specs.events.event_definitions import ErrorEventDefinition, EscalationEventDefinition, CancelEventDefinition from ..specs.events.event_definitions import CorrelationProperty, NamedEventDefinition from ..specs.BpmnSpecMixin import BpmnSpecMixin, SequenceFlow diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/task_spec_converters.py b/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/task_spec_converters.py index e2af6b32..852ebe4d 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/task_spec_converters.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/serializer/task_spec_converters.py @@ -2,7 +2,7 @@ from uuid import UUID from .bpmn_converters import BpmnTaskSpecConverter -from ...specs import StartTask +from ...specs.StartTask import StartTask from ...specs.Simple import Simple from ...specs.LoopResetTask import LoopResetTask @@ -19,7 +19,9 @@ from ..specs.ExclusiveGateway import ExclusiveGateway from ..specs.InclusiveGateway import InclusiveGateway from ..specs.ParallelGateway import ParallelGateway -from ..specs.events import StartEvent, EndEvent, BoundaryEvent, IntermediateCatchEvent, IntermediateThrowEvent +from ..specs.events.StartEvent import StartEvent +from ..specs.events.EndEvent import EndEvent +from ..specs.events.IntermediateEvent import BoundaryEvent, IntermediateCatchEvent, IntermediateThrowEvent from ..specs.events.IntermediateEvent import _BoundaryEventParent, SendTask, ReceiveTask from ..workflow import BpmnWorkflow diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/specs/BpmnSpecMixin.py b/SpiffWorkflow/SpiffWorkflow/bpmn/specs/BpmnSpecMixin.py index 3b8f4b26..459a3882 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/specs/BpmnSpecMixin.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/specs/BpmnSpecMixin.py @@ -19,7 +19,7 @@ from ...task import TaskState from ...operators import Operator -from ...specs import TaskSpec +from ...specs.base import TaskSpec class _BpmnCondition(Operator): diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/specs/ExclusiveGateway.py b/SpiffWorkflow/SpiffWorkflow/bpmn/specs/ExclusiveGateway.py index 391a2932..89a1445f 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/specs/ExclusiveGateway.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/specs/ExclusiveGateway.py @@ -19,7 +19,7 @@ from ...exceptions import WorkflowException from .BpmnSpecMixin import BpmnSpecMixin -from ...specs import TaskSpec +from ...specs.base import TaskSpec from ...specs.ExclusiveChoice import ExclusiveChoice diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/specs/ManualTask.py b/SpiffWorkflow/SpiffWorkflow/bpmn/specs/ManualTask.py index a0283445..c8d4e7c2 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/specs/ManualTask.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/specs/ManualTask.py @@ -18,7 +18,7 @@ # 02110-1301 USA from ...bpmn.specs.BpmnSpecMixin import BpmnSpecMixin -from ...specs import Simple +from ...specs.Simple import Simple class ManualTask(Simple, BpmnSpecMixin): @@ -32,4 +32,4 @@ class ManualTask(Simple, BpmnSpecMixin): @property def spec_type(self): - return 'Manual Task' \ No newline at end of file + return 'Manual Task' diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/specs/MultiInstanceTask.py b/SpiffWorkflow/SpiffWorkflow/bpmn/specs/MultiInstanceTask.py index 77886d54..f0ceb3de 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/specs/MultiInstanceTask.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/specs/MultiInstanceTask.py @@ -29,7 +29,7 @@ from .ScriptTask import ScriptTask from .ExclusiveGateway import ExclusiveGateway from ...dmn.specs.BusinessRuleTask import BusinessRuleTask from ...operators import valueof, is_number -from ...specs import SubWorkflow +from ...specs.SubWorkflow import SubWorkflow from ...specs.base import TaskSpec from ...util.impl import get_class from ...task import Task, TaskState diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/specs/NoneTask.py b/SpiffWorkflow/SpiffWorkflow/bpmn/specs/NoneTask.py index 28abc80c..5c844e90 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/specs/NoneTask.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/specs/NoneTask.py @@ -16,7 +16,7 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -from ...specs import Simple +from ...specs.Simple import Simple from ...bpmn.specs.BpmnSpecMixin import BpmnSpecMixin diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/specs/SubWorkflowTask.py b/SpiffWorkflow/SpiffWorkflow/bpmn/specs/SubWorkflowTask.py index c8b4409e..6f616f1d 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/specs/SubWorkflowTask.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/specs/SubWorkflowTask.py @@ -3,7 +3,7 @@ from copy import deepcopy from SpiffWorkflow.task import TaskState from .BpmnSpecMixin import BpmnSpecMixin -from ...specs import TaskSpec +from ...specs.base import TaskSpec class SubWorkflowTask(BpmnSpecMixin): diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/specs/events/__init__.py b/SpiffWorkflow/SpiffWorkflow/bpmn/specs/events/__init__.py index b4d52665..e69de29b 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/specs/events/__init__.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/specs/events/__init__.py @@ -1,5 +0,0 @@ -from .StartEvent import StartEvent -from .EndEvent import EndEvent -from .IntermediateEvent import IntermediateCatchEvent, IntermediateThrowEvent, BoundaryEvent, _BoundaryEventParent, SendTask, ReceiveTask -from .event_definitions import (NoneEventDefinition, CancelEventDefinition, ErrorEventDefinition, EscalationEventDefinition, MessageEventDefinition, - SignalEventDefinition, TimerEventDefinition, CycleTimerEventDefinition, TerminateEventDefinition) \ No newline at end of file diff --git a/SpiffWorkflow/SpiffWorkflow/camunda/parser/CamundaParser.py b/SpiffWorkflow/SpiffWorkflow/camunda/parser/CamundaParser.py index b9d0dc26..e91be639 100644 --- a/SpiffWorkflow/SpiffWorkflow/camunda/parser/CamundaParser.py +++ b/SpiffWorkflow/SpiffWorkflow/camunda/parser/CamundaParser.py @@ -6,7 +6,9 @@ from SpiffWorkflow.dmn.parser.BpmnDmnParser import BpmnDmnParser from SpiffWorkflow.dmn.specs.BusinessRuleTask import BusinessRuleTask from SpiffWorkflow.camunda.parser.task_spec import BusinessRuleTaskParser -from SpiffWorkflow.bpmn.specs.events import EndEvent, IntermediateThrowEvent, StartEvent, IntermediateCatchEvent, BoundaryEvent +from SpiffWorkflow.bpmn.specs.events.StartEvent import StartEvent +from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent +from SpiffWorkflow.bpmn.specs.events.IntermediateEvent import IntermediateThrowEvent, IntermediateCatchEvent, BoundaryEvent from .event_parsers import CamundaStartEventParser, CamundaEndEventParser, \ CamundaIntermediateCatchEventParser, CamundaIntermediateThrowEventParser, CamundaBoundaryEventParser diff --git a/SpiffWorkflow/SpiffWorkflow/camunda/serializer/__init__.py b/SpiffWorkflow/SpiffWorkflow/camunda/serializer/__init__.py index 62e56488..e69de29b 100644 --- a/SpiffWorkflow/SpiffWorkflow/camunda/serializer/__init__.py +++ b/SpiffWorkflow/SpiffWorkflow/camunda/serializer/__init__.py @@ -1,2 +0,0 @@ -from .task_spec_converters import UserTaskConverter, StartEventConverter, EndEventConverter, \ - BoundaryEventConverter, IntermediateCatchEventConverter, IntermediateThrowEventConverter diff --git a/SpiffWorkflow/SpiffWorkflow/camunda/serializer/task_spec_converters.py b/SpiffWorkflow/SpiffWorkflow/camunda/serializer/task_spec_converters.py index bb247e33..3f997a0e 100644 --- a/SpiffWorkflow/SpiffWorkflow/camunda/serializer/task_spec_converters.py +++ b/SpiffWorkflow/SpiffWorkflow/camunda/serializer/task_spec_converters.py @@ -1,6 +1,8 @@ from functools import partial -from SpiffWorkflow.bpmn.specs.events import EndEvent, IntermediateThrowEvent, StartEvent, IntermediateCatchEvent, BoundaryEvent +from SpiffWorkflow.bpmn.specs.events.StartEvent import StartEvent +from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent +from SpiffWorkflow.bpmn.specs.events.IntermediateEvent import IntermediateThrowEvent, IntermediateCatchEvent, BoundaryEvent from ..specs.events.event_definitions import MessageEventDefinition from ...bpmn.serializer.bpmn_converters import BpmnTaskSpecConverter diff --git a/SpiffWorkflow/SpiffWorkflow/dmn/serializer/__init__.py b/SpiffWorkflow/SpiffWorkflow/dmn/serializer/__init__.py index 9f493c9d..e69de29b 100644 --- a/SpiffWorkflow/SpiffWorkflow/dmn/serializer/__init__.py +++ b/SpiffWorkflow/SpiffWorkflow/dmn/serializer/__init__.py @@ -1 +0,0 @@ -from .task_spec_converters import BusinessRuleTaskConverter \ No newline at end of file diff --git a/SpiffWorkflow/SpiffWorkflow/dmn/specs/BusinessRuleTask.py b/SpiffWorkflow/SpiffWorkflow/dmn/specs/BusinessRuleTask.py index 4a9684f2..5825c023 100644 --- a/SpiffWorkflow/SpiffWorkflow/dmn/specs/BusinessRuleTask.py +++ b/SpiffWorkflow/SpiffWorkflow/dmn/specs/BusinessRuleTask.py @@ -1,6 +1,6 @@ from SpiffWorkflow.bpmn.exceptions import WorkflowTaskExecException -from ...specs import Simple +from ...specs.Simple import Simple from ...bpmn.specs.BpmnSpecMixin import BpmnSpecMixin from ...util.deep_merge import DeepMerge diff --git a/SpiffWorkflow/SpiffWorkflow/serializer/base.py b/SpiffWorkflow/SpiffWorkflow/serializer/base.py index 28b2e60a..bbf4cc25 100644 --- a/SpiffWorkflow/SpiffWorkflow/serializer/base.py +++ b/SpiffWorkflow/SpiffWorkflow/serializer/base.py @@ -16,6 +16,71 @@ from builtins import object # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA +import re +from .. import operators +from .. import specs +from ..specs.AcquireMutex import AcquireMutex +from ..specs.Cancel import Cancel +from ..specs.CancelTask import CancelTask +from ..specs.Celery import Celery +from ..specs.Choose import Choose +from ..specs.ExclusiveChoice import ExclusiveChoice +from ..specs.Execute import Execute +from ..specs.Gate import Gate +from ..specs.Join import Join +from ..specs.Merge import Merge +from ..specs.MultiChoice import MultiChoice +from ..specs.MultiInstance import MultiInstance +from ..specs.ReleaseMutex import ReleaseMutex +from ..specs.Simple import Simple +from ..specs.StartTask import StartTask +from ..specs.SubWorkflow import SubWorkflow +from ..specs.ThreadStart import ThreadStart +from ..specs.ThreadMerge import ThreadMerge +from ..specs.ThreadSplit import ThreadSplit +from ..specs.Transform import Transform +from ..specs.Trigger import Trigger +from ..specs.WorkflowSpec import WorkflowSpec +from ..specs.LoopResetTask import LoopResetTask + +# Create a list of tag names out of the spec names. +def spec_map(): + return { + 'acquire-mutex': AcquireMutex, + 'cancel': Cancel, + 'cancel-task': CancelTask, + 'celery': Celery, + 'choose': Choose, + 'exclusive-choice': ExclusiveChoice, + 'execute': Execute, + 'gate': Gate, + 'join': Join, + 'merge': Merge, + 'multi-choice': MultiChoice, + 'multi-instance': MultiInstance, + 'release-mutex': ReleaseMutex, + 'simple': Simple, + 'start-task': StartTask, + 'sub-workflow': SubWorkflow, + 'thread-start': ThreadStart, + 'thread-merge': ThreadMerge, + 'thread-split': ThreadSplit, + 'transform': Transform, + 'trigger': Trigger, + 'workflow-spec': WorkflowSpec, + 'loop-reset-task': LoopResetTask, + 'task': Simple, + } + +def op_map(): + return { + 'equals': operators.Equal, + 'not-equals': operators.NotEqual, + 'less-than': operators.LessThan, + 'greater-than': operators.GreaterThan, + 'matches': operators.Match + } + class Serializer(object): diff --git a/SpiffWorkflow/SpiffWorkflow/serializer/dict.py b/SpiffWorkflow/SpiffWorkflow/serializer/dict.py index 5688b587..8d1e3ad4 100644 --- a/SpiffWorkflow/SpiffWorkflow/serializer/dict.py +++ b/SpiffWorkflow/SpiffWorkflow/serializer/dict.py @@ -24,11 +24,30 @@ from ..util.impl import get_class from ..task import Task from ..operators import (Attrib, PathAttrib, Equal, NotEqual, Operator, GreaterThan, LessThan, Match) -from ..specs import (Cancel, AcquireMutex, CancelTask, Celery, Choose, - ExclusiveChoice, Execute, Gate, Join, MultiChoice, - MultiInstance, ReleaseMutex, Simple, WorkflowSpec, - TaskSpec, SubWorkflow, StartTask, ThreadMerge, - ThreadSplit, ThreadStart, Merge, Trigger, LoopResetTask) +from ..specs.base import TaskSpec +from ..specs.AcquireMutex import AcquireMutex +from ..specs.Cancel import Cancel +from ..specs.CancelTask import CancelTask +from ..specs.Celery import Celery +from ..specs.Choose import Choose +from ..specs.ExclusiveChoice import ExclusiveChoice +from ..specs.Execute import Execute +from ..specs.Gate import Gate +from ..specs.Join import Join +from ..specs.Merge import Merge +from ..specs.MultiChoice import MultiChoice +from ..specs.MultiInstance import MultiInstance +from ..specs.ReleaseMutex import ReleaseMutex +from ..specs.Simple import Simple +from ..specs.StartTask import StartTask +from ..specs.SubWorkflow import SubWorkflow +from ..specs.ThreadStart import ThreadStart +from ..specs.ThreadMerge import ThreadMerge +from ..specs.ThreadSplit import ThreadSplit +from ..specs.Transform import Transform +from ..specs.Trigger import Trigger +from ..specs.WorkflowSpec import WorkflowSpec +from ..specs.LoopResetTask import LoopResetTask from .base import Serializer from .exceptions import TaskNotSupportedError, MissingSpecError import warnings diff --git a/SpiffWorkflow/SpiffWorkflow/serializer/prettyxml.py b/SpiffWorkflow/SpiffWorkflow/serializer/prettyxml.py index 25bec80e..9bd189c5 100644 --- a/SpiffWorkflow/SpiffWorkflow/serializer/prettyxml.py +++ b/SpiffWorkflow/SpiffWorkflow/serializer/prettyxml.py @@ -18,25 +18,15 @@ # 02110-1301 USA import re import xml.dom.minidom as minidom -from .. import operators, specs +from .. import operators +from ..specs.Simple import Simple +from ..specs.WorkflowSpec import WorkflowSpec from ..exceptions import StorageException -from .base import Serializer +from .base import Serializer, spec_map, op_map # Create a list of tag names out of the spec names. -_spec_map = dict() -for name in dir(specs): - if name.startswith('_'): - continue - module = specs.__dict__[name] - name = re.sub(r'(.)([A-Z])', r'\1-\2', name).lower() - _spec_map[name] = module -_spec_map['task'] = specs.Simple - -_op_map = {'equals': operators.Equal, - 'not-equals': operators.NotEqual, - 'less-than': operators.LessThan, - 'greater-than': operators.GreaterThan, - 'matches': operators.Match} +_spec_map = spec_map() +_op_map = op_map() _exc = StorageException @@ -299,9 +289,9 @@ class XmlSerializer(Serializer): _exc('%s without a name attribute' % node.nodeName) # Read all task specs and create a list of successors. - workflow_spec = specs.WorkflowSpec(name, filename) + workflow_spec = WorkflowSpec(name, filename) del workflow_spec.task_specs['Start'] - end = specs.Simple(workflow_spec, 'End'), [] + end = Simple(workflow_spec, 'End'), [] read_specs = dict(end=end) for child_node in node.childNodes: if child_node.nodeType != minidom.Node.ELEMENT_NODE: diff --git a/SpiffWorkflow/SpiffWorkflow/serializer/xml.py b/SpiffWorkflow/SpiffWorkflow/serializer/xml.py index f4b18b6c..c0d0572d 100644 --- a/SpiffWorkflow/SpiffWorkflow/serializer/xml.py +++ b/SpiffWorkflow/SpiffWorkflow/serializer/xml.py @@ -24,29 +24,35 @@ from .. import specs, operators from ..task import Task, TaskStateNames from ..operators import (Attrib, Assign, PathAttrib, Equal, NotEqual, GreaterThan, LessThan, Match) -from ..specs import (Cancel, AcquireMutex, CancelTask, Celery, Choose, - ExclusiveChoice, Execute, Gate, Join, MultiChoice, - MultiInstance, ReleaseMutex, Simple, WorkflowSpec, - SubWorkflow, StartTask, ThreadMerge, - ThreadSplit, ThreadStart, Merge, Trigger, LoopResetTask) -from .base import Serializer +from ..specs.AcquireMutex import AcquireMutex +from ..specs.Cancel import Cancel +from ..specs.CancelTask import CancelTask +from ..specs.Celery import Celery +from ..specs.Choose import Choose +from ..specs.ExclusiveChoice import ExclusiveChoice +from ..specs.Execute import Execute +from ..specs.Gate import Gate +from ..specs.Join import Join +from ..specs.Merge import Merge +from ..specs.MultiChoice import MultiChoice +from ..specs.MultiInstance import MultiInstance +from ..specs.ReleaseMutex import ReleaseMutex +from ..specs.Simple import Simple +from ..specs.StartTask import StartTask +from ..specs.SubWorkflow import SubWorkflow +from ..specs.ThreadStart import ThreadStart +from ..specs.ThreadMerge import ThreadMerge +from ..specs.ThreadSplit import ThreadSplit +from ..specs.Transform import Transform +from ..specs.Trigger import Trigger +from ..specs.WorkflowSpec import WorkflowSpec +from ..specs.LoopResetTask import LoopResetTask +from .base import Serializer, spec_map, op_map from .exceptions import TaskNotSupportedError # Create a list of tag names out of the spec names. -_spec_map = dict() -for name in dir(specs): - if name.startswith('_'): - continue - module = specs.__dict__[name] - name = re.sub(r'(.)([A-Z])', r'\1-\2', name).lower() - _spec_map[name] = module -_spec_map['task'] = specs.Simple - -_op_map = {'equals': operators.Equal, - 'not-equals': operators.NotEqual, - 'less-than': operators.LessThan, - 'greater-than': operators.GreaterThan, - 'matches': operators.Match} +_spec_map = spec_map() +_op_map = op_map() class XmlSerializer(Serializer): diff --git a/SpiffWorkflow/SpiffWorkflow/signavio/parser/__init__.py b/SpiffWorkflow/SpiffWorkflow/signavio/parser/__init__.py index 477cb987..e69de29b 100644 --- a/SpiffWorkflow/SpiffWorkflow/signavio/parser/__init__.py +++ b/SpiffWorkflow/SpiffWorkflow/signavio/parser/__init__.py @@ -1 +0,0 @@ -from .tasks import CallActivityParser \ No newline at end of file diff --git a/SpiffWorkflow/SpiffWorkflow/specs/SubWorkflow.py b/SpiffWorkflow/SpiffWorkflow/specs/SubWorkflow.py index 58d9f779..197a6912 100644 --- a/SpiffWorkflow/SpiffWorkflow/specs/SubWorkflow.py +++ b/SpiffWorkflow/SpiffWorkflow/specs/SubWorkflow.py @@ -18,7 +18,7 @@ # 02110-1301 USA import os -from . import StartTask +from .StartTask import StartTask from .base import TaskSpec from ..task import TaskState from ..exceptions import WorkflowException @@ -87,7 +87,7 @@ class SubWorkflow(TaskSpec): def _create_subworkflow(self, my_task): from ..serializer.prettyxml import XmlSerializer - from ..specs import WorkflowSpec + from ..specs.WorkflowSpec import WorkflowSpec from ..workflow import Workflow file_name = valueof(my_task, self.file) serializer = XmlSerializer() diff --git a/SpiffWorkflow/SpiffWorkflow/specs/ThreadMerge.py b/SpiffWorkflow/SpiffWorkflow/specs/ThreadMerge.py index 56e3a4c3..ddbd0d71 100644 --- a/SpiffWorkflow/SpiffWorkflow/specs/ThreadMerge.py +++ b/SpiffWorkflow/SpiffWorkflow/specs/ThreadMerge.py @@ -19,7 +19,7 @@ from ..task import TaskState from ..exceptions import WorkflowException from ..operators import valueof -from ..specs import Join +from ..specs.Join import Join class ThreadMerge(Join): diff --git a/SpiffWorkflow/SpiffWorkflow/specs/WorkflowSpec.py b/SpiffWorkflow/SpiffWorkflow/specs/WorkflowSpec.py index 8ea12710..7f084cb3 100644 --- a/SpiffWorkflow/SpiffWorkflow/specs/WorkflowSpec.py +++ b/SpiffWorkflow/SpiffWorkflow/specs/WorkflowSpec.py @@ -17,7 +17,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -from ..specs import StartTask +from ..specs.StartTask import StartTask class WorkflowSpec(object): @@ -82,7 +82,7 @@ class WorkflowSpec(object): :returns: empty list if valid, a list of errors if not """ results = [] - from ..specs import Join + from ..specs.Join import Join def recursive_find_loop(task, history): current = history[:] diff --git a/SpiffWorkflow/SpiffWorkflow/specs/__init__.py b/SpiffWorkflow/SpiffWorkflow/specs/__init__.py index 1b10f819..e69de29b 100644 --- a/SpiffWorkflow/SpiffWorkflow/specs/__init__.py +++ b/SpiffWorkflow/SpiffWorkflow/specs/__init__.py @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -# flake8: noqa - -from .base import TaskSpec -from .AcquireMutex import AcquireMutex -from .Cancel import Cancel -from .CancelTask import CancelTask -from .Celery import Celery -from .Choose import Choose -from .ExclusiveChoice import ExclusiveChoice -from .Execute import Execute -from .Gate import Gate -from .Join import Join -from .Merge import Merge -from .MultiChoice import MultiChoice -from .MultiInstance import MultiInstance -from .ReleaseMutex import ReleaseMutex -from .Simple import Simple -from .StartTask import StartTask -from .SubWorkflow import SubWorkflow -from .ThreadStart import ThreadStart -from .ThreadMerge import ThreadMerge -from .ThreadSplit import ThreadSplit -from .Transform import Transform -from .Trigger import Trigger -from .WorkflowSpec import WorkflowSpec -from .LoopResetTask import LoopResetTask - -import inspect -__all__ = [name for name, obj in list(locals().items()) - if not (name.startswith('_') or inspect.ismodule(obj))] diff --git a/SpiffWorkflow/SpiffWorkflow/spiff/parser/__init__.py b/SpiffWorkflow/SpiffWorkflow/spiff/parser/__init__.py index 6b38b362..e69de29b 100644 --- a/SpiffWorkflow/SpiffWorkflow/spiff/parser/__init__.py +++ b/SpiffWorkflow/SpiffWorkflow/spiff/parser/__init__.py @@ -1 +0,0 @@ -from .process import SpiffBpmnParser, VALIDATOR \ No newline at end of file diff --git a/SpiffWorkflow/SpiffWorkflow/spiff/parser/process.py b/SpiffWorkflow/SpiffWorkflow/spiff/parser/process.py index 7e3dd169..a8238b6b 100644 --- a/SpiffWorkflow/SpiffWorkflow/spiff/parser/process.py +++ b/SpiffWorkflow/SpiffWorkflow/spiff/parser/process.py @@ -3,8 +3,15 @@ import os from SpiffWorkflow.dmn.parser.BpmnDmnParser import BpmnDmnParser from SpiffWorkflow.bpmn.parser.BpmnParser import BpmnValidator, full_tag -from SpiffWorkflow.bpmn.specs.events import StartEvent, EndEvent, IntermediateThrowEvent, BoundaryEvent, IntermediateCatchEvent -from SpiffWorkflow.spiff.specs import NoneTask, ManualTask, UserTask, ScriptTask, SubWorkflowTask, TransactionSubprocess, CallActivity, ServiceTask +from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent +from SpiffWorkflow.bpmn.specs.events.StartEvent import StartEvent +from SpiffWorkflow.bpmn.specs.events.IntermediateEvent import IntermediateThrowEvent, BoundaryEvent, IntermediateCatchEvent +from SpiffWorkflow.spiff.specs.none_task import NoneTask +from SpiffWorkflow.spiff.specs.manual_task import ManualTask +from SpiffWorkflow.spiff.specs.user_task import UserTask +from SpiffWorkflow.spiff.specs.script_task import ScriptTask +from SpiffWorkflow.spiff.specs.subworkflow_task import SubWorkflowTask, TransactionSubprocess, CallActivity +from SpiffWorkflow.spiff.specs.service_task import ServiceTask from SpiffWorkflow.spiff.specs.events.event_types import SendTask, ReceiveTask from SpiffWorkflow.spiff.parser.task_spec import SpiffTaskParser, SubWorkflowParser, CallActivityParser, ServiceTaskParser, ScriptTaskParser from SpiffWorkflow.spiff.parser.event_parsers import (SpiffStartEventParser, SpiffEndEventParser, SpiffBoundaryEventParser, diff --git a/SpiffWorkflow/SpiffWorkflow/spiff/serializer/__init__.py b/SpiffWorkflow/SpiffWorkflow/spiff/serializer/__init__.py index 364c3ebf..e69de29b 100644 --- a/SpiffWorkflow/SpiffWorkflow/spiff/serializer/__init__.py +++ b/SpiffWorkflow/SpiffWorkflow/spiff/serializer/__init__.py @@ -1,4 +0,0 @@ -from .task_spec_converters import NoneTaskConverter, ManualTaskConverter, UserTaskConverter, ScriptTaskConverter -from .task_spec_converters import TransactionSubprocessConverter, CallActivityTaskConverter, SubWorkflowTaskConverter -from .task_spec_converters import StartEventConverter, EndEventConverter, IntermediateCatchEventConverter, IntermediateThrowEventConverter, \ - BoundaryEventConverter, SendTaskConverter, ReceiveTaskConverter, ServiceTaskConverter diff --git a/SpiffWorkflow/SpiffWorkflow/spiff/serializer/task_spec_converters.py b/SpiffWorkflow/SpiffWorkflow/spiff/serializer/task_spec_converters.py index 5b676968..a1c0525c 100644 --- a/SpiffWorkflow/SpiffWorkflow/spiff/serializer/task_spec_converters.py +++ b/SpiffWorkflow/SpiffWorkflow/spiff/serializer/task_spec_converters.py @@ -1,9 +1,16 @@ from functools import partial from SpiffWorkflow.bpmn.serializer.bpmn_converters import BpmnTaskSpecConverter -from SpiffWorkflow.bpmn.specs.events import EndEvent, StartEvent, IntermediateThrowEvent, IntermediateCatchEvent, BoundaryEvent -from SpiffWorkflow.spiff.specs import NoneTask, ManualTask, UserTask, ScriptTask, ServiceTask, SubWorkflowTask, TransactionSubprocess, CallActivity -from SpiffWorkflow.spiff.specs.events import SendTask, ReceiveTask +from SpiffWorkflow.bpmn.specs.events.StartEvent import StartEvent +from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent +from SpiffWorkflow.bpmn.specs.events.IntermediateEvent import IntermediateThrowEvent, IntermediateCatchEvent, BoundaryEvent +from SpiffWorkflow.spiff.specs.none_task import NoneTask +from SpiffWorkflow.spiff.specs.manual_task import ManualTask +from SpiffWorkflow.spiff.specs.user_task import UserTask +from SpiffWorkflow.spiff.specs.script_task import ScriptTask +from SpiffWorkflow.spiff.specs.service_task import ServiceTask +from SpiffWorkflow.spiff.specs.subworkflow_task import SubWorkflowTask, TransactionSubprocess, CallActivity +from SpiffWorkflow.spiff.specs.events.event_types import SendTask, ReceiveTask from SpiffWorkflow.spiff.specs.events.event_definitions import MessageEventDefinition diff --git a/SpiffWorkflow/SpiffWorkflow/spiff/specs/__init__.py b/SpiffWorkflow/SpiffWorkflow/spiff/specs/__init__.py index 1e18b634..e69de29b 100644 --- a/SpiffWorkflow/SpiffWorkflow/spiff/specs/__init__.py +++ b/SpiffWorkflow/SpiffWorkflow/spiff/specs/__init__.py @@ -1,6 +0,0 @@ -from .manual_task import ManualTask -from .none_task import NoneTask -from .subworkflow_task import SubWorkflowTask, TransactionSubprocess, CallActivity -from .user_task import UserTask -from .script_task import ScriptTask -from .service_task import ServiceTask diff --git a/SpiffWorkflow/SpiffWorkflow/spiff/specs/events/__init__.py b/SpiffWorkflow/SpiffWorkflow/spiff/specs/events/__init__.py index 2fc0bc11..e69de29b 100644 --- a/SpiffWorkflow/SpiffWorkflow/spiff/specs/events/__init__.py +++ b/SpiffWorkflow/SpiffWorkflow/spiff/specs/events/__init__.py @@ -1 +0,0 @@ -from .event_types import SendTask, ReceiveTask \ No newline at end of file diff --git a/SpiffWorkflow/SpiffWorkflow/workflow.py b/SpiffWorkflow/SpiffWorkflow/workflow.py index 376f2a01..906cfa5c 100644 --- a/SpiffWorkflow/SpiffWorkflow/workflow.py +++ b/SpiffWorkflow/SpiffWorkflow/workflow.py @@ -19,7 +19,7 @@ import logging -from . import specs +from .specs.Simple import Simple from .specs.LoopResetTask import LoopResetTask from .task import Task, TaskState from .util.compat import mutex @@ -61,7 +61,7 @@ class Workflow(object): if 'Root' in workflow_spec.task_specs: root = workflow_spec.task_specs['Root'] else: - root = specs.Simple(workflow_spec, 'Root') + root = Simple(workflow_spec, 'Root') logger.info('Initialize', extra=self.log_info()) # Setting TaskState.COMPLETED prevents the root task from being executed. diff --git a/SpiffWorkflow/doc/non-bpmn/tutorial/start.py b/SpiffWorkflow/doc/non-bpmn/tutorial/start.py index e52b66c6..4090384c 100644 --- a/SpiffWorkflow/doc/non-bpmn/tutorial/start.py +++ b/SpiffWorkflow/doc/non-bpmn/tutorial/start.py @@ -1,6 +1,6 @@ import json from SpiffWorkflow.workflow import Workflow -from SpiffWorkflow.specs import WorkflowSpec +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.serializer.json import JSONSerializer # Load from JSON diff --git a/SpiffWorkflow/tests/SpiffWorkflow/PatternTest.py b/SpiffWorkflow/tests/SpiffWorkflow/PatternTest.py index 8d11528f..f0d013ce 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/PatternTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/PatternTest.py @@ -6,7 +6,7 @@ import unittest import os sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) -from SpiffWorkflow.specs import WorkflowSpec +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.task import Task from SpiffWorkflow.serializer.prettyxml import XmlSerializer from tests.SpiffWorkflow.util import run_workflow diff --git a/SpiffWorkflow/tests/SpiffWorkflow/PersistSmallWorkflowTest.py b/SpiffWorkflow/tests/SpiffWorkflow/PersistSmallWorkflowTest.py index c580c87d..b554be18 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/PersistSmallWorkflowTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/PersistSmallWorkflowTest.py @@ -6,7 +6,9 @@ import os.path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) from SpiffWorkflow.workflow import Workflow -from SpiffWorkflow.specs import Join, MultiChoice, WorkflowSpec +from SpiffWorkflow.specs.Join import Join +from SpiffWorkflow.specs.MultiChoice import MultiChoice +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.operators import Attrib, Equal, PathAttrib from SpiffWorkflow.task import TaskState from SpiffWorkflow.specs.Simple import Simple diff --git a/SpiffWorkflow/tests/SpiffWorkflow/TaskTest.py b/SpiffWorkflow/tests/SpiffWorkflow/TaskTest.py index af4f28e3..e44f68e6 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/TaskTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/TaskTest.py @@ -7,7 +7,8 @@ import os.path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) from SpiffWorkflow.task import Task, TaskState, updateDotDict -from SpiffWorkflow.specs import WorkflowSpec, Simple +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec +from SpiffWorkflow.specs.Simple import Simple class MockWorkflow(object): diff --git a/SpiffWorkflow/tests/SpiffWorkflow/WorkflowTest.py b/SpiffWorkflow/tests/SpiffWorkflow/WorkflowTest.py index e47f069d..0d2b0b3d 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/WorkflowTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/WorkflowTest.py @@ -7,7 +7,9 @@ data_dir = os.path.join(os.path.dirname(__file__), 'data') sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) from SpiffWorkflow.workflow import Workflow -from SpiffWorkflow.specs import Cancel, Simple, WorkflowSpec +from SpiffWorkflow.specs.Cancel import Cancel +from SpiffWorkflow.specs.Simple import Simple +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.task import TaskState from SpiffWorkflow.serializer.prettyxml import XmlSerializer diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/ApprovalsTest.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/ApprovalsTest.py index 19f49380..7576a23f 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/ApprovalsTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/ApprovalsTest.py @@ -2,7 +2,7 @@ import unittest from SpiffWorkflow.bpmn.workflow import BpmnWorkflow -from SpiffWorkflow.bpmn.specs.events import MessageEventDefinition +from SpiffWorkflow.bpmn.specs.events.event_definitions import MessageEventDefinition from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase __author__ = 'matth' diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/BpmnLoaderForTests.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/BpmnLoaderForTests.py index 7a407cc7..54c18709 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/BpmnLoaderForTests.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/BpmnLoaderForTests.py @@ -10,7 +10,7 @@ from SpiffWorkflow.bpmn.serializer.bpmn_converters import BpmnTaskSpecConverter # Many of our tests relied on the Packager to set the calledElement attribute on # Call Activities. I've moved that code to a customized parser. -from SpiffWorkflow.signavio.parser import CallActivityParser +from SpiffWorkflow.signavio.parser.tasks import CallActivityParser from SpiffWorkflow.bpmn.specs.SubWorkflowTask import CallActivity __author__ = 'matth' diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/BpmnWorkflowSerializerTest.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/BpmnWorkflowSerializerTest.py index d0d1a947..ac2ae463 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/BpmnWorkflowSerializerTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/BpmnWorkflowSerializerTest.py @@ -6,7 +6,7 @@ from uuid import uuid4 from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine from SpiffWorkflow.bpmn.parser.BpmnParser import BpmnParser -from SpiffWorkflow.bpmn.serializer import BpmnWorkflowSerializer +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer from SpiffWorkflow.bpmn.serializer.BpmnSerializer import BpmnSerializer from SpiffWorkflow.bpmn.workflow import BpmnWorkflow from tests.SpiffWorkflow.bpmn.BpmnLoaderForTests import TestUserTaskConverter diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/BpmnWorkflowTestCase.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/BpmnWorkflowTestCase.py index 5a341188..e2864919 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/BpmnWorkflowTestCase.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/BpmnWorkflowTestCase.py @@ -7,7 +7,7 @@ from SpiffWorkflow.bpmn.parser.BpmnParser import BpmnValidator from SpiffWorkflow.task import TaskState -from SpiffWorkflow.bpmn.serializer import BpmnWorkflowSerializer +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer from .BpmnLoaderForTests import TestUserTaskConverter, TestBpmnParser __author__ = 'matth' diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/ProcessDependencyTest.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/ProcessDependencyTest.py index b3a79519..86514a03 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/ProcessDependencyTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/ProcessDependencyTest.py @@ -3,7 +3,7 @@ import os import unittest from SpiffWorkflow.camunda.parser.CamundaParser import CamundaParser -from SpiffWorkflow.spiff.parser import SpiffBpmnParser +from SpiffWorkflow.spiff.parser.process import SpiffBpmnParser from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/MultipleEventsTest.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/MultipleEventsTest.py index b1e2a24d..86c55235 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/MultipleEventsTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/MultipleEventsTest.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import unittest -from SpiffWorkflow.bpmn.specs.events import CancelEventDefinition, SignalEventDefinition +from SpiffWorkflow.bpmn.specs.events.event_definitions import CancelEventDefinition, SignalEventDefinition from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.workflow import BpmnWorkflow from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase diff --git a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py index e87de251..9bd1f322 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py @@ -5,7 +5,7 @@ import datetime import time from datetime import timedelta -from SpiffWorkflow.bpmn.specs.events import EndEvent +from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.workflow import BpmnWorkflow from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine diff --git a/SpiffWorkflow/tests/SpiffWorkflow/camunda/BaseTestCase.py b/SpiffWorkflow/tests/SpiffWorkflow/camunda/BaseTestCase.py index 6ea39d0a..b41a8aab 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/camunda/BaseTestCase.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/camunda/BaseTestCase.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- import os -from SpiffWorkflow.bpmn.serializer import BpmnWorkflowSerializer +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer from SpiffWorkflow.camunda.parser.CamundaParser import CamundaParser -from SpiffWorkflow.camunda.serializer import UserTaskConverter, StartEventConverter, EndEventConverter, \ +from SpiffWorkflow.camunda.serializer.task_spec_converters import UserTaskConverter, StartEventConverter, EndEventConverter, \ IntermediateCatchEventConverter, IntermediateThrowEventConverter, BoundaryEventConverter -from SpiffWorkflow.dmn.serializer import BusinessRuleTaskConverter +from SpiffWorkflow.dmn.serializer.task_spec_converters import BusinessRuleTaskConverter from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase diff --git a/SpiffWorkflow/tests/SpiffWorkflow/camunda/specs/UserTaskSpecTest.py b/SpiffWorkflow/tests/SpiffWorkflow/camunda/specs/UserTaskSpecTest.py index 2d3c16d7..33f62191 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/camunda/specs/UserTaskSpecTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/camunda/specs/UserTaskSpecTest.py @@ -3,7 +3,8 @@ import unittest from SpiffWorkflow.camunda.specs.UserTask import FormField, UserTask, Form, \ EnumFormField -from SpiffWorkflow.specs import WorkflowSpec, TaskSpec +from SpiffWorkflow.specs.base import TaskSpec +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec class UserTaskSpecTest(unittest.TestCase): diff --git a/SpiffWorkflow/tests/SpiffWorkflow/data/spiff/workflow1.py b/SpiffWorkflow/tests/SpiffWorkflow/data/spiff/workflow1.py index fba204bd..bc963fea 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/data/spiff/workflow1.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/data/spiff/workflow1.py @@ -1,6 +1,11 @@ # -*- coding: utf-8 -*- -from SpiffWorkflow.specs import ExclusiveChoice, Join, MultiChoice, MultiInstance, Simple, WorkflowSpec +from SpiffWorkflow.specs.ExclusiveChoice import ExclusiveChoice +from SpiffWorkflow.specs.Join import Join +from SpiffWorkflow.specs.MultiChoice import MultiChoice +from SpiffWorkflow.specs.MultiInstance import MultiInstance +from SpiffWorkflow.specs.Simple import Simple +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.operators import Attrib, Equal, NotEqual diff --git a/SpiffWorkflow/tests/SpiffWorkflow/serializer/baseTest.py b/SpiffWorkflow/tests/SpiffWorkflow/serializer/baseTest.py index 9bb79b13..7ac41f3c 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/serializer/baseTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/serializer/baseTest.py @@ -11,7 +11,7 @@ sys.path.insert(0, os.path.join(dirname, '..')) from PatternTest import run_workflow, PatternTest from SpiffWorkflow.serializer.base import Serializer -from SpiffWorkflow.specs import WorkflowSpec +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.workflow import Workflow from SpiffWorkflow.serializer.exceptions import TaskNotSupportedError diff --git a/SpiffWorkflow/tests/SpiffWorkflow/specs/CeleryTest.py b/SpiffWorkflow/tests/SpiffWorkflow/specs/CeleryTest.py index 9190dd33..ac811688 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/specs/CeleryTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/specs/CeleryTest.py @@ -6,7 +6,8 @@ import unittest import pickle sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) from .TaskSpecTest import TaskSpecTest -from SpiffWorkflow.specs import Celery, WorkflowSpec +from SpiffWorkflow.specs.Celery import Celery +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.operators import Attrib from SpiffWorkflow.serializer.dict import DictionarySerializer from base64 import b64encode diff --git a/SpiffWorkflow/tests/SpiffWorkflow/specs/ExecuteTest.py b/SpiffWorkflow/tests/SpiffWorkflow/specs/ExecuteTest.py index bd3b06fd..fc16db93 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/specs/ExecuteTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/specs/ExecuteTest.py @@ -10,7 +10,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) from tests.SpiffWorkflow.util import run_workflow from .TaskSpecTest import TaskSpecTest from SpiffWorkflow.task import TaskState -from SpiffWorkflow.specs import Execute +from SpiffWorkflow.specs.Execute import Execute class ExecuteTest(TaskSpecTest): diff --git a/SpiffWorkflow/tests/SpiffWorkflow/specs/JoinTest.py b/SpiffWorkflow/tests/SpiffWorkflow/specs/JoinTest.py index 3eeb9286..7224701f 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/specs/JoinTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/specs/JoinTest.py @@ -8,7 +8,7 @@ import unittest sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) from .TaskSpecTest import TaskSpecTest -from SpiffWorkflow.specs import Join +from SpiffWorkflow.specs.Join import Join class JoinTest(TaskSpecTest): diff --git a/SpiffWorkflow/tests/SpiffWorkflow/specs/MergeTest.py b/SpiffWorkflow/tests/SpiffWorkflow/specs/MergeTest.py index 68f028d9..6fc692a6 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/specs/MergeTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/specs/MergeTest.py @@ -8,7 +8,9 @@ import unittest sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) from .JoinTest import JoinTest -from SpiffWorkflow.specs import Merge, WorkflowSpec, Simple +from SpiffWorkflow.specs.Merge import Merge +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec +from SpiffWorkflow.specs.Simple import Simple from SpiffWorkflow.workflow import Workflow diff --git a/SpiffWorkflow/tests/SpiffWorkflow/specs/SubWorkflowTest.py b/SpiffWorkflow/tests/SpiffWorkflow/specs/SubWorkflowTest.py index aed09fc2..5baacf99 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/specs/SubWorkflowTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/specs/SubWorkflowTest.py @@ -6,7 +6,7 @@ import os sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) -from SpiffWorkflow.specs import WorkflowSpec +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.specs.SubWorkflow import SubWorkflow from SpiffWorkflow.serializer.prettyxml import XmlSerializer from SpiffWorkflow.task import TaskState diff --git a/SpiffWorkflow/tests/SpiffWorkflow/specs/TaskSpecTest.py b/SpiffWorkflow/tests/SpiffWorkflow/specs/TaskSpecTest.py index 2b213b4a..2509d618 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/specs/TaskSpecTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/specs/TaskSpecTest.py @@ -5,9 +5,11 @@ import unittest import os sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) -from SpiffWorkflow.specs import WorkflowSpec, Simple, Join +from SpiffWorkflow.specs.Join import Join +from SpiffWorkflow.specs.Simple import Simple +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.exceptions import WorkflowException -from SpiffWorkflow.specs import TaskSpec +from SpiffWorkflow.specs.base import TaskSpec from SpiffWorkflow.serializer.dict import DictionarySerializer diff --git a/SpiffWorkflow/tests/SpiffWorkflow/specs/TransformTest.py b/SpiffWorkflow/tests/SpiffWorkflow/specs/TransformTest.py index ca2aaf1c..228a038e 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/specs/TransformTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/specs/TransformTest.py @@ -9,7 +9,8 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) from tests.SpiffWorkflow.util import run_workflow from .TaskSpecTest import TaskSpecTest -from SpiffWorkflow.specs import Transform, Simple +from SpiffWorkflow.specs.Transform import Transform +from SpiffWorkflow.specs.Simple import Simple class TransformTest(TaskSpecTest): diff --git a/SpiffWorkflow/tests/SpiffWorkflow/specs/WorkflowSpecTest.py b/SpiffWorkflow/tests/SpiffWorkflow/specs/WorkflowSpecTest.py index 8b0289b8..9b4c0d32 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/specs/WorkflowSpecTest.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/specs/WorkflowSpecTest.py @@ -15,7 +15,8 @@ try: except ImportError as e: from tests.SpiffWorkflow.util import track_workflow from SpiffWorkflow.workflow import Workflow -from SpiffWorkflow.specs import Join, WorkflowSpec +from SpiffWorkflow.specs.Join import Join +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.serializer.prettyxml import XmlSerializer serializer = XmlSerializer() diff --git a/SpiffWorkflow/tests/SpiffWorkflow/spiff/BaseTestCase.py b/SpiffWorkflow/tests/SpiffWorkflow/spiff/BaseTestCase.py index 9e882fcb..b085d1f7 100644 --- a/SpiffWorkflow/tests/SpiffWorkflow/spiff/BaseTestCase.py +++ b/SpiffWorkflow/tests/SpiffWorkflow/spiff/BaseTestCase.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- import os -from SpiffWorkflow.spiff.parser import SpiffBpmnParser, VALIDATOR -from SpiffWorkflow.spiff.serializer import NoneTaskConverter, \ +from SpiffWorkflow.spiff.parser.process import SpiffBpmnParser, VALIDATOR +from SpiffWorkflow.spiff.serializer.task_spec_converters import NoneTaskConverter, \ ManualTaskConverter, UserTaskConverter, ScriptTaskConverter, \ SubWorkflowTaskConverter, TransactionSubprocessConverter, \ CallActivityTaskConverter, \ @@ -11,7 +11,7 @@ from SpiffWorkflow.spiff.serializer import NoneTaskConverter, \ IntermediateCatchEventConverter, IntermediateThrowEventConverter, \ ServiceTaskConverter from SpiffWorkflow.dmn.serializer.task_spec_converters import BusinessRuleTaskConverter -from SpiffWorkflow.bpmn.serializer import BpmnWorkflowSerializer +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index aa94704c..99c58bef 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -29,31 +29,39 @@ from SpiffWorkflow.bpmn.exceptions import WorkflowTaskExecException # type: ign from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException # type: ignore from SpiffWorkflow.bpmn.PythonScriptEngine import Box # type: ignore from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine -from SpiffWorkflow.bpmn.serializer import BpmnWorkflowSerializer # type: ignore +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore from SpiffWorkflow.bpmn.specs.BpmnProcessSpec import BpmnProcessSpec # type: ignore -from SpiffWorkflow.bpmn.specs.events import CancelEventDefinition # type: ignore -from SpiffWorkflow.bpmn.specs.events import EndEvent +from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent # type: ignore +from SpiffWorkflow.bpmn.specs.events.event_definitions import CancelEventDefinition # type: ignore from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore from SpiffWorkflow.dmn.parser.BpmnDmnParser import BpmnDmnParser # type: ignore -from SpiffWorkflow.dmn.serializer import BusinessRuleTaskConverter # type: ignore +from SpiffWorkflow.dmn.serializer.task_spec_converters import BusinessRuleTaskConverter # type: ignore from SpiffWorkflow.exceptions import WorkflowException # type: ignore from SpiffWorkflow.serializer.exceptions import MissingSpecError # type: ignore from SpiffWorkflow.spiff.parser.process import SpiffBpmnParser # type: ignore -from SpiffWorkflow.spiff.serializer import BoundaryEventConverter # type: ignore -from SpiffWorkflow.spiff.serializer import CallActivityTaskConverter -from SpiffWorkflow.spiff.serializer import EndEventConverter -from SpiffWorkflow.spiff.serializer import IntermediateCatchEventConverter -from SpiffWorkflow.spiff.serializer import IntermediateThrowEventConverter -from SpiffWorkflow.spiff.serializer import ManualTaskConverter -from SpiffWorkflow.spiff.serializer import NoneTaskConverter -from SpiffWorkflow.spiff.serializer import ReceiveTaskConverter -from SpiffWorkflow.spiff.serializer import ScriptTaskConverter -from SpiffWorkflow.spiff.serializer import SendTaskConverter -from SpiffWorkflow.spiff.serializer import ServiceTaskConverter -from SpiffWorkflow.spiff.serializer import StartEventConverter -from SpiffWorkflow.spiff.serializer import SubWorkflowTaskConverter -from SpiffWorkflow.spiff.serializer import TransactionSubprocessConverter -from SpiffWorkflow.spiff.serializer import UserTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import BoundaryEventConverter # type: ignore +from SpiffWorkflow.spiff.serializer.task_spec_converters import ( + CallActivityTaskConverter, +) +from SpiffWorkflow.spiff.serializer.task_spec_converters import EndEventConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import ( + IntermediateCatchEventConverter, +) +from SpiffWorkflow.spiff.serializer.task_spec_converters import ( + IntermediateThrowEventConverter, +) +from SpiffWorkflow.spiff.serializer.task_spec_converters import ManualTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import NoneTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import ReceiveTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import ScriptTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import SendTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import ServiceTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import StartEventConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import SubWorkflowTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import ( + TransactionSubprocessConverter, +) +from SpiffWorkflow.spiff.serializer.task_spec_converters import UserTaskConverter from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import TaskState from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore From a8e904c6b8f0d570b7454751ec3745facde846a5 Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Thu, 3 Nov 2022 15:07:46 -0400 Subject: [PATCH 20/46] Track spiff step details more granularly (#17) --- SpiffWorkflow/SpiffWorkflow/bpmn/workflow.py | 21 ++++++++++++-- spiffworkflow-backend/poetry.lock | 27 ++++++++++-------- spiffworkflow-backend/pyproject.toml | 4 +-- .../services/process_instance_processor.py | 28 ++++++++++--------- 4 files changed, 51 insertions(+), 29 deletions(-) diff --git a/SpiffWorkflow/SpiffWorkflow/bpmn/workflow.py b/SpiffWorkflow/SpiffWorkflow/bpmn/workflow.py index 39c265f9..d9b3994b 100644 --- a/SpiffWorkflow/SpiffWorkflow/bpmn/workflow.py +++ b/SpiffWorkflow/SpiffWorkflow/bpmn/workflow.py @@ -161,7 +161,9 @@ class BpmnWorkflow(Workflow): event_definition.payload = payload self.catch(event_definition, correlations=correlations) - def do_engine_steps(self, exit_at = None): + def do_engine_steps(self, exit_at = None, + will_complete_task=None, + did_complete_task=None): """ Execute any READY tasks that are engine specific (for example, gateways or script tasks). This is done in a loop, so it will keep completing @@ -169,6 +171,8 @@ class BpmnWorkflow(Workflow): left. :param exit_at: After executing a task with a name matching this param return the task object + :param will_complete_task: Callback that will be called prior to completing a task + :param did_complete_task: Callback that will be called after completing a task """ assert not self.read_only engine_steps = list( @@ -176,21 +180,34 @@ class BpmnWorkflow(Workflow): if self._is_engine_task(t.task_spec)]) while engine_steps: for task in engine_steps: + if will_complete_task is not None: + will_complete_task(task) task.complete() + if did_complete_task is not None: + did_complete_task(task) if task.task_spec.name == exit_at: return task engine_steps = list( [t for t in self.get_tasks(TaskState.READY) if self._is_engine_task(t.task_spec)]) - def refresh_waiting_tasks(self): + def refresh_waiting_tasks(self, + will_refresh_task=None, + did_refresh_task=None): """ Refresh the state of all WAITING tasks. This will, for example, update Catching Timer Events whose waiting time has passed. + + :param will_refresh_task: Callback that will be called prior to refreshing a task + :param did_refresh_task: Callback that will be called after refreshing a task """ assert not self.read_only for my_task in self.get_tasks(TaskState.WAITING): + if will_refresh_task is not None: + will_refresh_task(my_task) my_task.task_spec._update(my_task) + if did_refresh_task is not None: + did_refresh_task(my_task) def get_tasks_from_spec_name(self, name, workflow=None): return [t for t in self.get_tasks(workflow=workflow) if t.task_spec.name == name] diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock index c474c696..7c18c602 100644 --- a/spiffworkflow-backend/poetry.lock +++ b/spiffworkflow-backend/poetry.lock @@ -1860,20 +1860,16 @@ description = "A workflow framework and BPMN/DMN Processor" category = "main" optional = false python-versions = "*" -develop = false +develop = true [package.dependencies] celery = "*" configparser = "*" -dateparser = "*" lxml = "*" -pytz = "*" [package.source] -type = "git" -url = "https://github.com/sartography/SpiffWorkflow" -reference = "main" -resolved_reference = "a6392d19061f623394f5705fb78af23673d3940d" +type = "directory" +url = "../SpiffWorkflow" [[package]] name = "SQLAlchemy" @@ -2256,7 +2252,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "1.1" python-versions = ">=3.9,<3.11" -content-hash = "995be3a9a60b515b281f017ff32ff27a52ca178b1980611b348dccac6afb6b89" +content-hash = "9562df87977dc1c9273401fa150dd26742a6786d5162ec3e71844482207b4fba" [metadata.files] alabaster = [ @@ -2621,7 +2617,6 @@ greenlet = [ {file = "greenlet-1.1.3.post0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a954002064ee919b444b19c1185e8cce307a1f20600f47d6f4b6d336972c809"}, {file = "greenlet-1.1.3.post0-cp39-cp39-win32.whl", hash = "sha256:2ccdc818cc106cc238ff7eba0d71b9c77be868fdca31d6c3b1347a54c9b187b2"}, {file = "greenlet-1.1.3.post0-cp39-cp39-win_amd64.whl", hash = "sha256:91a84faf718e6f8b888ca63d0b2d6d185c8e2a198d2a7322d75c303e7097c8b7"}, - {file = "greenlet-1.1.3.post0.tar.gz", hash = "sha256:f5e09dc5c6e1796969fd4b775ea1417d70e49a5df29aaa8e5d10675d9e11872c"}, ] gunicorn = [ {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, @@ -2946,10 +2941,7 @@ orjson = [ {file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b68a42a31f8429728183c21fb440c21de1b62e5378d0d73f280e2d894ef8942e"}, {file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ff13410ddbdda5d4197a4a4c09969cb78c722a67550f0a63c02c07aadc624833"}, {file = "orjson-3.8.0-cp310-none-win_amd64.whl", hash = "sha256:2d81e6e56bbea44be0222fb53f7b255b4e7426290516771592738ca01dbd053b"}, - {file = "orjson-3.8.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:200eae21c33f1f8b02a11f5d88d76950cd6fd986d88f1afe497a8ae2627c49aa"}, - {file = "orjson-3.8.0-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:9529990f3eab54b976d327360aa1ff244a4b12cb5e4c5b3712fcdd96e8fe56d4"}, {file = "orjson-3.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e2defd9527651ad39ec20ae03c812adf47ef7662bdd6bc07dabb10888d70dc62"}, - {file = "orjson-3.8.0-cp311-none-win_amd64.whl", hash = "sha256:b21c7af0ff6228ca7105f54f0800636eb49201133e15ddb80ac20c1ce973ef07"}, {file = "orjson-3.8.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9e6ac22cec72d5b39035b566e4b86c74b84866f12b5b0b6541506a080fb67d6d"}, {file = "orjson-3.8.0-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:e2f4a5542f50e3d336a18cb224fc757245ca66b1fd0b70b5dd4471b8ff5f2b0e"}, {file = "orjson-3.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1418feeb8b698b9224b1f024555895169d481604d5d884498c1838d7412794c"}, @@ -3062,7 +3054,18 @@ py = [ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] pyasn1 = [ + {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, + {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, + {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, + {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, + {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, + {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, + {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, + {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, + {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, + {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, + {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, ] pycodestyle = [ diff --git a/spiffworkflow-backend/pyproject.toml b/spiffworkflow-backend/pyproject.toml index 7f2d09a4..764cebcf 100644 --- a/spiffworkflow-backend/pyproject.toml +++ b/spiffworkflow-backend/pyproject.toml @@ -27,8 +27,8 @@ flask-marshmallow = "*" flask-migrate = "*" flask-restful = "*" werkzeug = "*" -SpiffWorkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "main"} -#SpiffWorkflow = {develop = true, path = "../SpiffWorkflow" } +#SpiffWorkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "main"} +SpiffWorkflow = {develop = true, path = "../SpiffWorkflow" } sentry-sdk = "^1.10" sphinx-autoapi = "^2.0" flask-bpmn = {git = "https://github.com/sartography/flask-bpmn", rev = "main"} diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 99c58bef..0795bc36 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -557,10 +557,9 @@ class ProcessInstanceProcessor: "lane_assignment_id": lane_assignment_id, } - def save_spiff_step_details(self, bpmn_json: Optional[str]) -> None: + def save_spiff_step_details(self) -> None: """SaveSpiffStepDetails.""" - if bpmn_json is None: - return + bpmn_json = self.serialize() wf_json = json.loads(bpmn_json) task_json = "{}" if "tasks" in wf_json: @@ -979,11 +978,18 @@ class ProcessInstanceProcessor: def do_engine_steps(self, exit_at: None = None, save: bool = False) -> None: """Do_engine_steps.""" - self.increment_spiff_step() - try: - self.bpmn_process_instance.refresh_waiting_tasks() - self.bpmn_process_instance.do_engine_steps(exit_at=exit_at) + self.bpmn_process_instance.refresh_waiting_tasks( + will_refresh_task=lambda t: self.increment_spiff_step(), + did_refresh_task=lambda t: self.save_spiff_step_details(), + ) + + self.bpmn_process_instance.do_engine_steps( + exit_at=exit_at, + will_complete_task=lambda t: self.increment_spiff_step(), + did_complete_task=lambda t: self.save_spiff_step_details(), + ) + self.process_bpmn_messages() self.queue_waiting_receive_messages() @@ -993,10 +999,6 @@ class ProcessInstanceProcessor: finally: if save: self.save() - bpmn_json = self.process_instance_model.bpmn_json - else: - bpmn_json = self.serialize() - self.save_spiff_step_details(bpmn_json) def cancel_notify(self) -> None: """Cancel_notify.""" @@ -1009,6 +1011,7 @@ class ProcessInstanceProcessor: # A little hackly, but make the bpmn_process_instance catch a cancel event. bpmn_process_instance.signal("cancel") # generate a cancel signal. bpmn_process_instance.catch(CancelEventDefinition()) + # Due to this being static, can't save granular step details in this case bpmn_process_instance.do_engine_steps() except WorkflowTaskExecException as we: raise ApiError.from_workflow_exception("task_error", str(we), we) from we @@ -1109,8 +1112,7 @@ class ProcessInstanceProcessor: """Complete_task.""" self.increment_spiff_step() self.bpmn_process_instance.complete_task_from_id(task.id) - bpmn_json = self.serialize() - self.save_spiff_step_details(bpmn_json) + self.save_spiff_step_details() def get_data(self) -> dict[str, Any]: """Get_data.""" From d46ac958a2750ec1f876b4b43fd77fcbbd0b8639 Mon Sep 17 00:00:00 2001 From: jasquat Date: Thu, 3 Nov 2022 15:55:50 -0400 Subject: [PATCH 21/46] some more updates for group forms w/ burnettk --- .../src/spiffworkflow_backend/api.yml | 2 - .../models/process_group.py | 7 + .../routes/process_api_blueprint.py | 23 +-- .../services/process_model_service.py | 3 +- .../integration/test_process_api.py | 13 +- .../src/components/ButtonWithConfirmation.tsx | 44 +++-- .../src/components/ProcessGroupForm.tsx | 178 ++++++++++++++++++ .../src/components/ProcessModelSearch.tsx | 4 +- spiffworkflow-frontend/src/interfaces.ts | 1 + .../src/routes/ProcessGroupEdit.tsx | 74 +------- .../src/routes/ProcessGroupList.tsx | 12 +- .../src/routes/ProcessGroupNew.tsx | 71 ++----- 12 files changed, 266 insertions(+), 166 deletions(-) create mode 100644 spiffworkflow-frontend/src/components/ProcessGroupForm.tsx diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index de10f598..2ca57999 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -153,7 +153,6 @@ paths: description: The number of groups to show per page. Defaults to page 10. schema: type: integer - # process_groups_list get: operationId: spiffworkflow_backend.routes.process_api_blueprint.process_groups_list summary: get list @@ -168,7 +167,6 @@ paths: type: array items: $ref: "#/components/schemas/ProcessModelCategory" - # process_group_add post: operationId: spiffworkflow_backend.routes.process_api_blueprint.process_group_add summary: Add process group diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_group.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_group.py index 0b100ed4..549ab008 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_group.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_group.py @@ -1,5 +1,6 @@ """Process_group.""" from __future__ import annotations +import dataclasses from dataclasses import dataclass from dataclasses import field @@ -20,6 +21,7 @@ class ProcessGroup: id: str # A unique string name, lower case, under scores (ie, 'my_group') display_name: str + description: str | None = None display_order: int | None = 0 admin: bool | None = False process_models: list[ProcessModelInfo] = field( @@ -38,6 +40,11 @@ class ProcessGroup: return True return False + @property + def serialized(self) -> dict: + original_dict = dataclasses.asdict(self) + return {x: original_dict[x] for x in original_dict if x not in ["sort_index"]} + class ProcessGroupSchema(Schema): """ProcessGroupSchema.""" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index fd20341c..63c717d1 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -43,7 +43,7 @@ from spiffworkflow_backend.models.message_triggerable_process_model import ( MessageTriggerableProcessModel, ) from spiffworkflow_backend.models.principal import PrincipalModel -from spiffworkflow_backend.models.process_group import ProcessGroupSchema +from spiffworkflow_backend.models.process_group import ProcessGroup, ProcessGroupSchema from spiffworkflow_backend.models.process_instance import ProcessInstanceApiSchema from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModelSchema @@ -135,17 +135,13 @@ def permissions_check(body: Dict[str, Dict[str, list[str]]]) -> flask.wrappers.R def process_group_add( - body: Dict[str, Union[str, bool, int]] + body: dict ) -> flask.wrappers.Response: """Add_process_group.""" process_model_service = ProcessModelService() - process_group = ProcessGroupSchema().load(body) + process_group = ProcessGroup(**body) process_model_service.add_process_group(process_group) - return Response( - json.dumps(ProcessGroupSchema().dump(process_group)), - status=201, - mimetype="application/json", - ) + return make_response(jsonify(process_group), 201) def process_group_delete(process_group_id: str) -> flask.wrappers.Response: @@ -155,12 +151,12 @@ def process_group_delete(process_group_id: str) -> flask.wrappers.Response: def process_group_update( - process_group_id: str, body: Dict[str, Union[str, bool, int]] -) -> Dict[str, Union[str, bool, int]]: + process_group_id: str, body: dict +) -> flask.wrappers.Response: """Process Group Update.""" - process_group = ProcessGroupSchema().load(body) + process_group = ProcessGroup(id=process_group_id, **body) ProcessModelService().update_process_group(process_group) - return ProcessGroupSchema().dump(process_group) # type: ignore + return make_response(jsonify(process_group), 200) def process_groups_list(page: int = 1, per_page: int = 100) -> flask.wrappers.Response: @@ -173,6 +169,7 @@ def process_groups_list(page: int = 1, per_page: int = 100) -> flask.wrappers.Re remainder = len(process_groups) % per_page if remainder > 0: pages += 1 + response_json = { "results": ProcessGroupSchema(many=True).dump(batch), "pagination": { @@ -198,7 +195,7 @@ def process_group_show( status_code=400, ) ) from exception - return ProcessGroupSchema().dump(process_group) + return make_response(jsonify(process_group), 200) def process_model_add( diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py index 57d84229..7789493f 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py @@ -1,4 +1,5 @@ """Process_model_service.""" +import dataclasses import json import os import shutil @@ -170,7 +171,7 @@ class ProcessModelService(FileSystemService): json_path = os.path.join(cat_path, self.CAT_JSON_FILE) with open(json_path, "w") as cat_json: json.dump( - self.GROUP_SCHEMA.dump(process_group), + process_group.serialized, cat_json, indent=4, sort_keys=True, diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py index 9a923b97..6fe38f40 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1,4 +1,5 @@ """Test Process Api Blueprint.""" +import dataclasses import io import json import time @@ -8,6 +9,7 @@ import pytest from flask.app import Flask from flask.testing import FlaskClient from flask_bpmn.models.db import db +from spiffworkflow_backend import MyJSONEncoder from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec @@ -17,7 +19,7 @@ from spiffworkflow_backend.exceptions.process_entity_not_found_error import ( from spiffworkflow_backend.models.active_task import ActiveTaskModel from spiffworkflow_backend.models.group import GroupModel from spiffworkflow_backend.models.process_group import ProcessGroup -from spiffworkflow_backend.models.process_group import ProcessGroupSchema +# from spiffworkflow_backend.models.process_group import ProcessGroupSchema from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus from spiffworkflow_backend.models.process_instance_report import ( @@ -388,25 +390,30 @@ class TestProcessApi(BaseTest): display_name="Another Test Category", display_order=0, admin=False, + description="Test Description" ) response = client.post( "/v1.0/process-groups", headers=self.logged_in_headers(with_super_admin_user), content_type="application/json", - data=json.dumps(ProcessGroupSchema().dump(process_group)), + data=json.dumps(process_group.serialized), + ) assert response.status_code == 201 + assert response.json # Check what is returned - result = ProcessGroupSchema().loads(response.get_data(as_text=True)) + result = ProcessGroup(**response.json) assert result is not None assert result.display_name == "Another Test Category" assert result.id == "test" + assert result.description == "Test Description" # Check what is persisted persisted = ProcessModelService().get_process_group("test") assert persisted.display_name == "Another Test Category" assert persisted.id == "test" + assert persisted.description == "Test Description" def test_process_group_delete( self, diff --git a/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx b/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx index 663bae1e..f8e8bf77 100644 --- a/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx +++ b/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx @@ -41,23 +41,35 @@ export default function ButtonWithConfirmation({ const confirmationDialog = () => { return ( - - {title} - - {modalBodyElement()} - - - - - + open={showConfirmationPrompt} + danger + modalHeading={description} + modalLabel={title} + primaryButtonText={confirmButtonLabel} + secondaryButtonText="Cancel" + onSecondarySubmit={handleConfirmationPromptCancel} + onRequestSubmit={handleConfirmation} + /> ); + // return ( + // + // + // {title} + // + // {modalBodyElement()} + // + // + // + // + // + // ); }; return ( diff --git a/spiffworkflow-frontend/src/components/ProcessGroupForm.tsx b/spiffworkflow-frontend/src/components/ProcessGroupForm.tsx new file mode 100644 index 00000000..21cabc93 --- /dev/null +++ b/spiffworkflow-frontend/src/components/ProcessGroupForm.tsx @@ -0,0 +1,178 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +// @ts-ignore +import { Button, ButtonSet, Form, Stack, TextInput } from '@carbon/react'; +import { slugifyString } from '../helpers'; +import HttpService from '../services/HttpService'; +import { ProcessGroup } from '../interfaces'; +import ButtonWithConfirmation from './ButtonWithConfirmation'; + +type OwnProps = { + mode: string; + processGroup: ProcessGroup; + setProcessGroup: (..._args: any[]) => any; +}; + +export default function ProcessGroupForm({ + mode, + processGroup, + setProcessGroup, +}: OwnProps) { + const [identifierInvalid, setIdentifierInvalid] = useState(false); + const [idHasBeenUpdatedByUser, setIdHasBeenUpdatedByUser] = + useState(false); + const [displayNameInvalid, setDisplayNameInvalid] = useState(false); + const navigate = useNavigate(); + + const navigateToProcessGroup = (_result: any) => { + if (processGroup) { + navigate(`/admin/process-groups/${processGroup.id}`); + } + }; + + const navigateToProcessGroups = (_result: any) => { + navigate(`/admin/process-groups`); + }; + + const hasValidIdentifier = (identifierToCheck: string) => { + return identifierToCheck.match(/^[a-z0-9][0-9a-z-]+[a-z0-9]$/); + }; + + const deleteProcessGroup = () => { + HttpService.makeCallToBackend({ + path: `/process-groups/${processGroup.id}`, + successCallback: navigateToProcessGroups, + httpMethod: 'DELETE', + }); + }; + + const handleFormSubmission = (event: any) => { + event.preventDefault(); + let hasErrors = false; + if (!hasValidIdentifier(processGroup.id)) { + setIdentifierInvalid(true); + hasErrors = true; + } + if (processGroup.display_name === '') { + setDisplayNameInvalid(true); + hasErrors = true; + } + if (hasErrors) { + return; + } + let path = '/process-groups'; + if (mode === 'edit') { + path = `/process-groups/${processGroup.id}`; + } + let httpMethod = 'POST'; + if (mode === 'edit') { + httpMethod = 'PUT'; + } + const postBody = { + display_name: processGroup.display_name, + description: processGroup.description, + }; + if (mode === 'new') { + Object.assign(postBody, { id: processGroup.id }); + } + + HttpService.makeCallToBackend({ + path, + successCallback: navigateToProcessGroup, + httpMethod, + postBody, + }); + }; + + const updateProcessGroup = (newValues: any) => { + const processGroupToCopy = { + ...processGroup, + }; + Object.assign(processGroupToCopy, newValues); + setProcessGroup(processGroupToCopy); + }; + + const onDisplayNameChanged = (newDisplayName: any) => { + setDisplayNameInvalid(false); + const updateDict = { display_name: newDisplayName }; + if (!idHasBeenUpdatedByUser) { + Object.assign(updateDict, { id: slugifyString(newDisplayName) }); + } + updateProcessGroup(updateDict); + }; + + const formElements = () => { + const textInputs = [ + onDisplayNameChanged(event.target.value)} + onBlur={(event: any) => console.log('event', event)} + />, + ]; + + if (mode === 'new') { + textInputs.push( + { + updateProcessGroup({ id: event.target.value }); + // was invalid, and now valid + if (identifierInvalid && hasValidIdentifier(event.target.value)) { + setIdentifierInvalid(false); + } + setIdHasBeenUpdatedByUser(true); + }} + /> + ); + } + + textInputs.push( + + updateProcessGroup({ description: event.target.value }) + } + /> + ); + + return textInputs; + }; + + const formButtons = () => { + const buttons = [ + , + ]; + if (mode === 'edit') { + buttons.push( + + ); + } + return {buttons}; + }; + + return ( +
    + + {formElements()} + {formButtons()} + +
    + ); +} diff --git a/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx b/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx index 679831a5..1f138f6e 100644 --- a/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx +++ b/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx @@ -9,12 +9,14 @@ type OwnProps = { onChange: (..._args: any[]) => any; processModels: ProcessModel[]; selectedItem?: ProcessModel | null; + titleText?: string; }; export default function ProcessModelSearch({ processModels, selectedItem, onChange, + titleText = 'Process model', }: OwnProps) { const shouldFilterProcessModel = (options: any) => { const processModel: ProcessModel = options.item; @@ -38,7 +40,7 @@ export default function ProcessModelSearch({ }} shouldFilterItem={shouldFilterProcessModel} placeholder="Choose a process model" - titleText="Process model" + titleText={titleText} selectedItem={selectedItem} /> ); diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts index 08c8e4fa..14ead3ba 100644 --- a/spiffworkflow-frontend/src/interfaces.ts +++ b/spiffworkflow-frontend/src/interfaces.ts @@ -14,6 +14,7 @@ export interface RecentProcessModel { export interface ProcessGroup { id: string; display_name: string; + description?: string | null; } export interface ProcessModel { diff --git a/spiffworkflow-frontend/src/routes/ProcessGroupEdit.tsx b/spiffworkflow-frontend/src/routes/ProcessGroupEdit.tsx index 05f75fb1..d624309c 100644 --- a/spiffworkflow-frontend/src/routes/ProcessGroupEdit.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessGroupEdit.tsx @@ -1,21 +1,18 @@ import { useState, useEffect } from 'react'; -import { useParams, useNavigate } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; // @ts-ignore -import { Button, Stack } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import HttpService from '../services/HttpService'; -import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; +import ProcessGroupForm from '../components/ProcessGroupForm'; +import { ProcessGroup } from '../interfaces'; export default function ProcessGroupEdit() { - const [displayName, setDisplayName] = useState(''); const params = useParams(); - const navigate = useNavigate(); - const [processGroup, setProcessGroup] = useState(null); + const [processGroup, setProcessGroup] = useState(null); useEffect(() => { const setProcessGroupsFromResult = (result: any) => { setProcessGroup(result); - setDisplayName(result.display_name); }; HttpService.makeCallToBackend({ @@ -24,69 +21,16 @@ export default function ProcessGroupEdit() { }); }, [params]); - const navigateToProcessGroup = (_result: any) => { - navigate(`/admin/process-groups/${(processGroup as any).id}`); - }; - - const navigateToProcessGroups = (_result: any) => { - navigate(`/admin/process-groups`); - }; - - const updateProcessGroup = (event: any) => { - event.preventDefault(); - HttpService.makeCallToBackend({ - path: `/process-groups/${(processGroup as any).id}`, - successCallback: navigateToProcessGroup, - httpMethod: 'PUT', - postBody: { - display_name: displayName, - id: (processGroup as any).id, - }, - }); - }; - - const deleteProcessGroup = () => { - HttpService.makeCallToBackend({ - path: `/process-groups/${(processGroup as any).id}`, - successCallback: navigateToProcessGroups, - httpMethod: 'DELETE', - }); - }; - - const onDisplayNameChanged = (newDisplayName: any) => { - setDisplayName(newDisplayName); - }; - if (processGroup) { return ( <>

    Edit Process Group: {(processGroup as any).id}

    -
    - - onDisplayNameChanged(e.target.value)} - /> -
    -
    - - - - - -
    + ); } diff --git a/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx b/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx index f9881def..bb19e939 100644 --- a/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx @@ -1,15 +1,12 @@ import { useEffect, useState } from 'react'; import { Link, useNavigate, useSearchParams } from 'react-router-dom'; // @ts-ignore -import { Button, Form, Table } from '@carbon/react'; -import { InputGroup } from 'react-bootstrap'; -import { Typeahead } from 'react-bootstrap-typeahead'; -import { Option } from 'react-bootstrap-typeahead/types/types'; +import { Button, Table } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import PaginationForTable from '../components/PaginationForTable'; import HttpService from '../services/HttpService'; import { getPageInfoFromSearchParams } from '../helpers'; -import { CarbonComboBoxSelection, ProcessModel } from '../interfaces'; +import { CarbonComboBoxSelection } from '../interfaces'; import ProcessModelSearch from '../components/ProcessModelSearch'; // Example process group json @@ -111,6 +108,7 @@ export default function ProcessGroupList() { ); }; @@ -119,7 +117,9 @@ export default function ProcessGroupList() { return ( <> - +

    {processModelSearchArea()} diff --git a/spiffworkflow-frontend/src/routes/ProcessGroupNew.tsx b/spiffworkflow-frontend/src/routes/ProcessGroupNew.tsx index 2f3f3a35..d4d8b038 100644 --- a/spiffworkflow-frontend/src/routes/ProcessGroupNew.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessGroupNew.tsx @@ -1,71 +1,24 @@ import { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import Button from 'react-bootstrap/Button'; -import Form from 'react-bootstrap/Form'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; -import { slugifyString } from '../helpers'; -import HttpService from '../services/HttpService'; +import ProcessGroupForm from '../components/ProcessGroupForm'; +import { ProcessGroup } from '../interfaces'; export default function ProcessGroupNew() { - const [identifier, setIdentifier] = useState(''); - const [idHasBeenUpdatedByUser, setIdHasBeenUpdatedByUser] = useState(false); - const [displayName, setDisplayName] = useState(''); - const navigate = useNavigate(); - - const navigateToProcessGroup = (_result: any) => { - navigate(`/admin/process-groups/${identifier}`); - }; - - const addProcessGroup = (event: any) => { - event.preventDefault(); - HttpService.makeCallToBackend({ - path: `/process-groups`, - successCallback: navigateToProcessGroup, - httpMethod: 'POST', - postBody: { - id: identifier, - display_name: displayName, - }, - }); - }; - - const onDisplayNameChanged = (newDisplayName: any) => { - setDisplayName(newDisplayName); - if (!idHasBeenUpdatedByUser) { - setIdentifier(slugifyString(newDisplayName)); - } - }; + const [processGroup, setProcessGroup] = useState({ + id: '', + display_name: '', + description: '', + }); return ( <>

    Add Process Group

    -
    - - Display Name: - onDisplayNameChanged(e.target.value)} - /> - - - ID: - { - setIdentifier(e.target.value); - setIdHasBeenUpdatedByUser(true); - }} - /> - - -
    + ); } From 2420cbf80f9880e5820dae139abadf5646da9078 Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Thu, 3 Nov 2022 15:58:06 -0400 Subject: [PATCH 22/46] Point back to spiff main (#19) --- spiffworkflow-backend/poetry.lock | 10 ++++++---- spiffworkflow-backend/pyproject.toml | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock index 7c18c602..fdc27f61 100644 --- a/spiffworkflow-backend/poetry.lock +++ b/spiffworkflow-backend/poetry.lock @@ -1860,7 +1860,7 @@ description = "A workflow framework and BPMN/DMN Processor" category = "main" optional = false python-versions = "*" -develop = true +develop = false [package.dependencies] celery = "*" @@ -1868,8 +1868,10 @@ configparser = "*" lxml = "*" [package.source] -type = "directory" -url = "../SpiffWorkflow" +type = "git" +url = "https://github.com/sartography/SpiffWorkflow" +reference = "main" +resolved_reference = "8d820dce1f439bb76bc07e39629832d998d6f634" [[package]] name = "SQLAlchemy" @@ -2252,7 +2254,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "1.1" python-versions = ">=3.9,<3.11" -content-hash = "9562df87977dc1c9273401fa150dd26742a6786d5162ec3e71844482207b4fba" +content-hash = "995be3a9a60b515b281f017ff32ff27a52ca178b1980611b348dccac6afb6b89" [metadata.files] alabaster = [ diff --git a/spiffworkflow-backend/pyproject.toml b/spiffworkflow-backend/pyproject.toml index 764cebcf..7f2d09a4 100644 --- a/spiffworkflow-backend/pyproject.toml +++ b/spiffworkflow-backend/pyproject.toml @@ -27,8 +27,8 @@ flask-marshmallow = "*" flask-migrate = "*" flask-restful = "*" werkzeug = "*" -#SpiffWorkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "main"} -SpiffWorkflow = {develop = true, path = "../SpiffWorkflow" } +SpiffWorkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "main"} +#SpiffWorkflow = {develop = true, path = "../SpiffWorkflow" } sentry-sdk = "^1.10" sphinx-autoapi = "^2.0" flask-bpmn = {git = "https://github.com/sartography/flask-bpmn", rev = "main"} From 88e58865fb22b175e22c6fcc2b307a6825f2cf75 Mon Sep 17 00:00:00 2001 From: burnettk Date: Thu, 3 Nov 2022 16:23:15 -0400 Subject: [PATCH 23/46] work around parser.get_process_dependencies returning a set containing the element None --- .../src/spiffworkflow_backend/scripts/get_current_user.py | 4 ++-- .../src/spiffworkflow_backend/scripts/get_process_info.py | 2 +- .../services/process_instance_processor.py | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_current_user.py b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_current_user.py index b8c314c0..a1a1b47e 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_current_user.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_current_user.py @@ -9,8 +9,8 @@ from spiffworkflow_backend.models.script_attributes_context import ( from spiffworkflow_backend.scripts.script import Script -class GetUser(Script): - """GetUser.""" +class GetCurrentUser(Script): + """GetCurrentUser.""" def get_description(self) -> str: """Get_description.""" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_process_info.py b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_process_info.py index b8ad3d68..45c70d6b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_process_info.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_process_info.py @@ -8,7 +8,7 @@ from spiffworkflow_backend.scripts.script import Script class GetProcessInfo(Script): - """GetUser.""" + """GetProcessInfo.""" def get_description(self) -> str: """Get_description.""" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 0795bc36..dd80aa85 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -732,6 +732,10 @@ class ProcessInstanceProcessor: if processed_identifiers is None: processed_identifiers = set() processor_dependencies = parser.get_process_dependencies() + + # since get_process_dependencies() returns a set with None sometimes, we need to remove it + processor_dependencies = processor_dependencies - {None} + processor_dependencies_new = processor_dependencies - processed_identifiers bpmn_process_identifiers_in_parser = parser.get_process_ids() From 7d2f18bd42ef639b24dd5de43329d935d9a07bba Mon Sep 17 00:00:00 2001 From: burnettk Date: Thu, 3 Nov 2022 16:55:10 -0400 Subject: [PATCH 24/46] prevent backfill from exploding when a primary file name on another model does not exist --- .../services/process_instance_processor.py | 20 +++++++++++++++---- .../services/spec_file_service.py | 9 ++++++--- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index dd80aa85..35320f65 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -94,6 +94,9 @@ from spiffworkflow_backend.scripts.script import Script from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.service_task_service import ServiceTaskDelegate +from spiffworkflow_backend.services.spec_file_service import ( + ProcessModelFileNotFoundError, +) from spiffworkflow_backend.services.spec_file_service import SpecFileService from spiffworkflow_backend.services.user_service import UserService @@ -669,10 +672,14 @@ class ProcessInstanceProcessor: process_models = ProcessModelService().get_process_models() for process_model in process_models: if process_model.primary_file_name: - etree_element = SpecFileService.get_etree_element_from_file_name( - process_model, process_model.primary_file_name - ) - bpmn_process_identifiers = [] + try: + etree_element = SpecFileService.get_etree_element_from_file_name( + process_model, process_model.primary_file_name + ) + bpmn_process_identifiers = [] + except ProcessModelFileNotFoundError: + # if primary_file_name doesn't actually exist on disk, then just go on to the next process_model + continue try: bpmn_process_identifiers = ( @@ -700,6 +707,11 @@ class ProcessInstanceProcessor: bpmn_process_identifier: str, ) -> str: """Bpmn_file_full_path_from_bpmn_process_identifier.""" + if bpmn_process_identifier is None: + raise ValueError( + "bpmn_file_full_path_from_bpmn_process_identifier: bpmn_process_identifier is unexpectedly None" + ) + bpmn_process_id_lookup = BpmnProcessIdLookup.query.filter_by( bpmn_process_identifier=bpmn_process_identifier ).first() diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py index 4e1e30e2..eb2a322f 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py @@ -27,6 +27,10 @@ from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.process_model_service import ProcessModelService +class ProcessModelFileNotFoundError(Exception): + """ProcessModelFileNotFoundError.""" + + class SpecFileService(FileSystemService): """SpecFileService.""" @@ -90,9 +94,8 @@ class SpecFileService(FileSystemService): """Get_data.""" file_path = SpecFileService.file_path(process_model_info, file_name) if not os.path.exists(file_path): - raise ApiError( - "unknown_file", - f"No file found with name {file_name} in {process_model_info.display_name}", + raise ProcessModelFileNotFoundError( + f"No file found with name {file_name} in {process_model_info.display_name}" ) with open(file_path, "rb") as f_handle: spec_file_data = f_handle.read() From ecf2b7c9419c2753cc04a874cb2c288b555d1c45 Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Thu, 3 Nov 2022 18:57:26 -0400 Subject: [PATCH 25/46] Change steps when viewing a process instance model (#18) --- .../models/process_instance.py | 1 + .../src/routes/ProcessInstanceShow.tsx | 66 ++++++++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py index 0e4112d6..50c3c9f7 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py @@ -112,6 +112,7 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): "end_in_seconds": self.end_in_seconds, "process_initiator_id": self.process_initiator_id, "bpmn_xml_file_contents": local_bpmn_xml_file_contents, + "spiff_step": self.spiff_step, } @property diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index 677e5b09..642c7d65 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -96,6 +96,62 @@ export default function ProcessInstanceShow() { return taskIds; }; + const currentSpiffStep = (processInstanceToUse: any) => { + if (typeof params.spiff_step === 'undefined') { + return processInstanceToUse.spiff_step; + } + + return Number(params.spiff_step); + }; + + const showingFirstSpiffStep = (processInstanceToUse: any) => { + return currentSpiffStep(processInstanceToUse) === 1; + }; + + const showingLastSpiffStep = (processInstanceToUse: any) => { + return ( + currentSpiffStep(processInstanceToUse) === processInstanceToUse.spiff_step + ); + }; + + const spiffStepLink = ( + processInstanceToUse: any, + label: string, + distance: number + ) => { + return ( +
  • + + {label} + +
  • + ); + }; + + const previousStepLink = (processInstanceToUse: any) => { + if (showingFirstSpiffStep(processInstanceToUse)) { + return null; + } + + return spiffStepLink(processInstanceToUse, 'Previous Step', -1); + }; + + const nextStepLink = (processInstanceToUse: any) => { + if (showingLastSpiffStep(processInstanceToUse)) { + return null; + } + + return spiffStepLink(processInstanceToUse, 'Next Step', 1); + }; + const getInfoTag = (processInstanceToUse: any) => { const currentEndDate = convertSecondsToFormattedDate( processInstanceToUse.end_in_seconds @@ -135,6 +191,12 @@ export default function ProcessInstanceShow() { Messages +
  • + Step {currentSpiffStep(processInstanceToUse)} of{' '} + {processInstanceToUse.spiff_step} +
  • + {previousStepLink(processInstanceToUse)} + {nextStepLink(processInstanceToUse)} ); }; @@ -234,7 +296,9 @@ export default function ProcessInstanceShow() { }; const canEditTaskData = (task: any) => { - return task.state === 'READY'; + return ( + task.state === 'READY' && showingLastSpiffStep(processInstance as any) + ); }; const cancelEditingTaskData = () => { From 7ac4b51c6a607fdd43524307db08070b4d2c1db0 Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 4 Nov 2022 09:33:42 -0400 Subject: [PATCH 26/46] Squashed 'SpiffWorkflow/' changes from a6392d1906..8d820dce1f 8d820dce1f Track spiff step details more granularly (#17) 426da26d8f Clear the remaining __init__.py imports in SpiffWorkflow (#14) 9a1d1c484a Fix FutureWarning in SpiffWorkflow (#16) git-subtree-dir: SpiffWorkflow git-subtree-split: 8d820dce1f439bb76bc07e39629832d998d6f634 --- SpiffWorkflow/bpmn/parser/BpmnParser.py | 6 +- SpiffWorkflow/bpmn/parser/TaskParser.py | 3 +- SpiffWorkflow/bpmn/parser/event_parsers.py | 2 +- SpiffWorkflow/bpmn/parser/node_parser.py | 3 +- .../serializer/CompactWorkflowSerializer.py | 2 +- SpiffWorkflow/bpmn/serializer/__init__.py | 3 - .../bpmn/serializer/bpmn_converters.py | 6 +- .../bpmn/serializer/task_spec_converters.py | 6 +- SpiffWorkflow/bpmn/specs/BpmnSpecMixin.py | 2 +- SpiffWorkflow/bpmn/specs/ExclusiveGateway.py | 2 +- SpiffWorkflow/bpmn/specs/ManualTask.py | 4 +- SpiffWorkflow/bpmn/specs/MultiInstanceTask.py | 2 +- SpiffWorkflow/bpmn/specs/NoneTask.py | 2 +- SpiffWorkflow/bpmn/specs/SubWorkflowTask.py | 2 +- SpiffWorkflow/bpmn/specs/events/__init__.py | 5 -- SpiffWorkflow/bpmn/workflow.py | 21 +++++- SpiffWorkflow/camunda/parser/CamundaParser.py | 4 +- SpiffWorkflow/camunda/serializer/__init__.py | 2 - .../serializer/task_spec_converters.py | 4 +- SpiffWorkflow/dmn/serializer/__init__.py | 1 - SpiffWorkflow/dmn/specs/BusinessRuleTask.py | 2 +- SpiffWorkflow/serializer/base.py | 65 +++++++++++++++++++ SpiffWorkflow/serializer/dict.py | 29 +++++++-- SpiffWorkflow/serializer/prettyxml.py | 26 +++----- SpiffWorkflow/serializer/xml.py | 46 +++++++------ SpiffWorkflow/signavio/parser/__init__.py | 1 - SpiffWorkflow/specs/SubWorkflow.py | 4 +- SpiffWorkflow/specs/ThreadMerge.py | 2 +- SpiffWorkflow/specs/WorkflowSpec.py | 4 +- SpiffWorkflow/specs/__init__.py | 31 --------- SpiffWorkflow/spiff/parser/__init__.py | 1 - SpiffWorkflow/spiff/parser/process.py | 11 +++- SpiffWorkflow/spiff/serializer/__init__.py | 4 -- .../spiff/serializer/task_spec_converters.py | 13 +++- SpiffWorkflow/spiff/specs/__init__.py | 6 -- SpiffWorkflow/spiff/specs/events/__init__.py | 1 - SpiffWorkflow/workflow.py | 4 +- doc/non-bpmn/tutorial/start.py | 2 +- tests/SpiffWorkflow/PatternTest.py | 2 +- .../SpiffWorkflow/PersistSmallWorkflowTest.py | 4 +- tests/SpiffWorkflow/TaskTest.py | 3 +- tests/SpiffWorkflow/WorkflowTest.py | 4 +- tests/SpiffWorkflow/bpmn/ApprovalsTest.py | 2 +- .../SpiffWorkflow/bpmn/BpmnLoaderForTests.py | 2 +- .../bpmn/BpmnWorkflowSerializerTest.py | 2 +- .../bpmn/BpmnWorkflowTestCase.py | 2 +- .../bpmn/ProcessDependencyTest.py | 2 +- .../bpmn/events/MultipleEventsTest.py | 2 +- .../events/TimerDurationBoundaryOnTaskTest.py | 2 +- tests/SpiffWorkflow/camunda/BaseTestCase.py | 6 +- .../camunda/specs/UserTaskSpecTest.py | 3 +- tests/SpiffWorkflow/data/spiff/workflow1.py | 7 +- tests/SpiffWorkflow/serializer/baseTest.py | 2 +- tests/SpiffWorkflow/specs/CeleryTest.py | 3 +- tests/SpiffWorkflow/specs/ExecuteTest.py | 2 +- tests/SpiffWorkflow/specs/JoinTest.py | 2 +- tests/SpiffWorkflow/specs/MergeTest.py | 4 +- tests/SpiffWorkflow/specs/SubWorkflowTest.py | 2 +- tests/SpiffWorkflow/specs/TaskSpecTest.py | 6 +- tests/SpiffWorkflow/specs/TransformTest.py | 3 +- tests/SpiffWorkflow/specs/WorkflowSpecTest.py | 3 +- tests/SpiffWorkflow/spiff/BaseTestCase.py | 6 +- 62 files changed, 247 insertions(+), 163 deletions(-) diff --git a/SpiffWorkflow/bpmn/parser/BpmnParser.py b/SpiffWorkflow/bpmn/parser/BpmnParser.py index c6ed81bb..e6783e05 100644 --- a/SpiffWorkflow/bpmn/parser/BpmnParser.py +++ b/SpiffWorkflow/bpmn/parser/BpmnParser.py @@ -27,8 +27,10 @@ from SpiffWorkflow.bpmn.specs.events.event_definitions import NoneEventDefinitio from .ValidationException import ValidationException from ..specs.BpmnProcessSpec import BpmnProcessSpec -from ..specs.events import StartEvent, EndEvent, BoundaryEvent, IntermediateCatchEvent, IntermediateThrowEvent -from ..specs.events import SendTask, ReceiveTask +from ..specs.events.EndEvent import EndEvent +from ..specs.events.StartEvent import StartEvent +from ..specs.events.IntermediateEvent import BoundaryEvent, IntermediateCatchEvent, IntermediateThrowEvent +from ..specs.events.IntermediateEvent import SendTask, ReceiveTask from ..specs.SubWorkflowTask import CallActivity, SubWorkflowTask, TransactionSubprocess from ..specs.ExclusiveGateway import ExclusiveGateway from ..specs.InclusiveGateway import InclusiveGateway diff --git a/SpiffWorkflow/bpmn/parser/TaskParser.py b/SpiffWorkflow/bpmn/parser/TaskParser.py index afbd1972..5291b162 100644 --- a/SpiffWorkflow/bpmn/parser/TaskParser.py +++ b/SpiffWorkflow/bpmn/parser/TaskParser.py @@ -23,7 +23,8 @@ from .ValidationException import ValidationException from ..specs.NoneTask import NoneTask from ..specs.ScriptTask import ScriptTask from ..specs.UserTask import UserTask -from ..specs.events import _BoundaryEventParent, CancelEventDefinition +from ..specs.events.IntermediateEvent import _BoundaryEventParent +from ..specs.events.event_definitions import CancelEventDefinition from ..specs.MultiInstanceTask import getDynamicMIClass from ..specs.SubWorkflowTask import CallActivity, TransactionSubprocess, SubWorkflowTask from ..specs.ExclusiveGateway import ExclusiveGateway diff --git a/SpiffWorkflow/bpmn/parser/event_parsers.py b/SpiffWorkflow/bpmn/parser/event_parsers.py index d007fde8..07cef338 100644 --- a/SpiffWorkflow/bpmn/parser/event_parsers.py +++ b/SpiffWorkflow/bpmn/parser/event_parsers.py @@ -5,7 +5,7 @@ from SpiffWorkflow.bpmn.specs.events.event_definitions import CorrelationPropert from .ValidationException import ValidationException from .TaskParser import TaskParser from .util import first, one -from ..specs.events import (TimerEventDefinition, MessageEventDefinition, +from ..specs.events.event_definitions import (TimerEventDefinition, MessageEventDefinition, ErrorEventDefinition, EscalationEventDefinition, SignalEventDefinition, CancelEventDefinition, CycleTimerEventDefinition, diff --git a/SpiffWorkflow/bpmn/parser/node_parser.py b/SpiffWorkflow/bpmn/parser/node_parser.py index b76ccfbb..0b1c8ed4 100644 --- a/SpiffWorkflow/bpmn/parser/node_parser.py +++ b/SpiffWorkflow/bpmn/parser/node_parser.py @@ -35,7 +35,8 @@ class NodeParser: return expression.text if expression is not None else None def parse_documentation(self, sequence_flow=None): - documentation_node = first(self._xpath(sequence_flow or self.node, './/bpmn:documentation')) + node = sequence_flow if sequence_flow is not None else self.node + documentation_node = first(self._xpath(node, './/bpmn:documentation')) return None if documentation_node is None else documentation_node.text def parse_incoming_data_references(self): diff --git a/SpiffWorkflow/bpmn/serializer/CompactWorkflowSerializer.py b/SpiffWorkflow/bpmn/serializer/CompactWorkflowSerializer.py index ac38dc6b..9ed543fb 100644 --- a/SpiffWorkflow/bpmn/serializer/CompactWorkflowSerializer.py +++ b/SpiffWorkflow/bpmn/serializer/CompactWorkflowSerializer.py @@ -23,7 +23,7 @@ from builtins import object from collections import deque import json from ...task import TaskState -from ...specs import SubWorkflow +from ...specs.Subworkflow import SubWorkflow from ...serializer.base import Serializer from ..workflow import BpmnWorkflow diff --git a/SpiffWorkflow/bpmn/serializer/__init__.py b/SpiffWorkflow/bpmn/serializer/__init__.py index 507b6257..9a31a407 100644 --- a/SpiffWorkflow/bpmn/serializer/__init__.py +++ b/SpiffWorkflow/bpmn/serializer/__init__.py @@ -15,6 +15,3 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA - -from .workflow import BpmnWorkflowSerializer -from .bpmn_converters import BpmnDataConverter \ No newline at end of file diff --git a/SpiffWorkflow/bpmn/serializer/bpmn_converters.py b/SpiffWorkflow/bpmn/serializer/bpmn_converters.py index 99666b34..1e274c21 100644 --- a/SpiffWorkflow/bpmn/serializer/bpmn_converters.py +++ b/SpiffWorkflow/bpmn/serializer/bpmn_converters.py @@ -7,9 +7,9 @@ from SpiffWorkflow.bpmn.specs.BpmnProcessSpec import BpmnDataSpecification from .dictionary import DictionaryConverter -from ..specs.events import SignalEventDefinition, MessageEventDefinition, NoneEventDefinition -from ..specs.events import TimerEventDefinition, CycleTimerEventDefinition, TerminateEventDefinition -from ..specs.events import ErrorEventDefinition, EscalationEventDefinition, CancelEventDefinition +from ..specs.events.event_definitions import SignalEventDefinition, MessageEventDefinition, NoneEventDefinition +from ..specs.events.event_definitions import TimerEventDefinition, CycleTimerEventDefinition, TerminateEventDefinition +from ..specs.events.event_definitions import ErrorEventDefinition, EscalationEventDefinition, CancelEventDefinition from ..specs.events.event_definitions import CorrelationProperty, NamedEventDefinition from ..specs.BpmnSpecMixin import BpmnSpecMixin, SequenceFlow diff --git a/SpiffWorkflow/bpmn/serializer/task_spec_converters.py b/SpiffWorkflow/bpmn/serializer/task_spec_converters.py index e2af6b32..852ebe4d 100644 --- a/SpiffWorkflow/bpmn/serializer/task_spec_converters.py +++ b/SpiffWorkflow/bpmn/serializer/task_spec_converters.py @@ -2,7 +2,7 @@ from uuid import UUID from .bpmn_converters import BpmnTaskSpecConverter -from ...specs import StartTask +from ...specs.StartTask import StartTask from ...specs.Simple import Simple from ...specs.LoopResetTask import LoopResetTask @@ -19,7 +19,9 @@ from ..specs.ExclusiveGateway import ExclusiveGateway from ..specs.InclusiveGateway import InclusiveGateway from ..specs.ParallelGateway import ParallelGateway -from ..specs.events import StartEvent, EndEvent, BoundaryEvent, IntermediateCatchEvent, IntermediateThrowEvent +from ..specs.events.StartEvent import StartEvent +from ..specs.events.EndEvent import EndEvent +from ..specs.events.IntermediateEvent import BoundaryEvent, IntermediateCatchEvent, IntermediateThrowEvent from ..specs.events.IntermediateEvent import _BoundaryEventParent, SendTask, ReceiveTask from ..workflow import BpmnWorkflow diff --git a/SpiffWorkflow/bpmn/specs/BpmnSpecMixin.py b/SpiffWorkflow/bpmn/specs/BpmnSpecMixin.py index 3b8f4b26..459a3882 100644 --- a/SpiffWorkflow/bpmn/specs/BpmnSpecMixin.py +++ b/SpiffWorkflow/bpmn/specs/BpmnSpecMixin.py @@ -19,7 +19,7 @@ from ...task import TaskState from ...operators import Operator -from ...specs import TaskSpec +from ...specs.base import TaskSpec class _BpmnCondition(Operator): diff --git a/SpiffWorkflow/bpmn/specs/ExclusiveGateway.py b/SpiffWorkflow/bpmn/specs/ExclusiveGateway.py index 391a2932..89a1445f 100644 --- a/SpiffWorkflow/bpmn/specs/ExclusiveGateway.py +++ b/SpiffWorkflow/bpmn/specs/ExclusiveGateway.py @@ -19,7 +19,7 @@ from ...exceptions import WorkflowException from .BpmnSpecMixin import BpmnSpecMixin -from ...specs import TaskSpec +from ...specs.base import TaskSpec from ...specs.ExclusiveChoice import ExclusiveChoice diff --git a/SpiffWorkflow/bpmn/specs/ManualTask.py b/SpiffWorkflow/bpmn/specs/ManualTask.py index a0283445..c8d4e7c2 100644 --- a/SpiffWorkflow/bpmn/specs/ManualTask.py +++ b/SpiffWorkflow/bpmn/specs/ManualTask.py @@ -18,7 +18,7 @@ # 02110-1301 USA from ...bpmn.specs.BpmnSpecMixin import BpmnSpecMixin -from ...specs import Simple +from ...specs.Simple import Simple class ManualTask(Simple, BpmnSpecMixin): @@ -32,4 +32,4 @@ class ManualTask(Simple, BpmnSpecMixin): @property def spec_type(self): - return 'Manual Task' \ No newline at end of file + return 'Manual Task' diff --git a/SpiffWorkflow/bpmn/specs/MultiInstanceTask.py b/SpiffWorkflow/bpmn/specs/MultiInstanceTask.py index 77886d54..f0ceb3de 100644 --- a/SpiffWorkflow/bpmn/specs/MultiInstanceTask.py +++ b/SpiffWorkflow/bpmn/specs/MultiInstanceTask.py @@ -29,7 +29,7 @@ from .ScriptTask import ScriptTask from .ExclusiveGateway import ExclusiveGateway from ...dmn.specs.BusinessRuleTask import BusinessRuleTask from ...operators import valueof, is_number -from ...specs import SubWorkflow +from ...specs.SubWorkflow import SubWorkflow from ...specs.base import TaskSpec from ...util.impl import get_class from ...task import Task, TaskState diff --git a/SpiffWorkflow/bpmn/specs/NoneTask.py b/SpiffWorkflow/bpmn/specs/NoneTask.py index 28abc80c..5c844e90 100644 --- a/SpiffWorkflow/bpmn/specs/NoneTask.py +++ b/SpiffWorkflow/bpmn/specs/NoneTask.py @@ -16,7 +16,7 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -from ...specs import Simple +from ...specs.Simple import Simple from ...bpmn.specs.BpmnSpecMixin import BpmnSpecMixin diff --git a/SpiffWorkflow/bpmn/specs/SubWorkflowTask.py b/SpiffWorkflow/bpmn/specs/SubWorkflowTask.py index c8b4409e..6f616f1d 100644 --- a/SpiffWorkflow/bpmn/specs/SubWorkflowTask.py +++ b/SpiffWorkflow/bpmn/specs/SubWorkflowTask.py @@ -3,7 +3,7 @@ from copy import deepcopy from SpiffWorkflow.task import TaskState from .BpmnSpecMixin import BpmnSpecMixin -from ...specs import TaskSpec +from ...specs.base import TaskSpec class SubWorkflowTask(BpmnSpecMixin): diff --git a/SpiffWorkflow/bpmn/specs/events/__init__.py b/SpiffWorkflow/bpmn/specs/events/__init__.py index b4d52665..e69de29b 100644 --- a/SpiffWorkflow/bpmn/specs/events/__init__.py +++ b/SpiffWorkflow/bpmn/specs/events/__init__.py @@ -1,5 +0,0 @@ -from .StartEvent import StartEvent -from .EndEvent import EndEvent -from .IntermediateEvent import IntermediateCatchEvent, IntermediateThrowEvent, BoundaryEvent, _BoundaryEventParent, SendTask, ReceiveTask -from .event_definitions import (NoneEventDefinition, CancelEventDefinition, ErrorEventDefinition, EscalationEventDefinition, MessageEventDefinition, - SignalEventDefinition, TimerEventDefinition, CycleTimerEventDefinition, TerminateEventDefinition) \ No newline at end of file diff --git a/SpiffWorkflow/bpmn/workflow.py b/SpiffWorkflow/bpmn/workflow.py index 39c265f9..d9b3994b 100644 --- a/SpiffWorkflow/bpmn/workflow.py +++ b/SpiffWorkflow/bpmn/workflow.py @@ -161,7 +161,9 @@ class BpmnWorkflow(Workflow): event_definition.payload = payload self.catch(event_definition, correlations=correlations) - def do_engine_steps(self, exit_at = None): + def do_engine_steps(self, exit_at = None, + will_complete_task=None, + did_complete_task=None): """ Execute any READY tasks that are engine specific (for example, gateways or script tasks). This is done in a loop, so it will keep completing @@ -169,6 +171,8 @@ class BpmnWorkflow(Workflow): left. :param exit_at: After executing a task with a name matching this param return the task object + :param will_complete_task: Callback that will be called prior to completing a task + :param did_complete_task: Callback that will be called after completing a task """ assert not self.read_only engine_steps = list( @@ -176,21 +180,34 @@ class BpmnWorkflow(Workflow): if self._is_engine_task(t.task_spec)]) while engine_steps: for task in engine_steps: + if will_complete_task is not None: + will_complete_task(task) task.complete() + if did_complete_task is not None: + did_complete_task(task) if task.task_spec.name == exit_at: return task engine_steps = list( [t for t in self.get_tasks(TaskState.READY) if self._is_engine_task(t.task_spec)]) - def refresh_waiting_tasks(self): + def refresh_waiting_tasks(self, + will_refresh_task=None, + did_refresh_task=None): """ Refresh the state of all WAITING tasks. This will, for example, update Catching Timer Events whose waiting time has passed. + + :param will_refresh_task: Callback that will be called prior to refreshing a task + :param did_refresh_task: Callback that will be called after refreshing a task """ assert not self.read_only for my_task in self.get_tasks(TaskState.WAITING): + if will_refresh_task is not None: + will_refresh_task(my_task) my_task.task_spec._update(my_task) + if did_refresh_task is not None: + did_refresh_task(my_task) def get_tasks_from_spec_name(self, name, workflow=None): return [t for t in self.get_tasks(workflow=workflow) if t.task_spec.name == name] diff --git a/SpiffWorkflow/camunda/parser/CamundaParser.py b/SpiffWorkflow/camunda/parser/CamundaParser.py index b9d0dc26..e91be639 100644 --- a/SpiffWorkflow/camunda/parser/CamundaParser.py +++ b/SpiffWorkflow/camunda/parser/CamundaParser.py @@ -6,7 +6,9 @@ from SpiffWorkflow.dmn.parser.BpmnDmnParser import BpmnDmnParser from SpiffWorkflow.dmn.specs.BusinessRuleTask import BusinessRuleTask from SpiffWorkflow.camunda.parser.task_spec import BusinessRuleTaskParser -from SpiffWorkflow.bpmn.specs.events import EndEvent, IntermediateThrowEvent, StartEvent, IntermediateCatchEvent, BoundaryEvent +from SpiffWorkflow.bpmn.specs.events.StartEvent import StartEvent +from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent +from SpiffWorkflow.bpmn.specs.events.IntermediateEvent import IntermediateThrowEvent, IntermediateCatchEvent, BoundaryEvent from .event_parsers import CamundaStartEventParser, CamundaEndEventParser, \ CamundaIntermediateCatchEventParser, CamundaIntermediateThrowEventParser, CamundaBoundaryEventParser diff --git a/SpiffWorkflow/camunda/serializer/__init__.py b/SpiffWorkflow/camunda/serializer/__init__.py index 62e56488..e69de29b 100644 --- a/SpiffWorkflow/camunda/serializer/__init__.py +++ b/SpiffWorkflow/camunda/serializer/__init__.py @@ -1,2 +0,0 @@ -from .task_spec_converters import UserTaskConverter, StartEventConverter, EndEventConverter, \ - BoundaryEventConverter, IntermediateCatchEventConverter, IntermediateThrowEventConverter diff --git a/SpiffWorkflow/camunda/serializer/task_spec_converters.py b/SpiffWorkflow/camunda/serializer/task_spec_converters.py index bb247e33..3f997a0e 100644 --- a/SpiffWorkflow/camunda/serializer/task_spec_converters.py +++ b/SpiffWorkflow/camunda/serializer/task_spec_converters.py @@ -1,6 +1,8 @@ from functools import partial -from SpiffWorkflow.bpmn.specs.events import EndEvent, IntermediateThrowEvent, StartEvent, IntermediateCatchEvent, BoundaryEvent +from SpiffWorkflow.bpmn.specs.events.StartEvent import StartEvent +from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent +from SpiffWorkflow.bpmn.specs.events.IntermediateEvent import IntermediateThrowEvent, IntermediateCatchEvent, BoundaryEvent from ..specs.events.event_definitions import MessageEventDefinition from ...bpmn.serializer.bpmn_converters import BpmnTaskSpecConverter diff --git a/SpiffWorkflow/dmn/serializer/__init__.py b/SpiffWorkflow/dmn/serializer/__init__.py index 9f493c9d..e69de29b 100644 --- a/SpiffWorkflow/dmn/serializer/__init__.py +++ b/SpiffWorkflow/dmn/serializer/__init__.py @@ -1 +0,0 @@ -from .task_spec_converters import BusinessRuleTaskConverter \ No newline at end of file diff --git a/SpiffWorkflow/dmn/specs/BusinessRuleTask.py b/SpiffWorkflow/dmn/specs/BusinessRuleTask.py index 4a9684f2..5825c023 100644 --- a/SpiffWorkflow/dmn/specs/BusinessRuleTask.py +++ b/SpiffWorkflow/dmn/specs/BusinessRuleTask.py @@ -1,6 +1,6 @@ from SpiffWorkflow.bpmn.exceptions import WorkflowTaskExecException -from ...specs import Simple +from ...specs.Simple import Simple from ...bpmn.specs.BpmnSpecMixin import BpmnSpecMixin from ...util.deep_merge import DeepMerge diff --git a/SpiffWorkflow/serializer/base.py b/SpiffWorkflow/serializer/base.py index 28b2e60a..bbf4cc25 100644 --- a/SpiffWorkflow/serializer/base.py +++ b/SpiffWorkflow/serializer/base.py @@ -16,6 +16,71 @@ from builtins import object # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA +import re +from .. import operators +from .. import specs +from ..specs.AcquireMutex import AcquireMutex +from ..specs.Cancel import Cancel +from ..specs.CancelTask import CancelTask +from ..specs.Celery import Celery +from ..specs.Choose import Choose +from ..specs.ExclusiveChoice import ExclusiveChoice +from ..specs.Execute import Execute +from ..specs.Gate import Gate +from ..specs.Join import Join +from ..specs.Merge import Merge +from ..specs.MultiChoice import MultiChoice +from ..specs.MultiInstance import MultiInstance +from ..specs.ReleaseMutex import ReleaseMutex +from ..specs.Simple import Simple +from ..specs.StartTask import StartTask +from ..specs.SubWorkflow import SubWorkflow +from ..specs.ThreadStart import ThreadStart +from ..specs.ThreadMerge import ThreadMerge +from ..specs.ThreadSplit import ThreadSplit +from ..specs.Transform import Transform +from ..specs.Trigger import Trigger +from ..specs.WorkflowSpec import WorkflowSpec +from ..specs.LoopResetTask import LoopResetTask + +# Create a list of tag names out of the spec names. +def spec_map(): + return { + 'acquire-mutex': AcquireMutex, + 'cancel': Cancel, + 'cancel-task': CancelTask, + 'celery': Celery, + 'choose': Choose, + 'exclusive-choice': ExclusiveChoice, + 'execute': Execute, + 'gate': Gate, + 'join': Join, + 'merge': Merge, + 'multi-choice': MultiChoice, + 'multi-instance': MultiInstance, + 'release-mutex': ReleaseMutex, + 'simple': Simple, + 'start-task': StartTask, + 'sub-workflow': SubWorkflow, + 'thread-start': ThreadStart, + 'thread-merge': ThreadMerge, + 'thread-split': ThreadSplit, + 'transform': Transform, + 'trigger': Trigger, + 'workflow-spec': WorkflowSpec, + 'loop-reset-task': LoopResetTask, + 'task': Simple, + } + +def op_map(): + return { + 'equals': operators.Equal, + 'not-equals': operators.NotEqual, + 'less-than': operators.LessThan, + 'greater-than': operators.GreaterThan, + 'matches': operators.Match + } + class Serializer(object): diff --git a/SpiffWorkflow/serializer/dict.py b/SpiffWorkflow/serializer/dict.py index 5688b587..8d1e3ad4 100644 --- a/SpiffWorkflow/serializer/dict.py +++ b/SpiffWorkflow/serializer/dict.py @@ -24,11 +24,30 @@ from ..util.impl import get_class from ..task import Task from ..operators import (Attrib, PathAttrib, Equal, NotEqual, Operator, GreaterThan, LessThan, Match) -from ..specs import (Cancel, AcquireMutex, CancelTask, Celery, Choose, - ExclusiveChoice, Execute, Gate, Join, MultiChoice, - MultiInstance, ReleaseMutex, Simple, WorkflowSpec, - TaskSpec, SubWorkflow, StartTask, ThreadMerge, - ThreadSplit, ThreadStart, Merge, Trigger, LoopResetTask) +from ..specs.base import TaskSpec +from ..specs.AcquireMutex import AcquireMutex +from ..specs.Cancel import Cancel +from ..specs.CancelTask import CancelTask +from ..specs.Celery import Celery +from ..specs.Choose import Choose +from ..specs.ExclusiveChoice import ExclusiveChoice +from ..specs.Execute import Execute +from ..specs.Gate import Gate +from ..specs.Join import Join +from ..specs.Merge import Merge +from ..specs.MultiChoice import MultiChoice +from ..specs.MultiInstance import MultiInstance +from ..specs.ReleaseMutex import ReleaseMutex +from ..specs.Simple import Simple +from ..specs.StartTask import StartTask +from ..specs.SubWorkflow import SubWorkflow +from ..specs.ThreadStart import ThreadStart +from ..specs.ThreadMerge import ThreadMerge +from ..specs.ThreadSplit import ThreadSplit +from ..specs.Transform import Transform +from ..specs.Trigger import Trigger +from ..specs.WorkflowSpec import WorkflowSpec +from ..specs.LoopResetTask import LoopResetTask from .base import Serializer from .exceptions import TaskNotSupportedError, MissingSpecError import warnings diff --git a/SpiffWorkflow/serializer/prettyxml.py b/SpiffWorkflow/serializer/prettyxml.py index 25bec80e..9bd189c5 100644 --- a/SpiffWorkflow/serializer/prettyxml.py +++ b/SpiffWorkflow/serializer/prettyxml.py @@ -18,25 +18,15 @@ # 02110-1301 USA import re import xml.dom.minidom as minidom -from .. import operators, specs +from .. import operators +from ..specs.Simple import Simple +from ..specs.WorkflowSpec import WorkflowSpec from ..exceptions import StorageException -from .base import Serializer +from .base import Serializer, spec_map, op_map # Create a list of tag names out of the spec names. -_spec_map = dict() -for name in dir(specs): - if name.startswith('_'): - continue - module = specs.__dict__[name] - name = re.sub(r'(.)([A-Z])', r'\1-\2', name).lower() - _spec_map[name] = module -_spec_map['task'] = specs.Simple - -_op_map = {'equals': operators.Equal, - 'not-equals': operators.NotEqual, - 'less-than': operators.LessThan, - 'greater-than': operators.GreaterThan, - 'matches': operators.Match} +_spec_map = spec_map() +_op_map = op_map() _exc = StorageException @@ -299,9 +289,9 @@ class XmlSerializer(Serializer): _exc('%s without a name attribute' % node.nodeName) # Read all task specs and create a list of successors. - workflow_spec = specs.WorkflowSpec(name, filename) + workflow_spec = WorkflowSpec(name, filename) del workflow_spec.task_specs['Start'] - end = specs.Simple(workflow_spec, 'End'), [] + end = Simple(workflow_spec, 'End'), [] read_specs = dict(end=end) for child_node in node.childNodes: if child_node.nodeType != minidom.Node.ELEMENT_NODE: diff --git a/SpiffWorkflow/serializer/xml.py b/SpiffWorkflow/serializer/xml.py index f4b18b6c..c0d0572d 100644 --- a/SpiffWorkflow/serializer/xml.py +++ b/SpiffWorkflow/serializer/xml.py @@ -24,29 +24,35 @@ from .. import specs, operators from ..task import Task, TaskStateNames from ..operators import (Attrib, Assign, PathAttrib, Equal, NotEqual, GreaterThan, LessThan, Match) -from ..specs import (Cancel, AcquireMutex, CancelTask, Celery, Choose, - ExclusiveChoice, Execute, Gate, Join, MultiChoice, - MultiInstance, ReleaseMutex, Simple, WorkflowSpec, - SubWorkflow, StartTask, ThreadMerge, - ThreadSplit, ThreadStart, Merge, Trigger, LoopResetTask) -from .base import Serializer +from ..specs.AcquireMutex import AcquireMutex +from ..specs.Cancel import Cancel +from ..specs.CancelTask import CancelTask +from ..specs.Celery import Celery +from ..specs.Choose import Choose +from ..specs.ExclusiveChoice import ExclusiveChoice +from ..specs.Execute import Execute +from ..specs.Gate import Gate +from ..specs.Join import Join +from ..specs.Merge import Merge +from ..specs.MultiChoice import MultiChoice +from ..specs.MultiInstance import MultiInstance +from ..specs.ReleaseMutex import ReleaseMutex +from ..specs.Simple import Simple +from ..specs.StartTask import StartTask +from ..specs.SubWorkflow import SubWorkflow +from ..specs.ThreadStart import ThreadStart +from ..specs.ThreadMerge import ThreadMerge +from ..specs.ThreadSplit import ThreadSplit +from ..specs.Transform import Transform +from ..specs.Trigger import Trigger +from ..specs.WorkflowSpec import WorkflowSpec +from ..specs.LoopResetTask import LoopResetTask +from .base import Serializer, spec_map, op_map from .exceptions import TaskNotSupportedError # Create a list of tag names out of the spec names. -_spec_map = dict() -for name in dir(specs): - if name.startswith('_'): - continue - module = specs.__dict__[name] - name = re.sub(r'(.)([A-Z])', r'\1-\2', name).lower() - _spec_map[name] = module -_spec_map['task'] = specs.Simple - -_op_map = {'equals': operators.Equal, - 'not-equals': operators.NotEqual, - 'less-than': operators.LessThan, - 'greater-than': operators.GreaterThan, - 'matches': operators.Match} +_spec_map = spec_map() +_op_map = op_map() class XmlSerializer(Serializer): diff --git a/SpiffWorkflow/signavio/parser/__init__.py b/SpiffWorkflow/signavio/parser/__init__.py index 477cb987..e69de29b 100644 --- a/SpiffWorkflow/signavio/parser/__init__.py +++ b/SpiffWorkflow/signavio/parser/__init__.py @@ -1 +0,0 @@ -from .tasks import CallActivityParser \ No newline at end of file diff --git a/SpiffWorkflow/specs/SubWorkflow.py b/SpiffWorkflow/specs/SubWorkflow.py index 58d9f779..197a6912 100644 --- a/SpiffWorkflow/specs/SubWorkflow.py +++ b/SpiffWorkflow/specs/SubWorkflow.py @@ -18,7 +18,7 @@ # 02110-1301 USA import os -from . import StartTask +from .StartTask import StartTask from .base import TaskSpec from ..task import TaskState from ..exceptions import WorkflowException @@ -87,7 +87,7 @@ class SubWorkflow(TaskSpec): def _create_subworkflow(self, my_task): from ..serializer.prettyxml import XmlSerializer - from ..specs import WorkflowSpec + from ..specs.WorkflowSpec import WorkflowSpec from ..workflow import Workflow file_name = valueof(my_task, self.file) serializer = XmlSerializer() diff --git a/SpiffWorkflow/specs/ThreadMerge.py b/SpiffWorkflow/specs/ThreadMerge.py index 56e3a4c3..ddbd0d71 100644 --- a/SpiffWorkflow/specs/ThreadMerge.py +++ b/SpiffWorkflow/specs/ThreadMerge.py @@ -19,7 +19,7 @@ from ..task import TaskState from ..exceptions import WorkflowException from ..operators import valueof -from ..specs import Join +from ..specs.Join import Join class ThreadMerge(Join): diff --git a/SpiffWorkflow/specs/WorkflowSpec.py b/SpiffWorkflow/specs/WorkflowSpec.py index 8ea12710..7f084cb3 100644 --- a/SpiffWorkflow/specs/WorkflowSpec.py +++ b/SpiffWorkflow/specs/WorkflowSpec.py @@ -17,7 +17,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -from ..specs import StartTask +from ..specs.StartTask import StartTask class WorkflowSpec(object): @@ -82,7 +82,7 @@ class WorkflowSpec(object): :returns: empty list if valid, a list of errors if not """ results = [] - from ..specs import Join + from ..specs.Join import Join def recursive_find_loop(task, history): current = history[:] diff --git a/SpiffWorkflow/specs/__init__.py b/SpiffWorkflow/specs/__init__.py index 1b10f819..e69de29b 100644 --- a/SpiffWorkflow/specs/__init__.py +++ b/SpiffWorkflow/specs/__init__.py @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -# flake8: noqa - -from .base import TaskSpec -from .AcquireMutex import AcquireMutex -from .Cancel import Cancel -from .CancelTask import CancelTask -from .Celery import Celery -from .Choose import Choose -from .ExclusiveChoice import ExclusiveChoice -from .Execute import Execute -from .Gate import Gate -from .Join import Join -from .Merge import Merge -from .MultiChoice import MultiChoice -from .MultiInstance import MultiInstance -from .ReleaseMutex import ReleaseMutex -from .Simple import Simple -from .StartTask import StartTask -from .SubWorkflow import SubWorkflow -from .ThreadStart import ThreadStart -from .ThreadMerge import ThreadMerge -from .ThreadSplit import ThreadSplit -from .Transform import Transform -from .Trigger import Trigger -from .WorkflowSpec import WorkflowSpec -from .LoopResetTask import LoopResetTask - -import inspect -__all__ = [name for name, obj in list(locals().items()) - if not (name.startswith('_') or inspect.ismodule(obj))] diff --git a/SpiffWorkflow/spiff/parser/__init__.py b/SpiffWorkflow/spiff/parser/__init__.py index 6b38b362..e69de29b 100644 --- a/SpiffWorkflow/spiff/parser/__init__.py +++ b/SpiffWorkflow/spiff/parser/__init__.py @@ -1 +0,0 @@ -from .process import SpiffBpmnParser, VALIDATOR \ No newline at end of file diff --git a/SpiffWorkflow/spiff/parser/process.py b/SpiffWorkflow/spiff/parser/process.py index 7e3dd169..a8238b6b 100644 --- a/SpiffWorkflow/spiff/parser/process.py +++ b/SpiffWorkflow/spiff/parser/process.py @@ -3,8 +3,15 @@ import os from SpiffWorkflow.dmn.parser.BpmnDmnParser import BpmnDmnParser from SpiffWorkflow.bpmn.parser.BpmnParser import BpmnValidator, full_tag -from SpiffWorkflow.bpmn.specs.events import StartEvent, EndEvent, IntermediateThrowEvent, BoundaryEvent, IntermediateCatchEvent -from SpiffWorkflow.spiff.specs import NoneTask, ManualTask, UserTask, ScriptTask, SubWorkflowTask, TransactionSubprocess, CallActivity, ServiceTask +from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent +from SpiffWorkflow.bpmn.specs.events.StartEvent import StartEvent +from SpiffWorkflow.bpmn.specs.events.IntermediateEvent import IntermediateThrowEvent, BoundaryEvent, IntermediateCatchEvent +from SpiffWorkflow.spiff.specs.none_task import NoneTask +from SpiffWorkflow.spiff.specs.manual_task import ManualTask +from SpiffWorkflow.spiff.specs.user_task import UserTask +from SpiffWorkflow.spiff.specs.script_task import ScriptTask +from SpiffWorkflow.spiff.specs.subworkflow_task import SubWorkflowTask, TransactionSubprocess, CallActivity +from SpiffWorkflow.spiff.specs.service_task import ServiceTask from SpiffWorkflow.spiff.specs.events.event_types import SendTask, ReceiveTask from SpiffWorkflow.spiff.parser.task_spec import SpiffTaskParser, SubWorkflowParser, CallActivityParser, ServiceTaskParser, ScriptTaskParser from SpiffWorkflow.spiff.parser.event_parsers import (SpiffStartEventParser, SpiffEndEventParser, SpiffBoundaryEventParser, diff --git a/SpiffWorkflow/spiff/serializer/__init__.py b/SpiffWorkflow/spiff/serializer/__init__.py index 364c3ebf..e69de29b 100644 --- a/SpiffWorkflow/spiff/serializer/__init__.py +++ b/SpiffWorkflow/spiff/serializer/__init__.py @@ -1,4 +0,0 @@ -from .task_spec_converters import NoneTaskConverter, ManualTaskConverter, UserTaskConverter, ScriptTaskConverter -from .task_spec_converters import TransactionSubprocessConverter, CallActivityTaskConverter, SubWorkflowTaskConverter -from .task_spec_converters import StartEventConverter, EndEventConverter, IntermediateCatchEventConverter, IntermediateThrowEventConverter, \ - BoundaryEventConverter, SendTaskConverter, ReceiveTaskConverter, ServiceTaskConverter diff --git a/SpiffWorkflow/spiff/serializer/task_spec_converters.py b/SpiffWorkflow/spiff/serializer/task_spec_converters.py index 5b676968..a1c0525c 100644 --- a/SpiffWorkflow/spiff/serializer/task_spec_converters.py +++ b/SpiffWorkflow/spiff/serializer/task_spec_converters.py @@ -1,9 +1,16 @@ from functools import partial from SpiffWorkflow.bpmn.serializer.bpmn_converters import BpmnTaskSpecConverter -from SpiffWorkflow.bpmn.specs.events import EndEvent, StartEvent, IntermediateThrowEvent, IntermediateCatchEvent, BoundaryEvent -from SpiffWorkflow.spiff.specs import NoneTask, ManualTask, UserTask, ScriptTask, ServiceTask, SubWorkflowTask, TransactionSubprocess, CallActivity -from SpiffWorkflow.spiff.specs.events import SendTask, ReceiveTask +from SpiffWorkflow.bpmn.specs.events.StartEvent import StartEvent +from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent +from SpiffWorkflow.bpmn.specs.events.IntermediateEvent import IntermediateThrowEvent, IntermediateCatchEvent, BoundaryEvent +from SpiffWorkflow.spiff.specs.none_task import NoneTask +from SpiffWorkflow.spiff.specs.manual_task import ManualTask +from SpiffWorkflow.spiff.specs.user_task import UserTask +from SpiffWorkflow.spiff.specs.script_task import ScriptTask +from SpiffWorkflow.spiff.specs.service_task import ServiceTask +from SpiffWorkflow.spiff.specs.subworkflow_task import SubWorkflowTask, TransactionSubprocess, CallActivity +from SpiffWorkflow.spiff.specs.events.event_types import SendTask, ReceiveTask from SpiffWorkflow.spiff.specs.events.event_definitions import MessageEventDefinition diff --git a/SpiffWorkflow/spiff/specs/__init__.py b/SpiffWorkflow/spiff/specs/__init__.py index 1e18b634..e69de29b 100644 --- a/SpiffWorkflow/spiff/specs/__init__.py +++ b/SpiffWorkflow/spiff/specs/__init__.py @@ -1,6 +0,0 @@ -from .manual_task import ManualTask -from .none_task import NoneTask -from .subworkflow_task import SubWorkflowTask, TransactionSubprocess, CallActivity -from .user_task import UserTask -from .script_task import ScriptTask -from .service_task import ServiceTask diff --git a/SpiffWorkflow/spiff/specs/events/__init__.py b/SpiffWorkflow/spiff/specs/events/__init__.py index 2fc0bc11..e69de29b 100644 --- a/SpiffWorkflow/spiff/specs/events/__init__.py +++ b/SpiffWorkflow/spiff/specs/events/__init__.py @@ -1 +0,0 @@ -from .event_types import SendTask, ReceiveTask \ No newline at end of file diff --git a/SpiffWorkflow/workflow.py b/SpiffWorkflow/workflow.py index 376f2a01..906cfa5c 100644 --- a/SpiffWorkflow/workflow.py +++ b/SpiffWorkflow/workflow.py @@ -19,7 +19,7 @@ import logging -from . import specs +from .specs.Simple import Simple from .specs.LoopResetTask import LoopResetTask from .task import Task, TaskState from .util.compat import mutex @@ -61,7 +61,7 @@ class Workflow(object): if 'Root' in workflow_spec.task_specs: root = workflow_spec.task_specs['Root'] else: - root = specs.Simple(workflow_spec, 'Root') + root = Simple(workflow_spec, 'Root') logger.info('Initialize', extra=self.log_info()) # Setting TaskState.COMPLETED prevents the root task from being executed. diff --git a/doc/non-bpmn/tutorial/start.py b/doc/non-bpmn/tutorial/start.py index e52b66c6..4090384c 100644 --- a/doc/non-bpmn/tutorial/start.py +++ b/doc/non-bpmn/tutorial/start.py @@ -1,6 +1,6 @@ import json from SpiffWorkflow.workflow import Workflow -from SpiffWorkflow.specs import WorkflowSpec +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.serializer.json import JSONSerializer # Load from JSON diff --git a/tests/SpiffWorkflow/PatternTest.py b/tests/SpiffWorkflow/PatternTest.py index 8d11528f..f0d013ce 100644 --- a/tests/SpiffWorkflow/PatternTest.py +++ b/tests/SpiffWorkflow/PatternTest.py @@ -6,7 +6,7 @@ import unittest import os sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) -from SpiffWorkflow.specs import WorkflowSpec +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.task import Task from SpiffWorkflow.serializer.prettyxml import XmlSerializer from tests.SpiffWorkflow.util import run_workflow diff --git a/tests/SpiffWorkflow/PersistSmallWorkflowTest.py b/tests/SpiffWorkflow/PersistSmallWorkflowTest.py index c580c87d..b554be18 100644 --- a/tests/SpiffWorkflow/PersistSmallWorkflowTest.py +++ b/tests/SpiffWorkflow/PersistSmallWorkflowTest.py @@ -6,7 +6,9 @@ import os.path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) from SpiffWorkflow.workflow import Workflow -from SpiffWorkflow.specs import Join, MultiChoice, WorkflowSpec +from SpiffWorkflow.specs.Join import Join +from SpiffWorkflow.specs.MultiChoice import MultiChoice +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.operators import Attrib, Equal, PathAttrib from SpiffWorkflow.task import TaskState from SpiffWorkflow.specs.Simple import Simple diff --git a/tests/SpiffWorkflow/TaskTest.py b/tests/SpiffWorkflow/TaskTest.py index af4f28e3..e44f68e6 100644 --- a/tests/SpiffWorkflow/TaskTest.py +++ b/tests/SpiffWorkflow/TaskTest.py @@ -7,7 +7,8 @@ import os.path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) from SpiffWorkflow.task import Task, TaskState, updateDotDict -from SpiffWorkflow.specs import WorkflowSpec, Simple +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec +from SpiffWorkflow.specs.Simple import Simple class MockWorkflow(object): diff --git a/tests/SpiffWorkflow/WorkflowTest.py b/tests/SpiffWorkflow/WorkflowTest.py index e47f069d..0d2b0b3d 100644 --- a/tests/SpiffWorkflow/WorkflowTest.py +++ b/tests/SpiffWorkflow/WorkflowTest.py @@ -7,7 +7,9 @@ data_dir = os.path.join(os.path.dirname(__file__), 'data') sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) from SpiffWorkflow.workflow import Workflow -from SpiffWorkflow.specs import Cancel, Simple, WorkflowSpec +from SpiffWorkflow.specs.Cancel import Cancel +from SpiffWorkflow.specs.Simple import Simple +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.task import TaskState from SpiffWorkflow.serializer.prettyxml import XmlSerializer diff --git a/tests/SpiffWorkflow/bpmn/ApprovalsTest.py b/tests/SpiffWorkflow/bpmn/ApprovalsTest.py index 19f49380..7576a23f 100644 --- a/tests/SpiffWorkflow/bpmn/ApprovalsTest.py +++ b/tests/SpiffWorkflow/bpmn/ApprovalsTest.py @@ -2,7 +2,7 @@ import unittest from SpiffWorkflow.bpmn.workflow import BpmnWorkflow -from SpiffWorkflow.bpmn.specs.events import MessageEventDefinition +from SpiffWorkflow.bpmn.specs.events.event_definitions import MessageEventDefinition from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase __author__ = 'matth' diff --git a/tests/SpiffWorkflow/bpmn/BpmnLoaderForTests.py b/tests/SpiffWorkflow/bpmn/BpmnLoaderForTests.py index 7a407cc7..54c18709 100644 --- a/tests/SpiffWorkflow/bpmn/BpmnLoaderForTests.py +++ b/tests/SpiffWorkflow/bpmn/BpmnLoaderForTests.py @@ -10,7 +10,7 @@ from SpiffWorkflow.bpmn.serializer.bpmn_converters import BpmnTaskSpecConverter # Many of our tests relied on the Packager to set the calledElement attribute on # Call Activities. I've moved that code to a customized parser. -from SpiffWorkflow.signavio.parser import CallActivityParser +from SpiffWorkflow.signavio.parser.tasks import CallActivityParser from SpiffWorkflow.bpmn.specs.SubWorkflowTask import CallActivity __author__ = 'matth' diff --git a/tests/SpiffWorkflow/bpmn/BpmnWorkflowSerializerTest.py b/tests/SpiffWorkflow/bpmn/BpmnWorkflowSerializerTest.py index d0d1a947..ac2ae463 100644 --- a/tests/SpiffWorkflow/bpmn/BpmnWorkflowSerializerTest.py +++ b/tests/SpiffWorkflow/bpmn/BpmnWorkflowSerializerTest.py @@ -6,7 +6,7 @@ from uuid import uuid4 from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine from SpiffWorkflow.bpmn.parser.BpmnParser import BpmnParser -from SpiffWorkflow.bpmn.serializer import BpmnWorkflowSerializer +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer from SpiffWorkflow.bpmn.serializer.BpmnSerializer import BpmnSerializer from SpiffWorkflow.bpmn.workflow import BpmnWorkflow from tests.SpiffWorkflow.bpmn.BpmnLoaderForTests import TestUserTaskConverter diff --git a/tests/SpiffWorkflow/bpmn/BpmnWorkflowTestCase.py b/tests/SpiffWorkflow/bpmn/BpmnWorkflowTestCase.py index 5a341188..e2864919 100644 --- a/tests/SpiffWorkflow/bpmn/BpmnWorkflowTestCase.py +++ b/tests/SpiffWorkflow/bpmn/BpmnWorkflowTestCase.py @@ -7,7 +7,7 @@ from SpiffWorkflow.bpmn.parser.BpmnParser import BpmnValidator from SpiffWorkflow.task import TaskState -from SpiffWorkflow.bpmn.serializer import BpmnWorkflowSerializer +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer from .BpmnLoaderForTests import TestUserTaskConverter, TestBpmnParser __author__ = 'matth' diff --git a/tests/SpiffWorkflow/bpmn/ProcessDependencyTest.py b/tests/SpiffWorkflow/bpmn/ProcessDependencyTest.py index b3a79519..86514a03 100644 --- a/tests/SpiffWorkflow/bpmn/ProcessDependencyTest.py +++ b/tests/SpiffWorkflow/bpmn/ProcessDependencyTest.py @@ -3,7 +3,7 @@ import os import unittest from SpiffWorkflow.camunda.parser.CamundaParser import CamundaParser -from SpiffWorkflow.spiff.parser import SpiffBpmnParser +from SpiffWorkflow.spiff.parser.process import SpiffBpmnParser from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase diff --git a/tests/SpiffWorkflow/bpmn/events/MultipleEventsTest.py b/tests/SpiffWorkflow/bpmn/events/MultipleEventsTest.py index b1e2a24d..86c55235 100644 --- a/tests/SpiffWorkflow/bpmn/events/MultipleEventsTest.py +++ b/tests/SpiffWorkflow/bpmn/events/MultipleEventsTest.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import unittest -from SpiffWorkflow.bpmn.specs.events import CancelEventDefinition, SignalEventDefinition +from SpiffWorkflow.bpmn.specs.events.event_definitions import CancelEventDefinition, SignalEventDefinition from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.workflow import BpmnWorkflow from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase diff --git a/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py b/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py index e87de251..9bd1f322 100644 --- a/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py +++ b/tests/SpiffWorkflow/bpmn/events/TimerDurationBoundaryOnTaskTest.py @@ -5,7 +5,7 @@ import datetime import time from datetime import timedelta -from SpiffWorkflow.bpmn.specs.events import EndEvent +from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent from SpiffWorkflow.task import TaskState from SpiffWorkflow.bpmn.workflow import BpmnWorkflow from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine diff --git a/tests/SpiffWorkflow/camunda/BaseTestCase.py b/tests/SpiffWorkflow/camunda/BaseTestCase.py index 6ea39d0a..b41a8aab 100644 --- a/tests/SpiffWorkflow/camunda/BaseTestCase.py +++ b/tests/SpiffWorkflow/camunda/BaseTestCase.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- import os -from SpiffWorkflow.bpmn.serializer import BpmnWorkflowSerializer +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer from SpiffWorkflow.camunda.parser.CamundaParser import CamundaParser -from SpiffWorkflow.camunda.serializer import UserTaskConverter, StartEventConverter, EndEventConverter, \ +from SpiffWorkflow.camunda.serializer.task_spec_converters import UserTaskConverter, StartEventConverter, EndEventConverter, \ IntermediateCatchEventConverter, IntermediateThrowEventConverter, BoundaryEventConverter -from SpiffWorkflow.dmn.serializer import BusinessRuleTaskConverter +from SpiffWorkflow.dmn.serializer.task_spec_converters import BusinessRuleTaskConverter from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase diff --git a/tests/SpiffWorkflow/camunda/specs/UserTaskSpecTest.py b/tests/SpiffWorkflow/camunda/specs/UserTaskSpecTest.py index 2d3c16d7..33f62191 100644 --- a/tests/SpiffWorkflow/camunda/specs/UserTaskSpecTest.py +++ b/tests/SpiffWorkflow/camunda/specs/UserTaskSpecTest.py @@ -3,7 +3,8 @@ import unittest from SpiffWorkflow.camunda.specs.UserTask import FormField, UserTask, Form, \ EnumFormField -from SpiffWorkflow.specs import WorkflowSpec, TaskSpec +from SpiffWorkflow.specs.base import TaskSpec +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec class UserTaskSpecTest(unittest.TestCase): diff --git a/tests/SpiffWorkflow/data/spiff/workflow1.py b/tests/SpiffWorkflow/data/spiff/workflow1.py index fba204bd..bc963fea 100644 --- a/tests/SpiffWorkflow/data/spiff/workflow1.py +++ b/tests/SpiffWorkflow/data/spiff/workflow1.py @@ -1,6 +1,11 @@ # -*- coding: utf-8 -*- -from SpiffWorkflow.specs import ExclusiveChoice, Join, MultiChoice, MultiInstance, Simple, WorkflowSpec +from SpiffWorkflow.specs.ExclusiveChoice import ExclusiveChoice +from SpiffWorkflow.specs.Join import Join +from SpiffWorkflow.specs.MultiChoice import MultiChoice +from SpiffWorkflow.specs.MultiInstance import MultiInstance +from SpiffWorkflow.specs.Simple import Simple +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.operators import Attrib, Equal, NotEqual diff --git a/tests/SpiffWorkflow/serializer/baseTest.py b/tests/SpiffWorkflow/serializer/baseTest.py index 9bb79b13..7ac41f3c 100644 --- a/tests/SpiffWorkflow/serializer/baseTest.py +++ b/tests/SpiffWorkflow/serializer/baseTest.py @@ -11,7 +11,7 @@ sys.path.insert(0, os.path.join(dirname, '..')) from PatternTest import run_workflow, PatternTest from SpiffWorkflow.serializer.base import Serializer -from SpiffWorkflow.specs import WorkflowSpec +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.workflow import Workflow from SpiffWorkflow.serializer.exceptions import TaskNotSupportedError diff --git a/tests/SpiffWorkflow/specs/CeleryTest.py b/tests/SpiffWorkflow/specs/CeleryTest.py index 9190dd33..ac811688 100644 --- a/tests/SpiffWorkflow/specs/CeleryTest.py +++ b/tests/SpiffWorkflow/specs/CeleryTest.py @@ -6,7 +6,8 @@ import unittest import pickle sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) from .TaskSpecTest import TaskSpecTest -from SpiffWorkflow.specs import Celery, WorkflowSpec +from SpiffWorkflow.specs.Celery import Celery +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.operators import Attrib from SpiffWorkflow.serializer.dict import DictionarySerializer from base64 import b64encode diff --git a/tests/SpiffWorkflow/specs/ExecuteTest.py b/tests/SpiffWorkflow/specs/ExecuteTest.py index bd3b06fd..fc16db93 100644 --- a/tests/SpiffWorkflow/specs/ExecuteTest.py +++ b/tests/SpiffWorkflow/specs/ExecuteTest.py @@ -10,7 +10,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) from tests.SpiffWorkflow.util import run_workflow from .TaskSpecTest import TaskSpecTest from SpiffWorkflow.task import TaskState -from SpiffWorkflow.specs import Execute +from SpiffWorkflow.specs.Execute import Execute class ExecuteTest(TaskSpecTest): diff --git a/tests/SpiffWorkflow/specs/JoinTest.py b/tests/SpiffWorkflow/specs/JoinTest.py index 3eeb9286..7224701f 100644 --- a/tests/SpiffWorkflow/specs/JoinTest.py +++ b/tests/SpiffWorkflow/specs/JoinTest.py @@ -8,7 +8,7 @@ import unittest sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) from .TaskSpecTest import TaskSpecTest -from SpiffWorkflow.specs import Join +from SpiffWorkflow.specs.Join import Join class JoinTest(TaskSpecTest): diff --git a/tests/SpiffWorkflow/specs/MergeTest.py b/tests/SpiffWorkflow/specs/MergeTest.py index 68f028d9..6fc692a6 100644 --- a/tests/SpiffWorkflow/specs/MergeTest.py +++ b/tests/SpiffWorkflow/specs/MergeTest.py @@ -8,7 +8,9 @@ import unittest sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) from .JoinTest import JoinTest -from SpiffWorkflow.specs import Merge, WorkflowSpec, Simple +from SpiffWorkflow.specs.Merge import Merge +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec +from SpiffWorkflow.specs.Simple import Simple from SpiffWorkflow.workflow import Workflow diff --git a/tests/SpiffWorkflow/specs/SubWorkflowTest.py b/tests/SpiffWorkflow/specs/SubWorkflowTest.py index aed09fc2..5baacf99 100644 --- a/tests/SpiffWorkflow/specs/SubWorkflowTest.py +++ b/tests/SpiffWorkflow/specs/SubWorkflowTest.py @@ -6,7 +6,7 @@ import os sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) -from SpiffWorkflow.specs import WorkflowSpec +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.specs.SubWorkflow import SubWorkflow from SpiffWorkflow.serializer.prettyxml import XmlSerializer from SpiffWorkflow.task import TaskState diff --git a/tests/SpiffWorkflow/specs/TaskSpecTest.py b/tests/SpiffWorkflow/specs/TaskSpecTest.py index 2b213b4a..2509d618 100644 --- a/tests/SpiffWorkflow/specs/TaskSpecTest.py +++ b/tests/SpiffWorkflow/specs/TaskSpecTest.py @@ -5,9 +5,11 @@ import unittest import os sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) -from SpiffWorkflow.specs import WorkflowSpec, Simple, Join +from SpiffWorkflow.specs.Join import Join +from SpiffWorkflow.specs.Simple import Simple +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.exceptions import WorkflowException -from SpiffWorkflow.specs import TaskSpec +from SpiffWorkflow.specs.base import TaskSpec from SpiffWorkflow.serializer.dict import DictionarySerializer diff --git a/tests/SpiffWorkflow/specs/TransformTest.py b/tests/SpiffWorkflow/specs/TransformTest.py index ca2aaf1c..228a038e 100644 --- a/tests/SpiffWorkflow/specs/TransformTest.py +++ b/tests/SpiffWorkflow/specs/TransformTest.py @@ -9,7 +9,8 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) from tests.SpiffWorkflow.util import run_workflow from .TaskSpecTest import TaskSpecTest -from SpiffWorkflow.specs import Transform, Simple +from SpiffWorkflow.specs.Transform import Transform +from SpiffWorkflow.specs.Simple import Simple class TransformTest(TaskSpecTest): diff --git a/tests/SpiffWorkflow/specs/WorkflowSpecTest.py b/tests/SpiffWorkflow/specs/WorkflowSpecTest.py index 8b0289b8..9b4c0d32 100644 --- a/tests/SpiffWorkflow/specs/WorkflowSpecTest.py +++ b/tests/SpiffWorkflow/specs/WorkflowSpecTest.py @@ -15,7 +15,8 @@ try: except ImportError as e: from tests.SpiffWorkflow.util import track_workflow from SpiffWorkflow.workflow import Workflow -from SpiffWorkflow.specs import Join, WorkflowSpec +from SpiffWorkflow.specs.Join import Join +from SpiffWorkflow.specs.WorkflowSpec import WorkflowSpec from SpiffWorkflow.serializer.prettyxml import XmlSerializer serializer = XmlSerializer() diff --git a/tests/SpiffWorkflow/spiff/BaseTestCase.py b/tests/SpiffWorkflow/spiff/BaseTestCase.py index 9e882fcb..b085d1f7 100644 --- a/tests/SpiffWorkflow/spiff/BaseTestCase.py +++ b/tests/SpiffWorkflow/spiff/BaseTestCase.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- import os -from SpiffWorkflow.spiff.parser import SpiffBpmnParser, VALIDATOR -from SpiffWorkflow.spiff.serializer import NoneTaskConverter, \ +from SpiffWorkflow.spiff.parser.process import SpiffBpmnParser, VALIDATOR +from SpiffWorkflow.spiff.serializer.task_spec_converters import NoneTaskConverter, \ ManualTaskConverter, UserTaskConverter, ScriptTaskConverter, \ SubWorkflowTaskConverter, TransactionSubprocessConverter, \ CallActivityTaskConverter, \ @@ -11,7 +11,7 @@ from SpiffWorkflow.spiff.serializer import NoneTaskConverter, \ IntermediateCatchEventConverter, IntermediateThrowEventConverter, \ ServiceTaskConverter from SpiffWorkflow.dmn.serializer.task_spec_converters import BusinessRuleTaskConverter -from SpiffWorkflow.bpmn.serializer import BpmnWorkflowSerializer +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer from tests.SpiffWorkflow.bpmn.BpmnWorkflowTestCase import BpmnWorkflowTestCase From a0b923c9ad98d07d3cf46dca689a01b13d41aa02 Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 4 Nov 2022 09:33:44 -0400 Subject: [PATCH 27/46] Squashed 'spiffworkflow-backend/' changes from dba09086ba..5c6601237e 5c6601237e upgrade spiff and add commented out assertion 150cb68824 Change steps when viewing a process instance model (#18) beac7f40c4 prevent backfill from exploding when a primary file name on another model does not exist 3e6b61911e work around parser.get_process_dependencies returning a set containing the element None 65c343337d Point back to spiff main (#19) 7eafd5c994 Track spiff step details more granularly (#17) 85829dd56d Clear the remaining __init__.py imports in SpiffWorkflow (#14) c3468ca548 From the logs, allow viewing a diagram in a previous state (#15) git-subtree-dir: spiffworkflow-backend git-subtree-split: 5c6601237ebdccf864b23dac74bf3e1ca77ead1e --- .../{bdd1d64689db_.py => b1647eff45c9_.py} | 20 ++- poetry.lock | 40 +++-- src/spiffworkflow_backend/api.yml | 6 + .../load_database_models.py | 3 + .../models/process_instance.py | 3 + .../models/spiff_logging.py | 1 + .../models/spiff_step_details.py | 23 +++ .../routes/process_api_blueprint.py | 17 ++- .../scripts/get_current_user.py | 4 +- .../scripts/get_process_info.py | 2 +- .../services/logging_service.py | 8 + .../services/process_instance_processor.py | 138 ++++++++++++++---- .../services/spec_file_service.py | 9 +- .../integration/test_process_api.py | 2 + .../unit/test_spiff_logging.py | 1 + 15 files changed, 219 insertions(+), 58 deletions(-) rename migrations/versions/{bdd1d64689db_.py => b1647eff45c9_.py} (95%) create mode 100644 src/spiffworkflow_backend/models/spiff_step_details.py diff --git a/migrations/versions/bdd1d64689db_.py b/migrations/versions/b1647eff45c9_.py similarity index 95% rename from migrations/versions/bdd1d64689db_.py rename to migrations/versions/b1647eff45c9_.py index 55566149..d6ff25e3 100644 --- a/migrations/versions/bdd1d64689db_.py +++ b/migrations/versions/b1647eff45c9_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: bdd1d64689db +Revision ID: b1647eff45c9 Revises: -Create Date: 2022-11-02 11:31:50.606843 +Create Date: 2022-11-02 14:25:09.992800 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'bdd1d64689db' +revision = 'b1647eff45c9' down_revision = None branch_labels = None depends_on = None @@ -106,6 +106,7 @@ def upgrade(): sa.Column('status', sa.String(length=50), nullable=True), sa.Column('bpmn_version_control_type', sa.String(length=50), nullable=True), sa.Column('bpmn_version_control_identifier', sa.String(length=255), nullable=True), + sa.Column('spiff_step', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['process_initiator_id'], ['user.id'], ), sa.PrimaryKeyConstraint('id') ) @@ -229,10 +230,22 @@ def upgrade(): sa.Column('timestamp', sa.DECIMAL(precision=17, scale=6), nullable=False), sa.Column('message', sa.String(length=255), nullable=True), sa.Column('current_user_id', sa.Integer(), nullable=True), + sa.Column('spiff_step', sa.Integer(), nullable=False), sa.ForeignKeyConstraint(['current_user_id'], ['user.id'], ), sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), sa.PrimaryKeyConstraint('id') ) + op.create_table('spiff_step_details', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('process_instance_id', sa.Integer(), nullable=False), + sa.Column('spiff_step', sa.Integer(), nullable=False), + sa.Column('task_json', sa.JSON(), nullable=False), + sa.Column('timestamp', sa.DECIMAL(precision=17, scale=6), nullable=False), + sa.Column('completed_by_user_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['completed_by_user_id'], ['user.id'], ), + sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), + sa.PrimaryKeyConstraint('id') + ) op.create_table('active_task_user', sa.Column('id', sa.Integer(), nullable=False), sa.Column('active_task_id', sa.Integer(), nullable=False), @@ -266,6 +279,7 @@ def downgrade(): op.drop_index(op.f('ix_active_task_user_user_id'), table_name='active_task_user') op.drop_index(op.f('ix_active_task_user_active_task_id'), table_name='active_task_user') op.drop_table('active_task_user') + op.drop_table('spiff_step_details') op.drop_table('spiff_logging') op.drop_table('permission_assignment') op.drop_table('message_instance') diff --git a/poetry.lock b/poetry.lock index c474c696..8b1e9cb7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -95,7 +95,7 @@ python-versions = ">=3.5" dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] -tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] +tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] [[package]] name = "Babel" @@ -268,7 +268,7 @@ optional = false python-versions = ">=3.6.0" [package.extras] -unicode-backport = ["unicodedata2"] +unicode_backport = ["unicodedata2"] [[package]] name = "classify-imports" @@ -1512,7 +1512,7 @@ urllib3 = ">=1.21.1,<1.27" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-toolbelt" @@ -1625,7 +1625,7 @@ falcon = ["falcon (>=1.4)"] fastapi = ["fastapi (>=0.79.0)"] flask = ["blinker (>=1.1)", "flask (>=0.11)"] httpx = ["httpx (>=0.16.0)"] -pure-eval = ["asttokens", "executing", "pure-eval"] +pure_eval = ["asttokens", "executing", "pure-eval"] pyspark = ["pyspark (>=2.4.4)"] quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] rq = ["rq (>=0.6)"] @@ -1873,7 +1873,7 @@ pytz = "*" type = "git" url = "https://github.com/sartography/SpiffWorkflow" reference = "main" -resolved_reference = "a6392d19061f623394f5705fb78af23673d3940d" +resolved_reference = "8d820dce1f439bb76bc07e39629832d998d6f634" [[package]] name = "SQLAlchemy" @@ -1891,19 +1891,19 @@ aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] asyncio = ["greenlet (!=0.4.17)"] asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"] +mariadb_connector = ["mariadb (>=1.0.1,!=1.1.2)"] mssql = ["pyodbc"] -mssql-pymssql = ["pymssql"] -mssql-pyodbc = ["pyodbc"] +mssql_pymssql = ["pymssql"] +mssql_pyodbc = ["pyodbc"] mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] -mysql-connector = ["mysql-connector-python"] +mysql_connector = ["mysql-connector-python"] oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"] postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] -postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] -postgresql-psycopg2binary = ["psycopg2-binary"] -postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql_asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql_pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] +postgresql_psycopg2binary = ["psycopg2-binary"] +postgresql_psycopg2cffi = ["psycopg2cffi"] pymysql = ["pymysql", "pymysql (<1)"] sqlcipher = ["sqlcipher3_binary"] @@ -2946,10 +2946,7 @@ orjson = [ {file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b68a42a31f8429728183c21fb440c21de1b62e5378d0d73f280e2d894ef8942e"}, {file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ff13410ddbdda5d4197a4a4c09969cb78c722a67550f0a63c02c07aadc624833"}, {file = "orjson-3.8.0-cp310-none-win_amd64.whl", hash = "sha256:2d81e6e56bbea44be0222fb53f7b255b4e7426290516771592738ca01dbd053b"}, - {file = "orjson-3.8.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:200eae21c33f1f8b02a11f5d88d76950cd6fd986d88f1afe497a8ae2627c49aa"}, - {file = "orjson-3.8.0-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:9529990f3eab54b976d327360aa1ff244a4b12cb5e4c5b3712fcdd96e8fe56d4"}, {file = "orjson-3.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e2defd9527651ad39ec20ae03c812adf47ef7662bdd6bc07dabb10888d70dc62"}, - {file = "orjson-3.8.0-cp311-none-win_amd64.whl", hash = "sha256:b21c7af0ff6228ca7105f54f0800636eb49201133e15ddb80ac20c1ce973ef07"}, {file = "orjson-3.8.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9e6ac22cec72d5b39035b566e4b86c74b84866f12b5b0b6541506a080fb67d6d"}, {file = "orjson-3.8.0-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:e2f4a5542f50e3d336a18cb224fc757245ca66b1fd0b70b5dd4471b8ff5f2b0e"}, {file = "orjson-3.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1418feeb8b698b9224b1f024555895169d481604d5d884498c1838d7412794c"}, @@ -3062,7 +3059,18 @@ py = [ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] pyasn1 = [ + {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, + {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, + {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, + {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, + {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, + {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, + {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, + {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, + {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, + {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, + {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, ] pycodestyle = [ diff --git a/src/spiffworkflow_backend/api.yml b/src/spiffworkflow_backend/api.yml index 614d4f26..0f0a49c2 100755 --- a/src/spiffworkflow_backend/api.yml +++ b/src/spiffworkflow_backend/api.yml @@ -1000,6 +1000,12 @@ paths: description: If true, this wil return all tasks associated with the process instance and not just user tasks. schema: type: boolean + - name: spiff_step + in: query + required: false + description: If set will return the tasks as they were during a specific step of execution. + schema: + type: integer get: tags: - Process Instances diff --git a/src/spiffworkflow_backend/load_database_models.py b/src/spiffworkflow_backend/load_database_models.py index 7283b19b..14dcac0d 100644 --- a/src/spiffworkflow_backend/load_database_models.py +++ b/src/spiffworkflow_backend/load_database_models.py @@ -46,6 +46,9 @@ from spiffworkflow_backend.models.process_instance_report import ( from spiffworkflow_backend.models.refresh_token import RefreshTokenModel # noqa: F401 from spiffworkflow_backend.models.secret_model import SecretModel # noqa: F401 from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel # noqa: F401 +from spiffworkflow_backend.models.spiff_step_details import ( + SpiffStepDetailsModel, +) # noqa: F401 from spiffworkflow_backend.models.user import UserModel # noqa: F401 from spiffworkflow_backend.models.group import GroupModel # noqa: F401 diff --git a/src/spiffworkflow_backend/models/process_instance.py b/src/spiffworkflow_backend/models/process_instance.py index 1c2098e9..50c3c9f7 100644 --- a/src/spiffworkflow_backend/models/process_instance.py +++ b/src/spiffworkflow_backend/models/process_instance.py @@ -81,6 +81,7 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): spiff_logs = relationship("SpiffLoggingModel", cascade="delete") # type: ignore message_instances = relationship("MessageInstanceModel", cascade="delete") # type: ignore message_correlations = relationship("MessageCorrelationModel", cascade="delete") # type: ignore + spiff_step_details = relationship("SpiffStepDetailsModel", cascade="delete") # type: ignore bpmn_json: str | None = deferred(db.Column(db.JSON)) # type: ignore start_in_seconds: int | None = db.Column(db.Integer) @@ -92,6 +93,7 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): bpmn_xml_file_contents: bytes | None = None bpmn_version_control_type: str = db.Column(db.String(50)) bpmn_version_control_identifier: str = db.Column(db.String(255)) + spiff_step: int = db.Column(db.Integer) @property def serialized(self) -> dict[str, Any]: @@ -110,6 +112,7 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): "end_in_seconds": self.end_in_seconds, "process_initiator_id": self.process_initiator_id, "bpmn_xml_file_contents": local_bpmn_xml_file_contents, + "spiff_step": self.spiff_step, } @property diff --git a/src/spiffworkflow_backend/models/spiff_logging.py b/src/spiffworkflow_backend/models/spiff_logging.py index a655ec51..58f13cd4 100644 --- a/src/spiffworkflow_backend/models/spiff_logging.py +++ b/src/spiffworkflow_backend/models/spiff_logging.py @@ -25,3 +25,4 @@ class SpiffLoggingModel(SpiffworkflowBaseDBModel): timestamp: float = db.Column(db.DECIMAL(17, 6), nullable=False) message: Optional[str] = db.Column(db.String(255), nullable=True) current_user_id: int = db.Column(ForeignKey(UserModel.id), nullable=True) + spiff_step: int = db.Column(db.Integer, nullable=False) diff --git a/src/spiffworkflow_backend/models/spiff_step_details.py b/src/spiffworkflow_backend/models/spiff_step_details.py new file mode 100644 index 00000000..1706c2e9 --- /dev/null +++ b/src/spiffworkflow_backend/models/spiff_step_details.py @@ -0,0 +1,23 @@ +"""Spiff_step_details.""" +from dataclasses import dataclass + +from flask_bpmn.models.db import db +from flask_bpmn.models.db import SpiffworkflowBaseDBModel +from sqlalchemy import ForeignKey +from sqlalchemy.orm import deferred + +from spiffworkflow_backend.models.process_instance import ProcessInstanceModel +from spiffworkflow_backend.models.user import UserModel + + +@dataclass +class SpiffStepDetailsModel(SpiffworkflowBaseDBModel): + """SpiffStepDetailsModel.""" + + __tablename__ = "spiff_step_details" + id: int = db.Column(db.Integer, primary_key=True) + process_instance_id: int = db.Column(ForeignKey(ProcessInstanceModel.id), nullable=False) # type: ignore + spiff_step: int = db.Column(db.Integer, nullable=False) + task_json: str | None = deferred(db.Column(db.JSON, nullable=False)) # type: ignore + timestamp: float = db.Column(db.DECIMAL(17, 6), nullable=False) + completed_by_user_id: int = db.Column(ForeignKey(UserModel.id), nullable=True) diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index 0523ad25..41cd9d99 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -56,6 +56,7 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema from spiffworkflow_backend.models.secret_model import SecretModel from spiffworkflow_backend.models.secret_model import SecretModelSchema from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel +from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.routes.user import verify_token from spiffworkflow_backend.services.authorization_service import AuthorizationService @@ -954,10 +955,23 @@ def task_list_my_tasks(page: int = 1, per_page: int = 100) -> flask.wrappers.Res def process_instance_task_list( - process_instance_id: int, all_tasks: bool = False + process_instance_id: int, all_tasks: bool = False, spiff_step: int = 0 ) -> flask.wrappers.Response: """Process_instance_task_list.""" process_instance = find_process_instance_by_id_or_raise(process_instance_id) + + if spiff_step > 0: + step_detail = ( + db.session.query(SpiffStepDetailsModel) + .filter( + SpiffStepDetailsModel.process_instance_id == process_instance.id, + SpiffStepDetailsModel.spiff_step == spiff_step, + ) + .first() + ) + if step_detail is not None: + process_instance.bpmn_json = json.dumps(step_detail.task_json) + processor = ProcessInstanceProcessor(process_instance) spiff_tasks = None @@ -1233,6 +1247,7 @@ def script_unit_test_run( """Script_unit_test_run.""" # FIXME: We should probably clear this somewhere else but this works current_app.config["THREAD_LOCAL_DATA"].process_instance_id = None + current_app.config["THREAD_LOCAL_DATA"].spiff_step = None python_script = _get_required_parameter_or_raise("python_script", body) input_json = _get_required_parameter_or_raise("input_json", body) diff --git a/src/spiffworkflow_backend/scripts/get_current_user.py b/src/spiffworkflow_backend/scripts/get_current_user.py index b8c314c0..a1a1b47e 100644 --- a/src/spiffworkflow_backend/scripts/get_current_user.py +++ b/src/spiffworkflow_backend/scripts/get_current_user.py @@ -9,8 +9,8 @@ from spiffworkflow_backend.models.script_attributes_context import ( from spiffworkflow_backend.scripts.script import Script -class GetUser(Script): - """GetUser.""" +class GetCurrentUser(Script): + """GetCurrentUser.""" def get_description(self) -> str: """Get_description.""" diff --git a/src/spiffworkflow_backend/scripts/get_process_info.py b/src/spiffworkflow_backend/scripts/get_process_info.py index b8ad3d68..45c70d6b 100644 --- a/src/spiffworkflow_backend/scripts/get_process_info.py +++ b/src/spiffworkflow_backend/scripts/get_process_info.py @@ -8,7 +8,7 @@ from spiffworkflow_backend.scripts.script import Script class GetProcessInfo(Script): - """GetUser.""" + """GetProcessInfo.""" def get_description(self) -> str: """Get_description.""" diff --git a/src/spiffworkflow_backend/services/logging_service.py b/src/spiffworkflow_backend/services/logging_service.py index ce30e8b9..13f66e00 100644 --- a/src/spiffworkflow_backend/services/logging_service.py +++ b/src/spiffworkflow_backend/services/logging_service.py @@ -108,6 +108,8 @@ class SpiffFilter(logging.Filter): if hasattr(tld, "process_instance_id"): process_instance_id = tld.process_instance_id setattr(record, "process_instance_id", process_instance_id) # noqa: B010 + if hasattr(tld, "spiff_step"): + setattr(record, "spiff_step", tld.spiff_step) # noqa: 8010 if hasattr(g, "user") and g.user: setattr(record, "current_user_id", g.user.id) # noqa: B010 return True @@ -204,6 +206,11 @@ class DBHandler(logging.Handler): timestamp = record.created message = record.msg if hasattr(record, "msg") else None current_user_id = record.current_user_id if hasattr(record, "current_user_id") else None # type: ignore + spiff_step = ( + record.spiff_step # type: ignore + if hasattr(record, "spiff_step") and record.spiff_step is not None # type: ignore + else 1 + ) spiff_log = SpiffLoggingModel( process_instance_id=record.process_instance_id, # type: ignore bpmn_process_identifier=bpmn_process_identifier, @@ -214,6 +221,7 @@ class DBHandler(logging.Handler): message=message, timestamp=timestamp, current_user_id=current_user_id, + spiff_step=spiff_step, ) db.session.add(spiff_log) db.session.commit() diff --git a/src/spiffworkflow_backend/services/process_instance_processor.py b/src/spiffworkflow_backend/services/process_instance_processor.py index fecde1b9..35320f65 100644 --- a/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/src/spiffworkflow_backend/services/process_instance_processor.py @@ -29,31 +29,39 @@ from SpiffWorkflow.bpmn.exceptions import WorkflowTaskExecException # type: ign from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException # type: ignore from SpiffWorkflow.bpmn.PythonScriptEngine import Box # type: ignore from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine -from SpiffWorkflow.bpmn.serializer import BpmnWorkflowSerializer # type: ignore +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore from SpiffWorkflow.bpmn.specs.BpmnProcessSpec import BpmnProcessSpec # type: ignore -from SpiffWorkflow.bpmn.specs.events import CancelEventDefinition # type: ignore -from SpiffWorkflow.bpmn.specs.events import EndEvent +from SpiffWorkflow.bpmn.specs.events.EndEvent import EndEvent # type: ignore +from SpiffWorkflow.bpmn.specs.events.event_definitions import CancelEventDefinition # type: ignore from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore from SpiffWorkflow.dmn.parser.BpmnDmnParser import BpmnDmnParser # type: ignore -from SpiffWorkflow.dmn.serializer import BusinessRuleTaskConverter # type: ignore +from SpiffWorkflow.dmn.serializer.task_spec_converters import BusinessRuleTaskConverter # type: ignore from SpiffWorkflow.exceptions import WorkflowException # type: ignore from SpiffWorkflow.serializer.exceptions import MissingSpecError # type: ignore from SpiffWorkflow.spiff.parser.process import SpiffBpmnParser # type: ignore -from SpiffWorkflow.spiff.serializer import BoundaryEventConverter # type: ignore -from SpiffWorkflow.spiff.serializer import CallActivityTaskConverter -from SpiffWorkflow.spiff.serializer import EndEventConverter -from SpiffWorkflow.spiff.serializer import IntermediateCatchEventConverter -from SpiffWorkflow.spiff.serializer import IntermediateThrowEventConverter -from SpiffWorkflow.spiff.serializer import ManualTaskConverter -from SpiffWorkflow.spiff.serializer import NoneTaskConverter -from SpiffWorkflow.spiff.serializer import ReceiveTaskConverter -from SpiffWorkflow.spiff.serializer import ScriptTaskConverter -from SpiffWorkflow.spiff.serializer import SendTaskConverter -from SpiffWorkflow.spiff.serializer import ServiceTaskConverter -from SpiffWorkflow.spiff.serializer import StartEventConverter -from SpiffWorkflow.spiff.serializer import SubWorkflowTaskConverter -from SpiffWorkflow.spiff.serializer import TransactionSubprocessConverter -from SpiffWorkflow.spiff.serializer import UserTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import BoundaryEventConverter # type: ignore +from SpiffWorkflow.spiff.serializer.task_spec_converters import ( + CallActivityTaskConverter, +) +from SpiffWorkflow.spiff.serializer.task_spec_converters import EndEventConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import ( + IntermediateCatchEventConverter, +) +from SpiffWorkflow.spiff.serializer.task_spec_converters import ( + IntermediateThrowEventConverter, +) +from SpiffWorkflow.spiff.serializer.task_spec_converters import ManualTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import NoneTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import ReceiveTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import ScriptTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import SendTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import ServiceTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import StartEventConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import SubWorkflowTaskConverter +from SpiffWorkflow.spiff.serializer.task_spec_converters import ( + TransactionSubprocessConverter, +) +from SpiffWorkflow.spiff.serializer.task_spec_converters import UserTaskConverter from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import TaskState from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore @@ -79,12 +87,16 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.script_attributes_context import ( ScriptAttributesContext, ) +from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user import UserModelSchema from spiffworkflow_backend.scripts.script import Script from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.service_task_service import ServiceTaskDelegate +from spiffworkflow_backend.services.spec_file_service import ( + ProcessModelFileNotFoundError, +) from spiffworkflow_backend.services.spec_file_service import SpecFileService from spiffworkflow_backend.services.user_service import UserService @@ -276,9 +288,9 @@ class ProcessInstanceProcessor: self, process_instance_model: ProcessInstanceModel, validate_only: bool = False ) -> None: """Create a Workflow Processor based on the serialized information available in the process_instance model.""" - current_app.config[ - "THREAD_LOCAL_DATA" - ].process_instance_id = process_instance_model.id + tld = current_app.config["THREAD_LOCAL_DATA"] + tld.process_instance_id = process_instance_model.id + tld.spiff_step = process_instance_model.spiff_step # we want this to be the fully qualified path to the process model including all group subcomponents current_app.config["THREAD_LOCAL_DATA"].process_model_identifier = ( @@ -411,10 +423,8 @@ class ProcessInstanceProcessor: bpmn_process_spec, subprocesses ) - def add_user_info_to_process_instance( - self, bpmn_process_instance: BpmnWorkflow - ) -> None: - """Add_user_info_to_process_instance.""" + def current_user(self) -> Any: + """Current_user.""" current_user = None if UserService.has_user(): current_user = UserService.current_user() @@ -425,6 +435,14 @@ class ProcessInstanceProcessor: elif self.process_instance_model.process_initiator_id: current_user = self.process_instance_model.process_initiator + return current_user + + def add_user_info_to_process_instance( + self, bpmn_process_instance: BpmnWorkflow + ) -> None: + """Add_user_info_to_process_instance.""" + current_user = self.current_user() + if current_user: current_user_data = UserModelSchema().dump(current_user) tasks = bpmn_process_instance.get_tasks(TaskState.READY) @@ -542,9 +560,31 @@ class ProcessInstanceProcessor: "lane_assignment_id": lane_assignment_id, } + def save_spiff_step_details(self) -> None: + """SaveSpiffStepDetails.""" + bpmn_json = self.serialize() + wf_json = json.loads(bpmn_json) + task_json = "{}" + if "tasks" in wf_json: + task_json = json.dumps(wf_json["tasks"]) + + # TODO want to just save the tasks, something wasn't immediately working + # so after the flow works with the full wf_json revisit this + task_json = wf_json + details_model = SpiffStepDetailsModel( + process_instance_id=self.process_instance_model.id, + spiff_step=self.process_instance_model.spiff_step or 1, + task_json=task_json, + timestamp=round(time.time()), + completed_by_user_id=self.current_user().id, + ) + db.session.add(details_model) + db.session.commit() + def save(self) -> None: """Saves the current state of this processor to the database.""" self.process_instance_model.bpmn_json = self.serialize() + complete_states = [TaskState.CANCELLED, TaskState.COMPLETED] user_tasks = list(self.get_all_user_tasks()) self.process_instance_model.status = self.get_status().value @@ -632,10 +672,14 @@ class ProcessInstanceProcessor: process_models = ProcessModelService().get_process_models() for process_model in process_models: if process_model.primary_file_name: - etree_element = SpecFileService.get_etree_element_from_file_name( - process_model, process_model.primary_file_name - ) - bpmn_process_identifiers = [] + try: + etree_element = SpecFileService.get_etree_element_from_file_name( + process_model, process_model.primary_file_name + ) + bpmn_process_identifiers = [] + except ProcessModelFileNotFoundError: + # if primary_file_name doesn't actually exist on disk, then just go on to the next process_model + continue try: bpmn_process_identifiers = ( @@ -663,6 +707,11 @@ class ProcessInstanceProcessor: bpmn_process_identifier: str, ) -> str: """Bpmn_file_full_path_from_bpmn_process_identifier.""" + if bpmn_process_identifier is None: + raise ValueError( + "bpmn_file_full_path_from_bpmn_process_identifier: bpmn_process_identifier is unexpectedly None" + ) + bpmn_process_id_lookup = BpmnProcessIdLookup.query.filter_by( bpmn_process_identifier=bpmn_process_identifier ).first() @@ -695,6 +744,10 @@ class ProcessInstanceProcessor: if processed_identifiers is None: processed_identifiers = set() processor_dependencies = parser.get_process_dependencies() + + # since get_process_dependencies() returns a set with None sometimes, we need to remove it + processor_dependencies = processor_dependencies - {None} + processor_dependencies_new = processor_dependencies - processed_identifiers bpmn_process_identifiers_in_parser = parser.get_process_ids() @@ -930,11 +983,29 @@ class ProcessInstanceProcessor: db.session.commit() + def increment_spiff_step(self) -> None: + """Spiff_step++.""" + spiff_step = self.process_instance_model.spiff_step or 0 + spiff_step += 1 + self.process_instance_model.spiff_step = spiff_step + current_app.config["THREAD_LOCAL_DATA"].spiff_step = spiff_step + db.session.add(self.process_instance_model) + db.session.commit() + def do_engine_steps(self, exit_at: None = None, save: bool = False) -> None: """Do_engine_steps.""" try: - self.bpmn_process_instance.refresh_waiting_tasks() - self.bpmn_process_instance.do_engine_steps(exit_at=exit_at) + self.bpmn_process_instance.refresh_waiting_tasks( + will_refresh_task=lambda t: self.increment_spiff_step(), + did_refresh_task=lambda t: self.save_spiff_step_details(), + ) + + self.bpmn_process_instance.do_engine_steps( + exit_at=exit_at, + will_complete_task=lambda t: self.increment_spiff_step(), + did_complete_task=lambda t: self.save_spiff_step_details(), + ) + self.process_bpmn_messages() self.queue_waiting_receive_messages() @@ -956,6 +1027,7 @@ class ProcessInstanceProcessor: # A little hackly, but make the bpmn_process_instance catch a cancel event. bpmn_process_instance.signal("cancel") # generate a cancel signal. bpmn_process_instance.catch(CancelEventDefinition()) + # Due to this being static, can't save granular step details in this case bpmn_process_instance.do_engine_steps() except WorkflowTaskExecException as we: raise ApiError.from_workflow_exception("task_error", str(we), we) from we @@ -1054,7 +1126,9 @@ class ProcessInstanceProcessor: def complete_task(self, task: SpiffTask) -> None: """Complete_task.""" + self.increment_spiff_step() self.bpmn_process_instance.complete_task_from_id(task.id) + self.save_spiff_step_details() def get_data(self) -> dict[str, Any]: """Get_data.""" diff --git a/src/spiffworkflow_backend/services/spec_file_service.py b/src/spiffworkflow_backend/services/spec_file_service.py index 4e1e30e2..eb2a322f 100644 --- a/src/spiffworkflow_backend/services/spec_file_service.py +++ b/src/spiffworkflow_backend/services/spec_file_service.py @@ -27,6 +27,10 @@ from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.process_model_service import ProcessModelService +class ProcessModelFileNotFoundError(Exception): + """ProcessModelFileNotFoundError.""" + + class SpecFileService(FileSystemService): """SpecFileService.""" @@ -90,9 +94,8 @@ class SpecFileService(FileSystemService): """Get_data.""" file_path = SpecFileService.file_path(process_model_info, file_name) if not os.path.exists(file_path): - raise ApiError( - "unknown_file", - f"No file found with name {file_name} in {process_model_info.display_name}", + raise ProcessModelFileNotFoundError( + f"No file found with name {file_name} in {process_model_info.display_name}" ) with open(file_path, "rb") as f_handle: spec_file_data = f_handle.read() diff --git a/tests/spiffworkflow_backend/integration/test_process_api.py b/tests/spiffworkflow_backend/integration/test_process_api.py index 9a923b97..7ac6b923 100644 --- a/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1119,6 +1119,8 @@ class TestProcessApi(BaseTest): ) assert response.json is not None + # assert response.json['next_task'] is not None + active_tasks = ( db.session.query(ActiveTaskModel) .filter(ActiveTaskModel.process_instance_id == process_instance_id) diff --git a/tests/spiffworkflow_backend/unit/test_spiff_logging.py b/tests/spiffworkflow_backend/unit/test_spiff_logging.py index c4a5984f..d8680b71 100644 --- a/tests/spiffworkflow_backend/unit/test_spiff_logging.py +++ b/tests/spiffworkflow_backend/unit/test_spiff_logging.py @@ -36,6 +36,7 @@ class TestSpiffLogging(BaseTest): bpmn_task_identifier=bpmn_task_identifier, message=message, timestamp=timestamp, + spiff_step=1, ) assert spiff_log.timestamp == timestamp From fbebc727257a57ad5505e19d0aaecdfd4a9166c8 Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 4 Nov 2022 09:33:46 -0400 Subject: [PATCH 28/46] Squashed 'spiffworkflow-frontend/' changes from ca2bc557f1..1df0c1a9b2 1df0c1a9b2 Change steps when viewing a process instance model (#18) b471cbaa77 Prettier 01fc31aa27 From the logs, allow viewing a diagram in a previous state (#15) git-subtree-dir: spiffworkflow-frontend git-subtree-split: 1df0c1a9b2af5e22343da13f78e585096f877c65 --- src/routes/AdminRoutes.tsx | 4 ++ src/routes/ProcessInstanceLogList.tsx | 11 +++- src/routes/ProcessInstanceShow.tsx | 80 +++++++++++++++++++++++++-- 3 files changed, 88 insertions(+), 7 deletions(-) diff --git a/src/routes/AdminRoutes.tsx b/src/routes/AdminRoutes.tsx index c3e39e16..776a5f34 100644 --- a/src/routes/AdminRoutes.tsx +++ b/src/routes/AdminRoutes.tsx @@ -76,6 +76,10 @@ export default function AdminRoutes() { path="process-models/:process_group_id/:process_model_id/process-instances/:process_instance_id" element={} /> + } + /> } diff --git a/src/routes/ProcessInstanceLogList.tsx b/src/routes/ProcessInstanceLogList.tsx index f2d297f0..83e56ac5 100644 --- a/src/routes/ProcessInstanceLogList.tsx +++ b/src/routes/ProcessInstanceLogList.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; import { Table } from 'react-bootstrap'; -import { useParams, useSearchParams } from 'react-router-dom'; +import { useParams, useSearchParams, Link } from 'react-router-dom'; import PaginationForTable from '../components/PaginationForTable'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import { @@ -39,7 +39,14 @@ export default function ProcessInstanceLogList() { {rowToUse.bpmn_task_name} {rowToUse.bpmn_task_type} {rowToUse.username} - {convertSecondsToFormattedDate(rowToUse.timestamp)} + + + {convertSecondsToFormattedDate(rowToUse.timestamp)} + + ); }); diff --git a/src/routes/ProcessInstanceShow.tsx b/src/routes/ProcessInstanceShow.tsx index f5336df8..642c7d65 100644 --- a/src/routes/ProcessInstanceShow.tsx +++ b/src/routes/ProcessInstanceShow.tsx @@ -32,10 +32,16 @@ export default function ProcessInstanceShow() { path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/${params.process_instance_id}`, successCallback: setProcessInstance, }); - HttpService.makeCallToBackend({ - path: `/process-instance/${params.process_instance_id}/tasks?all_tasks=true`, - successCallback: setTasks, - }); + if (typeof params.spiff_step === 'undefined') + HttpService.makeCallToBackend({ + path: `/process-instance/${params.process_instance_id}/tasks?all_tasks=true`, + successCallback: setTasks, + }); + else + HttpService.makeCallToBackend({ + path: `/process-instance/${params.process_instance_id}/tasks?all_tasks=true&spiff_step=${params.spiff_step}`, + successCallback: setTasks, + }); }, [params]); const deleteProcessInstance = () => { @@ -90,6 +96,62 @@ export default function ProcessInstanceShow() { return taskIds; }; + const currentSpiffStep = (processInstanceToUse: any) => { + if (typeof params.spiff_step === 'undefined') { + return processInstanceToUse.spiff_step; + } + + return Number(params.spiff_step); + }; + + const showingFirstSpiffStep = (processInstanceToUse: any) => { + return currentSpiffStep(processInstanceToUse) === 1; + }; + + const showingLastSpiffStep = (processInstanceToUse: any) => { + return ( + currentSpiffStep(processInstanceToUse) === processInstanceToUse.spiff_step + ); + }; + + const spiffStepLink = ( + processInstanceToUse: any, + label: string, + distance: number + ) => { + return ( +
  • + + {label} + +
  • + ); + }; + + const previousStepLink = (processInstanceToUse: any) => { + if (showingFirstSpiffStep(processInstanceToUse)) { + return null; + } + + return spiffStepLink(processInstanceToUse, 'Previous Step', -1); + }; + + const nextStepLink = (processInstanceToUse: any) => { + if (showingLastSpiffStep(processInstanceToUse)) { + return null; + } + + return spiffStepLink(processInstanceToUse, 'Next Step', 1); + }; + const getInfoTag = (processInstanceToUse: any) => { const currentEndDate = convertSecondsToFormattedDate( processInstanceToUse.end_in_seconds @@ -129,6 +191,12 @@ export default function ProcessInstanceShow() { Messages +
  • + Step {currentSpiffStep(processInstanceToUse)} of{' '} + {processInstanceToUse.spiff_step} +
  • + {previousStepLink(processInstanceToUse)} + {nextStepLink(processInstanceToUse)} ); }; @@ -228,7 +296,9 @@ export default function ProcessInstanceShow() { }; const canEditTaskData = (task: any) => { - return task.state === 'READY'; + return ( + task.state === 'READY' && showingLastSpiffStep(processInstance as any) + ); }; const cancelEditingTaskData = () => { From edfa95124a718a2e7211757c0973f534cdc142cd Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 4 Nov 2022 11:03:40 -0400 Subject: [PATCH 29/46] pyl is passing w/ burnettk --- .../models/process_group.py | 3 ++- .../models/process_model.py | 1 - .../routes/process_api_blueprint.py | 21 ++++++++------- .../services/process_model_service.py | 2 -- .../integration/test_process_api.py | 15 +++++------ .../src/routes/ProcessGroupList.tsx | 27 ++++++++++++++++--- 6 files changed, 43 insertions(+), 26 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_group.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_group.py index 549ab008..381f8f63 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_group.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_group.py @@ -1,7 +1,7 @@ """Process_group.""" from __future__ import annotations -import dataclasses +import dataclasses from dataclasses import dataclass from dataclasses import field from typing import Any @@ -42,6 +42,7 @@ class ProcessGroup: @property def serialized(self) -> dict: + """Serialized.""" original_dict = dataclasses.asdict(self) return {x: original_dict[x] for x in original_dict if x not in ["sort_index"]} diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py index 9558a79b..1928f312 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py @@ -30,7 +30,6 @@ class ProcessModelInfo: display_name: str description: str process_group_id: str = "" - process_group: Any | None = None primary_file_name: str | None = None primary_process_id: str | None = None display_order: int | None = 0 diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index 63c717d1..65fc299b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -43,7 +43,8 @@ from spiffworkflow_backend.models.message_triggerable_process_model import ( MessageTriggerableProcessModel, ) from spiffworkflow_backend.models.principal import PrincipalModel -from spiffworkflow_backend.models.process_group import ProcessGroup, ProcessGroupSchema +from spiffworkflow_backend.models.process_group import ProcessGroup +from spiffworkflow_backend.models.process_group import ProcessGroupSchema from spiffworkflow_backend.models.process_instance import ProcessInstanceApiSchema from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModelSchema @@ -134,9 +135,7 @@ def permissions_check(body: Dict[str, Dict[str, list[str]]]) -> flask.wrappers.R return make_response(jsonify({"results": response_dict}), 200) -def process_group_add( - body: dict -) -> flask.wrappers.Response: +def process_group_add(body: dict) -> flask.wrappers.Response: """Add_process_group.""" process_model_service = ProcessModelService() process_group = ProcessGroup(**body) @@ -150,11 +149,16 @@ def process_group_delete(process_group_id: str) -> flask.wrappers.Response: return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") -def process_group_update( - process_group_id: str, body: dict -) -> flask.wrappers.Response: +def process_group_update(process_group_id: str, body: dict) -> flask.wrappers.Response: """Process Group Update.""" - process_group = ProcessGroup(id=process_group_id, **body) + body_include_list = ["display_name", "description"] + body_filtered = { + include_item: body[include_item] + for include_item in body_include_list + if include_item in body + } + + process_group = ProcessGroup(id=process_group_id, **body_filtered) ProcessModelService().update_process_group(process_group) return make_response(jsonify(process_group), 200) @@ -221,7 +225,6 @@ def process_model_add( status_code=400, ) - process_model_info.process_group = process_group process_model_service.add_spec(process_model_info) return Response( json.dumps(ProcessModelInfoSchema().dump(process_model_info)), diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py index 7789493f..e10092d5 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py @@ -1,5 +1,4 @@ """Process_model_service.""" -import dataclasses import json import os import shutil @@ -275,6 +274,5 @@ class ProcessModelService(FileSystemService): with open(spec_path, "w") as wf_json: json.dump(self.WF_SCHEMA.dump(spec), wf_json, indent=4) if process_group: - spec.process_group = process_group spec.process_group_id = process_group.id return spec diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py index 6fe38f40..9b8b75da 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1,5 +1,4 @@ """Test Process Api Blueprint.""" -import dataclasses import io import json import time @@ -9,7 +8,6 @@ import pytest from flask.app import Flask from flask.testing import FlaskClient from flask_bpmn.models.db import db -from spiffworkflow_backend import MyJSONEncoder from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec @@ -19,7 +17,6 @@ from spiffworkflow_backend.exceptions.process_entity_not_found_error import ( from spiffworkflow_backend.models.active_task import ActiveTaskModel from spiffworkflow_backend.models.group import GroupModel from spiffworkflow_backend.models.process_group import ProcessGroup -# from spiffworkflow_backend.models.process_group import ProcessGroupSchema from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus from spiffworkflow_backend.models.process_instance_report import ( @@ -390,14 +387,13 @@ class TestProcessApi(BaseTest): display_name="Another Test Category", display_order=0, admin=False, - description="Test Description" + description="Test Description", ) response = client.post( "/v1.0/process-groups", headers=self.logged_in_headers(with_super_admin_user), content_type="application/json", data=json.dumps(process_group.serialized), - ) assert response.status_code == 201 assert response.json @@ -468,7 +464,7 @@ class TestProcessApi(BaseTest): f"/v1.0/process-groups/{group_id}", headers=self.logged_in_headers(with_super_admin_user), content_type="application/json", - data=json.dumps(ProcessGroupSchema().dump(process_group)), + data=json.dumps(process_group.serialized), ) assert response.status_code == 200 @@ -795,6 +791,7 @@ class TestProcessApi(BaseTest): f"/v1.0/process-groups/{test_process_group_id}", headers=self.logged_in_headers(with_super_admin_user), ) + assert response.status_code == 200 assert response.json is not None assert response.json["id"] == test_process_group_id @@ -1304,7 +1301,7 @@ class TestProcessApi(BaseTest): # start > 2000, end < 5000 - this should eliminate the first 2 and the last response = client.get( - "/v1.0/process-instances?start_from=2001&end_till=5999", + "/v1.0/process-instances?start_from=2001&end_to=5999", headers=self.logged_in_headers(with_super_admin_user), ) assert response.json is not None @@ -1315,7 +1312,7 @@ class TestProcessApi(BaseTest): # start > 1000, start < 4000 - this should eliminate the first and the last 2 response = client.get( - "/v1.0/process-instances?start_from=1001&start_till=3999", + "/v1.0/process-instances?start_from=1001&start_to=3999", headers=self.logged_in_headers(with_super_admin_user), ) assert response.json is not None @@ -1326,7 +1323,7 @@ class TestProcessApi(BaseTest): # end > 2000, end < 6000 - this should eliminate the first and the last response = client.get( - "/v1.0/process-instances?end_from=2001&end_till=5999", + "/v1.0/process-instances?end_from=2001&end_to=5999", headers=self.logged_in_headers(with_super_admin_user), ) assert response.json is not None diff --git a/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx b/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx index bb19e939..3e396448 100644 --- a/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx @@ -1,12 +1,20 @@ import { useEffect, useState } from 'react'; import { Link, useNavigate, useSearchParams } from 'react-router-dom'; -// @ts-ignore -import { Button, Table } from '@carbon/react'; +import { + Button, + Table, + ExpandableTile, + TileAboveTheFoldContent, + TileBelowTheFoldContent, + TextInput, + ClickableTile, + // @ts-ignore +} from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import PaginationForTable from '../components/PaginationForTable'; import HttpService from '../services/HttpService'; import { getPageInfoFromSearchParams } from '../helpers'; -import { CarbonComboBoxSelection } from '../interfaces'; +import { CarbonComboBoxSelection, ProcessGroup } from '../interfaces'; import ProcessModelSearch from '../components/ProcessModelSearch'; // Example process group json @@ -49,7 +57,7 @@ export default function ProcessGroupList() { }, [searchParams]); const buildTable = () => { - const rows = processGroups.map((row) => { + const rows = processGroups.map((row: ProcessGroup) => { return ( @@ -73,6 +81,17 @@ export default function ProcessGroupList() { {rows} ); + // const rows = processGroups.map((row: ProcessGroup) => { + // return ( + // + // + // {row.display_name} + // + // + // ); + // }); + // + // return
    {rows}
    ; }; const processGroupsDisplayArea = () => { From 79c0ffa6e1e14df9f191dc11fdb65075c6f4bbe8 Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 4 Nov 2022 12:20:55 -0400 Subject: [PATCH 30/46] process group cypress tests are passing w/ burnettk --- spiffworkflow-frontend/cypress.config.js | 5 +++ .../cypress/e2e/process_groups.cy.js | 4 +-- .../cypress/support/commands.js | 19 +++++----- spiffworkflow-frontend/package-lock.json | 12 +++---- .../src/components/ButtonWithConfirmation.tsx | 8 +---- .../src/components/NavigationBar.tsx | 12 +++++-- .../src/components/PaginationForTable.tsx | 36 ++++++++++--------- .../src/components/ProcessGroupForm.tsx | 5 ++- .../src/routes/ProcessGroupList.tsx | 10 +++--- 9 files changed, 61 insertions(+), 50 deletions(-) diff --git a/spiffworkflow-frontend/cypress.config.js b/spiffworkflow-frontend/cypress.config.js index c4bf94c4..1c8c1c6e 100644 --- a/spiffworkflow-frontend/cypress.config.js +++ b/spiffworkflow-frontend/cypress.config.js @@ -10,4 +10,9 @@ module.exports = defineConfig({ // implement node event listeners here }, }, + + // this scrolls away from the elements for some reason with carbon when set to top + // https://github.com/cypress-io/cypress/issues/2353 + // https://docs.cypress.io/guides/core-concepts/interacting-with-elements#Scrolling + scrollBehavior: "center", }); diff --git a/spiffworkflow-frontend/cypress/e2e/process_groups.cy.js b/spiffworkflow-frontend/cypress/e2e/process_groups.cy.js index 1f2c675e..4860a68b 100644 --- a/spiffworkflow-frontend/cypress/e2e/process_groups.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/process_groups.cy.js @@ -30,9 +30,9 @@ describe('process-groups', () => { newGroupDisplayName ); - cy.contains('Delete').click(); + cy.contains('Delete Process Group').click(); cy.contains('Are you sure'); - cy.contains('OK').click(); + cy.getBySel('modal-confirmation-dialog').get('.cds--btn--danger').click(); cy.url().should('include', `process-groups`); cy.contains(groupId).should('not.exist'); }); diff --git a/spiffworkflow-frontend/cypress/support/commands.js b/spiffworkflow-frontend/cypress/support/commands.js index 1b9c9241..e6d801d8 100644 --- a/spiffworkflow-frontend/cypress/support/commands.js +++ b/spiffworkflow-frontend/cypress/support/commands.js @@ -101,18 +101,15 @@ Cypress.Commands.add( ); Cypress.Commands.add('basicPaginationTest', () => { - cy.get('#pagination-page-dropdown') - .type('typing_to_open_dropdown_box....FIXME') - .find('.dropdown-item') - .contains(/^2$/) - .click(); + cy.getBySel('pagination-options').scrollIntoView(); + cy.get('.cds--select__item-count').find('.cds--select-input').select('2'); - cy.contains(/^1-2 of \d+$/); - cy.getBySel('pagination-previous-button-inactive'); - cy.getBySel('pagination-next-button').click(); - cy.contains(/^3-4 of \d+$/); - cy.getBySel('pagination-previous-button').click(); - cy.contains(/^1-2 of \d+$/); + // NOTE: this is a em dash instead of en dash + cy.contains(/\b1–2 of \d+/); + cy.get('.cds--pagination__button--forward').click(); + cy.contains(/\b3–4 of \d+/); + cy.get('.cds--pagination__button--backward').click(); + cy.contains(/\b1–2 of \d+/); }); Cypress.Commands.add('assertAtLeastOneItemInPaginatedResults', () => { diff --git a/spiffworkflow-frontend/package-lock.json b/spiffworkflow-frontend/package-lock.json index a72fc406..a08036b5 100644 --- a/spiffworkflow-frontend/package-lock.json +++ b/spiffworkflow-frontend/package-lock.json @@ -9193,9 +9193,9 @@ "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==" }, "node_modules/cypress": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.8.0.tgz", - "integrity": "sha512-QVse0dnLm018hgti2enKMVZR9qbIO488YGX06nH5j3Dg1isL38DwrBtyrax02CANU6y8F4EJUuyW6HJKw1jsFA==", + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.11.0.tgz", + "integrity": "sha512-lsaE7dprw5DoXM00skni6W5ElVVLGAdRUUdZjX2dYsGjbY/QnpzWZ95Zom1mkGg0hAaO/QVTZoFVS7Jgr/GUPA==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -36484,9 +36484,9 @@ "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==" }, "cypress": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.8.0.tgz", - "integrity": "sha512-QVse0dnLm018hgti2enKMVZR9qbIO488YGX06nH5j3Dg1isL38DwrBtyrax02CANU6y8F4EJUuyW6HJKw1jsFA==", + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.11.0.tgz", + "integrity": "sha512-lsaE7dprw5DoXM00skni6W5ElVVLGAdRUUdZjX2dYsGjbY/QnpzWZ95Zom1mkGg0hAaO/QVTZoFVS7Jgr/GUPA==", "dev": true, "requires": { "@cypress/request": "^2.88.10", diff --git a/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx b/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx index f8e8bf77..31a8e5ef 100644 --- a/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx +++ b/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx @@ -26,13 +26,6 @@ export default function ButtonWithConfirmation({ setShowConfirmationPrompt(false); }; - const modalBodyElement = () => { - if (description) { - return {description}; - } - return null; - }; - const handleConfirmation = () => { onConfirmation(); setShowConfirmationPrompt(false); @@ -43,6 +36,7 @@ export default function ButtonWithConfirmation({ {UserService.getUsername()} - + ); } return ( - + ); diff --git a/spiffworkflow-frontend/src/components/PaginationForTable.tsx b/spiffworkflow-frontend/src/components/PaginationForTable.tsx index d9d94b84..3b65c78c 100644 --- a/spiffworkflow-frontend/src/components/PaginationForTable.tsx +++ b/spiffworkflow-frontend/src/components/PaginationForTable.tsx @@ -35,20 +35,24 @@ export default function PaginationForTable({ navigate(`${path}?page=${newPage}&per_page=${pageSize}${queryParamString}`); }; - return ( - <> - {tableToDisplay} - - - ); + if (pagination) { + return ( + <> + {tableToDisplay} + + + ); + } + return null; } diff --git a/spiffworkflow-frontend/src/components/ProcessGroupForm.tsx b/spiffworkflow-frontend/src/components/ProcessGroupForm.tsx index 21cabc93..99db3434 100644 --- a/spiffworkflow-frontend/src/components/ProcessGroupForm.tsx +++ b/spiffworkflow-frontend/src/components/ProcessGroupForm.tsx @@ -105,6 +105,7 @@ export default function ProcessGroupForm({ const textInputs = [ @@ -159,7 +162,7 @@ export default function ProcessGroupForm({ ); diff --git a/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx b/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx index 3e396448..13f3d455 100644 --- a/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx @@ -3,11 +3,11 @@ import { Link, useNavigate, useSearchParams } from 'react-router-dom'; import { Button, Table, - ExpandableTile, - TileAboveTheFoldContent, - TileBelowTheFoldContent, - TextInput, - ClickableTile, + // ExpandableTile, + // TileAboveTheFoldContent, + // TileBelowTheFoldContent, + // TextInput, + // ClickableTile, // @ts-ignore } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; From 08bfbccffb398ffb36962bf280b61961c0700d7f Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 4 Nov 2022 12:48:05 -0400 Subject: [PATCH 31/46] process model cypress tests are passing w/ burnettk --- .../cypress/e2e/process_models.cy.js | 12 ++--- .../src/components/ButtonWithConfirmation.tsx | 21 +-------- .../src/components/ProcessModelSearch.tsx | 1 + .../src/components/ReactDiagramEditor.tsx | 3 +- .../src/routes/ProcessModelEditDiagram.tsx | 44 ++++++++---------- .../src/routes/ReactFormEditor.tsx | 46 ++++++++----------- 6 files changed, 47 insertions(+), 80 deletions(-) diff --git a/spiffworkflow-frontend/cypress/e2e/process_models.cy.js b/spiffworkflow-frontend/cypress/e2e/process_models.cy.js index 3b28514b..48b65d5d 100644 --- a/spiffworkflow-frontend/cypress/e2e/process_models.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/process_models.cy.js @@ -99,7 +99,7 @@ describe('process-models', () => { cy.contains(/^Process Model File$/); // Some reason, cypress evals json strings so we have to escape it it with '{{}' cy.get('.view-line').type('{{} "test_key": "test_value" }'); - cy.contains('Save').click(); + cy.getBySel('file-save-button').click(); cy.get('input[name=file_name]').type(jsonFileName); cy.contains('Save Changes').click(); cy.contains(`Process Model File: ${jsonFileName}`); @@ -168,12 +168,8 @@ describe('process-models', () => { }); it('can allow searching for model', () => { - cy.get('[name=process-model-selection]').click(); - cy.get('[name=process-model-selection]').type('model-3'); - cy.get( - `[aria-label="acceptance-tests-group-one/acceptance-tests-model-3"]` - ).click(); - - cy.contains('Process Instances').click(); + cy.getBySel('process-model-selection').click().type('model-3'); + cy.contains('acceptance-tests-group-one/acceptance-tests-model-3').click(); + cy.contains('List').click(); }); }); diff --git a/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx b/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx index 31a8e5ef..25aaadc1 100644 --- a/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx +++ b/spiffworkflow-frontend/src/components/ButtonWithConfirmation.tsx @@ -45,30 +45,11 @@ export default function ButtonWithConfirmation({ onRequestSubmit={handleConfirmation} /> ); - // return ( - // - // - // {title} - // - // {modalBodyElement()} - // - // - // - // - // - // ); }; return ( <> - {confirmationDialog()} diff --git a/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx b/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx index 1f138f6e..98419d6c 100644 --- a/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx +++ b/spiffworkflow-frontend/src/components/ProcessModelSearch.tsx @@ -29,6 +29,7 @@ export default function ProcessModelSearch({ { if (processModel) { diff --git a/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx b/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx index 9728e7d5..2bbf64db 100644 --- a/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx +++ b/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx @@ -18,7 +18,8 @@ import { } from 'dmn-js-properties-panel'; import React, { useRef, useEffect, useState } from 'react'; -import Button from 'react-bootstrap/Button'; +// @ts-ignore +import { Button } from '@carbon/react'; import 'bpmn-js/dist/assets/diagram-js.css'; import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'; diff --git a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx index 787db12c..f6fb211f 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx @@ -194,31 +194,25 @@ export default function ProcessModelEditDiagram() { const newFileNameBox = () => { const fileExtension = `.${searchParams.get('file_type')}`; return ( - - - Process Model File Name - -
    - - - setNewFileName(e.target.value)} - autoFocus - /> - {fileExtension} - - - - - -
    + + + + setNewFileName(e.target.value)} + autoFocus + /> + {fileExtension} + ); }; diff --git a/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx b/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx index 2b92ea19..e706a3ef 100644 --- a/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx +++ b/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx @@ -119,31 +119,25 @@ export default function ReactFormEditor() { const newFileNameBox = () => { return ( - - - Process Model File Name - -
    - - - setNewFileName(e.target.value)} - autoFocus - /> - .{fileExtension} - - - - - -
    + + + + setNewFileName(e.target.value)} + autoFocus + /> + {fileExtension} + ); }; @@ -161,7 +155,7 @@ export default function ReactFormEditor() { {processModelFile ? `: ${(processModelFile as any).name}` : ''} {newFileNameBox()} - {params.file_name ? ( From bac7dbcac1b6b1751cd076e00109407ee6b18368 Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 4 Nov 2022 15:07:40 -0400 Subject: [PATCH 32/46] instances tests are mostly passing, issues with table resizing on the list w/ burnettk --- .../cypress/e2e/process_groups.cy.js | 2 +- .../cypress/e2e/process_instances.cy.js | 10 +- .../cypress/support/commands.js | 2 +- .../src/components/NavigationBar.tsx | 79 +++++--- spiffworkflow-frontend/src/index.css | 4 + spiffworkflow-frontend/src/index.scss | 2 + .../src/routes/ProcessInstanceList.tsx | 188 +++++++++++++----- .../src/routes/ProcessModelEditDiagram.tsx | 40 ++-- 8 files changed, 229 insertions(+), 98 deletions(-) diff --git a/spiffworkflow-frontend/cypress/e2e/process_groups.cy.js b/spiffworkflow-frontend/cypress/e2e/process_groups.cy.js index 4860a68b..9e974084 100644 --- a/spiffworkflow-frontend/cypress/e2e/process_groups.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/process_groups.cy.js @@ -32,7 +32,7 @@ describe('process-groups', () => { cy.contains('Delete Process Group').click(); cy.contains('Are you sure'); - cy.getBySel('modal-confirmation-dialog').get('.cds--btn--danger').click(); + cy.getBySel('modal-confirmation-dialog').find('.cds--btn--danger').click(); cy.url().should('include', `process-groups`); cy.contains(groupId).should('not.exist'); }); diff --git a/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js b/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js index d5383d5a..a2fcb266 100644 --- a/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js @@ -48,7 +48,7 @@ const updateBpmnPythonScriptWithMonaco = ( .focused() // change subject to currently focused element // .type('{ctrl}a') // had been doing it this way, but it turns out to be flaky relative to clear() .clear() - .type(pythonScript); + .type(pythonScript, { delay: 30 }); cy.contains('Close').click(); // wait for a little bit for the xml to get set before saving @@ -111,8 +111,10 @@ describe('process-instances', () => { }); it('can create a new instance and can modify with monaco text editor', () => { - const originalPythonScript = 'person = "Kevin"'; - const newPythonScript = 'person = "Mike"'; + // leave off the ending double quote since manco adds it + const originalPythonScript = 'person = "Kevin'; + const newPythonScript = 'person = "Mike'; + const bpmnFile = 'process_model_one.bpmn'; // Change bpmn @@ -152,7 +154,7 @@ describe('process-instances', () => { cy.basicPaginationTest(); }); - it('can filter', () => { + it.only('can filter', () => { cy.getBySel('process-instance-list-link').click(); cy.assertAtLeastOneItemInPaginatedResults(); diff --git a/spiffworkflow-frontend/cypress/support/commands.js b/spiffworkflow-frontend/cypress/support/commands.js index e6d801d8..3a1a0ac4 100644 --- a/spiffworkflow-frontend/cypress/support/commands.js +++ b/spiffworkflow-frontend/cypress/support/commands.js @@ -35,7 +35,7 @@ Cypress.Commands.add('navigateToHome', () => { }); Cypress.Commands.add('navigateToAdmin', () => { - cy.getBySel('spiffworkflow-logo').click(); + cy.visit('/admin'); }); Cypress.Commands.add('login', (selector, ...args) => { diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index 8dd0ed6b..4dd81f9a 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -1,6 +1,14 @@ import { Header, + HeaderContainer, + HeaderMenuButton, + SkipToContent, Theme, + HeaderMenu, + SideNav, + SideNavItem, + SideNavItems, + HeaderSideNavItems, HeaderName, HeaderNavigation, HeaderMenuItem, @@ -77,35 +85,60 @@ export default function NavigationBar() { ); }; - if (activeKey) { + const headerMenuItems = () => { return ( -
    - -
    - + <> + + Home + + + Processes + + + Process Instances + + + ); + }; + + if (activeKey) { + // TODO: apply theme g100 to the header + return ( + ( +
    + + + logo - - - Home - - - Processes - - - Process Instances - + + {headerMenuItems()} + + + {headerMenuItems()} + + {loginAndLogoutAction()}
    - -
    + )} + /> ); } return null; diff --git a/spiffworkflow-frontend/src/index.css b/spiffworkflow-frontend/src/index.css index c51398c4..67494b82 100644 --- a/spiffworkflow-frontend/src/index.css +++ b/spiffworkflow-frontend/src/index.css @@ -30,6 +30,10 @@ span.bjs-crumb { margin-bottom: 2em; } +// forces the navigation items to be displayed at all viewport sizes +.cds--header__nav { + display: block; +} .active-task-highlight:not(.djs-connection) .djs-visual > :nth-child(1) { fill: yellow !important; diff --git a/spiffworkflow-frontend/src/index.scss b/spiffworkflow-frontend/src/index.scss index c9671651..b4c90da1 100644 --- a/spiffworkflow-frontend/src/index.scss +++ b/spiffworkflow-frontend/src/index.scss @@ -1,3 +1,5 @@ +// @use '@carbon/react/scss/themes'; +// @use '@carbon/react/scss/theme' with ($theme: themes.$g100); @use '@carbon/react'; @use '@carbon/styles'; // @include grid.flex-grid(); diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx index 10ac2c0b..84ce676f 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx @@ -17,6 +17,11 @@ import { Grid, Column, MultiSelect, + TableHeader, + TableHead, + TableRow, + TableBody, + TableCell, // @ts-ignore } from '@carbon/react'; import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config'; @@ -373,55 +378,144 @@ export default function ProcessInstanceList() { }; const buildTable = () => { - const rows = processInstances.map((row: any) => { - const formattedStartDate = - convertSecondsToFormattedDate(row.start_in_seconds) || '-'; - const formattedEndDate = - convertSecondsToFormattedDate(row.end_in_seconds) || '-'; + // const rows = processInstances.map((row: any) => { + // const formattedStartDate = + // convertSecondsToFormattedDate(row.start_in_seconds) || '-'; + // const formattedEndDate = + // convertSecondsToFormattedDate(row.end_in_seconds) || '-'; + // + // return ( + // + // + // + // {row.id} + // + // + // + // + // {row.process_group_identifier} + // + // + // + // + // {row.process_model_identifier} + // + // + // {formattedStartDate} + // {formattedEndDate} + // + // {row.status} + // + // + // ); + // }); + // return ( + // + // + // + // + // + // + // + // + // + // + // + // {rows} + //
    Process Instance IdProcess GroupProcess ModelStart TimeEnd TimeStatus
    + // ); + const rows = [ + { + id: 'load-balancer-1', + name: 'Load Balancer 1', + rule: 'Round robin', + Status: 'Starting', + other: 'Test', + example: '22', + }, + { + id: 'load-balancer-2', + name: 'Load Balancer 2', + rule: 'DNS delegation', + status: 'Active', + other: 'Test', + example: '22', + }, + { + id: 'load-balancer-3', + name: 'Load Balancer 3', + rule: 'Round robin', + status: 'Disabled', + other: 'Test', + example: '22', + }, + { + id: 'load-balancer-4', + name: 'Load Balancer 4', + rule: 'Round robin', + status: 'Disabled', + other: 'Test', + example: '22', + }, + { + id: 'load-balancer-5', + name: 'Load Balancer 5', + rule: 'Round robin', + status: 'Disabled', + other: 'Test', + example: '22', + }, + { + id: 'load-balancer-6', + name: 'Load Balancer 6', + rule: 'Round robin', + status: 'Disabled', + other: 'Test', + example: '22', + }, + { + id: 'load-balancer-7', + name: 'Load Balancer 7', + rule: 'Round robin', + status: 'Disabled', + other: 'Test', + example: '22', + }, + ]; + const headers = ['Name', 'Rule', 'Status', 'Other', 'Example']; - return ( - - - - {row.id} - - - - - {row.process_group_identifier} - - - - - {row.process_model_identifier} - - - {formattedStartDate} - {formattedEndDate} - - {row.status} - - - ); - }); return ( - - - - - - - - - - - - {rows} +
    Process Instance IdProcess GroupProcess ModelStart TimeEnd TimeStatus
    + + + {headers.map((header) => ( + + {header} + + ))} + + + + {rows.map((row) => ( + + {Object.keys(row) + .filter((key) => key !== 'id') + .map((key) => { + return {(row as any)[key]}; + })} + + ))} +
    ); }; diff --git a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx index f6fb211f..8776df97 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx @@ -283,7 +283,6 @@ export default function ProcessModelEditDiagram() { // we should update this to act like updating scripts // where we pass an event to bpmn-js setScriptModeling(modeling); - setScriptText(script || ''); setScriptType(scriptTypeString); setScriptEventBus(eventBus); @@ -559,28 +558,25 @@ export default function ProcessModelEditDiagram() { if (scriptElement) { scriptName = (scriptElement as any).di.bpmnElement.name; } + return ( - - - Editing Script: {scriptName} - - - - {scriptUnitTestEditorElement()} - - - - + + + {scriptUnitTestEditorElement()} ); }; From 915020e521d140074aa7f9f6d8bf96dd0858e4f4 Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 4 Nov 2022 17:14:48 -0400 Subject: [PATCH 33/46] fix a few tests --- spiffworkflow-frontend/cypress/e2e/tasks.cy.js | 1 + spiffworkflow-frontend/cypress/support/commands.js | 4 +++- spiffworkflow-frontend/src/components/NavigationBar.tsx | 6 +++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/spiffworkflow-frontend/cypress/e2e/tasks.cy.js b/spiffworkflow-frontend/cypress/e2e/tasks.cy.js index 86298f59..377b05ac 100644 --- a/spiffworkflow-frontend/cypress/e2e/tasks.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/tasks.cy.js @@ -21,6 +21,7 @@ describe('tasks', () => { cy.logout(); }); + // TODO: need to fix the next_task thing to make this pass it('can complete and navigate a form', () => { const groupDisplayName = 'Acceptance Tests Group One'; const modelId = `acceptance-tests-model-2`; diff --git a/spiffworkflow-frontend/cypress/support/commands.js b/spiffworkflow-frontend/cypress/support/commands.js index 3a1a0ac4..affce0d1 100644 --- a/spiffworkflow-frontend/cypress/support/commands.js +++ b/spiffworkflow-frontend/cypress/support/commands.js @@ -31,7 +31,9 @@ Cypress.Commands.add('getBySel', (selector, ...args) => { }); Cypress.Commands.add('navigateToHome', () => { - cy.getBySel('nav-home').click(); + cy.get('button[aria-label="Open menu"]').click(); + cy.getBySel('side-nav-items').contains('Home').click(); + // cy.getBySel('nav-home').click(); }); Cypress.Commands.add('navigateToAdmin', () => { diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index 4dd81f9a..3f7e124d 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -123,10 +123,14 @@ export default function NavigationBar() { logo - + {headerMenuItems()} Date: Fri, 4 Nov 2022 17:21:25 -0400 Subject: [PATCH 34/46] unhardcode --- .../src/routes/ProcessInstanceList.tsx | 183 +++++------------- 1 file changed, 47 insertions(+), 136 deletions(-) diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx index 84ce676f..39794f9f 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx @@ -378,144 +378,55 @@ export default function ProcessInstanceList() { }; const buildTable = () => { - // const rows = processInstances.map((row: any) => { - // const formattedStartDate = - // convertSecondsToFormattedDate(row.start_in_seconds) || '-'; - // const formattedEndDate = - // convertSecondsToFormattedDate(row.end_in_seconds) || '-'; - // - // return ( - // - // - // - // {row.id} - // - // - // - // - // {row.process_group_identifier} - // - // - // - // - // {row.process_model_identifier} - // - // - // {formattedStartDate} - // {formattedEndDate} - // - // {row.status} - // - // - // ); - // }); - // return ( - // - // - // - // - // - // - // - // - // - // - // - // {rows} - //
    Process Instance IdProcess GroupProcess ModelStart TimeEnd TimeStatus
    - // ); - const rows = [ - { - id: 'load-balancer-1', - name: 'Load Balancer 1', - rule: 'Round robin', - Status: 'Starting', - other: 'Test', - example: '22', - }, - { - id: 'load-balancer-2', - name: 'Load Balancer 2', - rule: 'DNS delegation', - status: 'Active', - other: 'Test', - example: '22', - }, - { - id: 'load-balancer-3', - name: 'Load Balancer 3', - rule: 'Round robin', - status: 'Disabled', - other: 'Test', - example: '22', - }, - { - id: 'load-balancer-4', - name: 'Load Balancer 4', - rule: 'Round robin', - status: 'Disabled', - other: 'Test', - example: '22', - }, - { - id: 'load-balancer-5', - name: 'Load Balancer 5', - rule: 'Round robin', - status: 'Disabled', - other: 'Test', - example: '22', - }, - { - id: 'load-balancer-6', - name: 'Load Balancer 6', - rule: 'Round robin', - status: 'Disabled', - other: 'Test', - example: '22', - }, - { - id: 'load-balancer-7', - name: 'Load Balancer 7', - rule: 'Round robin', - status: 'Disabled', - other: 'Test', - example: '22', - }, - ]; - const headers = ['Name', 'Rule', 'Status', 'Other', 'Example']; + const rows = processInstances.map((row: any) => { + const formattedStartDate = + convertSecondsToFormattedDate(row.start_in_seconds) || '-'; + const formattedEndDate = + convertSecondsToFormattedDate(row.end_in_seconds) || '-'; + return ( + + + + {row.id} + + + + + {row.process_group_identifier} + + + + + {row.process_model_identifier} + + + {formattedStartDate} + {formattedEndDate} + + {row.status} + + + ); + }); return ( - - - - {headers.map((header) => ( - - {header} - - ))} - - - - {rows.map((row) => ( - - {Object.keys(row) - .filter((key) => key !== 'id') - .map((key) => { - return {(row as any)[key]}; - })} - - ))} - +
    + + + + + + + + + + + {rows}
    Process Instance IdProcess GroupProcess ModelStart TimeEnd TimeStatus
    ); }; From 6f0a50b60c34f2990707fc03cdbba8fdabe3816e Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 4 Nov 2022 17:37:32 -0400 Subject: [PATCH 35/46] fix label and comment artificial slowdown --- spiffworkflow-frontend/cypress/e2e/process_groups.cy.js | 2 +- spiffworkflow-frontend/cypress/e2e/process_instances.cy.js | 7 +++++++ spiffworkflow-frontend/src/components/ProcessGroupForm.tsx | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/spiffworkflow-frontend/cypress/e2e/process_groups.cy.js b/spiffworkflow-frontend/cypress/e2e/process_groups.cy.js index 9e974084..629b18c6 100644 --- a/spiffworkflow-frontend/cypress/e2e/process_groups.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/process_groups.cy.js @@ -30,7 +30,7 @@ describe('process-groups', () => { newGroupDisplayName ); - cy.contains('Delete Process Group').click(); + cy.contains('Delete').click(); cy.contains('Are you sure'); cy.getBySel('modal-confirmation-dialog').find('.cds--btn--danger').click(); cy.url().should('include', `process-groups`); diff --git a/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js b/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js index a2fcb266..62fe3cbc 100644 --- a/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js @@ -43,6 +43,13 @@ const updateBpmnPythonScriptWithMonaco = ( cy.contains('Launch Editor').click(); // sometimes, Loading... appears for more than 4 seconds. not sure why. cy.contains('Loading...').should('not.exist'); + + // the delay 30 is because, at some point, monaco started automatically + // adding a second double quote when we type a double quote. when it does + // that, there is a race condition where it sometimes gets in more text + // before the second double quote appears because the robot is typing faster + // than a human being could, so we artificially slow it down to make it more + // human. cy.get('.monaco-editor textarea:first') .click() .focused() // change subject to currently focused element diff --git a/spiffworkflow-frontend/src/components/ProcessGroupForm.tsx b/spiffworkflow-frontend/src/components/ProcessGroupForm.tsx index 99db3434..128a13c0 100644 --- a/spiffworkflow-frontend/src/components/ProcessGroupForm.tsx +++ b/spiffworkflow-frontend/src/components/ProcessGroupForm.tsx @@ -162,7 +162,7 @@ export default function ProcessGroupForm({ ); From 96daac637bba4812530a0f0a8433ad79b532947e Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 4 Nov 2022 18:02:09 -0400 Subject: [PATCH 36/46] lint --- .../cypress/e2e/process_instances.cy.js | 2 +- .../src/components/NavigationBar.tsx | 3 --- .../src/routes/ProcessInstanceList.tsx | 10 +++++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js b/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js index 62fe3cbc..b7bbf9d4 100644 --- a/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js @@ -161,7 +161,7 @@ describe('process-instances', () => { cy.basicPaginationTest(); }); - it.only('can filter', () => { + it('can filter', () => { cy.getBySel('process-instance-list-link').click(); cy.assertAtLeastOneItemInPaginatedResults(); diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index 3f7e124d..30fcb70c 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -3,10 +3,7 @@ import { HeaderContainer, HeaderMenuButton, SkipToContent, - Theme, - HeaderMenu, SideNav, - SideNavItem, SideNavItems, HeaderSideNavItems, HeaderName, diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx index 39794f9f..42ecf674 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx @@ -17,11 +17,11 @@ import { Grid, Column, MultiSelect, - TableHeader, - TableHead, - TableRow, - TableBody, - TableCell, + // TableHeader, + // TableHead, + // TableRow, + // TableBody, + // TableCell, // @ts-ignore } from '@carbon/react'; import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config'; From 8be06184d5ea22af2b3f44d6d2dfef27ad9cf567 Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 4 Nov 2022 18:18:28 -0400 Subject: [PATCH 37/46] fix last modal --- .../src/routes/ProcessInstanceShow.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index 0d97e608..0c0342b2 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -339,15 +339,15 @@ export default function ProcessInstanceShow() { const taskToUse: any = { ...taskToDisplay, data: taskDataToDisplay }; if (taskToDisplay) { return ( - - - - - {taskToUse.name} ({taskToUse.type}): {taskToUse.state} - {taskDataButtons(taskToUse)} - - - + + + {taskToUse.name} ({taskToUse.type}): {taskToUse.state} + {taskDataButtons(taskToUse)} + {taskDataContainer()} ); From 8262e2b6913f962f8b31228303ac0166bc8680dd Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 4 Nov 2022 18:27:10 -0400 Subject: [PATCH 38/46] Stack direction to orientation --- spiffworkflow-frontend/src/routes/ProcessGroupShow.tsx | 2 +- spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx | 2 +- spiffworkflow-frontend/src/routes/ProcessModelEdit.tsx | 2 +- spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx | 4 ++-- spiffworkflow-frontend/src/routes/ProcessModelShow.tsx | 2 +- spiffworkflow-frontend/src/routes/SecretNew.tsx | 2 +- spiffworkflow-frontend/src/routes/SecretShow.tsx | 4 ++-- spiffworkflow-frontend/src/routes/TaskShow.tsx | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spiffworkflow-frontend/src/routes/ProcessGroupShow.tsx b/spiffworkflow-frontend/src/routes/ProcessGroupShow.tsx index 02625a8f..25e58044 100644 --- a/spiffworkflow-frontend/src/routes/ProcessGroupShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessGroupShow.tsx @@ -82,7 +82,7 @@ export default function ProcessGroupShow() { ]} />
      - + diff --git a/spiffworkflow-frontend/src/routes/SecretNew.tsx b/spiffworkflow-frontend/src/routes/SecretNew.tsx index e1f34e89..016fec2e 100644 --- a/spiffworkflow-frontend/src/routes/SecretNew.tsx +++ b/spiffworkflow-frontend/src/routes/SecretNew.tsx @@ -68,7 +68,7 @@ export default function SecretNew() { }} /> - + diff --git a/spiffworkflow-frontend/src/routes/SecretShow.tsx b/spiffworkflow-frontend/src/routes/SecretShow.tsx index 2ee75e19..9882247b 100644 --- a/spiffworkflow-frontend/src/routes/SecretShow.tsx +++ b/spiffworkflow-frontend/src/routes/SecretShow.tsx @@ -65,8 +65,8 @@ export default function SecretShow() { if (secret) { return ( <> - -

      Secret Key: {secret.key}

      +

      Secret Key: {secret.key}

      + + {userTasksElement} From 41b80fe78df1995450b2b5fb594ba1171ade2670 Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 4 Nov 2022 18:30:45 -0400 Subject: [PATCH 39/46] remove garbage css rule --- spiffworkflow-frontend/src/index.css | 5 ----- 1 file changed, 5 deletions(-) diff --git a/spiffworkflow-frontend/src/index.css b/spiffworkflow-frontend/src/index.css index 67494b82..988fae27 100644 --- a/spiffworkflow-frontend/src/index.css +++ b/spiffworkflow-frontend/src/index.css @@ -30,11 +30,6 @@ span.bjs-crumb { margin-bottom: 2em; } -// forces the navigation items to be displayed at all viewport sizes -.cds--header__nav { - display: block; -} - .active-task-highlight:not(.djs-connection) .djs-visual > :nth-child(1) { fill: yellow !important; opacity: .6; From 12cc510fe998dc4f1860e4b25a87277d8ea762fd Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 4 Nov 2022 18:35:44 -0400 Subject: [PATCH 40/46] Squashed 'spiffworkflow-backend/' changes from 5c6601237e..c354b846ca c354b846ca Merge remote-tracking branch 'origin/main' into feature/carbon_ui ad13a41127 pyl is passing w/ burnettk 2366b93b73 Merge commit 'a0b923c9ad98d07d3cf46dca689a01b13d41aa02' bdc8121211 some more updates for group forms w/ burnettk 8483b98788 ergeremote-tracking branch 'origin/main' into feature/carbon_ui 623144aad2 add date ranges for process instances search w/ burnettk git-subtree-dir: spiffworkflow-backend git-subtree-split: c354b846cab6ac27d117ce9c10f9f56aec35df75 --- poetry.lock | 3 -- src/spiffworkflow_backend/api.yml | 6 +-- .../models/process_group.py | 8 ++++ .../models/process_model.py | 1 - .../routes/process_api_blueprint.py | 44 +++++++++---------- .../services/process_model_service.py | 3 +- .../integration/test_process_api.py | 18 +++++--- 7 files changed, 44 insertions(+), 39 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8b1e9cb7..8c918630 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1865,9 +1865,7 @@ develop = false [package.dependencies] celery = "*" configparser = "*" -dateparser = "*" lxml = "*" -pytz = "*" [package.source] type = "git" @@ -2621,7 +2619,6 @@ greenlet = [ {file = "greenlet-1.1.3.post0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a954002064ee919b444b19c1185e8cce307a1f20600f47d6f4b6d336972c809"}, {file = "greenlet-1.1.3.post0-cp39-cp39-win32.whl", hash = "sha256:2ccdc818cc106cc238ff7eba0d71b9c77be868fdca31d6c3b1347a54c9b187b2"}, {file = "greenlet-1.1.3.post0-cp39-cp39-win_amd64.whl", hash = "sha256:91a84faf718e6f8b888ca63d0b2d6d185c8e2a198d2a7322d75c303e7097c8b7"}, - {file = "greenlet-1.1.3.post0.tar.gz", hash = "sha256:f5e09dc5c6e1796969fd4b775ea1417d70e49a5df29aaa8e5d10675d9e11872c"}, ] gunicorn = [ {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, diff --git a/src/spiffworkflow_backend/api.yml b/src/spiffworkflow_backend/api.yml index 0f0a49c2..1ef9cf67 100755 --- a/src/spiffworkflow_backend/api.yml +++ b/src/spiffworkflow_backend/api.yml @@ -153,7 +153,6 @@ paths: description: The number of groups to show per page. Defaults to page 10. schema: type: integer - # process_groups_list get: operationId: spiffworkflow_backend.routes.process_api_blueprint.process_groups_list summary: get list @@ -168,7 +167,6 @@ paths: type: array items: $ref: "#/components/schemas/ProcessModelCategory" - # process_group_add post: operationId: spiffworkflow_backend.routes.process_api_blueprint.process_group_add summary: Add process group @@ -429,7 +427,7 @@ paths: description: For filtering - beginning of start window - in seconds since epoch schema: type: integer - - name: start_till + - name: start_to in: query required: false description: For filtering - end of start window - in seconds since epoch @@ -441,7 +439,7 @@ paths: description: For filtering - beginning of end window - in seconds since epoch schema: type: integer - - name: end_till + - name: end_to in: query required: false description: For filtering - end of end window - in seconds since epoch diff --git a/src/spiffworkflow_backend/models/process_group.py b/src/spiffworkflow_backend/models/process_group.py index 0b100ed4..381f8f63 100644 --- a/src/spiffworkflow_backend/models/process_group.py +++ b/src/spiffworkflow_backend/models/process_group.py @@ -1,6 +1,7 @@ """Process_group.""" from __future__ import annotations +import dataclasses from dataclasses import dataclass from dataclasses import field from typing import Any @@ -20,6 +21,7 @@ class ProcessGroup: id: str # A unique string name, lower case, under scores (ie, 'my_group') display_name: str + description: str | None = None display_order: int | None = 0 admin: bool | None = False process_models: list[ProcessModelInfo] = field( @@ -38,6 +40,12 @@ class ProcessGroup: return True return False + @property + def serialized(self) -> dict: + """Serialized.""" + original_dict = dataclasses.asdict(self) + return {x: original_dict[x] for x in original_dict if x not in ["sort_index"]} + class ProcessGroupSchema(Schema): """ProcessGroupSchema.""" diff --git a/src/spiffworkflow_backend/models/process_model.py b/src/spiffworkflow_backend/models/process_model.py index 9558a79b..1928f312 100644 --- a/src/spiffworkflow_backend/models/process_model.py +++ b/src/spiffworkflow_backend/models/process_model.py @@ -30,7 +30,6 @@ class ProcessModelInfo: display_name: str description: str process_group_id: str = "" - process_group: Any | None = None primary_file_name: str | None = None primary_process_id: str | None = None display_order: int | None = 0 diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index 41cd9d99..4ff25a75 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -43,6 +43,7 @@ from spiffworkflow_backend.models.message_triggerable_process_model import ( MessageTriggerableProcessModel, ) from spiffworkflow_backend.models.principal import PrincipalModel +from spiffworkflow_backend.models.process_group import ProcessGroup from spiffworkflow_backend.models.process_group import ProcessGroupSchema from spiffworkflow_backend.models.process_instance import ProcessInstanceApiSchema from spiffworkflow_backend.models.process_instance import ProcessInstanceModel @@ -135,18 +136,12 @@ def permissions_check(body: Dict[str, Dict[str, list[str]]]) -> flask.wrappers.R return make_response(jsonify({"results": response_dict}), 200) -def process_group_add( - body: Dict[str, Union[str, bool, int]] -) -> flask.wrappers.Response: +def process_group_add(body: dict) -> flask.wrappers.Response: """Add_process_group.""" process_model_service = ProcessModelService() - process_group = ProcessGroupSchema().load(body) + process_group = ProcessGroup(**body) process_model_service.add_process_group(process_group) - return Response( - json.dumps(ProcessGroupSchema().dump(process_group)), - status=201, - mimetype="application/json", - ) + return make_response(jsonify(process_group), 201) def process_group_delete(process_group_id: str) -> flask.wrappers.Response: @@ -155,13 +150,18 @@ def process_group_delete(process_group_id: str) -> flask.wrappers.Response: return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") -def process_group_update( - process_group_id: str, body: Dict[str, Union[str, bool, int]] -) -> Dict[str, Union[str, bool, int]]: +def process_group_update(process_group_id: str, body: dict) -> flask.wrappers.Response: """Process Group Update.""" - process_group = ProcessGroupSchema().load(body) + body_include_list = ["display_name", "description"] + body_filtered = { + include_item: body[include_item] + for include_item in body_include_list + if include_item in body + } + + process_group = ProcessGroup(id=process_group_id, **body_filtered) ProcessModelService().update_process_group(process_group) - return ProcessGroupSchema().dump(process_group) # type: ignore + return make_response(jsonify(process_group), 200) def process_groups_list(page: int = 1, per_page: int = 100) -> flask.wrappers.Response: @@ -174,6 +174,7 @@ def process_groups_list(page: int = 1, per_page: int = 100) -> flask.wrappers.Re remainder = len(process_groups) % per_page if remainder > 0: pages += 1 + response_json = { "results": ProcessGroupSchema(many=True).dump(batch), "pagination": { @@ -199,7 +200,7 @@ def process_group_show( status_code=400, ) ) from exception - return ProcessGroupSchema().dump(process_group) + return make_response(jsonify(process_group), 200) def process_model_add( @@ -225,7 +226,6 @@ def process_model_add( status_code=400, ) - process_model_info.process_group = process_group process_model_service.add_spec(process_model_info) return Response( json.dumps(ProcessModelInfoSchema().dump(process_model_info)), @@ -651,9 +651,9 @@ def process_instance_list( page: int = 1, per_page: int = 100, start_from: Optional[int] = None, - start_till: Optional[int] = None, + start_to: Optional[int] = None, end_from: Optional[int] = None, - end_till: Optional[int] = None, + end_to: Optional[int] = None, process_status: Optional[str] = None, ) -> flask.wrappers.Response: """Process_instance_list.""" @@ -684,17 +684,17 @@ def process_instance_list( process_instance_query = process_instance_query.filter( ProcessInstanceModel.start_in_seconds >= start_from ) - if start_till is not None: + if start_to is not None: process_instance_query = process_instance_query.filter( - ProcessInstanceModel.start_in_seconds <= start_till + ProcessInstanceModel.start_in_seconds <= start_to ) if end_from is not None: process_instance_query = process_instance_query.filter( ProcessInstanceModel.end_in_seconds >= end_from ) - if end_till is not None: + if end_to is not None: process_instance_query = process_instance_query.filter( - ProcessInstanceModel.end_in_seconds <= end_till + ProcessInstanceModel.end_in_seconds <= end_to ) if process_status is not None: process_status_array = process_status.split(",") diff --git a/src/spiffworkflow_backend/services/process_model_service.py b/src/spiffworkflow_backend/services/process_model_service.py index 57d84229..e10092d5 100644 --- a/src/spiffworkflow_backend/services/process_model_service.py +++ b/src/spiffworkflow_backend/services/process_model_service.py @@ -170,7 +170,7 @@ class ProcessModelService(FileSystemService): json_path = os.path.join(cat_path, self.CAT_JSON_FILE) with open(json_path, "w") as cat_json: json.dump( - self.GROUP_SCHEMA.dump(process_group), + process_group.serialized, cat_json, indent=4, sort_keys=True, @@ -274,6 +274,5 @@ class ProcessModelService(FileSystemService): with open(spec_path, "w") as wf_json: json.dump(self.WF_SCHEMA.dump(spec), wf_json, indent=4) if process_group: - spec.process_group = process_group spec.process_group_id = process_group.id return spec diff --git a/tests/spiffworkflow_backend/integration/test_process_api.py b/tests/spiffworkflow_backend/integration/test_process_api.py index 7ac6b923..bad47ce9 100644 --- a/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/tests/spiffworkflow_backend/integration/test_process_api.py @@ -17,7 +17,6 @@ from spiffworkflow_backend.exceptions.process_entity_not_found_error import ( from spiffworkflow_backend.models.active_task import ActiveTaskModel from spiffworkflow_backend.models.group import GroupModel from spiffworkflow_backend.models.process_group import ProcessGroup -from spiffworkflow_backend.models.process_group import ProcessGroupSchema from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus from spiffworkflow_backend.models.process_instance_report import ( @@ -388,25 +387,29 @@ class TestProcessApi(BaseTest): display_name="Another Test Category", display_order=0, admin=False, + description="Test Description", ) response = client.post( "/v1.0/process-groups", headers=self.logged_in_headers(with_super_admin_user), content_type="application/json", - data=json.dumps(ProcessGroupSchema().dump(process_group)), + data=json.dumps(process_group.serialized), ) assert response.status_code == 201 + assert response.json # Check what is returned - result = ProcessGroupSchema().loads(response.get_data(as_text=True)) + result = ProcessGroup(**response.json) assert result is not None assert result.display_name == "Another Test Category" assert result.id == "test" + assert result.description == "Test Description" # Check what is persisted persisted = ProcessModelService().get_process_group("test") assert persisted.display_name == "Another Test Category" assert persisted.id == "test" + assert persisted.description == "Test Description" def test_process_group_delete( self, @@ -461,7 +464,7 @@ class TestProcessApi(BaseTest): f"/v1.0/process-groups/{group_id}", headers=self.logged_in_headers(with_super_admin_user), content_type="application/json", - data=json.dumps(ProcessGroupSchema().dump(process_group)), + data=json.dumps(process_group.serialized), ) assert response.status_code == 200 @@ -788,6 +791,7 @@ class TestProcessApi(BaseTest): f"/v1.0/process-groups/{test_process_group_id}", headers=self.logged_in_headers(with_super_admin_user), ) + assert response.status_code == 200 assert response.json is not None assert response.json["id"] == test_process_group_id @@ -1299,7 +1303,7 @@ class TestProcessApi(BaseTest): # start > 2000, end < 5000 - this should eliminate the first 2 and the last response = client.get( - "/v1.0/process-instances?start_from=2001&end_till=5999", + "/v1.0/process-instances?start_from=2001&end_to=5999", headers=self.logged_in_headers(with_super_admin_user), ) assert response.json is not None @@ -1310,7 +1314,7 @@ class TestProcessApi(BaseTest): # start > 1000, start < 4000 - this should eliminate the first and the last 2 response = client.get( - "/v1.0/process-instances?start_from=1001&start_till=3999", + "/v1.0/process-instances?start_from=1001&start_to=3999", headers=self.logged_in_headers(with_super_admin_user), ) assert response.json is not None @@ -1321,7 +1325,7 @@ class TestProcessApi(BaseTest): # end > 2000, end < 6000 - this should eliminate the first and the last response = client.get( - "/v1.0/process-instances?end_from=2001&end_till=5999", + "/v1.0/process-instances?end_from=2001&end_to=5999", headers=self.logged_in_headers(with_super_admin_user), ) assert response.json is not None From 48a74301fb186aa69c2a9b463db9b61142975d0c Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 4 Nov 2022 19:03:53 -0400 Subject: [PATCH 41/46] do not chew all memory --- spiffworkflow-frontend/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spiffworkflow-frontend/Dockerfile b/spiffworkflow-frontend/Dockerfile index 4a373fc2..f6d1df81 100644 --- a/spiffworkflow-frontend/Dockerfile +++ b/spiffworkflow-frontend/Dockerfile @@ -6,6 +6,9 @@ ADD package.json /app/ ADD package-lock.json /app/ COPY . /app/ +# this matches total memory on spiffworkflow-demo +ENV NODE_OPTIONS=--max_old_space_size=2048 + # npm ci because it respects the lock file. # --ignore-scripts because authors can do bad things in postinstall scripts. # https://cheatsheetseries.owasp.org/cheatsheets/NPM_Security_Cheat_Sheet.html From ea8dcd26d1be4c0c7f585ef1fda85744678b1c76 Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 4 Nov 2022 19:04:59 -0400 Subject: [PATCH 42/46] it would be nice to know which command is failing --- spiffworkflow-frontend/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spiffworkflow-frontend/Dockerfile b/spiffworkflow-frontend/Dockerfile index f6d1df81..f7c6ac80 100644 --- a/spiffworkflow-frontend/Dockerfile +++ b/spiffworkflow-frontend/Dockerfile @@ -13,6 +13,7 @@ ENV NODE_OPTIONS=--max_old_space_size=2048 # --ignore-scripts because authors can do bad things in postinstall scripts. # https://cheatsheetseries.owasp.org/cheatsheets/NPM_Security_Cheat_Sheet.html # npx can-i-ignore-scripts can check that it's safe to ignore scripts. -RUN npm ci --ignore-scripts && npm run build +RUN npm ci --ignore-scripts +RUN npm run build ENTRYPOINT ["/app/bin/boot_server_in_docker"] From f116134cb50294122e9d99670fdcefe1fd1ccb44 Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 4 Nov 2022 22:44:38 -0400 Subject: [PATCH 43/46] theme header --- spiffworkflow-frontend/src/components/NavigationBar.tsx | 2 +- spiffworkflow-frontend/src/index.scss | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index 30fcb70c..fa7eb7f4 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -110,7 +110,7 @@ export default function NavigationBar() { return ( ( -
      +
      Date: Sun, 6 Nov 2022 13:24:12 -0500 Subject: [PATCH 44/46] add nav items so people can get everywhere --- .../src/components/NavigationBar.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index fa7eb7f4..b3932515 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -101,6 +101,24 @@ export default function NavigationBar() { > Process Instances + + Messages + + + Secrets + + + Authentications + ); }; From 4b42d6cde62288b92c17d44f8181e56389a2a6ca Mon Sep 17 00:00:00 2001 From: burnettk Date: Sun, 6 Nov 2022 13:24:57 -0500 Subject: [PATCH 45/46] Squashed 'spiffworkflow-frontend/' changes from 1df0c1a9b2..942256a4ab 942256a4ab theme header 7d02fab545 it would be nice to know which command is failing cecdb467fa do not chew all memory d3d94fb307 Merge remote-tracking branch 'origin/main' into feature/carbon_ui c6d6108fb7 remove garbage css rule 7ec4f725a6 Stack direction to orientation 77731cd7ac fix last modal 0f0962fd1a lint 700f3c4965 fix label and comment artificial slowdown 033d051992 unhardcode f283cc6302 fix a few tests 43848ea1c0 instances tests are mostly passing, issues with table resizing on the list w/ burnettk f9b8a161eb process model cypress tests are passing w/ burnettk e52157fe58 process group cypress tests are passing w/ burnettk 8ce7886326 pyl is passing w/ burnettk 999134fc8f some more updates for group forms w/ burnettk bba9ed27f1 added search bar on process groups page w/ burnettk d80e9971cc added login_logout buttons w/ burnettk 6126faaebc finished process instance list filter w/ burnettk 3d2b1ad1d4 add date ranges for process instances search w/ burnettk 2831206f29 a simple date picker w/ burnettk 02d165c52a filter is working for status and process model w/ burnettk 5b118a69e6 process instance list page is mostly set up now w/ burnettk efd0663ef4 merged in main and resolved conflicts w/ burnettk 6191cfedf3 some more changes... navigation bar is broken due to header container w/ burnettk 877d543b02 many things have been switched to carbon and the home page loads w/ burnettk c6a5bb081f added carbon react package w/ burnettk git-subtree-dir: spiffworkflow-frontend git-subtree-split: 942256a4ab74949d0cba48cd93cc5a70001e0a72 --- Dockerfile | 6 +- cypress.config.js | 5 + cypress/e2e/process_groups.cy.js | 2 +- cypress/e2e/process_instances.cy.js | 15 +- cypress/e2e/process_models.cy.js | 12 +- cypress/e2e/tasks.cy.js | 1 + cypress/support/commands.js | 25 +- package-lock.json | 591 +++++++++++++++++++++- package.json | 7 + src/App.tsx | 49 +- src/components/ButtonWithConfirmation.tsx | 38 +- src/components/NavigationBar.tsx | 168 ++++-- src/components/PaginationForTable.tsx | 165 +----- src/components/ProcessGroupForm.tsx | 181 +++++++ src/components/ProcessModelSearch.tsx | 48 ++ src/components/ReactDiagramEditor.tsx | 3 +- src/config.tsx | 4 +- src/helpers.tsx | 33 +- src/index.css | 18 +- src/index.scss | 10 + src/index.tsx | 1 + src/interfaces.ts | 11 + src/routes/AuthenticationList.tsx | 3 +- src/routes/HomePage.tsx | 28 +- src/routes/MessageInstanceList.tsx | 3 +- src/routes/ProcessGroupEdit.tsx | 75 +-- src/routes/ProcessGroupList.tsx | 71 +-- src/routes/ProcessGroupNew.tsx | 71 +-- src/routes/ProcessGroupShow.tsx | 5 +- src/routes/ProcessInstanceList.tsx | 375 +++++++------- src/routes/ProcessInstanceLogList.tsx | 3 +- src/routes/ProcessInstanceReportList.tsx | 3 +- src/routes/ProcessInstanceReportShow.tsx | 3 +- src/routes/ProcessInstanceShow.tsx | 23 +- src/routes/ProcessModelEdit.tsx | 5 +- src/routes/ProcessModelEditDiagram.tsx | 97 ++-- src/routes/ProcessModelShow.tsx | 5 +- src/routes/ReactFormEditor.tsx | 49 +- src/routes/SecretList.tsx | 3 +- src/routes/SecretNew.tsx | 5 +- src/routes/SecretShow.tsx | 7 +- src/routes/TaskShow.tsx | 5 +- 42 files changed, 1505 insertions(+), 727 deletions(-) create mode 100644 src/components/ProcessGroupForm.tsx create mode 100644 src/components/ProcessModelSearch.tsx create mode 100644 src/index.scss diff --git a/Dockerfile b/Dockerfile index 4a373fc2..f7c6ac80 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,10 +6,14 @@ ADD package.json /app/ ADD package-lock.json /app/ COPY . /app/ +# this matches total memory on spiffworkflow-demo +ENV NODE_OPTIONS=--max_old_space_size=2048 + # npm ci because it respects the lock file. # --ignore-scripts because authors can do bad things in postinstall scripts. # https://cheatsheetseries.owasp.org/cheatsheets/NPM_Security_Cheat_Sheet.html # npx can-i-ignore-scripts can check that it's safe to ignore scripts. -RUN npm ci --ignore-scripts && npm run build +RUN npm ci --ignore-scripts +RUN npm run build ENTRYPOINT ["/app/bin/boot_server_in_docker"] diff --git a/cypress.config.js b/cypress.config.js index c4bf94c4..1c8c1c6e 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -10,4 +10,9 @@ module.exports = defineConfig({ // implement node event listeners here }, }, + + // this scrolls away from the elements for some reason with carbon when set to top + // https://github.com/cypress-io/cypress/issues/2353 + // https://docs.cypress.io/guides/core-concepts/interacting-with-elements#Scrolling + scrollBehavior: "center", }); diff --git a/cypress/e2e/process_groups.cy.js b/cypress/e2e/process_groups.cy.js index 1f2c675e..629b18c6 100644 --- a/cypress/e2e/process_groups.cy.js +++ b/cypress/e2e/process_groups.cy.js @@ -32,7 +32,7 @@ describe('process-groups', () => { cy.contains('Delete').click(); cy.contains('Are you sure'); - cy.contains('OK').click(); + cy.getBySel('modal-confirmation-dialog').find('.cds--btn--danger').click(); cy.url().should('include', `process-groups`); cy.contains(groupId).should('not.exist'); }); diff --git a/cypress/e2e/process_instances.cy.js b/cypress/e2e/process_instances.cy.js index d5383d5a..b7bbf9d4 100644 --- a/cypress/e2e/process_instances.cy.js +++ b/cypress/e2e/process_instances.cy.js @@ -43,12 +43,19 @@ const updateBpmnPythonScriptWithMonaco = ( cy.contains('Launch Editor').click(); // sometimes, Loading... appears for more than 4 seconds. not sure why. cy.contains('Loading...').should('not.exist'); + + // the delay 30 is because, at some point, monaco started automatically + // adding a second double quote when we type a double quote. when it does + // that, there is a race condition where it sometimes gets in more text + // before the second double quote appears because the robot is typing faster + // than a human being could, so we artificially slow it down to make it more + // human. cy.get('.monaco-editor textarea:first') .click() .focused() // change subject to currently focused element // .type('{ctrl}a') // had been doing it this way, but it turns out to be flaky relative to clear() .clear() - .type(pythonScript); + .type(pythonScript, { delay: 30 }); cy.contains('Close').click(); // wait for a little bit for the xml to get set before saving @@ -111,8 +118,10 @@ describe('process-instances', () => { }); it('can create a new instance and can modify with monaco text editor', () => { - const originalPythonScript = 'person = "Kevin"'; - const newPythonScript = 'person = "Mike"'; + // leave off the ending double quote since manco adds it + const originalPythonScript = 'person = "Kevin'; + const newPythonScript = 'person = "Mike'; + const bpmnFile = 'process_model_one.bpmn'; // Change bpmn diff --git a/cypress/e2e/process_models.cy.js b/cypress/e2e/process_models.cy.js index 3b28514b..48b65d5d 100644 --- a/cypress/e2e/process_models.cy.js +++ b/cypress/e2e/process_models.cy.js @@ -99,7 +99,7 @@ describe('process-models', () => { cy.contains(/^Process Model File$/); // Some reason, cypress evals json strings so we have to escape it it with '{{}' cy.get('.view-line').type('{{} "test_key": "test_value" }'); - cy.contains('Save').click(); + cy.getBySel('file-save-button').click(); cy.get('input[name=file_name]').type(jsonFileName); cy.contains('Save Changes').click(); cy.contains(`Process Model File: ${jsonFileName}`); @@ -168,12 +168,8 @@ describe('process-models', () => { }); it('can allow searching for model', () => { - cy.get('[name=process-model-selection]').click(); - cy.get('[name=process-model-selection]').type('model-3'); - cy.get( - `[aria-label="acceptance-tests-group-one/acceptance-tests-model-3"]` - ).click(); - - cy.contains('Process Instances').click(); + cy.getBySel('process-model-selection').click().type('model-3'); + cy.contains('acceptance-tests-group-one/acceptance-tests-model-3').click(); + cy.contains('List').click(); }); }); diff --git a/cypress/e2e/tasks.cy.js b/cypress/e2e/tasks.cy.js index 86298f59..377b05ac 100644 --- a/cypress/e2e/tasks.cy.js +++ b/cypress/e2e/tasks.cy.js @@ -21,6 +21,7 @@ describe('tasks', () => { cy.logout(); }); + // TODO: need to fix the next_task thing to make this pass it('can complete and navigate a form', () => { const groupDisplayName = 'Acceptance Tests Group One'; const modelId = `acceptance-tests-model-2`; diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 1b9c9241..affce0d1 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -31,11 +31,13 @@ Cypress.Commands.add('getBySel', (selector, ...args) => { }); Cypress.Commands.add('navigateToHome', () => { - cy.getBySel('nav-home').click(); + cy.get('button[aria-label="Open menu"]').click(); + cy.getBySel('side-nav-items').contains('Home').click(); + // cy.getBySel('nav-home').click(); }); Cypress.Commands.add('navigateToAdmin', () => { - cy.getBySel('spiffworkflow-logo').click(); + cy.visit('/admin'); }); Cypress.Commands.add('login', (selector, ...args) => { @@ -101,18 +103,15 @@ Cypress.Commands.add( ); Cypress.Commands.add('basicPaginationTest', () => { - cy.get('#pagination-page-dropdown') - .type('typing_to_open_dropdown_box....FIXME') - .find('.dropdown-item') - .contains(/^2$/) - .click(); + cy.getBySel('pagination-options').scrollIntoView(); + cy.get('.cds--select__item-count').find('.cds--select-input').select('2'); - cy.contains(/^1-2 of \d+$/); - cy.getBySel('pagination-previous-button-inactive'); - cy.getBySel('pagination-next-button').click(); - cy.contains(/^3-4 of \d+$/); - cy.getBySel('pagination-previous-button').click(); - cy.contains(/^1-2 of \d+$/); + // NOTE: this is a em dash instead of en dash + cy.contains(/\b1–2 of \d+/); + cy.get('.cds--pagination__button--forward').click(); + cy.contains(/\b3–4 of \d+/); + cy.get('.cds--pagination__button--backward').click(); + cy.contains(/\b1–2 of \d+/); }); Cypress.Commands.add('assertAtLeastOneItemInPaginatedResults', () => { diff --git a/package-lock.json b/package-lock.json index 08286743..a08036b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,9 @@ "@babel/core": "^7.18.10", "@babel/plugin-transform-react-jsx": "^7.18.6", "@babel/preset-react": "^7.18.6", + "@carbon/icons-react": "^11.10.0", + "@carbon/react": "^1.16.0", + "@carbon/styles": "^1.16.0", "@ginkgo-bioworks/react-json-schema-form-builder": "^2.9.0", "@monaco-editor/react": "^4.4.5", "@rjsf/core": "^4.2.0", @@ -2140,6 +2143,138 @@ "resolved": "https://registry.npmjs.org/@camunda/zeebe-element-templates-json-schema/-/zeebe-element-templates-json-schema-0.6.0.tgz", "integrity": "sha512-qawIFM52lp1hW2vWrHaX8ywguZsp2olE0DRTHUY+KWH5GwszZwGWECP3tji1KVih2TasQyf28kcQVh8TeQ6dAg==" }, + "node_modules/@carbon/colors": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/@carbon/colors/-/colors-11.7.0.tgz", + "integrity": "sha512-2B57vtirYTpxGg7p9yIO+s4cwL5QB8ogGQB5Pz+zdaHoVxrLsGlxAg28CQgk7yxw7doqST79bRVYS6FaJq+qJw==" + }, + "node_modules/@carbon/feature-flags": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@carbon/feature-flags/-/feature-flags-0.9.0.tgz", + "integrity": "sha512-uNCRkxsNwLdJVNwQ5S5vrLAm4yawWzhFBwyP9EgXVmRzJH85Aim+miC+QNjRXntJDYhZYDuLABTeb3VD692ypA==" + }, + "node_modules/@carbon/grid": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/@carbon/grid/-/grid-11.7.0.tgz", + "integrity": "sha512-OCDXQNSPVOoBQN08Qn5wo2WpKRGRm/kEBo6ZTl2NoDCl21mcYJZ0IDbKPTukixJB1sUNDUlnrpwMoaznPj48dw==", + "dependencies": { + "@carbon/layout": "^11.7.0" + } + }, + "node_modules/@carbon/icon-helpers": { + "version": "10.34.0", + "resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.34.0.tgz", + "integrity": "sha512-Ov9EBc1tR/DDrMI0pN1drj2jb27ISmYFBLdDji+aivVJkLPy8R/jikJOsOBgIq2kUjQJYNN199k2acHKjZdYIg==" + }, + "node_modules/@carbon/icons-react": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/@carbon/icons-react/-/icons-react-11.10.0.tgz", + "integrity": "sha512-807RWTfbvVzmsDg2DJ4FjwYNbJSgkrEd1Ui8I07YheJVb3sbYGGZMG7aCS0qXVlrQOhB2hggtxSW1w9NksUXNA==", + "hasInstallScript": true, + "dependencies": { + "@carbon/icon-helpers": "^10.34.0", + "@carbon/telemetry": "0.1.0", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": ">=16" + } + }, + "node_modules/@carbon/layout": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/@carbon/layout/-/layout-11.7.0.tgz", + "integrity": "sha512-p4YQvW8U5Go0Tz1PZZgllGSPmoq8xBB5PHByuHiAjzwGclxPsBmY6Ea7tftINFW8VlcjDcampyT8VfZXhP2lFg==" + }, + "node_modules/@carbon/motion": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/@carbon/motion/-/motion-11.5.0.tgz", + "integrity": "sha512-5QEALh+xZzICdgVLanSpiDfBTErzVgEze/xUKs7ZdSbd6p1FaDKDGvCmj9RCsaz/CMVHIWo65MshIglSWX5Xvw==" + }, + "node_modules/@carbon/react": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@carbon/react/-/react-1.16.0.tgz", + "integrity": "sha512-kVeL/iyVqN2tfcoE1xliIm4w2ex9L4m/b8KuGo7ZuqBmRzANQdfSYGfj11KU0TXT1CbOVFrqsT/aBxApsP5IDg==", + "hasInstallScript": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@carbon/feature-flags": "^0.9.0", + "@carbon/icons-react": "^11.10.0", + "@carbon/layout": "^11.7.0", + "@carbon/styles": "^1.16.0", + "@carbon/telemetry": "0.1.0", + "classnames": "2.3.2", + "copy-to-clipboard": "^3.3.1", + "downshift": "5.2.1", + "flatpickr": "4.6.9", + "invariant": "^2.2.3", + "lodash.debounce": "^4.0.8", + "lodash.findlast": "^4.5.0", + "lodash.isequal": "^4.5.0", + "lodash.omit": "^4.5.0", + "lodash.throttle": "^4.1.1", + "prop-types": "^15.7.2", + "react-is": "^17.0.2", + "use-resize-observer": "^6.0.0", + "wicg-inert": "^3.1.1", + "window-or-global": "^1.0.1" + }, + "peerDependencies": { + "react": "^16.8.6 || ^17.0.1", + "react-dom": "^16.8.6 || ^17.0.1", + "sass": "^1.33.0" + } + }, + "node_modules/@carbon/react/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/@carbon/styles": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@carbon/styles/-/styles-1.16.0.tgz", + "integrity": "sha512-LSjRw2Ws8rWI1a96KYUuX10jG+rpSn68dHlZhhDq+RJWsMGpjFhKUxPoTiJli2hHXyxey6rXV0hfr7oBJ0ud7w==", + "dependencies": { + "@carbon/colors": "^11.7.0", + "@carbon/feature-flags": "^0.9.0", + "@carbon/grid": "^11.7.0", + "@carbon/layout": "^11.7.0", + "@carbon/motion": "^11.5.0", + "@carbon/themes": "^11.11.0", + "@carbon/type": "^11.11.0", + "@ibm/plex": "6.0.0-next.6" + }, + "peerDependencies": { + "sass": "^1.33.0" + } + }, + "node_modules/@carbon/telemetry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@carbon/telemetry/-/telemetry-0.1.0.tgz", + "integrity": "sha512-kNWt0bkgPwGW0i5h7HFuljbKRXPvIhsKbB+1tEURAYLXoJg9iJLF1eGvWN5iVoFCS2zje4GR3OGOsvvKVe7Hlg==", + "bin": { + "carbon-telemetry": "bin/carbon-telemetry.js" + } + }, + "node_modules/@carbon/themes": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@carbon/themes/-/themes-11.11.0.tgz", + "integrity": "sha512-EhLizr2oMqJXNubI2zWw09qcSPLZdWoBOQ6uNcjHzpXgoGNMwbVJE/qGMy/ivr+EOs6Fe0z5T0u97v+ZhSmRhg==", + "dependencies": { + "@carbon/colors": "^11.7.0", + "@carbon/layout": "^11.7.0", + "@carbon/type": "^11.11.0", + "color": "^4.0.0" + } + }, + "node_modules/@carbon/type": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@carbon/type/-/type-11.11.0.tgz", + "integrity": "sha512-eX6z8BibP1En1xBm2wUd01Nzk0Tm1jftR2QSD4JBn4xhnkGR824gpcbLTAIMGx9/Mr3R65Enbam3uFO0OOaH8g==", + "dependencies": { + "@carbon/grid": "^11.7.0", + "@carbon/layout": "^11.7.0" + } + }, "node_modules/@codemirror/autocomplete": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.3.0.tgz", @@ -2899,6 +3034,14 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "node_modules/@ibm/plex": { + "version": "6.0.0-next.6", + "resolved": "https://registry.npmjs.org/@ibm/plex/-/plex-6.0.0-next.6.tgz", + "integrity": "sha512-B3uGruTn2rS5gweynLmfSe7yCawSRsJguJJQHVQiqf4rh2RNgJFu8YLE2Zd/JHV0ZXoVMOslcXP2k3hMkxKEyA==", + "engines": { + "node": ">=14" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -8009,9 +8152,9 @@ } }, "node_modules/classnames": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", - "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" }, "node_modules/clean-css": { "version": "5.3.1", @@ -8207,6 +8350,18 @@ "node": ">=0.10.0" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -8220,6 +8375,31 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -8484,6 +8664,14 @@ "node": ">=0.10.0" } }, + "node_modules/copy-to-clipboard": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz", + "integrity": "sha512-Vme1Z6RUDzrb6xAI7EZlVZ5uvOk2F//GaxKUxajDqm9LhOVM1inxNAD2vy+UZDYsd0uyA9s7b3/FVZPSxqrCfg==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, "node_modules/core_d": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/core_d/-/core_d-5.0.1.tgz", @@ -9005,9 +9193,9 @@ "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==" }, "node_modules/cypress": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.8.0.tgz", - "integrity": "sha512-QVse0dnLm018hgti2enKMVZR9qbIO488YGX06nH5j3Dg1isL38DwrBtyrax02CANU6y8F4EJUuyW6HJKw1jsFA==", + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.11.0.tgz", + "integrity": "sha512-lsaE7dprw5DoXM00skni6W5ElVVLGAdRUUdZjX2dYsGjbY/QnpzWZ95Zom1mkGg0hAaO/QVTZoFVS7Jgr/GUPA==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -9872,6 +10060,25 @@ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" }, + "node_modules/downshift": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/downshift/-/downshift-5.2.1.tgz", + "integrity": "sha512-uHX2OLbWthLR8QbR8NCI8OmjvvJxq8+PrA95KblFd9JyB1zVZh1HnszzsWMMCnMuH6IvsUtVfF5HY7XfijJ2dw==", + "dependencies": { + "@babel/runtime": "^7.9.1", + "compute-scroll-into-view": "^1.0.13", + "prop-types": "^15.7.2", + "react-is": "^16.13.1" + }, + "peerDependencies": { + "react": ">=0.14.9" + } + }, + "node_modules/downshift/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -11596,6 +11803,11 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/flatpickr": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.9.tgz", + "integrity": "sha512-F0azNNi8foVWKSF+8X+ZJzz8r9sE1G4hl06RyceIaLvyltKvDl6vqk9Lm/6AUUCi5HWaIjiUbk7UpeE/fOXOpw==" + }, "node_modules/flatted": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", @@ -12794,6 +13006,12 @@ "url": "https://opencollective.com/immer" } }, + "node_modules/immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "peer": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -18240,6 +18458,16 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.findlast": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.findlast/-/lodash.findlast-4.6.0.tgz", + "integrity": "sha512-+OGwb1FVKjhc2aIEQ9vKqNDW1a0/HaCLr0iCIK10jfVif3dBE0nhQD0jOZNZLh7zOlmFUTrk+vt85eXoH4vKuA==" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -18250,6 +18478,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -18261,6 +18494,11 @@ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -23919,6 +24157,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -24247,6 +24490,23 @@ "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" }, + "node_modules/sass": { + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.55.0.tgz", + "integrity": "sha512-Pk+PMy7OGLs9WaxZGJMn7S96dvlyVBwwtToX895WmCpAOr5YiJYEUJfiJidMuKb613z2xNWcXCHEuOvjZbqC6A==", + "peer": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -24717,6 +24977,19 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -26202,6 +26475,11 @@ "ret": "~0.1.10" } }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -27747,6 +28025,18 @@ "react": "^16.8.0 || ^17.0.0" } }, + "node_modules/use-resize-observer": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-6.1.0.tgz", + "integrity": "sha512-SiPcWHiIQ1CnHmb6PxbYtygqiZXR0U9dNkkbpX9VYnlstUwF8+QqpUTrzh13pjPwcjMVGR+QIC+nvF5ujfFNng==", + "dependencies": { + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", @@ -28868,6 +29158,11 @@ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, + "node_modules/wicg-inert": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/wicg-inert/-/wicg-inert-3.1.2.tgz", + "integrity": "sha512-Ba9tGNYxXwaqKEi9sJJvPMKuo063umUPsHN0JJsjrs2j8KDSzkWLMZGZ+MH1Jf1Fq4OWZ5HsESJID6nRza2ang==" + }, "node_modules/widest-line": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", @@ -28923,6 +29218,11 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/window-or-global": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/window-or-global/-/window-or-global-1.0.1.tgz", + "integrity": "sha512-tE12J/NenOv4xdVobD+AD3fT06T4KNqnzRhkv5nBIu7K+pvOH2oLCEgYP+i+5mF2jtI6FEADheOdZkA8YWET9w==" + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -30836,6 +31136,124 @@ "resolved": "https://registry.npmjs.org/@camunda/zeebe-element-templates-json-schema/-/zeebe-element-templates-json-schema-0.6.0.tgz", "integrity": "sha512-qawIFM52lp1hW2vWrHaX8ywguZsp2olE0DRTHUY+KWH5GwszZwGWECP3tji1KVih2TasQyf28kcQVh8TeQ6dAg==" }, + "@carbon/colors": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/@carbon/colors/-/colors-11.7.0.tgz", + "integrity": "sha512-2B57vtirYTpxGg7p9yIO+s4cwL5QB8ogGQB5Pz+zdaHoVxrLsGlxAg28CQgk7yxw7doqST79bRVYS6FaJq+qJw==" + }, + "@carbon/feature-flags": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@carbon/feature-flags/-/feature-flags-0.9.0.tgz", + "integrity": "sha512-uNCRkxsNwLdJVNwQ5S5vrLAm4yawWzhFBwyP9EgXVmRzJH85Aim+miC+QNjRXntJDYhZYDuLABTeb3VD692ypA==" + }, + "@carbon/grid": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/@carbon/grid/-/grid-11.7.0.tgz", + "integrity": "sha512-OCDXQNSPVOoBQN08Qn5wo2WpKRGRm/kEBo6ZTl2NoDCl21mcYJZ0IDbKPTukixJB1sUNDUlnrpwMoaznPj48dw==", + "requires": { + "@carbon/layout": "^11.7.0" + } + }, + "@carbon/icon-helpers": { + "version": "10.34.0", + "resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.34.0.tgz", + "integrity": "sha512-Ov9EBc1tR/DDrMI0pN1drj2jb27ISmYFBLdDji+aivVJkLPy8R/jikJOsOBgIq2kUjQJYNN199k2acHKjZdYIg==" + }, + "@carbon/icons-react": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/@carbon/icons-react/-/icons-react-11.10.0.tgz", + "integrity": "sha512-807RWTfbvVzmsDg2DJ4FjwYNbJSgkrEd1Ui8I07YheJVb3sbYGGZMG7aCS0qXVlrQOhB2hggtxSW1w9NksUXNA==", + "requires": { + "@carbon/icon-helpers": "^10.34.0", + "@carbon/telemetry": "0.1.0", + "prop-types": "^15.7.2" + } + }, + "@carbon/layout": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/@carbon/layout/-/layout-11.7.0.tgz", + "integrity": "sha512-p4YQvW8U5Go0Tz1PZZgllGSPmoq8xBB5PHByuHiAjzwGclxPsBmY6Ea7tftINFW8VlcjDcampyT8VfZXhP2lFg==" + }, + "@carbon/motion": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/@carbon/motion/-/motion-11.5.0.tgz", + "integrity": "sha512-5QEALh+xZzICdgVLanSpiDfBTErzVgEze/xUKs7ZdSbd6p1FaDKDGvCmj9RCsaz/CMVHIWo65MshIglSWX5Xvw==" + }, + "@carbon/react": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@carbon/react/-/react-1.16.0.tgz", + "integrity": "sha512-kVeL/iyVqN2tfcoE1xliIm4w2ex9L4m/b8KuGo7ZuqBmRzANQdfSYGfj11KU0TXT1CbOVFrqsT/aBxApsP5IDg==", + "requires": { + "@babel/runtime": "^7.18.3", + "@carbon/feature-flags": "^0.9.0", + "@carbon/icons-react": "^11.10.0", + "@carbon/layout": "^11.7.0", + "@carbon/styles": "^1.16.0", + "@carbon/telemetry": "0.1.0", + "classnames": "2.3.2", + "copy-to-clipboard": "^3.3.1", + "downshift": "5.2.1", + "flatpickr": "4.6.9", + "invariant": "^2.2.3", + "lodash.debounce": "^4.0.8", + "lodash.findlast": "^4.5.0", + "lodash.isequal": "^4.5.0", + "lodash.omit": "^4.5.0", + "lodash.throttle": "^4.1.1", + "prop-types": "^15.7.2", + "react-is": "^17.0.2", + "use-resize-observer": "^6.0.0", + "wicg-inert": "^3.1.1", + "window-or-global": "^1.0.1" + }, + "dependencies": { + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "@carbon/styles": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@carbon/styles/-/styles-1.16.0.tgz", + "integrity": "sha512-LSjRw2Ws8rWI1a96KYUuX10jG+rpSn68dHlZhhDq+RJWsMGpjFhKUxPoTiJli2hHXyxey6rXV0hfr7oBJ0ud7w==", + "requires": { + "@carbon/colors": "^11.7.0", + "@carbon/feature-flags": "^0.9.0", + "@carbon/grid": "^11.7.0", + "@carbon/layout": "^11.7.0", + "@carbon/motion": "^11.5.0", + "@carbon/themes": "^11.11.0", + "@carbon/type": "^11.11.0", + "@ibm/plex": "6.0.0-next.6" + } + }, + "@carbon/telemetry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@carbon/telemetry/-/telemetry-0.1.0.tgz", + "integrity": "sha512-kNWt0bkgPwGW0i5h7HFuljbKRXPvIhsKbB+1tEURAYLXoJg9iJLF1eGvWN5iVoFCS2zje4GR3OGOsvvKVe7Hlg==" + }, + "@carbon/themes": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@carbon/themes/-/themes-11.11.0.tgz", + "integrity": "sha512-EhLizr2oMqJXNubI2zWw09qcSPLZdWoBOQ6uNcjHzpXgoGNMwbVJE/qGMy/ivr+EOs6Fe0z5T0u97v+ZhSmRhg==", + "requires": { + "@carbon/colors": "^11.7.0", + "@carbon/layout": "^11.7.0", + "@carbon/type": "^11.11.0", + "color": "^4.0.0" + } + }, + "@carbon/type": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@carbon/type/-/type-11.11.0.tgz", + "integrity": "sha512-eX6z8BibP1En1xBm2wUd01Nzk0Tm1jftR2QSD4JBn4xhnkGR824gpcbLTAIMGx9/Mr3R65Enbam3uFO0OOaH8g==", + "requires": { + "@carbon/grid": "^11.7.0", + "@carbon/layout": "^11.7.0" + } + }, "@codemirror/autocomplete": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.3.0.tgz", @@ -31364,6 +31782,11 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "@ibm/plex": { + "version": "6.0.0-next.6", + "resolved": "https://registry.npmjs.org/@ibm/plex/-/plex-6.0.0-next.6.tgz", + "integrity": "sha512-B3uGruTn2rS5gweynLmfSe7yCawSRsJguJJQHVQiqf4rh2RNgJFu8YLE2Zd/JHV0ZXoVMOslcXP2k3hMkxKEyA==" + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -35236,9 +35659,9 @@ } }, "classnames": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", - "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" }, "clean-css": { "version": "5.3.1", @@ -35376,6 +35799,30 @@ "object-visit": "^1.0.0" } }, + "color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "requires": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "dependencies": { + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -35389,6 +35836,15 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -35614,6 +36070,14 @@ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==" }, + "copy-to-clipboard": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz", + "integrity": "sha512-Vme1Z6RUDzrb6xAI7EZlVZ5uvOk2F//GaxKUxajDqm9LhOVM1inxNAD2vy+UZDYsd0uyA9s7b3/FVZPSxqrCfg==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, "core_d": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/core_d/-/core_d-5.0.1.tgz", @@ -36020,9 +36484,9 @@ "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==" }, "cypress": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.8.0.tgz", - "integrity": "sha512-QVse0dnLm018hgti2enKMVZR9qbIO488YGX06nH5j3Dg1isL38DwrBtyrax02CANU6y8F4EJUuyW6HJKw1jsFA==", + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.11.0.tgz", + "integrity": "sha512-lsaE7dprw5DoXM00skni6W5ElVVLGAdRUUdZjX2dYsGjbY/QnpzWZ95Zom1mkGg0hAaO/QVTZoFVS7Jgr/GUPA==", "dev": true, "requires": { "@cypress/request": "^2.88.10", @@ -36715,6 +37179,24 @@ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" }, + "downshift": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/downshift/-/downshift-5.2.1.tgz", + "integrity": "sha512-uHX2OLbWthLR8QbR8NCI8OmjvvJxq8+PrA95KblFd9JyB1zVZh1HnszzsWMMCnMuH6IvsUtVfF5HY7XfijJ2dw==", + "requires": { + "@babel/runtime": "^7.9.1", + "compute-scroll-into-view": "^1.0.13", + "prop-types": "^15.7.2", + "react-is": "^16.13.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, "duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -38020,6 +38502,11 @@ "rimraf": "^3.0.2" } }, + "flatpickr": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.9.tgz", + "integrity": "sha512-F0azNNi8foVWKSF+8X+ZJzz8r9sE1G4hl06RyceIaLvyltKvDl6vqk9Lm/6AUUCi5HWaIjiUbk7UpeE/fOXOpw==" + }, "flatted": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", @@ -38876,6 +39363,12 @@ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz", "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==" }, + "immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "peer": true + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -43000,6 +43493,16 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "lodash.findlast": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.findlast/-/lodash.findlast-4.6.0.tgz", + "integrity": "sha512-+OGwb1FVKjhc2aIEQ9vKqNDW1a0/HaCLr0iCIK10jfVif3dBE0nhQD0jOZNZLh7zOlmFUTrk+vt85eXoH4vKuA==" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -43010,6 +43513,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" + }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -43021,6 +43529,11 @@ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" }, + "lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -46934,6 +47447,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -47167,6 +47685,17 @@ "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" }, + "sass": { + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.55.0.tgz", + "integrity": "sha512-Pk+PMy7OGLs9WaxZGJMn7S96dvlyVBwwtToX895WmCpAOr5YiJYEUJfiJidMuKb613z2xNWcXCHEuOvjZbqC6A==", + "peer": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -47565,6 +48094,21 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + } + } + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -48736,6 +49280,11 @@ "is-number": "^7.0.0" } }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -49905,6 +50454,14 @@ "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==", "requires": {} }, + "use-resize-observer": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-6.1.0.tgz", + "integrity": "sha512-SiPcWHiIQ1CnHmb6PxbYtygqiZXR0U9dNkkbpX9VYnlstUwF8+QqpUTrzh13pjPwcjMVGR+QIC+nvF5ujfFNng==", + "requires": { + "resize-observer-polyfill": "^1.5.1" + } + }, "util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", @@ -50782,6 +51339,11 @@ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, + "wicg-inert": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/wicg-inert/-/wicg-inert-3.1.2.tgz", + "integrity": "sha512-Ba9tGNYxXwaqKEi9sJJvPMKuo063umUPsHN0JJsjrs2j8KDSzkWLMZGZ+MH1Jf1Fq4OWZ5HsESJID6nRza2ang==" + }, "widest-line": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", @@ -50815,6 +51377,11 @@ } } }, + "window-or-global": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/window-or-global/-/window-or-global-1.0.1.tgz", + "integrity": "sha512-tE12J/NenOv4xdVobD+AD3fT06T4KNqnzRhkv5nBIu7K+pvOH2oLCEgYP+i+5mF2jtI6FEADheOdZkA8YWET9w==" + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/package.json b/package.json index e0a8b242..c9127d7c 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,9 @@ "@babel/core": "^7.18.10", "@babel/plugin-transform-react-jsx": "^7.18.6", "@babel/preset-react": "^7.18.6", + "@carbon/icons-react": "^11.10.0", + "@carbon/react": "^1.16.0", + "@carbon/styles": "^1.16.0", "@ginkgo-bioworks/react-json-schema-form-builder": "^2.9.0", "@monaco-editor/react": "^4.4.5", "@rjsf/core": "^4.2.0", @@ -55,6 +58,10 @@ "@ginkgo-bioworks/react-json-schema-form-builder": { "react": "^18.2.0", "bootstrap": "^5.2.0-beta1" + }, + "@carbon/react": { + "react": "^18.2.0", + "react-dom": "^18.2.0" } }, "scripts": { diff --git a/src/App.tsx b/src/App.tsx index 0444f042..186ac513 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,6 @@ import { useMemo, useState } from 'react'; -import { Container } from 'react-bootstrap'; +// @ts-ignore +import { Content } from '@carbon/react'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; import ErrorContext from './contexts/ErrorContext'; @@ -9,7 +10,6 @@ import HomePage from './routes/HomePage'; import TaskShow from './routes/TaskShow'; import ErrorBoundary from './components/ErrorBoundary'; import AdminRoutes from './routes/AdminRoutes'; -import SubNavigation from './components/SubNavigation'; import { ErrorForDisplay } from './interfaces'; export default function App() { @@ -47,30 +47,27 @@ export default function App() { return ( - - - {errorTag} - - - -
      - - } /> - } /> - } /> - } - /> - } - /> - -
      -
      -
      -
      + + + + {errorTag} + + + } /> + } /> + } /> + } + /> + } + /> + + + +
      ); } diff --git a/src/components/ButtonWithConfirmation.tsx b/src/components/ButtonWithConfirmation.tsx index 6956dadc..25aaadc1 100644 --- a/src/components/ButtonWithConfirmation.tsx +++ b/src/components/ButtonWithConfirmation.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; -import { Button, Modal } from 'react-bootstrap'; +// @ts-ignore +import { Button, Modal } from '@carbon/react'; type OwnProps = { description?: string; @@ -25,13 +26,6 @@ export default function ButtonWithConfirmation({ setShowConfirmationPrompt(false); }; - const modalBodyElement = () => { - if (description) { - return {description}; - } - return null; - }; - const handleConfirmation = () => { onConfirmation(); setShowConfirmationPrompt(false); @@ -40,28 +34,22 @@ export default function ButtonWithConfirmation({ const confirmationDialog = () => { return ( - - {title} - - {modalBodyElement()} - - - - - + open={showConfirmationPrompt} + danger + data-qa="modal-confirmation-dialog" + modalHeading={description} + modalLabel={title} + primaryButtonText={confirmButtonLabel} + secondaryButtonText="Cancel" + onSecondarySubmit={handleConfirmationPromptCancel} + onRequestSubmit={handleConfirmation} + /> ); }; return ( <> - {confirmationDialog()} diff --git a/src/components/NavigationBar.tsx b/src/components/NavigationBar.tsx index 9b070a55..fa7eb7f4 100644 --- a/src/components/NavigationBar.tsx +++ b/src/components/NavigationBar.tsx @@ -1,12 +1,28 @@ -import { Button, Navbar, Nav, Container } from 'react-bootstrap'; +import { + Header, + HeaderContainer, + HeaderMenuButton, + SkipToContent, + SideNav, + SideNavItems, + HeaderSideNavItems, + HeaderName, + HeaderNavigation, + HeaderMenuItem, + HeaderGlobalAction, + HeaderGlobalBar, + // @ts-ignore +} from '@carbon/react'; +// @ts-ignore +import { Logout, Login } from '@carbon/icons-react'; +import { useEffect, useState } from 'react'; +import { useLocation } from 'react-router-dom'; // @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 UserService from '../services/UserService'; // for ref: https://react-bootstrap.github.io/components/navbar/ export default function NavigationBar() { - const navElements = null; - const handleLogout = () => { UserService.doLogout(); }; @@ -15,56 +31,116 @@ export default function NavigationBar() { UserService.doLogin(); }; - const loginLink = () => { - if (!UserService.isLoggedIn()) { - return ( - - - - - - ); + const location = useLocation(); + const [activeKey, setActiveKey] = useState(''); + + useEffect(() => { + let newActiveKey = '/admin/process-groups'; + if (location.pathname.match(/^\/admin\/messages\b/)) { + newActiveKey = '/admin/messages'; + } else if (location.pathname.match(/^\/admin\/process-instances\b/)) { + newActiveKey = '/admin/process-instances'; + } else if (location.pathname.match(/^\/admin\/secrets\b/)) { + newActiveKey = '/admin/secrets'; + } else if (location.pathname.match(/^\/admin\/authentications\b/)) { + newActiveKey = '/admin/authentications'; + } else if (location.pathname === '/') { + newActiveKey = '/'; + } else if (location.pathname.match(/^\/tasks\b/)) { + newActiveKey = '/'; } - return null; + setActiveKey(newActiveKey); + }, [location]); + + const isActivePage = (menuItemPath: string) => { + return activeKey === menuItemPath; }; - const logoutLink = () => { + const loginAndLogoutAction = () => { if (UserService.isLoggedIn()) { return ( - - - Signed in as: {UserService.getUsername()} - - - - - + <> + {UserService.getUsername()} + + + + ); } - return null; + return ( + + + + ); }; - return ( - - - - logo - - - - - - {loginLink()} - {logoutLink()} - - - ); + const headerMenuItems = () => { + return ( + <> + + Home + + + Processes + + + Process Instances + + + ); + }; + + if (activeKey) { + // TODO: apply theme g100 to the header + return ( + ( +
      + + + + logo + + + {headerMenuItems()} + + + + {headerMenuItems()} + + + {loginAndLogoutAction()} +
      + )} + /> + ); + } + return null; } diff --git a/src/components/PaginationForTable.tsx b/src/components/PaginationForTable.tsx index 2f648f52..3b65c78c 100644 --- a/src/components/PaginationForTable.tsx +++ b/src/components/PaginationForTable.tsx @@ -1,7 +1,8 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; -import { Dropdown, Stack } from 'react-bootstrap'; +// @ts-ignore +import { Pagination } from '@carbon/react'; +import { PaginationObject } from '../interfaces'; export const DEFAULT_PER_PAGE = 50; export const DEFAULT_PAGE = 1; @@ -10,9 +11,7 @@ type OwnProps = { page: number; perPage: number; perPageOptions?: number[]; - pagination: { - [key: string]: number; - }; + pagination: PaginationObject | null; tableToDisplay: any; queryParamString?: string; path: string; @@ -28,140 +27,32 @@ export default function PaginationForTable({ path, }: OwnProps) { const PER_PAGE_OPTIONS = [2, 10, 50, 100]; + const navigate = useNavigate(); - const buildPerPageDropdown = () => { - const perPageDropdownRows = (perPageOptions || PER_PAGE_OPTIONS).map( - (perPageOption) => { - if (perPageOption === perPage) { - return ( - - {perPageOption} - - ); - } - return ( - - {perPageOption} - - ); - } - ); - return ( - - - - Number to show: {perPage} - - - {perPageDropdownRows} - - - ); + const updateRows = (args: any) => { + const newPage = args.page; + const { pageSize } = args; + navigate(`${path}?page=${newPage}&per_page=${pageSize}${queryParamString}`); }; - const buildPaginationNav = () => { - let previousPageTag = null; - if (page === 1) { - previousPageTag = ( -
    • - - « - -
    • - ); - } else { - previousPageTag = ( -
    • - - « - -
    • - ); - } - - let nextPageTag = null; - if (page >= pagination.pages) { - nextPageTag = ( -
    • - - » - -
    • - ); - } else { - nextPageTag = ( -
    • - - » - -
    • - ); - } - - let startingNumber = (page - 1) * perPage + 1; - let endingNumber = page * perPage; - if (endingNumber > pagination.total) { - endingNumber = pagination.total; - } - if (startingNumber > pagination.total) { - startingNumber = pagination.total; - } - + if (pagination) { return ( - -

      - {startingNumber}-{endingNumber} of{' '} - {pagination.total} -

      - -
      + <> + {tableToDisplay} + + ); - }; - - return ( -
      - {buildPaginationNav()} - {tableToDisplay} - {buildPerPageDropdown()} -
      - ); + } + return null; } diff --git a/src/components/ProcessGroupForm.tsx b/src/components/ProcessGroupForm.tsx new file mode 100644 index 00000000..128a13c0 --- /dev/null +++ b/src/components/ProcessGroupForm.tsx @@ -0,0 +1,181 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +// @ts-ignore +import { Button, ButtonSet, Form, Stack, TextInput } from '@carbon/react'; +import { slugifyString } from '../helpers'; +import HttpService from '../services/HttpService'; +import { ProcessGroup } from '../interfaces'; +import ButtonWithConfirmation from './ButtonWithConfirmation'; + +type OwnProps = { + mode: string; + processGroup: ProcessGroup; + setProcessGroup: (..._args: any[]) => any; +}; + +export default function ProcessGroupForm({ + mode, + processGroup, + setProcessGroup, +}: OwnProps) { + const [identifierInvalid, setIdentifierInvalid] = useState(false); + const [idHasBeenUpdatedByUser, setIdHasBeenUpdatedByUser] = + useState(false); + const [displayNameInvalid, setDisplayNameInvalid] = useState(false); + const navigate = useNavigate(); + + const navigateToProcessGroup = (_result: any) => { + if (processGroup) { + navigate(`/admin/process-groups/${processGroup.id}`); + } + }; + + const navigateToProcessGroups = (_result: any) => { + navigate(`/admin/process-groups`); + }; + + const hasValidIdentifier = (identifierToCheck: string) => { + return identifierToCheck.match(/^[a-z0-9][0-9a-z-]+[a-z0-9]$/); + }; + + const deleteProcessGroup = () => { + HttpService.makeCallToBackend({ + path: `/process-groups/${processGroup.id}`, + successCallback: navigateToProcessGroups, + httpMethod: 'DELETE', + }); + }; + + const handleFormSubmission = (event: any) => { + event.preventDefault(); + let hasErrors = false; + if (!hasValidIdentifier(processGroup.id)) { + setIdentifierInvalid(true); + hasErrors = true; + } + if (processGroup.display_name === '') { + setDisplayNameInvalid(true); + hasErrors = true; + } + if (hasErrors) { + return; + } + let path = '/process-groups'; + if (mode === 'edit') { + path = `/process-groups/${processGroup.id}`; + } + let httpMethod = 'POST'; + if (mode === 'edit') { + httpMethod = 'PUT'; + } + const postBody = { + display_name: processGroup.display_name, + description: processGroup.description, + }; + if (mode === 'new') { + Object.assign(postBody, { id: processGroup.id }); + } + + HttpService.makeCallToBackend({ + path, + successCallback: navigateToProcessGroup, + httpMethod, + postBody, + }); + }; + + const updateProcessGroup = (newValues: any) => { + const processGroupToCopy = { + ...processGroup, + }; + Object.assign(processGroupToCopy, newValues); + setProcessGroup(processGroupToCopy); + }; + + const onDisplayNameChanged = (newDisplayName: any) => { + setDisplayNameInvalid(false); + const updateDict = { display_name: newDisplayName }; + if (!idHasBeenUpdatedByUser) { + Object.assign(updateDict, { id: slugifyString(newDisplayName) }); + } + updateProcessGroup(updateDict); + }; + + const formElements = () => { + const textInputs = [ + onDisplayNameChanged(event.target.value)} + onBlur={(event: any) => console.log('event', event)} + />, + ]; + + if (mode === 'new') { + textInputs.push( + { + updateProcessGroup({ id: event.target.value }); + // was invalid, and now valid + if (identifierInvalid && hasValidIdentifier(event.target.value)) { + setIdentifierInvalid(false); + } + setIdHasBeenUpdatedByUser(true); + }} + /> + ); + } + + textInputs.push( + + updateProcessGroup({ description: event.target.value }) + } + /> + ); + + return textInputs; + }; + + const formButtons = () => { + const buttons = [ + , + ]; + if (mode === 'edit') { + buttons.push( + + ); + } + return {buttons}; + }; + + return ( +
      + + {formElements()} + {formButtons()} + +
      + ); +} diff --git a/src/components/ProcessModelSearch.tsx b/src/components/ProcessModelSearch.tsx new file mode 100644 index 00000000..98419d6c --- /dev/null +++ b/src/components/ProcessModelSearch.tsx @@ -0,0 +1,48 @@ +import { + ComboBox, + // @ts-ignore +} from '@carbon/react'; +import { truncateString } from '../helpers'; +import { ProcessModel } from '../interfaces'; + +type OwnProps = { + onChange: (..._args: any[]) => any; + processModels: ProcessModel[]; + selectedItem?: ProcessModel | null; + titleText?: string; +}; + +export default function ProcessModelSearch({ + processModels, + selectedItem, + onChange, + titleText = 'Process model', +}: OwnProps) { + const shouldFilterProcessModel = (options: any) => { + const processModel: ProcessModel = options.item; + const { inputValue } = options; + return `${processModel.process_group_id}/${processModel.id} (${processModel.display_name})`.includes( + inputValue + ); + }; + return ( + { + if (processModel) { + return `${processModel.process_group_id}/${ + processModel.id + } (${truncateString(processModel.display_name, 20)})`; + } + return null; + }} + shouldFilterItem={shouldFilterProcessModel} + placeholder="Choose a process model" + titleText={titleText} + selectedItem={selectedItem} + /> + ); +} diff --git a/src/components/ReactDiagramEditor.tsx b/src/components/ReactDiagramEditor.tsx index 9728e7d5..2bbf64db 100644 --- a/src/components/ReactDiagramEditor.tsx +++ b/src/components/ReactDiagramEditor.tsx @@ -18,7 +18,8 @@ import { } from 'dmn-js-properties-panel'; import React, { useRef, useEffect, useState } from 'react'; -import Button from 'react-bootstrap/Button'; +// @ts-ignore +import { Button } from '@carbon/react'; import 'bpmn-js/dist/assets/diagram-js.css'; import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'; diff --git a/src/config.tsx b/src/config.tsx index 94519ba8..c9e94eb0 100644 --- a/src/config.tsx +++ b/src/config.tsx @@ -16,4 +16,6 @@ export const PROCESS_STATUSES = [ 'suspended', ]; -export const DATE_FORMAT = 'yyyy-MM-dd HH:mm:ss'; +// with time: yyyy-MM-dd HH:mm:ss +export const DATE_FORMAT = 'yyyy-MM-dd'; +export const DATE_FORMAT_CARBON = 'Y-m-d'; diff --git a/src/helpers.tsx b/src/helpers.tsx index 59922acf..6b27fb4c 100644 --- a/src/helpers.tsx +++ b/src/helpers.tsx @@ -20,7 +20,10 @@ export const capitalizeFirstLetter = (string: any) => { return string.charAt(0).toUpperCase() + string.slice(1); }; -export const convertDateToSeconds = (date: any, onChangeFunction: any) => { +export const convertDateToSeconds = ( + date: any, + onChangeFunction: any = null +) => { let dateInSeconds = date; if (date !== null) { let dateInMilliseconds = date; @@ -39,14 +42,26 @@ export const convertDateToSeconds = (date: any, onChangeFunction: any) => { return null; }; -export const convertSecondsToFormattedDate = (seconds: number) => { - if (seconds) { - const startDate = new Date(seconds * 1000); - return format(startDate, DATE_FORMAT); +export const convertStringToDate = (dateString: string) => { + if (dateString) { + return new Date(dateString); } return null; }; +export const convertSecondsToFormattedDate = (seconds: number) => { + if (seconds) { + const dateObject = new Date(seconds * 1000); + return format(dateObject, DATE_FORMAT); + } + return null; +}; + +export const convertDateStringToSeconds = (dateString: string) => { + const dateObject = convertStringToDate(dateString); + return convertDateToSeconds(dateObject); +}; + export const objectIsEmpty = (obj: object) => { return Object.keys(obj).length === 0; }; @@ -90,3 +105,11 @@ export const getProcessModelFullIdentifierFromSearchParams = ( } return processModelFullIdentifier; }; + +// https://stackoverflow.com/a/71352046/6090676 +export const truncateString = (text: string, len: number) => { + if (text.length > len && text.length > 0) { + return `${text.split(' ').slice(0, len).join(' ')} ...`; + } + return text; +}; diff --git a/src/index.css b/src/index.css index fd156c23..988fae27 100644 --- a/src/index.css +++ b/src/index.css @@ -20,12 +20,15 @@ span.bjs-crumb { } .app-logo { - height: 35%; - width: 35%; + height: 85%; + width: 85%; margin-top: 1em; margin-bottom: 1em; } +.spiffworkflow-header-container { + margin-bottom: 2em; +} .active-task-highlight:not(.djs-connection) .djs-visual > :nth-child(1) { fill: yellow !important; @@ -44,6 +47,17 @@ span.bjs-crumb { margin:auto; } +.cds--btn.button-white-background { + color: #393939; + background: #FFFFFF; + background-blend-mode: multiply; + border: 1px solid #393939; +} + +.with-bottom-margin { + margin-bottom: 1em; +} + .diagram-viewer-canvas { border:1px solid #000000; height:70vh; diff --git a/src/index.scss b/src/index.scss new file mode 100644 index 00000000..7ef64a65 --- /dev/null +++ b/src/index.scss @@ -0,0 +1,10 @@ +// @use '@carbon/react/scss/themes'; +// @use '@carbon/react/scss/theme' with ($theme: themes.$g100); +@use '@carbon/react'; +@use '@carbon/styles'; +// @include grid.flex-grid(); + +// @use '@carbon/react/scss/colors'; +// .cds--header, a.cds--header__menu-item { +// background-color: colors.$gray-100; +// } diff --git a/src/index.tsx b/src/index.tsx index ebd40415..a2fab5c0 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,6 +4,7 @@ import App from './App'; import 'bootstrap/dist/css/bootstrap.css'; import './index.css'; +import './index.scss'; import reportWebVitals from './reportWebVitals'; import UserService from './services/UserService'; diff --git a/src/interfaces.ts b/src/interfaces.ts index 8e153c80..14ead3ba 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -14,6 +14,7 @@ export interface RecentProcessModel { export interface ProcessGroup { id: string; display_name: string; + description?: string | null; } export interface ProcessModel { @@ -41,3 +42,13 @@ export interface AuthenticationItem { id: string; parameters: AuthenticationParam[]; } + +export interface PaginationObject { + count: number; + total: number; + pages: number; +} + +export interface CarbonComboBoxSelection { + selectedItem: ProcessModel; +} diff --git a/src/routes/AuthenticationList.tsx b/src/routes/AuthenticationList.tsx index 6be0f054..a249aa27 100644 --- a/src/routes/AuthenticationList.tsx +++ b/src/routes/AuthenticationList.tsx @@ -1,5 +1,6 @@ import { useContext, useEffect, useState } from 'react'; -import { Table } from 'react-bootstrap'; +// @ts-ignore +import { Table } from '@carbon/react'; import ErrorContext from '../contexts/ErrorContext'; import { AuthenticationItem } from '../interfaces'; import HttpService from '../services/HttpService'; diff --git a/src/routes/HomePage.tsx b/src/routes/HomePage.tsx index 4d348ed2..2cfc0078 100644 --- a/src/routes/HomePage.tsx +++ b/src/routes/HomePage.tsx @@ -1,17 +1,18 @@ import { useEffect, useState } from 'react'; -import { Button, Table } from 'react-bootstrap'; +// @ts-ignore +import { Button, Table } from '@carbon/react'; import { Link, useSearchParams } from 'react-router-dom'; import PaginationForTable from '../components/PaginationForTable'; import { getPageInfoFromSearchParams } from '../helpers'; import HttpService from '../services/HttpService'; -import { RecentProcessModel } from '../interfaces'; +import { PaginationObject, RecentProcessModel } from '../interfaces'; const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5; export default function HomePage() { const [searchParams] = useSearchParams(); const [tasks, setTasks] = useState([]); - const [pagination, setPagination] = useState(null); + const [pagination, setPagination] = useState(null); useEffect(() => { const { page, perPage } = getPageInfoFromSearchParams( @@ -53,7 +54,7 @@ export default function HomePage() { data-qa="process-instance-show-link" to={`/admin/process-models/${rowToUse.process_group_identifier}/${rowToUse.process_model_identifier}/process-instances/${rowToUse.process_instance_id}`} > - View + View {rowToUse.process_instance_id} 0 && buildRecentProcessModelSection(); - - if (pagination) { + const tasksWaitingForMeComponent = () => { + if (pagination && pagination.total < 1) { + return null; + } const { page, perPage } = getPageInfoFromSearchParams( searchParams, PER_PAGE_FOR_TASKS_ON_HOME_PAGE @@ -144,6 +145,17 @@ export default function HomePage() { tableToDisplay={buildTable()} path="/tasks" /> + + ); + }; + + const relevantProcessModelSection = + recentProcessModels.length > 0 && buildRecentProcessModelSection(); + + if (pagination) { + return ( + <> + {tasksWaitingForMeComponent()} {relevantProcessModelSection} ); diff --git a/src/routes/MessageInstanceList.tsx b/src/routes/MessageInstanceList.tsx index 90c74c29..6bc0a57d 100644 --- a/src/routes/MessageInstanceList.tsx +++ b/src/routes/MessageInstanceList.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react'; -import { Table } from 'react-bootstrap'; +// @ts-ignore +import { Table } from '@carbon/react'; import { Link, useParams, useSearchParams } from 'react-router-dom'; import PaginationForTable from '../components/PaginationForTable'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; diff --git a/src/routes/ProcessGroupEdit.tsx b/src/routes/ProcessGroupEdit.tsx index 83379999..d624309c 100644 --- a/src/routes/ProcessGroupEdit.tsx +++ b/src/routes/ProcessGroupEdit.tsx @@ -1,20 +1,18 @@ import { useState, useEffect } from 'react'; -import { useParams, useNavigate } from 'react-router-dom'; -import { Button, Stack } from 'react-bootstrap'; +import { useParams } from 'react-router-dom'; +// @ts-ignore import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import HttpService from '../services/HttpService'; -import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; +import ProcessGroupForm from '../components/ProcessGroupForm'; +import { ProcessGroup } from '../interfaces'; export default function ProcessGroupEdit() { - const [displayName, setDisplayName] = useState(''); const params = useParams(); - const navigate = useNavigate(); - const [processGroup, setProcessGroup] = useState(null); + const [processGroup, setProcessGroup] = useState(null); useEffect(() => { const setProcessGroupsFromResult = (result: any) => { setProcessGroup(result); - setDisplayName(result.display_name); }; HttpService.makeCallToBackend({ @@ -23,69 +21,16 @@ export default function ProcessGroupEdit() { }); }, [params]); - const navigateToProcessGroup = (_result: any) => { - navigate(`/admin/process-groups/${(processGroup as any).id}`); - }; - - const navigateToProcessGroups = (_result: any) => { - navigate(`/admin/process-groups`); - }; - - const updateProcessGroup = (event: any) => { - event.preventDefault(); - HttpService.makeCallToBackend({ - path: `/process-groups/${(processGroup as any).id}`, - successCallback: navigateToProcessGroup, - httpMethod: 'PUT', - postBody: { - display_name: displayName, - id: (processGroup as any).id, - }, - }); - }; - - const deleteProcessGroup = () => { - HttpService.makeCallToBackend({ - path: `/process-groups/${(processGroup as any).id}`, - successCallback: navigateToProcessGroups, - httpMethod: 'DELETE', - }); - }; - - const onDisplayNameChanged = (newDisplayName: any) => { - setDisplayName(newDisplayName); - }; - if (processGroup) { return ( <>

      Edit Process Group: {(processGroup as any).id}

      -
      - - onDisplayNameChanged(e.target.value)} - /> -
      -
      - - - - - -
      + ); } diff --git a/src/routes/ProcessGroupList.tsx b/src/routes/ProcessGroupList.tsx index 3c1be15c..13f3d455 100644 --- a/src/routes/ProcessGroupList.tsx +++ b/src/routes/ProcessGroupList.tsx @@ -1,13 +1,21 @@ import { useEffect, useState } from 'react'; import { Link, useNavigate, useSearchParams } from 'react-router-dom'; -import { Button, Form, InputGroup, Table } from 'react-bootstrap'; -import { Typeahead } from 'react-bootstrap-typeahead'; -import { Option } from 'react-bootstrap-typeahead/types/types'; +import { + Button, + Table, + // ExpandableTile, + // TileAboveTheFoldContent, + // TileBelowTheFoldContent, + // TextInput, + // ClickableTile, + // @ts-ignore +} from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import PaginationForTable from '../components/PaginationForTable'; import HttpService from '../services/HttpService'; import { getPageInfoFromSearchParams } from '../helpers'; -import { ProcessModel } from '../interfaces'; +import { CarbonComboBoxSelection, ProcessGroup } from '../interfaces'; +import ProcessModelSearch from '../components/ProcessModelSearch'; // Example process group json // {'process_group_id': 'sure', 'display_name': 'Test Workflows', 'id': 'test_process_group'} @@ -17,8 +25,9 @@ export default function ProcessGroupList() { const [processGroups, setProcessGroups] = useState([]); const [pagination, setPagination] = useState(null); - const [processModeleSelectionOptions, setProcessModelSelectionOptions] = - useState([]); + const [processModelAvailableItems, setProcessModelAvailableItems] = useState( + [] + ); useEffect(() => { const setProcessGroupsFromResult = (result: any) => { @@ -31,7 +40,7 @@ export default function ProcessGroupList() { Object.assign(item, { label }); return item; }); - setProcessModelSelectionOptions(selectionArray); + setProcessModelAvailableItems(selectionArray); }; const { page, perPage } = getPageInfoFromSearchParams(searchParams); @@ -48,7 +57,7 @@ export default function ProcessGroupList() { }, [searchParams]); const buildTable = () => { - const rows = processGroups.map((row) => { + const rows = processGroups.map((row: ProcessGroup) => { return ( @@ -72,6 +81,17 @@ export default function ProcessGroupList() { {rows} ); + // const rows = processGroups.map((row: ProcessGroup) => { + // return ( + // + // + // {row.display_name} + // + // + // ); + // }); + // + // return
      {rows}
      ; }; const processGroupsDisplayArea = () => { @@ -97,35 +117,18 @@ export default function ProcessGroupList() { }; const processModelSearchArea = () => { - const processModelSearchOnChange = (selected: Option[]) => { - const processModel = selected[0] as ProcessModel; + const processModelSearchOnChange = (selection: CarbonComboBoxSelection) => { + const processModel = selection.selectedItem; navigate( `/admin/process-models/${processModel.process_group_id}/${processModel.id}` ); }; return ( -
      -

      Search

      - - - - Process Model:{' '} - - - - -
      + ); }; @@ -133,7 +136,9 @@ export default function ProcessGroupList() { return ( <> - +

      {processModelSearchArea()} diff --git a/src/routes/ProcessGroupNew.tsx b/src/routes/ProcessGroupNew.tsx index 2f3f3a35..d4d8b038 100644 --- a/src/routes/ProcessGroupNew.tsx +++ b/src/routes/ProcessGroupNew.tsx @@ -1,71 +1,24 @@ import { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import Button from 'react-bootstrap/Button'; -import Form from 'react-bootstrap/Form'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; -import { slugifyString } from '../helpers'; -import HttpService from '../services/HttpService'; +import ProcessGroupForm from '../components/ProcessGroupForm'; +import { ProcessGroup } from '../interfaces'; export default function ProcessGroupNew() { - const [identifier, setIdentifier] = useState(''); - const [idHasBeenUpdatedByUser, setIdHasBeenUpdatedByUser] = useState(false); - const [displayName, setDisplayName] = useState(''); - const navigate = useNavigate(); - - const navigateToProcessGroup = (_result: any) => { - navigate(`/admin/process-groups/${identifier}`); - }; - - const addProcessGroup = (event: any) => { - event.preventDefault(); - HttpService.makeCallToBackend({ - path: `/process-groups`, - successCallback: navigateToProcessGroup, - httpMethod: 'POST', - postBody: { - id: identifier, - display_name: displayName, - }, - }); - }; - - const onDisplayNameChanged = (newDisplayName: any) => { - setDisplayName(newDisplayName); - if (!idHasBeenUpdatedByUser) { - setIdentifier(slugifyString(newDisplayName)); - } - }; + const [processGroup, setProcessGroup] = useState({ + id: '', + display_name: '', + description: '', + }); return ( <>

      Add Process Group

      -
      - - Display Name: - onDisplayNameChanged(e.target.value)} - /> - - - ID: - { - setIdentifier(e.target.value); - setIdHasBeenUpdatedByUser(true); - }} - /> - - -
      + ); } diff --git a/src/routes/ProcessGroupShow.tsx b/src/routes/ProcessGroupShow.tsx index 522058c3..25e58044 100644 --- a/src/routes/ProcessGroupShow.tsx +++ b/src/routes/ProcessGroupShow.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; import { Link, useSearchParams, useParams } from 'react-router-dom'; -import { Button, Table, Stack } from 'react-bootstrap'; +// @ts-ignore +import { Button, Table, Stack } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import PaginationForTable from '../components/PaginationForTable'; import HttpService from '../services/HttpService'; @@ -81,7 +82,7 @@ export default function ProcessGroupShow() { ]} />
        - + - - -
    -
    -
    - + <> + + + + setProcessModelSelection(selection.selectedItem) + } + processModels={processModelAvailableItems} + selectedItem={processModelSelection} + /> + + {processStatusSearch()} + + + + {dateComponent( + 'Start date from', + 'start-from', + startFrom, + setStartFrom + )} + + + {dateComponent('Start date to', 'start-to', startTo, setStartTo)} + + + {dateComponent('End date from', 'end-from', endFrom, setEndFrom)} + + + {dateComponent('End date to', 'end-to', endTo, setEndTo)} + + + + + + + + + + + ); }; @@ -404,7 +415,7 @@ export default function ProcessInstanceList() { ); }); return ( - +
    @@ -436,20 +447,42 @@ export default function ProcessInstanceList() { ); }; + const toggleShowFilterOptions = () => { + setShowFilterOptions(!showFilterOptions); + }; + if (pagination) { const { page, perPage } = getPageInfoFromSearchParams(searchParams); return ( <> {processInstanceTitleElement()} + + + + - - - + + + + setNewFileName(e.target.value)} + autoFocus + /> + {fileExtension} + ); }; @@ -288,7 +283,6 @@ export default function ProcessModelEditDiagram() { // we should update this to act like updating scripts // where we pass an event to bpmn-js setScriptModeling(modeling); - setScriptText(script || ''); setScriptType(scriptTypeString); setScriptEventBus(eventBus); @@ -478,7 +472,7 @@ export default function ProcessModelEditDiagram() { } return (
    - +
    {scriptUnitTestResultBoolElement} - - + + {unitTestFailureElement()} - +
    Input Json:
    @@ -564,28 +558,25 @@ export default function ProcessModelEditDiagram() { if (scriptElement) { scriptName = (scriptElement as any).di.bpmnElement.name; } + return ( - - - Editing Script: {scriptName} - - - - {scriptUnitTestEditorElement()} - - - - + + + {scriptUnitTestEditorElement()} ); }; diff --git a/src/routes/ProcessModelShow.tsx b/src/routes/ProcessModelShow.tsx index 63217d2d..30f81e6d 100644 --- a/src/routes/ProcessModelShow.tsx +++ b/src/routes/ProcessModelShow.tsx @@ -1,6 +1,7 @@ import { useContext, useEffect, useState } from 'react'; import { Link, useParams } from 'react-router-dom'; -import { Button, Stack } from 'react-bootstrap'; +// @ts-ignore +import { Button, Stack } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import FileInput from '../components/FileInput'; import HttpService from '../services/HttpService'; @@ -210,7 +211,7 @@ export default function ProcessModelShow() { const processModelButtons = () => { return ( - + diff --git a/src/routes/ReactFormEditor.tsx b/src/routes/ReactFormEditor.tsx index 40dd2d89..e706a3ef 100644 --- a/src/routes/ReactFormEditor.tsx +++ b/src/routes/ReactFormEditor.tsx @@ -1,7 +1,8 @@ import { useEffect, useState } from 'react'; import Editor from '@monaco-editor/react'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; -import { Button, Modal } from 'react-bootstrap'; +// @ts-ignore +import { Button, Modal } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import HttpService from '../services/HttpService'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; @@ -118,31 +119,25 @@ export default function ReactFormEditor() { const newFileNameBox = () => { return ( - - - Process Model File Name - -
    - - - setNewFileName(e.target.value)} - autoFocus - /> - .{fileExtension} - - - - - - + + + + setNewFileName(e.target.value)} + autoFocus + /> + {fileExtension} + ); }; @@ -160,7 +155,7 @@ export default function ReactFormEditor() { {processModelFile ? `: ${(processModelFile as any).name}` : ''} {newFileNameBox()} - {params.file_name ? ( diff --git a/src/routes/SecretList.tsx b/src/routes/SecretList.tsx index 2b9e756a..7d165c01 100644 --- a/src/routes/SecretList.tsx +++ b/src/routes/SecretList.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; import { Link, useSearchParams } from 'react-router-dom'; -import { Button, Table } from 'react-bootstrap'; +// @ts-ignore +import { Button, Table } from '@carbon/react'; import { MdDelete } from 'react-icons/md'; import PaginationForTable from '../components/PaginationForTable'; import HttpService from '../services/HttpService'; diff --git a/src/routes/SecretNew.tsx b/src/routes/SecretNew.tsx index 7a697a98..016fec2e 100644 --- a/src/routes/SecretNew.tsx +++ b/src/routes/SecretNew.tsx @@ -1,6 +1,7 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Stack } from 'react-bootstrap'; +// @ts-ignore +import { Stack } from '@carbon/react'; import Button from 'react-bootstrap/Button'; import Form from 'react-bootstrap/Form'; import HttpService from '../services/HttpService'; @@ -67,7 +68,7 @@ export default function SecretNew() { }} /> - + diff --git a/src/routes/SecretShow.tsx b/src/routes/SecretShow.tsx index 707f0d14..9882247b 100644 --- a/src/routes/SecretShow.tsx +++ b/src/routes/SecretShow.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; -import { Stack, Table, Button } from 'react-bootstrap'; +// @ts-ignore +import { Stack, Table, Button } from '@carbon/react'; import HttpService from '../services/HttpService'; import { Secret } from '../interfaces'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; @@ -64,8 +65,8 @@ export default function SecretShow() { if (secret) { return ( <> - -

    Secret Key: {secret.key}

    +

    Secret Key: {secret.key}

    + + {userTasksElement} From 34c0b5d2a3cffda0f1f96cd834e9729bf323d968 Mon Sep 17 00:00:00 2001 From: burnettk Date: Mon, 7 Nov 2022 10:10:47 -0500 Subject: [PATCH 46/46] explicit themes --- spiffworkflow-frontend/src/App.tsx | 48 ++++++++++--------- .../src/components/NavigationBar.tsx | 1 - spiffworkflow-frontend/src/index.scss | 11 +++-- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/spiffworkflow-frontend/src/App.tsx b/spiffworkflow-frontend/src/App.tsx index 186ac513..2561c2a8 100644 --- a/spiffworkflow-frontend/src/App.tsx +++ b/spiffworkflow-frontend/src/App.tsx @@ -46,28 +46,30 @@ export default function App() { } return ( - - - - - {errorTag} - - - } /> - } /> - } /> - } - /> - } - /> - - - - - +
    + + + + + {errorTag} + + + } /> + } /> + } /> + } + /> + } + /> + + + + + +
    ); } diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index b3932515..7ecd7c47 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -124,7 +124,6 @@ export default function NavigationBar() { }; if (activeKey) { - // TODO: apply theme g100 to the header return ( ( diff --git a/spiffworkflow-frontend/src/index.scss b/spiffworkflow-frontend/src/index.scss index 7ef64a65..ee9ca2c2 100644 --- a/spiffworkflow-frontend/src/index.scss +++ b/spiffworkflow-frontend/src/index.scss @@ -5,6 +5,11 @@ // @include grid.flex-grid(); // @use '@carbon/react/scss/colors'; -// .cds--header, a.cds--header__menu-item { -// background-color: colors.$gray-100; -// } + +// site is mainly using white theme. +// header is mainly using g100 +// mockup wanted white, not grey, text +.cds--header, a.cds--header__menu-item { + // background-color: colors.$gray-100; + color: white; +}
    Process Instance Id