From cbfc347d10c5b3c92b83fcb01fc9eb23a2e1f765 Mon Sep 17 00:00:00 2001 From: burnettk Date: Wed, 9 Nov 2022 09:18:01 -0500 Subject: [PATCH 01/22] camelcase --- spiffworkflow-frontend/src/components/ProcessModelForm.tsx | 6 +++--- spiffworkflow-frontend/src/routes/ProcessModelEdit.tsx | 2 +- spiffworkflow-frontend/src/routes/ProcessModelNew.tsx | 2 +- spiffworkflow-frontend/src/routes/ReactFormEditor.tsx | 5 ++++- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/spiffworkflow-frontend/src/components/ProcessModelForm.tsx b/spiffworkflow-frontend/src/components/ProcessModelForm.tsx index 49156ed4..9764bad9 100644 --- a/spiffworkflow-frontend/src/components/ProcessModelForm.tsx +++ b/spiffworkflow-frontend/src/components/ProcessModelForm.tsx @@ -14,14 +14,14 @@ import ButtonWithConfirmation from './ButtonWithConfirmation'; type OwnProps = { mode: string; processModel: ProcessModel; - process_group_id?: string; + processGroupId?: string; setProcessModel: (..._args: any[]) => any; }; export default function ProcessModelForm({ mode, processModel, - process_group_id, + processGroupId, setProcessModel, }: OwnProps) { const [identifierInvalid, setIdentifierInvalid] = useState(false); @@ -88,7 +88,7 @@ export default function ProcessModelForm({ }; if (mode === 'new') { Object.assign(postBody, { - id: `${process_group_id}/${processModel.id}`, + id: `${processGroupId}/${processModel.id}`, }); } diff --git a/spiffworkflow-frontend/src/routes/ProcessModelEdit.tsx b/spiffworkflow-frontend/src/routes/ProcessModelEdit.tsx index 4ee063e3..d87c5026 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelEdit.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelEdit.tsx @@ -24,7 +24,7 @@ export default function ProcessModelEdit() {

Edit Process Model: {(processModel as any).id}

diff --git a/spiffworkflow-frontend/src/routes/ProcessModelNew.tsx b/spiffworkflow-frontend/src/routes/ProcessModelNew.tsx index f147fa56..2dcfdb30 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelNew.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelNew.tsx @@ -20,7 +20,7 @@ export default function ProcessModelNew() {

Add Process Model

diff --git a/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx b/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx index d2ba03b7..0e4f9f61 100644 --- a/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx +++ b/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx @@ -145,6 +145,9 @@ export default function ReactFormEditor() { ); }; + const processModelFilePossibleNameString = processModelFile + ? (processModelFile as any).name + : ''; if (processModelFile || !params.file_name) { return (
@@ -158,7 +161,7 @@ export default function ReactFormEditor() { `Process Model: ${params.process_model_id}`, `process_model:${params.process_model_id}:link`, ], - [(processModelFile as any).name || ''], + [processModelFilePossibleNameString], ]} />

From cab007fe419c83730533d2b07414942a417692f9 Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 9 Nov 2022 10:12:13 -0500 Subject: [PATCH 02/22] process model cypress tests are passing --- .../cypress/e2e/process_models.cy.js | 45 ++++++++----------- .../cypress/support/commands.js | 3 +- spiffworkflow-frontend/src/helpers.tsx | 17 +++---- .../src/routes/ProcessInstanceList.tsx | 6 ++- .../src/routes/ProcessModelEditDiagram.tsx | 6 +-- .../src/routes/ProcessModelShow.tsx | 4 +- .../src/routes/ReactFormEditor.tsx | 25 ++++++----- 7 files changed, 52 insertions(+), 54 deletions(-) diff --git a/spiffworkflow-frontend/cypress/e2e/process_models.cy.js b/spiffworkflow-frontend/cypress/e2e/process_models.cy.js index 22eb8b7a..cc4ef606 100644 --- a/spiffworkflow-frontend/cypress/e2e/process_models.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/process_models.cy.js @@ -16,20 +16,14 @@ describe('process-models', () => { const modelId = `test-model-2-${id}`; cy.contains(groupDisplayName).click(); cy.createModel(groupId, modelId, modelDisplayName); - cy.contains(`Process Group: ${groupId}`).click(); - cy.contains(modelId); - - cy.contains(modelId).click(); - cy.url().should('include', `process-models/${groupId}/${modelId}`); - cy.contains(`Process Model: ${modelId}`); + cy.url().should('include', `process-models/${groupId}:${modelId}`); + cy.contains(`Process Model: ${modelDisplayName}`); cy.contains('Edit process model').click(); cy.get('input[name=display_name]').clear().type(newModelDisplayName); cy.contains('Submit').click(); - const modifiedModelId = cy.modifyProcessModelPath(modelId); - cy.contains(`Process Model: ${modifiedModelId}`); - - cy.contains('Edit process model').click(); + cy.contains(`Process Model: ${groupId}/${modelId}`); + cy.contains('Submit').click(); cy.get('input[name=display_name]').should( 'have.value', newModelDisplayName @@ -37,7 +31,7 @@ describe('process-models', () => { 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/${groupId}`); cy.contains(modelId).should('not.exist'); }); @@ -57,8 +51,6 @@ describe('process-models', () => { cy.contains(groupDisplayName).click(); cy.createModel(groupId, modelId, modelDisplayName); cy.contains(groupId).click(); - cy.contains(modelId); - cy.contains(modelId).click(); cy.url().should('include', `process-models/${groupId}:${modelId}`); cy.contains(`Process Model: ${modelDisplayName}`); @@ -117,7 +109,7 @@ describe('process-models', () => { cy.contains('Edit process model').click(); 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/${groupId}`); cy.contains(modelId).should('not.exist'); }); @@ -133,36 +125,35 @@ describe('process-models', () => { cy.contains(groupDisplayName).click(); cy.createModel(groupId, modelId, modelDisplayName); - // seeing if getBySel works better, because we are seeing tests fail in CI - // when looking for the "Add a process model" link, so it seems like the - // click on the breadcrumb element must have failed. - cy.getBySel('process-group-breadcrumb-link').click(); - // cy.contains(`Process Group: ${groupId}`).click(); - + cy.contains(`${groupId}`).click(); cy.contains('Add a process model'); - cy.contains(modelId).click(); - cy.url().should('include', `process-models/${groupId}/${modelId}`); - cy.contains(`Process Model: ${modelId}`); + cy.url().should('include', `process-models/${groupId}:${modelId}`); + cy.contains(`Process Model: ${modelDisplayName}`); - cy.get('input[type=file]').selectFile( + cy.getBySel('files-accordion').click(); + cy.getBySel('upload-file-button').click(); + cy.contains('Add file').selectFile( 'cypress/fixtures/test_bpmn_file_upload.bpmn' ); - cy.contains('Submit').click(); + cy.getBySel('modal-upload-file-dialog') + .find('.cds--btn--primary') + .contains('Upload') + .click(); cy.runPrimaryBpmnFile(); cy.getBySel('process-instance-list-link').click(); cy.getBySel('process-instance-show-link').click(); cy.contains('Delete').click(); cy.contains('Are you sure'); - cy.contains('OK').click(); + cy.getBySel('modal-confirmation-dialog').find('.cds--btn--danger').click(); cy.contains(`Process Instances for: ${groupId}/${modelId}`); cy.contains(modelId).click(); cy.contains('Edit process model').click(); 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/${groupId}`); cy.contains(modelId).should('not.exist'); }); diff --git a/spiffworkflow-frontend/cypress/support/commands.js b/spiffworkflow-frontend/cypress/support/commands.js index 8602b572..c6518917 100644 --- a/spiffworkflow-frontend/cypress/support/commands.js +++ b/spiffworkflow-frontend/cypress/support/commands.js @@ -122,5 +122,6 @@ Cypress.Commands.add('assertNoItemInPaginatedResults', () => { }); Cypress.Commands.add('modifyProcessModelPath', (path) => { - return path.replace('/', ':') || ''; + path.replace('/', ':'); + return path; }); diff --git a/spiffworkflow-frontend/src/helpers.tsx b/spiffworkflow-frontend/src/helpers.tsx index 89521a3a..adb28e73 100644 --- a/spiffworkflow-frontend/src/helpers.tsx +++ b/spiffworkflow-frontend/src/helpers.tsx @@ -95,13 +95,10 @@ export const getProcessModelFullIdentifierFromSearchParams = ( searchParams: any ) => { let processModelFullIdentifier = null; - if ( - searchParams.get('process_model_identifier') && - searchParams.get('process_group_identifier') - ) { + if (searchParams.get('process_model_identifier')) { processModelFullIdentifier = `${searchParams.get( - 'process_group_identifier' - )}/${searchParams.get('process_model_identifier')}`; + 'process_model_identifier' + )}`; } return processModelFullIdentifier; }; @@ -116,19 +113,19 @@ export const truncateString = (text: string, len: number) => { // Because of limitations in the way openapi defines parameters, we have to modify process models ids // which are basically paths to the models -export const modifyProcessModelPath = (path: String) => { +export const modifyProcessModelPath = (path: string) => { return path.replace('/', ':') || ''; }; -export const unModifyProcessModelPath = (path: String) => { +export const unModifyProcessModelPath = (path: string) => { return path.replace(':', '/') || ''; }; -export const getGroupFromModifiedModelId = (modifiedId: String) => { +export const getGroupFromModifiedModelId = (modifiedId: string) => { const finalSplitIndex = modifiedId.lastIndexOf(':'); return modifiedId.slice(0, finalSplitIndex); }; -export const splitProcessModelId = (processModelId: String) => { +export const splitProcessModelId = (processModelId: string) => { return processModelId.split('/'); }; diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx index a515dfdd..32b58ac3 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx @@ -461,7 +461,11 @@ export default function ProcessInstanceList() { return (

Process Instances for:{' '} - + {processModelFullIdentifier}

diff --git a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx index 85dfc482..ede6d85c 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx @@ -776,9 +776,7 @@ export default function ProcessModelEditDiagram() { // if a file name is not given then this is a new model and the ReactDiagramEditor component will handle it if ((bpmnXmlForDiagramRendering || !params.file_name) && processModel) { - const processModelFileName = processModelFile - ? `: ${processModelFile.name}` - : ''; + const processModelFileName = processModelFile ? processModelFile.name : ''; return ( <>

- Process Model File + Process Model File{processModelFile ? ': ' : ''} {processModelFileName}

{appropriateEditor()} diff --git a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx index cd864742..7294112b 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx @@ -400,13 +400,14 @@ export default function ProcessModelShow() { httpMethod: 'POST', postBody: formData, }); - setShowFileUploadModal(false); } + setShowFileUploadModal(false); }; const fileUploadModal = () => { return ( - @@ -392,12 +401,13 @@ export default function ProcessInstanceList() { return headerLabels[header] ?? header; }; const headers = (reportMetadata as any).columns.map((column: any) => { - return {getHeaderLabel((column as any).Header)}; + // return {getHeaderLabel((column as any).Header)}; + return getHeaderLabel((column as any).Header); }); const formatProcessInstanceId = (row: any, id: any) => { const modifiedProcessModelId: String = modifyProcessModelPath( - (row as any).process_model_identifier + row.process_model_identifier ); return ( ); }; - const formatProcessModelIdentifier = (row: any, identifier: any) => { + const formatProcessModelIdentifier = (_row: any, identifier: any) => { return ( ); }; - const formatSecondsForDisplay = (row: any, seconds: any) => { + const formatSecondsForDisplay = (_row: any, seconds: any) => { return convertSecondsToFormattedDate(seconds) || '-'; }; - const defaultFormatter = (row: any, value: any) => { + const defaultFormatter = (_row: any, value: any) => { return value; }; @@ -433,20 +443,32 @@ export default function ProcessInstanceList() { const formattedColumn = (row: any, column: any) => { const formatter = columnFormatters[column.accessor] ?? defaultFormatter; const value = row[column.accessor]; + if (column.accessor === 'status') { + return ( + + {formatter(row, value)} + + ); + } return {formatter(row, value)}; }; - const rows = processInstances.map((row) => { + const rows = processInstances.map((row: any) => { const currentRow = (reportMetadata as any).columns.map((column: any) => { return formattedColumn(row, column); }); - return {currentRow}; + return {currentRow}; }); + return ( - - {headers} - + + + {headers.map((header: any) => ( + {header} + ))} + + {rows}
); @@ -482,9 +504,13 @@ export default function ProcessInstanceList() { <> {processInstanceTitleElement()} - - +

); From 3534a8b54b235fe0455442a2eec7e18ebdd6e2de Mon Sep 17 00:00:00 2001 From: burnettk Date: Wed, 9 Nov 2022 12:46:39 -0500 Subject: [PATCH 07/22] remove leading slash from model identifiers --- .../spiffworkflow_backend/services/process_instance_processor.py | 1 - 1 file changed, 1 deletion(-) 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 34ab46cb..7c27fb54 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -294,7 +294,6 @@ class ProcessInstanceProcessor: # 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 = ( - f"{process_instance_model.process_group_identifier}/" f"{process_instance_model.process_model_identifier}" ) From 02195b84f857589865fd572df0aa335739e9c213 Mon Sep 17 00:00:00 2001 From: burnettk Date: Wed, 9 Nov 2022 13:19:01 -0500 Subject: [PATCH 08/22] fix breadcrumb --- .../src/routes/ProcessInstanceList.tsx | 3 -- .../src/routes/ProcessInstanceShow.tsx | 11 ++++-- .../src/routes/ProcessModelEditDiagram.tsx | 37 ++++++++----------- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx index ad77f002..04fcfd87 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx @@ -20,9 +20,6 @@ import { TableHeader, TableHead, TableRow, - TableBody, - TableCell, - Row, // @ts-ignore } from '@carbon/react'; import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config'; diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index bd20ac31..f04ddda6 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -443,9 +443,14 @@ export default function ProcessInstanceShow() { return ( <>

Process Instance Id: {processInstanceToUse.id}

diff --git a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx index 7d539c33..a60a42f0 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx @@ -684,39 +684,34 @@ export default function ProcessModelEditDiagram() { * fixme: Not currently in use. This would only work for bpmn files within the process model. Which is right for DMN and json, but not right here. Need to merge in work on the nested process groups before tackling this. * @param processId */ + + const fileNameTemplatePath = + '/admin/process-models/:process_model_id/files/:file_name'; + const onLaunchBpmnEditor = (processId: string) => { const file = findFileNameForReferenceId(processId, 'bpmn'); if (file) { - const path = generatePath( - '/admin/process-models/:process_model_id/files/:file_name', - { - process_model_id: params.process_model_id, - file_name: file.name, - } - ); + const path = generatePath(fileNameTemplatePath, { + process_model_id: params.process_model_id, + file_name: file.name, + }); window.open(path); } }; const onLaunchJsonEditor = (fileName: string) => { - const path = generatePath( - '/admin/process-models/:process_model_id/files/:file_name', - { - process_model_id: params.process_model_id, - file_name: fileName, - } - ); + const path = generatePath(fileNameTemplatePath, { + process_model_id: params.process_model_id, + file_name: fileName, + }); window.open(path); }; const onLaunchDmnEditor = (processId: string) => { const file = findFileNameForReferenceId(processId, 'dmn'); if (file) { - const path = generatePath( - '/admin/process-models/:process_model_id/files/:file_name', - { - process_model_id: params.process_model_id, - file_name: file.name, - } - ); + const path = generatePath(fileNameTemplatePath, { + process_model_id: params.process_model_id, + file_name: file.name, + }); window.open(path); } }; From 4a852a5b3b8ff28b2052683f7494adb7e1f310f7 Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Wed, 9 Nov 2022 13:45:16 -0500 Subject: [PATCH 09/22] Move to Python 3.11 (#27) --- .tool-versions | 1 + SpiffWorkflow/.tool-versions | 2 +- flask-bpmn/.tool-versions | 2 +- flask-bpmn/poetry.lock | 323 ++++-------------- flask-bpmn/pyproject.toml | 1 + poetry.lock | 275 ++------------- pyproject.toml | 4 +- spiffworkflow-backend/.tool-versions | 2 +- spiffworkflow-backend/Dockerfile | 2 +- spiffworkflow-backend/bin/update_all_json.py | 6 +- spiffworkflow-backend/conftest.py | 11 +- spiffworkflow-backend/migrations/env.py | 2 - spiffworkflow-backend/poetry.lock | 133 ++------ spiffworkflow-backend/pyproject.toml | 4 +- .../models/process_instance.py | 6 +- .../models/process_instance_report.py | 20 +- .../routes/process_api_blueprint.py | 36 +- .../src/spiffworkflow_backend/routes/user.py | 2 +- .../services/data_setup_service.py | 4 +- .../services/file_system_service.py | 4 +- .../services/process_instance_processor.py | 4 +- .../services/process_instance_service.py | 2 - .../services/process_model_service.py | 30 +- .../services/spec_file_service.py | 21 +- .../helpers/base_test.py | 38 ++- .../helpers/example_data.py | 2 +- .../helpers/test_data.py | 3 +- .../integration/test_logging_service.py | 10 +- .../integration/test_nested_groups.py | 90 ++--- .../integration/test_process_api.py | 168 ++++----- .../integration/test_secret_service.py | 4 +- .../scripts/test_get_group_members.py | 8 +- .../scripts/test_get_localtime.py | 10 +- .../unit/test_authorization_service.py | 11 +- .../unit/test_dot_notation.py | 15 +- .../unit/test_message_instance.py | 46 ++- .../unit/test_message_service.py | 23 +- .../unit/test_permissions.py | 18 +- .../unit/test_process_instance_processor.py | 16 +- .../unit/test_process_model.py | 30 +- .../unit/test_process_model_service.py | 12 +- .../unit/test_restricted_script_engine.py | 20 +- .../unit/test_script_unit_test_runner.py | 32 +- .../unit/test_spec_file_service.py | 33 +- .../unit/test_various_bpmn_constructs.py | 13 +- 45 files changed, 560 insertions(+), 939 deletions(-) create mode 100644 .tool-versions diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000..a7b6ef2e --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +python 3.11.0 diff --git a/SpiffWorkflow/.tool-versions b/SpiffWorkflow/.tool-versions index 7e78d9af..a7b6ef2e 100644 --- a/SpiffWorkflow/.tool-versions +++ b/SpiffWorkflow/.tool-versions @@ -1 +1 @@ -python 3.10.4 +python 3.11.0 diff --git a/flask-bpmn/.tool-versions b/flask-bpmn/.tool-versions index 7e78d9af..a7b6ef2e 100644 --- a/flask-bpmn/.tool-versions +++ b/flask-bpmn/.tool-versions @@ -1 +1 @@ -python 3.10.4 +python 3.11.0 diff --git a/flask-bpmn/poetry.lock b/flask-bpmn/poetry.lock index 7a63efc6..c3297b3f 100644 --- a/flask-bpmn/poetry.lock +++ b/flask-bpmn/poetry.lock @@ -93,17 +93,6 @@ python-versions = ">=3.6" [package.dependencies] pytz = ">=2015.7" -[[package]] -name = "backports.zoneinfo" -version = "0.2.1" -description = "Backport of the standard library zoneinfo module" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -tzdata = ["tzdata"] - [[package]] name = "bandit" version = "1.7.2" @@ -390,25 +379,6 @@ category = "dev" optional = false python-versions = ">=3.6,<4.0" -[[package]] -name = "dateparser" -version = "1.1.1" -description = "Date parsing library designed to parse dates from HTML pages" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -python-dateutil = "*" -pytz = "*" -regex = "<2019.02.19 || >2019.02.19,<2021.8.27 || >2021.8.27,<2022.3.15" -tzlocal = "*" - -[package.extras] -calendars = ["convertdate", "convertdate", "hijri-converter"] -fasttext = ["fasttext"] -langdetect = ["langdetect"] - [[package]] name = "distlib" version = "0.3.4" @@ -704,14 +674,15 @@ typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\"" [[package]] name = "greenlet" -version = "1.1.2" +version = "2.0.1" description = "Lightweight in-process concurrent programming" category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" [package.extras] -docs = ["Sphinx"] +docs = ["Sphinx", "docutils (<0.18)"] +test = ["faulthandler", "objgraph", "psutil"] [[package]] name = "identify" @@ -1173,17 +1144,6 @@ toml = "*" [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] -[[package]] -name = "python-dateutil" -version = "2.8.2" -description = "Extensions to the standard Python datetime module" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" - -[package.dependencies] -six = ">=1.5" - [[package]] name = "pytz" version = "2022.1" @@ -1192,18 +1152,6 @@ category = "main" optional = false python-versions = "*" -[[package]] -name = "pytz-deprecation-shim" -version = "0.1.0.post0" -description = "Shims to make deprecation of pytz easier" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" - -[package.dependencies] -"backports.zoneinfo" = {version = "*", markers = "python_version >= \"3.6\" and python_version < \"3.9\""} -tzdata = {version = "*", markers = "python_version >= \"3.6\""} - [[package]] name = "pyupgrade" version = "3.2.0" @@ -1223,14 +1171,6 @@ category = "main" optional = false python-versions = ">=3.6" -[[package]] -name = "regex" -version = "2022.3.2" -description = "Alternative regular expression module, to replace re." -category = "main" -optional = false -python-versions = ">=3.6" - [[package]] name = "reorder-python-imports" version = "3.9.0" @@ -1569,16 +1509,14 @@ develop = false [package.dependencies] celery = "*" configparser = "*" -dateparser = "*" importlib-metadata = {version = "<5.0", markers = "python_version <= \"3.7\""} lxml = "*" -pytz = "*" [package.source] type = "git" url = "https://github.com/sartography/SpiffWorkflow" reference = "main" -resolved_reference = "5cdb881edc4621502bfd61ce67565cf1148199f0" +resolved_reference = "5d35f5c50d06e35b17c7e045fcf6000a6e16d8b6" [[package]] name = "sqlalchemy" @@ -1697,31 +1635,6 @@ python-versions = "*" mypy-extensions = ">=0.3.0" typing-extensions = ">=3.7.4" -[[package]] -name = "tzdata" -version = "2022.2" -description = "Provider of IANA time zone data" -category = "main" -optional = false -python-versions = ">=2" - -[[package]] -name = "tzlocal" -version = "4.2" -description = "tzinfo object for the local timezone" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -"backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""} -pytz-deprecation-shim = "*" -tzdata = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -devenv = ["black", "pyroma", "pytest-cov", "zest.releaser"] -test = ["pytest (>=4.3)", "pytest-mock (>=3.3)"] - [[package]] name = "unidecode" version = "1.3.4" @@ -1853,7 +1766,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>= [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "7d1d5e13f2546566277c6f0b5935753c89804db2abb7a1e76498b582f40f9a01" +content-hash = "1ccf043617fe4f620f66e6db1ee5f4e1ad0de4aab531981196d1a089bf8b1933" [metadata.files] alabaster = [ @@ -1888,24 +1801,6 @@ babel = [ {file = "Babel-2.10.1-py3-none-any.whl", hash = "sha256:3f349e85ad3154559ac4930c3918247d319f21910d5ce4b25d439ed8693b98d2"}, {file = "Babel-2.10.1.tar.gz", hash = "sha256:98aeaca086133efb3e1e2aad0396987490c8425929ddbcfe0550184fdc54cd13"}, ] -"backports.zoneinfo" = [ - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, - {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, -] bandit = [ {file = "bandit-1.7.2-py3-none-any.whl", hash = "sha256:e20402cadfd126d85b68ed4c8862959663c8c372dbbb1fca8f8e2c9f55a067ec"}, {file = "bandit-1.7.2.tar.gz", hash = "sha256:6d11adea0214a43813887bfe71a377b5a9955e4c826c8ffd341b494e3ab25260"}, @@ -2113,10 +2008,6 @@ darglint = [ {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, {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"}, -] distlib = [ {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, @@ -2203,61 +2094,63 @@ gitpython = [ {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, ] greenlet = [ - {file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"}, - {file = "greenlet-1.1.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a"}, - {file = "greenlet-1.1.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:833e1551925ed51e6b44c800e71e77dacd7e49181fdc9ac9a0bf3714d515785d"}, - {file = "greenlet-1.1.2-cp27-cp27m-win32.whl", hash = "sha256:aa5b467f15e78b82257319aebc78dd2915e4c1436c3c0d1ad6f53e47ba6e2713"}, - {file = "greenlet-1.1.2-cp27-cp27m-win_amd64.whl", hash = "sha256:40b951f601af999a8bf2ce8c71e8aaa4e8c6f78ff8afae7b808aae2dc50d4c40"}, - {file = "greenlet-1.1.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:95e69877983ea39b7303570fa6760f81a3eec23d0e3ab2021b7144b94d06202d"}, - {file = "greenlet-1.1.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:356b3576ad078c89a6107caa9c50cc14e98e3a6c4874a37c3e0273e4baf33de8"}, - {file = "greenlet-1.1.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8639cadfda96737427330a094476d4c7a56ac03de7265622fcf4cfe57c8ae18d"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58"}, - {file = "greenlet-1.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b336501a05e13b616ef81ce329c0e09ac5ed8c732d9ba7e3e983fcc1a9e86965"}, - {file = "greenlet-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708"}, - {file = "greenlet-1.1.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23"}, - {file = "greenlet-1.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee"}, - {file = "greenlet-1.1.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:fa877ca7f6b48054f847b61d6fa7bed5cebb663ebc55e018fda12db09dcc664c"}, - {file = "greenlet-1.1.2-cp35-cp35m-win32.whl", hash = "sha256:7cbd7574ce8e138bda9df4efc6bf2ab8572c9aff640d8ecfece1b006b68da963"}, - {file = "greenlet-1.1.2-cp35-cp35m-win_amd64.whl", hash = "sha256:903bbd302a2378f984aef528f76d4c9b1748f318fe1294961c072bdc7f2ffa3e"}, - {file = "greenlet-1.1.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:049fe7579230e44daef03a259faa24511d10ebfa44f69411d99e6a184fe68073"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:dd0b1e9e891f69e7675ba5c92e28b90eaa045f6ab134ffe70b52e948aa175b3c"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7418b6bfc7fe3331541b84bb2141c9baf1ec7132a7ecd9f375912eca810e714e"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168"}, - {file = "greenlet-1.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b8c008de9d0daba7b6666aa5bbfdc23dcd78cafc33997c9b7741ff6353bafb7f"}, - {file = "greenlet-1.1.2-cp36-cp36m-win32.whl", hash = "sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa"}, - {file = "greenlet-1.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d"}, - {file = "greenlet-1.1.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fdcec0b8399108577ec290f55551d926d9a1fa6cad45882093a7a07ac5ec147b"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:93f81b134a165cc17123626ab8da2e30c0455441d4ab5576eed73a64c025b25c"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5"}, - {file = "greenlet-1.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c5d5b35f789a030ebb95bff352f1d27a93d81069f2adb3182d99882e095cefe"}, - {file = "greenlet-1.1.2-cp37-cp37m-win32.whl", hash = "sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc"}, - {file = "greenlet-1.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06"}, - {file = "greenlet-1.1.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:eb6ea6da4c787111adf40f697b4e58732ee0942b5d3bd8f435277643329ba627"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f3acda1924472472ddd60c29e5b9db0cec629fbe3c5c5accb74d6d6d14773478"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b"}, - {file = "greenlet-1.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2bde6792f313f4e918caabc46532aa64aa27a0db05d75b20edfc5c6f46479de2"}, - {file = "greenlet-1.1.2-cp38-cp38-win32.whl", hash = "sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd"}, - {file = "greenlet-1.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3"}, - {file = "greenlet-1.1.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:572e1787d1460da79590bf44304abbc0a2da944ea64ec549188fa84d89bba7ab"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:be5f425ff1f5f4b3c1e33ad64ab994eed12fc284a6ea71c5243fd564502ecbe5"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3"}, - {file = "greenlet-1.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0051c6f1f27cb756ffc0ffbac7d2cd48cb0362ac1736871399a739b2885134d3"}, - {file = "greenlet-1.1.2-cp39-cp39-win32.whl", hash = "sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf"}, - {file = "greenlet-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd"}, - {file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"}, + {file = "greenlet-2.0.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:9ed358312e63bf683b9ef22c8e442ef6c5c02973f0c2a939ec1d7b50c974015c"}, + {file = "greenlet-2.0.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4f09b0010e55bec3239278f642a8a506b91034f03a4fb28289a7d448a67f1515"}, + {file = "greenlet-2.0.1-cp27-cp27m-win32.whl", hash = "sha256:1407fe45246632d0ffb7a3f4a520ba4e6051fc2cbd61ba1f806900c27f47706a"}, + {file = "greenlet-2.0.1-cp27-cp27m-win_amd64.whl", hash = "sha256:3001d00eba6bbf084ae60ec7f4bb8ed375748f53aeaefaf2a37d9f0370558524"}, + {file = "greenlet-2.0.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d566b82e92ff2e09dd6342df7e0eb4ff6275a3f08db284888dcd98134dbd4243"}, + {file = "greenlet-2.0.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:0722c9be0797f544a3ed212569ca3fe3d9d1a1b13942d10dd6f0e8601e484d26"}, + {file = "greenlet-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d37990425b4687ade27810e3b1a1c37825d242ebc275066cfee8cb6b8829ccd"}, + {file = "greenlet-2.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be35822f35f99dcc48152c9839d0171a06186f2d71ef76dc57fa556cc9bf6b45"}, + {file = "greenlet-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c140e7eb5ce47249668056edf3b7e9900c6a2e22fb0eaf0513f18a1b2c14e1da"}, + {file = "greenlet-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d21681f09e297a5adaa73060737e3aa1279a13ecdcfcc6ef66c292cb25125b2d"}, + {file = "greenlet-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fb412b7db83fe56847df9c47b6fe3f13911b06339c2aa02dcc09dce8bbf582cd"}, + {file = "greenlet-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6a08799e9e88052221adca55741bf106ec7ea0710bca635c208b751f0d5b617"}, + {file = "greenlet-2.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e112e03d37987d7b90c1e98ba5e1b59e1645226d78d73282f45b326f7bddcb9"}, + {file = "greenlet-2.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56961cfca7da2fdd178f95ca407fa330c64f33289e1804b592a77d5593d9bd94"}, + {file = "greenlet-2.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13ba6e8e326e2116c954074c994da14954982ba2795aebb881c07ac5d093a58a"}, + {file = "greenlet-2.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bf633a50cc93ed17e494015897361010fc08700d92676c87931d3ea464123ce"}, + {file = "greenlet-2.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9f2c221eecb7ead00b8e3ddb913c67f75cba078fd1d326053225a3f59d850d72"}, + {file = "greenlet-2.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:13ebf93c343dd8bd010cd98e617cb4c1c1f352a0cf2524c82d3814154116aa82"}, + {file = "greenlet-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:6f61d71bbc9b4a3de768371b210d906726535d6ca43506737682caa754b956cd"}, + {file = "greenlet-2.0.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:2d0bac0385d2b43a7bd1d651621a4e0f1380abc63d6fb1012213a401cbd5bf8f"}, + {file = "greenlet-2.0.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:f6327b6907b4cb72f650a5b7b1be23a2aab395017aa6f1adb13069d66360eb3f"}, + {file = "greenlet-2.0.1-cp35-cp35m-win32.whl", hash = "sha256:81b0ea3715bf6a848d6f7149d25bf018fd24554a4be01fcbbe3fdc78e890b955"}, + {file = "greenlet-2.0.1-cp35-cp35m-win_amd64.whl", hash = "sha256:38255a3f1e8942573b067510f9611fc9e38196077b0c8eb7a8c795e105f9ce77"}, + {file = "greenlet-2.0.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:04957dc96669be041e0c260964cfef4c77287f07c40452e61abe19d647505581"}, + {file = "greenlet-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:4aeaebcd91d9fee9aa768c1b39cb12214b30bf36d2b7370505a9f2165fedd8d9"}, + {file = "greenlet-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:974a39bdb8c90a85982cdb78a103a32e0b1be986d411303064b28a80611f6e51"}, + {file = "greenlet-2.0.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dca09dedf1bd8684767bc736cc20c97c29bc0c04c413e3276e0962cd7aeb148"}, + {file = "greenlet-2.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4c0757db9bd08470ff8277791795e70d0bf035a011a528ee9a5ce9454b6cba2"}, + {file = "greenlet-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:5067920de254f1a2dee8d3d9d7e4e03718e8fd2d2d9db962c8c9fa781ae82a39"}, + {file = "greenlet-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:5a8e05057fab2a365c81abc696cb753da7549d20266e8511eb6c9d9f72fe3e92"}, + {file = "greenlet-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:3d75b8d013086b08e801fbbb896f7d5c9e6ccd44f13a9241d2bf7c0df9eda928"}, + {file = "greenlet-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:097e3dae69321e9100202fc62977f687454cd0ea147d0fd5a766e57450c569fd"}, + {file = "greenlet-2.0.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:cb242fc2cda5a307a7698c93173d3627a2a90d00507bccf5bc228851e8304963"}, + {file = "greenlet-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:72b00a8e7c25dcea5946692a2485b1a0c0661ed93ecfedfa9b6687bd89a24ef5"}, + {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5b0ff9878333823226d270417f24f4d06f235cb3e54d1103b71ea537a6a86ce"}, + {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be9e0fb2ada7e5124f5282d6381903183ecc73ea019568d6d63d33f25b2a9000"}, + {file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b493db84d124805865adc587532ebad30efa68f79ad68f11b336e0a51ec86c2"}, + {file = "greenlet-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a20d33124935d27b80e6fdacbd34205732660e0a1d35d8b10b3328179a2b51a1"}, + {file = "greenlet-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:ea688d11707d30e212e0110a1aac7f7f3f542a259235d396f88be68b649e47d1"}, + {file = "greenlet-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:afe07421c969e259e9403c3bb658968702bc3b78ec0b6fde3ae1e73440529c23"}, + {file = "greenlet-2.0.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:cd4ccc364cf75d1422e66e247e52a93da6a9b73cefa8cad696f3cbbb75af179d"}, + {file = "greenlet-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c8b1c43e75c42a6cafcc71defa9e01ead39ae80bd733a2608b297412beede68"}, + {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:659f167f419a4609bc0516fb18ea69ed39dbb25594934bd2dd4d0401660e8a1e"}, + {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:356e4519d4dfa766d50ecc498544b44c0249b6de66426041d7f8b751de4d6b48"}, + {file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:811e1d37d60b47cb8126e0a929b58c046251f28117cb16fcd371eed61f66b764"}, + {file = "greenlet-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0109af1138afbfb8ae647e31a2b1ab030f58b21dd8528c27beaeb0093b7938a9"}, + {file = "greenlet-2.0.1-cp38-cp38-win32.whl", hash = "sha256:88c8d517e78acdf7df8a2134a3c4b964415b575d2840a2746ddb1cc6175f8608"}, + {file = "greenlet-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:d6ee1aa7ab36475035eb48c01efae87d37936a8173fc4d7b10bb02c2d75dd8f6"}, + {file = "greenlet-2.0.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:b1992ba9d4780d9af9726bbcef6a1db12d9ab1ccc35e5773685a24b7fb2758eb"}, + {file = "greenlet-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:b5e83e4de81dcc9425598d9469a624826a0b1211380ac444c7c791d4a2137c19"}, + {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:505138d4fa69462447a562a7c2ef723c6025ba12ac04478bc1ce2fcc279a2db5"}, + {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce1e90dd302f45716a7715517c6aa0468af0bf38e814ad4eab58e88fc09f7f7"}, + {file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e9744c657d896c7b580455e739899e492a4a452e2dd4d2b3e459f6b244a638d"}, + {file = "greenlet-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:41b825d65f31e394b523c84db84f9383a2f7eefc13d987f308f4663794d2687e"}, + {file = "greenlet-2.0.1-cp39-cp39-win32.whl", hash = "sha256:db38f80540083ea33bdab614a9d28bcec4b54daa5aff1668d7827a9fc769ae0a"}, + {file = "greenlet-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b23d2a46d53210b498e5b701a1913697671988f4bf8e10f935433f6e7c332fb6"}, + {file = "greenlet-2.0.1.tar.gz", hash = "sha256:42e602564460da0e8ee67cb6d7236363ee5e131aa15943b6670e44e5c2ed0f67"}, ] identify = [ {file = "identify-2.5.0-py2.py3-none-any.whl", hash = "sha256:3acfe15a96e4272b4ec5662ee3e231ceba976ef63fd9980ed2ce9cc415df393f"}, @@ -2595,18 +2488,10 @@ pytest = [ {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, ] -python-dateutil = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] pytz = [ {file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"}, {file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"}, ] -pytz-deprecation-shim = [ - {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, - {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"}, -] pyupgrade = [ {file = "pyupgrade-3.2.0-py2.py3-none-any.whl", hash = "sha256:2aa6c40e49ea5a350e6e45b8c7847b1741aef274a35d4f0b2bf91731ec8ab796"}, {file = "pyupgrade-3.2.0.tar.gz", hash = "sha256:70e1ac1e6b34a90fb21f5cada1907ef035b12dfc1d9f13cefd367acf3b530310"}, @@ -2653,82 +2538,6 @@ pyyaml = [ {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] -regex = [ - {file = "regex-2022.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab69b4fe09e296261377d209068d52402fb85ef89dc78a9ac4a29a895f4e24a7"}, - {file = "regex-2022.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5bc5f921be39ccb65fdda741e04b2555917a4bced24b4df14eddc7569be3b493"}, - {file = "regex-2022.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43eba5c46208deedec833663201752e865feddc840433285fbadee07b84b464d"}, - {file = "regex-2022.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c68d2c04f7701a418ec2e5631b7f3552efc32f6bcc1739369c6eeb1af55f62e0"}, - {file = "regex-2022.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:caa2734ada16a44ae57b229d45091f06e30a9a52ace76d7574546ab23008c635"}, - {file = "regex-2022.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef806f684f17dbd6263d72a54ad4073af42b42effa3eb42b877e750c24c76f86"}, - {file = "regex-2022.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be319f4eb400ee567b722e9ea63d5b2bb31464e3cf1b016502e3ee2de4f86f5c"}, - {file = "regex-2022.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:42bb37e2b2d25d958c25903f6125a41aaaa1ed49ca62c103331f24b8a459142f"}, - {file = "regex-2022.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fbc88d3ba402b5d041d204ec2449c4078898f89c4a6e6f0ed1c1a510ef1e221d"}, - {file = "regex-2022.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:91e0f7e7be77250b808a5f46d90bf0032527d3c032b2131b63dee54753a4d729"}, - {file = "regex-2022.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:cb3652bbe6720786b9137862205986f3ae54a09dec8499a995ed58292bdf77c2"}, - {file = "regex-2022.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:878c626cbca3b649e14e972c14539a01191d79e58934e3f3ef4a9e17f90277f8"}, - {file = "regex-2022.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6df070a986fc064d865c381aecf0aaff914178fdf6874da2f2387e82d93cc5bd"}, - {file = "regex-2022.3.2-cp310-cp310-win32.whl", hash = "sha256:b549d851f91a4efb3e65498bd4249b1447ab6035a9972f7fc215eb1f59328834"}, - {file = "regex-2022.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:8babb2b5751105dc0aef2a2e539f4ba391e738c62038d8cb331c710f6b0f3da7"}, - {file = "regex-2022.3.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1977bb64264815d3ef016625adc9df90e6d0e27e76260280c63eca993e3f455f"}, - {file = "regex-2022.3.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e73652057473ad3e6934944af090852a02590c349357b79182c1b681da2c772"}, - {file = "regex-2022.3.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b22ff939a8856a44f4822da38ef4868bd3a9ade22bb6d9062b36957c850e404f"}, - {file = "regex-2022.3.2-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:878f5d649ba1db9f52cc4ef491f7dba2d061cdc48dd444c54260eebc0b1729b9"}, - {file = "regex-2022.3.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0008650041531d0eadecc96a73d37c2dc4821cf51b0766e374cb4f1ddc4e1c14"}, - {file = "regex-2022.3.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06b1df01cf2aef3a9790858af524ae2588762c8a90e784ba00d003f045306204"}, - {file = "regex-2022.3.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57484d39447f94967e83e56db1b1108c68918c44ab519b8ecfc34b790ca52bf7"}, - {file = "regex-2022.3.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:74d86e8924835f863c34e646392ef39039405f6ce52956d8af16497af4064a30"}, - {file = "regex-2022.3.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:ae17fc8103f3b63345709d3e9654a274eee1c6072592aec32b026efd401931d0"}, - {file = "regex-2022.3.2-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:5f92a7cdc6a0ae2abd184e8dfd6ef2279989d24c85d2c85d0423206284103ede"}, - {file = "regex-2022.3.2-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:5dcc4168536c8f68654f014a3db49b6b4a26b226f735708be2054314ed4964f4"}, - {file = "regex-2022.3.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:1e30762ddddb22f7f14c4f59c34d3addabc789216d813b0f3e2788d7bcf0cf29"}, - {file = "regex-2022.3.2-cp36-cp36m-win32.whl", hash = "sha256:286ff9ec2709d56ae7517040be0d6c502642517ce9937ab6d89b1e7d0904f863"}, - {file = "regex-2022.3.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d326ff80ed531bf2507cba93011c30fff2dd51454c85f55df0f59f2030b1687b"}, - {file = "regex-2022.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9d828c5987d543d052b53c579a01a52d96b86f937b1777bbfe11ef2728929357"}, - {file = "regex-2022.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c87ac58b9baaf50b6c1b81a18d20eda7e2883aa9a4fb4f1ca70f2e443bfcdc57"}, - {file = "regex-2022.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6c2441538e4fadd4291c8420853431a229fcbefc1bf521810fbc2629d8ae8c2"}, - {file = "regex-2022.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f3356afbb301ec34a500b8ba8b47cba0b44ed4641c306e1dd981a08b416170b5"}, - {file = "regex-2022.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d96eec8550fd2fd26f8e675f6d8b61b159482ad8ffa26991b894ed5ee19038b"}, - {file = "regex-2022.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf668f26604e9f7aee9f8eaae4ca07a948168af90b96be97a4b7fa902a6d2ac1"}, - {file = "regex-2022.3.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0eb0e2845e81bdea92b8281a3969632686502565abf4a0b9e4ab1471c863d8f3"}, - {file = "regex-2022.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:87bc01226cd288f0bd9a4f9f07bf6827134dc97a96c22e2d28628e824c8de231"}, - {file = "regex-2022.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:09b4b6ccc61d4119342b26246ddd5a04accdeebe36bdfe865ad87a0784efd77f"}, - {file = "regex-2022.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:9557545c10d52c845f270b665b52a6a972884725aa5cf12777374e18f2ea8960"}, - {file = "regex-2022.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:0be0c34a39e5d04a62fd5342f0886d0e57592a4f4993b3f9d257c1f688b19737"}, - {file = "regex-2022.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7b103dffb9f6a47ed7ffdf352b78cfe058b1777617371226c1894e1be443afec"}, - {file = "regex-2022.3.2-cp37-cp37m-win32.whl", hash = "sha256:f8169ec628880bdbca67082a9196e2106060a4a5cbd486ac51881a4df805a36f"}, - {file = "regex-2022.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:4b9c16a807b17b17c4fa3a1d8c242467237be67ba92ad24ff51425329e7ae3d0"}, - {file = "regex-2022.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:67250b36edfa714ba62dc62d3f238e86db1065fccb538278804790f578253640"}, - {file = "regex-2022.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5510932596a0f33399b7fff1bd61c59c977f2b8ee987b36539ba97eb3513584a"}, - {file = "regex-2022.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6f7ee2289176cb1d2c59a24f50900f8b9580259fa9f1a739432242e7d254f93"}, - {file = "regex-2022.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d7a68fa53688e1f612c3246044157117403c7ce19ebab7d02daf45bd63913e"}, - {file = "regex-2022.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaf5317c961d93c1a200b9370fb1c6b6836cc7144fef3e5a951326912bf1f5a3"}, - {file = "regex-2022.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad397bc7d51d69cb07ef89e44243f971a04ce1dca9bf24c992c362406c0c6573"}, - {file = "regex-2022.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:297c42ede2c81f0cb6f34ea60b5cf6dc965d97fa6936c11fc3286019231f0d66"}, - {file = "regex-2022.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:af4d8cc28e4c7a2f6a9fed544228c567340f8258b6d7ea815b62a72817bbd178"}, - {file = "regex-2022.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:452519bc4c973e961b1620c815ea6dd8944a12d68e71002be5a7aff0a8361571"}, - {file = "regex-2022.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cb34c2d66355fb70ae47b5595aafd7218e59bb9c00ad8cc3abd1406ca5874f07"}, - {file = "regex-2022.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d146e5591cb67c5e836229a04723a30af795ef9b70a0bbd913572e14b7b940f"}, - {file = "regex-2022.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:03299b0bcaa7824eb7c0ebd7ef1e3663302d1b533653bfe9dc7e595d453e2ae9"}, - {file = "regex-2022.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9ccb0a4ab926016867260c24c192d9df9586e834f5db83dfa2c8fffb3a6e5056"}, - {file = "regex-2022.3.2-cp38-cp38-win32.whl", hash = "sha256:f7e8f1ee28e0a05831c92dc1c0c1c94af5289963b7cf09eca5b5e3ce4f8c91b0"}, - {file = "regex-2022.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:35ed2f3c918a00b109157428abfc4e8d1ffabc37c8f9abc5939ebd1e95dabc47"}, - {file = "regex-2022.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:55820bc631684172b9b56a991d217ec7c2e580d956591dc2144985113980f5a3"}, - {file = "regex-2022.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:83f03f0bd88c12e63ca2d024adeee75234d69808b341e88343b0232329e1f1a1"}, - {file = "regex-2022.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42d6007722d46bd2c95cce700181570b56edc0dcbadbfe7855ec26c3f2d7e008"}, - {file = "regex-2022.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:320c2f4106962ecea0f33d8d31b985d3c185757c49c1fb735501515f963715ed"}, - {file = "regex-2022.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbd3fe37353c62fd0eb19fb76f78aa693716262bcd5f9c14bb9e5aca4b3f0dc4"}, - {file = "regex-2022.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17e51ad1e6131c496b58d317bc9abec71f44eb1957d32629d06013a21bc99cac"}, - {file = "regex-2022.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72bc3a5effa5974be6d965ed8301ac1e869bc18425c8a8fac179fbe7876e3aee"}, - {file = "regex-2022.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e5602a9b5074dcacc113bba4d2f011d2748f50e3201c8139ac5b68cf2a76bd8b"}, - {file = "regex-2022.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:729aa8ca624c42f309397c5fc9e21db90bf7e2fdd872461aabdbada33de9063c"}, - {file = "regex-2022.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d6ecfd1970b3380a569d7b3ecc5dd70dba295897418ed9e31ec3c16a5ab099a5"}, - {file = "regex-2022.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:13bbf0c9453c6d16e5867bda7f6c0c7cff1decf96c5498318bb87f8136d2abd4"}, - {file = "regex-2022.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:58ba41e462653eaf68fc4a84ec4d350b26a98d030be1ab24aba1adcc78ffe447"}, - {file = "regex-2022.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c0446b2871335d5a5e9fcf1462f954586b09a845832263db95059dcd01442015"}, - {file = "regex-2022.3.2-cp39-cp39-win32.whl", hash = "sha256:20e6a27959f162f979165e496add0d7d56d7038237092d1aba20b46de79158f1"}, - {file = "regex-2022.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:9efa41d1527b366c88f265a227b20bcec65bda879962e3fc8a2aee11e81266d7"}, - {file = "regex-2022.3.2.tar.gz", hash = "sha256:79e5af1ff258bc0fe0bdd6f69bc4ae33935a898e3cbefbbccf22e88a27fa053b"}, -] reorder-python-imports = [ {file = "reorder_python_imports-3.9.0-py2.py3-none-any.whl", hash = "sha256:3f9c16e8781f54c944756d0d1eb34a8c863554f7a4eb3693f574fe19b1a29b56"}, {file = "reorder_python_imports-3.9.0.tar.gz", hash = "sha256:49292ed537829a6bece9fb3746fc1bbe98f52643be5de01a4e13680268a5b0ec"}, @@ -2991,14 +2800,6 @@ typing-inspect = [ {file = "typing_inspect-0.7.1-py3-none-any.whl", hash = "sha256:3cd7d4563e997719a710a3bfe7ffb544c6b72069b6812a02e9b414a8fa3aaa6b"}, {file = "typing_inspect-0.7.1.tar.gz", hash = "sha256:047d4097d9b17f46531bf6f014356111a1b6fb821a24fe7ac909853ca2a782aa"}, ] -tzdata = [ - {file = "tzdata-2022.2-py2.py3-none-any.whl", hash = "sha256:c3119520447d68ef3eb8187a55a4f44fa455f30eb1b4238fa5691ba094f2b05b"}, - {file = "tzdata-2022.2.tar.gz", hash = "sha256:21f4f0d7241572efa7f7a4fdabb052e61b55dc48274e6842697ccdf5253e5451"}, -] -tzlocal = [ - {file = "tzlocal-4.2-py3-none-any.whl", hash = "sha256:89885494684c929d9191c57aa27502afc87a579be5cdd3225c77c463ea043745"}, - {file = "tzlocal-4.2.tar.gz", hash = "sha256:ee5842fa3a795f023514ac2d801c4a81d1743bbe642e3940143326b3a00addd7"}, -] unidecode = [ {file = "Unidecode-1.3.4-py3-none-any.whl", hash = "sha256:afa04efcdd818a93237574791be9b2817d7077c25a068b00f8cff7baa4e59257"}, {file = "Unidecode-1.3.4.tar.gz", hash = "sha256:8e4352fb93d5a735c788110d2e7ac8e8031eb06ccbfe8d324ab71735015f9342"}, diff --git a/flask-bpmn/pyproject.toml b/flask-bpmn/pyproject.toml index 352cd2dd..a68b6d92 100644 --- a/flask-bpmn/pyproject.toml +++ b/flask-bpmn/pyproject.toml @@ -30,6 +30,7 @@ werkzeug = "*" spiffworkflow = "*" sentry-sdk = "*" sphinx-autoapi = "^2.0.0" +greenlet = "^2.0.1" [tool.poetry.dev-dependencies] diff --git a/poetry.lock b/poetry.lock index 79f92ac8..4258ac61 100644 --- a/poetry.lock +++ b/poetry.lock @@ -80,8 +80,7 @@ python-versions = ">=3.7.2" [package.dependencies] lazy-object-proxy = ">=1.4.0" -typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} -wrapt = {version = ">=1.11,<2", markers = "python_version < \"3.11\""} +wrapt = {version = ">=1.14,<2", markers = "python_version >= \"3.11\""} [[package]] name = "attrs" @@ -175,8 +174,6 @@ click = ">=8.0.0" mypy-extensions = ">=0.4.3" pathspec = ">=0.9.0" platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] colorama = ["colorama (>=0.4.3)"] @@ -394,9 +391,6 @@ category = "dev" optional = false python-versions = ">=3.7" -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli"] @@ -408,25 +402,6 @@ category = "dev" optional = false python-versions = ">=3.6,<4.0" -[[package]] -name = "dateparser" -version = "1.1.2" -description = "Date parsing library designed to parse dates from HTML pages" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -python-dateutil = "*" -pytz = "*" -regex = "<2019.02.19 || >2019.02.19,<2021.8.27 || >2021.8.27,<2022.3.15" -tzlocal = "*" - -[package.extras] -calendars = ["convertdate", "convertdate", "hijri-converter"] -fasttext = ["fasttext"] -langdetect = ["langdetect"] - [[package]] name = "distlib" version = "0.3.6" @@ -474,17 +449,6 @@ six = ">=1.9.0" gmpy = ["gmpy"] gmpy2 = ["gmpy2"] -[[package]] -name = "exceptiongroup" -version = "1.0.0" -description = "Backport of PEP 654 (exception groups)" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "filelock" version = "3.8.0" @@ -585,7 +549,6 @@ python-versions = ">=3.7" [package.dependencies] click = ">=8.0" -importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} itsdangerous = ">=2.0" Jinja2 = ">=3.0" Werkzeug = ">=2.2.2" @@ -650,7 +613,7 @@ werkzeug = "*" type = "git" url = "https://github.com/sartography/flask-bpmn" reference = "main" -resolved_reference = "a8b90f2ca09ef1cbb24a491c36f1cc9437477325" +resolved_reference = "17434e0907cc35914d013614bb79288eed1bd437" [[package]] name = "flask-cors" @@ -829,22 +792,6 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -[[package]] -name = "importlib-metadata" -version = "5.0.0" -description = "Read metadata from Python packages" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] - [[package]] name = "inflection" version = "0.5.1" @@ -1078,7 +1025,6 @@ python-versions = ">=3.7" [package.dependencies] mypy-extensions = ">=0.4.3" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=3.10" [package.extras] @@ -1217,7 +1163,6 @@ python-versions = ">=3.7" [package.dependencies] "ruamel.yaml" = ">=0.15" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [[package]] name = "prompt-toolkit" @@ -1339,11 +1284,9 @@ python-versions = ">=3.7" [package.dependencies] attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] @@ -1396,17 +1339,6 @@ pytest = ">=5.0" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] -[[package]] -name = "python-dateutil" -version = "2.8.2" -description = "Extensions to the standard Python datetime module" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" - -[package.dependencies] -six = ">=1.5" - [[package]] name = "python-jose" version = "3.3.0" @@ -1480,14 +1412,6 @@ category = "main" optional = false python-versions = ">=3.6" -[[package]] -name = "regex" -version = "2022.3.2" -description = "Alternative regular expression module, to replace re." -category = "main" -optional = false -python-versions = ">=3.6" - [[package]] name = "reorder-python-imports" version = "3.9.0" @@ -1530,11 +1454,11 @@ requests = ">=2.0.1,<3.0.0" [[package]] name = "restrictedpython" -version = "5.2" +version = "6.0" description = "RestrictedPython is a defined subset of the Python language which allows to provide a program input into a trusted environment." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <3.11" +python-versions = ">=3.6, <3.12" [package.extras] docs = ["Sphinx", "sphinx-rtd-theme"] @@ -1570,21 +1494,10 @@ category = "dev" optional = false python-versions = ">=3" -[package.dependencies] -"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} - [package.extras] docs = ["ryd"] jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] -[[package]] -name = "ruamel-yaml-clib" -version = "0.2.7" -description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -category = "dev" -optional = false -python-versions = ">=3.5" - [[package]] name = "safety" version = "2.3.1" @@ -1704,7 +1617,6 @@ babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.14,<0.20" imagesize = ">=1.3" -importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.12" @@ -1868,15 +1780,13 @@ develop = false [package.dependencies] celery = "*" configparser = "*" -dateparser = "*" lxml = "*" -pytz = "*" [package.source] type = "git" url = "https://github.com/sartography/SpiffWorkflow" reference = "main" -resolved_reference = "12f81480a5f9e848e64221b9287c6cfa6cb682b4" +resolved_reference = "8d820dce1f439bb76bc07e39629832d998d6f634" [[package]] name = "sqlalchemy" @@ -1967,14 +1877,6 @@ category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -category = "main" -optional = false -python-versions = ">=3.7" - [[package]] name = "tornado" version = "6.2" @@ -2236,22 +2138,10 @@ runtime-strict = ["six (==1.11.0)"] tests = ["cmake", "codecov", "ninja", "pybind11", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest-cov", "pytest-cov", "pytest-cov", "pytest-cov", "scikit-build", "typing"] tests-strict = ["cmake (==3.21.2)", "codecov (==2.0.15)", "ninja (==1.10.2)", "pybind11 (==2.7.1)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "scikit-build (==0.11.1)", "typing (==3.7.4)"] -[[package]] -name = "zipp" -version = "3.10.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - [metadata] lock-version = "1.1" -python-versions = ">=3.9,<3.11" -content-hash = "bfb51ebc4ef76d4a74f670f44dc4d7ca7e91874b096f56521c2776f1837f6a63" +python-versions = ">=3.11,<3.12" +content-hash = "2b730b9fd814bf805d68d2d7e3bc1caaf2e907c9f4bab4085f34322995670d27" [metadata.files] alabaster = [ @@ -2456,10 +2346,6 @@ darglint = [ {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, ] -dateparser = [ - {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"}, {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, @@ -2476,10 +2362,6 @@ ecdsa = [ {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, ] -exceptiongroup = [ - {file = "exceptiongroup-1.0.0-py3-none-any.whl", hash = "sha256:2ac84b496be68464a2da60da518af3785fff8b7ec0d090a581604bc870bdee41"}, - {file = "exceptiongroup-1.0.0.tar.gz", hash = "sha256:affbabf13fb6e98988c38d9c5650e701569fe3c1de3233cfb61c5f33774690ad"}, -] filelock = [ {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, @@ -2620,7 +2502,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"}, @@ -2638,10 +2519,6 @@ imagesize = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {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"}, -] inflection = [ {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, @@ -3043,7 +2920,18 @@ psycopg2 = [ {file = "psycopg2-2.9.5.tar.gz", hash = "sha256:a5246d2e683a972e2187a8714b5c2cf8156c064629f9a9b1a873c1730d9e245a"}, ] 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 = [ @@ -3109,10 +2997,6 @@ pytest-mock = [ {file = "pytest-mock-3.10.0.tar.gz", hash = "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f"}, {file = "pytest_mock-3.10.0-py3-none-any.whl", hash = "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b"}, ] -python-dateutil = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] python-jose = [ {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, @@ -3175,82 +3059,6 @@ pyyaml = [ {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] -regex = [ - {file = "regex-2022.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab69b4fe09e296261377d209068d52402fb85ef89dc78a9ac4a29a895f4e24a7"}, - {file = "regex-2022.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5bc5f921be39ccb65fdda741e04b2555917a4bced24b4df14eddc7569be3b493"}, - {file = "regex-2022.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43eba5c46208deedec833663201752e865feddc840433285fbadee07b84b464d"}, - {file = "regex-2022.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c68d2c04f7701a418ec2e5631b7f3552efc32f6bcc1739369c6eeb1af55f62e0"}, - {file = "regex-2022.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:caa2734ada16a44ae57b229d45091f06e30a9a52ace76d7574546ab23008c635"}, - {file = "regex-2022.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef806f684f17dbd6263d72a54ad4073af42b42effa3eb42b877e750c24c76f86"}, - {file = "regex-2022.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be319f4eb400ee567b722e9ea63d5b2bb31464e3cf1b016502e3ee2de4f86f5c"}, - {file = "regex-2022.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:42bb37e2b2d25d958c25903f6125a41aaaa1ed49ca62c103331f24b8a459142f"}, - {file = "regex-2022.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fbc88d3ba402b5d041d204ec2449c4078898f89c4a6e6f0ed1c1a510ef1e221d"}, - {file = "regex-2022.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:91e0f7e7be77250b808a5f46d90bf0032527d3c032b2131b63dee54753a4d729"}, - {file = "regex-2022.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:cb3652bbe6720786b9137862205986f3ae54a09dec8499a995ed58292bdf77c2"}, - {file = "regex-2022.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:878c626cbca3b649e14e972c14539a01191d79e58934e3f3ef4a9e17f90277f8"}, - {file = "regex-2022.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6df070a986fc064d865c381aecf0aaff914178fdf6874da2f2387e82d93cc5bd"}, - {file = "regex-2022.3.2-cp310-cp310-win32.whl", hash = "sha256:b549d851f91a4efb3e65498bd4249b1447ab6035a9972f7fc215eb1f59328834"}, - {file = "regex-2022.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:8babb2b5751105dc0aef2a2e539f4ba391e738c62038d8cb331c710f6b0f3da7"}, - {file = "regex-2022.3.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1977bb64264815d3ef016625adc9df90e6d0e27e76260280c63eca993e3f455f"}, - {file = "regex-2022.3.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e73652057473ad3e6934944af090852a02590c349357b79182c1b681da2c772"}, - {file = "regex-2022.3.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b22ff939a8856a44f4822da38ef4868bd3a9ade22bb6d9062b36957c850e404f"}, - {file = "regex-2022.3.2-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:878f5d649ba1db9f52cc4ef491f7dba2d061cdc48dd444c54260eebc0b1729b9"}, - {file = "regex-2022.3.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0008650041531d0eadecc96a73d37c2dc4821cf51b0766e374cb4f1ddc4e1c14"}, - {file = "regex-2022.3.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06b1df01cf2aef3a9790858af524ae2588762c8a90e784ba00d003f045306204"}, - {file = "regex-2022.3.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57484d39447f94967e83e56db1b1108c68918c44ab519b8ecfc34b790ca52bf7"}, - {file = "regex-2022.3.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:74d86e8924835f863c34e646392ef39039405f6ce52956d8af16497af4064a30"}, - {file = "regex-2022.3.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:ae17fc8103f3b63345709d3e9654a274eee1c6072592aec32b026efd401931d0"}, - {file = "regex-2022.3.2-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:5f92a7cdc6a0ae2abd184e8dfd6ef2279989d24c85d2c85d0423206284103ede"}, - {file = "regex-2022.3.2-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:5dcc4168536c8f68654f014a3db49b6b4a26b226f735708be2054314ed4964f4"}, - {file = "regex-2022.3.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:1e30762ddddb22f7f14c4f59c34d3addabc789216d813b0f3e2788d7bcf0cf29"}, - {file = "regex-2022.3.2-cp36-cp36m-win32.whl", hash = "sha256:286ff9ec2709d56ae7517040be0d6c502642517ce9937ab6d89b1e7d0904f863"}, - {file = "regex-2022.3.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d326ff80ed531bf2507cba93011c30fff2dd51454c85f55df0f59f2030b1687b"}, - {file = "regex-2022.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9d828c5987d543d052b53c579a01a52d96b86f937b1777bbfe11ef2728929357"}, - {file = "regex-2022.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c87ac58b9baaf50b6c1b81a18d20eda7e2883aa9a4fb4f1ca70f2e443bfcdc57"}, - {file = "regex-2022.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6c2441538e4fadd4291c8420853431a229fcbefc1bf521810fbc2629d8ae8c2"}, - {file = "regex-2022.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f3356afbb301ec34a500b8ba8b47cba0b44ed4641c306e1dd981a08b416170b5"}, - {file = "regex-2022.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d96eec8550fd2fd26f8e675f6d8b61b159482ad8ffa26991b894ed5ee19038b"}, - {file = "regex-2022.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf668f26604e9f7aee9f8eaae4ca07a948168af90b96be97a4b7fa902a6d2ac1"}, - {file = "regex-2022.3.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0eb0e2845e81bdea92b8281a3969632686502565abf4a0b9e4ab1471c863d8f3"}, - {file = "regex-2022.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:87bc01226cd288f0bd9a4f9f07bf6827134dc97a96c22e2d28628e824c8de231"}, - {file = "regex-2022.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:09b4b6ccc61d4119342b26246ddd5a04accdeebe36bdfe865ad87a0784efd77f"}, - {file = "regex-2022.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:9557545c10d52c845f270b665b52a6a972884725aa5cf12777374e18f2ea8960"}, - {file = "regex-2022.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:0be0c34a39e5d04a62fd5342f0886d0e57592a4f4993b3f9d257c1f688b19737"}, - {file = "regex-2022.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7b103dffb9f6a47ed7ffdf352b78cfe058b1777617371226c1894e1be443afec"}, - {file = "regex-2022.3.2-cp37-cp37m-win32.whl", hash = "sha256:f8169ec628880bdbca67082a9196e2106060a4a5cbd486ac51881a4df805a36f"}, - {file = "regex-2022.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:4b9c16a807b17b17c4fa3a1d8c242467237be67ba92ad24ff51425329e7ae3d0"}, - {file = "regex-2022.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:67250b36edfa714ba62dc62d3f238e86db1065fccb538278804790f578253640"}, - {file = "regex-2022.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5510932596a0f33399b7fff1bd61c59c977f2b8ee987b36539ba97eb3513584a"}, - {file = "regex-2022.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6f7ee2289176cb1d2c59a24f50900f8b9580259fa9f1a739432242e7d254f93"}, - {file = "regex-2022.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d7a68fa53688e1f612c3246044157117403c7ce19ebab7d02daf45bd63913e"}, - {file = "regex-2022.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaf5317c961d93c1a200b9370fb1c6b6836cc7144fef3e5a951326912bf1f5a3"}, - {file = "regex-2022.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad397bc7d51d69cb07ef89e44243f971a04ce1dca9bf24c992c362406c0c6573"}, - {file = "regex-2022.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:297c42ede2c81f0cb6f34ea60b5cf6dc965d97fa6936c11fc3286019231f0d66"}, - {file = "regex-2022.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:af4d8cc28e4c7a2f6a9fed544228c567340f8258b6d7ea815b62a72817bbd178"}, - {file = "regex-2022.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:452519bc4c973e961b1620c815ea6dd8944a12d68e71002be5a7aff0a8361571"}, - {file = "regex-2022.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cb34c2d66355fb70ae47b5595aafd7218e59bb9c00ad8cc3abd1406ca5874f07"}, - {file = "regex-2022.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d146e5591cb67c5e836229a04723a30af795ef9b70a0bbd913572e14b7b940f"}, - {file = "regex-2022.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:03299b0bcaa7824eb7c0ebd7ef1e3663302d1b533653bfe9dc7e595d453e2ae9"}, - {file = "regex-2022.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9ccb0a4ab926016867260c24c192d9df9586e834f5db83dfa2c8fffb3a6e5056"}, - {file = "regex-2022.3.2-cp38-cp38-win32.whl", hash = "sha256:f7e8f1ee28e0a05831c92dc1c0c1c94af5289963b7cf09eca5b5e3ce4f8c91b0"}, - {file = "regex-2022.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:35ed2f3c918a00b109157428abfc4e8d1ffabc37c8f9abc5939ebd1e95dabc47"}, - {file = "regex-2022.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:55820bc631684172b9b56a991d217ec7c2e580d956591dc2144985113980f5a3"}, - {file = "regex-2022.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:83f03f0bd88c12e63ca2d024adeee75234d69808b341e88343b0232329e1f1a1"}, - {file = "regex-2022.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42d6007722d46bd2c95cce700181570b56edc0dcbadbfe7855ec26c3f2d7e008"}, - {file = "regex-2022.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:320c2f4106962ecea0f33d8d31b985d3c185757c49c1fb735501515f963715ed"}, - {file = "regex-2022.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbd3fe37353c62fd0eb19fb76f78aa693716262bcd5f9c14bb9e5aca4b3f0dc4"}, - {file = "regex-2022.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17e51ad1e6131c496b58d317bc9abec71f44eb1957d32629d06013a21bc99cac"}, - {file = "regex-2022.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72bc3a5effa5974be6d965ed8301ac1e869bc18425c8a8fac179fbe7876e3aee"}, - {file = "regex-2022.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e5602a9b5074dcacc113bba4d2f011d2748f50e3201c8139ac5b68cf2a76bd8b"}, - {file = "regex-2022.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:729aa8ca624c42f309397c5fc9e21db90bf7e2fdd872461aabdbada33de9063c"}, - {file = "regex-2022.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d6ecfd1970b3380a569d7b3ecc5dd70dba295897418ed9e31ec3c16a5ab099a5"}, - {file = "regex-2022.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:13bbf0c9453c6d16e5867bda7f6c0c7cff1decf96c5498318bb87f8136d2abd4"}, - {file = "regex-2022.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:58ba41e462653eaf68fc4a84ec4d350b26a98d030be1ab24aba1adcc78ffe447"}, - {file = "regex-2022.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c0446b2871335d5a5e9fcf1462f954586b09a845832263db95059dcd01442015"}, - {file = "regex-2022.3.2-cp39-cp39-win32.whl", hash = "sha256:20e6a27959f162f979165e496add0d7d56d7038237092d1aba20b46de79158f1"}, - {file = "regex-2022.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:9efa41d1527b366c88f265a227b20bcec65bda879962e3fc8a2aee11e81266d7"}, - {file = "regex-2022.3.2.tar.gz", hash = "sha256:79e5af1ff258bc0fe0bdd6f69bc4ae33935a898e3cbefbbccf22e88a27fa053b"}, -] reorder-python-imports = [ {file = "reorder_python_imports-3.9.0-py2.py3-none-any.whl", hash = "sha256:3f9c16e8781f54c944756d0d1eb34a8c863554f7a4eb3693f574fe19b1a29b56"}, {file = "reorder_python_imports-3.9.0.tar.gz", hash = "sha256:49292ed537829a6bece9fb3746fc1bbe98f52643be5de01a4e13680268a5b0ec"}, @@ -3264,8 +3072,8 @@ requests-toolbelt = [ {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, ] restrictedpython = [ - {file = "RestrictedPython-5.2-py2.py3-none-any.whl", hash = "sha256:fdf8621034c5dcb990a2a198f232f66b2d48866dd16d848e00ac7d187ae452ba"}, - {file = "RestrictedPython-5.2.tar.gz", hash = "sha256:634da1f6c5c122a262f433b083ee3d17a9a039f8f1b3778597efb47461cd361b"}, + {file = "RestrictedPython-6.0-py3-none-any.whl", hash = "sha256:3479303f7bff48a7dedad76f96e7704993c5e86c5adbd67f607295d5352f0fb8"}, + {file = "RestrictedPython-6.0.tar.gz", hash = "sha256:405cf0bd9eec2f19b1326b5f48228efe56d6590b4e91826b8cc3b2cd400a96ad"}, ] restructuredtext-lint = [ {file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"}, @@ -3278,41 +3086,6 @@ ruamel-yaml = [ {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, ] -ruamel-yaml-clib = [ - {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71"}, - {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7"}, - {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:df5828871e6648db72d1c19b4bd24819b80a755c4541d3409f0f7acd0f335c80"}, - {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:efa08d63ef03d079dcae1dfe334f6c8847ba8b645d08df286358b1f5293d24ab"}, - {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win32.whl", hash = "sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231"}, - {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a"}, - {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"}, - {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_12_6_arm64.whl", hash = "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5"}, - {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072"}, - {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_12_0_arm64.whl", hash = "sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8"}, - {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3"}, - {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:370445fd795706fd291ab00c9df38a0caed0f17a6fb46b0f607668ecb16ce763"}, - {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-win32.whl", hash = "sha256:ecdf1a604009bd35c674b9225a8fa609e0282d9b896c03dd441a91e5f53b534e"}, - {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-win_amd64.whl", hash = "sha256:f34019dced51047d6f70cb9383b2ae2853b7fc4dce65129a5acd49f4f9256646"}, - {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2aa261c29a5545adfef9296b7e33941f46aa5bbd21164228e833412af4c9c75f"}, - {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f01da5790e95815eb5a8a138508c01c758e5f5bc0ce4286c4f7028b8dd7ac3d0"}, - {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:40d030e2329ce5286d6b231b8726959ebbe0404c92f0a578c0e2482182e38282"}, - {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c3ca1fbba4ae962521e5eb66d72998b51f0f4d0f608d3c0347a48e1af262efa7"}, - {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-win32.whl", hash = "sha256:7bdb4c06b063f6fd55e472e201317a3bb6cdeeee5d5a38512ea5c01e1acbdd93"}, - {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:be2a7ad8fd8f7442b24323d24ba0b56c51219513cfa45b9ada3b87b76c374d4b"}, - {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:91a789b4aa0097b78c93e3dc4b40040ba55bef518f84a40d4442f713b4094acb"}, - {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:99e77daab5d13a48a4054803d052ff40780278240a902b880dd37a51ba01a307"}, - {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:3243f48ecd450eddadc2d11b5feb08aca941b5cd98c9b1db14b2fd128be8c697"}, - {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8831a2cedcd0f0927f788c5bdf6567d9dc9cc235646a434986a852af1cb54b4b"}, - {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-win32.whl", hash = "sha256:3110a99e0f94a4a3470ff67fc20d3f96c25b13d24c6980ff841e82bafe827cac"}, - {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:92460ce908546ab69770b2e576e4f99fbb4ce6ab4b245345a3869a0a0410488f"}, - {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5bc0667c1eb8f83a3752b71b9c4ba55ef7c7058ae57022dd9b29065186a113d9"}, - {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:4a4d8d417868d68b979076a9be6a38c676eca060785abaa6709c7b31593c35d1"}, - {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf9a6bc4a0221538b1a7de3ed7bca4c93c02346853f44e1cd764be0023cd3640"}, - {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a7b301ff08055d73223058b5c46c55638917f04d21577c95e00e0c4d79201a6b"}, - {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win32.whl", hash = "sha256:d5e51e2901ec2366b79f16c2299a03e74ba4531ddcfacc1416639c557aef0ad8"}, - {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:184faeaec61dbaa3cace407cffc5819f7b977e75360e8d5ca19461cd851a5fc5"}, - {file = "ruamel.yaml.clib-0.2.7.tar.gz", hash = "sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497"}, -] safety = [ {file = "safety-2.3.1-py3-none-any.whl", hash = "sha256:8f098d12b607db2756886280e85c28ece8db1bba4f45fc5f981f4663217bd619"}, {file = "safety-2.3.1.tar.gz", hash = "sha256:6e6fcb7d4e8321098cf289f59b65051cafd3467f089c6e57c9f894ae32c23b71"}, @@ -3509,10 +3282,6 @@ toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] -tomli = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] tornado = [ {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"}, {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"}, @@ -3680,7 +3449,3 @@ xdoctest = [ {file = "xdoctest-1.1.0-py3-none-any.whl", hash = "sha256:da330c4dacee51f3c785820bc743188fb6f7c64c5fa1c54bff8836b3cf23d69b"}, {file = "xdoctest-1.1.0.tar.gz", hash = "sha256:0fd4fad7932f0a2f082dfdfb857dd6ca41603757586c39b1e5b4d333fc389f8a"}, ] -zipp = [ - {file = "zipp-3.10.0-py3-none-any.whl", hash = "sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1"}, - {file = "zipp-3.10.0.tar.gz", hash = "sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8"}, -] diff --git a/pyproject.toml b/pyproject.toml index 95030f13..f071db60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ classifiers = [ ] [tool.poetry.dependencies] -python = ">=3.9,<3.11" +python = ">=3.11,<3.12" click = "^8.0.1" flask = "2.2.2" flask-admin = "*" @@ -46,7 +46,7 @@ gunicorn = "^20.1.0" python-keycloak = "^2.5.0" APScheduler = "^3.9.1" Jinja2 = "^3.1.2" -RestrictedPython = "^5.2" +RestrictedPython = "^6.0" Flask-SQLAlchemy = "^3" orjson = "^3.8.0" diff --git a/spiffworkflow-backend/.tool-versions b/spiffworkflow-backend/.tool-versions index 7e78d9af..a7b6ef2e 100644 --- a/spiffworkflow-backend/.tool-versions +++ b/spiffworkflow-backend/.tool-versions @@ -1 +1 @@ -python 3.10.4 +python 3.11.0 diff --git a/spiffworkflow-backend/Dockerfile b/spiffworkflow-backend/Dockerfile index 6c3fabcf..6c533206 100644 --- a/spiffworkflow-backend/Dockerfile +++ b/spiffworkflow-backend/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/sartography/python:3.10 +FROM ghcr.io/sartography/python:3.11 RUN pip install poetry RUN useradd _gunicorn --no-create-home --user-group diff --git a/spiffworkflow-backend/bin/update_all_json.py b/spiffworkflow-backend/bin/update_all_json.py index 0cfbb11f..4e6b1b8a 100644 --- a/spiffworkflow-backend/bin/update_all_json.py +++ b/spiffworkflow-backend/bin/update_all_json.py @@ -1,4 +1,4 @@ -"""Updates all JSON files, based on the current state of BPMN_SPEC_ABSOLUTE_DIR""" +"""Updates all JSON files, based on the current state of BPMN_SPEC_ABSOLUTE_DIR.""" from spiffworkflow_backend import get_hacked_up_app_for_script from spiffworkflow_backend.services.process_model_service import ProcessModelService @@ -12,8 +12,8 @@ def main() -> None: for group in groups: for process_model in group.process_models: update_items = { - 'process_group_id': '', - 'id': f"{group.id}/{process_model.id}" + "process_group_id": "", + "id": f"{group.id}/{process_model.id}", } ProcessModelService().update_spec(process_model, update_items) diff --git a/spiffworkflow-backend/conftest.py b/spiffworkflow-backend/conftest.py index 4e9e72e7..d73693c2 100644 --- a/spiffworkflow-backend/conftest.py +++ b/spiffworkflow-backend/conftest.py @@ -8,7 +8,6 @@ from flask.testing import FlaskClient from flask_bpmn.models.db import db from flask_bpmn.models.db import SpiffworkflowBaseDBModel from tests.spiffworkflow_backend.helpers.base_test import BaseTest -from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from spiffworkflow_backend.models.active_task_user import ActiveTaskUserModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModel @@ -21,6 +20,8 @@ from spiffworkflow_backend.services.process_instance_service import ( ) from spiffworkflow_backend.services.process_model_service import ProcessModelService +# from tests.spiffworkflow_backend.helpers.test_data import load_test_spec + # We need to call this before importing spiffworkflow_backend # otherwise typeguard cannot work. hence the noqa: E402 @@ -67,12 +68,14 @@ def with_super_admin_user() -> UserModel: @pytest.fixture() -def setup_process_instances_for_reports(client: FlaskClient, with_super_admin_user: UserModel) -> list[ProcessInstanceModel]: +def setup_process_instances_for_reports( + client: FlaskClient, with_super_admin_user: UserModel +) -> list[ProcessInstanceModel]: """Setup_process_instances_for_reports.""" user = with_super_admin_user process_group_id = "runs_without_input" process_model_id = "sample" - bpmn_file_name = "sample.bpmn" + # bpmn_file_name = "sample.bpmn" bpmn_file_location = "sample" process_model_identifier = BaseTest().basic_test_setup( client, @@ -80,7 +83,7 @@ def setup_process_instances_for_reports(client: FlaskClient, with_super_admin_us process_group_id=process_group_id, process_model_id=process_model_id, # bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) # BaseTest().create_process_group( diff --git a/spiffworkflow-backend/migrations/env.py b/spiffworkflow-backend/migrations/env.py index 68feded2..630e381a 100644 --- a/spiffworkflow-backend/migrations/env.py +++ b/spiffworkflow-backend/migrations/env.py @@ -1,5 +1,3 @@ -from __future__ import with_statement - import logging from logging.config import fileConfig diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock index 8c918630..cf161845 100644 --- a/spiffworkflow-backend/poetry.lock +++ b/spiffworkflow-backend/poetry.lock @@ -80,8 +80,7 @@ python-versions = ">=3.7.2" [package.dependencies] lazy-object-proxy = ">=1.4.0" -typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} -wrapt = {version = ">=1.11,<2", markers = "python_version < \"3.11\""} +wrapt = {version = ">=1.14,<2", markers = "python_version >= \"3.11\""} [[package]] name = "attrs" @@ -95,7 +94,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" @@ -175,8 +174,6 @@ click = ">=8.0.0" mypy-extensions = ">=0.4.3" pathspec = ">=0.9.0" platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] colorama = ["colorama (>=0.4.3)"] @@ -268,7 +265,7 @@ optional = false python-versions = ">=3.6.0" [package.extras] -unicode_backport = ["unicodedata2"] +unicode-backport = ["unicodedata2"] [[package]] name = "classify-imports" @@ -394,9 +391,6 @@ category = "dev" optional = false python-versions = ">=3.7" -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli"] @@ -574,7 +568,6 @@ python-versions = ">=3.7" [package.dependencies] click = ">=8.0" -importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} itsdangerous = ">=2.0" Jinja2 = ">=3.0" Werkzeug = ">=2.2.2" @@ -639,7 +632,7 @@ werkzeug = "*" type = "git" url = "https://github.com/sartography/flask-bpmn" reference = "main" -resolved_reference = "191f0f32798720c9ce1e5307732c90ac26433298" +resolved_reference = "17434e0907cc35914d013614bb79288eed1bd437" [[package]] name = "Flask-Cors" @@ -818,22 +811,6 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -[[package]] -name = "importlib-metadata" -version = "4.13.0" -description = "Read metadata from Python packages" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] - [[package]] name = "inflection" version = "0.5.1" @@ -1067,7 +1044,6 @@ python-versions = ">=3.7" [package.dependencies] mypy-extensions = ">=0.4.3" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=3.10" [package.extras] @@ -1206,7 +1182,6 @@ python-versions = ">=3.7" [package.dependencies] "ruamel.yaml" = ">=0.15" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [[package]] name = "prompt-toolkit" @@ -1512,7 +1487,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" @@ -1526,12 +1501,12 @@ python-versions = "*" requests = ">=2.0.1,<3.0.0" [[package]] -name = "RestrictedPython" -version = "5.2" +name = "restrictedpython" +version = "6.0" description = "RestrictedPython is a defined subset of the Python language which allows to provide a program input into a trusted environment." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <3.11" +python-versions = ">=3.6, <3.12" [package.extras] docs = ["Sphinx", "sphinx-rtd-theme"] @@ -1567,21 +1542,10 @@ category = "dev" optional = false python-versions = ">=3" -[package.dependencies] -"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} - [package.extras] docs = ["ryd"] jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] -[[package]] -name = "ruamel.yaml.clib" -version = "0.2.6" -description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -category = "dev" -optional = false -python-versions = ">=3.5" - [[package]] name = "safety" version = "2.3.1" @@ -1625,7 +1589,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)"] @@ -1701,7 +1665,6 @@ babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.14,<0.20" imagesize = ">=1.3" -importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.12" @@ -1889,19 +1852,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"] @@ -2239,22 +2202,10 @@ runtime-strict = ["six (==1.11.0)"] tests = ["cmake", "codecov", "ninja", "pybind11", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest-cov", "pytest-cov", "pytest-cov", "pytest-cov", "scikit-build", "typing"] tests-strict = ["cmake (==3.21.2)", "codecov (==2.0.15)", "ninja (==1.10.2)", "pybind11 (==2.7.1)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "scikit-build (==0.11.1)", "typing (==3.7.4)"] -[[package]] -name = "zipp" -version = "3.9.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - [metadata] lock-version = "1.1" -python-versions = ">=3.9,<3.11" -content-hash = "995be3a9a60b515b281f017ff32ff27a52ca178b1980611b348dccac6afb6b89" +python-versions = ">=3.11,<3.12" +content-hash = "1ba9277969015f0ef348dccb79e9977e20665720958f7ba22360398fba9da092" [metadata.files] alabaster = [ @@ -2636,10 +2587,6 @@ imagesize = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] -importlib-metadata = [ - {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"}, {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, @@ -3287,9 +3234,9 @@ requests-toolbelt = [ {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, ] -RestrictedPython = [ - {file = "RestrictedPython-5.2-py2.py3-none-any.whl", hash = "sha256:fdf8621034c5dcb990a2a198f232f66b2d48866dd16d848e00ac7d187ae452ba"}, - {file = "RestrictedPython-5.2.tar.gz", hash = "sha256:634da1f6c5c122a262f433b083ee3d17a9a039f8f1b3778597efb47461cd361b"}, +restrictedpython = [ + {file = "RestrictedPython-6.0-py3-none-any.whl", hash = "sha256:3479303f7bff48a7dedad76f96e7704993c5e86c5adbd67f607295d5352f0fb8"}, + {file = "RestrictedPython-6.0.tar.gz", hash = "sha256:405cf0bd9eec2f19b1326b5f48228efe56d6590b4e91826b8cc3b2cd400a96ad"}, ] restructuredtext-lint = [ {file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"}, @@ -3302,38 +3249,6 @@ rsa = [ {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, ] -"ruamel.yaml.clib" = [ - {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0"}, - {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:066f886bc90cc2ce44df8b5f7acfc6a7e2b2e672713f027136464492b0c34d7c"}, - {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7"}, - {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win32.whl", hash = "sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee"}, - {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de"}, - {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751"}, - {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527"}, - {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win32.whl", hash = "sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5"}, - {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win_amd64.whl", hash = "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c"}, - {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502"}, - {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78"}, - {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d3c620a54748a3d4cf0bcfe623e388407c8e85a4b06b8188e126302bcab93ea8"}, - {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win32.whl", hash = "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94"}, - {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win_amd64.whl", hash = "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468"}, - {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd"}, - {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99"}, - {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:210c8fcfeff90514b7133010bf14e3bad652c8efde6b20e00c43854bf94fa5a6"}, - {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win32.whl", hash = "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb"}, - {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win_amd64.whl", hash = "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe"}, - {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233"}, - {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84"}, - {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:61bc5e5ca632d95925907c569daa559ea194a4d16084ba86084be98ab1cec1c6"}, - {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win32.whl", hash = "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b"}, - {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win_amd64.whl", hash = "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277"}, - {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed"}, - {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0"}, - {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:1b4139a6ffbca8ef60fdaf9b33dec05143ba746a6f0ae0f9d11d38239211d335"}, - {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win32.whl", hash = "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104"}, - {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win_amd64.whl", hash = "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7"}, - {file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"}, -] safety = [ {file = "safety-2.3.1-py3-none-any.whl", hash = "sha256:8f098d12b607db2756886280e85c28ece8db1bba4f45fc5f981f4663217bd619"}, {file = "safety-2.3.1.tar.gz", hash = "sha256:6e6fcb7d4e8321098cf289f59b65051cafd3467f089c6e57c9f894ae32c23b71"}, @@ -3705,7 +3620,3 @@ xdoctest = [ {file = "xdoctest-1.1.0-py3-none-any.whl", hash = "sha256:da330c4dacee51f3c785820bc743188fb6f7c64c5fa1c54bff8836b3cf23d69b"}, {file = "xdoctest-1.1.0.tar.gz", hash = "sha256:0fd4fad7932f0a2f082dfdfb857dd6ca41603757586c39b1e5b4d333fc389f8a"}, ] -zipp = [ - {file = "zipp-3.9.0-py3-none-any.whl", hash = "sha256:972cfa31bc2fedd3fa838a51e9bc7e64b7fb725a8c00e7431554311f180e9980"}, - {file = "zipp-3.9.0.tar.gz", hash = "sha256:3a7af91c3db40ec72dd9d154ae18e008c69efe8ca88dde4f9a731bb82fe2f9eb"}, -] diff --git a/spiffworkflow-backend/pyproject.toml b/spiffworkflow-backend/pyproject.toml index 7f2d09a4..abb6d175 100644 --- a/spiffworkflow-backend/pyproject.toml +++ b/spiffworkflow-backend/pyproject.toml @@ -16,7 +16,7 @@ classifiers = [ Changelog = "https://github.com/sartography/spiffworkflow-backend/releases" [tool.poetry.dependencies] -python = ">=3.9,<3.11" +python = ">=3.11,<3.12" click = "^8.0.1" flask = "2.2.2" flask-admin = "*" @@ -47,7 +47,7 @@ gunicorn = "^20.1.0" python-keycloak = "^2.5.0" APScheduler = "^3.9.1" Jinja2 = "^3.1.2" -RestrictedPython = "^5.2" +RestrictedPython = "^6.0" Flask-SQLAlchemy = "^3" orjson = "^3.8.0" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py index c06126f2..8d4cb538 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py @@ -72,7 +72,9 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): __tablename__ = "process_instance" id: int = db.Column(db.Integer, primary_key=True) - process_model_identifier: str = db.Column(db.String(255), nullable=False, index=True) + process_model_identifier: str = db.Column( + db.String(255), nullable=False, index=True + ) process_group_identifier: str = db.Column(db.String(50), nullable=False, index=True) process_initiator_id: int = db.Column(ForeignKey(UserModel.id), nullable=False) process_initiator = relationship("UserModel") @@ -265,7 +267,7 @@ class ProcessInstanceMetadata: id=process_instance.id, display_name=process_model.display_name, description=process_model.description, - process_group_id=process_model.process_group_id, + process_group_id=process_model.process_group, state_message=process_instance.state_message, status=process_instance.status, completed_tasks=process_instance.completed_tasks, diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_report.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_report.py index 478858a6..b6f16288 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_report.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_report.py @@ -238,18 +238,18 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel): user: UserModel, ) -> ProcessInstanceReportModel: """Create_with_attributes.""" -# <<<<<<< HEAD -# process_model = ProcessModelService().get_process_model( -# process_model_id=f"{process_model_identifier}" -# ) -# process_instance_report = cls( -# identifier=identifier, -# process_group_identifier="process_model.process_group_id", -# process_model_identifier=process_model.id, -# ======= + # <<<<<<< HEAD + # process_model = ProcessModelService().get_process_model( + # process_model_id=f"{process_model_identifier}" + # ) + # process_instance_report = cls( + # identifier=identifier, + # process_group_identifier="process_model.process_group_id", + # process_model_identifier=process_model.id, + # ======= process_instance_report = cls( identifier=identifier, -# >>>>>>> main + # >>>>>>> main created_by_id=user.id, report_metadata=report_metadata, ) 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 1081494b..2f60e994 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -138,11 +138,13 @@ def permissions_check(body: Dict[str, Dict[str, list[str]]]) -> flask.wrappers.R def modify_process_model_id(process_model_id: str) -> str: - return process_model_id.replace('/', ':') + """Modify_process_model_id.""" + return process_model_id.replace("/", ":") def un_modify_modified_process_model_id(modified_process_model_id: str) -> str: - return modified_process_model_id.replace(':', '/') + """Un_modify_modified_process_model_id.""" + return modified_process_model_id.replace(":", "/") def process_group_add(body: dict) -> flask.wrappers.Response: @@ -226,9 +228,7 @@ def process_model_add( process_group_id, _ = os.path.split(process_model_info.id) process_model_service = ProcessModelService() - process_group = process_model_service.get_process_group( - process_group_id - ) + process_group = process_model_service.get_process_group(process_group_id) if process_group is None: raise ApiError( error_code="process_model_could_not_be_created", @@ -245,7 +245,7 @@ def process_model_add( def process_model_delete( - modified_process_model_identifier: str + modified_process_model_identifier: str, ) -> flask.wrappers.Response: """Process_model_delete.""" process_model_identifier = modified_process_model_identifier.replace(":", "/") @@ -259,7 +259,12 @@ def process_model_update( ) -> Any: """Process_model_update.""" process_model_identifier = modified_process_model_identifier.replace(":", "/") - body_include_list = ["display_name", "primary_file_name", "primary_process_id", "description"] + body_include_list = [ + "display_name", + "primary_file_name", + "primary_process_id", + "description", + ] body_filtered = { include_item: body[include_item] for include_item in body_include_list @@ -409,11 +414,11 @@ def add_file(modified_process_model_id: str) -> flask.wrappers.Response: ) -def process_instance_create( - modified_process_model_id: str -) -> flask.wrappers.Response: +def process_instance_create(modified_process_model_id: str) -> flask.wrappers.Response: """Create_process_instance.""" - process_model_identifier = un_modify_modified_process_model_id(modified_process_model_id) + process_model_identifier = un_modify_modified_process_model_id( + modified_process_model_id + ) process_instance = ProcessInstanceService.create_process_instance( process_model_identifier, g.user ) @@ -779,9 +784,7 @@ def process_instance_show( return make_response(jsonify(process_instance), 200) -def process_instance_delete( - process_instance_id: int -) -> flask.wrappers.Response: +def process_instance_delete(process_instance_id: int) -> flask.wrappers.Response: """Create_process_instance.""" process_instance = find_process_instance_by_id_or_raise(process_instance_id) @@ -900,7 +903,6 @@ def process_instance_report_show( per_page: int = 100, ) -> flask.wrappers.Response: """Process_instance_list.""" - process_instances = ProcessInstanceModel.query.order_by( # .filter_by(process_model_identifier=process_model.id) ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore ).paginate( @@ -1296,9 +1298,7 @@ def get_process_model(process_model_id: str) -> ProcessModelInfo: """Get_process_model.""" process_model = None try: - process_model = ProcessModelService().get_process_model( - process_model_id - ) + process_model = ProcessModelService().get_process_model(process_model_id) except ProcessEntityNotFoundError as exception: raise ( ApiError( diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/user.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/user.py index 5bf4e48b..66299272 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/user.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/user.py @@ -105,7 +105,7 @@ def verify_token( raise ApiError( error_code="fail_get_user_info", message="Cannot get user info from token", - status_code=401 + status_code=401, ) from e if ( diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/data_setup_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/data_setup_service.py index 88bd6048..23df25f3 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/data_setup_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/data_setup_service.py @@ -48,7 +48,7 @@ class DataSetupService: except Exception as ex: failing_process_models.append( ( - f"{process_model.process_group_id}/{process_model.id}/{process_model_file.name}", + f"{process_model.process_group}/{process_model.id}/{process_model_file.name}", str(ex), ) ) @@ -87,7 +87,7 @@ class DataSetupService: else: failing_process_models.append( ( - f"{process_model.process_group_id}/{process_model.id}", + f"{process_model.process_group}/{process_model.id}", "primary_file_name not set", ) ) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/file_system_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/file_system_service.py index 2903ff2f..a4a01b83 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/file_system_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/file_system_service.py @@ -67,7 +67,9 @@ class FileSystemService: @staticmethod def full_path_to_process_model_file(spec: ProcessModelInfo) -> str: """Full_path_to_process_model_file.""" - return os.path.join(FileSystemService.workflow_path(spec), spec.primary_file_name) + return os.path.join( + FileSystemService.workflow_path(spec), spec.primary_file_name # type: ignore + ) def next_display_order(self, spec: ProcessModelInfo) -> int: """Next_display_order.""" 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 7c27fb54..0ed85511 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -293,7 +293,9 @@ class ProcessInstanceProcessor: 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 = ( + current_app.config[ + "THREAD_LOCAL_DATA" + ].process_model_identifier = ( f"{process_instance_model.process_model_identifier}" ) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py index a8b027d3..7854537e 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py @@ -2,12 +2,10 @@ import time from typing import Any from typing import List -from typing import Optional from flask import current_app from flask_bpmn.api.api_error import ApiError from flask_bpmn.models.db import db -from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore from SpiffWorkflow.task import Task as SpiffTask # type: ignore from spiffworkflow_backend.models.process_instance import ProcessInstanceApi 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 b91459e4..c650e1f5 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py @@ -34,13 +34,15 @@ class ProcessModelService(FileSystemService): GROUP_SCHEMA = ProcessGroupSchema() WF_SCHEMA = ProcessModelInfoSchema() - def is_group(self, path): + def is_group(self, path: str) -> bool: + """Is_group.""" group_json_path = os.path.join(path, self.CAT_JSON_FILE) if os.path.exists(group_json_path): return True return False - def is_model(self, path): + def is_model(self, path: str) -> bool: + """Is_model.""" model_json_path = os.path.join(path, self.WF_JSON_FILE) if os.path.exists(model_json_path): return True @@ -92,7 +94,7 @@ class ProcessModelService(FileSystemService): error_code="existing_instances", message=f"We cannot delete the model `{process_model_id}`, there are existing instances that depend on it.", ) - process_model = self.get_process_model(process_model_id) + self.get_process_model(process_model_id) # path = self.workflow_path(process_model) path = f"{FileSystemService.root_path()}/{process_model_id}" shutil.rmtree(path) @@ -107,11 +109,11 @@ class ProcessModelService(FileSystemService): path = os.path.join(FileSystemService.root_path(), relative_path) return cls().__scan_spec(path, process_group=process_group) - def get_process_model( - self, process_model_id: str - ) -> ProcessModelInfo: + def get_process_model(self, process_model_id: str) -> ProcessModelInfo: """Get a process model from a model and group id. - process_model_id is the full path to the model--including groups""" + + process_model_id is the full path to the model--including groups. + """ if not os.path.exists(FileSystemService.root_path()): raise ProcessEntityNotFoundError("process_model_root_not_found") @@ -168,7 +170,9 @@ class ProcessModelService(FileSystemService): def get_process_group(self, process_group_id: str) -> ProcessGroup: """Look for a given process_group, and return it.""" if os.path.exists(FileSystemService.root_path()): - process_group_path = os.path.join(FileSystemService.root_path(), process_group_id) + process_group_path = os.path.join( + FileSystemService.root_path(), process_group_id + ) if self.is_group(process_group_path): return self.__scan_process_group(process_group_path) # nested_groups = [] @@ -235,7 +239,7 @@ class ProcessModelService(FileSystemService): process_groups = [] for item in directory_items: # if item.is_dir() and not item.name[0] == ".": - if item.is_dir() and self.is_group(item): + if item.is_dir() and self.is_group(item): # type: ignore scanned_process_group = self.__scan_process_group(item.path) process_groups.append(scanned_process_group) return process_groups @@ -253,7 +257,7 @@ class ProcessModelService(FileSystemService): message=f"We could not load the process_group from disk from: {dir_path}", ) else: - process_group_id = dir_path.replace(FileSystemService.root_path(), '') + process_group_id = dir_path.replace(FileSystemService.root_path(), "") process_group = ProcessGroup( id=process_group_id, display_name=process_group_id, @@ -273,7 +277,9 @@ class ProcessModelService(FileSystemService): elif self.is_model(nested_item.path): process_group.process_models.append( self.__scan_spec( - nested_item.path, nested_item.name, process_group=process_group + nested_item.path, + nested_item.name, + process_group=process_group, ) ) process_group.process_models.sort() @@ -316,5 +322,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_id = process_group.id + spec.process_group = process_group.id return spec 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 b710394c..92e905bf 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py @@ -107,7 +107,9 @@ class SpecFileService(FileSystemService): """Update_file.""" SpecFileService.assert_valid_file_name(file_name) # file_path = SpecFileService.file_path(process_model_info, file_name) - file_path = os.path.join(FileSystemService.root_path(), process_model_info.id, file_name) + file_path = os.path.join( + FileSystemService.root_path(), process_model_info.id, file_name + ) SpecFileService.write_file_data_to_system(file_path, binary_data) file = SpecFileService.to_file_object(file_name, file_path) @@ -132,7 +134,9 @@ class SpecFileService(FileSystemService): def get_data(process_model_info: ProcessModelInfo, file_name: str) -> bytes: """Get_data.""" # file_path = SpecFileService.file_path(process_model_info, file_name) - file_path = os.path.join(FileSystemService.root_path(), process_model_info.id, file_name) + file_path = os.path.join( + FileSystemService.root_path(), process_model_info.id, file_name + ) if not os.path.exists(file_path): raise ProcessModelFileNotFoundError( f"No file found with name {file_name} in {process_model_info.display_name}" @@ -465,10 +469,12 @@ class SpecFileService(FileSystemService): ) if message_triggerable_process_model is None: - message_triggerable_process_model = MessageTriggerableProcessModel( - message_model_id=message_model.id, - process_model_identifier=process_model_info.id, - process_group_identifier="process_group_identifier", + message_triggerable_process_model = ( + MessageTriggerableProcessModel( + message_model_id=message_model.id, + process_model_identifier=process_model_info.id, + process_group_identifier="process_group_identifier", + ) ) db.session.add(message_triggerable_process_model) db.session.commit() @@ -480,8 +486,7 @@ class SpecFileService(FileSystemService): # != process_model_info.process_group_id ): raise ValidationException( - "Message model is already used to start process model" - f"'{process_model_info.process_group_id}/{process_model_info.id}'" + f"Message model is already used to start process model {process_model_info.id}" ) for child in et_root: diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py index 27c662f0..794e1cea 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py @@ -12,10 +12,6 @@ from flask.app import Flask from flask.testing import FlaskClient from flask_bpmn.api.api_error import ApiError from flask_bpmn.models.db import db - -from spiffworkflow_backend.services.file_system_service import FileSystemService -from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor -from spiffworkflow_backend.services.process_instance_service import ProcessInstanceService from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from werkzeug.test import TestResponse # type: ignore @@ -29,6 +25,7 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.authorization_service import AuthorizationService +from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.user_service import UserService @@ -45,19 +42,22 @@ class BaseTest: process_group_id: Optional[str] = "test_group", process_model_id: Optional[str] = "random_fact", bpmn_file_name: Optional[str] = None, - bpmn_file_location: Optional[str] = None + bpmn_file_location: Optional[str] = None, ) -> str: - """Creates a process group - Creates a process model - Adds a bpmn file to the model""" + """Creates a process group. - process_group_display_name = process_group_id - process_group_description = process_group_id + Creates a process model + Adds a bpmn file to the model. + """ + process_group_display_name = process_group_id or "" + process_group_description = process_group_id or "" process_model_identifier = f"{process_group_id}/{process_model_id}" if bpmn_file_location is None: bpmn_file_location = process_model_id - self.create_process_group(client, user, process_group_id, process_group_display_name) + self.create_process_group( + client, user, process_group_description, process_group_display_name + ) self.create_process_model_with_api( client, @@ -70,7 +70,7 @@ class BaseTest: load_test_spec( process_model_id=process_model_identifier, bpmn_file_name=bpmn_file_name, - process_model_source_directory=bpmn_file_location + process_model_source_directory=bpmn_file_location, ) return process_model_identifier @@ -115,8 +115,9 @@ class BaseTest: headers: Dict[str, str], ) -> TestResponse: """Create_process_instance. - There must be an existing process model to instantiate.""" + There must be an existing process model to instantiate. + """ modified_process_model_id = test_process_model_id.replace("/", ":") response = client.post( f"/v1.0/process-models/{modified_process_model_id}/process-instances", @@ -138,7 +139,6 @@ class BaseTest: user: Optional[UserModel] = None, ) -> TestResponse: """Create_process_model.""" - if process_model_id is not None: # make sure we have a group @@ -174,7 +174,9 @@ class BaseTest: else: raise Exception("You must create the group first") else: - raise Exception("You must include the process_model_id, which must be a path to the model") + raise Exception( + "You must include the process_model_id, which must be a path to the model" + ) def create_spec_file( self, @@ -187,18 +189,20 @@ class BaseTest: user: Optional[UserModel] = None, ) -> Any: """Test_create_spec_file. + Adds a bpmn file to the model. process_model_id is the destination path process_model_location is the source path - because of permissions, user might be required now..., not sure yet""" + because of permissions, user might be required now..., not sure yet. + """ if process_model_location is None: process_model_location = file_name.split(".")[0] if process_model is None: process_model = load_test_spec( process_model_id=process_model_id, bpmn_file_name=file_name, - process_model_source_directory=process_model_location + process_model_source_directory=process_model_location, ) data = {"file": (io.BytesIO(file_data), file_name)} if user is None: diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/example_data.py b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/example_data.py index fd8b3353..ac1e8dc8 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/example_data.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/example_data.py @@ -21,7 +21,7 @@ class ExampleDataLoader: display_order: int = 0, # from_tests: bool = False, bpmn_file_name: Optional[str] = None, - process_model_source_directory: str = None, + process_model_source_directory: Optional[str] = None, ) -> ProcessModelInfo: """Assumes that process_model_source_directory exists in static/bpmn and contains bpmn_file_name. diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/test_data.py b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/test_data.py index 36b35998..d6b4f730 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/test_data.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/test_data.py @@ -38,10 +38,9 @@ def assure_process_group_exists(process_group_id: Optional[str] = None) -> Proce def load_test_spec( process_model_id: str, bpmn_file_name: Optional[str] = None, - process_model_source_directory: str = None, + process_model_source_directory: Optional[str] = None, ) -> ProcessModelInfo: """Loads a bpmn file into the process model dir based on a directory in tests/data.""" - if process_model_source_directory is None: raise Exception("You must inclode a `process_model_source_directory`.") diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_logging_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_logging_service.py index 2635aada..02d8ade1 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_logging_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_logging_service.py @@ -19,15 +19,17 @@ class TestLoggingService(BaseTest): """Test_process_instance_run.""" process_group_id = "test_logging_spiff_logger" process_model_id = "simple_script" - self.create_process_group(client=client, user=with_super_admin_user, process_group_id=process_group_id) + self.create_process_group( + client=client, user=with_super_admin_user, process_group_id=process_group_id + ) process_model_identifier = f"{process_group_id}/{process_model_id}" # create the model - process_model_info = self.create_process_model_with_api( + self.create_process_model_with_api( client=client, process_model_id=process_model_identifier, process_model_display_name="Simple Script", process_model_description="Simple Script", - user=with_super_admin_user + user=with_super_admin_user, ) bpmn_file_name = "simple_script.bpmn" @@ -40,7 +42,7 @@ class TestLoggingService(BaseTest): process_model_id=process_model_identifier, file_name=bpmn_file_name, file_data=bpmn_file_data_bytes, - user=with_super_admin_user + user=with_super_admin_user, ) headers = self.logged_in_headers(with_super_admin_user) response = self.create_process_instance( diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_nested_groups.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_nested_groups.py index 770d4f04..ef89a561 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_nested_groups.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_nested_groups.py @@ -1,41 +1,46 @@ +"""Test_nested_groups.""" import json -from spiffworkflow_backend.models.process_group import ProcessGroup, ProcessGroupSchema -from spiffworkflow_backend.models.process_model import ProcessModelInfo, ProcessModelInfoSchema -from spiffworkflow_backend.models.user import UserModel -from tests.spiffworkflow_backend.helpers.base_test import BaseTest from flask.app import Flask from flask.testing import FlaskClient +from tests.spiffworkflow_backend.helpers.base_test import BaseTest + +from spiffworkflow_backend.models.process_group import ProcessGroup +from spiffworkflow_backend.models.process_group import ProcessGroupSchema +from spiffworkflow_backend.models.process_model import ProcessModelInfo +from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema +from spiffworkflow_backend.models.user import UserModel class TestNestedGroups(BaseTest): + """TestNestedGroups.""" def test_nested_groups( - self, - app: Flask, - client: FlaskClient, - with_db_and_bpmn_file_cleanup: None, + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, ) -> None: + """Test_nested_groups.""" # /process-groups/{process_group_path}/show target_uri = "/v1.0/process-groups/group_a,group_b" user = self.find_or_create_user() self.add_permissions_to_user( user, target_uri=target_uri, permission_names=["read"] ) - response = client.get( - target_uri, - headers=self.logged_in_headers(user) + response = client.get( # noqa: F841 + target_uri, headers=self.logged_in_headers(user) ) print("test_nested_groups") def test_add_nested_group( - self, - app: Flask, - client: FlaskClient, - with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel, + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: - target_uri = "/process-groups" + """Test_add_nested_group.""" # user = self.find_or_create_user() # self.add_permissions_to_user( # user, target_uri=target_uri, permission_names=["read", "create"] @@ -46,33 +51,31 @@ class TestNestedGroups(BaseTest): display_order=0, admin=False, ) - response_a = client.post( + response_a = client.post( # noqa: F841 "/v1.0/process-groups", headers=self.logged_in_headers(with_super_admin_user), content_type="application/json", data=json.dumps(ProcessGroupSchema().dump(process_group_a)), ) - process_group_b = ProcessGroup( id="group_a/group_b", display_name="Group B", display_order=0, admin=False, ) - response_b = client.post( + response_b = client.post( # noqa: F841 "/v1.0/process-groups", headers=self.logged_in_headers(with_super_admin_user), content_type="application/json", data=json.dumps(ProcessGroupSchema().dump(process_group_b)), ) - process_group_c = ProcessGroup( id="group_a/group_b/group_c", display_name="Group C", display_order=0, admin=False, ) - response_c = client.post( + response_c = client.post( # noqa: F841 "/v1.0/process-groups", headers=self.logged_in_headers(with_super_admin_user), content_type="application/json", @@ -82,32 +85,32 @@ class TestNestedGroups(BaseTest): print("test_add_nested_group") def test_process_model_add( - self, - app: Flask, - client: FlaskClient, - with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel, - ): + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Test_process_model_add.""" process_group_a = ProcessGroup( id="group_a", display_name="Group A", display_order=0, admin=False, ) - response_a = client.post( + response_a = client.post( # noqa: F841 "/v1.0/process-groups", headers=self.logged_in_headers(with_super_admin_user), content_type="application/json", data=json.dumps(ProcessGroupSchema().dump(process_group_a)), ) - process_group_b = ProcessGroup( id="group_a/group_b", display_name="Group B", display_order=0, admin=False, ) - response_b = client.post( + response_b = client.post( # noqa: F841 "/v1.0/process-groups", headers=self.logged_in_headers(with_super_admin_user), content_type="application/json", @@ -119,24 +122,24 @@ class TestNestedGroups(BaseTest): description="Process Model", primary_file_name="primary_file.bpmn", primary_process_id="primary_process_id", - display_order=0 + display_order=0, ) - model_response = client.post( + model_response = client.post( # noqa: F841 "v1.0/process-models", headers=self.logged_in_headers(with_super_admin_user), content_type="application/json", - data=json.dumps(ProcessModelInfoSchema().dump(process_model)) + data=json.dumps(ProcessModelInfoSchema().dump(process_model)), ) print("test_process_model_add") def test_process_group_show( - self, - app: Flask, - client: FlaskClient, - with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel, + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: - + """Test_process_group_show.""" # target_uri = "/process-groups/{process_group_id}" # user = self.find_or_create_user("testadmin1") # self.add_permissions_to_user( @@ -152,7 +155,7 @@ class TestNestedGroups(BaseTest): display_order=0, admin=False, ) - response_create_a = client.post( + response_create_a = client.post( # noqa: F841 "/v1.0/process-groups", headers=self.logged_in_headers(with_super_admin_user), content_type="application/json", @@ -164,9 +167,8 @@ class TestNestedGroups(BaseTest): self.add_permissions_to_user( user, target_uri=target_uri, permission_names=["read"] ) - response = client.get( - target_uri, - headers=self.logged_in_headers(user) + response = client.get( # noqa: F841 + target_uri, headers=self.logged_in_headers(user) ) print("test_process_group_show: ") 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 3ecc32c0..91355e0e 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -9,8 +9,6 @@ import pytest from flask.app import Flask from flask.testing import FlaskClient from flask_bpmn.models.db import db - -from spiffworkflow_backend.services.process_instance_service import ProcessInstanceService from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec @@ -33,6 +31,9 @@ from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, ) +from spiffworkflow_backend.services.process_instance_service import ( + ProcessInstanceService, +) from spiffworkflow_backend.services.process_model_service import ProcessModelService # from spiffworkflow_backend.services.git_service import GitService @@ -114,7 +115,9 @@ class TestProcessApi(BaseTest): process_group_id = "test_process_group" process_group_display_name = "Test Process Group" # creates the group directory, and the json file - self.create_process_group(client, with_super_admin_user, process_group_id, process_group_display_name) + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_display_name + ) process_model_id = "sample" model_display_name = "Sample" @@ -170,14 +173,16 @@ class TestProcessApi(BaseTest): process_model_identifier = f"{process_group_id}/{process_model_id}" initial_primary_process_id = "sample" terminal_primary_process_id = "new_process_id" - self.create_process_group(client=client, user=with_super_admin_user, process_group_id=process_group_id) + self.create_process_group( + client=client, user=with_super_admin_user, process_group_id=process_group_id + ) bpmn_file_name = f"{process_model_id}.bpmn" bpmn_file_source_directory = process_model_id process_model = load_test_spec( process_model_id=process_model_identifier, bpmn_file_name=bpmn_file_name, - process_model_source_directory=process_model_id + process_model_source_directory=process_model_id, ) assert process_model.primary_process_id == initial_primary_process_id @@ -220,7 +225,9 @@ class TestProcessApi(BaseTest): process_group_description = "Test Process Group" process_model_id = "sample" process_model_identifier = f"{process_group_id}/{process_model_id}" - self.create_process_group(client, with_super_admin_user, process_group_id, process_group_description) + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_description + ) self.create_process_model_with_api( client, process_model_id=process_model_identifier, @@ -228,7 +235,9 @@ class TestProcessApi(BaseTest): ) # assert we have a model - process_model = ProcessModelService().get_process_model(process_model_identifier) + process_model = ProcessModelService().get_process_model( + process_model_identifier + ) assert process_model is not None assert process_model.id == process_model_identifier @@ -261,7 +270,9 @@ class TestProcessApi(BaseTest): process_model_identifier = f"{test_process_group_id}/{test_process_model_id}" modified_process_model_identifier = process_model_identifier.replace("/", ":") self.create_process_group(client, with_super_admin_user, test_process_group_id) - self.create_process_model_with_api(client, process_model_identifier, user=with_super_admin_user) + self.create_process_model_with_api( + client, process_model_identifier, user=with_super_admin_user + ) bpmn_file_data_bytes = self.get_test_data_file_contents( bpmn_file_name, bpmn_file_location ) @@ -271,7 +282,7 @@ class TestProcessApi(BaseTest): process_model_location=test_process_model_id, file_name=bpmn_file_name, file_data=bpmn_file_data_bytes, - user=with_super_admin_user + user=with_super_admin_user, ) headers = self.logged_in_headers(with_super_admin_user) # create an instance from a model @@ -306,14 +317,18 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_model_update.""" - self.create_process_group(client, with_super_admin_user, "test_process_group", "Test Process Group") + self.create_process_group( + client, with_super_admin_user, "test_process_group", "Test Process Group" + ) process_model_identifier = "test_process_group/make_cookies" self.create_process_model_with_api( client, process_model_id=process_model_identifier, user=with_super_admin_user, ) - process_model = ProcessModelService().get_process_model(process_model_identifier) + process_model = ProcessModelService().get_process_model( + process_model_identifier + ) assert process_model.id == process_model_identifier assert process_model.display_name == "Cooooookies" assert process_model.is_review is False @@ -608,7 +623,6 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_model_file_update.""" - process_model_identifier = self.basic_test_setup(client, with_super_admin_user) modified_process_model_id = process_model_identifier.replace("/", ":") @@ -661,7 +675,9 @@ class TestProcessApi(BaseTest): process_group_description = "Test Group" process_model_id = "random_fact" process_model_identifier = f"{process_group_id}/{process_model_id}" - self.create_process_group(client, with_super_admin_user, process_group_id, process_group_description) + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_description + ) self.create_process_model_with_api( client, process_model_id=process_model_identifier, @@ -672,7 +688,7 @@ class TestProcessApi(BaseTest): original_file = load_test_spec( process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - process_model_source_directory="random_fact" + process_model_source_directory="random_fact", ) modified_process_model_id = process_model_identifier.replace("/", ":") @@ -712,7 +728,9 @@ class TestProcessApi(BaseTest): # process_model = load_test_spec("random_fact") bad_process_model_identifier = f"x{process_model_identifier}" - modified_bad_process_model_identifier = bad_process_model_identifier.replace("/", ":") + modified_bad_process_model_identifier = bad_process_model_identifier.replace( + "/", ":" + ) response = client.delete( f"/v1.0/process-models/{modified_bad_process_model_identifier}/files/random_fact.svg", follow_redirects=True, @@ -920,9 +938,7 @@ class TestProcessApi(BaseTest): """Test_process_instance_create.""" test_process_model_id = "runs_without_input/sample" headers = self.logged_in_headers(with_super_admin_user) - response = self.create_process_instance( - client, test_process_model_id, headers - ) + response = self.create_process_instance(client, test_process_model_id, headers) assert response.json is not None assert response.json["updated_at_in_seconds"] is not None assert response.json["status"] == "not_started" @@ -946,7 +962,7 @@ class TestProcessApi(BaseTest): process_group_id="runs_without_input", process_model_id="sample", bpmn_file_name=None, - bpmn_file_location="sample" + bpmn_file_location="sample", ) headers = self.logged_in_headers(with_super_admin_user) @@ -955,7 +971,6 @@ class TestProcessApi(BaseTest): ) assert response.json is not None process_instance_id = response.json["id"] - modified_process_model_identifier = process_model_identifier.replace("/", ":") response = client.post( f"/v1.0/process-instances/{process_instance_id}/run", headers=self.logged_in_headers(with_super_admin_user), @@ -987,7 +1002,7 @@ class TestProcessApi(BaseTest): client, with_super_admin_user, process_group_id=process_group_id, - process_model_id=process_model_id + process_model_id=process_model_id, ) modified_process_model_identifier = process_model_identifier.replace("/", ":") headers = self.logged_in_headers(with_super_admin_user) @@ -1006,7 +1021,9 @@ class TestProcessApi(BaseTest): ) assert show_response.json is not None file_system_root = FileSystemService.root_path() - file_path = f"{file_system_root}/{process_model_identifier}/{process_model_id}.bpmn" + file_path = ( + f"{file_system_root}/{process_model_identifier}/{process_model_id}.bpmn" + ) with open(file_path) as f_open: xml_file_contents = f_open.read() assert show_response.json["bpmn_xml_file_contents"] == xml_file_contents @@ -1030,7 +1047,7 @@ class TestProcessApi(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) message_model_identifier = "message_send" @@ -1078,9 +1095,8 @@ class TestProcessApi(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) - modified_process_model_identifier = process_model_identifier.replace("/", ":") message_model_identifier = "message_response" payload = { @@ -1147,9 +1163,8 @@ class TestProcessApi(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) - modified_process_model_identifier = process_model_identifier.replace("/", ":") response = self.create_process_instance( client, @@ -1197,9 +1212,8 @@ class TestProcessApi(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) - modified_process_model_identifier = process_model_identifier.replace("/", ":") headers = self.logged_in_headers(with_super_admin_user) response = self.create_process_instance( @@ -1230,7 +1244,6 @@ class TestProcessApi(BaseTest): """Test_process_instance_run_user_task.""" process_group_id = "my_process_group" process_model_id = "dynamic_enum_select_fields" - bpmn_file_name = "dynamic_enums_ask_for_color.bpmn" bpmn_file_location = "dynamic_enum_select_fields" process_model_identifier = self.basic_test_setup( client, @@ -1238,9 +1251,8 @@ class TestProcessApi(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, # bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) - modified_process_model_identifier = process_model_identifier.replace("/", ":") headers = self.logged_in_headers(with_super_admin_user) response = self.create_process_instance( @@ -1255,7 +1267,7 @@ class TestProcessApi(BaseTest): ) assert response.json is not None - assert response.json['next_task'] is not None + assert response.json["next_task"] is not None active_tasks = ( db.session.query(ActiveTaskModel) @@ -1290,13 +1302,11 @@ class TestProcessApi(BaseTest): with_super_admin_user, process_group_id=process_group_id, process_model_id=process_model_id, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) headers = self.logged_in_headers(with_super_admin_user) - self.create_process_instance( - client, process_model_identifier, headers - ) + self.create_process_instance(client, process_model_identifier, headers) response = client.get( "/v1.0/process-instances", @@ -1312,7 +1322,8 @@ class TestProcessApi(BaseTest): process_instance_dict = response.json["results"][0] assert type(process_instance_dict["id"]) is int assert ( - process_instance_dict["process_model_identifier"] == process_model_identifier + process_instance_dict["process_model_identifier"] + == process_model_identifier ) assert type(process_instance_dict["start_in_seconds"]) is int assert process_instance_dict["start_in_seconds"] > 0 @@ -1337,24 +1348,14 @@ class TestProcessApi(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) headers = self.logged_in_headers(with_super_admin_user) - self.create_process_instance( - client, process_model_identifier, headers - ) - self.create_process_instance( - client, process_model_identifier, headers - ) - self.create_process_instance( - client, process_model_identifier, headers - ) - self.create_process_instance( - client, process_model_identifier, headers - ) - self.create_process_instance( - client, process_model_identifier, headers - ) + self.create_process_instance(client, process_model_identifier, headers) + self.create_process_instance(client, process_model_identifier, headers) + self.create_process_instance(client, process_model_identifier, headers) + self.create_process_instance(client, process_model_identifier, headers) + self.create_process_instance(client, process_model_identifier, headers) response = client.get( "/v1.0/process-instances?per_page=2&page=3", @@ -1396,7 +1397,7 @@ class TestProcessApi(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) statuses = [status.value for status in ProcessInstanceStatus] @@ -1503,15 +1504,14 @@ class TestProcessApi(BaseTest): process_model_id = "sample" bpmn_file_name = "sample.bpmn" bpmn_file_location = "sample" - process_model_identifier = self.basic_test_setup( + process_model_identifier = self.basic_test_setup( # noqa: F841 client, with_super_admin_user, process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) - modified_process_model_identifier = process_model_identifier.replace("/", ":") self.logged_in_headers(with_super_admin_user) report_identifier = "testreport" @@ -1553,7 +1553,6 @@ class TestProcessApi(BaseTest): # bpmn_file_location=bpmn_file_location # ) process_model_identifier = f"{process_group_id}/{process_model_id}" - modified_process_model_identifier = process_model_identifier.replace("/", ":") report_metadata = { "columns": [ @@ -1594,7 +1593,8 @@ class TestProcessApi(BaseTest): process_instance_dict = response.json["results"][0] assert type(process_instance_dict["id"]) is int assert ( - process_instance_dict["process_model_identifier"] == process_model_identifier + process_instance_dict["process_model_identifier"] + == process_model_identifier ) assert type(process_instance_dict["start_in_seconds"]) is int assert process_instance_dict["start_in_seconds"] > 0 @@ -1609,7 +1609,6 @@ class TestProcessApi(BaseTest): setup_process_instances_for_reports: list[ProcessInstanceModel], ) -> None: """Test_process_instance_report_show_with_default_list.""" - report_metadata = { "filter_by": [ { @@ -1659,9 +1658,7 @@ class TestProcessApi(BaseTest): ) -> Any: """Setup_testing_instance.""" headers = self.logged_in_headers(with_super_admin_user) - response = self.create_process_instance( - client, process_model_id, headers - ) + response = self.create_process_instance(client, process_model_id, headers) process_instance = response.json assert isinstance(process_instance, dict) process_instance_id = process_instance["id"] @@ -1685,7 +1682,7 @@ class TestProcessApi(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) process_instance_id = self.setup_testing_instance( @@ -1739,7 +1736,7 @@ class TestProcessApi(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) process_instance_id = self.setup_testing_instance( @@ -1793,7 +1790,7 @@ class TestProcessApi(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) process_instance_id = self.setup_testing_instance( @@ -1852,7 +1849,7 @@ class TestProcessApi(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) result = self.create_spec_file( @@ -1885,7 +1882,7 @@ class TestProcessApi(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) # load_test_spec( # "message_receiver", @@ -1977,7 +1974,7 @@ class TestProcessApi(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) # process_model = load_test_spec( @@ -2171,6 +2168,7 @@ class TestProcessApi(BaseTest): with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel, ) -> None: + """Test_process_instance_suspend.""" bpmn_file_name = "manual_task.bpmn" bpmn_file_location = "manual_task" process_model_identifier = self.basic_test_setup( @@ -2178,7 +2176,7 @@ class TestProcessApi(BaseTest): user=with_super_admin_user, process_model_id="manual_task", bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) bpmn_file_data_bytes = self.get_test_data_file_contents( @@ -2190,7 +2188,7 @@ class TestProcessApi(BaseTest): process_model_location=process_model_identifier, file_name=bpmn_file_name, file_data=bpmn_file_data_bytes, - user=with_super_admin_user + user=with_super_admin_user, ) headers = self.logged_in_headers(with_super_admin_user) @@ -2205,14 +2203,18 @@ class TestProcessApi(BaseTest): headers=self.logged_in_headers(with_super_admin_user), ) - process_instance = ProcessInstanceService().get_process_instance(process_instance_id) + process_instance = ProcessInstanceService().get_process_instance( + process_instance_id + ) assert process_instance.status == "user_input_required" client.post( f"/v1.0/process-instances/{process_instance_id}/suspend", headers=self.logged_in_headers(with_super_admin_user), ) - process_instance = ProcessInstanceService().get_process_instance(process_instance_id) + process_instance = ProcessInstanceService().get_process_instance( + process_instance_id + ) assert process_instance.status == "suspended" # TODO: Why can I run a suspended process instance? @@ -2232,6 +2234,7 @@ class TestProcessApi(BaseTest): with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel, ) -> None: + """Test_script_unit_test_run.""" process_group_id = "test_group" process_model_id = "simple_script" bpmn_file_name = "simple_script.bpmn" @@ -2242,7 +2245,7 @@ class TestProcessApi(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) bpmn_file_data_bytes = self.get_test_data_file_contents( @@ -2254,7 +2257,7 @@ class TestProcessApi(BaseTest): process_model_location=process_model_identifier, file_name=bpmn_file_name, file_data=bpmn_file_data_bytes, - user=with_super_admin_user + user=with_super_admin_user, ) # python_script = _get_required_parameter_or_raise("python_script", body) @@ -2263,22 +2266,21 @@ class TestProcessApi(BaseTest): # "expected_output_json", body # ) python_script = "c = a + b" - input_json = {'a': 1, 'b': 2} - expected_output_json = {'a': 1, 'b': 2, 'c': 3} + input_json = {"a": 1, "b": 2} + expected_output_json = {"a": 1, "b": 2, "c": 3} # bpmn_task_identifier = "Activity_CalculateNewData" data = { - 'python_script': python_script, - 'input_json': input_json, - 'expected_output_json': expected_output_json, + "python_script": python_script, + "input_json": input_json, + "expected_output_json": expected_output_json, } - response = client.post( + response = client.post( # noqa: F841 f"/v1.0/process-models/{process_group_id}/{process_model_id}/script-unit-tests/run", headers=self.logged_in_headers(with_super_admin_user), content_type="application/json", data=json.dumps(data), ) - print("test_script_unit_test_run") diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_secret_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_secret_service.py index 7e0e458f..071ef6cc 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_secret_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_secret_service.py @@ -42,7 +42,9 @@ class SecretServiceTestHelpers(BaseTest): self.test_process_group_id, display_name=self.test_process_group_display_name, ) - process_model_identifier = f"{self.test_process_group_id}/{self.test_process_model_id}" + process_model_identifier = ( + f"{self.test_process_group_id}/{self.test_process_model_id}" + ) self.create_process_model_with_api( client, process_model_id=process_model_identifier, diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_group_members.py b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_group_members.py index 12104d8e..8a6046b5 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_group_members.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_group_members.py @@ -21,7 +21,7 @@ class TestGetGroupMembers(BaseTest): app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel + with_super_admin_user: UserModel, ) -> None: """Test_can_get_members_of_a_group.""" initiator_user = self.find_or_create_user("initiator_user") @@ -38,11 +38,13 @@ class TestGetGroupMembers(BaseTest): UserService.add_user_to_group(testuser2, group_a) UserService.add_user_to_group(testuser3, group_b) - self.create_process_group(client, with_super_admin_user, "test_group", "test_group") + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) process_model = load_test_spec( process_model_id="test_group/get_group_members", bpmn_file_name="get_group_members.bpmn", - process_model_source_directory="get_group_members" + process_model_source_directory="get_group_members", ) process_instance = self.create_process_instance_from_process_model( process_model=process_model, user=initiator_user diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_localtime.py b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_localtime.py index 54afb61a..9e65b970 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_localtime.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_localtime.py @@ -50,13 +50,17 @@ class TestGetLocaltime(BaseTest): """Test_process_instance_run.""" initiator_user = self.find_or_create_user("initiator_user") self.add_permissions_to_user( - initiator_user, target_uri="/v1.0/process-groups", permission_names=["read", "create"] + initiator_user, + target_uri="/v1.0/process-groups", + permission_names=["read", "create"], + ) + self.create_process_group( + client=client, user=initiator_user, process_group_id="test_group" ) - self.create_process_group(client=client, user=initiator_user, process_group_id="test_group") process_model = load_test_spec( process_model_id="test_group/get_localtime", bpmn_file_name="get_localtime.bpmn", - process_model_source_directory="get_localtime" + process_model_source_directory="get_localtime", ) process_instance = self.create_process_instance_from_process_model( process_model=process_model, user=initiator_user diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py index 2c8b6879..5d0a10be 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py @@ -2,12 +2,10 @@ import pytest from flask import Flask from flask.testing import FlaskClient - -from spiffworkflow_backend.services.process_model_service import ProcessModelService from tests.spiffworkflow_backend.helpers.base_test import BaseTest -from tests.spiffworkflow_backend.helpers.test_data import load_test_spec -from spiffworkflow_backend.models.user import UserModel, UserNotFoundError +from spiffworkflow_backend.models.user import UserModel +from spiffworkflow_backend.models.user import UserNotFoundError from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, @@ -15,6 +13,7 @@ from spiffworkflow_backend.services.process_instance_processor import ( from spiffworkflow_backend.services.process_instance_service import ( ProcessInstanceService, ) +from spiffworkflow_backend.services.process_model_service import ProcessModelService class TestAuthorizationService(BaseTest): @@ -96,7 +95,7 @@ class TestAuthorizationService(BaseTest): app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel + with_super_admin_user: UserModel, ) -> None: """Test_user_can_be_added_to_active_task_on_first_login.""" initiator_user = self.find_or_create_user("initiator_user") @@ -111,7 +110,7 @@ class TestAuthorizationService(BaseTest): process_group_id="test_group", process_model_id="model_with_lanes", bpmn_file_name="lanes.bpmn", - bpmn_file_location="model_with_lanes" + bpmn_file_location="model_with_lanes", ) process_model = ProcessModelService().get_process_model( diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_dot_notation.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_dot_notation.py index 5ea6af39..ff37c3b5 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_dot_notation.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_dot_notation.py @@ -1,10 +1,7 @@ """Test_various_bpmn_constructs.""" from flask.app import Flask from flask.testing import FlaskClient - -from spiffworkflow_backend.services.authorization_service import AuthorizationService from tests.spiffworkflow_backend.helpers.base_test import BaseTest -from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_instance_processor import ( @@ -19,7 +16,11 @@ class TestDotNotation(BaseTest): """TestVariousBpmnConstructs.""" def test_dot_notation( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_form_data_conversion_to_dot_dict.""" process_group_id = "dot_notation_group" @@ -32,7 +33,7 @@ class TestDotNotation(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) headers = self.logged_in_headers(with_super_admin_user) @@ -40,7 +41,9 @@ class TestDotNotation(BaseTest): client, process_model_identifier, headers ) process_instance_id = response.json["id"] - process_instance = ProcessInstanceService().get_process_instance(process_instance_id) + process_instance = ProcessInstanceService().get_process_instance( + process_instance_id + ) processor = ProcessInstanceProcessor(process_instance) processor.do_engine_steps(save=True) diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_instance.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_instance.py index f340eefc..39b37f2c 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_instance.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_instance.py @@ -3,19 +3,19 @@ import pytest from flask import Flask from flask.testing import FlaskClient from flask_bpmn.models.db import db - -from spiffworkflow_backend.services.process_model_service import ProcessModelService from tests.spiffworkflow_backend.helpers.base_test import BaseTest from spiffworkflow_backend.models.message_instance import MessageInstanceModel from spiffworkflow_backend.models.message_model import MessageModel from spiffworkflow_backend.models.user import UserModel +from spiffworkflow_backend.services.process_model_service import ProcessModelService class TestMessageInstance(BaseTest): """TestMessageInstance.""" def setup_message_tests(self, client: FlaskClient, user: UserModel) -> str: + """Setup_message_tests.""" process_group_id = "test_group" process_model_id = "hello_world" bpmn_file_name = "hello_world.bpmn" @@ -26,17 +26,23 @@ class TestMessageInstance(BaseTest): process_group_id=process_group_id, process_model_id=process_model_id, bpmn_file_name=bpmn_file_name, - bpmn_file_location=bpmn_file_location + bpmn_file_location=bpmn_file_location, ) return process_model_identifier def test_can_create_message_instance( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_create_message_instance.""" message_model_identifier = "message_model_one" message_model = self.create_message_model(message_model_identifier) - process_model_identifier = self.setup_message_tests(client, with_super_admin_user) + process_model_identifier = self.setup_message_tests( + client, with_super_admin_user + ) process_model = ProcessModelService().get_process_model( process_model_id=process_model_identifier @@ -62,12 +68,18 @@ class TestMessageInstance(BaseTest): assert queued_message_from_query is not None def test_cannot_set_invalid_status( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_cannot_set_invalid_status.""" message_model_identifier = "message_model_one" message_model = self.create_message_model(message_model_identifier) - process_model_identifier = self.setup_message_tests(client, with_super_admin_user) + process_model_identifier = self.setup_message_tests( + client, with_super_admin_user + ) process_model = ProcessModelService().get_process_model( process_model_id=process_model_identifier @@ -102,12 +114,18 @@ class TestMessageInstance(BaseTest): ) def test_cannot_set_invalid_message_type( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_cannot_set_invalid_message_type.""" message_model_identifier = "message_model_one" message_model = self.create_message_model(message_model_identifier) - process_model_identifier = self.setup_message_tests(client, with_super_admin_user) + process_model_identifier = self.setup_message_tests( + client, with_super_admin_user + ) process_model = ProcessModelService().get_process_model( process_model_id=process_model_identifier @@ -143,12 +161,18 @@ class TestMessageInstance(BaseTest): ) def test_force_failure_cause_if_status_is_failure( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_force_failure_cause_if_status_is_failure.""" message_model_identifier = "message_model_one" message_model = self.create_message_model(message_model_identifier) - process_model_identifier = self.setup_message_tests(client, with_super_admin_user) + process_model_identifier = self.setup_message_tests( + client, with_super_admin_user + ) process_model = ProcessModelService().get_process_model( process_model_id=process_model_identifier diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_service.py index 311a5157..aa1f2805 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_service.py @@ -24,16 +24,22 @@ class TestMessageService(BaseTest): """TestMessageService.""" def test_can_send_message_to_waiting_message( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_send_message_to_waiting_message.""" process_group_id = "test_group" - self.create_process_group(client, with_super_admin_user, process_group_id, process_group_id) + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_id + ) load_test_spec( "test_group/message_receiver", process_model_source_directory="message_send_one_conversation", - bpmn_file_name="message_receiver.bpmn" + bpmn_file_name="message_receiver.bpmn", ) process_model_sender = load_test_spec( "test_group/message_sender", @@ -118,12 +124,17 @@ class TestMessageService(BaseTest): assert process_instance.status == "complete" def test_can_send_message_to_multiple_process_models( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_send_message_to_multiple_process_models.""" - process_group_id = "test_group" - self.create_process_group(client, with_super_admin_user, process_group_id, process_group_id) + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_id + ) process_model_sender = load_test_spec( "test_group/message_sender", diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_permissions.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_permissions.py index eec3c47c..117fd0af 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_permissions.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_permissions.py @@ -24,15 +24,21 @@ class TestPermissions(BaseTest): """TestPermissions.""" def test_user_can_be_given_permission_to_administer_process_group( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_user_can_be_given_permission_to_administer_process_group.""" process_group_id = "group-a" - self.create_process_group(client, with_super_admin_user, process_group_id, process_group_id) + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_id + ) load_test_spec( "group-a/timers_intermediate_catch_event", bpmn_file_name="timers_intermediate_catch_event.bpmn", - process_model_source_directory="timers_intermediate_catch_event" + process_model_source_directory="timers_intermediate_catch_event", ) dan = self.find_or_create_user() principal = dan.principal @@ -61,7 +67,7 @@ class TestPermissions(BaseTest): load_test_spec( f"{process_group_id}/timers_intermediate_catch_event", bpmn_file_name="timers_intermediate_catch_event", - process_model_source_directory="timers_intermediate_catch_event" + process_model_source_directory="timers_intermediate_catch_event", ) group_a_admin = self.find_or_create_user() @@ -95,7 +101,7 @@ class TestPermissions(BaseTest): load_test_spec( f"{process_group_id}/timers_intermediate_catch_event", bpmn_file_name="timers_intermediate_catch_event.bpmn", - process_model_source_directory="timers_intermediate_catch_event" + process_model_source_directory="timers_intermediate_catch_event", ) user = self.find_or_create_user() group = GroupModel(identifier="groupA") @@ -134,7 +140,7 @@ class TestPermissions(BaseTest): load_test_spec( f"{process_group_id}/timers_intermediate_catch_event", bpmn_file_name="timers_intermediate_catch_event.bpmn", - process_model_source_directory="timers_intermediate_catch_event" + process_model_source_directory="timers_intermediate_catch_event", ) group_a_admin = self.find_or_create_user() diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py index d39f18db..ad7aefe3 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py @@ -54,10 +54,12 @@ class TestProcessInstanceProcessor(BaseTest): app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel + with_super_admin_user: UserModel, ) -> None: """Test_sets_permission_correctly_on_active_task.""" - self.create_process_group(client, with_super_admin_user, "test_group", "test_group") + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) initiator_user = self.find_or_create_user("initiator_user") finance_user = self.find_or_create_user("testuser2") assert initiator_user.principal is not None @@ -70,7 +72,7 @@ class TestProcessInstanceProcessor(BaseTest): process_model = load_test_spec( process_model_id="test_group/model_with_lanes", bpmn_file_name="lanes.bpmn", - process_model_source_directory="model_with_lanes" + process_model_source_directory="model_with_lanes", ) process_instance = self.create_process_instance_from_process_model( process_model=process_model, user=initiator_user @@ -132,10 +134,12 @@ class TestProcessInstanceProcessor(BaseTest): app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel + with_super_admin_user: UserModel, ) -> None: """Test_sets_permission_correctly_on_active_task_when_using_dict.""" - self.create_process_group(client, with_super_admin_user, "test_group", "test_group") + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) initiator_user = self.find_or_create_user("initiator_user") finance_user_three = self.find_or_create_user("testuser3") finance_user_four = self.find_or_create_user("testuser4") @@ -150,7 +154,7 @@ class TestProcessInstanceProcessor(BaseTest): process_model = load_test_spec( process_model_id="test_group/model_with_lanes", bpmn_file_name="lanes_with_owner_dict.bpmn", - process_model_source_directory="model_with_lanes" + process_model_source_directory="model_with_lanes", ) process_instance = self.create_process_instance_from_process_model( process_model=process_model, user=initiator_user diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model.py index cbb13195..5b5b9f25 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model.py @@ -24,10 +24,16 @@ class TestProcessModel(BaseTest): assert process_model_one.files == [] def test_can_run_process_model_with_call_activities_when_in_same_process_model_directory( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_run_process_model_with_call_activities.""" - self.create_process_group(client, with_super_admin_user, "test_group", "test_group") + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) process_model = load_test_spec( "test_group/call_activity_test", # bpmn_file_name="call_activity_test.bpmn", @@ -42,10 +48,16 @@ class TestProcessModel(BaseTest): assert process_instance.status == "complete" def test_can_run_process_model_with_call_activities_when_not_in_same_directory( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_run_process_model_with_call_activities.""" - self.create_process_group(client, with_super_admin_user, "test_group", "test_group") + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) process_model = load_test_spec( "test_group/call_activity_nested", process_model_source_directory="call_activity_nested", @@ -71,10 +83,16 @@ class TestProcessModel(BaseTest): assert process_instance.status == "complete" def test_can_run_process_model_with_call_activities_when_process_identifier_is_not_in_database( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_run_process_model_with_call_activities.""" - self.create_process_group(client, with_super_admin_user, "test_group", "test_group") + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) process_model = load_test_spec( "test_group/call_activity_nested", process_model_source_directory="call_activity_nested", diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model_service.py index 5611f985..438ef89d 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model_service.py @@ -12,14 +12,20 @@ class TestProcessModelService(BaseTest): """TestProcessModelService.""" def test_can_update_specified_attributes( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_update_specified_attributes.""" - self.create_process_group(client, with_super_admin_user, "test_group", "test_group") + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) process_model = load_test_spec( "test_group/hello_world", bpmn_file_name="hello_world.bpmn", - process_model_source_directory="hello_world" + process_model_source_directory="hello_world", ) assert process_model.display_name == "test_group/hello_world" diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_restricted_script_engine.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_restricted_script_engine.py index ac8c44ec..d31ea424 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_restricted_script_engine.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_restricted_script_engine.py @@ -16,10 +16,16 @@ class TestOpenFile(BaseTest): """TestVariousBpmnConstructs.""" def test_dot_notation( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_form_data_conversion_to_dot_dict.""" - self.create_process_group(client, with_super_admin_user, "test_group", "test_group") + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) process_model = load_test_spec( "test_group/dangerous", bpmn_file_name="read_etc_passwd.bpmn", @@ -41,10 +47,16 @@ class TestImportModule(BaseTest): """TestVariousBpmnConstructs.""" def test_dot_notation( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_form_data_conversion_to_dot_dict.""" - self.create_process_group(client, with_super_admin_user, "test_group", "test_group") + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) process_model = load_test_spec( "test_group/dangerous", bpmn_file_name="read_env.bpmn", diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_script_unit_test_runner.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_script_unit_test_runner.py index f9cc7cfa..9ece043a 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_script_unit_test_runner.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_script_unit_test_runner.py @@ -20,19 +20,21 @@ class TestScriptUnitTestRunner(BaseTest): app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel + with_super_admin_user: UserModel, ) -> None: """Test_takes_data_and_returns_expected_result.""" app.config["THREAD_LOCAL_DATA"].process_instance_id = None process_group_id = "test_logging_spiff_logger" - self.create_process_group(client, with_super_admin_user, process_group_id, process_group_id) + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_id + ) process_model_id = "simple_script" process_model_identifier = f"{process_group_id}/{process_model_id}" load_test_spec( process_model_identifier, bpmn_file_name=process_model_id, - process_model_source_directory=process_model_id + process_model_source_directory=process_model_id, ) bpmn_process_instance = ( ProcessInstanceProcessor.get_bpmn_process_instance_from_process_model( @@ -60,20 +62,22 @@ class TestScriptUnitTestRunner(BaseTest): app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel + with_super_admin_user: UserModel, ) -> None: """Test_fails_when_expected_output_does_not_match_actual_output.""" app.config["THREAD_LOCAL_DATA"].process_instance_id = None process_group_id = "test_logging_spiff_logger" - self.create_process_group(client, with_super_admin_user, process_group_id, process_group_id) + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_id + ) process_model_id = "simple_script" process_model_identifier = f"{process_group_id}/{process_model_id}" load_test_spec( process_model_identifier, bpmn_file_name=process_model_id, - process_model_source_directory=process_model_id + process_model_source_directory=process_model_id, ) bpmn_process_instance = ( ProcessInstanceProcessor.get_bpmn_process_instance_from_process_model( @@ -101,20 +105,22 @@ class TestScriptUnitTestRunner(BaseTest): app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel + with_super_admin_user: UserModel, ) -> None: """Test_script_with_unit_tests_when_hey_is_passed_in.""" app.config["THREAD_LOCAL_DATA"].process_instance_id = None process_group_id = "script_with_unit_tests" - self.create_process_group(client, with_super_admin_user, process_group_id, process_group_id) + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_id + ) process_model_id = "script_with_unit_tests" process_model_identifier = f"{process_group_id}/{process_model_id}" load_test_spec( process_model_identifier, bpmn_file_name=process_model_id, - process_model_source_directory=process_model_id + process_model_source_directory=process_model_id, ) bpmn_process_instance = ( ProcessInstanceProcessor.get_bpmn_process_instance_from_process_model( @@ -140,13 +146,15 @@ class TestScriptUnitTestRunner(BaseTest): app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel + with_super_admin_user: UserModel, ) -> None: """Test_script_with_unit_tests_when_hey_is_not_passed_in.""" app.config["THREAD_LOCAL_DATA"].process_instance_id = None process_group_id = "script_with_unit_tests" - self.create_process_group(client, with_super_admin_user, process_group_id, process_group_id) + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_id + ) process_model_id = "script_with_unit_tests" process_model_identifier = f"{process_group_id}/{process_model_id}" @@ -154,7 +162,7 @@ class TestScriptUnitTestRunner(BaseTest): load_test_spec( process_model_identifier, bpmn_file_name=process_model_id, - process_model_source_directory=process_model_id + process_model_source_directory=process_model_id, ) bpmn_process_instance = ( ProcessInstanceProcessor.get_bpmn_process_instance_from_process_model( diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spec_file_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spec_file_service.py index edfade0d..d74acb47 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spec_file_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spec_file_service.py @@ -28,7 +28,11 @@ class TestSpecFileService(BaseTest): ) def test_can_store_process_ids_for_lookup( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_store_process_ids_for_lookup.""" self.basic_test_setup( @@ -37,7 +41,7 @@ class TestSpecFileService(BaseTest): process_group_id=self.process_group_id, process_model_id=self.process_model_id, bpmn_file_name=self.bpmn_file_name, - bpmn_file_location="call_activity_nested" + bpmn_file_location="call_activity_nested", ) bpmn_process_id_lookups = BpmnProcessIdLookup.query.all() assert len(bpmn_process_id_lookups) == 1 @@ -48,7 +52,11 @@ class TestSpecFileService(BaseTest): ) def test_fails_to_save_duplicate_process_id( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_fails_to_save_duplicate_process_id.""" bpmn_process_identifier = "Level1" @@ -58,7 +66,7 @@ class TestSpecFileService(BaseTest): process_group_id=self.process_group_id, process_model_id=self.process_model_id, bpmn_file_name=self.bpmn_file_name, - bpmn_file_location=self.process_model_id + bpmn_file_location=self.process_model_id, ) bpmn_process_id_lookups = BpmnProcessIdLookup.query.all() assert len(bpmn_process_id_lookups) == 1 @@ -81,7 +89,11 @@ class TestSpecFileService(BaseTest): ) def test_updates_relative_file_path_when_appropriate( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_updates_relative_file_path_when_appropriate.""" bpmn_process_identifier = "Level1" @@ -98,7 +110,7 @@ class TestSpecFileService(BaseTest): process_group_id=self.process_group_id, process_model_id=self.process_model_id, bpmn_file_name=self.bpmn_file_name, - bpmn_file_location=self.process_model_id + bpmn_file_location=self.process_model_id, ) bpmn_process_id_lookups = BpmnProcessIdLookup.query.all() @@ -113,7 +125,11 @@ class TestSpecFileService(BaseTest): ) def test_load_reference_information( - self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_load_reference_information. @@ -128,14 +144,13 @@ class TestSpecFileService(BaseTest): """ process_group_id = "test_group" process_model_id = "call_activity_nested" - bpmn_file_name = "call_activity_nested.bpmn" process_model_identifier = self.basic_test_setup( client=client, user=with_super_admin_user, process_group_id=process_group_id, process_model_id=process_model_id, # bpmn_file_name=bpmn_file_name, - bpmn_file_location=process_model_id + bpmn_file_location=process_model_id, ) # load_test_spec( # , diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_various_bpmn_constructs.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_various_bpmn_constructs.py index 56e6439e..c655d3ff 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_various_bpmn_constructs.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_various_bpmn_constructs.py @@ -1,11 +1,14 @@ """Test_various_bpmn_constructs.""" from flask.app import Flask from flask.testing import FlaskClient -from spiffworkflow_backend.models.user import UserModel -from spiffworkflow_backend.services.process_model_service import ProcessModelService -from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor from tests.spiffworkflow_backend.helpers.base_test import BaseTest +from spiffworkflow_backend.models.user import UserModel +from spiffworkflow_backend.services.process_instance_processor import ( + ProcessInstanceProcessor, +) +from spiffworkflow_backend.services.process_model_service import ProcessModelService + class TestVariousBpmnConstructs(BaseTest): """TestVariousBpmnConstructs.""" @@ -15,14 +18,14 @@ class TestVariousBpmnConstructs(BaseTest): app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel + with_super_admin_user: UserModel, ) -> None: """Test_running_process_with_timer_intermediate_catch_event.""" process_model_identifier = self.basic_test_setup( client, with_super_admin_user, "test_group", - "timer_intermediate_catch_event" + "timer_intermediate_catch_event", ) process_model = ProcessModelService().get_process_model( From 2cf66729d0529f5301ac3d4bdb464633f49886a0 Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 9 Nov 2022 14:51:22 -0500 Subject: [PATCH 10/22] process instance cypress tests pass now w/ burnettk --- .../cypress/e2e/process_instances.cy.js | 38 +++++++++---------- .../cypress/support/commands.js | 2 +- spiffworkflow-frontend/src/helpers.test.tsx | 7 +++- spiffworkflow-frontend/src/helpers.tsx | 4 +- 4 files changed, 29 insertions(+), 22 deletions(-) diff --git a/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js b/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js index d25350bc..6999acc2 100644 --- a/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js @@ -165,34 +165,34 @@ describe('process-instances', () => { cy.basicPaginationTest(); }); - it.only('can filter', () => { + it('can filter', () => { cy.getBySel('process-instance-list-link').click(); cy.assertAtLeastOneItemInPaginatedResults(); - // PROCESS_STATUSES.forEach((processStatus) => { - // if (!['all', 'waiting'].includes(processStatus)) { - // cy.get('#process-instance-status-select').click(); - // cy.get('#process-instance-status-select') - // .contains(processStatus) - // .click(); - // // close the dropdown again - // cy.get('#process-instance-status-select').click(); - // cy.getBySel('filter-button').click(); - // cy.assertAtLeastOneItemInPaginatedResults(); - // cy.getBySel(`process-instance-status-${processStatus}`).contains( - // processStatus - // ); - // // there should really only be one, but in CI there are sometimes more - // cy.get('div[aria-label="Clear all selected items"]:first').click(); - // } - // }); + PROCESS_STATUSES.forEach((processStatus) => { + if (!['all', 'waiting'].includes(processStatus)) { + cy.get('#process-instance-status-select').click(); + cy.get('#process-instance-status-select') + .contains(processStatus) + .click(); + // close the dropdown again + cy.get('#process-instance-status-select').click(); + cy.getBySel('filter-button').click(); + cy.assertAtLeastOneItemInPaginatedResults(); + cy.getBySel(`process-instance-status-${processStatus}`).contains( + processStatus + ); + // there should really only be one, but in CI there are sometimes more + cy.get('div[aria-label="Clear all selected items"]:first').click(); + } + }); const date = new Date(); date.setHours(date.getHours() - 1); filterByDate(date); cy.assertAtLeastOneItemInPaginatedResults(); - date.setHours(date.getHours() + 2); + date.setHours(date.getHours() + 26); filterByDate(date); cy.assertNoItemInPaginatedResults(); }); diff --git a/spiffworkflow-frontend/cypress/support/commands.js b/spiffworkflow-frontend/cypress/support/commands.js index c6518917..899bbb93 100644 --- a/spiffworkflow-frontend/cypress/support/commands.js +++ b/spiffworkflow-frontend/cypress/support/commands.js @@ -118,7 +118,7 @@ Cypress.Commands.add('assertAtLeastOneItemInPaginatedResults', () => { }); Cypress.Commands.add('assertNoItemInPaginatedResults', () => { - cy.getBySel('total-paginated-items').contains('0'); + cy.contains(/\b0–0 of 0 items/); }); Cypress.Commands.add('modifyProcessModelPath', (path) => { diff --git a/spiffworkflow-frontend/src/helpers.test.tsx b/spiffworkflow-frontend/src/helpers.test.tsx index 9a58f5e6..1031fc46 100644 --- a/spiffworkflow-frontend/src/helpers.test.tsx +++ b/spiffworkflow-frontend/src/helpers.test.tsx @@ -1,7 +1,12 @@ -import { slugifyString } from './helpers'; +import { convertSecondsToFormattedDate, slugifyString } from './helpers'; test('it can slugify a string', () => { expect(slugifyString('hello---world_ and then Some such-')).toEqual( 'hello-world-and-then-some-such' ); }); + +test('it can keep the correct date when converting seconds to date', () => { + const dateString = convertSecondsToFormattedDate(1666325400); + expect(dateString).toEqual('2022-10-21'); +}); diff --git a/spiffworkflow-frontend/src/helpers.tsx b/spiffworkflow-frontend/src/helpers.tsx index adb28e73..eb5902e3 100644 --- a/spiffworkflow-frontend/src/helpers.tsx +++ b/spiffworkflow-frontend/src/helpers.tsx @@ -44,7 +44,9 @@ export const convertDateToSeconds = ( export const convertStringToDate = (dateString: string) => { if (dateString) { - return new Date(dateString); + // add midnight time to the date so it c uses the correct date + // after converting to timezone + return new Date(`${dateString}T00:10:00`); } return null; }; From cc24e8685b4a965955e9ce2fdd9d55fed6d360fc Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 9 Nov 2022 15:02:17 -0500 Subject: [PATCH 11/22] Squashed 'SpiffWorkflow/' changes from 8d820dce1..5d35f5c50 5d35f5c50 Quick fix on the readthedocs. git-subtree-dir: SpiffWorkflow git-subtree-split: 5d35f5c50d06e35b17c7e045fcf6000a6e16d8b6 --- .readthedocs.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 90938128..488d9830 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,16 +1,19 @@ # .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - +version: 2 build: - image: latest + os: ubuntu-20.04 + tools: + python: "3.10" # Optionally build your docs in additional formats such as PDF formats: [] python: - pip_install: true - version: 3.10 - extra_requirements: + install: + - method: pip + path: . + extra_requirements: - docs From 23f0b12e9ab61b746e9d8d038c16ba8bc6ff0e2b Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 9 Nov 2022 15:02:19 -0500 Subject: [PATCH 12/22] Squashed 'spiffworkflow-backend/' changes from b8924100d..64ac70428 64ac70428 Move to Python 3.11 (#27) 167851df1 remove leading slash from model identifiers 3503fe400 one instance test left for cypress w/ burnettk 7e09363c4 merged in main and resolved conflicts w/ burnettk cullerton afbc3157d fixed some acceptance tests w/ burnettk cullerton 96e7b63c6 made a process model form w/ burnettk c19aac25f Somethings up w/authentication. Maybe the stored token is bad d17007eaa Merge branch 'main' into feature/nested-groups 729b13881 script to modify all model json files to use full path for model id. Also removes process_group_id 7856b8caa return next task when running an instance w/ burnettk d183b961c First pass at custom report/perspective for Process Instance List (#23) f303c0f77 remove process_group_identifier from process-instances endpoint 207de9ab8 Merge branch 'main' into feature/nested-groups fec8d06b8 started test for test_script_unit_test_run ea31c7a12 fixed process_model_identifier in script_unit_test_create 5d058cbea Fix url in test f8afd7bdd Remove the `_2` methods 7c13ec802 Fix urls for uspend and resume endpoints 19158d7b0 Get testing bpmn files out of the config directory 5f1ee7f16 fixed failing test de3b4b81d Merge branch 'main' into feature/nested-groups 440871d57 First stab at nested folders. Added temp endpoints Changes to tests and test helpers 1bd6a199f Don't need parent. Just use the whole path for the group id a2ab420b2 Committing so Jon can view code git-subtree-dir: spiffworkflow-backend git-subtree-split: 64ac7042887af80869963bc103c01f56404727f2 --- .tool-versions | 2 +- Dockerfile | 2 +- bin/update_all_json.py | 22 + conftest.py | 34 +- .../{b1647eff45c9_.py => 50dd2e016d94_.py} | 18 +- poetry.lock | 133 +-- pyproject.toml | 4 +- src/spiffworkflow_backend/api.yml | 298 +++---- .../config/permissions/testing.yml | 12 + src/spiffworkflow_backend/config/testing.py | 3 + .../models/process_instance.py | 6 +- .../models/process_instance_report.py | 80 +- .../models/process_model.py | 5 +- .../routes/process_api_blueprint.py | 180 +++-- src/spiffworkflow_backend/routes/user.py | 2 + .../services/acceptance_test_fixtures.py | 4 +- .../services/data_setup_service.py | 4 +- .../services/error_handling_service.py | 2 +- .../services/file_system_service.py | 14 +- .../services/message_service.py | 1 - .../services/process_instance_processor.py | 21 +- .../services/process_instance_service.py | 20 +- .../services/process_model_service.py | 144 ++-- .../services/spec_file_service.py | 38 +- tests/data/manual_task/manual_task.bpmn | 41 + tests/data/simple_script/simple_script.bpmn | 110 +-- .../helpers/base_test.py | 154 ++-- .../helpers/example_data.py | 46 +- .../helpers/test_data.py | 43 +- .../integration/test_logging_service.py | 31 +- .../integration/test_nested_groups.py | 174 ++++ .../integration/test_process_api.py | 754 +++++++++++++----- .../integration/test_secret_service.py | 8 +- .../scripts/test_get_group_members.py | 10 +- .../scripts/test_get_localtime.py | 12 +- .../unit/test_authorization_service.py | 23 +- .../unit/test_dot_notation.py | 38 +- .../unit/test_message_instance.py | 79 +- .../unit/test_message_service.py | 56 +- .../unit/test_permissions.py | 32 +- .../unit/test_process_instance_processor.py | 19 +- .../unit/test_process_instance_report.py | 2 - .../unit/test_process_model.py | 41 +- .../unit/test_process_model_service.py | 19 +- .../unit/test_restricted_script_engine.py | 24 +- .../unit/test_script_unit_test_runner.py | 66 +- .../unit/test_spec_file_service.py | 89 ++- .../unit/test_various_bpmn_constructs.py | 22 +- 48 files changed, 1941 insertions(+), 1001 deletions(-) create mode 100644 bin/update_all_json.py rename migrations/versions/{b1647eff45c9_.py => 50dd2e016d94_.py} (94%) create mode 100644 tests/data/manual_task/manual_task.bpmn create mode 100644 tests/spiffworkflow_backend/integration/test_nested_groups.py diff --git a/.tool-versions b/.tool-versions index 7e78d9af..a7b6ef2e 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -python 3.10.4 +python 3.11.0 diff --git a/Dockerfile b/Dockerfile index 6c3fabcf..6c533206 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/sartography/python:3.10 +FROM ghcr.io/sartography/python:3.11 RUN pip install poetry RUN useradd _gunicorn --no-create-home --user-group diff --git a/bin/update_all_json.py b/bin/update_all_json.py new file mode 100644 index 00000000..4e6b1b8a --- /dev/null +++ b/bin/update_all_json.py @@ -0,0 +1,22 @@ +"""Updates all JSON files, based on the current state of BPMN_SPEC_ABSOLUTE_DIR.""" +from spiffworkflow_backend import get_hacked_up_app_for_script +from spiffworkflow_backend.services.process_model_service import ProcessModelService + + +def main() -> None: + """Main.""" + app = get_hacked_up_app_for_script() + with app.app_context(): + + groups = ProcessModelService().get_process_groups() + for group in groups: + for process_model in group.process_models: + update_items = { + "process_group_id": "", + "id": f"{group.id}/{process_model.id}", + } + ProcessModelService().update_spec(process_model, update_items) + + +if __name__ == "__main__": + main() diff --git a/conftest.py b/conftest.py index 87da7c8d..d73693c2 100644 --- a/conftest.py +++ b/conftest.py @@ -4,10 +4,10 @@ import shutil import pytest from flask.app import Flask +from flask.testing import FlaskClient from flask_bpmn.models.db import db from flask_bpmn.models.db import SpiffworkflowBaseDBModel from tests.spiffworkflow_backend.helpers.base_test import BaseTest -from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from spiffworkflow_backend.models.active_task_user import ActiveTaskUserModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModel @@ -20,6 +20,8 @@ from spiffworkflow_backend.services.process_instance_service import ( ) from spiffworkflow_backend.services.process_model_service import ProcessModelService +# from tests.spiffworkflow_backend.helpers.test_data import load_test_spec + # We need to call this before importing spiffworkflow_backend # otherwise typeguard cannot work. hence the noqa: E402 @@ -66,17 +68,37 @@ def with_super_admin_user() -> UserModel: @pytest.fixture() -def setup_process_instances_for_reports() -> list[ProcessInstanceModel]: +def setup_process_instances_for_reports( + client: FlaskClient, with_super_admin_user: UserModel +) -> list[ProcessInstanceModel]: """Setup_process_instances_for_reports.""" - user = BaseTest.find_or_create_user() + user = with_super_admin_user process_group_id = "runs_without_input" process_model_id = "sample" - load_test_spec(process_group_id=process_group_id, process_model_id=process_model_id) + # bpmn_file_name = "sample.bpmn" + bpmn_file_location = "sample" + process_model_identifier = BaseTest().basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + # bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, + ) + + # BaseTest().create_process_group( + # client=client, user=user, process_group_id=process_group_id, display_name=process_group_id + # ) + # process_model_id = "runs_without_input/sample" + # load_test_spec( + # process_model_id=f"{process_group_id}/{process_model_id}", + # process_model_source_directory="sample" + # ) process_instances = [] for data in [kay(), ray(), jay()]: process_instance = ProcessInstanceService.create_process_instance( - process_group_identifier=process_group_id, - process_model_identifier=process_model_id, + # process_group_identifier=process_group_id, + process_model_identifier=process_model_identifier, user=user, ) processor = ProcessInstanceProcessor(process_instance) diff --git a/migrations/versions/b1647eff45c9_.py b/migrations/versions/50dd2e016d94_.py similarity index 94% rename from migrations/versions/b1647eff45c9_.py rename to migrations/versions/50dd2e016d94_.py index d6ff25e3..a702c5a4 100644 --- a/migrations/versions/b1647eff45c9_.py +++ b/migrations/versions/50dd2e016d94_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: b1647eff45c9 +Revision ID: 50dd2e016d94 Revises: -Create Date: 2022-11-02 14:25:09.992800 +Create Date: 2022-11-08 16:28:18.991635 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'b1647eff45c9' +revision = '50dd2e016d94' down_revision = None branch_labels = None depends_on = None @@ -95,7 +95,7 @@ def upgrade(): ) op.create_table('process_instance', sa.Column('id', sa.Integer(), nullable=False), - sa.Column('process_model_identifier', sa.String(length=50), nullable=False), + sa.Column('process_model_identifier', sa.String(length=255), nullable=False), sa.Column('process_group_identifier', sa.String(length=50), nullable=False), sa.Column('process_initiator_id', sa.Integer(), nullable=False), sa.Column('bpmn_json', sa.JSON(), nullable=True), @@ -115,19 +115,16 @@ def upgrade(): op.create_table('process_instance_report', sa.Column('id', sa.Integer(), nullable=False), sa.Column('identifier', sa.String(length=50), nullable=False), - sa.Column('process_model_identifier', sa.String(length=50), nullable=False), - sa.Column('process_group_identifier', sa.String(length=50), nullable=False), sa.Column('report_metadata', sa.JSON(), nullable=True), sa.Column('created_by_id', sa.Integer(), nullable=False), sa.Column('created_at_in_seconds', sa.Integer(), nullable=True), sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ), sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('process_group_identifier', 'process_model_identifier', 'identifier', name='process_instance_report_unique') + sa.UniqueConstraint('created_by_id', 'identifier', name='process_instance_report_unique') ) + op.create_index(op.f('ix_process_instance_report_created_by_id'), 'process_instance_report', ['created_by_id'], unique=False) op.create_index(op.f('ix_process_instance_report_identifier'), 'process_instance_report', ['identifier'], unique=False) - op.create_index(op.f('ix_process_instance_report_process_group_identifier'), 'process_instance_report', ['process_group_identifier'], unique=False) - op.create_index(op.f('ix_process_instance_report_process_model_identifier'), 'process_instance_report', ['process_model_identifier'], unique=False) op.create_table('refresh_token', sa.Column('id', sa.Integer(), nullable=False), sa.Column('user_id', sa.Integer(), nullable=False), @@ -292,9 +289,8 @@ def downgrade(): op.drop_table('user_group_assignment') op.drop_table('secret') op.drop_table('refresh_token') - op.drop_index(op.f('ix_process_instance_report_process_model_identifier'), table_name='process_instance_report') - op.drop_index(op.f('ix_process_instance_report_process_group_identifier'), table_name='process_instance_report') op.drop_index(op.f('ix_process_instance_report_identifier'), table_name='process_instance_report') + op.drop_index(op.f('ix_process_instance_report_created_by_id'), table_name='process_instance_report') op.drop_table('process_instance_report') op.drop_index(op.f('ix_process_instance_process_model_identifier'), table_name='process_instance') op.drop_index(op.f('ix_process_instance_process_group_identifier'), table_name='process_instance') diff --git a/poetry.lock b/poetry.lock index 8c918630..cf161845 100644 --- a/poetry.lock +++ b/poetry.lock @@ -80,8 +80,7 @@ python-versions = ">=3.7.2" [package.dependencies] lazy-object-proxy = ">=1.4.0" -typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} -wrapt = {version = ">=1.11,<2", markers = "python_version < \"3.11\""} +wrapt = {version = ">=1.14,<2", markers = "python_version >= \"3.11\""} [[package]] name = "attrs" @@ -95,7 +94,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" @@ -175,8 +174,6 @@ click = ">=8.0.0" mypy-extensions = ">=0.4.3" pathspec = ">=0.9.0" platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] colorama = ["colorama (>=0.4.3)"] @@ -268,7 +265,7 @@ optional = false python-versions = ">=3.6.0" [package.extras] -unicode_backport = ["unicodedata2"] +unicode-backport = ["unicodedata2"] [[package]] name = "classify-imports" @@ -394,9 +391,6 @@ category = "dev" optional = false python-versions = ">=3.7" -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli"] @@ -574,7 +568,6 @@ python-versions = ">=3.7" [package.dependencies] click = ">=8.0" -importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} itsdangerous = ">=2.0" Jinja2 = ">=3.0" Werkzeug = ">=2.2.2" @@ -639,7 +632,7 @@ werkzeug = "*" type = "git" url = "https://github.com/sartography/flask-bpmn" reference = "main" -resolved_reference = "191f0f32798720c9ce1e5307732c90ac26433298" +resolved_reference = "17434e0907cc35914d013614bb79288eed1bd437" [[package]] name = "Flask-Cors" @@ -818,22 +811,6 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -[[package]] -name = "importlib-metadata" -version = "4.13.0" -description = "Read metadata from Python packages" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] - [[package]] name = "inflection" version = "0.5.1" @@ -1067,7 +1044,6 @@ python-versions = ">=3.7" [package.dependencies] mypy-extensions = ">=0.4.3" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=3.10" [package.extras] @@ -1206,7 +1182,6 @@ python-versions = ">=3.7" [package.dependencies] "ruamel.yaml" = ">=0.15" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [[package]] name = "prompt-toolkit" @@ -1512,7 +1487,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" @@ -1526,12 +1501,12 @@ python-versions = "*" requests = ">=2.0.1,<3.0.0" [[package]] -name = "RestrictedPython" -version = "5.2" +name = "restrictedpython" +version = "6.0" description = "RestrictedPython is a defined subset of the Python language which allows to provide a program input into a trusted environment." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <3.11" +python-versions = ">=3.6, <3.12" [package.extras] docs = ["Sphinx", "sphinx-rtd-theme"] @@ -1567,21 +1542,10 @@ category = "dev" optional = false python-versions = ">=3" -[package.dependencies] -"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} - [package.extras] docs = ["ryd"] jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] -[[package]] -name = "ruamel.yaml.clib" -version = "0.2.6" -description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -category = "dev" -optional = false -python-versions = ">=3.5" - [[package]] name = "safety" version = "2.3.1" @@ -1625,7 +1589,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)"] @@ -1701,7 +1665,6 @@ babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.14,<0.20" imagesize = ">=1.3" -importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.12" @@ -1889,19 +1852,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"] @@ -2239,22 +2202,10 @@ runtime-strict = ["six (==1.11.0)"] tests = ["cmake", "codecov", "ninja", "pybind11", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest-cov", "pytest-cov", "pytest-cov", "pytest-cov", "scikit-build", "typing"] tests-strict = ["cmake (==3.21.2)", "codecov (==2.0.15)", "ninja (==1.10.2)", "pybind11 (==2.7.1)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "scikit-build (==0.11.1)", "typing (==3.7.4)"] -[[package]] -name = "zipp" -version = "3.9.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - [metadata] lock-version = "1.1" -python-versions = ">=3.9,<3.11" -content-hash = "995be3a9a60b515b281f017ff32ff27a52ca178b1980611b348dccac6afb6b89" +python-versions = ">=3.11,<3.12" +content-hash = "1ba9277969015f0ef348dccb79e9977e20665720958f7ba22360398fba9da092" [metadata.files] alabaster = [ @@ -2636,10 +2587,6 @@ imagesize = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] -importlib-metadata = [ - {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"}, {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, @@ -3287,9 +3234,9 @@ requests-toolbelt = [ {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, ] -RestrictedPython = [ - {file = "RestrictedPython-5.2-py2.py3-none-any.whl", hash = "sha256:fdf8621034c5dcb990a2a198f232f66b2d48866dd16d848e00ac7d187ae452ba"}, - {file = "RestrictedPython-5.2.tar.gz", hash = "sha256:634da1f6c5c122a262f433b083ee3d17a9a039f8f1b3778597efb47461cd361b"}, +restrictedpython = [ + {file = "RestrictedPython-6.0-py3-none-any.whl", hash = "sha256:3479303f7bff48a7dedad76f96e7704993c5e86c5adbd67f607295d5352f0fb8"}, + {file = "RestrictedPython-6.0.tar.gz", hash = "sha256:405cf0bd9eec2f19b1326b5f48228efe56d6590b4e91826b8cc3b2cd400a96ad"}, ] restructuredtext-lint = [ {file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"}, @@ -3302,38 +3249,6 @@ rsa = [ {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, ] -"ruamel.yaml.clib" = [ - {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0"}, - {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:066f886bc90cc2ce44df8b5f7acfc6a7e2b2e672713f027136464492b0c34d7c"}, - {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7"}, - {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win32.whl", hash = "sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee"}, - {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de"}, - {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751"}, - {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527"}, - {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win32.whl", hash = "sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5"}, - {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win_amd64.whl", hash = "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c"}, - {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502"}, - {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78"}, - {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d3c620a54748a3d4cf0bcfe623e388407c8e85a4b06b8188e126302bcab93ea8"}, - {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win32.whl", hash = "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94"}, - {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win_amd64.whl", hash = "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468"}, - {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd"}, - {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99"}, - {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:210c8fcfeff90514b7133010bf14e3bad652c8efde6b20e00c43854bf94fa5a6"}, - {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win32.whl", hash = "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb"}, - {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win_amd64.whl", hash = "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe"}, - {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233"}, - {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84"}, - {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:61bc5e5ca632d95925907c569daa559ea194a4d16084ba86084be98ab1cec1c6"}, - {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win32.whl", hash = "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b"}, - {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win_amd64.whl", hash = "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277"}, - {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed"}, - {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0"}, - {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:1b4139a6ffbca8ef60fdaf9b33dec05143ba746a6f0ae0f9d11d38239211d335"}, - {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win32.whl", hash = "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104"}, - {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win_amd64.whl", hash = "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7"}, - {file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"}, -] safety = [ {file = "safety-2.3.1-py3-none-any.whl", hash = "sha256:8f098d12b607db2756886280e85c28ece8db1bba4f45fc5f981f4663217bd619"}, {file = "safety-2.3.1.tar.gz", hash = "sha256:6e6fcb7d4e8321098cf289f59b65051cafd3467f089c6e57c9f894ae32c23b71"}, @@ -3705,7 +3620,3 @@ xdoctest = [ {file = "xdoctest-1.1.0-py3-none-any.whl", hash = "sha256:da330c4dacee51f3c785820bc743188fb6f7c64c5fa1c54bff8836b3cf23d69b"}, {file = "xdoctest-1.1.0.tar.gz", hash = "sha256:0fd4fad7932f0a2f082dfdfb857dd6ca41603757586c39b1e5b4d333fc389f8a"}, ] -zipp = [ - {file = "zipp-3.9.0-py3-none-any.whl", hash = "sha256:972cfa31bc2fedd3fa838a51e9bc7e64b7fb725a8c00e7431554311f180e9980"}, - {file = "zipp-3.9.0.tar.gz", hash = "sha256:3a7af91c3db40ec72dd9d154ae18e008c69efe8ca88dde4f9a731bb82fe2f9eb"}, -] diff --git a/pyproject.toml b/pyproject.toml index 7f2d09a4..abb6d175 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ classifiers = [ Changelog = "https://github.com/sartography/spiffworkflow-backend/releases" [tool.poetry.dependencies] -python = ">=3.9,<3.11" +python = ">=3.11,<3.12" click = "^8.0.1" flask = "2.2.2" flask-admin = "*" @@ -47,7 +47,7 @@ gunicorn = "^20.1.0" python-keycloak = "^2.5.0" APScheduler = "^3.9.1" Jinja2 = "^3.1.2" -RestrictedPython = "^5.2" +RestrictedPython = "^6.0" Flask-SQLAlchemy = "^3" orjson = "^3.8.0" diff --git a/src/spiffworkflow_backend/api.yml b/src/spiffworkflow_backend/api.yml index 1ef9cf67..7f2fbc91 100755 --- a/src/spiffworkflow_backend/api.yml +++ b/src/spiffworkflow_backend/api.yml @@ -286,21 +286,14 @@ paths: schema: $ref: "#/components/schemas/ProcessModel" - /process-models/{process_group_id}/{process_model_id}/files: + /process-models/{modified_process_model_id}/files: parameters: - - name: process_group_id + - name: modified_process_model_id in: path required: true - description: The group containing the models we want to return + description: The process_model_id, modified to replace slashes (/) schema: type: string - - name: process_model_id - in: path - required: true - description: The unique id of an existing process model to validate. - schema: - type: string - # add_file post: operationId: spiffworkflow_backend.routes.process_api_blueprint.add_file summary: Add a new workflow spec file @@ -322,36 +315,15 @@ paths: application/json: schema: $ref: "#/components/schemas/File" - # get: - # operationId: spiffworkflow_backend.api.process_api_blueprint.get_files - # summary: Provide a list of workflow spec files for the given workflow_spec_id. IMPORTANT, only includes metadata, not the file content. - # tags: - # - Process Model Files - # responses: - # '200': - # description: An array of file descriptions (not the file content) - # content: - # application/json: - # schema: - # type: array - # items: - # $ref: "#/components/schemas/File" - /process-models/{process_group_id}/{process_model_id}: + /process-models/{modified_process_model_identifier}: parameters: - - name: process_group_id + - name: modified_process_model_identifier in: path required: true - description: The unique id of an existing process group + description: the modified process model id schema: type: string - - name: process_model_id - in: path - required: true - description: The unique id of an existing process model. - schema: - type: string - # process_model_show get: operationId: spiffworkflow_backend.routes.process_api_blueprint.process_model_show summary: Returns a single process model @@ -364,22 +336,9 @@ paths: application/json: schema: $ref: "#/components/schemas/ProcessModel" - # process_model_delete - delete: - operationId: spiffworkflow_backend.routes.process_api_blueprint.process_model_delete - summary: Removes an existing process model - tags: - - Process Models - responses: - "200": - description: The process model has been removed. - content: - application/json: - schema: - $ref: "#/components/schemas/OkTrue" put: operationId: spiffworkflow_backend.routes.process_api_blueprint.process_model_update - summary: Modifies an existing process mosel with the given parameters. + summary: Modifies an existing process model with the given parameters. tags: - Process Models requestBody: @@ -394,15 +353,21 @@ paths: application/json: schema: $ref: "#/components/schemas/ProcessModel" + delete: + operationId: spiffworkflow_backend.routes.process_api_blueprint.process_model_delete + summary: Removes an existing process model + tags: + - Process Models + responses: + "200": + description: The process model has been removed. + content: + application/json: + schema: + $ref: "#/components/schemas/OkTrue" /process-instances: parameters: - - name: process_group_identifier - in: query - required: false - description: The unique id of an existing process group - schema: - type: string - name: process_model_identifier in: query required: false @@ -548,15 +513,9 @@ paths: schema: $ref: "#/components/schemas/Workflow" - /process-models/{process_group_id}/{process_model_id}/process-instances: + /process-models/{modified_process_model_id}/process-instances: parameters: - - name: process_group_id - in: path - required: true - description: The unique id of an existing process group - schema: - type: string - - name: process_model_id + - name: modified_process_model_id in: path required: true description: The unique id of an existing process model. @@ -576,18 +535,33 @@ paths: schema: $ref: "#/components/schemas/Workflow" - /process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}: + /process-instances/{process_instance_id}: parameters: - - name: process_group_id + - name: process_instance_id in: path required: true - description: The unique id of an existing process group + description: The unique id of an existing process instance. schema: - type: string - - name: process_model_id + type: integer + delete: + operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_delete + summary: Deletes a single process instance + tags: + - Process Instances + responses: + "200": + description: The process instance was deleted. + content: + application/json: + schema: + $ref: "#/components/schemas/OkTrue" + + /process-models/{modified_process_model_identifier}/process-instances/{process_instance_id}: + parameters: + - name: modified_process_model_identifier in: path required: true - description: The unique id of an existing process model. + description: The unique id of an existing process model schema: type: string - name: process_instance_id @@ -608,34 +582,9 @@ paths: application/json: schema: $ref: "#/components/schemas/Workflow" - # process_instance_delete - delete: - operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_delete - summary: Deletes a single process instance - tags: - - Process Instances - responses: - "200": - description: The process instance was deleted. - content: - application/json: - schema: - $ref: "#/components/schemas/OkTrue" - /process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}/run: + /process-instances/{process_instance_id}/run: parameters: - - name: process_group_id - in: path - required: true - description: The unique id of an existing process group - schema: - type: string - - name: process_model_id - in: path - required: true - description: The unique id of an existing process model. - schema: - type: string - name: process_instance_id in: path required: true @@ -662,20 +611,8 @@ paths: schema: $ref: "#/components/schemas/Workflow" - /process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}/terminate: + /process-instances/{process_instance_id}/terminate: parameters: - - name: process_group_id - in: path - required: true - description: The unique id of an existing process group - schema: - type: string - - name: process_model_id - in: path - required: true - description: The unique id of an existing process model. - schema: - type: string - name: process_instance_id in: path required: true @@ -695,20 +632,8 @@ paths: schema: $ref: "#/components/schemas/OkTrue" - /process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}/suspend: + /process-instances/{process_instance_id}/suspend: parameters: - - name: process_group_id - in: path - required: true - description: The unique id of an existing process group - schema: - type: string - - name: process_model_id - in: path - required: true - description: The unique id of an existing process model. - schema: - type: string - name: process_instance_id in: path required: true @@ -728,20 +653,8 @@ paths: schema: $ref: "#/components/schemas/OkTrue" - /process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}/resume: + /process-instances/{process_instance_id}/resume: parameters: - - name: process_group_id - in: path - required: true - description: The unique id of an existing process group - schema: - type: string - - name: process_model_id - in: path - required: true - description: The unique id of an existing process model. - schema: - type: string - name: process_instance_id in: path required: true @@ -761,6 +674,35 @@ paths: schema: $ref: "#/components/schemas/OkTrue" + /process-instances/reports: + parameters: + - name: page + in: query + required: false + description: The page number to return. Defaults to page 1. + schema: + type: integer + - name: per_page + in: query + required: false + description: The page number to return. Defaults to page 1. + schema: + type: integer + get: + operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_report_list + summary: Returns all process instance reports for process model + tags: + - Process Instances + responses: + "200": + description: Workflow. + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Workflow" + /process-models/{process_group_id}/{process_model_id}/process-instances/reports: parameters: - name: process_group_id @@ -787,20 +729,6 @@ paths: description: The page number to return. Defaults to page 1. schema: type: integer - get: - operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_report_list - summary: Returns all process instance reports for process model - tags: - - Process Instances - responses: - "200": - description: Workflow. - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Workflow" post: operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_report_create summary: Returns all process instance reports for process model @@ -814,6 +742,41 @@ paths: schema: $ref: "#/components/schemas/OkTrue" + /process-instances/reports/{report_identifier}: + parameters: + - name: report_identifier + in: path + required: true + description: The unique id of an existing report + schema: + type: string + - name: page + in: query + required: false + description: The page number to return. Defaults to page 1. + schema: + type: integer + - name: per_page + in: query + required: false + description: The page number to return. Defaults to page 1. + schema: + type: integer + get: + operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_report_show + summary: Returns a report of process instances for a given process model + tags: + - Process Instances + responses: + "200": + description: Workflow. + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Workflow" + /process-models/{process_group_id}/{process_model_id}/process-instances/reports/{report_identifier}: parameters: - name: process_group_id @@ -846,20 +809,6 @@ paths: description: The page number to return. Defaults to page 1. schema: type: integer - get: - operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_report_show - summary: Returns a report of process instances for a given process model - tags: - - Process Instances - responses: - "200": - description: Workflow. - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Workflow" put: operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_report_update summary: Updates a process instance report @@ -885,18 +834,12 @@ paths: schema: $ref: "#/components/schemas/OkTrue" - /process-models/{process_group_id}/{process_model_id}/files/{file_name}: + /process-models/{modified_process_model_id}/files/{file_name}: parameters: - - name: process_group_id + - name: modified_process_model_id in: path required: true - description: The unique id of an existing process group - schema: - type: string - - name: process_model_id - in: path - required: true - description: The unique id of an existing process model to validate. + description: The modified process model id schema: type: string - name: file_name @@ -905,7 +848,6 @@ paths: description: The id of the spec file schema: type: string - # get_file get: operationId: spiffworkflow_backend.routes.process_api_blueprint.get_file summary: Returns metadata about the file @@ -1203,20 +1145,8 @@ paths: schema: $ref: "#/components/schemas/Workflow" - /process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}/logs: + /process-instances/{process_instance_id}/logs: parameters: - - name: process_group_id - in: path - required: true - description: The unique id of an existing process group - schema: - type: string - - name: process_model_id - in: path - required: true - description: The unique id of an existing process model. - schema: - type: string - name: process_instance_id in: path required: true diff --git a/src/spiffworkflow_backend/config/permissions/testing.yml b/src/spiffworkflow_backend/config/permissions/testing.yml index 333ced14..c678205d 100644 --- a/src/spiffworkflow_backend/config/permissions/testing.yml +++ b/src/spiffworkflow_backend/config/permissions/testing.yml @@ -41,3 +41,15 @@ permissions: users: [testuser4] allowed_permissions: [create, read, update, delete] uri: /v1.0/process-models/finance/* + + finance-admin-model-lanes: + groups: ["Finance Team"] + users: [testuser4] + allowed_permissions: [create, read, update, delete] + uri: /v1.0/process-models/finance:model_with_lanes/* + + finance-admin-instance-run: + groups: ["Finance Team"] + users: [testuser4] + allowed_permissions: [create, read, update, delete] + uri: /v1.0/process-instances/* diff --git a/src/spiffworkflow_backend/config/testing.py b/src/spiffworkflow_backend/config/testing.py index ce422587..bbda9db9 100644 --- a/src/spiffworkflow_backend/config/testing.py +++ b/src/spiffworkflow_backend/config/testing.py @@ -20,6 +20,9 @@ SPIFFWORKFLOW_BACKEND_LOG_LEVEL = environ.get( # different places and this allows us to know exactly where we are at the start BPMN_SPEC_ABSOLUTE_DIR = os.path.join( os.path.dirname(__file__), + "..", + "..", + "..", "tests", "spiffworkflow_backend", "files", diff --git a/src/spiffworkflow_backend/models/process_instance.py b/src/spiffworkflow_backend/models/process_instance.py index 50c3c9f7..8d4cb538 100644 --- a/src/spiffworkflow_backend/models/process_instance.py +++ b/src/spiffworkflow_backend/models/process_instance.py @@ -72,7 +72,9 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): __tablename__ = "process_instance" id: int = db.Column(db.Integer, primary_key=True) - process_model_identifier: str = db.Column(db.String(50), nullable=False, index=True) + process_model_identifier: str = db.Column( + db.String(255), nullable=False, index=True + ) process_group_identifier: str = db.Column(db.String(50), nullable=False, index=True) process_initiator_id: int = db.Column(ForeignKey(UserModel.id), nullable=False) process_initiator = relationship("UserModel") @@ -265,7 +267,7 @@ class ProcessInstanceMetadata: id=process_instance.id, display_name=process_model.display_name, description=process_model.description, - process_group_id=process_model.process_group_id, + process_group_id=process_model.process_group, state_message=process_instance.state_message, status=process_instance.status, completed_tasks=process_instance.completed_tasks, diff --git a/src/spiffworkflow_backend/models/process_instance_report.py b/src/spiffworkflow_backend/models/process_instance_report.py index 8f8886bf..b6f16288 100644 --- a/src/spiffworkflow_backend/models/process_instance_report.py +++ b/src/spiffworkflow_backend/models/process_instance_report.py @@ -21,7 +21,6 @@ from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, ) -from spiffworkflow_backend.services.process_model_service import ProcessModelService ReportMetadata = dict[str, Any] @@ -58,8 +57,7 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel): __tablename__ = "process_instance_report" __table_args__ = ( db.UniqueConstraint( - "process_group_identifier", - "process_model_identifier", + "created_by_id", "identifier", name="process_instance_report_unique", ), @@ -67,21 +65,53 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel): id = db.Column(db.Integer, primary_key=True) identifier: str = db.Column(db.String(50), nullable=False, index=True) - process_model_identifier: str = db.Column(db.String(50), nullable=False, index=True) - process_group_identifier = db.Column(db.String(50), nullable=False, index=True) report_metadata: dict = deferred(db.Column(db.JSON)) # type: ignore - created_by_id = db.Column(ForeignKey(UserModel.id), nullable=False) + created_by_id = db.Column(ForeignKey(UserModel.id), nullable=False, index=True) created_by = relationship("UserModel") created_at_in_seconds = db.Column(db.Integer) updated_at_in_seconds = db.Column(db.Integer) + @classmethod + def default_report(cls, user: UserModel) -> ProcessInstanceReportModel: + """Default_report.""" + identifier = "default" + process_instance_report = ProcessInstanceReportModel.query.filter_by( + identifier=identifier, created_by_id=user.id + ).first() + + if process_instance_report is None: + report_metadata = { + "columns": [ + {"Header": "id", "accessor": "id"}, + { + "Header": "process_group_identifier", + "accessor": "process_group_identifier", + }, + { + "Header": "process_model_identifier", + "accessor": "process_model_identifier", + }, + {"Header": "start_in_seconds", "accessor": "start_in_seconds"}, + {"Header": "end_in_seconds", "accessor": "end_in_seconds"}, + {"Header": "status", "accessor": "status"}, + ], + } + + process_instance_report = cls( + identifier=identifier, + created_by_id=user.id, + report_metadata=report_metadata, + ) + + return process_instance_report # type: ignore + @classmethod def add_fixtures(cls) -> None: """Add_fixtures.""" try: - process_model = ProcessModelService().get_process_model( - group_id="sartography-admin", process_model_id="ticket" - ) + # process_model = ProcessModelService().get_process_model( + # process_model_id="sartography-admin/ticket" + # ) user = UserModel.query.first() columns = [ {"Header": "id", "accessor": "id"}, @@ -96,29 +126,21 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel): cls.create_report( identifier="standard", - process_group_identifier=process_model.process_group_id, - process_model_identifier=process_model.id, user=user, report_metadata=json, ) cls.create_report( identifier="for-month", - process_group_identifier="sartography-admin", - process_model_identifier="ticket", user=user, report_metadata=cls.ticket_for_month_report(), ) cls.create_report( identifier="for-month-3", - process_group_identifier="sartography-admin", - process_model_identifier="ticket", user=user, report_metadata=cls.ticket_for_month_3_report(), ) cls.create_report( identifier="hot-report", - process_group_identifier="category_number_one", - process_model_identifier="process-model-with-form", user=user, report_metadata=cls.process_model_with_form_report_fixture(), ) @@ -130,23 +152,18 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel): def create_report( cls, identifier: str, - process_group_identifier: str, - process_model_identifier: str, user: UserModel, report_metadata: ReportMetadata, ) -> None: """Make_fixture_report.""" process_instance_report = ProcessInstanceReportModel.query.filter_by( identifier=identifier, - process_group_identifier=process_group_identifier, - process_model_identifier=process_model_identifier, + created_by_id=user.id, ).first() if process_instance_report is None: process_instance_report = cls( identifier=identifier, - process_group_identifier=process_group_identifier, - process_model_identifier=process_model_identifier, created_by_id=user.id, report_metadata=report_metadata, ) @@ -217,19 +234,22 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel): def create_with_attributes( cls, identifier: str, - process_group_identifier: str, - process_model_identifier: str, report_metadata: dict, user: UserModel, ) -> ProcessInstanceReportModel: """Create_with_attributes.""" - process_model = ProcessModelService().get_process_model( - group_id=process_group_identifier, process_model_id=process_model_identifier - ) + # <<<<<<< HEAD + # process_model = ProcessModelService().get_process_model( + # process_model_id=f"{process_model_identifier}" + # ) + # process_instance_report = cls( + # identifier=identifier, + # process_group_identifier="process_model.process_group_id", + # process_model_identifier=process_model.id, + # ======= process_instance_report = cls( identifier=identifier, - process_group_identifier=process_model.process_group_id, - process_model_identifier=process_model.id, + # >>>>>>> main created_by_id=user.id, report_metadata=report_metadata, ) diff --git a/src/spiffworkflow_backend/models/process_model.py b/src/spiffworkflow_backend/models/process_model.py index 1928f312..d63197be 100644 --- a/src/spiffworkflow_backend/models/process_model.py +++ b/src/spiffworkflow_backend/models/process_model.py @@ -29,7 +29,7 @@ class ProcessModelInfo: id: str 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 @@ -40,7 +40,7 @@ class ProcessModelInfo: def __post_init__(self) -> None: """__post_init__.""" - self.sort_index = f"{self.process_group_id}:{self.id}" + self.sort_index = self.id def __eq__(self, other: Any) -> bool: """__eq__.""" @@ -66,7 +66,6 @@ class ProcessModelInfoSchema(Schema): primary_file_name = marshmallow.fields.String(allow_none=True) primary_process_id = marshmallow.fields.String(allow_none=True) is_review = marshmallow.fields.Boolean(allow_none=True) - process_group_id = marshmallow.fields.String(allow_none=True) files = marshmallow.fields.List(marshmallow.fields.Nested("FileSchema")) fault_or_suspend_on_exception = marshmallow.fields.String() exception_notification_addresses = marshmallow.fields.List( diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index f0b9065c..2f60e994 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -137,6 +137,16 @@ def permissions_check(body: Dict[str, Dict[str, list[str]]]) -> flask.wrappers.R return make_response(jsonify({"results": response_dict}), 200) +def modify_process_model_id(process_model_id: str) -> str: + """Modify_process_model_id.""" + return process_model_id.replace("/", ":") + + +def un_modify_modified_process_model_id(modified_process_model_id: str) -> str: + """Un_modify_modified_process_model_id.""" + return modified_process_model_id.replace(":", "/") + + def process_group_add(body: dict) -> flask.wrappers.Response: """Add_process_group.""" process_model_service = ProcessModelService() @@ -216,10 +226,9 @@ def process_model_add( status_code=400, ) + process_group_id, _ = os.path.split(process_model_info.id) process_model_service = ProcessModelService() - process_group = process_model_service.get_process_group( - process_model_info.process_group_id - ) + process_group = process_model_service.get_process_group(process_group_id) if process_group is None: raise ApiError( error_code="process_model_could_not_be_created", @@ -236,32 +245,45 @@ def process_model_add( def process_model_delete( - process_group_id: str, process_model_id: str + modified_process_model_identifier: str, ) -> flask.wrappers.Response: """Process_model_delete.""" - ProcessModelService().process_model_delete(process_model_id) + process_model_identifier = modified_process_model_identifier.replace(":", "/") + # process_model_identifier = f"{process_group_id}/{process_model_id}" + ProcessModelService().process_model_delete(process_model_identifier) return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") def process_model_update( - process_group_id: str, process_model_id: str, body: Dict[str, Union[str, bool, int]] + modified_process_model_identifier: str, body: Dict[str, Union[str, bool, int]] ) -> Any: """Process_model_update.""" - body_include_list = ["display_name", "primary_file_name", "primary_process_id"] + process_model_identifier = modified_process_model_identifier.replace(":", "/") + body_include_list = [ + "display_name", + "primary_file_name", + "primary_process_id", + "description", + ] body_filtered = { include_item: body[include_item] for include_item in body_include_list if include_item in body } - process_model = get_process_model(process_model_id, process_group_id) + # process_model_identifier = f"{process_group_id}/{process_model_id}" + process_model = get_process_model(process_model_identifier) ProcessModelService().update_spec(process_model, body_filtered) return ProcessModelInfoSchema().dump(process_model) -def process_model_show(process_group_id: str, process_model_id: str) -> Any: +def process_model_show(modified_process_model_identifier: str) -> Any: """Process_model_show.""" - process_model = get_process_model(process_model_id, process_group_id) + process_model_identifier = modified_process_model_identifier.replace(":", "/") + # process_model_identifier = f"{process_group_id}/{process_model_id}" + process_model = get_process_model(process_model_identifier) + # TODO: Temporary. Should not need the next line once models have correct ids + # process_model.id = process_model_identifier files = sorted(SpecFileService.get_files(process_model)) process_model.files = files for file in process_model.files: @@ -298,15 +320,16 @@ def process_model_list( return Response(json.dumps(response_json), status=200, mimetype="application/json") -def get_file(process_group_id: str, process_model_id: str, file_name: str) -> Any: +def get_file(modified_process_model_id: str, file_name: str) -> Any: """Get_file.""" - process_model = get_process_model(process_model_id, process_group_id) + process_model_identifier = modified_process_model_id.replace(":", "/") + process_model = get_process_model(process_model_identifier) files = SpecFileService.get_files(process_model, file_name) if len(files) == 0: raise ApiError( error_code="unknown file", message=f"No information exists for file {file_name}" - f" it does not exist in workflow {process_model_id}.", + f" it does not exist in workflow {process_model_identifier}.", status_code=404, ) @@ -314,15 +337,17 @@ def get_file(process_group_id: str, process_model_id: str, file_name: str) -> An file_contents = SpecFileService.get_data(process_model, file.name) file.file_contents = file_contents file.process_model_id = process_model.id - file.process_group_id = process_model.process_group_id + # file.process_group_id = process_model.process_group_id return FileSchema().dump(file) def process_model_file_update( - process_group_id: str, process_model_id: str, file_name: str + modified_process_model_id: str, file_name: str ) -> flask.wrappers.Response: """Process_model_file_update.""" - process_model = get_process_model(process_model_id, process_group_id) + process_model_identifier = modified_process_model_id.replace(":", "/") + # process_model_identifier = f"{process_group_id}/{process_model_id}" + process_model = get_process_model(process_model_identifier) request_file = get_file_from_request() request_file_contents = request_file.stream.read() @@ -337,7 +362,7 @@ def process_model_file_update( if current_app.config["GIT_COMMIT_ON_SAVE"]: git_output = GitService.commit( - message=f"User: {g.user.username} clicked save for {process_group_id}/{process_model_id}/{file_name}" + message=f"User: {g.user.username} clicked save for {process_model_identifier}/{file_name}" ) current_app.logger.info(f"git output: {git_output}") else: @@ -347,10 +372,11 @@ def process_model_file_update( def process_model_file_delete( - process_group_id: str, process_model_id: str, file_name: str + modified_process_model_id: str, file_name: str ) -> flask.wrappers.Response: """Process_model_file_delete.""" - process_model = get_process_model(process_model_id, process_group_id) + process_model_identifier = modified_process_model_id.replace(":", "/") + process_model = get_process_model(process_model_identifier) try: SpecFileService.delete_file(process_model, file_name) except FileNotFoundError as exception: @@ -365,9 +391,10 @@ def process_model_file_delete( return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") -def add_file(process_group_id: str, process_model_id: str) -> flask.wrappers.Response: +def add_file(modified_process_model_id: str) -> flask.wrappers.Response: """Add_file.""" - process_model = get_process_model(process_model_id, process_group_id) + process_model_identifier = modified_process_model_id.replace(":", "/") + process_model = get_process_model(process_model_identifier) request_file = get_file_from_request() if not request_file.filename: raise ApiError( @@ -382,18 +409,18 @@ def add_file(process_group_id: str, process_model_id: str) -> flask.wrappers.Res file_contents = SpecFileService.get_data(process_model, file.name) file.file_contents = file_contents file.process_model_id = process_model.id - file.process_group_id = process_model.process_group_id return Response( json.dumps(FileSchema().dump(file)), status=201, mimetype="application/json" ) -def process_instance_create( - process_group_id: str, process_model_id: str -) -> flask.wrappers.Response: +def process_instance_create(modified_process_model_id: str) -> flask.wrappers.Response: """Create_process_instance.""" + process_model_identifier = un_modify_modified_process_model_id( + modified_process_model_id + ) process_instance = ProcessInstanceService.create_process_instance( - process_model_id, g.user, process_group_identifier=process_group_id + process_model_identifier, g.user ) return Response( json.dumps(ProcessInstanceModelSchema().dump(process_instance)), @@ -403,8 +430,6 @@ def process_instance_create( def process_instance_run( - process_group_id: str, - process_model_id: str, process_instance_id: int, do_engine_steps: bool = True, ) -> flask.wrappers.Response: @@ -446,10 +471,7 @@ def process_instance_run( def process_instance_terminate( - process_group_id: str, - process_model_id: str, process_instance_id: int, - do_engine_steps: bool = True, ) -> flask.wrappers.Response: """Process_instance_run.""" process_instance = ProcessInstanceService().get_process_instance( @@ -461,8 +483,6 @@ def process_instance_terminate( def process_instance_suspend( - process_group_id: str, - process_model_id: str, process_instance_id: int, ) -> flask.wrappers.Response: """Process_instance_suspend.""" @@ -475,8 +495,6 @@ def process_instance_suspend( def process_instance_resume( - process_group_id: str, - process_model_id: str, process_instance_id: int, ) -> flask.wrappers.Response: """Process_instance_resume.""" @@ -489,8 +507,6 @@ def process_instance_resume( def process_instance_log_list( - process_group_id: str, - process_model_id: str, process_instance_id: int, page: int = 1, per_page: int = 100, @@ -651,7 +667,6 @@ def message_start( def process_instance_list( - process_group_identifier: Optional[str] = None, process_model_identifier: Optional[str] = None, page: int = 1, per_page: int = 100, @@ -662,10 +677,11 @@ def process_instance_list( process_status: Optional[str] = None, ) -> flask.wrappers.Response: """Process_instance_list.""" + # process_model_identifier = un_modify_modified_process_model_id(modified_process_model_identifier) process_instance_query = ProcessInstanceModel.query - if process_model_identifier is not None and process_group_identifier is not None: + if process_model_identifier is not None: process_model = get_process_model( - process_model_identifier, process_group_identifier + f"{process_model_identifier}", ) process_instance_query = process_instance_query.filter_by( @@ -711,10 +727,29 @@ def process_instance_list( ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore ).paginate(page=page, per_page=per_page, error_out=False) + process_instance_report = ProcessInstanceReportModel.default_report(g.user) + + # TODO need to look into this more - how the filter here interacts with the + # one defined in the report. + # TODO need to look into test failures when the results from result_dict is + # used instead of the process instances + + # substitution_variables = request.args.to_dict() + # result_dict = process_instance_report.generate_report( + # process_instances.items, substitution_variables + # ) + + # results = result_dict["results"] + # report_metadata = result_dict["report_metadata"] + + results = process_instances.items + report_metadata = process_instance_report.report_metadata + response_json = { - "results": process_instances.items, + "report_metadata": report_metadata, + "results": results, "pagination": { - "count": len(process_instances.items), + "count": len(results), "total": process_instances.total, "pages": process_instances.pages, }, @@ -724,12 +759,13 @@ def process_instance_list( def process_instance_show( - process_group_id: str, process_model_id: str, process_instance_id: int + modified_process_model_identifier: str, process_instance_id: int ) -> flask.wrappers.Response: """Create_process_instance.""" + process_model_identifier = modified_process_model_identifier.replace(":", "/") process_instance = find_process_instance_by_id_or_raise(process_instance_id) current_version_control_revision = GitService.get_current_revision() - process_model = get_process_model(process_model_id, process_group_id) + process_model = get_process_model(process_model_identifier) if process_model.primary_file_name: if ( @@ -748,9 +784,7 @@ def process_instance_show( return make_response(jsonify(process_instance), 200) -def process_instance_delete( - process_group_id: str, process_model_id: str, process_instance_id: int -) -> flask.wrappers.Response: +def process_instance_delete(process_instance_id: int) -> flask.wrappers.Response: """Create_process_instance.""" process_instance = find_process_instance_by_id_or_raise(process_instance_id) @@ -762,27 +796,20 @@ def process_instance_delete( def process_instance_report_list( - process_group_id: str, process_model_id: str, page: int = 1, per_page: int = 100 + page: int = 1, per_page: int = 100 ) -> flask.wrappers.Response: """Process_instance_report_list.""" - process_model = get_process_model(process_model_id, process_group_id) - process_instance_reports = ProcessInstanceReportModel.query.filter_by( - process_group_identifier=process_group_id, - process_model_identifier=process_model.id, + created_by_id=g.user.id, ).all() return make_response(jsonify(process_instance_reports), 200) -def process_instance_report_create( - process_group_id: str, process_model_id: str, body: Dict[str, Any] -) -> flask.wrappers.Response: +def process_instance_report_create(body: Dict[str, Any]) -> flask.wrappers.Response: """Process_instance_report_create.""" ProcessInstanceReportModel.create_report( identifier=body["identifier"], - process_group_identifier=process_group_id, - process_model_identifier=process_model_id, user=g.user, report_metadata=body["report_metadata"], ) @@ -791,16 +818,13 @@ def process_instance_report_create( def process_instance_report_update( - process_group_id: str, - process_model_id: str, report_identifier: str, body: Dict[str, Any], ) -> flask.wrappers.Response: """Process_instance_report_create.""" process_instance_report = ProcessInstanceReportModel.query.filter_by( identifier=report_identifier, - process_group_identifier=process_group_id, - process_model_identifier=process_model_id, + created_by_id=g.user.id, ).first() if process_instance_report is None: raise ApiError( @@ -816,15 +840,12 @@ def process_instance_report_update( def process_instance_report_delete( - process_group_id: str, - process_model_id: str, report_identifier: str, ) -> flask.wrappers.Response: """Process_instance_report_create.""" process_instance_report = ProcessInstanceReportModel.query.filter_by( identifier=report_identifier, - process_group_identifier=process_group_id, - process_model_identifier=process_model_id, + created_by_id=g.user.id, ).first() if process_instance_report is None: raise ApiError( @@ -877,25 +898,20 @@ def authentication_callback( def process_instance_report_show( - process_group_id: str, - process_model_id: str, report_identifier: str, page: int = 1, per_page: int = 100, ) -> flask.wrappers.Response: """Process_instance_list.""" - process_model = get_process_model(process_model_id, process_group_id) - - process_instances = ( - ProcessInstanceModel.query.filter_by(process_model_identifier=process_model.id) - .order_by( - ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore - ) - .paginate(page=page, per_page=per_page, error_out=False) + process_instances = ProcessInstanceModel.query.order_by( # .filter_by(process_model_identifier=process_model.id) + ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore + ).paginate( + page=page, per_page=per_page, error_out=False ) process_instance_report = ProcessInstanceReportModel.query.filter_by( - identifier=report_identifier + identifier=report_identifier, + created_by_id=g.user.id, ).first() if process_instance_report is None: raise ApiError( @@ -1007,7 +1023,6 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response process_model = get_process_model( process_instance.process_model_identifier, - process_instance.process_group_identifier, ) form_schema_file_name = "" @@ -1159,7 +1174,7 @@ def task_submit( def script_unit_test_create( process_group_id: str, process_model_id: str, body: Dict[str, Union[str, bool, int]] ) -> flask.wrappers.Response: - """Script_unit_test_run.""" + """Script_unit_test_create.""" bpmn_task_identifier = _get_required_parameter_or_raise( "bpmn_task_identifier", body ) @@ -1168,7 +1183,8 @@ def script_unit_test_create( "expected_output_json", body ) - process_model = get_process_model(process_model_id, process_group_id) + process_model_identifier = f"{process_group_id}/{process_model_id}" + process_model = get_process_model(process_model_identifier) file = SpecFileService.get_files(process_model, process_model.primary_file_name)[0] if file is None: raise ApiError( @@ -1278,13 +1294,11 @@ def get_file_from_request() -> Any: return request_file -def get_process_model(process_model_id: str, process_group_id: str) -> ProcessModelInfo: +def get_process_model(process_model_id: str) -> ProcessModelInfo: """Get_process_model.""" process_model = None try: - process_model = ProcessModelService().get_process_model( - process_model_id, group_id=process_group_id - ) + process_model = ProcessModelService().get_process_model(process_model_id) except ProcessEntityNotFoundError as exception: raise ( ApiError( diff --git a/src/spiffworkflow_backend/routes/user.py b/src/spiffworkflow_backend/routes/user.py index aa5bcdd8..66299272 100644 --- a/src/spiffworkflow_backend/routes/user.py +++ b/src/spiffworkflow_backend/routes/user.py @@ -46,6 +46,7 @@ def verify_token( ApiError: If not on production and token is not valid, returns an 'invalid_token' 403 error. If on production and user is not authenticated, returns a 'no_user' 403 error. """ + user_info = None if not force_run and AuthorizationService.should_disable_auth_for_request(): return None @@ -104,6 +105,7 @@ def verify_token( raise ApiError( error_code="fail_get_user_info", message="Cannot get user info from token", + status_code=401, ) from e if ( diff --git a/src/spiffworkflow_backend/services/acceptance_test_fixtures.py b/src/spiffworkflow_backend/services/acceptance_test_fixtures.py index 144c5ef2..c6c1b578 100644 --- a/src/spiffworkflow_backend/services/acceptance_test_fixtures.py +++ b/src/spiffworkflow_backend/services/acceptance_test_fixtures.py @@ -13,8 +13,8 @@ from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus def load_acceptance_test_fixtures() -> list[ProcessInstanceModel]: """Load_fixtures.""" current_app.logger.debug("load_acceptance_test_fixtures() start") - test_process_group_id = "acceptance-tests-group-one" - test_process_model_id = "acceptance-tests-model-1" + test_process_group_id = "" + test_process_model_id = "acceptance-tests-group-one/acceptance-tests-model-1" user = BaseTest.find_or_create_user() statuses = ProcessInstanceStatus.list() current_time = round(time.time()) diff --git a/src/spiffworkflow_backend/services/data_setup_service.py b/src/spiffworkflow_backend/services/data_setup_service.py index 88bd6048..23df25f3 100644 --- a/src/spiffworkflow_backend/services/data_setup_service.py +++ b/src/spiffworkflow_backend/services/data_setup_service.py @@ -48,7 +48,7 @@ class DataSetupService: except Exception as ex: failing_process_models.append( ( - f"{process_model.process_group_id}/{process_model.id}/{process_model_file.name}", + f"{process_model.process_group}/{process_model.id}/{process_model_file.name}", str(ex), ) ) @@ -87,7 +87,7 @@ class DataSetupService: else: failing_process_models.append( ( - f"{process_model.process_group_id}/{process_model.id}", + f"{process_model.process_group}/{process_model.id}", "primary_file_name not set", ) ) diff --git a/src/spiffworkflow_backend/services/error_handling_service.py b/src/spiffworkflow_backend/services/error_handling_service.py index 36c66d93..3f1622a4 100644 --- a/src/spiffworkflow_backend/services/error_handling_service.py +++ b/src/spiffworkflow_backend/services/error_handling_service.py @@ -35,7 +35,7 @@ class ErrorHandlingService: ) -> None: """On unhandled exceptions, set instance.status based on model.fault_or_suspend_on_exception.""" process_model = ProcessModelService().get_process_model( - _processor.process_model_identifier, _processor.process_group_identifier + _processor.process_model_identifier ) if process_model.fault_or_suspend_on_exception == "suspend": self.set_instance_status( diff --git a/src/spiffworkflow_backend/services/file_system_service.py b/src/spiffworkflow_backend/services/file_system_service.py index 3b23ce1b..a4a01b83 100644 --- a/src/spiffworkflow_backend/services/file_system_service.py +++ b/src/spiffworkflow_backend/services/file_system_service.py @@ -54,18 +54,22 @@ class FileSystemService: @staticmethod def process_group_path_for_spec(spec: ProcessModelInfo) -> str: """Category_path_for_spec.""" - return FileSystemService.process_group_path(spec.process_group_id) + process_group_id, _ = os.path.split(spec.id) + return FileSystemService.process_group_path(process_group_id) @staticmethod def workflow_path(spec: ProcessModelInfo) -> str: """Workflow_path.""" - process_group_path = FileSystemService.process_group_path_for_spec(spec) - return os.path.join(process_group_path, spec.id) + process_model_path = os.path.join(FileSystemService.root_path(), spec.id) + # process_group_path = FileSystemService.process_group_path_for_spec(spec) + return process_model_path @staticmethod - def full_path_to_process_model_file(spec: ProcessModelInfo, file_name: str) -> str: + def full_path_to_process_model_file(spec: ProcessModelInfo) -> str: """Full_path_to_process_model_file.""" - return os.path.join(FileSystemService.workflow_path(spec), file_name) + return os.path.join( + FileSystemService.workflow_path(spec), spec.primary_file_name # type: ignore + ) def next_display_order(self, spec: ProcessModelInfo) -> int: """Next_display_order.""" diff --git a/src/spiffworkflow_backend/services/message_service.py b/src/spiffworkflow_backend/services/message_service.py index da1e6224..216a66a5 100644 --- a/src/spiffworkflow_backend/services/message_service.py +++ b/src/spiffworkflow_backend/services/message_service.py @@ -120,7 +120,6 @@ class MessageService: process_instance_receive = ProcessInstanceService.create_process_instance( message_triggerable_process_model.process_model_identifier, user, - process_group_identifier=message_triggerable_process_model.process_group_identifier, ) processor_receive = ProcessInstanceProcessor(process_instance_receive) processor_receive.do_engine_steps(save=False) diff --git a/src/spiffworkflow_backend/services/process_instance_processor.py b/src/spiffworkflow_backend/services/process_instance_processor.py index 35320f65..0ed85511 100644 --- a/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/src/spiffworkflow_backend/services/process_instance_processor.py @@ -293,8 +293,9 @@ class ProcessInstanceProcessor: 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 = ( - f"{process_instance_model.process_group_identifier}/" + current_app.config[ + "THREAD_LOCAL_DATA" + ].process_model_identifier = ( f"{process_instance_model.process_model_identifier}" ) @@ -307,8 +308,7 @@ class ProcessInstanceProcessor: bpmn_process_spec, subprocesses, ) = ProcessInstanceProcessor.get_process_model_and_subprocesses( - process_instance_model.process_model_identifier, - process_instance_model.process_group_identifier, + process_instance_model.process_model_identifier ) else: bpmn_json_length = len(process_instance_model.bpmn_json.encode("utf-8")) @@ -359,7 +359,7 @@ class ProcessInstanceProcessor: check_sub_specs(test_spec, 5) self.process_model_identifier = process_instance_model.process_model_identifier - self.process_group_identifier = process_instance_model.process_group_identifier + # self.process_group_identifier = process_instance_model.process_group_identifier try: self.bpmn_process_instance = self.__get_bpmn_process_instance( @@ -394,17 +394,17 @@ class ProcessInstanceProcessor: @classmethod def get_process_model_and_subprocesses( - cls, process_model_identifier: str, process_group_identifier: str + cls, process_model_identifier: str ) -> Tuple[BpmnProcessSpec, IdToBpmnProcessSpecMapping]: """Get_process_model_and_subprocesses.""" process_model_info = ProcessModelService().get_process_model( - process_model_identifier, process_group_identifier + process_model_identifier ) if process_model_info is None: raise ( ApiError( "process_model_not_found", - f"The given process model was not found: {process_group_identifier}/{process_model_identifier}.", + f"The given process model was not found: {process_model_identifier}.", ) ) spec_files = SpecFileService.get_files(process_model_info) @@ -412,12 +412,11 @@ class ProcessInstanceProcessor: @classmethod def get_bpmn_process_instance_from_process_model( - cls, process_model_identifier: str, process_group_identifier: str + cls, process_model_identifier: str ) -> BpmnWorkflow: """Get_all_bpmn_process_identifiers_for_process_model.""" (bpmn_process_spec, subprocesses) = cls.get_process_model_and_subprocesses( process_model_identifier, - process_group_identifier, ) return cls.get_bpmn_process_instance_from_workflow_spec( bpmn_process_spec, subprocesses @@ -698,7 +697,7 @@ class ProcessInstanceProcessor: etree_element, ) return FileSystemService.full_path_to_process_model_file( - process_model, process_model.primary_file_name + process_model ) return None diff --git a/src/spiffworkflow_backend/services/process_instance_service.py b/src/spiffworkflow_backend/services/process_instance_service.py index 062d0ef7..7854537e 100644 --- a/src/spiffworkflow_backend/services/process_instance_service.py +++ b/src/spiffworkflow_backend/services/process_instance_service.py @@ -2,7 +2,6 @@ import time from typing import Any from typing import List -from typing import Optional from flask import current_app from flask_bpmn.api.api_error import ApiError @@ -32,7 +31,6 @@ class ProcessInstanceService: def create_process_instance( process_model_identifier: str, user: UserModel, - process_group_identifier: Optional[str] = None, ) -> ProcessInstanceModel: """Get_process_instance_from_spec.""" current_git_revision = GitService.get_current_revision() @@ -40,7 +38,7 @@ class ProcessInstanceService: status=ProcessInstanceStatus.not_started.value, process_initiator=user, process_model_identifier=process_model_identifier, - process_group_identifier=process_group_identifier, + process_group_identifier="", start_in_seconds=round(time.time()), bpmn_version_control_type="git", bpmn_version_control_identifier=current_git_revision, @@ -97,7 +95,7 @@ class ProcessInstanceService: next_task=None, # navigation=navigation, process_model_identifier=processor.process_model_identifier, - process_group_identifier=processor.process_group_identifier, + process_group_identifier="", # total_tasks=len(navigation), completed_tasks=processor.process_instance_model.completed_tasks, updated_at_in_seconds=processor.process_instance_model.updated_at_in_seconds, @@ -105,6 +103,20 @@ class ProcessInstanceService: 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: + process_instance_api.next_task = ( + ProcessInstanceService.spiff_task_to_api_task( + next_task_trying_again, add_docs_and_forms=True + ) + ) + return process_instance_api def get_process_instance(self, process_instance_id: int) -> Any: diff --git a/src/spiffworkflow_backend/services/process_model_service.py b/src/spiffworkflow_backend/services/process_model_service.py index e10092d5..c650e1f5 100644 --- a/src/spiffworkflow_backend/services/process_model_service.py +++ b/src/spiffworkflow_backend/services/process_model_service.py @@ -34,6 +34,20 @@ class ProcessModelService(FileSystemService): GROUP_SCHEMA = ProcessGroupSchema() WF_SCHEMA = ProcessModelInfoSchema() + def is_group(self, path: str) -> bool: + """Is_group.""" + group_json_path = os.path.join(path, self.CAT_JSON_FILE) + if os.path.exists(group_json_path): + return True + return False + + def is_model(self, path: str) -> bool: + """Is_model.""" + model_json_path = os.path.join(path, self.WF_JSON_FILE) + if os.path.exists(model_json_path): + return True + return False + @staticmethod def get_batch( items: list[T], @@ -62,7 +76,7 @@ class ProcessModelService(FileSystemService): def save_process_model(self, process_model: ProcessModelInfo) -> None: """Save_process_model.""" - spec_path = self.workflow_path(process_model) + spec_path = os.path.join(FileSystemService.root_path(), process_model.id) os.makedirs(spec_path, exist_ok=True) json_path = os.path.join(spec_path, self.WF_JSON_FILE) with open(json_path, "w") as wf_json: @@ -80,8 +94,9 @@ class ProcessModelService(FileSystemService): error_code="existing_instances", message=f"We cannot delete the model `{process_model_id}`, there are existing instances that depend on it.", ) - process_model = self.get_process_model(process_model_id) - path = self.workflow_path(process_model) + self.get_process_model(process_model_id) + # path = self.workflow_path(process_model) + path = f"{FileSystemService.root_path()}/{process_model_id}" shutil.rmtree(path) @classmethod @@ -89,36 +104,43 @@ class ProcessModelService(FileSystemService): cls, relative_path: str ) -> ProcessModelInfo: """Get_process_model_from_relative_path.""" - process_group_identifier = os.path.dirname(relative_path) + process_group_identifier, _ = os.path.split(relative_path) process_group = cls().get_process_group(process_group_identifier) path = os.path.join(FileSystemService.root_path(), relative_path) return cls().__scan_spec(path, process_group=process_group) - def get_process_model( - self, process_model_id: str, group_id: Optional[str] = None - ) -> ProcessModelInfo: - """Get a process model from a model and group id.""" - if not os.path.exists(FileSystemService.root_path()): - raise ProcessEntityNotFoundError("process_model_not_found") + def get_process_model(self, process_model_id: str) -> ProcessModelInfo: + """Get a process model from a model and group id. - if group_id is not None: - process_group = self.get_process_group(group_id) - if process_group is not None: - for process_model in process_group.process_models: - if process_model_id == process_model.id: - return process_model - with os.scandir(FileSystemService.root_path()) as process_group_dirs: - for item in process_group_dirs: - process_group_dir = item - if item.is_dir(): - with os.scandir(item.path) as spec_dirs: - for sd in spec_dirs: - if sd.name == process_model_id: - # Now we have the process_group direcotry, and spec directory - process_group = self.__scan_process_group( - process_group_dir - ) - return self.__scan_spec(sd.path, sd.name, process_group) + process_model_id is the full path to the model--including groups. + """ + if not os.path.exists(FileSystemService.root_path()): + raise ProcessEntityNotFoundError("process_model_root_not_found") + + model_path = os.path.join(FileSystemService.root_path(), process_model_id) + if self.is_model(model_path): + process_model = self.get_process_model_from_relative_path(process_model_id) + return process_model + + # group_path, model_id = os.path.split(process_model_id) + # if group_path is not None: + # process_group = self.get_process_group(group_path) + # if process_group is not None: + # for process_model in process_group.process_models: + # if process_model_id == process_model.id: + # return process_model + # with os.scandir(FileSystemService.root_path()) as process_group_dirs: + # for item in process_group_dirs: + # process_group_dir = item + # if item.is_dir(): + # with os.scandir(item.path) as spec_dirs: + # for sd in spec_dirs: + # if sd.name == process_model_id: + # # Now we have the process_group directory, and spec directory + # process_group = self.__scan_process_group( + # process_group_dir + # ) + # return self.__scan_spec(sd.path, sd.name, process_group) raise ProcessEntityNotFoundError("process_model_not_found") def get_process_models( @@ -148,10 +170,24 @@ class ProcessModelService(FileSystemService): def get_process_group(self, process_group_id: str) -> ProcessGroup: """Look for a given process_group, and return it.""" if os.path.exists(FileSystemService.root_path()): - with os.scandir(FileSystemService.root_path()) as directory_items: - for item in directory_items: - if item.is_dir() and item.name == process_group_id: - return self.__scan_process_group(item) + process_group_path = os.path.join( + FileSystemService.root_path(), process_group_id + ) + if self.is_group(process_group_path): + return self.__scan_process_group(process_group_path) + # nested_groups = [] + # process_group_dir = os.scandir(process_group_path) + # for item in process_group_dir: + # if self.is_group(item.path): + # nested_group = self.get_process_group(os.path.join(process_group_path, item.path)) + # nested_groups.append(nested_group) + # elif self.is_model(item.path): + # print("get_process_group: ") + # return self.__scan_process_group(process_group_path) + # with os.scandir(FileSystemService.root_path()) as directory_items: + # for item in directory_items: + # if item.is_dir() and item.name == process_group_id: + # return self.__scan_process_group(item) raise ProcessEntityNotFoundError( "process_group_not_found", f"Process Group Id: {process_group_id}" @@ -202,13 +238,15 @@ class ProcessModelService(FileSystemService): with os.scandir(FileSystemService.root_path()) as directory_items: process_groups = [] for item in directory_items: - if item.is_dir() and not item.name[0] == ".": - process_groups.append(self.__scan_process_group(item)) + # if item.is_dir() and not item.name[0] == ".": + if item.is_dir() and self.is_group(item): # type: ignore + scanned_process_group = self.__scan_process_group(item.path) + process_groups.append(scanned_process_group) return process_groups - def __scan_process_group(self, dir_item: os.DirEntry) -> ProcessGroup: - """Reads the process_group.json file, and any workflow directories.""" - cat_path = os.path.join(dir_item.path, self.CAT_JSON_FILE) + def __scan_process_group(self, dir_path: str) -> ProcessGroup: + """Reads the process_group.json file, and any nested directories.""" + cat_path = os.path.join(dir_path, self.CAT_JSON_FILE) if os.path.exists(cat_path): with open(cat_path) as cat_json: data = json.load(cat_json) @@ -216,26 +254,34 @@ class ProcessModelService(FileSystemService): if process_group is None: raise ApiError( error_code="process_group_could_not_be_loaded_from_disk", - message=f"We could not load the process_group from disk from: {dir_item}", + message=f"We could not load the process_group from disk from: {dir_path}", ) else: + process_group_id = dir_path.replace(FileSystemService.root_path(), "") process_group = ProcessGroup( - id=dir_item.name, - display_name=dir_item.name, + id=process_group_id, + display_name=process_group_id, display_order=10000, admin=False, ) with open(cat_path, "w") as wf_json: json.dump(self.GROUP_SCHEMA.dump(process_group), wf_json, indent=4) - with os.scandir(dir_item.path) as workflow_dirs: + with os.scandir(dir_path) as nested_items: process_group.process_models = [] - for item in workflow_dirs: - if item.is_dir(): - process_group.process_models.append( - self.__scan_spec( - item.path, item.name, process_group=process_group + for nested_item in nested_items: + if nested_item.is_dir(): + # TODO: check whether this is a group or model + if self.is_group(nested_item.path): + # This is a nested group + ... + elif self.is_model(nested_item.path): + process_group.process_models.append( + self.__scan_spec( + nested_item.path, + nested_item.name, + process_group=process_group, + ) ) - ) process_group.process_models.sort() return process_group @@ -251,6 +297,8 @@ class ProcessModelService(FileSystemService): if os.path.exists(spec_path): with open(spec_path) as wf_json: data = json.load(wf_json) + if "process_group_id" in data: + data.pop("process_group_id") spec = ProcessModelInfo(**data) if spec is None: raise ApiError( @@ -274,5 +322,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_id = process_group.id + spec.process_group = process_group.id return spec diff --git a/src/spiffworkflow_backend/services/spec_file_service.py b/src/spiffworkflow_backend/services/spec_file_service.py index c6b86d3e..92e905bf 100644 --- a/src/spiffworkflow_backend/services/spec_file_service.py +++ b/src/spiffworkflow_backend/services/spec_file_service.py @@ -48,7 +48,8 @@ class SpecFileService(FileSystemService): extension_filter: str = "", ) -> List[File]: """Return all files associated with a workflow specification.""" - path = SpecFileService.workflow_path(process_model_info) + # path = SpecFileService.workflow_path(process_model_info) + path = os.path.join(FileSystemService.root_path(), process_model_info.id) files = SpecFileService._get_files(path, file_name) if extension_filter != "": files = list( @@ -105,7 +106,10 @@ class SpecFileService(FileSystemService): ) -> File: """Update_file.""" SpecFileService.assert_valid_file_name(file_name) - file_path = SpecFileService.file_path(process_model_info, file_name) + # file_path = SpecFileService.file_path(process_model_info, file_name) + file_path = os.path.join( + FileSystemService.root_path(), process_model_info.id, file_name + ) SpecFileService.write_file_data_to_system(file_path, binary_data) file = SpecFileService.to_file_object(file_name, file_path) @@ -129,7 +133,10 @@ class SpecFileService(FileSystemService): @staticmethod def get_data(process_model_info: ProcessModelInfo, file_name: str) -> bytes: """Get_data.""" - file_path = SpecFileService.file_path(process_model_info, file_name) + # file_path = SpecFileService.file_path(process_model_info, file_name) + file_path = os.path.join( + FileSystemService.root_path(), process_model_info.id, file_name + ) if not os.path.exists(file_path): raise ProcessModelFileNotFoundError( f"No file found with name {file_name} in {process_model_info.display_name}" @@ -163,7 +170,8 @@ class SpecFileService(FileSystemService): # for lf in lookup_files: # session.query(LookupDataModel).filter_by(lookup_file_model_id=lf.id).delete() # session.query(LookupFileModel).filter_by(id=lf.id).delete() - file_path = SpecFileService.file_path(spec, file_name) + # file_path = SpecFileService.file_path(spec, file_name) + file_path = os.path.join(FileSystemService.root_path(), spec.id, file_name) os.remove(file_path) @staticmethod @@ -367,9 +375,8 @@ class SpecFileService(FileSystemService): process_model_info: ProcessModelInfo, bpmn_file_name: str, et_root: _Element ) -> None: """Store_bpmn_process_identifiers.""" - relative_process_model_path = SpecFileService.process_model_relative_path( - process_model_info - ) + relative_process_model_path = process_model_info.id + relative_bpmn_file_path = os.path.join( relative_process_model_path, bpmn_file_name ) @@ -462,10 +469,12 @@ class SpecFileService(FileSystemService): ) if message_triggerable_process_model is None: - message_triggerable_process_model = MessageTriggerableProcessModel( - message_model_id=message_model.id, - process_model_identifier=process_model_info.id, - process_group_identifier=process_model_info.process_group_id, + message_triggerable_process_model = ( + MessageTriggerableProcessModel( + message_model_id=message_model.id, + process_model_identifier=process_model_info.id, + process_group_identifier="process_group_identifier", + ) ) db.session.add(message_triggerable_process_model) db.session.commit() @@ -473,12 +482,11 @@ class SpecFileService(FileSystemService): if ( message_triggerable_process_model.process_model_identifier != process_model_info.id - or message_triggerable_process_model.process_group_identifier - != process_model_info.process_group_id + # or message_triggerable_process_model.process_group_identifier + # != process_model_info.process_group_id ): raise ValidationException( - "Message model is already used to start process model" - f"'{process_model_info.process_group_id}/{process_model_info.id}'" + f"Message model is already used to start process model {process_model_info.id}" ) for child in et_root: diff --git a/tests/data/manual_task/manual_task.bpmn b/tests/data/manual_task/manual_task.bpmn new file mode 100644 index 00000000..aefbb376 --- /dev/null +++ b/tests/data/manual_task/manual_task.bpmn @@ -0,0 +1,41 @@ + + + + + Flow_1xlck7g + + + + Flow_0nnh2x9 + + + + + ## Hello + + Flow_1xlck7g + Flow_0nnh2x9 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/data/simple_script/simple_script.bpmn b/tests/data/simple_script/simple_script.bpmn index ab573ce4..6e14807f 100644 --- a/tests/data/simple_script/simple_script.bpmn +++ b/tests/data/simple_script/simple_script.bpmn @@ -1,67 +1,87 @@ - - + + - Flow_1k9q28c + Flow_0r3ua0i - - - - Flow_10610n2 - - - - Flow_1k9q28c - Flow_1fviiob + + + Flow_0r3ua0i + Flow_19g4f88 a = 1 -b = 2 -c = a + b -norris=fact_service(type='norris') +b = 2 + + + + + + {'a': 1, 'b': 2} + {'a': 1, 'b': 2, 'c': 3} + + + + Flow_19g4f88 + Flow_152cqfw + c = a + b + + - ## Display Data + + ## Data - -### a +### A {{ a }} - -### b +### B {{ b }} - -### c -{{ c }} - Flow_1fviiob - Flow_10610n2 +### C +{{ c }} + + Flow_152cqfw + Flow_1vqk60p + + Flow_1vqk60p + + - - - - - - - - - - - - - + - + - - + + + - - + + + - - + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/spiffworkflow_backend/helpers/base_test.py b/tests/spiffworkflow_backend/helpers/base_test.py index a573e8a3..794e1cea 100644 --- a/tests/spiffworkflow_backend/helpers/base_test.py +++ b/tests/spiffworkflow_backend/helpers/base_test.py @@ -25,6 +25,7 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.authorization_service import AuthorizationService +from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.user_service import UserService @@ -34,6 +35,46 @@ from spiffworkflow_backend.services.user_service import UserService class BaseTest: """BaseTest.""" + def basic_test_setup( + self, + client: FlaskClient, + user: UserModel, + process_group_id: Optional[str] = "test_group", + process_model_id: Optional[str] = "random_fact", + bpmn_file_name: Optional[str] = None, + bpmn_file_location: Optional[str] = None, + ) -> str: + """Creates a process group. + + Creates a process model + Adds a bpmn file to the model. + """ + process_group_display_name = process_group_id or "" + process_group_description = process_group_id or "" + process_model_identifier = f"{process_group_id}/{process_model_id}" + if bpmn_file_location is None: + bpmn_file_location = process_model_id + + self.create_process_group( + client, user, process_group_description, process_group_display_name + ) + + self.create_process_model_with_api( + client, + process_model_id=process_model_identifier, + process_model_display_name=process_group_display_name, + process_model_description=process_group_description, + user=user, + ) + + load_test_spec( + process_model_id=process_model_identifier, + bpmn_file_name=bpmn_file_name, + process_model_source_directory=bpmn_file_location, + ) + + return process_model_identifier + @staticmethod def find_or_create_user(username: str = "test_user_1") -> UserModel: """Find_or_create_user.""" @@ -67,17 +108,19 @@ class BaseTest: open_id_client_secret_key, ) + @staticmethod def create_process_instance( - self, client: FlaskClient, - test_process_group_id: str, test_process_model_id: str, headers: Dict[str, str], ) -> TestResponse: - """Create_process_instance.""" - load_test_spec(test_process_model_id, process_group_id=test_process_group_id) + """Create_process_instance. + + There must be an existing process model to instantiate. + """ + modified_process_model_id = test_process_model_id.replace("/", ":") response = client.post( - f"/v1.0/process-models/{test_process_group_id}/{test_process_model_id}/process-instances", + f"/v1.0/process-models/{modified_process_model_id}/process-instances", headers=headers, ) assert response.status_code == 201 @@ -86,8 +129,7 @@ class BaseTest: def create_process_model_with_api( self, client: FlaskClient, - process_group_id: Optional[str] = None, - process_model_id: str = "make_cookies", + process_model_id: Optional[str] = None, process_model_display_name: str = "Cooooookies", process_model_description: str = "Om nom nom delicious cookies", fault_or_suspend_on_exception: str = NotificationType.suspend.value, @@ -97,65 +139,77 @@ class BaseTest: user: Optional[UserModel] = None, ) -> TestResponse: """Create_process_model.""" - process_model_service = ProcessModelService() + if process_model_id is not None: - # make sure we have a group - if process_group_id is None: - process_group_tmp = ProcessGroup( - id="test_cat", - display_name="Test Category", - display_order=0, - admin=False, - ) - process_group = process_model_service.add_process_group(process_group_tmp) + # make sure we have a group + process_group_id, _ = os.path.split(process_model_id) + process_group_path = f"{FileSystemService.root_path()}/{process_group_id}" + if ProcessModelService().is_group(process_group_path): + + if exception_notification_addresses is None: + exception_notification_addresses = [] + + model = ProcessModelInfo( + id=process_model_id, + display_name=process_model_display_name, + description=process_model_description, + is_review=False, + primary_process_id=primary_process_id, + primary_file_name=primary_file_name, + fault_or_suspend_on_exception=fault_or_suspend_on_exception, + exception_notification_addresses=exception_notification_addresses, + ) + if user is None: + user = self.find_or_create_user() + + response = client.post( + "/v1.0/process-models", + content_type="application/json", + data=json.dumps(ProcessModelInfoSchema().dump(model)), + headers=self.logged_in_headers(user), + ) + assert response.status_code == 201 + return response + + else: + raise Exception("You must create the group first") else: - process_group = ProcessModelService().get_process_group(process_group_id) - - if exception_notification_addresses is None: - exception_notification_addresses = [] - model = ProcessModelInfo( - id=process_model_id, - display_name=process_model_display_name, - description=process_model_description, - process_group_id=process_group.id, - is_review=False, - primary_process_id=primary_process_id, - primary_file_name=primary_file_name, - fault_or_suspend_on_exception=fault_or_suspend_on_exception, - exception_notification_addresses=exception_notification_addresses, - ) - if user is None: - user = self.find_or_create_user() - - response = client.post( - "/v1.0/process-models", - content_type="application/json", - data=json.dumps(ProcessModelInfoSchema().dump(model)), - headers=self.logged_in_headers(user), - ) - assert response.status_code == 201 - return response + raise Exception( + "You must include the process_model_id, which must be a path to the model" + ) def create_spec_file( self, client: FlaskClient, - process_group_id: str = "random_fact", - process_model_id: str = "random_fact", + process_model_id: str, + process_model_location: Optional[str] = None, process_model: Optional[ProcessModelInfo] = None, file_name: str = "random_fact.svg", file_data: bytes = b"abcdef", user: Optional[UserModel] = None, ) -> Any: - """Test_create_spec_file.""" + """Test_create_spec_file. + + Adds a bpmn file to the model. + process_model_id is the destination path + process_model_location is the source path + + because of permissions, user might be required now..., not sure yet. + """ + if process_model_location is None: + process_model_location = file_name.split(".")[0] if process_model is None: process_model = load_test_spec( - process_model_id, process_group_id=process_group_id + process_model_id=process_model_id, + bpmn_file_name=file_name, + process_model_source_directory=process_model_location, ) data = {"file": (io.BytesIO(file_data), file_name)} if user is None: user = self.find_or_create_user() + modified_process_model_id = process_model.id.replace("/", ":") response = client.post( - f"/v1.0/process-models/{process_model.process_group_id}/{process_model.id}/files", + f"/v1.0/process-models/{modified_process_model_id}/files", data=data, follow_redirects=True, content_type="multipart/form-data", @@ -168,7 +222,7 @@ class BaseTest: # assert "image/svg+xml" == file["content_type"] response = client.get( - f"/v1.0/process-models/{process_model.process_group_id}/{process_model.id}/files/{file_name}", + f"/v1.0/process-models/{modified_process_model_id}/files/{file_name}", headers=self.logged_in_headers(user), ) assert response.status_code == 200 @@ -221,7 +275,7 @@ class BaseTest: status=status, process_initiator=user, process_model_identifier=process_model.id, - process_group_identifier=process_model.process_group_id, + process_group_identifier="", updated_at_in_seconds=round(time.time()), start_in_seconds=current_time - (3600 * 1), end_in_seconds=current_time - (3600 * 1 - 20), diff --git a/tests/spiffworkflow_backend/helpers/example_data.py b/tests/spiffworkflow_backend/helpers/example_data.py index a94be48e..ac1e8dc8 100644 --- a/tests/spiffworkflow_backend/helpers/example_data.py +++ b/tests/spiffworkflow_backend/helpers/example_data.py @@ -13,27 +13,30 @@ from spiffworkflow_backend.services.spec_file_service import SpecFileService class ExampleDataLoader: """ExampleDataLoader.""" + @staticmethod def create_spec( - self, process_model_id: str, display_name: str = "", description: str = "", - process_group_id: str = "", display_order: int = 0, - from_tests: bool = False, + # from_tests: bool = False, bpmn_file_name: Optional[str] = None, process_model_source_directory: Optional[str] = None, ) -> ProcessModelInfo: - """Assumes that a directory exists in static/bpmn with the same name as the given process_model_id. + """Assumes that process_model_source_directory exists in static/bpmn and contains bpmn_file_name. - further assumes that the [process_model_id].bpmn is the primary file for the process model. - returns an array of data models to be added to the database. + further assumes that bpmn_file_name is the primary file for the process model. + + if bpmn_file_name is None we load all files in process_model_source_directory, + otherwise, we only load bpmn_file_name """ + if process_model_source_directory is None: + raise Exception("You must include `process_model_source_directory`.") + spec = ProcessModelInfo( id=process_model_id, display_name=display_name, description=description, - process_group_id=process_group_id, display_order=display_order, is_review=False, ) @@ -55,25 +58,16 @@ class ExampleDataLoader: if bpmn_file_name: file_name_matcher = bpmn_file_name_with_extension - file_glob = "" - if from_tests: - file_glob = os.path.join( - current_app.instance_path, - "..", - "..", - "tests", - "data", - process_model_source_directory_to_use, - file_name_matcher, - ) - else: - file_glob = os.path.join( - current_app.root_path, - "static", - "bpmn", - process_model_source_directory_to_use, - file_name_matcher, - ) + # file_glob = "" + file_glob = os.path.join( + current_app.root_path, + "..", + "..", + "tests", + "data", + process_model_source_directory_to_use, + file_name_matcher, + ) files = glob.glob(file_glob) for file_path in files: diff --git a/tests/spiffworkflow_backend/helpers/test_data.py b/tests/spiffworkflow_backend/helpers/test_data.py index fd418aa2..d6b4f730 100644 --- a/tests/spiffworkflow_backend/helpers/test_data.py +++ b/tests/spiffworkflow_backend/helpers/test_data.py @@ -37,40 +37,17 @@ def assure_process_group_exists(process_group_id: Optional[str] = None) -> Proce def load_test_spec( process_model_id: str, - process_group_id: Optional[str] = None, bpmn_file_name: Optional[str] = None, process_model_source_directory: Optional[str] = None, ) -> ProcessModelInfo: - """Loads a process model into the bpmn dir based on a directory in tests/data.""" - process_group = None - process_model_service = ProcessModelService() - if process_group_id is None: - process_group_id = "test_process_group_id" - process_group = assure_process_group_exists(process_group_id) - process_group_id = process_group.id + """Loads a bpmn file into the process model dir based on a directory in tests/data.""" + if process_model_source_directory is None: + raise Exception("You must inclode a `process_model_source_directory`.") - try: - return process_model_service.get_process_model( - process_model_id, group_id=process_group_id - ) - except ProcessEntityNotFoundError: - spec = ExampleDataLoader().create_spec( - process_model_id=process_model_id, - from_tests=True, - display_name=process_model_id, - process_group_id=process_group_id, - bpmn_file_name=bpmn_file_name, - process_model_source_directory=process_model_source_directory, - ) - return spec - - -# def user_info_to_query_string(user_info, redirect_url): -# query_string_list = [] -# items = user_info.items() -# for key, value in items: -# query_string_list.append('%s=%s' % (key, urllib.parse.quote(value))) -# -# query_string_list.append('redirect_url=%s' % redirect_url) -# -# return '?%s' % '&'.join(query_string_list) + spec = ExampleDataLoader.create_spec( + process_model_id=process_model_id, + display_name=process_model_id, + bpmn_file_name=bpmn_file_name, + process_model_source_directory=process_model_source_directory, + ) + return spec diff --git a/tests/spiffworkflow_backend/integration/test_logging_service.py b/tests/spiffworkflow_backend/integration/test_logging_service.py index a8d2720f..02d8ade1 100644 --- a/tests/spiffworkflow_backend/integration/test_logging_service.py +++ b/tests/spiffworkflow_backend/integration/test_logging_service.py @@ -19,20 +19,45 @@ class TestLoggingService(BaseTest): """Test_process_instance_run.""" process_group_id = "test_logging_spiff_logger" process_model_id = "simple_script" + self.create_process_group( + client=client, user=with_super_admin_user, process_group_id=process_group_id + ) + process_model_identifier = f"{process_group_id}/{process_model_id}" + # create the model + self.create_process_model_with_api( + client=client, + process_model_id=process_model_identifier, + process_model_display_name="Simple Script", + process_model_description="Simple Script", + user=with_super_admin_user, + ) + + bpmn_file_name = "simple_script.bpmn" + bpmn_file_data_bytes = self.get_test_data_file_contents( + bpmn_file_name, "simple_script" + ) + # add bpmn to the model + self.create_spec_file( + client=client, + process_model_id=process_model_identifier, + file_name=bpmn_file_name, + file_data=bpmn_file_data_bytes, + user=with_super_admin_user, + ) headers = self.logged_in_headers(with_super_admin_user) response = self.create_process_instance( - client, process_group_id, process_model_id, headers + client, process_model_identifier, 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", + f"/v1.0/process-instances/{process_instance_id}/run", headers=headers, ) assert response.status_code == 200 log_response = client.get( - f"/v1.0/process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}/logs", + f"/v1.0/process-instances/{process_instance_id}/logs", headers=headers, ) assert log_response.status_code == 200 diff --git a/tests/spiffworkflow_backend/integration/test_nested_groups.py b/tests/spiffworkflow_backend/integration/test_nested_groups.py new file mode 100644 index 00000000..ef89a561 --- /dev/null +++ b/tests/spiffworkflow_backend/integration/test_nested_groups.py @@ -0,0 +1,174 @@ +"""Test_nested_groups.""" +import json + +from flask.app import Flask +from flask.testing import FlaskClient +from tests.spiffworkflow_backend.helpers.base_test import BaseTest + +from spiffworkflow_backend.models.process_group import ProcessGroup +from spiffworkflow_backend.models.process_group import ProcessGroupSchema +from spiffworkflow_backend.models.process_model import ProcessModelInfo +from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema +from spiffworkflow_backend.models.user import UserModel + + +class TestNestedGroups(BaseTest): + """TestNestedGroups.""" + + def test_nested_groups( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + ) -> None: + """Test_nested_groups.""" + # /process-groups/{process_group_path}/show + target_uri = "/v1.0/process-groups/group_a,group_b" + user = self.find_or_create_user() + self.add_permissions_to_user( + user, target_uri=target_uri, permission_names=["read"] + ) + response = client.get( # noqa: F841 + target_uri, headers=self.logged_in_headers(user) + ) + print("test_nested_groups") + + def test_add_nested_group( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Test_add_nested_group.""" + # user = self.find_or_create_user() + # self.add_permissions_to_user( + # user, target_uri=target_uri, permission_names=["read", "create"] + # ) + process_group_a = ProcessGroup( + id="group_a", + display_name="Group A", + display_order=0, + admin=False, + ) + response_a = client.post( # noqa: F841 + "/v1.0/process-groups", + headers=self.logged_in_headers(with_super_admin_user), + content_type="application/json", + data=json.dumps(ProcessGroupSchema().dump(process_group_a)), + ) + process_group_b = ProcessGroup( + id="group_a/group_b", + display_name="Group B", + display_order=0, + admin=False, + ) + response_b = client.post( # noqa: F841 + "/v1.0/process-groups", + headers=self.logged_in_headers(with_super_admin_user), + content_type="application/json", + data=json.dumps(ProcessGroupSchema().dump(process_group_b)), + ) + process_group_c = ProcessGroup( + id="group_a/group_b/group_c", + display_name="Group C", + display_order=0, + admin=False, + ) + response_c = client.post( # noqa: F841 + "/v1.0/process-groups", + headers=self.logged_in_headers(with_super_admin_user), + content_type="application/json", + data=json.dumps(ProcessGroupSchema().dump(process_group_c)), + ) + + print("test_add_nested_group") + + def test_process_model_add( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Test_process_model_add.""" + process_group_a = ProcessGroup( + id="group_a", + display_name="Group A", + display_order=0, + admin=False, + ) + response_a = client.post( # noqa: F841 + "/v1.0/process-groups", + headers=self.logged_in_headers(with_super_admin_user), + content_type="application/json", + data=json.dumps(ProcessGroupSchema().dump(process_group_a)), + ) + process_group_b = ProcessGroup( + id="group_a/group_b", + display_name="Group B", + display_order=0, + admin=False, + ) + response_b = client.post( # noqa: F841 + "/v1.0/process-groups", + headers=self.logged_in_headers(with_super_admin_user), + content_type="application/json", + data=json.dumps(ProcessGroupSchema().dump(process_group_b)), + ) + process_model = ProcessModelInfo( + id="process_model", + display_name="Process Model", + description="Process Model", + primary_file_name="primary_file.bpmn", + primary_process_id="primary_process_id", + display_order=0, + ) + model_response = client.post( # noqa: F841 + "v1.0/process-models", + headers=self.logged_in_headers(with_super_admin_user), + content_type="application/json", + data=json.dumps(ProcessModelInfoSchema().dump(process_model)), + ) + print("test_process_model_add") + + def test_process_group_show( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Test_process_group_show.""" + # target_uri = "/process-groups/{process_group_id}" + # user = self.find_or_create_user("testadmin1") + # self.add_permissions_to_user( + # user, target_uri="v1.0/process-groups", permission_names=["read", "create"] + # ) + # self.add_permissions_to_user( + # user, target_uri="/process-groups/{process_group_id}", permission_names=["read", "create"] + # ) + + process_group_a = ProcessGroup( + id="group_a", + display_name="Group A", + display_order=0, + admin=False, + ) + response_create_a = client.post( # noqa: F841 + "/v1.0/process-groups", + headers=self.logged_in_headers(with_super_admin_user), + content_type="application/json", + data=json.dumps(ProcessGroupSchema().dump(process_group_a)), + ) + + target_uri = "/v1.0/process-groups/group_a" + user = self.find_or_create_user() + self.add_permissions_to_user( + user, target_uri=target_uri, permission_names=["read"] + ) + response = client.get( # noqa: F841 + target_uri, headers=self.logged_in_headers(user) + ) + + print("test_process_group_show: ") diff --git a/tests/spiffworkflow_backend/integration/test_process_api.py b/tests/spiffworkflow_backend/integration/test_process_api.py index bad47ce9..91355e0e 100644 --- a/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1,6 +1,7 @@ """Test Process Api Blueprint.""" import io import json +import os import time from typing import Any @@ -30,6 +31,9 @@ from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, ) +from spiffworkflow_backend.services.process_instance_service import ( + ProcessInstanceService, +) from spiffworkflow_backend.services.process_model_service import ProcessModelService # from spiffworkflow_backend.services.git_service import GitService @@ -108,10 +112,19 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_add_new_process_model.""" - process_model_identifier = "sample" - model_display_name = "Sample" - model_description = "The sample" + process_group_id = "test_process_group" + process_group_display_name = "Test Process Group" + # creates the group directory, and the json file + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_display_name + ) + process_model_id = "sample" + model_display_name = "Sample" + model_description = "The Sample" + process_model_identifier = f"{process_group_id}/{process_model_id}" + + # creates the model directory, and adds the json file self.create_process_model_with_api( client, process_model_id=process_model_identifier, @@ -120,23 +133,27 @@ class TestProcessApi(BaseTest): user=with_super_admin_user, ) process_model = ProcessModelService().get_process_model( - process_model_identifier + process_model_identifier, ) assert model_display_name == process_model.display_name assert 0 == process_model.display_order assert 1 == len(ProcessModelService().get_process_groups()) + # add bpmn file to the model bpmn_file_name = "sample.bpmn" bpmn_file_data_bytes = self.get_test_data_file_contents( bpmn_file_name, "sample" ) self.create_spec_file( client, + process_model_id=process_model.id, + process_model_location="sample", + process_model=process_model, file_name=bpmn_file_name, file_data=bpmn_file_data_bytes, - process_model=process_model, user=with_super_admin_user, ) + # get the model, assert that primary is set process_model = ProcessModelService().get_process_model( process_model_identifier ) @@ -151,16 +168,26 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_primary_process_id_updates_via_xml.""" - process_model_identifier = "sample" + process_group_id = "test_group" + process_model_id = "sample" + process_model_identifier = f"{process_group_id}/{process_model_id}" initial_primary_process_id = "sample" terminal_primary_process_id = "new_process_id" + self.create_process_group( + client=client, user=with_super_admin_user, process_group_id=process_group_id + ) - process_model = load_test_spec(process_model_id=process_model_identifier) + bpmn_file_name = f"{process_model_id}.bpmn" + bpmn_file_source_directory = process_model_id + process_model = load_test_spec( + process_model_id=process_model_identifier, + bpmn_file_name=bpmn_file_name, + process_model_source_directory=process_model_id, + ) assert process_model.primary_process_id == initial_primary_process_id - bpmn_file_name = "sample.bpmn" bpmn_file_data_bytes = self.get_test_data_file_contents( - bpmn_file_name, "sample" + bpmn_file_name, bpmn_file_source_directory ) bpmn_file_data_string = bpmn_file_data_bytes.decode("utf-8") old_string = f'bpmn:process id="{initial_primary_process_id}"' @@ -171,8 +198,9 @@ class TestProcessApi(BaseTest): updated_bpmn_file_data_bytes = bytearray(updated_bpmn_file_data_string, "utf-8") data = {"file": (io.BytesIO(updated_bpmn_file_data_bytes), bpmn_file_name)} + modified_process_model_id = process_model_identifier.replace("/", ":") response = client.put( - f"/v1.0/process-models/{process_model.process_group_id}/{process_model.id}/files/{bpmn_file_name}", + f"/v1.0/process-models/{modified_process_model_id}/files/{bpmn_file_name}", data=data, follow_redirects=True, content_type="multipart/form-data", @@ -193,19 +221,30 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_model_delete.""" + process_group_id = "test_process_group" + process_group_description = "Test Process Group" + process_model_id = "sample" + process_model_identifier = f"{process_group_id}/{process_model_id}" + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_description + ) self.create_process_model_with_api( client, + process_model_id=process_model_identifier, user=with_super_admin_user, ) # assert we have a model - process_model = ProcessModelService().get_process_model("make_cookies") + process_model = ProcessModelService().get_process_model( + process_model_identifier + ) assert process_model is not None - assert process_model.id == "make_cookies" + assert process_model.id == process_model_identifier # delete the model + modified_process_model_identifier = process_model_identifier.replace("/", ":") response = client.delete( - f"/v1.0/process-models/{process_model.process_group_id}/{process_model.id}", + f"/v1.0/process-models/{modified_process_model_identifier}", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 200 @@ -214,7 +253,7 @@ class TestProcessApi(BaseTest): # assert we no longer have a model with pytest.raises(ProcessEntityNotFoundError): - ProcessModelService().get_process_model("make_cookies") + ProcessModelService().get_process_model(process_model_identifier) def test_process_model_delete_with_instances( self, @@ -226,19 +265,38 @@ class TestProcessApi(BaseTest): """Test_process_model_delete_with_instances.""" test_process_group_id = "runs_without_input" test_process_model_id = "sample" + bpmn_file_name = "sample.bpmn" + bpmn_file_location = "sample" + process_model_identifier = f"{test_process_group_id}/{test_process_model_id}" + modified_process_model_identifier = process_model_identifier.replace("/", ":") + self.create_process_group(client, with_super_admin_user, test_process_group_id) + self.create_process_model_with_api( + client, process_model_identifier, user=with_super_admin_user + ) + bpmn_file_data_bytes = self.get_test_data_file_contents( + bpmn_file_name, bpmn_file_location + ) + self.create_spec_file( + client=client, + process_model_id=process_model_identifier, + process_model_location=test_process_model_id, + file_name=bpmn_file_name, + file_data=bpmn_file_data_bytes, + user=with_super_admin_user, + ) headers = self.logged_in_headers(with_super_admin_user) # create an instance from a model response = self.create_process_instance( - client, test_process_group_id, test_process_model_id, headers + client, process_model_identifier, headers ) data = json.loads(response.get_data(as_text=True)) # make sure the instance has the correct model - assert data["process_model_identifier"] == test_process_model_id + assert data["process_model_identifier"] == process_model_identifier # try to delete the model response = client.delete( - f"/v1.0/process-models/{test_process_group_id}/{test_process_model_id}", + f"/v1.0/process-models/{modified_process_model_identifier}", headers=self.logged_in_headers(with_super_admin_user), ) @@ -248,7 +306,7 @@ class TestProcessApi(BaseTest): assert data["error_code"] == "existing_instances" assert ( data["message"] - == "We cannot delete the model `sample`, there are existing instances that depend on it." + == f"We cannot delete the model `{process_model_identifier}`, there are existing instances that depend on it." ) def test_process_model_update( @@ -259,12 +317,19 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_model_update.""" + self.create_process_group( + client, with_super_admin_user, "test_process_group", "Test Process Group" + ) + process_model_identifier = "test_process_group/make_cookies" self.create_process_model_with_api( client, + process_model_id=process_model_identifier, user=with_super_admin_user, ) - process_model = ProcessModelService().get_process_model("make_cookies") - assert process_model.id == "make_cookies" + process_model = ProcessModelService().get_process_model( + process_model_identifier + ) + assert process_model.id == process_model_identifier assert process_model.display_name == "Cooooookies" assert process_model.is_review is False assert process_model.primary_file_name is None @@ -275,8 +340,9 @@ class TestProcessApi(BaseTest): process_model.primary_process_id = "superduper" process_model.is_review = True # not in the include list, so get ignored + modified_process_model_identifier = process_model_identifier.replace("/", ":") response = client.put( - f"/v1.0/process-models/{process_model.process_group_id}/{process_model.id}", + f"/v1.0/process-models/{modified_process_model_identifier}", headers=self.logged_in_headers(with_super_admin_user), content_type="application/json", data=json.dumps(ProcessModelInfoSchema().dump(process_model)), @@ -302,12 +368,11 @@ class TestProcessApi(BaseTest): # add 5 models to the group for i in range(5): - process_model_identifier = f"test_model_{i}" + process_model_identifier = f"{group_id}/test_model_{i}" model_display_name = f"Test Model {i}" model_description = f"Test Model {i} Description" self.create_process_model_with_api( client, - process_group_id=group_id, process_model_id=process_model_identifier, process_model_display_name=model_display_name, process_model_description=model_description, @@ -332,7 +397,7 @@ class TestProcessApi(BaseTest): ) assert response.json is not None assert len(response.json["results"]) == 1 - assert response.json["results"][0]["id"] == "test_model_0" + assert response.json["results"][0]["id"] == "test_group/test_model_0" assert response.json["pagination"]["count"] == 1 assert response.json["pagination"]["total"] == 5 assert response.json["pagination"]["pages"] == 5 @@ -344,7 +409,7 @@ class TestProcessApi(BaseTest): ) assert response.json is not None assert len(response.json["results"]) == 1 - assert response.json["results"][0]["id"] == "test_model_1" + assert response.json["results"][0]["id"] == "test_group/test_model_1" assert response.json["pagination"]["count"] == 1 assert response.json["pagination"]["total"] == 5 assert response.json["pagination"]["pages"] == 5 @@ -356,7 +421,7 @@ class TestProcessApi(BaseTest): ) assert response.json is not None assert len(response.json["results"]) == 3 - assert response.json["results"][0]["id"] == "test_model_0" + assert response.json["results"][0]["id"] == "test_group/test_model_0" assert response.json["pagination"]["count"] == 3 assert response.json["pagination"]["total"] == 5 assert response.json["pagination"]["pages"] == 2 @@ -369,7 +434,7 @@ class TestProcessApi(BaseTest): # there should only be 2 left assert response.json is not None assert len(response.json["results"]) == 2 - assert response.json["results"][0]["id"] == "test_model_3" + assert response.json["results"][0]["id"] == "test_group/test_model_3" assert response.json["pagination"]["count"] == 2 assert response.json["pagination"]["total"] == 5 assert response.json["pagination"]["pages"] == 2 @@ -558,12 +623,12 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_model_file_update.""" - self.create_spec_file(client, user=with_super_admin_user) + process_model_identifier = self.basic_test_setup(client, with_super_admin_user) + modified_process_model_id = process_model_identifier.replace("/", ":") - process_model = load_test_spec("random_fact") data = {"key1": "THIS DATA"} response = client.put( - f"/v1.0/process-models/{process_model.process_group_id}/{process_model.id}/files/random_fact.svg", + f"/v1.0/process-models/{modified_process_model_id}/files/random_fact.svg", data=data, follow_redirects=True, content_type="multipart/form-data", @@ -582,12 +647,12 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_model_file_update.""" - self.create_spec_file(client, user=with_super_admin_user) + process_model_identifier = self.basic_test_setup(client, with_super_admin_user) + modified_process_model_id = process_model_identifier.replace("/", ":") - process_model = load_test_spec("random_fact") data = {"file": (io.BytesIO(b""), "random_fact.svg")} response = client.put( - f"/v1.0/process-models/{process_model.process_group_id}/{process_model.id}/files/random_fact.svg", + f"/v1.0/process-models/{modified_process_model_id}/files/random_fact.svg", data=data, follow_redirects=True, content_type="multipart/form-data", @@ -606,13 +671,31 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_model_file_update.""" - original_file = self.create_spec_file(client, user=with_super_admin_user) + process_group_id = "test_group" + process_group_description = "Test Group" + process_model_id = "random_fact" + process_model_identifier = f"{process_group_id}/{process_model_id}" + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_description + ) + self.create_process_model_with_api( + client, + process_model_id=process_model_identifier, + user=with_super_admin_user, + ) - process_model = load_test_spec("random_fact") + bpmn_file_name = "random_fact.bpmn" + original_file = load_test_spec( + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + process_model_source_directory="random_fact", + ) + + modified_process_model_id = process_model_identifier.replace("/", ":") new_file_contents = b"THIS_IS_NEW_DATA" data = {"file": (io.BytesIO(new_file_contents), "random_fact.svg")} response = client.put( - f"/v1.0/process-models/{process_model.process_group_id}/{process_model.id}/files/random_fact.svg", + f"/v1.0/process-models/{modified_process_model_id}/files/random_fact.svg", data=data, follow_redirects=True, content_type="multipart/form-data", @@ -624,7 +707,7 @@ class TestProcessApi(BaseTest): assert response.json["ok"] response = client.get( - f"/v1.0/process-models/{process_model.process_group_id}/{process_model.id}/files/random_fact.svg", + f"/v1.0/process-models/{modified_process_model_id}/files/random_fact.svg", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 200 @@ -640,11 +723,16 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_model_file_update.""" - self.create_spec_file(client, user=with_super_admin_user) + process_model_identifier = self.basic_test_setup(client, with_super_admin_user) + # self.create_spec_file(client, user=with_super_admin_user) - process_model = load_test_spec("random_fact") + # process_model = load_test_spec("random_fact") + bad_process_model_identifier = f"x{process_model_identifier}" + modified_bad_process_model_identifier = bad_process_model_identifier.replace( + "/", ":" + ) response = client.delete( - f"/v1.0/process-models/INCORRECT-NON-EXISTENT-GROUP/{process_model.id}/files/random_fact.svg", + f"/v1.0/process-models/{modified_bad_process_model_identifier}/files/random_fact.svg", follow_redirects=True, headers=self.logged_in_headers(with_super_admin_user), ) @@ -661,11 +749,11 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_model_file_update.""" - self.create_spec_file(client, user=with_super_admin_user) + process_model_identifier = self.basic_test_setup(client, with_super_admin_user) + modified_process_model_identifier = process_model_identifier.replace("/", ":") - process_model = load_test_spec("random_fact") response = client.delete( - f"/v1.0/process-models/{process_model.process_group_id}/{process_model.id}/files/random_fact_DOES_NOT_EXIST.svg", + f"/v1.0/process-models/{modified_process_model_identifier}/files/random_fact_DOES_NOT_EXIST.svg", follow_redirects=True, headers=self.logged_in_headers(with_super_admin_user), ) @@ -682,11 +770,11 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_model_file_update.""" - self.create_spec_file(client, user=with_super_admin_user) + process_model_identifier = self.basic_test_setup(client, with_super_admin_user) + modified_process_model_identifier = process_model_identifier.replace("/", ":") - process_model = load_test_spec("random_fact") response = client.delete( - f"/v1.0/process-models/{process_model.process_group_id}/{process_model.id}/files/random_fact.svg", + f"/v1.0/process-models/{modified_process_model_identifier}/files/random_fact.bpmn", follow_redirects=True, headers=self.logged_in_headers(with_super_admin_user), ) @@ -696,7 +784,7 @@ class TestProcessApi(BaseTest): assert response.json["ok"] response = client.get( - f"/v1.0/process-models/{process_model.process_group_id}/{process_model.id}/files/random_fact.svg", + f"/v1.0/process-models/{modified_process_model_identifier}/files/random_fact.svg", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 404 @@ -709,18 +797,17 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_get_file.""" - test_process_group_id = "group_id1" - process_model_dir_name = "hello_world" - load_test_spec(process_model_dir_name, process_group_id=test_process_group_id) + process_model_identifier = self.basic_test_setup(client, with_super_admin_user) + modified_process_model_identifier = process_model_identifier.replace("/", ":") + response = client.get( - f"/v1.0/process-models/{test_process_group_id}/{process_model_dir_name}/files/hello_world.bpmn", + f"/v1.0/process-models/{modified_process_model_identifier}/files/random_fact.bpmn", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 200 assert response.json is not None - assert response.json["name"] == "hello_world.bpmn" - assert response.json["process_group_id"] == "group_id1" - assert response.json["process_model_id"] == "hello_world" + assert response.json["name"] == "random_fact.bpmn" + assert response.json["process_model_id"] == "test_group/random_fact" def test_get_workflow_from_workflow_spec( self, @@ -730,15 +817,16 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_get_workflow_from_workflow_spec.""" - process_model = load_test_spec("hello_world") + process_model_identifier = self.basic_test_setup(client, with_super_admin_user) + modified_process_model_identifier = process_model_identifier.replace("/", ":") + response = client.post( - f"/v1.0/process-models/{process_model.process_group_id}/{process_model.id}/process-instances", + f"/v1.0/process-models/{modified_process_model_identifier}/process-instances", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 201 assert response.json is not None - assert "hello_world" == response.json["process_model_identifier"] - # assert('Task_GetName' == response.json['next_task']['name']) + assert "test_group/random_fact" == response.json["process_model_identifier"] def test_get_process_groups_when_none( self, @@ -764,7 +852,7 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_get_process_groups_when_there_are_some.""" - load_test_spec("hello_world") + self.basic_test_setup(client, with_super_admin_user) response = client.get( "/v1.0/process-groups", headers=self.logged_in_headers(with_super_admin_user), @@ -784,18 +872,18 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_get_process_group_when_found.""" - test_process_group_id = "group_id1" - process_model_dir_name = "hello_world" - load_test_spec(process_model_dir_name, process_group_id=test_process_group_id) + process_model_identifier = self.basic_test_setup(client, with_super_admin_user) + process_group_id, process_model_id = os.path.split(process_model_identifier) + response = client.get( - f"/v1.0/process-groups/{test_process_group_id}", + f"/v1.0/process-groups/{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 - assert response.json["process_models"][0]["id"] == process_model_dir_name + assert response.json["id"] == process_group_id + assert response.json["process_models"][0]["id"] == process_model_identifier def test_get_process_model_when_found( self, @@ -805,18 +893,20 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_get_process_model_when_found.""" - test_process_group_id = "group_id1" - process_model_dir_name = "hello_world" - load_test_spec(process_model_dir_name, process_group_id=test_process_group_id) + process_model_identifier = self.basic_test_setup( + client, with_super_admin_user, bpmn_file_name="random_fact.bpmn" + ) + modified_process_model_identifier = process_model_identifier.replace("/", ":") + response = client.get( - f"/v1.0/process-models/{test_process_group_id}/{process_model_dir_name}", + f"/v1.0/process-models/{modified_process_model_identifier}", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 200 assert response.json is not None - assert response.json["id"] == process_model_dir_name + assert response.json["id"] == process_model_identifier assert len(response.json["files"]) == 1 - assert response.json["files"][0]["name"] == "hello_world.bpmn" + assert response.json["files"][0]["name"] == "random_fact.bpmn" def test_get_process_model_when_not_found( self, @@ -828,8 +918,10 @@ class TestProcessApi(BaseTest): """Test_get_process_model_when_not_found.""" process_model_dir_name = "THIS_NO_EXISTS" group_id = self.create_process_group(client, with_super_admin_user, "my_group") + bad_process_model_id = f"{group_id}/{process_model_dir_name}" + modified_bad_process_model_id = bad_process_model_id.replace("/", ":") response = client.get( - f"/v1.0/process-models/{group_id}/{process_model_dir_name}", + f"/v1.0/process-models/{modified_bad_process_model_id}", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 400 @@ -844,12 +936,9 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_instance_create.""" - test_process_group_id = "runs_without_input" - test_process_model_id = "sample" + test_process_model_id = "runs_without_input/sample" headers = self.logged_in_headers(with_super_admin_user) - response = self.create_process_instance( - client, test_process_group_id, test_process_model_id, headers - ) + response = self.create_process_instance(client, test_process_model_id, headers) assert response.json is not None assert response.json["updated_at_in_seconds"] is not None assert response.json["status"] == "not_started" @@ -866,16 +955,24 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_instance_run.""" - process_group_id = "runs_without_input" - process_model_id = "sample" + # process_model_id = "runs_without_input/sample" + process_model_identifier = self.basic_test_setup( + client=client, + user=with_super_admin_user, + process_group_id="runs_without_input", + process_model_id="sample", + bpmn_file_name=None, + bpmn_file_location="sample", + ) + headers = self.logged_in_headers(with_super_admin_user) response = self.create_process_instance( - client, process_group_id, process_model_id, headers + client, process_model_identifier, 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", + f"/v1.0/process-instances/{process_instance_id}/run", headers=self.logged_in_headers(with_super_admin_user), ) @@ -883,7 +980,7 @@ class TestProcessApi(BaseTest): assert type(response.json["updated_at_in_seconds"]) is int assert response.json["updated_at_in_seconds"] > 0 assert response.json["status"] == "complete" - assert response.json["process_model_identifier"] == process_model_id + assert response.json["process_model_identifier"] == process_model_identifier assert ( response.json["data"]["current_user"]["username"] == with_super_admin_user.username @@ -901,23 +998,32 @@ class TestProcessApi(BaseTest): """Test_process_instance_show.""" process_group_id = "simple_script" process_model_id = "simple_script" + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + ) + modified_process_model_identifier = process_model_identifier.replace("/", ":") headers = self.logged_in_headers(with_super_admin_user) create_response = self.create_process_instance( - client, process_group_id, process_model_id, headers + client, process_model_identifier, headers ) assert create_response.json is not None process_instance_id = create_response.json["id"] client.post( - f"/v1.0/process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}/run", + f"/v1.0/process-instances/{process_instance_id}/run", headers=self.logged_in_headers(with_super_admin_user), ) show_response = client.get( - f"/v1.0/process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}", + f"/v1.0/process-models/{modified_process_model_identifier}/process-instances/{process_instance_id}", headers=self.logged_in_headers(with_super_admin_user), ) assert show_response.json is not None file_system_root = FileSystemService.root_path() - file_path = f"{file_system_root}/{process_group_id}/{process_model_id}/{process_model_id}.bpmn" + file_path = ( + f"{file_system_root}/{process_model_identifier}/{process_model_id}.bpmn" + ) with open(file_path) as f_open: xml_file_contents = f_open.read() assert show_response.json["bpmn_xml_file_contents"] == xml_file_contents @@ -930,12 +1036,20 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_message_start_when_starting_process_instance.""" - # ensure process model is loaded in db - load_test_spec( - "message_receiver", - process_model_source_directory="message_send_one_conversation", - bpmn_file_name="message_receiver", + # ensure process model is loaded + process_group_id = "test_message_start" + process_model_id = "message_receiver" + bpmn_file_name = "message_receiver.bpmn" + bpmn_file_location = "message_send_one_conversation" + self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, ) + message_model_identifier = "message_send" payload = { "topica": "the_topica_string", @@ -971,11 +1085,19 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_message_start_when_providing_message_to_running_process_instance.""" - process_model = load_test_spec( - "message_sender", - process_model_source_directory="message_send_one_conversation", - bpmn_file_name="message_sender", + process_group_id = "test_message_start" + process_model_id = "message_sender" + bpmn_file_name = "message_sender.bpmn" + bpmn_file_location = "message_send_one_conversation" + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, ) + message_model_identifier = "message_response" payload = { "the_payload": { @@ -986,16 +1108,14 @@ class TestProcessApi(BaseTest): } response = self.create_process_instance( client, - process_model.process_group_id, - process_model.id, + process_model_identifier, self.logged_in_headers(with_super_admin_user), ) assert response.json is not None process_instance_id = response.json["id"] response = client.post( - f"/v1.0/process-models/{process_model.process_group_id}/" - f"{process_model.id}/process-instances/{process_instance_id}/run", + f"/v1.0/process-instances/{process_instance_id}/run", headers=self.logged_in_headers(with_super_admin_user), ) @@ -1033,31 +1153,36 @@ class TestProcessApi(BaseTest): ) -> None: """Test_message_start_when_providing_message_to_running_process_instance.""" # this task will wait on a catch event - process_model = load_test_spec( - "message_sender", - process_model_source_directory="message_send_one_conversation", - bpmn_file_name="message_sender", + process_group_id = "test_message_start" + process_model_id = "message_sender" + bpmn_file_name = "message_sender.bpmn" + bpmn_file_location = "message_send_one_conversation" + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, ) + response = self.create_process_instance( client, - process_model.process_group_id, - process_model.id, + process_model_identifier, self.logged_in_headers(with_super_admin_user), ) assert response.json is not None process_instance_id = response.json["id"] response = client.post( - f"/v1.0/process-models/{process_model.process_group_id}/" - f"{process_model.id}/process-instances/{process_instance_id}/run", + f"/v1.0/process-instances/{process_instance_id}/run", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 200 assert response.json is not None response = client.post( - f"/v1.0/process-models/{process_model.process_group_id}/" - f"{process_model.id}/process-instances/{process_instance_id}/terminate", + f"/v1.0/process-instances/{process_instance_id}/terminate", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 200 @@ -1079,22 +1204,32 @@ class TestProcessApi(BaseTest): """Test_process_instance_delete.""" process_group_id = "my_process_group" process_model_id = "user_task" + bpmn_file_name = "user_task.bpmn" + bpmn_file_location = "user_task" + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, + ) headers = self.logged_in_headers(with_super_admin_user) response = self.create_process_instance( - client, process_group_id, process_model_id, headers + client, process_model_identifier, 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", + f"/v1.0/process-instances/{process_instance_id}/run", headers=self.logged_in_headers(with_super_admin_user), ) assert response.json is not None delete_response = client.delete( - f"/v1.0/process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}", + f"/v1.0/process-instances/{process_instance_id}", headers=self.logged_in_headers(with_super_admin_user), ) assert delete_response.status_code == 200 @@ -1109,21 +1244,30 @@ class TestProcessApi(BaseTest): """Test_process_instance_run_user_task.""" process_group_id = "my_process_group" process_model_id = "dynamic_enum_select_fields" + bpmn_file_location = "dynamic_enum_select_fields" + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + # bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, + ) headers = self.logged_in_headers(with_super_admin_user) response = self.create_process_instance( - client, process_group_id, process_model_id, headers + client, process_model_identifier, 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", + f"/v1.0/process-instances/{process_instance_id}/run", headers=self.logged_in_headers(with_super_admin_user), ) assert response.json is not None - # assert response.json['next_task'] is not None + assert response.json["next_task"] is not None active_tasks = ( db.session.query(ActiveTaskModel) @@ -1150,13 +1294,20 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_instance_list_with_default_list.""" - test_process_group_id = "runs_without_input" - process_model_dir_name = "sample" - headers = self.logged_in_headers(with_super_admin_user) - self.create_process_instance( - client, test_process_group_id, process_model_dir_name, headers + process_group_id = "runs_without_input" + process_model_id = "sample" + bpmn_file_location = "sample" + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_location=bpmn_file_location, ) + headers = self.logged_in_headers(with_super_admin_user) + self.create_process_instance(client, process_model_identifier, headers) + response = client.get( "/v1.0/process-instances", headers=self.logged_in_headers(with_super_admin_user), @@ -1171,10 +1322,8 @@ class TestProcessApi(BaseTest): process_instance_dict = response.json["results"][0] assert type(process_instance_dict["id"]) is int assert ( - process_instance_dict["process_model_identifier"] == process_model_dir_name - ) - assert ( - process_instance_dict["process_group_identifier"] == test_process_group_id + process_instance_dict["process_model_identifier"] + == process_model_identifier ) assert type(process_instance_dict["start_in_seconds"]) is int assert process_instance_dict["start_in_seconds"] > 0 @@ -1189,24 +1338,24 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_instance_list_with_paginated_items.""" - test_process_group_id = "runs_without_input" - process_model_dir_name = "sample" + process_group_id = "runs_without_input" + process_model_id = "sample" + bpmn_file_name = "sample.bpmn" + bpmn_file_location = "sample" + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, + ) headers = self.logged_in_headers(with_super_admin_user) - self.create_process_instance( - client, test_process_group_id, process_model_dir_name, headers - ) - self.create_process_instance( - client, test_process_group_id, process_model_dir_name, headers - ) - self.create_process_instance( - client, test_process_group_id, process_model_dir_name, headers - ) - self.create_process_instance( - client, test_process_group_id, process_model_dir_name, headers - ) - self.create_process_instance( - client, test_process_group_id, process_model_dir_name, headers - ) + self.create_process_instance(client, process_model_identifier, headers) + self.create_process_instance(client, process_model_identifier, headers) + self.create_process_instance(client, process_model_identifier, headers) + self.create_process_instance(client, process_model_identifier, headers) + self.create_process_instance(client, process_model_identifier, headers) response = client.get( "/v1.0/process-instances?per_page=2&page=3", @@ -1238,9 +1387,18 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_instance_list_filter.""" - test_process_group_id = "runs_without_input" - test_process_model_id = "sample" - load_test_spec(test_process_model_id, process_group_id=test_process_group_id) + process_group_id = "runs_without_input" + process_model_id = "sample" + bpmn_file_name = "sample.bpmn" + bpmn_file_location = "sample" + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, + ) statuses = [status.value for status in ProcessInstanceStatus] # create 5 instances with different status, and different start_in_seconds/end_in_seconds @@ -1248,8 +1406,8 @@ class TestProcessApi(BaseTest): process_instance = ProcessInstanceModel( status=ProcessInstanceStatus[statuses[i]].value, process_initiator=with_super_admin_user, - process_model_identifier=test_process_model_id, - process_group_identifier=test_process_group_id, + process_model_identifier=process_model_identifier, + process_group_identifier="test_process_group_id", updated_at_in_seconds=round(time.time()), start_in_seconds=(1000 * i) + 1000, end_in_seconds=(1000 * i) + 2000, @@ -1260,7 +1418,7 @@ class TestProcessApi(BaseTest): # Without filtering we should get all 5 instances response = client.get( - f"/v1.0/process-instances?process_group_identifier={test_process_group_id}&process_model_identifier={test_process_model_id}", + f"/v1.0/process-instances?process_model_identifier={process_model_identifier}", headers=self.logged_in_headers(with_super_admin_user), ) assert response.json is not None @@ -1271,7 +1429,7 @@ class TestProcessApi(BaseTest): # we should get 1 instance each time for i in range(5): response = client.get( - f"/v1.0/process-instances?process_status={ProcessInstanceStatus[statuses[i]].value}&process_group_identifier={test_process_group_id}&process_model_identifier={test_process_model_id}", + f"/v1.0/process-instances?process_status={ProcessInstanceStatus[statuses[i]].value}&process_model_identifier={process_model_identifier}", headers=self.logged_in_headers(with_super_admin_user), ) assert response.json is not None @@ -1280,7 +1438,7 @@ class TestProcessApi(BaseTest): assert results[0]["status"] == ProcessInstanceStatus[statuses[i]].value response = client.get( - f"/v1.0/process-instances?process_status=not_started,complete&process_group_identifier={test_process_group_id}&process_model_identifier={test_process_model_id}", + f"/v1.0/process-instances?process_status=not_started,complete&process_model_identifier={process_model_identifier}", headers=self.logged_in_headers(with_super_admin_user), ) assert response.json is not None @@ -1342,23 +1500,29 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_process_instance_report_list.""" - process_group_identifier = "runs_without_input" - process_model_identifier = "sample" - self.logged_in_headers(with_super_admin_user) - load_test_spec( - process_model_identifier, process_group_id=process_group_identifier + process_group_id = "runs_without_input" + process_model_id = "sample" + bpmn_file_name = "sample.bpmn" + bpmn_file_location = "sample" + process_model_identifier = self.basic_test_setup( # noqa: F841 + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, ) + self.logged_in_headers(with_super_admin_user) + report_identifier = "testreport" report_metadata = {"order_by": ["month"]} ProcessInstanceReportModel.create_with_attributes( identifier=report_identifier, - process_group_identifier=process_group_identifier, - process_model_identifier=process_model_identifier, report_metadata=report_metadata, user=with_super_admin_user, ) response = client.get( - f"/v1.0/process-models/{process_group_identifier}/{process_model_identifier}/process-instances/reports", + "/v1.0/process-instances/reports", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 200 @@ -1376,8 +1540,19 @@ class TestProcessApi(BaseTest): setup_process_instances_for_reports: list[ProcessInstanceModel], ) -> None: """Test_process_instance_report_show_with_default_list.""" - test_process_group_id = "runs_without_input" - process_model_dir_name = "sample" + process_group_id = "runs_without_input" + process_model_id = "sample" + # bpmn_file_name = "sample.bpmn" + # bpmn_file_location = "sample" + # process_model_identifier = self.basic_test_setup( + # client, + # with_super_admin_user, + # process_group_id=process_group_id, + # process_model_id=process_model_id, + # bpmn_file_name=bpmn_file_name, + # bpmn_file_location=bpmn_file_location + # ) + process_model_identifier = f"{process_group_id}/{process_model_id}" report_metadata = { "columns": [ @@ -1400,14 +1575,12 @@ class TestProcessApi(BaseTest): ProcessInstanceReportModel.create_with_attributes( identifier="sure", - process_group_identifier=test_process_group_id, - process_model_identifier=process_model_dir_name, report_metadata=report_metadata, user=with_super_admin_user, ) response = client.get( - f"/v1.0/process-models/{test_process_group_id}/{process_model_dir_name}/process-instances/reports/sure", + "/v1.0/process-instances/reports/sure", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 200 @@ -1420,10 +1593,8 @@ class TestProcessApi(BaseTest): process_instance_dict = response.json["results"][0] assert type(process_instance_dict["id"]) is int assert ( - process_instance_dict["process_model_identifier"] == process_model_dir_name - ) - assert ( - process_instance_dict["process_group_identifier"] == test_process_group_id + process_instance_dict["process_model_identifier"] + == process_model_identifier ) assert type(process_instance_dict["start_in_seconds"]) is int assert process_instance_dict["start_in_seconds"] > 0 @@ -1438,9 +1609,6 @@ class TestProcessApi(BaseTest): setup_process_instances_for_reports: list[ProcessInstanceModel], ) -> None: """Test_process_instance_report_show_with_default_list.""" - test_process_group_id = "runs_without_input" - process_model_dir_name = "sample" - report_metadata = { "filter_by": [ { @@ -1453,14 +1621,12 @@ class TestProcessApi(BaseTest): ProcessInstanceReportModel.create_with_attributes( identifier="sure", - process_group_identifier=test_process_group_id, - process_model_identifier=process_model_dir_name, report_metadata=report_metadata, user=with_super_admin_user, ) response = client.get( - f"/v1.0/process-models/{test_process_group_id}/{process_model_dir_name}/process-instances/reports/sure?grade_level=1", + "/v1.0/process-instances/reports/sure?grade_level=1", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 200 @@ -1476,11 +1642,8 @@ class TestProcessApi(BaseTest): setup_process_instances_for_reports: list[ProcessInstanceModel], ) -> None: """Test_process_instance_report_show_with_default_list.""" - test_process_group_id = "runs_without_input" - process_model_dir_name = "sample" - response = client.get( - f"/v1.0/process-models/{test_process_group_id}/{process_model_dir_name}/process-instances/reports/sure?grade_level=1", + "/v1.0/process-instances/reports/sure?grade_level=1", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 404 @@ -1490,15 +1653,12 @@ class TestProcessApi(BaseTest): def setup_testing_instance( self, client: FlaskClient, - process_group_id: str, process_model_id: str, with_super_admin_user: UserModel, ) -> Any: """Setup_testing_instance.""" headers = self.logged_in_headers(with_super_admin_user) - response = self.create_process_instance( - client, process_group_id, process_model_id, headers - ) + response = self.create_process_instance(client, process_model_id, headers) process_instance = response.json assert isinstance(process_instance, dict) process_instance_id = process_instance["id"] @@ -1514,9 +1674,19 @@ class TestProcessApi(BaseTest): """Test_error_handler.""" process_group_id = "data" process_model_id = "error" + bpmn_file_name = "error.bpmn" + bpmn_file_location = "error" + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, + ) process_instance_id = self.setup_testing_instance( - client, process_group_id, process_model_id, with_super_admin_user + client, process_model_identifier, with_super_admin_user ) process = ( @@ -1528,7 +1698,7 @@ class TestProcessApi(BaseTest): assert process.status == "not_started" response = client.post( - f"/v1.0/process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}/run", + f"/v1.0/process-instances/{process_instance_id}/run", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 400 @@ -1558,12 +1728,22 @@ class TestProcessApi(BaseTest): """Test_error_handler_suspend.""" process_group_id = "data" process_model_id = "error" + bpmn_file_name = "error.bpmn" + bpmn_file_location = "error" + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, + ) process_instance_id = self.setup_testing_instance( - client, process_group_id, process_model_id, with_super_admin_user + client, process_model_identifier, with_super_admin_user ) process_model = ProcessModelService().get_process_model( - process_model_id, process_group_id + process_model_identifier ) ProcessModelService().update_spec( process_model, @@ -1579,7 +1759,7 @@ class TestProcessApi(BaseTest): assert process.status == "not_started" response = client.post( - f"/v1.0/process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}/run", + f"/v1.0/process-instances/{process_instance_id}/run", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 400 @@ -1602,13 +1782,23 @@ class TestProcessApi(BaseTest): """Test_error_handler.""" process_group_id = "data" process_model_id = "error" + bpmn_file_name = "error.bpmn" + bpmn_file_location = "error" + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, + ) process_instance_id = self.setup_testing_instance( - client, process_group_id, process_model_id, with_super_admin_user + client, process_model_identifier, with_super_admin_user ) process_model = ProcessModelService().get_process_model( - process_model_id, process_group_id + process_model_identifier ) ProcessModelService().update_spec( process_model, @@ -1619,7 +1809,7 @@ class TestProcessApi(BaseTest): with mail.record_messages() as outbox: response = client.post( - f"/v1.0/process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}/run", + f"/v1.0/process-instances/{process_instance_id}/run", headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 400 @@ -1651,17 +1841,26 @@ class TestProcessApi(BaseTest): process_model_id = "hello_world" file_name = "hello_world.svg" file_data = b"abc123" + bpmn_file_name = "hello_world.bpmn" + bpmn_file_location = "hello_world" + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, + ) result = self.create_spec_file( client, - process_group_id=process_group_id, - process_model_id=process_model_id, + process_model_id=process_model_identifier, file_name=file_name, file_data=file_data, user=with_super_admin_user, ) - assert result["process_group_id"] == process_group_id - assert result["process_model_id"] == process_model_id + + assert result["process_model_id"] == process_model_identifier assert result["name"] == file_name assert bytes(str(result["file_contents"]), "utf-8") == file_data @@ -1673,11 +1872,23 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_can_get_message_instances_by_process_instance_id.""" - load_test_spec( - "message_receiver", - process_model_source_directory="message_send_one_conversation", - bpmn_file_name="message_receiver", + process_group_id = "test_message_start" + process_model_id = "message_receiver" + bpmn_file_name = "message_receiver.bpmn" + bpmn_file_location = "message_send_one_conversation" + self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, ) + # load_test_spec( + # "message_receiver", + # process_model_source_directory="message_send_one_conversation", + # bpmn_file_name="message_receiver", + # ) message_model_identifier = "message_send" payload = { "topica": "the_topica_string", @@ -1753,16 +1964,29 @@ class TestProcessApi(BaseTest): finance_group = GroupModel.query.filter_by(identifier="Finance Team").first() assert finance_group is not None - process_model = load_test_spec( - process_model_id="model_with_lanes", - bpmn_file_name="lanes.bpmn", - process_group_id="finance", + process_group_id = "finance" + process_model_id = "model_with_lanes" + bpmn_file_name = "lanes.bpmn" + bpmn_file_location = "model_with_lanes" + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, ) + # process_model = load_test_spec( + # process_model_id="model_with_lanes", + # bpmn_file_name="lanes.bpmn", + # process_group_id="finance", + # ) + response = self.create_process_instance( client, - process_model.process_group_id, - process_model.id, + # process_model.process_group_id, + process_model_identifier, headers=self.logged_in_headers(initiator_user), ) assert response.status_code == 201 @@ -1770,7 +1994,7 @@ class TestProcessApi(BaseTest): assert response.json is not None process_instance_id = response.json["id"] response = client.post( - f"/v1.0/process-models/{process_model.process_group_id}/{process_model.id}/process-instances/{process_instance_id}/run", + f"/v1.0/process-instances/{process_instance_id}/run", headers=self.logged_in_headers(initiator_user), ) assert response.status_code == 200 @@ -1936,3 +2160,127 @@ class TestProcessApi(BaseTest): # assert "pagingInfo" in rpc_json_data["result"] # # print("get_waku_messages") + + def test_process_instance_suspend( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Test_process_instance_suspend.""" + bpmn_file_name = "manual_task.bpmn" + bpmn_file_location = "manual_task" + process_model_identifier = self.basic_test_setup( + client=client, + user=with_super_admin_user, + process_model_id="manual_task", + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, + ) + + bpmn_file_data_bytes = self.get_test_data_file_contents( + bpmn_file_name, bpmn_file_location + ) + self.create_spec_file( + client=client, + process_model_id=process_model_identifier, + process_model_location=process_model_identifier, + file_name=bpmn_file_name, + file_data=bpmn_file_data_bytes, + user=with_super_admin_user, + ) + + headers = self.logged_in_headers(with_super_admin_user) + response = self.create_process_instance( + client, process_model_identifier, headers + ) + assert response.json is not None + process_instance_id = response.json["id"] + + client.post( + f"/v1.0/process-instances/{process_instance_id}/run", + headers=self.logged_in_headers(with_super_admin_user), + ) + + process_instance = ProcessInstanceService().get_process_instance( + process_instance_id + ) + assert process_instance.status == "user_input_required" + + client.post( + f"/v1.0/process-instances/{process_instance_id}/suspend", + headers=self.logged_in_headers(with_super_admin_user), + ) + process_instance = ProcessInstanceService().get_process_instance( + process_instance_id + ) + assert process_instance.status == "suspended" + + # TODO: Why can I run a suspended process instance? + response = client.post( + f"/v1.0/process-instances/{process_instance_id}/run", + headers=self.logged_in_headers(with_super_admin_user), + ) + + # task = response.json['next_task'] + + print("test_process_instance_suspend") + + def test_script_unit_test_run( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Test_script_unit_test_run.""" + process_group_id = "test_group" + process_model_id = "simple_script" + bpmn_file_name = "simple_script.bpmn" + bpmn_file_location = "simple_script" + process_model_identifier = self.basic_test_setup( + client=client, + user=with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, + ) + + bpmn_file_data_bytes = self.get_test_data_file_contents( + bpmn_file_name, bpmn_file_location + ) + self.create_spec_file( + client=client, + process_model_id=process_model_identifier, + process_model_location=process_model_identifier, + file_name=bpmn_file_name, + file_data=bpmn_file_data_bytes, + user=with_super_admin_user, + ) + + # python_script = _get_required_parameter_or_raise("python_script", body) + # input_json = _get_required_parameter_or_raise("input_json", body) + # expected_output_json = _get_required_parameter_or_raise( + # "expected_output_json", body + # ) + python_script = "c = a + b" + input_json = {"a": 1, "b": 2} + expected_output_json = {"a": 1, "b": 2, "c": 3} + # bpmn_task_identifier = "Activity_CalculateNewData" + + data = { + "python_script": python_script, + "input_json": input_json, + "expected_output_json": expected_output_json, + } + + response = client.post( # noqa: F841 + f"/v1.0/process-models/{process_group_id}/{process_model_id}/script-unit-tests/run", + headers=self.logged_in_headers(with_super_admin_user), + content_type="application/json", + data=json.dumps(data), + ) + + print("test_script_unit_test_run") diff --git a/tests/spiffworkflow_backend/integration/test_secret_service.py b/tests/spiffworkflow_backend/integration/test_secret_service.py index 3735ebc5..071ef6cc 100644 --- a/tests/spiffworkflow_backend/integration/test_secret_service.py +++ b/tests/spiffworkflow_backend/integration/test_secret_service.py @@ -42,16 +42,18 @@ class SecretServiceTestHelpers(BaseTest): self.test_process_group_id, display_name=self.test_process_group_display_name, ) + process_model_identifier = ( + f"{self.test_process_group_id}/{self.test_process_model_id}" + ) self.create_process_model_with_api( client, - process_group_id=self.test_process_group_id, - process_model_id=self.test_process_model_id, + process_model_id=process_model_identifier, process_model_display_name=self.test_process_model_display_name, process_model_description=self.test_process_model_description, user=user, ) process_model_info = ProcessModelService().get_process_model( - self.test_process_model_id, self.test_process_group_id + process_model_identifier ) return process_model_info diff --git a/tests/spiffworkflow_backend/scripts/test_get_group_members.py b/tests/spiffworkflow_backend/scripts/test_get_group_members.py index 34a144db..8a6046b5 100644 --- a/tests/spiffworkflow_backend/scripts/test_get_group_members.py +++ b/tests/spiffworkflow_backend/scripts/test_get_group_members.py @@ -1,10 +1,12 @@ """Test_get_localtime.""" from flask.app import Flask +from flask.testing import FlaskClient from flask_bpmn.models.db import db from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from spiffworkflow_backend.models.group import GroupModel +from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, ) @@ -17,7 +19,9 @@ class TestGetGroupMembers(BaseTest): def test_can_get_members_of_a_group( self, app: Flask, + client: FlaskClient, with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_get_members_of_a_group.""" initiator_user = self.find_or_create_user("initiator_user") @@ -34,9 +38,13 @@ class TestGetGroupMembers(BaseTest): UserService.add_user_to_group(testuser2, group_a) UserService.add_user_to_group(testuser3, group_b) + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) process_model = load_test_spec( - process_model_id="get_group_members", + process_model_id="test_group/get_group_members", bpmn_file_name="get_group_members.bpmn", + process_model_source_directory="get_group_members", ) process_instance = self.create_process_instance_from_process_model( process_model=process_model, user=initiator_user diff --git a/tests/spiffworkflow_backend/scripts/test_get_localtime.py b/tests/spiffworkflow_backend/scripts/test_get_localtime.py index e3d1e50c..9e65b970 100644 --- a/tests/spiffworkflow_backend/scripts/test_get_localtime.py +++ b/tests/spiffworkflow_backend/scripts/test_get_localtime.py @@ -49,8 +49,18 @@ class TestGetLocaltime(BaseTest): ) -> None: """Test_process_instance_run.""" initiator_user = self.find_or_create_user("initiator_user") + self.add_permissions_to_user( + initiator_user, + target_uri="/v1.0/process-groups", + permission_names=["read", "create"], + ) + self.create_process_group( + client=client, user=initiator_user, process_group_id="test_group" + ) process_model = load_test_spec( - process_model_id="get_localtime", bpmn_file_name="get_localtime.bpmn" + process_model_id="test_group/get_localtime", + bpmn_file_name="get_localtime.bpmn", + process_model_source_directory="get_localtime", ) process_instance = self.create_process_instance_from_process_model( process_model=process_model, user=initiator_user diff --git a/tests/spiffworkflow_backend/unit/test_authorization_service.py b/tests/spiffworkflow_backend/unit/test_authorization_service.py index ff2ac9bc..5d0a10be 100644 --- a/tests/spiffworkflow_backend/unit/test_authorization_service.py +++ b/tests/spiffworkflow_backend/unit/test_authorization_service.py @@ -1,9 +1,10 @@ """Test_message_service.""" import pytest from flask import Flask +from flask.testing import FlaskClient from tests.spiffworkflow_backend.helpers.base_test import BaseTest -from tests.spiffworkflow_backend.helpers.test_data import load_test_spec +from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user import UserNotFoundError from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.process_instance_processor import ( @@ -12,6 +13,7 @@ from spiffworkflow_backend.services.process_instance_processor import ( from spiffworkflow_backend.services.process_instance_service import ( ProcessInstanceService, ) +from spiffworkflow_backend.services.process_model_service import ProcessModelService class TestAuthorizationService(BaseTest): @@ -89,7 +91,11 @@ class TestAuthorizationService(BaseTest): ) def test_user_can_be_added_to_active_task_on_first_login( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_user_can_be_added_to_active_task_on_first_login.""" initiator_user = self.find_or_create_user("initiator_user") @@ -98,8 +104,17 @@ class TestAuthorizationService(BaseTest): self.find_or_create_user("testuser1") AuthorizationService.import_permissions_from_yaml_file() - process_model = load_test_spec( - process_model_id="model_with_lanes", bpmn_file_name="lanes.bpmn" + process_model_identifier = self.basic_test_setup( + client=client, + user=with_super_admin_user, + process_group_id="test_group", + process_model_id="model_with_lanes", + bpmn_file_name="lanes.bpmn", + bpmn_file_location="model_with_lanes", + ) + + process_model = ProcessModelService().get_process_model( + process_model_id=process_model_identifier ) process_instance = self.create_process_instance_from_process_model( process_model=process_model, user=initiator_user diff --git a/tests/spiffworkflow_backend/unit/test_dot_notation.py b/tests/spiffworkflow_backend/unit/test_dot_notation.py index 4446d4d9..ff37c3b5 100644 --- a/tests/spiffworkflow_backend/unit/test_dot_notation.py +++ b/tests/spiffworkflow_backend/unit/test_dot_notation.py @@ -1,8 +1,9 @@ """Test_various_bpmn_constructs.""" from flask.app import Flask +from flask.testing import FlaskClient from tests.spiffworkflow_backend.helpers.base_test import BaseTest -from tests.spiffworkflow_backend.helpers.test_data import load_test_spec +from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, ) @@ -15,21 +16,36 @@ class TestDotNotation(BaseTest): """TestVariousBpmnConstructs.""" def test_dot_notation( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_form_data_conversion_to_dot_dict.""" - process_model = load_test_spec( - "test_dot_notation", - bpmn_file_name="diagram.bpmn", - process_model_source_directory="dot_notation", + process_group_id = "dot_notation_group" + process_model_id = "test_dot_notation" + bpmn_file_name = "diagram.bpmn" + bpmn_file_location = "dot_notation" + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, ) - current_user = self.find_or_create_user() - process_instance = self.create_process_instance_from_process_model( - process_model + headers = self.logged_in_headers(with_super_admin_user) + response = self.create_process_instance( + client, process_model_identifier, headers ) + process_instance_id = response.json["id"] + process_instance = ProcessInstanceService().get_process_instance( + process_instance_id + ) + processor = ProcessInstanceProcessor(process_instance) - processor.do_engine_steps(save=True) user_task = processor.get_ready_user_tasks()[0] @@ -41,7 +57,7 @@ class TestDotNotation(BaseTest): "invoice.dueDate": "09/30/2022", } ProcessInstanceService.complete_form_task( - processor, user_task, form_data, current_user + processor, user_task, form_data, with_super_admin_user ) expected = { diff --git a/tests/spiffworkflow_backend/unit/test_message_instance.py b/tests/spiffworkflow_backend/unit/test_message_instance.py index 842d5ff4..39b37f2c 100644 --- a/tests/spiffworkflow_backend/unit/test_message_instance.py +++ b/tests/spiffworkflow_backend/unit/test_message_instance.py @@ -1,24 +1,52 @@ """Test_message_instance.""" import pytest from flask import Flask +from flask.testing import FlaskClient from flask_bpmn.models.db import db from tests.spiffworkflow_backend.helpers.base_test import BaseTest -from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from spiffworkflow_backend.models.message_instance import MessageInstanceModel from spiffworkflow_backend.models.message_model import MessageModel +from spiffworkflow_backend.models.user import UserModel +from spiffworkflow_backend.services.process_model_service import ProcessModelService class TestMessageInstance(BaseTest): """TestMessageInstance.""" + def setup_message_tests(self, client: FlaskClient, user: UserModel) -> str: + """Setup_message_tests.""" + process_group_id = "test_group" + process_model_id = "hello_world" + bpmn_file_name = "hello_world.bpmn" + bpmn_file_location = "hello_world" + process_model_identifier = self.basic_test_setup( + client, + user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, + ) + return process_model_identifier + def test_can_create_message_instance( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_create_message_instance.""" message_model_identifier = "message_model_one" message_model = self.create_message_model(message_model_identifier) - process_model = load_test_spec("hello_world") + process_model_identifier = self.setup_message_tests( + client, with_super_admin_user + ) + + process_model = ProcessModelService().get_process_model( + process_model_id=process_model_identifier + ) process_instance = self.create_process_instance_from_process_model( process_model, "waiting" ) @@ -40,12 +68,22 @@ class TestMessageInstance(BaseTest): assert queued_message_from_query is not None def test_cannot_set_invalid_status( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_cannot_set_invalid_status.""" message_model_identifier = "message_model_one" message_model = self.create_message_model(message_model_identifier) - process_model = load_test_spec("hello_world") + process_model_identifier = self.setup_message_tests( + client, with_super_admin_user + ) + + process_model = ProcessModelService().get_process_model( + process_model_id=process_model_identifier + ) process_instance = self.create_process_instance_from_process_model( process_model, "waiting" ) @@ -76,12 +114,22 @@ class TestMessageInstance(BaseTest): ) def test_cannot_set_invalid_message_type( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_cannot_set_invalid_message_type.""" message_model_identifier = "message_model_one" message_model = self.create_message_model(message_model_identifier) - process_model = load_test_spec("hello_world") + process_model_identifier = self.setup_message_tests( + client, with_super_admin_user + ) + + process_model = ProcessModelService().get_process_model( + process_model_id=process_model_identifier + ) process_instance = self.create_process_instance_from_process_model( process_model, "waiting" ) @@ -113,12 +161,22 @@ class TestMessageInstance(BaseTest): ) def test_force_failure_cause_if_status_is_failure( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_force_failure_cause_if_status_is_failure.""" message_model_identifier = "message_model_one" message_model = self.create_message_model(message_model_identifier) - process_model = load_test_spec("hello_world") + process_model_identifier = self.setup_message_tests( + client, with_super_admin_user + ) + + process_model = ProcessModelService().get_process_model( + process_model_id=process_model_identifier + ) process_instance = self.create_process_instance_from_process_model( process_model, "waiting" ) @@ -154,7 +212,8 @@ class TestMessageInstance(BaseTest): assert queued_message.id is not None assert queued_message.failure_cause == "THIS TEST FAILURE" - def create_message_model(self, message_model_identifier: str) -> MessageModel: + @staticmethod + def create_message_model(message_model_identifier: str) -> MessageModel: """Create_message_model.""" message_model = MessageModel(identifier=message_model_identifier) db.session.add(message_model) diff --git a/tests/spiffworkflow_backend/unit/test_message_service.py b/tests/spiffworkflow_backend/unit/test_message_service.py index 38079c96..aa1f2805 100644 --- a/tests/spiffworkflow_backend/unit/test_message_service.py +++ b/tests/spiffworkflow_backend/unit/test_message_service.py @@ -1,5 +1,6 @@ """Test_message_service.""" from flask import Flask +from flask.testing import FlaskClient from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec @@ -9,6 +10,7 @@ from spiffworkflow_backend.models.message_correlation_message_instance import ( ) from spiffworkflow_backend.models.message_instance import MessageInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModel +from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.message_service import MessageService from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, @@ -22,25 +24,32 @@ class TestMessageService(BaseTest): """TestMessageService.""" def test_can_send_message_to_waiting_message( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_send_message_to_waiting_message.""" - process_model_sender = load_test_spec( - "message_sender", - process_model_source_directory="message_send_one_conversation", - bpmn_file_name="message_sender", + process_group_id = "test_group" + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_id ) + load_test_spec( - "message_receiver", + "test_group/message_receiver", process_model_source_directory="message_send_one_conversation", - bpmn_file_name="message_receiver", + bpmn_file_name="message_receiver.bpmn", + ) + process_model_sender = load_test_spec( + "test_group/message_sender", + process_model_source_directory="message_send_one_conversation", + bpmn_file_name="message_sender.bpmn", ) - user = self.find_or_create_user() process_instance_sender = ProcessInstanceService.create_process_instance( process_model_sender.id, - user, - process_group_identifier=process_model_sender.process_group_id, + with_super_admin_user, ) processor_sender = ProcessInstanceProcessor(process_instance_sender) @@ -115,21 +124,30 @@ class TestMessageService(BaseTest): assert process_instance.status == "complete" def test_can_send_message_to_multiple_process_models( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_send_message_to_multiple_process_models.""" + process_group_id = "test_group" + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_id + ) + process_model_sender = load_test_spec( - "message_sender", + "test_group/message_sender", process_model_source_directory="message_send_two_conversations", bpmn_file_name="message_sender", ) load_test_spec( - "message_receiver_one", + "test_group/message_receiver_one", process_model_source_directory="message_send_two_conversations", bpmn_file_name="message_receiver_one", ) load_test_spec( - "message_receiver_two", + "test_group/message_receiver_two", process_model_source_directory="message_send_two_conversations", bpmn_file_name="message_receiver_two", ) @@ -139,7 +157,7 @@ class TestMessageService(BaseTest): process_instance_sender = ProcessInstanceService.create_process_instance( process_model_sender.id, user, - process_group_identifier=process_model_sender.process_group_id, + # process_group_identifier=process_model_sender.process_group_id, ) processor_sender = ProcessInstanceProcessor(process_instance_sender) @@ -189,24 +207,24 @@ class TestMessageService(BaseTest): assert len(process_instance_result) == 3 process_instance_receiver_one = ProcessInstanceModel.query.filter_by( - process_model_identifier="message_receiver_one" + process_model_identifier="test_group/message_receiver_one" ).first() assert process_instance_receiver_one is not None process_instance_receiver_two = ProcessInstanceModel.query.filter_by( - process_model_identifier="message_receiver_two" + process_model_identifier="test_group/message_receiver_two" ).first() assert process_instance_receiver_two is not None # just make sure it's a different process instance assert ( process_instance_receiver_one.process_model_identifier - == "message_receiver_one" + == "test_group/message_receiver_one" ) assert process_instance_receiver_one.id != process_instance_sender.id assert process_instance_receiver_one.status == "complete" assert ( process_instance_receiver_two.process_model_identifier - == "message_receiver_two" + == "test_group/message_receiver_two" ) assert process_instance_receiver_two.id != process_instance_sender.id assert process_instance_receiver_two.status == "complete" diff --git a/tests/spiffworkflow_backend/unit/test_permissions.py b/tests/spiffworkflow_backend/unit/test_permissions.py index 39f857e2..117fd0af 100644 --- a/tests/spiffworkflow_backend/unit/test_permissions.py +++ b/tests/spiffworkflow_backend/unit/test_permissions.py @@ -1,5 +1,6 @@ """Test Permissions.""" from flask.app import Flask +from flask.testing import FlaskClient from flask_bpmn.models.db import db from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec @@ -8,6 +9,7 @@ from spiffworkflow_backend.models.group import GroupModel from spiffworkflow_backend.models.permission_assignment import PermissionAssignmentModel from spiffworkflow_backend.models.permission_target import PermissionTargetModel from spiffworkflow_backend.models.principal import PrincipalModel +from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.user_service import UserService @@ -22,13 +24,21 @@ class TestPermissions(BaseTest): """TestPermissions.""" def test_user_can_be_given_permission_to_administer_process_group( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_user_can_be_given_permission_to_administer_process_group.""" process_group_id = "group-a" + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_id + ) load_test_spec( - "timers_intermediate_catch_event", - process_group_id=process_group_id, + "group-a/timers_intermediate_catch_event", + bpmn_file_name="timers_intermediate_catch_event.bpmn", + process_model_source_directory="timers_intermediate_catch_event", ) dan = self.find_or_create_user() principal = dan.principal @@ -55,8 +65,9 @@ class TestPermissions(BaseTest): process_group_b_id = process_group_ids[1] for process_group_id in process_group_ids: load_test_spec( - "timers_intermediate_catch_event", - process_group_id=process_group_id, + f"{process_group_id}/timers_intermediate_catch_event", + bpmn_file_name="timers_intermediate_catch_event", + process_model_source_directory="timers_intermediate_catch_event", ) group_a_admin = self.find_or_create_user() @@ -86,11 +97,11 @@ class TestPermissions(BaseTest): """Test_user_can_be_granted_access_through_a_group.""" process_group_ids = ["group-a", "group-b"] process_group_a_id = process_group_ids[0] - process_group_ids[1] for process_group_id in process_group_ids: load_test_spec( - "timers_intermediate_catch_event", - process_group_id=process_group_id, + f"{process_group_id}/timers_intermediate_catch_event", + bpmn_file_name="timers_intermediate_catch_event.bpmn", + process_model_source_directory="timers_intermediate_catch_event", ) user = self.find_or_create_user() group = GroupModel(identifier="groupA") @@ -127,8 +138,9 @@ class TestPermissions(BaseTest): process_group_b_id = process_group_ids[1] for process_group_id in process_group_ids: load_test_spec( - "timers_intermediate_catch_event", - process_group_id=process_group_id, + f"{process_group_id}/timers_intermediate_catch_event", + bpmn_file_name="timers_intermediate_catch_event.bpmn", + process_model_source_directory="timers_intermediate_catch_event", ) group_a_admin = self.find_or_create_user() diff --git a/tests/spiffworkflow_backend/unit/test_process_instance_processor.py b/tests/spiffworkflow_backend/unit/test_process_instance_processor.py index 543b99c0..ad7aefe3 100644 --- a/tests/spiffworkflow_backend/unit/test_process_instance_processor.py +++ b/tests/spiffworkflow_backend/unit/test_process_instance_processor.py @@ -2,11 +2,13 @@ import pytest from flask import g from flask.app import Flask +from flask.testing import FlaskClient from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from spiffworkflow_backend.models.group import GroupModel from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus +from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.authorization_service import ( UserDoesNotHaveAccessToTaskError, @@ -50,9 +52,14 @@ class TestProcessInstanceProcessor(BaseTest): def test_sets_permission_correctly_on_active_task( self, app: Flask, + client: FlaskClient, with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_sets_permission_correctly_on_active_task.""" + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) initiator_user = self.find_or_create_user("initiator_user") finance_user = self.find_or_create_user("testuser2") assert initiator_user.principal is not None @@ -63,7 +70,9 @@ class TestProcessInstanceProcessor(BaseTest): assert finance_group is not None process_model = load_test_spec( - process_model_id="model_with_lanes", bpmn_file_name="lanes.bpmn" + process_model_id="test_group/model_with_lanes", + bpmn_file_name="lanes.bpmn", + process_model_source_directory="model_with_lanes", ) process_instance = self.create_process_instance_from_process_model( process_model=process_model, user=initiator_user @@ -123,9 +132,14 @@ class TestProcessInstanceProcessor(BaseTest): def test_sets_permission_correctly_on_active_task_when_using_dict( self, app: Flask, + client: FlaskClient, with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_sets_permission_correctly_on_active_task_when_using_dict.""" + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) initiator_user = self.find_or_create_user("initiator_user") finance_user_three = self.find_or_create_user("testuser3") finance_user_four = self.find_or_create_user("testuser4") @@ -138,8 +152,9 @@ class TestProcessInstanceProcessor(BaseTest): assert finance_group is not None process_model = load_test_spec( - process_model_id="model_with_lanes", + process_model_id="test_group/model_with_lanes", bpmn_file_name="lanes_with_owner_dict.bpmn", + process_model_source_directory="model_with_lanes", ) process_instance = self.create_process_instance_from_process_model( process_model=process_model, user=initiator_user diff --git a/tests/spiffworkflow_backend/unit/test_process_instance_report.py b/tests/spiffworkflow_backend/unit/test_process_instance_report.py index acfac138..48239507 100644 --- a/tests/spiffworkflow_backend/unit/test_process_instance_report.py +++ b/tests/spiffworkflow_backend/unit/test_process_instance_report.py @@ -128,8 +128,6 @@ def do_report_with_metadata_and_instances( """Do_report_with_metadata_and_instances.""" process_instance_report = ProcessInstanceReportModel.create_with_attributes( identifier="sure", - process_group_identifier=process_instances[0].process_group_identifier, - process_model_identifier=process_instances[0].process_model_identifier, report_metadata=report_metadata, user=BaseTest.find_or_create_user(), ) diff --git a/tests/spiffworkflow_backend/unit/test_process_model.py b/tests/spiffworkflow_backend/unit/test_process_model.py index 479db44a..5b5b9f25 100644 --- a/tests/spiffworkflow_backend/unit/test_process_model.py +++ b/tests/spiffworkflow_backend/unit/test_process_model.py @@ -1,11 +1,13 @@ """Process Model.""" from flask.app import Flask +from flask.testing import FlaskClient from flask_bpmn.models.db import db from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from spiffworkflow_backend.models.bpmn_process_id_lookup import BpmnProcessIdLookup from spiffworkflow_backend.models.process_model import ProcessModelInfo +from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, ) @@ -22,11 +24,19 @@ class TestProcessModel(BaseTest): assert process_model_one.files == [] def test_can_run_process_model_with_call_activities_when_in_same_process_model_directory( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_run_process_model_with_call_activities.""" + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) process_model = load_test_spec( - "call_activity_test", + "test_group/call_activity_test", + # bpmn_file_name="call_activity_test.bpmn", process_model_source_directory="call_activity_same_directory", ) @@ -38,11 +48,18 @@ class TestProcessModel(BaseTest): assert process_instance.status == "complete" def test_can_run_process_model_with_call_activities_when_not_in_same_directory( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_run_process_model_with_call_activities.""" + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) process_model = load_test_spec( - "call_activity_nested", + "test_group/call_activity_nested", process_model_source_directory="call_activity_nested", bpmn_file_name="call_activity_nested", ) @@ -54,7 +71,7 @@ class TestProcessModel(BaseTest): ] for bpmn_file_name in bpmn_file_names: load_test_spec( - bpmn_file_name, + f"test_group/{bpmn_file_name}", process_model_source_directory="call_activity_nested", bpmn_file_name=bpmn_file_name, ) @@ -66,11 +83,18 @@ class TestProcessModel(BaseTest): assert process_instance.status == "complete" def test_can_run_process_model_with_call_activities_when_process_identifier_is_not_in_database( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_run_process_model_with_call_activities.""" + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) process_model = load_test_spec( - "call_activity_nested", + "test_group/call_activity_nested", process_model_source_directory="call_activity_nested", bpmn_file_name="call_activity_nested", ) @@ -82,7 +106,7 @@ class TestProcessModel(BaseTest): ] for bpmn_file_name in bpmn_file_names: load_test_spec( - bpmn_file_name, + f"test_group/{bpmn_file_name}", process_model_source_directory="call_activity_nested", bpmn_file_name=bpmn_file_name, ) @@ -93,6 +117,7 @@ class TestProcessModel(BaseTest): # delete all of the id lookup items to force to processor to find the correct # process model when running the process db.session.query(BpmnProcessIdLookup).delete() + db.session.commit() processor = ProcessInstanceProcessor(process_instance) processor.do_engine_steps(save=True) assert process_instance.status == "complete" diff --git a/tests/spiffworkflow_backend/unit/test_process_model_service.py b/tests/spiffworkflow_backend/unit/test_process_model_service.py index 535dc03d..438ef89d 100644 --- a/tests/spiffworkflow_backend/unit/test_process_model_service.py +++ b/tests/spiffworkflow_backend/unit/test_process_model_service.py @@ -1,8 +1,10 @@ """Test_process_model_service.""" from flask import Flask +from flask.testing import FlaskClient from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec +from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_model_service import ProcessModelService @@ -10,11 +12,22 @@ class TestProcessModelService(BaseTest): """TestProcessModelService.""" def test_can_update_specified_attributes( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_update_specified_attributes.""" - process_model = load_test_spec("hello_world") - assert process_model.display_name == "hello_world" + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) + process_model = load_test_spec( + "test_group/hello_world", + bpmn_file_name="hello_world.bpmn", + process_model_source_directory="hello_world", + ) + assert process_model.display_name == "test_group/hello_world" primary_process_id = process_model.primary_process_id assert primary_process_id == "Process_HelloWorld" diff --git a/tests/spiffworkflow_backend/unit/test_restricted_script_engine.py b/tests/spiffworkflow_backend/unit/test_restricted_script_engine.py index 9b6f1bb3..d31ea424 100644 --- a/tests/spiffworkflow_backend/unit/test_restricted_script_engine.py +++ b/tests/spiffworkflow_backend/unit/test_restricted_script_engine.py @@ -1,10 +1,12 @@ """Test_various_bpmn_constructs.""" import pytest from flask.app import Flask +from flask.testing import FlaskClient from flask_bpmn.api.api_error import ApiError from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec +from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, ) @@ -14,11 +16,18 @@ class TestOpenFile(BaseTest): """TestVariousBpmnConstructs.""" def test_dot_notation( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_form_data_conversion_to_dot_dict.""" + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) process_model = load_test_spec( - "dangerous", + "test_group/dangerous", bpmn_file_name="read_etc_passwd.bpmn", process_model_source_directory="dangerous-scripts", ) @@ -38,11 +47,18 @@ class TestImportModule(BaseTest): """TestVariousBpmnConstructs.""" def test_dot_notation( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_form_data_conversion_to_dot_dict.""" + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) process_model = load_test_spec( - "dangerous", + "test_group/dangerous", bpmn_file_name="read_env.bpmn", process_model_source_directory="dangerous-scripts", ) diff --git a/tests/spiffworkflow_backend/unit/test_script_unit_test_runner.py b/tests/spiffworkflow_backend/unit/test_script_unit_test_runner.py index 69c54851..9ece043a 100644 --- a/tests/spiffworkflow_backend/unit/test_script_unit_test_runner.py +++ b/tests/spiffworkflow_backend/unit/test_script_unit_test_runner.py @@ -1,8 +1,10 @@ """Test Permissions.""" from flask.app import Flask +from flask.testing import FlaskClient from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec +from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, ) @@ -16,21 +18,31 @@ class TestScriptUnitTestRunner(BaseTest): def test_takes_data_and_returns_expected_result( self, app: Flask, + client: FlaskClient, with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_takes_data_and_returns_expected_result.""" app.config["THREAD_LOCAL_DATA"].process_instance_id = None process_group_id = "test_logging_spiff_logger" + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_id + ) process_model_id = "simple_script" - load_test_spec(process_model_id, process_group_id=process_group_id) + process_model_identifier = f"{process_group_id}/{process_model_id}" + load_test_spec( + process_model_identifier, + bpmn_file_name=process_model_id, + process_model_source_directory=process_model_id, + ) bpmn_process_instance = ( ProcessInstanceProcessor.get_bpmn_process_instance_from_process_model( - process_model_id, process_group_id + process_model_identifier ) ) task = ProcessInstanceProcessor.get_task_by_bpmn_identifier( - "Activity_RunScript", bpmn_process_instance + "Activity_CalculateNewData", bpmn_process_instance ) assert task is not None @@ -48,21 +60,32 @@ class TestScriptUnitTestRunner(BaseTest): def test_fails_when_expected_output_does_not_match_actual_output( self, app: Flask, + client: FlaskClient, with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_fails_when_expected_output_does_not_match_actual_output.""" app.config["THREAD_LOCAL_DATA"].process_instance_id = None process_group_id = "test_logging_spiff_logger" + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_id + ) + process_model_id = "simple_script" - load_test_spec(process_model_id, process_group_id=process_group_id) + process_model_identifier = f"{process_group_id}/{process_model_id}" + load_test_spec( + process_model_identifier, + bpmn_file_name=process_model_id, + process_model_source_directory=process_model_id, + ) bpmn_process_instance = ( ProcessInstanceProcessor.get_bpmn_process_instance_from_process_model( - process_model_id, process_group_id + process_model_identifier ) ) task = ProcessInstanceProcessor.get_task_by_bpmn_identifier( - "Activity_RunScript", bpmn_process_instance + "Activity_CalculateNewData", bpmn_process_instance ) assert task is not None @@ -80,17 +103,28 @@ class TestScriptUnitTestRunner(BaseTest): def test_script_with_unit_tests_when_hey_is_passed_in( self, app: Flask, + client: FlaskClient, with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_script_with_unit_tests_when_hey_is_passed_in.""" app.config["THREAD_LOCAL_DATA"].process_instance_id = None process_group_id = "script_with_unit_tests" + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_id + ) + process_model_id = "script_with_unit_tests" - load_test_spec(process_model_id, process_group_id=process_group_id) + process_model_identifier = f"{process_group_id}/{process_model_id}" + load_test_spec( + process_model_identifier, + bpmn_file_name=process_model_id, + process_model_source_directory=process_model_id, + ) bpmn_process_instance = ( ProcessInstanceProcessor.get_bpmn_process_instance_from_process_model( - process_model_id, process_group_id + process_model_identifier ) ) task = ProcessInstanceProcessor.get_task_by_bpmn_identifier( @@ -110,17 +144,29 @@ class TestScriptUnitTestRunner(BaseTest): def test_script_with_unit_tests_when_hey_is_not_passed_in( self, app: Flask, + client: FlaskClient, with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_script_with_unit_tests_when_hey_is_not_passed_in.""" app.config["THREAD_LOCAL_DATA"].process_instance_id = None process_group_id = "script_with_unit_tests" + self.create_process_group( + client, with_super_admin_user, process_group_id, process_group_id + ) + process_model_id = "script_with_unit_tests" - load_test_spec(process_model_id, process_group_id=process_group_id) + process_model_identifier = f"{process_group_id}/{process_model_id}" + + load_test_spec( + process_model_identifier, + bpmn_file_name=process_model_id, + process_model_source_directory=process_model_id, + ) bpmn_process_instance = ( ProcessInstanceProcessor.get_bpmn_process_instance_from_process_model( - process_model_id, process_group_id + process_model_identifier ) ) task = ProcessInstanceProcessor.get_task_by_bpmn_identifier( diff --git a/tests/spiffworkflow_backend/unit/test_spec_file_service.py b/tests/spiffworkflow_backend/unit/test_spec_file_service.py index 85adb298..d74acb47 100644 --- a/tests/spiffworkflow_backend/unit/test_spec_file_service.py +++ b/tests/spiffworkflow_backend/unit/test_spec_file_service.py @@ -3,6 +3,7 @@ import os import pytest from flask import Flask +from flask.testing import FlaskClient from flask_bpmn.api.api_error import ApiError from flask_bpmn.models.db import db from SpiffWorkflow.dmn.parser.BpmnDmnParser import BpmnDmnParser # type: ignore @@ -10,6 +11,7 @@ from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from spiffworkflow_backend.models.bpmn_process_id_lookup import BpmnProcessIdLookup +from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.spec_file_service import SpecFileService @@ -17,18 +19,29 @@ from spiffworkflow_backend.services.spec_file_service import SpecFileService class TestSpecFileService(BaseTest): """TestSpecFileService.""" + process_group_id = "test_process_group_id" + process_model_id = "call_activity_nested" + bpmn_file_name = "call_activity_nested.bpmn" + call_activity_nested_relative_file_path = os.path.join( - "test_process_group_id", "call_activity_nested", "call_activity_nested.bpmn" + process_group_id, process_model_id, bpmn_file_name ) def test_can_store_process_ids_for_lookup( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_can_store_process_ids_for_lookup.""" - load_test_spec( - "call_activity_nested", - process_model_source_directory="call_activity_nested", - bpmn_file_name="call_activity_nested", + self.basic_test_setup( + client=client, + user=with_super_admin_user, + process_group_id=self.process_group_id, + process_model_id=self.process_model_id, + bpmn_file_name=self.bpmn_file_name, + bpmn_file_location="call_activity_nested", ) bpmn_process_id_lookups = BpmnProcessIdLookup.query.all() assert len(bpmn_process_id_lookups) == 1 @@ -39,14 +52,21 @@ class TestSpecFileService(BaseTest): ) def test_fails_to_save_duplicate_process_id( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_fails_to_save_duplicate_process_id.""" bpmn_process_identifier = "Level1" - load_test_spec( - "call_activity_nested", - process_model_source_directory="call_activity_nested", - bpmn_file_name="call_activity_nested", + self.basic_test_setup( + client=client, + user=with_super_admin_user, + process_group_id=self.process_group_id, + process_model_id=self.process_model_id, + bpmn_file_name=self.bpmn_file_name, + bpmn_file_location=self.process_model_id, ) bpmn_process_id_lookups = BpmnProcessIdLookup.query.all() assert len(bpmn_process_id_lookups) == 1 @@ -69,25 +89,30 @@ class TestSpecFileService(BaseTest): ) def test_updates_relative_file_path_when_appropriate( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_updates_relative_file_path_when_appropriate.""" bpmn_process_identifier = "Level1" - bpmn_file_relative_path = os.path.join( - "test_process_group_id", "call_activity_nested", "new_bpmn_file.bpmn" - ) process_id_lookup = BpmnProcessIdLookup( bpmn_process_identifier=bpmn_process_identifier, - bpmn_file_relative_path=bpmn_file_relative_path, + bpmn_file_relative_path=self.call_activity_nested_relative_file_path, ) db.session.add(process_id_lookup) db.session.commit() - load_test_spec( - "call_activity_nested", - process_model_source_directory="call_activity_nested", - bpmn_file_name="call_activity_nested", + self.basic_test_setup( + client=client, + user=with_super_admin_user, + process_group_id=self.process_group_id, + process_model_id=self.process_model_id, + bpmn_file_name=self.bpmn_file_name, + bpmn_file_location=self.process_model_id, ) + bpmn_process_id_lookups = BpmnProcessIdLookup.query.all() assert len(bpmn_process_id_lookups) == 1 assert ( @@ -100,7 +125,11 @@ class TestSpecFileService(BaseTest): ) def test_load_reference_information( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_load_reference_information. @@ -113,12 +142,22 @@ class TestSpecFileService(BaseTest): a DMN file can (theoretically) contain many decisions. So this is an array. """ - load_test_spec( - "call_activity_nested", - process_model_source_directory="call_activity_nested", + process_group_id = "test_group" + process_model_id = "call_activity_nested" + process_model_identifier = self.basic_test_setup( + client=client, + user=with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + # bpmn_file_name=bpmn_file_name, + bpmn_file_location=process_model_id, ) + # load_test_spec( + # , + # process_model_source_directory="call_activity_nested", + # ) process_model_info = ProcessModelService().get_process_model( - "call_activity_nested" + process_model_identifier ) files = SpecFileService.get_files(process_model_info) diff --git a/tests/spiffworkflow_backend/unit/test_various_bpmn_constructs.py b/tests/spiffworkflow_backend/unit/test_various_bpmn_constructs.py index c97803d8..c655d3ff 100644 --- a/tests/spiffworkflow_backend/unit/test_various_bpmn_constructs.py +++ b/tests/spiffworkflow_backend/unit/test_various_bpmn_constructs.py @@ -1,23 +1,35 @@ """Test_various_bpmn_constructs.""" from flask.app import Flask +from flask.testing import FlaskClient from tests.spiffworkflow_backend.helpers.base_test import BaseTest -from tests.spiffworkflow_backend.helpers.test_data import load_test_spec +from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, ) +from spiffworkflow_backend.services.process_model_service import ProcessModelService class TestVariousBpmnConstructs(BaseTest): """TestVariousBpmnConstructs.""" def test_running_process_with_timer_intermediate_catch_event( - self, app: Flask, with_db_and_bpmn_file_cleanup: None + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: """Test_running_process_with_timer_intermediate_catch_event.""" - process_model = load_test_spec( - "timers_intermediate_catch_event", - process_model_source_directory="timer_intermediate_catch_event", + process_model_identifier = self.basic_test_setup( + client, + with_super_admin_user, + "test_group", + "timer_intermediate_catch_event", + ) + + process_model = ProcessModelService().get_process_model( + process_model_id=process_model_identifier ) process_instance = self.create_process_instance_from_process_model( From 0a0bb2281aa03c58e3c803d4e9453660957735bc Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 9 Nov 2022 15:02:22 -0500 Subject: [PATCH 13/22] Squashed 'spiffworkflow-frontend/' changes from cdae31a57..2149f03f5 2149f03f5 fix breadcrumb 4106c5aac Merge pull request #28 from sartography/bug/browser_lock_on_dmn_selection d9f04b932 one instance test left for cypress w/ burnettk db4ff9019 kill a few consoles 8fd0cbafc fixing up routes for launching editor. Also some fixes in the bpmn-js-spiffworkflow to avoid locking up the browser if no files are available. 063fc1e0c process model cypress tests are passing a1de9eca1 camelcase 67116e6ac fix process model create and a couple tests, docker build refactor 6dbdbffad fixed lint issue w/ burnettk cullerton 441e300c6 merged in main and resolved conflicts w/ burnettk cullerton 8c29bc3f6 fixed some acceptance tests w/ burnettk cullerton cf9af691e updated breadcrumbs to work with new process models ids w/ burnettk cullerton 23ff4e816 made a process model form w/ burnettk dbe0b9c71 Merge branch 'main' into feature/nested-groups 099ce24bb lint da1bd979d return next task when running an instance w/ burnettk ee76c5c81 More frontend changes ff0e4c762 process model show page lists files as accordion with action icons w/ burnettk a8cf19162 Merge remote-tracking branch 'origin/main' into feature/carbon_process_model_show 0b334f08d some minor chnages to prepare for chnaging actions dropdown to buttons 4e7d4733f First pass at custom report/perspective for Process Instance List (#23) 381cd4578 change action dropdown direction based on if it is the last one or not ec72afceb add back run and edit and add actions menu 331c079e1 added a table for files w/ burnettk 65874023b updated process modal show page to use accordion component w/ burnettk d50d23f14 First stab at fixing routes and urls d26b67865 Merge remote-tracking branch 'origin/main' into feature/carbon_process_model_show 6d8fee5eb Merge commit '44e49e6ae6a1f644162489a27618c39194f4628d' into main 01218b3ca update gitignore. ee11c1c2f updated the breadcrumb component and added a test for buttons in file dropdown git-subtree-dir: spiffworkflow-frontend git-subtree-split: 2149f03f5d352ba2f40b4dc41e9435cfb396d0e5 --- .gitignore | 3 + Dockerfile | 10 +- cypress/e2e/process_instances.cy.js | 62 +-- cypress/e2e/process_models.cy.js | 66 ++- cypress/e2e/tasks.cy.js | 10 +- cypress/support/commands.js | 19 +- package-lock.json | 20 +- package.json | 2 +- src/components/ButtonWithConfirmation.tsx | 18 +- src/components/FileInput.tsx | 6 +- src/components/ProcessBreadcrumb.tsx | 102 ++-- src/components/ProcessGroupForm.tsx | 2 +- src/components/ProcessModelForm.tsx | 198 ++++++++ src/components/ProcessModelSearch.tsx | 9 +- src/components/ReactDiagramEditor.tsx | 6 +- src/helpers.tsx | 28 +- src/index.css | 12 + src/index.scss | 84 ++++ src/interfaces.ts | 6 +- src/routes/AdminRoutes.tsx | 22 +- src/routes/HomePage.tsx | 17 +- src/routes/ProcessGroupList.tsx | 9 +- src/routes/ProcessGroupShow.tsx | 4 +- src/routes/ProcessInstanceList.tsx | 182 +++++--- src/routes/ProcessInstanceLogList.tsx | 11 +- src/routes/ProcessInstanceReportEdit.tsx | 6 +- src/routes/ProcessInstanceReportList.tsx | 10 +- src/routes/ProcessInstanceReportNew.tsx | 2 +- src/routes/ProcessInstanceReportShow.tsx | 2 +- src/routes/ProcessInstanceShow.tsx | 49 +- src/routes/ProcessModelEdit.tsx | 90 +--- src/routes/ProcessModelEditDiagram.tsx | 84 ++-- src/routes/ProcessModelNew.tsx | 78 +--- src/routes/ProcessModelShow.tsx | 536 ++++++++++++++++------ src/routes/ReactFormEditor.tsx | 41 +- 35 files changed, 1207 insertions(+), 599 deletions(-) create mode 100644 src/components/ProcessModelForm.tsx diff --git a/.gitignore b/.gitignore index 0f43a99f..a694da80 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ cypress/screenshots # i keep accidentally committing these /test*.json + +# Editors +.idea \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index f7c6ac80..b64169c2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,23 @@ ### STAGE 1: Build ### FROM quay.io/sartography/node:latest + RUN mkdir /app WORKDIR /app -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 +ADD package.json /app/ +ADD package-lock.json /app/ + # 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 + +COPY . /app/ + RUN npm run build ENTRYPOINT ["/app/bin/boot_server_in_docker"] diff --git a/cypress/e2e/process_instances.cy.js b/cypress/e2e/process_instances.cy.js index b7bbf9d4..d25350bc 100644 --- a/cypress/e2e/process_instances.cy.js +++ b/cypress/e2e/process_instances.cy.js @@ -3,10 +3,10 @@ import { DATE_FORMAT, PROCESS_STATUSES } from '../../src/config'; const filterByDate = (fromDate) => { cy.get('#date-picker-start-from').clear().type(format(fromDate, DATE_FORMAT)); - cy.contains('Start Range').click(); + cy.contains('Start date from').click(); cy.get('#date-picker-end-from').clear().type(format(fromDate, DATE_FORMAT)); - cy.contains('Start Range').click(); - cy.contains('Filter').click(); + cy.contains('End date from').click(); + cy.getBySel('filter-button').click(); }; const updateDmnText = (oldText, newText, elementId = 'wonderful_process') => { @@ -68,6 +68,7 @@ describe('process-instances', () => { cy.login(); cy.navigateToProcessModel( 'Acceptance Tests Group One', + 'Acceptance Tests Model 1', 'acceptance-tests-model-1' ); }); @@ -90,28 +91,29 @@ describe('process-instances', () => { cy.runPrimaryBpmnFile(); // Change dmn - cy.contains(dmnFile).click(); - cy.contains(`Process Model File: ${dmnFile}`); + cy.getBySel('files-accordion').click(); + cy.getBySel(`edit-file-${dmnFile.replace('.', '-')}`).click(); updateDmnText(originalDmnOutputForKevin, newDmnOutputForKevin); cy.contains('acceptance-tests-model-1').click(); cy.runPrimaryBpmnFile(); - cy.contains(dmnFile).click(); - cy.contains(`Process Model File: ${dmnFile}`); + cy.getBySel('files-accordion').click(); + cy.getBySel(`edit-file-${dmnFile.replace('.', '-')}`).click(); updateDmnText(newDmnOutputForKevin, originalDmnOutputForKevin); cy.contains('acceptance-tests-model-1').click(); cy.runPrimaryBpmnFile(); // Change bpmn - cy.contains(bpmnFile).click(); + cy.getBySel('files-accordion').click(); + cy.getBySel(`edit-file-${bpmnFile.replace('.', '-')}`).click(); cy.contains(`Process Model File: ${bpmnFile}`); updateBpmnPythonScript(newPythonScript); cy.contains('acceptance-tests-model-1').click(); cy.runPrimaryBpmnFile(); - cy.contains(bpmnFile).click(); - cy.contains(`Process Model File: ${bpmnFile}`); + cy.getBySel('files-accordion').click(); + cy.getBySel(`edit-file-${bpmnFile.replace('.', '-')}`).click(); updateBpmnPythonScript(originalPythonScript); cy.contains('acceptance-tests-model-1').click(); cy.runPrimaryBpmnFile(); @@ -125,13 +127,15 @@ describe('process-instances', () => { const bpmnFile = 'process_model_one.bpmn'; // Change bpmn - cy.contains(bpmnFile).click(); + cy.getBySel('files-accordion').click(); + cy.getBySel(`edit-file-${bpmnFile.replace('.', '-')}`).click(); cy.contains(`Process Model File: ${bpmnFile}`); updateBpmnPythonScriptWithMonaco(newPythonScript); cy.contains('acceptance-tests-model-1').click(); cy.runPrimaryBpmnFile(); - cy.contains(bpmnFile).click(); + cy.getBySel('files-accordion').click(); + cy.getBySel(`edit-file-${bpmnFile.replace('.', '-')}`).click(); cy.contains(`Process Model File: ${bpmnFile}`); updateBpmnPythonScriptWithMonaco(originalPythonScript); cy.contains('acceptance-tests-model-1').click(); @@ -161,25 +165,27 @@ describe('process-instances', () => { cy.basicPaginationTest(); }); - it('can filter', () => { + it.only('can filter', () => { cy.getBySel('process-instance-list-link').click(); cy.assertAtLeastOneItemInPaginatedResults(); - PROCESS_STATUSES.forEach((processStatus) => { - if (!['all', 'waiting'].includes(processStatus)) { - cy.get('[name=process-status-selection]').click(); - cy.get('[name=process-status-selection]').type(processStatus); - cy.get(`[aria-label=${processStatus}]`).click(); - cy.contains('Process Status').click(); - cy.contains('Filter').click(); - cy.assertAtLeastOneItemInPaginatedResults(); - cy.getBySel(`process-instance-status-${processStatus}`).contains( - processStatus - ); - // there should really only be one, but in CI there are sometimes more - cy.get('button[aria-label=Remove]:first').click(); - } - }); + // PROCESS_STATUSES.forEach((processStatus) => { + // if (!['all', 'waiting'].includes(processStatus)) { + // cy.get('#process-instance-status-select').click(); + // cy.get('#process-instance-status-select') + // .contains(processStatus) + // .click(); + // // close the dropdown again + // cy.get('#process-instance-status-select').click(); + // cy.getBySel('filter-button').click(); + // cy.assertAtLeastOneItemInPaginatedResults(); + // cy.getBySel(`process-instance-status-${processStatus}`).contains( + // processStatus + // ); + // // there should really only be one, but in CI there are sometimes more + // cy.get('div[aria-label="Clear all selected items"]:first').click(); + // } + // }); const date = new Date(); date.setHours(date.getHours() - 1); diff --git a/cypress/e2e/process_models.cy.js b/cypress/e2e/process_models.cy.js index 48b65d5d..cc4ef606 100644 --- a/cypress/e2e/process_models.cy.js +++ b/cypress/e2e/process_models.cy.js @@ -16,19 +16,14 @@ describe('process-models', () => { const modelId = `test-model-2-${id}`; cy.contains(groupDisplayName).click(); cy.createModel(groupId, modelId, modelDisplayName); - cy.contains(`Process Group: ${groupId}`).click(); - cy.contains(modelId); - - cy.contains(modelId).click(); - cy.url().should('include', `process-models/${groupId}/${modelId}`); - cy.contains(`Process Model: ${modelId}`); + cy.url().should('include', `process-models/${groupId}:${modelId}`); + cy.contains(`Process Model: ${modelDisplayName}`); cy.contains('Edit process model').click(); cy.get('input[name=display_name]').clear().type(newModelDisplayName); cy.contains('Submit').click(); - cy.contains(`Process Model: ${modelId}`); - - cy.contains('Edit process model').click(); + cy.contains(`Process Model: ${groupId}/${modelId}`); + cy.contains('Submit').click(); cy.get('input[name=display_name]').should( 'have.value', newModelDisplayName @@ -36,7 +31,7 @@ describe('process-models', () => { 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/${groupId}`); cy.contains(modelId).should('not.exist'); }); @@ -55,18 +50,17 @@ describe('process-models', () => { cy.contains(groupDisplayName).click(); cy.createModel(groupId, modelId, modelDisplayName); - cy.contains(`Process Group: ${groupId}`).click(); - cy.contains(modelId); - + cy.contains(groupId).click(); cy.contains(modelId).click(); - cy.url().should('include', `process-models/${groupId}/${modelId}`); - cy.contains(`Process Model: ${modelId}`); + cy.url().should('include', `process-models/${groupId}:${modelId}`); + cy.contains(`Process Model: ${modelDisplayName}`); + cy.getBySel('files-accordion').click(); cy.contains(`${bpmnFileName}.bpmn`).should('not.exist'); cy.contains(`${dmnFileName}.dmn`).should('not.exist'); cy.contains(`${jsonFileName}.json`).should('not.exist'); // add new bpmn file - cy.contains('Add New BPMN File').click(); + cy.contains('New BPMN File').click(); cy.contains(/^Process Model File$/); cy.get('g[data-element-id=StartEvent_1]').click().should('exist'); cy.contains('General').click(); @@ -78,11 +72,12 @@ describe('process-models', () => { cy.contains('Save Changes').click(); cy.contains(`Process Model File: ${bpmnFileName}`); cy.contains(modelId).click(); - cy.contains(`Process Model: ${modelId}`); + cy.contains(`Process Model: ${modelDisplayName}`); + cy.getBySel('files-accordion').click(); cy.contains(`${bpmnFileName}.bpmn`).should('exist'); // add new dmn file - cy.contains('Add New DMN File').click(); + cy.contains('New DMN File').click(); cy.contains(/^Process Model File$/); cy.get('g[data-element-id=decision_1]').click().should('exist'); cy.contains('General').click(); @@ -91,11 +86,12 @@ describe('process-models', () => { cy.contains('Save Changes').click(); cy.contains(`Process Model File: ${dmnFileName}`); cy.contains(modelId).click(); - cy.contains(`Process Model: ${modelId}`); + cy.contains(`Process Model: ${modelDisplayName}`); + cy.getBySel('files-accordion').click(); cy.contains(`${dmnFileName}.dmn`).should('exist'); // add new json file - cy.contains('Add New JSON File').click(); + cy.contains('New JSON File').click(); 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" }'); @@ -106,13 +102,14 @@ describe('process-models', () => { // wait for json to load before clicking away to avoid network errors cy.wait(500); cy.contains(modelId).click(); - cy.contains(`Process Model: ${modelId}`); + cy.contains(`Process Model: ${modelDisplayName}`); + cy.getBySel('files-accordion').click(); cy.contains(`${jsonFileName}.json`).should('exist'); cy.contains('Edit process model').click(); 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/${groupId}`); cy.contains(modelId).should('not.exist'); }); @@ -128,36 +125,35 @@ describe('process-models', () => { cy.contains(groupDisplayName).click(); cy.createModel(groupId, modelId, modelDisplayName); - // seeing if getBySel works better, because we are seeing tests fail in CI - // when looking for the "Add a process model" link, so it seems like the - // click on the breadcrumb element must have failed. - cy.getBySel('process-group-breadcrumb-link').click(); - // cy.contains(`Process Group: ${groupId}`).click(); - + cy.contains(`${groupId}`).click(); cy.contains('Add a process model'); - cy.contains(modelId).click(); - cy.url().should('include', `process-models/${groupId}/${modelId}`); - cy.contains(`Process Model: ${modelId}`); + cy.url().should('include', `process-models/${groupId}:${modelId}`); + cy.contains(`Process Model: ${modelDisplayName}`); - cy.get('input[type=file]').selectFile( + cy.getBySel('files-accordion').click(); + cy.getBySel('upload-file-button').click(); + cy.contains('Add file').selectFile( 'cypress/fixtures/test_bpmn_file_upload.bpmn' ); - cy.contains('Submit').click(); + cy.getBySel('modal-upload-file-dialog') + .find('.cds--btn--primary') + .contains('Upload') + .click(); cy.runPrimaryBpmnFile(); cy.getBySel('process-instance-list-link').click(); cy.getBySel('process-instance-show-link').click(); cy.contains('Delete').click(); cy.contains('Are you sure'); - cy.contains('OK').click(); + cy.getBySel('modal-confirmation-dialog').find('.cds--btn--danger').click(); cy.contains(`Process Instances for: ${groupId}/${modelId}`); cy.contains(modelId).click(); cy.contains('Edit process model').click(); 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/${groupId}`); cy.contains(modelId).should('not.exist'); }); diff --git a/cypress/e2e/tasks.cy.js b/cypress/e2e/tasks.cy.js index 377b05ac..9d5b836a 100644 --- a/cypress/e2e/tasks.cy.js +++ b/cypress/e2e/tasks.cy.js @@ -25,10 +25,11 @@ describe('tasks', () => { it('can complete and navigate a form', () => { const groupDisplayName = 'Acceptance Tests Group One'; const modelId = `acceptance-tests-model-2`; + const modelDisplayName = `Acceptance Tests Model 2`; const completedTaskClassName = 'completed-task-highlight'; const activeTaskClassName = 'active-task-highlight'; - cy.navigateToProcessModel(groupDisplayName, modelId); + cy.navigateToProcessModel(groupDisplayName, modelDisplayName, modelId); // avoid reloading so we can click on the task link that appears on running the process instance cy.runPrimaryBpmnFile(false); @@ -67,7 +68,7 @@ describe('tasks', () => { ); cy.contains('Task: get_user_generated_number_four'); - cy.navigateToProcessModel(groupDisplayName, modelId); + cy.navigateToProcessModel(groupDisplayName, modelDisplayName, modelId); cy.getBySel('process-instance-list-link').click(); cy.assertAtLeastOneItemInPaginatedResults(); @@ -84,7 +85,7 @@ describe('tasks', () => { checkTaskHasClass('form2', completedTaskClassName); checkTaskHasClass('form3', completedTaskClassName); checkTaskHasClass('form4', activeTaskClassName); - cy.get('.modal .btn-close').click(); + cy.get('.is-visible .cds--modal-close').click(); cy.navigateToHome(); cy.contains('Tasks').should('exist'); @@ -99,7 +100,7 @@ describe('tasks', () => { ); cy.url().should('include', '/tasks'); - cy.navigateToProcessModel(groupDisplayName, modelId); + cy.navigateToProcessModel(groupDisplayName, modelDisplayName, modelId); cy.getBySel('process-instance-list-link').click(); cy.assertAtLeastOneItemInPaginatedResults(); @@ -112,6 +113,7 @@ describe('tasks', () => { it('can paginate items', () => { cy.navigateToProcessModel( 'Acceptance Tests Group One', + 'Acceptance Tests Model 2', 'acceptance-tests-model-2' ); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index affce0d1..c6518917 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -76,8 +76,8 @@ Cypress.Commands.add('createModel', (groupId, modelId, modelDisplayName) => { cy.get('input[name=id]').should('have.value', modelId); cy.contains('Submit').click(); - cy.url().should('include', `process-models/${groupId}/${modelId}`); - cy.contains(`Process Model: ${modelId}`); + cy.url().should('include', `process-models/${groupId}:${modelId}`); + cy.contains(`Process Model: ${modelDisplayName}`); }); Cypress.Commands.add('runPrimaryBpmnFile', (reload = true) => { @@ -91,13 +91,12 @@ Cypress.Commands.add('runPrimaryBpmnFile', (reload = true) => { Cypress.Commands.add( 'navigateToProcessModel', - (groupDisplayName, modelDisplayName) => { + (groupDisplayName, modelDisplayName, modelIdentifier) => { cy.navigateToAdmin(); cy.contains(groupDisplayName).click(); cy.contains(`Process Group: ${groupDisplayName}`); // https://stackoverflow.com/q/51254946/6090676 - cy.getBySel('process-model-show-link').contains(modelDisplayName).click(); - // cy.url().should('include', `process-models/${groupDisplayName}/${modelDisplayName}`); + cy.getBySel('process-model-show-link').contains(modelIdentifier).click(); cy.contains(`Process Model: ${modelDisplayName}`); } ); @@ -115,12 +114,14 @@ Cypress.Commands.add('basicPaginationTest', () => { }); Cypress.Commands.add('assertAtLeastOneItemInPaginatedResults', () => { - cy.getBySel('total-paginated-items') - .invoke('text') - .then(parseFloat) - .should('be.gt', 0); + cy.contains(/\b[1-9]\d*–[1-9]\d* of [1-9]\d*/); }); Cypress.Commands.add('assertNoItemInPaginatedResults', () => { cy.getBySel('total-paginated-items').contains('0'); }); + +Cypress.Commands.add('modifyProcessModelPath', (path) => { + path.replace('/', ':'); + return path; +}); diff --git a/package-lock.json b/package-lock.json index eee5ba6c..42c0fad8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "bootstrap": "^5.2.0", "bpmn-js": "^9.3.2", "bpmn-js-properties-panel": "^1.10.0", - "bpmn-js-spiffworkflow": "sartography/bpmn-js-spiffworkflow#feature/more_launch_buttons_and_dropdowns", + "bpmn-js-spiffworkflow": "sartography/bpmn-js-spiffworkflow#main", "craco": "^0.0.3", "date-fns": "^2.28.0", "diagram-js": "^8.5.0", @@ -7485,7 +7485,7 @@ }, "node_modules/bpmn-js-spiffworkflow": { "version": "0.0.8", - "resolved": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#84593aee1ead7328efdc7da03ab3c9cd34364496", + "resolved": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#24c2cc36067adf8fed75990c6bf4a1a67bc9122b", "license": "MIT", "dependencies": { "inherits": "^2.0.4", @@ -7890,9 +7890,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001418", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz", - "integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==", + "version": "1.0.30001431", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", + "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "funding": [ { "type": "opencollective", @@ -35755,8 +35755,8 @@ } }, "bpmn-js-spiffworkflow": { - "version": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#84593aee1ead7328efdc7da03ab3c9cd34364496", - "from": "bpmn-js-spiffworkflow@sartography/bpmn-js-spiffworkflow#feature/more_launch_buttons_and_dropdowns", + "version": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#24c2cc36067adf8fed75990c6bf4a1a67bc9122b", + "from": "bpmn-js-spiffworkflow@sartography/bpmn-js-spiffworkflow#main", "requires": { "inherits": "^2.0.4", "inherits-browser": "^0.0.1", @@ -36070,9 +36070,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001418", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz", - "integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==" + "version": "1.0.30001431", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", + "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==" }, "case-sensitive-paths-webpack-plugin": { "version": "2.4.0", diff --git a/package.json b/package.json index 37f9e345..92ba23aa 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "bootstrap": "^5.2.0", "bpmn-js": "^9.3.2", "bpmn-js-properties-panel": "^1.10.0", - "bpmn-js-spiffworkflow": "sartography/bpmn-js-spiffworkflow#feature/more_launch_buttons_and_dropdowns", + "bpmn-js-spiffworkflow": "sartography/bpmn-js-spiffworkflow#main", "craco": "^0.0.3", "date-fns": "^2.28.0", "diagram-js": "^8.5.0", diff --git a/src/components/ButtonWithConfirmation.tsx b/src/components/ButtonWithConfirmation.tsx index 25aaadc1..88386919 100644 --- a/src/components/ButtonWithConfirmation.tsx +++ b/src/components/ButtonWithConfirmation.tsx @@ -4,10 +4,14 @@ import { Button, Modal } from '@carbon/react'; type OwnProps = { description?: string; - buttonLabel: string; + buttonLabel?: string; onConfirmation: (..._args: any[]) => any; title?: string; confirmButtonLabel?: string; + kind?: string; + renderIcon?: boolean; + iconDescription?: string | null; + hasIconOnly?: boolean; }; export default function ButtonWithConfirmation({ @@ -16,6 +20,10 @@ export default function ButtonWithConfirmation({ onConfirmation, title = 'Are you sure?', confirmButtonLabel = 'OK', + kind = 'danger', + renderIcon = false, + iconDescription = null, + hasIconOnly = false, }: OwnProps) { const [showConfirmationPrompt, setShowConfirmationPrompt] = useState(false); @@ -49,7 +57,13 @@ export default function ButtonWithConfirmation({ return ( <> - {confirmationDialog()} diff --git a/src/components/FileInput.tsx b/src/components/FileInput.tsx index e57b010b..b86e2111 100644 --- a/src/components/FileInput.tsx +++ b/src/components/FileInput.tsx @@ -1,5 +1,6 @@ import React from 'react'; import HttpService from '../services/HttpService'; +import { modifyProcessModelPath } from '../helpers'; type Props = { processGroupId: string; @@ -27,7 +28,10 @@ export default class FileInput extends React.Component { handleSubmit(event: any) { event.preventDefault(); - const url = `/process-models/${this.processGroupId}/${this.processModelId}/files`; + const modifiedProcessModelId = modifyProcessModelPath( + `${this.processGroupId}/${this.processModelId}` + ); + const url = `/process-models/${modifiedProcessModelId}/files`; const formData = new FormData(); formData.append('file', this.fileInput.current.files[0]); formData.append('fileName', this.fileInput.current.files[0].name); diff --git a/src/components/ProcessBreadcrumb.tsx b/src/components/ProcessBreadcrumb.tsx index 241aaeb7..dcdd7bd8 100644 --- a/src/components/ProcessBreadcrumb.tsx +++ b/src/components/ProcessBreadcrumb.tsx @@ -1,12 +1,46 @@ -import { Link } from 'react-router-dom'; -import Breadcrumb from 'react-bootstrap/Breadcrumb'; -import { BreadcrumbItem } from '../interfaces'; +// @ts-ignore +import { Breadcrumb, BreadcrumbItem } from '@carbon/react'; +import { splitProcessModelId } from '../helpers'; +import { HotCrumbItem } from '../interfaces'; type OwnProps = { processModelId?: string; processGroupId?: string; linkProcessModel?: boolean; - hotCrumbs?: BreadcrumbItem[]; + hotCrumbs?: HotCrumbItem[]; +}; + +const explodeCrumb = (crumb: HotCrumbItem) => { + const url: string = crumb[1] || ''; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [_unused, processModelId, link] = url.split(':'); + const processModelIdSegments = splitProcessModelId(processModelId); + const paths: string[] = []; + const lastPathItem = processModelIdSegments.pop(); + const breadcrumbItems = processModelIdSegments.map( + (processModelIdSegment: string) => { + paths.push(processModelIdSegment); + const fullUrl = `/admin/process-groups/${paths.join(':')}`; + return ( + + {processModelIdSegment} + + ); + } + ); + if (link === 'link') { + const lastUrl = `/admin/process-models/${paths.join(':')}:${lastPathItem}`; + breadcrumbItems.push( + + {lastPathItem} + + ); + } else { + breadcrumbItems.push( + {lastPathItem} + ); + } + return breadcrumbItems; }; export default function ProcessBreadcrumb({ @@ -18,66 +52,58 @@ export default function ProcessBreadcrumb({ let processGroupBreadcrumb = null; let processModelBreadcrumb = null; if (hotCrumbs) { - const lastItem = hotCrumbs.pop(); - if (lastItem === undefined) { - return null; - } - const lastCrumb = {lastItem[0]}; - const leadingCrumbLinks = hotCrumbs.map((crumb) => { + const leadingCrumbLinks = hotCrumbs.map((crumb: any) => { const valueLabel = crumb[0]; const url = crumb[1]; + if (!url) { + return {valueLabel}; + } + if (url && url.startsWith('process_model:')) { + return explodeCrumb(crumb); + } return ( - + {valueLabel} - + ); }); - return ( - - {leadingCrumbLinks} - {lastCrumb} - - ); + return {leadingCrumbLinks}; } if (processModelId) { if (linkProcessModel) { processModelBreadcrumb = ( - - Process Model: {processModelId} - + {`Process Model: ${processModelId}`} + ); } else { processModelBreadcrumb = ( - - Process Model: {processModelId} - + + {`Process Model: ${processModelId}`} + ); } processGroupBreadcrumb = ( - - Process Group: {processGroupId} - + {`Process Group: ${processGroupId}`} + ); } else if (processGroupId) { processGroupBreadcrumb = ( - Process Group: {processGroupId} + + {`Process Group: ${processGroupId}`} + ); } return ( - - - Process Groups - + + Process Groups {processGroupBreadcrumb} {processModelBreadcrumb} diff --git a/src/components/ProcessGroupForm.tsx b/src/components/ProcessGroupForm.tsx index 128a13c0..7f83923f 100644 --- a/src/components/ProcessGroupForm.tsx +++ b/src/components/ProcessGroupForm.tsx @@ -95,7 +95,7 @@ export default function ProcessGroupForm({ const onDisplayNameChanged = (newDisplayName: any) => { setDisplayNameInvalid(false); const updateDict = { display_name: newDisplayName }; - if (!idHasBeenUpdatedByUser) { + if (!idHasBeenUpdatedByUser && mode === 'new') { Object.assign(updateDict, { id: slugifyString(newDisplayName) }); } updateProcessGroup(updateDict); diff --git a/src/components/ProcessModelForm.tsx b/src/components/ProcessModelForm.tsx new file mode 100644 index 00000000..9764bad9 --- /dev/null +++ b/src/components/ProcessModelForm.tsx @@ -0,0 +1,198 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +// @ts-ignore +import { Button, ButtonSet, Form, Stack, TextInput } from '@carbon/react'; +import { + getGroupFromModifiedModelId, + modifyProcessModelPath, + slugifyString, +} from '../helpers'; +import HttpService from '../services/HttpService'; +import { ProcessModel } from '../interfaces'; +import ButtonWithConfirmation from './ButtonWithConfirmation'; + +type OwnProps = { + mode: string; + processModel: ProcessModel; + processGroupId?: string; + setProcessModel: (..._args: any[]) => any; +}; + +export default function ProcessModelForm({ + mode, + processModel, + processGroupId, + setProcessModel, +}: OwnProps) { + const [identifierInvalid, setIdentifierInvalid] = useState(false); + const [idHasBeenUpdatedByUser, setIdHasBeenUpdatedByUser] = + useState(false); + const [displayNameInvalid, setDisplayNameInvalid] = useState(false); + const navigate = useNavigate(); + const modifiedProcessModelPath = modifyProcessModelPath(processModel.id); + + const navigateToProcessModel = (result: ProcessModel) => { + if ('id' in result) { + const modifiedProcessModelPathFromResult = modifyProcessModelPath( + result.id + ); + navigate(`/admin/process-models/${modifiedProcessModelPathFromResult}`); + } + }; + + const navigateToProcessModels = (_result: any) => { + navigate( + `/admin/process-groups/${getGroupFromModifiedModelId( + modifiedProcessModelPath + )}` + ); + }; + + const hasValidIdentifier = (identifierToCheck: string) => { + return identifierToCheck.match(/^[a-z0-9][0-9a-z-]+[a-z0-9]$/); + }; + + const deleteProcessModel = () => { + HttpService.makeCallToBackend({ + path: `/process-models/${modifiedProcessModelPath}`, + successCallback: navigateToProcessModels, + httpMethod: 'DELETE', + }); + }; + + const handleFormSubmission = (event: any) => { + event.preventDefault(); + let hasErrors = false; + if (!hasValidIdentifier(processModel.id)) { + setIdentifierInvalid(true); + hasErrors = true; + } + if (processModel.display_name === '') { + setDisplayNameInvalid(true); + hasErrors = true; + } + if (hasErrors) { + return; + } + let path = `/process-models`; + if (mode === 'edit') { + path = `/process-models/${modifiedProcessModelPath}`; + } + let httpMethod = 'POST'; + if (mode === 'edit') { + httpMethod = 'PUT'; + } + const postBody = { + display_name: processModel.display_name, + description: processModel.description, + }; + if (mode === 'new') { + Object.assign(postBody, { + id: `${processGroupId}/${processModel.id}`, + }); + } + + HttpService.makeCallToBackend({ + path, + successCallback: navigateToProcessModel, + httpMethod, + postBody, + }); + }; + + const updateProcessModel = (newValues: any) => { + const processModelToCopy = { + ...processModel, + }; + Object.assign(processModelToCopy, newValues); + setProcessModel(processModelToCopy); + }; + + const onDisplayNameChanged = (newDisplayName: any) => { + setDisplayNameInvalid(false); + const updateDict = { display_name: newDisplayName }; + if (!idHasBeenUpdatedByUser && mode === 'new') { + Object.assign(updateDict, { id: slugifyString(newDisplayName) }); + } + updateProcessModel(updateDict); + }; + + const formElements = () => { + const textInputs = [ + { + onDisplayNameChanged(event.target.value); + }} + onBlur={(event: any) => console.log('event', event)} + />, + ]; + + if (mode === 'new') { + textInputs.push( + { + updateProcessModel({ id: event.target.value }); + // was invalid, and now valid + if (identifierInvalid && hasValidIdentifier(event.target.value)) { + setIdentifierInvalid(false); + } + setIdHasBeenUpdatedByUser(true); + }} + /> + ); + } + + textInputs.push( + + updateProcessModel({ 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 index 98419d6c..8d5ca90b 100644 --- a/src/components/ProcessModelSearch.tsx +++ b/src/components/ProcessModelSearch.tsx @@ -21,7 +21,7 @@ export default function ProcessModelSearch({ const shouldFilterProcessModel = (options: any) => { const processModel: ProcessModel = options.item; const { inputValue } = options; - return `${processModel.process_group_id}/${processModel.id} (${processModel.display_name})`.includes( + return `${processModel.id} (${processModel.display_name})`.includes( inputValue ); }; @@ -33,9 +33,10 @@ export default function ProcessModelSearch({ items={processModels} itemToString={(processModel: ProcessModel) => { if (processModel) { - return `${processModel.process_group_id}/${ - processModel.id - } (${truncateString(processModel.display_name, 20)})`; + return `${processModel.id} (${truncateString( + processModel.display_name, + 20 + )})`; } return null; }} diff --git a/src/components/ReactDiagramEditor.tsx b/src/components/ReactDiagramEditor.tsx index 41e99792..e9ddb445 100644 --- a/src/components/ReactDiagramEditor.tsx +++ b/src/components/ReactDiagramEditor.tsx @@ -59,7 +59,6 @@ import { makeid } from '../helpers'; type OwnProps = { processModelId: string; - processGroupId: string; diagramType: string; readyOrWaitingBpmnTaskIds?: string[] | null; completedTasksBpmnIds?: string[] | null; @@ -83,7 +82,6 @@ type OwnProps = { // https://codesandbox.io/s/quizzical-lake-szfyo?file=/src/App.js was a handy reference export default function ReactDiagramEditor({ processModelId, - processGroupId, diagramType, readyOrWaitingBpmnTaskIds, completedTasksBpmnIds, @@ -408,6 +406,7 @@ export default function ReactDiagramEditor({ } function fetchDiagramFromURL(urlToUse: any) { + console.log(`urlToUse: ${urlToUse}`); fetch(urlToUse) .then((response) => response.text()) .then((text) => { @@ -424,7 +423,7 @@ export default function ReactDiagramEditor({ function fetchDiagramFromJsonAPI() { HttpService.makeCallToBackend({ - path: `/process-models/${processGroupId}/${processModelId}/files/${fileName}`, + path: `/process-models/${processModelId}/files/${fileName}`, successCallback: setDiagramXMLStringFromResponseJson, }); } @@ -470,7 +469,6 @@ export default function ReactDiagramEditor({ completedTasksBpmnIds, fileName, performingXmlUpdates, - processGroupId, processModelId, url, ]); diff --git a/src/helpers.tsx b/src/helpers.tsx index 6b27fb4c..adb28e73 100644 --- a/src/helpers.tsx +++ b/src/helpers.tsx @@ -95,13 +95,10 @@ export const getProcessModelFullIdentifierFromSearchParams = ( searchParams: any ) => { let processModelFullIdentifier = null; - if ( - searchParams.get('process_model_identifier') && - searchParams.get('process_group_identifier') - ) { + if (searchParams.get('process_model_identifier')) { processModelFullIdentifier = `${searchParams.get( - 'process_group_identifier' - )}/${searchParams.get('process_model_identifier')}`; + 'process_model_identifier' + )}`; } return processModelFullIdentifier; }; @@ -113,3 +110,22 @@ export const truncateString = (text: string, len: number) => { } return text; }; + +// Because of limitations in the way openapi defines parameters, we have to modify process models ids +// which are basically paths to the models +export const modifyProcessModelPath = (path: string) => { + return path.replace('/', ':') || ''; +}; + +export const unModifyProcessModelPath = (path: string) => { + return path.replace(':', '/') || ''; +}; + +export const getGroupFromModifiedModelId = (modifiedId: string) => { + const finalSplitIndex = modifiedId.lastIndexOf(':'); + return modifiedId.slice(0, finalSplitIndex); +}; + +export const splitProcessModelId = (processModelId: string) => { + return processModelId.split('/'); +}; diff --git a/src/index.css b/src/index.css index 988fae27..b1eedf70 100644 --- a/src/index.css +++ b/src/index.css @@ -40,6 +40,18 @@ span.bjs-crumb { opacity: .4; } +.accordion-item-label { + vertical-align: middle; +} + +.cds--breadcrumb { + margin-bottom: 2em; +} + +.process-description { + margin-bottom: 2em; +} + .diagram-editor-canvas { border:1px solid #000000; height:70vh; diff --git a/src/index.scss b/src/index.scss index ee9ca2c2..37766c21 100644 --- a/src/index.scss +++ b/src/index.scss @@ -1,10 +1,22 @@ // @use '@carbon/react/scss/themes'; // @use '@carbon/react/scss/theme' with ($theme: themes.$g100); + +// @use '@carbon/react/scss/theme' with +// ( +// $theme: ( +// cds-link-primary: #525252 +// ) +// ); + @use '@carbon/react'; @use '@carbon/styles'; // @include grid.flex-grid(); +@use '@carbon/colors'; // @use '@carbon/react/scss/colors'; +@use '@carbon/react/scss/themes'; + +// var(--cds-link-text-color, var(--cds-link-primary, #0f62fe)) // site is mainly using white theme. // header is mainly using g100 @@ -13,3 +25,75 @@ // background-color: colors.$gray-100; color: white; } +h1{ + height: 36px; + font-family: 'IBM Plex Sans'; + font-style: normal; + font-weight: 400; + font-size: 28px; + line-height: 36px; + color: #161616; + flex: none; + order: 0; + align-self: stretch; + flex-grow: 0; + margin-bottom: 1em +} + + +.cds--breadcrumb-item a.cds--link:hover { + color: #525252; +} +.cds--breadcrumb-item a.cds--link:visited { + color: #525252; +} +.cds--breadcrumb-item a.cds--link:visited:hover { + color: #525252; +} +.cds--breadcrumb-item a.cds--link { + color: #525252; +} + +.cds--btn--ghost { + color: black; +} +.cds--btn--ghost:visited { + color: black; +} +.cds--btn--ghost:hover { + color: black; +} +.cds--btn--ghost:visited:hover { + color: black; +} + +$slightly-lighter-gray: #474747; +$spiff-header-background-color: #161616; + +.cds--header__global .cds--btn--primary { + background-color: $spiff-header-background-color; +} +.cds--btn--primary { + background-color: #393939; +} +.cds--btn--primary:hover { + background-color: $slightly-lighter-gray; +} +// .cds--btn--ghost:visited { +// color: black; +// } +// .cds--btn--ghost:hover { +// color: black; +// } +// .cds--btn--ghost:visited:hover { +// color: black; +// } + + +// :root { +// --cds-link-primary: #525252; +// } +// .card { +// background: var(--orange); +// --orange: hsl(255, 72%, var(--lightness)); +// } diff --git a/src/interfaces.ts b/src/interfaces.ts index 9f49e715..1417a3a9 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -27,23 +27,23 @@ export interface ProcessFile { content_type: string; last_modified: string; name: string; - process_group_id: string; process_model_id: string; references: ProcessFileReference[]; size: number; type: string; + file_contents?: string; } export interface ProcessModel { id: string; - process_group_id: string; + description: string; display_name: string; primary_file_name: string; files: ProcessFile[]; } // tuple of display value and URL -export type BreadcrumbItem = [displayValue: string, url?: string]; +export type HotCrumbItem = [displayValue: string, url?: string]; export interface ErrorForDisplay { message: string; diff --git a/src/routes/AdminRoutes.tsx b/src/routes/AdminRoutes.tsx index 776a5f34..96293ee7 100644 --- a/src/routes/AdminRoutes.tsx +++ b/src/routes/AdminRoutes.tsx @@ -53,35 +53,35 @@ export default function AdminRoutes() { element={} /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> diff --git a/src/routes/HomePage.tsx b/src/routes/HomePage.tsx index 2cfc0078..2092e7af 100644 --- a/src/routes/HomePage.tsx +++ b/src/routes/HomePage.tsx @@ -3,7 +3,10 @@ import { useEffect, useState } from 'react'; import { Button, Table } from '@carbon/react'; import { Link, useSearchParams } from 'react-router-dom'; import PaginationForTable from '../components/PaginationForTable'; -import { getPageInfoFromSearchParams } from '../helpers'; +import { + getPageInfoFromSearchParams, + modifyProcessModelPath, +} from '../helpers'; import HttpService from '../services/HttpService'; import { PaginationObject, RecentProcessModel } from '../interfaces'; @@ -39,12 +42,15 @@ export default function HomePage() { const rows = tasks.map((row) => { const rowToUse = row as any; const taskUrl = `/tasks/${rowToUse.process_instance_id}/${rowToUse.id}`; + const modifiedProcessModelIdentifier = modifyProcessModelPath( + rowToUse.process_model_identifier + ); return ( {rowToUse.process_model_display_name} @@ -52,7 +58,7 @@ export default function HomePage() { View {rowToUse.process_instance_id} @@ -96,6 +102,9 @@ export default function HomePage() { const buildRecentProcessModelSection = () => { const rows = recentProcessModels.map((row) => { const rowToUse = row as any; + const modifiedProcessModelId = modifyProcessModelPath( + rowToUse.processModelIdentifier + ); return ( {rowToUse.processModelDisplayName} diff --git a/src/routes/ProcessGroupList.tsx b/src/routes/ProcessGroupList.tsx index 13f3d455..e84ec536 100644 --- a/src/routes/ProcessGroupList.tsx +++ b/src/routes/ProcessGroupList.tsx @@ -13,7 +13,10 @@ import { import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import PaginationForTable from '../components/PaginationForTable'; import HttpService from '../services/HttpService'; -import { getPageInfoFromSearchParams } from '../helpers'; +import { + getPageInfoFromSearchParams, + modifyProcessModelPath, +} from '../helpers'; import { CarbonComboBoxSelection, ProcessGroup } from '../interfaces'; import ProcessModelSearch from '../components/ProcessModelSearch'; @@ -36,7 +39,7 @@ export default function ProcessGroupList() { }; const processResultForProcessModels = (result: any) => { const selectionArray = result.results.map((item: any) => { - const label = `${item.process_group_id}/${item.id}`; + const label = `${item.id}`; Object.assign(item, { label }); return item; }); @@ -120,7 +123,7 @@ export default function ProcessGroupList() { const processModelSearchOnChange = (selection: CarbonComboBoxSelection) => { const processModel = selection.selectedItem; navigate( - `/admin/process-models/${processModel.process_group_id}/${processModel.id}` + `/admin/process-models/${modifyProcessModelPath(processModel.id)}` ); }; return ( diff --git a/src/routes/ProcessGroupShow.tsx b/src/routes/ProcessGroupShow.tsx index 25e58044..2c6be43c 100644 --- a/src/routes/ProcessGroupShow.tsx +++ b/src/routes/ProcessGroupShow.tsx @@ -24,6 +24,7 @@ export default function ProcessGroupShow() { setPagination(result.pagination); }; const processResult = (result: any) => { + console.log(result); setProcessGroup(result); HttpService.makeCallToBackend({ path: `/process-models?process_group_identifier=${params.process_group_id}&per_page=${perPage}&page=${page}`, @@ -41,11 +42,12 @@ export default function ProcessGroupShow() { return null; } const rows = processModels.map((row) => { + const modifiedProcessModelId: String = (row as any).id.replace('/', ':'); return ( {(row as any).id} diff --git a/src/routes/ProcessInstanceList.tsx b/src/routes/ProcessInstanceList.tsx index 42ecf674..04fcfd87 100644 --- a/src/routes/ProcessInstanceList.tsx +++ b/src/routes/ProcessInstanceList.tsx @@ -17,11 +17,9 @@ import { Grid, Column, MultiSelect, - // TableHeader, - // TableHead, - // TableRow, - // TableBody, - // TableCell, + TableHeader, + TableHead, + TableRow, // @ts-ignore } from '@carbon/react'; import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config'; @@ -30,6 +28,7 @@ import { convertSecondsToFormattedDate, getPageInfoFromSearchParams, getProcessModelFullIdentifierFromSearchParams, + modifyProcessModelPath, } from '../helpers'; import PaginationForTable from '../components/PaginationForTable'; @@ -49,6 +48,7 @@ export default function ProcessInstanceList() { const navigate = useNavigate(); const [processInstances, setProcessInstances] = useState([]); + const [reportMetadata, setReportMetadata] = useState({}); const [pagination, setPagination] = useState(null); const oneHourInSeconds = 3600; @@ -84,7 +84,6 @@ export default function ProcessInstanceList() { const parametersToGetFromSearchParams = useMemo(() => { return { - process_group_identifier: null, process_model_identifier: null, process_status: null, }; @@ -95,6 +94,7 @@ export default function ProcessInstanceList() { function setProcessInstancesFromResult(result: any) { const processInstancesFromApi = result.results; setProcessInstances(processInstancesFromApi); + setReportMetadata(result.report_metadata); setPagination(result.pagination); } function getProcessInstances() { @@ -111,6 +111,7 @@ export default function ProcessInstanceList() { searchParamValue as any ); functionToCall(dateString); + setShowFilterOptions(true); } }); @@ -123,6 +124,7 @@ export default function ProcessInstanceList() { if (functionToCall !== null) { functionToCall(searchParams.get(paramName) || ''); } + setShowFilterOptions(true); } } ); @@ -135,7 +137,7 @@ export default function ProcessInstanceList() { const processModelFullIdentifier = getProcessModelFullIdentifierFromSearchParams(searchParams); const selectionArray = result.results.map((item: any) => { - const label = `${item.process_group_id}/${item.id}`; + const label = `${item.id}`; Object.assign(item, { label }); if (label === processModelFullIdentifier) { setProcessModelSelection(item); @@ -160,6 +162,7 @@ export default function ProcessInstanceList() { getProcessInstances(); } + // populate process model selection HttpService.makeCallToBackend({ path: `/process-models?per_page=1000`, successCallback: processResultForProcessModels, @@ -241,7 +244,7 @@ export default function ProcessInstanceList() { } if (processModelSelection) { - queryParamString += `&process_group_identifier=${processModelSelection.process_group_id}&process_model_identifier=${processModelSelection.id}`; + queryParamString += `&process_model_identifier=${processModelSelection.id}`; } setErrorMessage(null); @@ -296,6 +299,7 @@ export default function ProcessInstanceList() { return ( Clear - @@ -378,54 +386,86 @@ 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} - - - ); + const headerLabels: Record = { + id: 'Process Instance Id', + process_model_identifier: 'Process Model', + start_in_seconds: 'Start Time', + end_in_seconds: 'End Time', + status: 'Status', + spiff_step: 'SpiffWorkflow Step', + }; + const getHeaderLabel = (header: string) => { + return headerLabels[header] ?? header; + }; + const headers = (reportMetadata as any).columns.map((column: any) => { + // return {getHeaderLabel((column as any).Header)}; + return getHeaderLabel((column as any).Header); }); + + const formatProcessInstanceId = (row: any, id: any) => { + const modifiedProcessModelId: String = modifyProcessModelPath( + row.process_model_identifier + ); + return ( + + {id} + + ); + }; + const formatProcessModelIdentifier = (_row: any, identifier: any) => { + return ( + + {identifier} + + ); + }; + const formatSecondsForDisplay = (_row: any, seconds: any) => { + return convertSecondsToFormattedDate(seconds) || '-'; + }; + const defaultFormatter = (_row: any, value: any) => { + return value; + }; + + const columnFormatters: Record = { + id: formatProcessInstanceId, + process_model_identifier: formatProcessModelIdentifier, + start_in_seconds: formatSecondsForDisplay, + end_in_seconds: formatSecondsForDisplay, + }; + const formattedColumn = (row: any, column: any) => { + const formatter = columnFormatters[column.accessor] ?? defaultFormatter; + const value = row[column.accessor]; + if (column.accessor === 'status') { + return ( + + {formatter(row, value)} + + ); + } + return {formatter(row, value)}; + }; + + const rows = processInstances.map((row: any) => { + const currentRow = (reportMetadata as any).columns.map((column: any) => { + return formattedColumn(row, column); + }); + return {currentRow}; + }); + return ( - - - - - - - - - - + + + {headers.map((header: any) => ( + {header} + ))} + + {rows}
Process Instance IdProcess GroupProcess ModelStart TimeEnd TimeStatus
); @@ -440,7 +480,11 @@ export default function ProcessInstanceList() { return (

Process Instances for:{' '} - + {processModelFullIdentifier}

@@ -457,9 +501,13 @@ export default function ProcessInstanceList() { <> {processInstanceTitleElement()} - - + diff --git a/src/routes/ProcessInstanceReportNew.tsx b/src/routes/ProcessInstanceReportNew.tsx index 5112617c..16d575fd 100644 --- a/src/routes/ProcessInstanceReportNew.tsx +++ b/src/routes/ProcessInstanceReportNew.tsx @@ -42,7 +42,7 @@ export default function ProcessInstanceReportNew() { .filter((n) => n); HttpService.makeCallToBackend({ - path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/reports`, + path: `/process-instances/reports`, successCallback: navigateToNewProcessInstance, httpMethod: 'POST', postBody: { diff --git a/src/routes/ProcessInstanceReportShow.tsx b/src/routes/ProcessInstanceReportShow.tsx index 1d26e57f..20bfc18f 100644 --- a/src/routes/ProcessInstanceReportShow.tsx +++ b/src/routes/ProcessInstanceReportShow.tsx @@ -39,7 +39,7 @@ export default function ProcessInstanceReport() { } }); HttpService.makeCallToBackend({ - path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/reports/${params.report_identifier}?${query}`, + path: `/process-instances/reports/${params.report_identifier}${query}`, successCallback: processResult, }); } diff --git a/src/routes/ProcessInstanceShow.tsx b/src/routes/ProcessInstanceShow.tsx index dcaa3361..f04ddda6 100644 --- a/src/routes/ProcessInstanceShow.tsx +++ b/src/routes/ProcessInstanceShow.tsx @@ -6,7 +6,10 @@ import { Button, Modal, Stack } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import HttpService from '../services/HttpService'; import ReactDiagramEditor from '../components/ReactDiagramEditor'; -import { convertSecondsToFormattedDate } from '../helpers'; +import { + convertSecondsToFormattedDate, + unModifyProcessModelPath, +} from '../helpers'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; import ErrorContext from '../contexts/ErrorContext'; @@ -22,15 +25,20 @@ export default function ProcessInstanceShow() { const setErrorMessage = (useContext as any)(ErrorContext)[1]; + const unModifiedProcessModelId = unModifyProcessModelPath( + `${params.process_model_id}` + ); + const modifiedProcessModelId = params.process_model_id; + const navigateToProcessInstances = (_result: any) => { navigate( - `/admin/process-instances?process_group_identifier=${params.process_group_id}&process_model_identifier=${params.process_model_id}` + `/admin/process-instances?process_model_identifier=${unModifiedProcessModelId}` ); }; useEffect(() => { HttpService.makeCallToBackend({ - path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/${params.process_instance_id}`, + path: `/process-models/${modifiedProcessModelId}/process-instances/${params.process_instance_id}`, successCallback: setProcessInstance, }); if (typeof params.spiff_step === 'undefined') @@ -43,11 +51,11 @@ export default function ProcessInstanceShow() { path: `/process-instance/${params.process_instance_id}/tasks?all_tasks=true&spiff_step=${params.spiff_step}`, successCallback: setTasks, }); - }, [params]); + }, [params, modifiedProcessModelId]); const deleteProcessInstance = () => { HttpService.makeCallToBackend({ - path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/${params.process_instance_id}`, + path: `/process-instances/${params.process_instance_id}`, successCallback: navigateToProcessInstances, httpMethod: 'DELETE', }); @@ -60,7 +68,7 @@ export default function ProcessInstanceShow() { const terminateProcessInstance = () => { HttpService.makeCallToBackend({ - path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/${params.process_instance_id}/terminate`, + path: `/process-instances/${params.process_instance_id}/terminate`, successCallback: refreshPage, httpMethod: 'POST', }); @@ -68,7 +76,7 @@ export default function ProcessInstanceShow() { const suspendProcessInstance = () => { HttpService.makeCallToBackend({ - path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/${params.process_instance_id}/suspend`, + path: `/process-instances/${params.process_instance_id}/suspend`, successCallback: refreshPage, httpMethod: 'POST', }); @@ -76,7 +84,7 @@ export default function ProcessInstanceShow() { const resumeProcessInstance = () => { HttpService.makeCallToBackend({ - path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/${params.process_instance_id}/resume`, + path: `/process-instances/${params.process_instance_id}/resume`, successCallback: refreshPage, httpMethod: 'POST', }); @@ -125,7 +133,7 @@ export default function ProcessInstanceShow() { Logs @@ -187,7 +195,7 @@ export default function ProcessInstanceShow() {
  • Messages @@ -284,7 +292,7 @@ export default function ProcessInstanceShow() { const taskToUse: any = taskToDisplay; const previousTask: any = getTaskById(taskToUse.parent); HttpService.makeCallToBackend({ - path: `/process-models/${params.process_group_id}/${params.process_model_id}/script-unit-tests`, + path: `/process-models/${modifiedProcessModelId}/script-unit-tests`, httpMethod: 'POST', successCallback: processScriptUnitTestCreateResult, postBody: { @@ -428,13 +436,21 @@ export default function ProcessInstanceShow() { if (processInstance && tasks) { const processInstanceToUse = processInstance as any; const taskIds = getTaskIds(); + const processModelId = unModifyProcessModelPath( + params.process_model_id ? params.process_model_id : '' + ); return ( <>

    Process Instance Id: {processInstanceToUse.id}

    @@ -450,8 +466,7 @@ export default function ProcessInstanceShow() { {getInfoTag(processInstanceToUse)} {taskDataDisplayArea()} { - const processResult = (result: any) => { - setProcessModel(result); - setDisplayName(result.display_name); - }; HttpService.makeCallToBackend({ path: `/${processModelPath}`, - successCallback: processResult, + successCallback: setProcessModel, }); }, [processModelPath]); - const navigateToProcessModel = (_result: any) => { - navigate(`/admin/${processModelPath}`); - }; - - const navigateToProcessModels = (_result: any) => { - navigate(`/admin/process-groups/${params.process_group_id}`); - }; - - const updateProcessModel = (event: any) => { - const processModelToUse = processModel as any; - event.preventDefault(); - const processModelToPass = Object.assign(processModelToUse, { - display_name: displayName, - }); - HttpService.makeCallToBackend({ - path: `/${processModelPath}`, - successCallback: navigateToProcessModel, - httpMethod: 'PUT', - postBody: processModelToPass, - }); - }; - - const deleteProcessModel = () => { - setErrorMessage(null); - const processModelToUse = processModel as any; - const processModelShowPath = `/process-models/${processModelToUse.process_group_id}/${processModelToUse.id}`; - HttpService.makeCallToBackend({ - path: `${processModelShowPath}`, - successCallback: navigateToProcessModels, - httpMethod: 'DELETE', - failureCallback: setErrorMessage, - }); - }; - - const onDisplayNameChanged = (newDisplayName: any) => { - setDisplayName(newDisplayName); - }; - if (processModel) { return ( <> - -

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

    -
    - - onDisplayNameChanged(e.target.value)} - /> -
    -
    - - - - - -
    + +

    Edit Process Model: {(processModel as any).id}

    + ); } diff --git a/src/routes/ProcessModelEditDiagram.tsx b/src/routes/ProcessModelEditDiagram.tsx index 76b278fc..a60a42f0 100644 --- a/src/routes/ProcessModelEditDiagram.tsx +++ b/src/routes/ProcessModelEditDiagram.tsx @@ -17,7 +17,7 @@ import ReactDiagramEditor from '../components/ReactDiagramEditor'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import HttpService from '../services/HttpService'; import ErrorContext from '../contexts/ErrorContext'; -import { makeid } from '../helpers'; +import { makeid, modifyProcessModelPath } from '../helpers'; import { ProcessFile, ProcessModel } from '../interfaces'; export default function ProcessModelEditDiagram() { @@ -77,12 +77,18 @@ export default function ProcessModelEditDiagram() { const [searchParams] = useSearchParams(); const setErrorMessage = (useContext as any)(ErrorContext)[1]; - const [processModelFile, setProcessModelFile] = useState(null); + const [processModelFile, setProcessModelFile] = useState( + null + ); const [newFileName, setNewFileName] = useState(''); const [bpmnXmlForDiagramRendering, setBpmnXmlForDiagramRendering] = useState(null); - const processModelPath = `process-models/${params.process_group_id}/${params.process_model_id}`; + const modifiedProcessModelId = modifyProcessModelPath( + (params as any).process_model_id + ); + + const processModelPath = `process-models/${modifiedProcessModelId}`; useEffect(() => { const processResult = (result: ProcessModel) => { @@ -119,7 +125,7 @@ export default function ProcessModelEditDiagram() { 'file_type' )}`; navigate( - `/admin/process-models/${params.process_group_id}/${params.process_model_id}/files/${fileNameWithExtension}` + `/admin/process-models/${modifiedProcessModelId}/files/${fileNameWithExtension}` ); } }; @@ -128,7 +134,7 @@ export default function ProcessModelEditDiagram() { setErrorMessage(null); setBpmnXmlForDiagramRendering(bpmnXML); - let url = `/process-models/${params.process_group_id}/${params.process_model_id}/files`; + let url = `/process-models/${modifiedProcessModelId}/files`; let httpMethod = 'PUT'; let fileNameWithExtension = fileName; @@ -162,13 +168,11 @@ export default function ProcessModelEditDiagram() { }; const onDeleteFile = (fileName = params.file_name) => { - const url = `/process-models/${params.process_group_id}/${params.process_model_id}/files/${fileName}`; + const url = `/process-models/${modifiedProcessModelId}/files/${fileName}`; const httpMethod = 'DELETE'; const navigateToProcessModelShow = (_httpResult: any) => { - navigate( - `/admin/process-models/${params.process_group_id}/${params.process_model_id}` - ); + navigate(`/admin/process-models/${modifiedProcessModelId}`); }; HttpService.makeCallToBackend({ path: url, @@ -178,7 +182,7 @@ export default function ProcessModelEditDiagram() { }; const onSetPrimaryFile = (fileName = params.file_name) => { - const url = `/process-models/${params.process_group_id}/${params.process_model_id}`; + const url = `/process-models/${modifiedProcessModelId}`; const httpMethod = 'PUT'; const navigateToProcessModelShow = (_httpResult: any) => { @@ -428,7 +432,7 @@ export default function ProcessModelEditDiagram() { if (currentScriptUnitTest && scriptElement) { resetUnitTextResult(); HttpService.makeCallToBackend({ - path: `/process-models/${params.process_group_id}/${params.process_model_id}/script-unit-tests/run`, + path: `/process-models/${modifiedProcessModelId}/script-unit-tests/run`, httpMethod: 'POST', successCallback: processScriptUnitTestRunResult, postBody: { @@ -680,42 +684,34 @@ export default function ProcessModelEditDiagram() { * fixme: Not currently in use. This would only work for bpmn files within the process model. Which is right for DMN and json, but not right here. Need to merge in work on the nested process groups before tackling this. * @param processId */ + + const fileNameTemplatePath = + '/admin/process-models/:process_model_id/files/:file_name'; + const onLaunchBpmnEditor = (processId: string) => { const file = findFileNameForReferenceId(processId, 'bpmn'); if (file) { - const path = generatePath( - '/admin/process-models/:process_group_id/:process_model_id/files/:file_name', - { - process_group_id: params.process_group_id, - process_model_id: params.process_model_id, - file_name: file.name, - } - ); + const path = generatePath(fileNameTemplatePath, { + process_model_id: params.process_model_id, + file_name: file.name, + }); window.open(path); } }; const onLaunchJsonEditor = (fileName: string) => { - const path = generatePath( - '/admin/process-models/:process_group_id/:process_model_id/form/:file_name', - { - process_group_id: params.process_group_id, - process_model_id: params.process_model_id, - file_name: fileName, - } - ); + const path = generatePath(fileNameTemplatePath, { + process_model_id: params.process_model_id, + file_name: fileName, + }); window.open(path); }; const onLaunchDmnEditor = (processId: string) => { const file = findFileNameForReferenceId(processId, 'dmn'); if (file) { - const path = generatePath( - '/admin/process-models/:process_group_id/:process_model_id/files/:file_name', - { - process_group_id: params.process_group_id, - process_model_id: params.process_model_id, - file_name: file.name, - } - ); + const path = generatePath(fileNameTemplatePath, { + process_model_id: params.process_model_id, + file_name: file.name, + }); window.open(path); } }; @@ -730,7 +726,6 @@ export default function ProcessModelEditDiagram() { return (

    - Process Model File - {processModelFile ? `: ${(processModelFile as any).name}` : ''} + Process Model File{processModelFile ? ': ' : ''} + {processModelFileName}

    {appropriateEditor()} {newFileNameBox()} diff --git a/src/routes/ProcessModelNew.tsx b/src/routes/ProcessModelNew.tsx index fdcc784d..2dcfdb30 100644 --- a/src/routes/ProcessModelNew.tsx +++ b/src/routes/ProcessModelNew.tsx @@ -1,75 +1,29 @@ import { useState } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; -import Button from 'react-bootstrap/Button'; -import Form from 'react-bootstrap/Form'; +import { useParams } from 'react-router-dom'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; -import { slugifyString } from '../helpers'; -import HttpService from '../services/HttpService'; +import { ProcessModel } from '../interfaces'; +import ProcessModelForm from '../components/ProcessModelForm'; export default function ProcessModelNew() { const params = useParams(); - - const [identifier, setIdentifier] = useState(''); - const [idHasBeenUpdatedByUser, setIdHasBeenUpdatedByUser] = useState(false); - const [displayName, setDisplayName] = useState(''); - const navigate = useNavigate(); - - const navigateToNewProcessModel = (_result: any) => { - navigate(`/admin/process-models/${params.process_group_id}/${identifier}`); - }; - - const addProcessModel = (event: any) => { - event.preventDefault(); - HttpService.makeCallToBackend({ - path: `/process-models`, - successCallback: navigateToNewProcessModel, - httpMethod: 'POST', - postBody: { - id: identifier, - display_name: displayName, - description: displayName, - process_group_id: params.process_group_id, - }, - }); - }; - - const onDisplayNameChanged = (newDisplayName: any) => { - setDisplayName(newDisplayName); - if (!idHasBeenUpdatedByUser) { - setIdentifier(slugifyString(newDisplayName)); - } - }; + const [processModel, setProcessModel] = useState({ + id: '', + display_name: '', + description: '', + primary_file_name: '', + files: [], + }); return ( <>

    Add Process Model

    -
    - - Display Name: - onDisplayNameChanged(e.target.value)} - /> - - - ID: - { - setIdentifier(e.target.value); - setIdHasBeenUpdatedByUser(true); - }} - /> - - -
    + ); } diff --git a/src/routes/ProcessModelShow.tsx b/src/routes/ProcessModelShow.tsx index 30f81e6d..7294112b 100644 --- a/src/routes/ProcessModelShow.tsx +++ b/src/routes/ProcessModelShow.tsx @@ -1,12 +1,36 @@ import { useContext, useEffect, useState } from 'react'; -import { Link, useParams } from 'react-router-dom'; -// @ts-ignore -import { Button, Stack } from '@carbon/react'; +import { Link, useNavigate, useParams } from 'react-router-dom'; +import { + Add, + Upload, + Download, + TrashCan, + Favorite, + Edit, + // @ts-ignore +} from '@carbon/icons-react'; +import { + Accordion, + AccordionItem, + Button, + Stack, + ButtonSet, + Modal, + FileUploader, + Table, + TableHead, + TableHeader, + TableRow, + TableCell, + TableBody, + // @ts-ignore +} from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; -import FileInput from '../components/FileInput'; import HttpService from '../services/HttpService'; import ErrorContext from '../contexts/ErrorContext'; -import { RecentProcessModel } from '../interfaces'; +import { modifyProcessModelPath, unModifyProcessModelPath } from '../helpers'; +import { ProcessFile, ProcessModel, RecentProcessModel } from '../interfaces'; +import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; const storeRecentProcessModelInLocalStorage = ( processModelForStorage: any, @@ -66,26 +90,34 @@ export default function ProcessModelShow() { const params = useParams(); const setErrorMessage = (useContext as any)(ErrorContext)[1]; - const [processModel, setProcessModel] = useState({}); + const [processModel, setProcessModel] = useState(null); const [processInstanceResult, setProcessInstanceResult] = useState(null); - const [reloadModel, setReloadModel] = useState(false); + const [reloadModel, setReloadModel] = useState(false); + const [filesToUpload, setFilesToUpload] = useState(null); + const [showFileUploadModal, setShowFileUploadModal] = + useState(false); + const navigate = useNavigate(); + + const modifiedProcessModelId = modifyProcessModelPath( + `${params.process_model_id}` + ); useEffect(() => { - const processResult = (result: object) => { + const processResult = (result: ProcessModel) => { setProcessModel(result); setReloadModel(false); storeRecentProcessModelInLocalStorage(result, params); }; HttpService.makeCallToBackend({ - path: `/process-models/${params.process_group_id}/${params.process_model_id}`, + path: `/process-models/${modifiedProcessModelId}`, successCallback: processResult, }); - }, [params, reloadModel]); + }, [params, reloadModel, modifiedProcessModelId]); const processModelRun = (processInstance: any) => { setErrorMessage(null); HttpService.makeCallToBackend({ - path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/${processInstance.id}/run`, + path: `/process-instances/${processInstance.id}/run`, successCallback: setProcessInstanceResult, failureCallback: setErrorMessage, httpMethod: 'POST', @@ -94,102 +126,246 @@ export default function ProcessModelShow() { const processInstanceCreateAndRun = () => { HttpService.makeCallToBackend({ - path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances`, + path: `/process-models/${modifiedProcessModelId}/process-instances`, successCallback: processModelRun, httpMethod: 'POST', }); }; - let processInstanceResultTag = null; - if (processInstanceResult) { - let takeMeToMyTaskBlurb = null; - // FIXME: ensure that the task is actually for the current user as well - const processInstanceId = (processInstanceResult as any).id; - const nextTask = (processInstanceResult as any).next_task; - if (nextTask && nextTask.state === 'READY') { - takeMeToMyTaskBlurb = ( - - You have a task to complete. Go to{' '} - my task - . - + const processInstanceResultTag = () => { + if (processModel && processInstanceResult) { + let takeMeToMyTaskBlurb = null; + // FIXME: ensure that the task is actually for the current user as well + const processInstanceId = (processInstanceResult as any).id; + const nextTask = (processInstanceResult as any).next_task; + if (nextTask && nextTask.state === 'READY') { + takeMeToMyTaskBlurb = ( + + You have a task to complete. Go to{' '} + + my task + + . + + ); + } + return ( +
    +

    + Process Instance {processInstanceId} kicked off ( + + view + + ). {takeMeToMyTaskBlurb} +

    +
    ); } - processInstanceResultTag = ( -
    -

    - Process Instance {processInstanceId} kicked off ( - - view - - ). {takeMeToMyTaskBlurb} -

    -
    - ); - } + return null; + }; const onUploadedCallback = () => { setReloadModel(true); }; + const reloadModelOhYeah = (_httpResult: any) => { + setReloadModel(!reloadModel); + }; - const processModelFileList = () => { - let constructedTag; - const tags = (processModel as any).files.map((processModelFile: any) => { + // Remove this code from + const onDeleteFile = (fileName: string) => { + const url = `/process-models/${modifiedProcessModelId}/files/${fileName}`; + const httpMethod = 'DELETE'; + HttpService.makeCallToBackend({ + path: url, + successCallback: reloadModelOhYeah, + httpMethod, + }); + }; + + const onSetPrimaryFile = (fileName: string) => { + const url = `/process-models/${modifiedProcessModelId}`; + const httpMethod = 'PUT'; + + const processModelToPass = { + primary_file_name: fileName, + }; + HttpService.makeCallToBackend({ + path: url, + successCallback: onUploadedCallback, + httpMethod, + postBody: processModelToPass, + }); + }; + const handleProcessModelFileResult = (processModelFile: ProcessFile) => { + if ( + !('file_contents' in processModelFile) || + processModelFile.file_contents === undefined + ) { + setErrorMessage({ + message: `Could not file file contents for file: ${processModelFile.name}`, + }); + return; + } + let contentType = 'application/xml'; + if (processModelFile.type === 'json') { + contentType = 'application/json'; + } + const element = document.createElement('a'); + const file = new Blob([processModelFile.file_contents], { + type: contentType, + }); + const downloadFileName = processModelFile.name; + element.href = URL.createObjectURL(file); + element.download = downloadFileName; + document.body.appendChild(element); + element.click(); + }; + + const downloadFile = (fileName: string) => { + setErrorMessage(null); + const processModelPath = `process-models/${modifiedProcessModelId}`; + HttpService.makeCallToBackend({ + path: `/${processModelPath}/files/${fileName}`, + successCallback: handleProcessModelFileResult, + }); + }; + + const navigateToFileEdit = (processModelFile: ProcessFile) => { + if (processModel) { if (processModelFile.name.match(/\.(dmn|bpmn)$/)) { - let primarySuffix = ''; - if (processModelFile.name === (processModel as any).primary_file_name) { - primarySuffix = '- Primary File'; - } - constructedTag = ( -
  • - - {processModelFile.name} - - {primarySuffix} -
  • + navigate( + `/admin/process-models/${modifiedProcessModelId}/files/${processModelFile.name}` ); } else if (processModelFile.name.match(/\.(json|md)$/)) { - constructedTag = ( -
  • - - {processModelFile.name} - -
  • - ); - } else { - constructedTag = ( -
  • {processModelFile.name}
  • + navigate( + `/admin/process-models/${modifiedProcessModelId}/form/${processModelFile.name}` ); } + } + }; + + const renderButtonElements = ( + processModelFile: ProcessFile, + isPrimaryBpmnFile: boolean + ) => { + const elements = []; + elements.push( + - - - - - -
    + + setFilesToUpload(event.target.files)} + /> + ); }; - if (Object.keys(processModel).length > 1) { + const processModelButtons = () => { + if (!processModel) { + return null; + } + return ( + + + + + + + } + > + + + + + + + +
    + {processModelFileList()} +
    +
    + ); + }; + + if (processModel) { return ( <> + {fileUploadModal()} - {processInstanceResultTag} - +

    Process Model: {processModel.display_name}

    +

    {processModel.description}

    + + + +
    +
    + {processInstanceResultTag()} {processModelButtons()}

    Process Instances

    {processInstancesUl()} -
    -
    -

    Files

    - {processModelFileList()} ); } diff --git a/src/routes/ReactFormEditor.tsx b/src/routes/ReactFormEditor.tsx index e706a3ef..5b3c53ec 100644 --- a/src/routes/ReactFormEditor.tsx +++ b/src/routes/ReactFormEditor.tsx @@ -6,6 +6,8 @@ import { Button, Modal } from '@carbon/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import HttpService from '../services/HttpService'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; +import { modifyProcessModelPath, unModifyProcessModelPath } from '../helpers'; +import { ProcessFile } from '../interfaces'; // NOTE: This is mostly the same as ProcessModelEditDiagram and if we go this route could // possibly be merged into it. I'm leaving as a separate file now in case it does @@ -18,7 +20,9 @@ export default function ReactFormEditor() { const handleShowFileNameEditor = () => setShowFileNameEditor(true); const navigate = useNavigate(); - const [processModelFile, setProcessModelFile] = useState(null); + const [processModelFile, setProcessModelFile] = useState( + null + ); const [processModelFileContents, setProcessModelFileContents] = useState(''); const fileExtension = (() => { @@ -34,6 +38,10 @@ export default function ReactFormEditor() { const editorDefaultLanguage = fileExtension === 'md' ? 'markdown' : 'json'; + const modifiedProcessModelId = modifyProcessModelPath( + `${params.process_model_id}` + ); + useEffect(() => { const processResult = (result: any) => { setProcessModelFile(result); @@ -42,23 +50,23 @@ export default function ReactFormEditor() { if (params.file_name) { HttpService.makeCallToBackend({ - path: `/process-models/${params.process_group_id}/${params.process_model_id}/files/${params.file_name}`, + path: `/process-models/${modifiedProcessModelId}/files/${params.file_name}`, successCallback: processResult, }); } - }, [params]); + }, [params, modifiedProcessModelId]); const navigateToProcessModelFile = (_result: any) => { if (!params.file_name) { const fileNameWithExtension = `${newFileName}.${fileExtension}`; navigate( - `/admin/process-models/${params.process_group_id}/${params.process_model_id}/form/${fileNameWithExtension}` + `/admin/process-models/${modifiedProcessModelId}/form/${fileNameWithExtension}` ); } }; const saveFile = () => { - let url = `/process-models/${params.process_group_id}/${params.process_model_id}/files`; + let url = `/process-models/${modifiedProcessModelId}/files`; let httpMethod = 'PUT'; let fileNameWithExtension = params.file_name; @@ -90,13 +98,11 @@ export default function ReactFormEditor() { }; const deleteFile = () => { - const url = `/process-models/${params.process_group_id}/${params.process_model_id}/files/${params.file_name}`; + const url = `/process-models/${modifiedProcessModelId}/files/${params.file_name}`; const httpMethod = 'DELETE'; const navigateToProcessModelShow = (_httpResult: any) => { - navigate( - `/admin/process-models/${params.process_group_id}/${params.process_model_id}` - ); + navigate(`/admin/process-models/${modifiedProcessModelId}`); }; HttpService.makeCallToBackend({ @@ -143,16 +149,29 @@ export default function ReactFormEditor() { }; if (processModelFile || !params.file_name) { + const processModelFileName = processModelFile ? processModelFile.name : ''; return (

    - Process Model File - {processModelFile ? `: ${(processModelFile as any).name}` : ''} + Process Model File{processModelFile ? ': ' : ''} + {processModelFileName}

    {newFileNameBox()}