merged in main and resolved conflicts w/ burnettk

This commit is contained in:
jasquat 2023-03-06 11:15:16 -05:00
commit 96ef81af34
14 changed files with 4263 additions and 4177 deletions

4037
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,10 @@
[tool.poetry] [tool.poetry]
name = "spiffworkflow-arean" name = "spiffworkflow-arena"
version = "0.0.0" version = "0.0.0"
description = "Spiffworkflow Arena" description = "Spiffworkflow Arena"
authors = ["Jason Lantz <sartography@users.noreply.github.com>"] authors = ["Jason Lantz <sartography@users.noreply.github.com>"]
license = "MIT" license = "MIT"
readme = "README.rst" readme = "README.md"
homepage = "https://github.com/sartography/spiffworkflow-arena" homepage = "https://github.com/sartography/spiffworkflow-arena"
repository = "https://github.com/sartography/spiffworkflow-arena" repository = "https://github.com/sartography/spiffworkflow-arena"
classifiers = [ classifiers = [
@ -48,7 +48,6 @@ APScheduler = "^3.9.1"
Jinja2 = "^3.1.2" Jinja2 = "^3.1.2"
RestrictedPython = "^6.0" RestrictedPython = "^6.0"
Flask-SQLAlchemy = "^3" Flask-SQLAlchemy = "^3"
orjson = "^3.8.0"
# type hinting stuff # type hinting stuff
# these need to be in the normal (non dev-dependencies) section # these need to be in the normal (non dev-dependencies) section

View File

@ -1,16 +1,16 @@
"""empty message """empty message
Revision ID: 55b76c4528c5 Revision ID: ede2ae7d3c80
Revises: Revises:
Create Date: 2023-03-06 11:11:55.431564 Create Date: 2023-03-06 11:14:40.739641
""" """
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = '55b76c4528c5' revision = 'ede2ae7d3c80'
down_revision = None down_revision = None
branch_labels = None branch_labels = None
depends_on = None depends_on = None
@ -303,7 +303,7 @@ def upgrade():
sa.Column('list_index', sa.Integer(), nullable=True), sa.Column('list_index', sa.Integer(), nullable=True),
sa.Column('mimetype', sa.String(length=255), nullable=False), sa.Column('mimetype', sa.String(length=255), nullable=False),
sa.Column('filename', sa.String(length=255), nullable=False), sa.Column('filename', sa.String(length=255), nullable=False),
sa.Column('contents', sa.LargeBinary(), nullable=False), sa.Column('contents', sa.LargeBinary().with_variant(mysql.LONGBLOB(), 'mysql'), nullable=False),
sa.Column('digest', sa.String(length=64), nullable=False), sa.Column('digest', sa.String(length=64), nullable=False),
sa.Column('updated_at_in_seconds', sa.Integer(), nullable=False), sa.Column('updated_at_in_seconds', sa.Integer(), nullable=False),
sa.Column('created_at_in_seconds', sa.Integer(), nullable=False), sa.Column('created_at_in_seconds', sa.Integer(), nullable=False),

File diff suppressed because it is too large Load Diff

View File

@ -75,7 +75,7 @@ pylint = "^2.15.10"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pytest = "*" pytest = "^7.1.2"
coverage = {extras = ["toml"], version = "^6.1"} coverage = {extras = ["toml"], version = "^6.1"}
safety = "^2.3.1" safety = "^2.3.1"
mypy = ">=0.961" mypy = ">=0.961"
@ -84,12 +84,12 @@ xdoctest = {extras = ["colors"], version = "^1.0.1"}
sphinx = "^5.0.2" sphinx = "^5.0.2"
sphinx-autobuild = ">=2021.3.14" sphinx-autobuild = ">=2021.3.14"
pre-commit = "^2.20.0" pre-commit = "^2.20.0"
flake8 = "*" flake8 = "^4.0.1"
black = ">=21.10b0" black = ">=21.10b0"
flake8-bandit = "*" flake8-bandit = "^2.1.2"
# 1.7.3 broke us. https://github.com/PyCQA/bandit/issues/841 # 1.7.3 broke us. https://github.com/PyCQA/bandit/issues/841
bandit = "*" bandit = "1.7.2"
flake8-bugbear = "^22.10.25" flake8-bugbear = "^22.10.25"
flake8-docstrings = "^1.6.0" flake8-docstrings = "^1.6.0"

View File

@ -3,6 +3,7 @@ from dataclasses import dataclass
from typing import Optional from typing import Optional
from sqlalchemy import ForeignKey from sqlalchemy import ForeignKey
from sqlalchemy.dialects.mysql import LONGBLOB
from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
@ -24,7 +25,9 @@ class ProcessInstanceFileDataModel(SpiffworkflowBaseDBModel):
mimetype: str = db.Column(db.String(255), nullable=False) mimetype: str = db.Column(db.String(255), nullable=False)
filename: str = db.Column(db.String(255), nullable=False) filename: str = db.Column(db.String(255), nullable=False)
# this is not deferred because there is no reason to query this model if you do not want the contents # this is not deferred because there is no reason to query this model if you do not want the contents
contents: str = db.Column(db.LargeBinary, nullable=False) contents: str = db.Column(
db.LargeBinary().with_variant(LONGBLOB, "mysql"), nullable=False
)
digest: str = db.Column(db.String(64), nullable=False, index=True) digest: str = db.Column(db.String(64), nullable=False, index=True)
updated_at_in_seconds: int = db.Column(db.Integer, nullable=False) updated_at_in_seconds: int = db.Column(db.Integer, nullable=False)

View File

@ -14,7 +14,6 @@ from sqlalchemy import ForeignKey
from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel
from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.task_definition import TaskDefinitionModel
class MultiInstanceType(enum.Enum): class MultiInstanceType(enum.Enum):

View File

@ -1,7 +1,7 @@
"""Process_instance_processor.""" """Process_instance_processor."""
import _strptime # type: ignore import _strptime # type: ignore
from SpiffWorkflow.task import TaskStateNames # type: ignore from SpiffWorkflow.task import TaskStateNames # type: ignore
from spiffworkflow_backend.models.task import TaskModel # noqa: F401 from spiffworkflow_backend.models.task import TaskModel # noqa: F401
import decimal import decimal
import json import json
import logging import logging
@ -987,7 +987,8 @@ class ProcessInstanceProcessor:
bpmn_process = None bpmn_process = None
if bpmn_process_parent is not None: if bpmn_process_parent is not None:
bpmn_process = BpmnProcessModel.query.filter_by(parent_process_id=bpmn_process_parent.id, guid=bpmn_process_guid).first() bpmn_process = BpmnProcessModel.query.filter_by(
parent_process_id=bpmn_process_parent.id, guid=bpmn_process_guid).first()
elif self.process_instance_model.bpmn_process_id is not None: elif self.process_instance_model.bpmn_process_id is not None:
bpmn_process = self.process_instance_model.bpmn_process bpmn_process = self.process_instance_model.bpmn_process

View File

@ -24,13 +24,19 @@ const deleteVideosOnSuccess = (on) => {
}) })
} }
let spiffWorkflowFrontendUrl = `http://localhost:${process.env.SPIFFWORKFLOW_FRONTEND_PORT || 7001}`
if (process.env.SPIFFWORKFLOW_FRONTEND_URL) {
spiffWorkflowFrontendUrl = process.env.SPIFFWORKFLOW_FRONTEND_URL
}
const cypressConfig = { const cypressConfig = {
projectId: 'crax1q', projectId: 'crax1q',
videoUploadOnPasses: false, videoUploadOnPasses: false,
chromeWebSecurity: false, chromeWebSecurity: false,
e2e: { e2e: {
baseUrl: `http://localhost:${process.env.SPIFFWORKFLOW_FRONTEND_PORT || 7001}`, baseUrl: spiffWorkflowFrontendUrl,
setupNodeEvents(on, config) { setupNodeEvents(on, config) {
deleteVideosOnSuccess(on) deleteVideosOnSuccess(on)
require('@cypress/grep/src/plugin')(config); require('@cypress/grep/src/plugin')(config);

View File

@ -0,0 +1,104 @@
const approveWithUser = (
username,
processInstanceId,
expectAdditionalApprovalInfoPage = false
) => {
cy.login(username, username);
cy.visit('/admin/process-instances/find-by-id');
cy.get('#process-instance-id-input').type(processInstanceId);
cy.get('button')
.contains(/^Submit$/)
.click();
cy.contains('Tasks I can complete', { timeout: 20000 });
cy.get('.cds--btn').contains(/^Go$/).click();
// approve!
cy.get('#root-app').click();
cy.get('button')
.contains(/^Submit$/)
.click();
if (expectAdditionalApprovalInfoPage) {
cy.contains(expectAdditionalApprovalInfoPage, { timeout: 20000 });
cy.get('button')
.contains(/^Continue$/)
.click();
}
cy.location({ timeout: 20000 }).should((loc) => {
expect(loc.pathname).to.eq('/tasks');
});
cy.logout();
};
describe('pp1', () => {
it('can run PP1', () => {
cy.login('core5.contributor', 'core5.contributor');
cy.visit('/');
cy.contains('Start New +').click();
cy.contains('Raise New Demand Request');
cy.runPrimaryBpmnFile(true);
cy.contains('Procurement').click();
// cy.contains('Submit').click();
cy.get('button')
.contains(/^Submit$/)
.click();
cy.contains(
'Submit a new demand request for the procurement of needed items',
{ timeout: 20000 }
);
cy.url().then((currentUrl) => {
// if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560"
// extract the digits after /tasks
const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0];
cy.get('#root_project').select('18564');
cy.get('#root_category').select('soft_and_lic');
cy.get('#root_purpose').clear().type('need the software for my work');
cy.get('#root_criticality').select('High');
cy.get('#root_period').clear().type('2023-10-10');
cy.get('#root_vendor').clear().type('sartography');
cy.get('#root_payment_method').select('Bank Transfer');
cy.get('#root_project').select('18564');
cy.get('#root_category').select('soft_and_lic');
cy.get('button')
.contains(/^Submit$/)
.click();
cy.contains('Task: Enter NDR Items', { timeout: 20000 });
cy.get('#root_0_sub_category').select('op_src');
cy.get('#root_0_item').clear().type('spiffworkflow');
cy.get('#root_0_qty').clear().type('1');
cy.get('#root_0_currency_type').select('Fiat');
cy.get('#root_0_currency').select('AUD');
cy.get('#root_0_unit_price').type('100');
cy.get('button')
.contains(/^Submit$/)
.click();
cy.contains(
'Review and provide any supporting information or files for your request.'
);
cy.contains('Submit the Request').click();
cy.get('input[value="Submit the Request"]').click();
cy.get('button')
.contains(/^Submit$/)
.click();
cy.logout();
approveWithUser(
'infra.project-lead',
processInstanceId,
'Task: Reminder: Request Additional Budget'
);
approveWithUser('ppg.ba.sme', processInstanceId);
approveWithUser('security.sme', processInstanceId);
approveWithUser(
'infra.sme',
processInstanceId,
'Task: Update Application Landscape'
);
approveWithUser('legal.sme', processInstanceId);
});
});
});

View File

@ -41,10 +41,15 @@ Cypress.Commands.add('navigateToAdmin', () => {
cy.visit('/admin'); cy.visit('/admin');
}); });
Cypress.Commands.add('login', (selector, ...args) => { Cypress.Commands.add('login', (username, password) => {
// Cypress.Commands.add('login', (selector, ...args) => {
cy.visit('/admin'); cy.visit('/admin');
const username = Cypress.env('SPIFFWORKFLOW_FRONTEND_USERNAME') || 'ciadmin1'; if (!username) {
const password = Cypress.env('SPIFFWORKFLOW_FRONTEND_PASSWORD') || 'ciadmin1'; const username =
Cypress.env('SPIFFWORKFLOW_FRONTEND_USERNAME') || 'ciadmin1';
const password =
Cypress.env('SPIFFWORKFLOW_FRONTEND_PASSWORD') || 'ciadmin1';
}
cy.get('#username').type(username); cy.get('#username').type(username);
cy.get('#password').type(password); cy.get('#password').type(password);
if (Cypress.env('SPIFFWORKFLOW_FRONTEND_AUTH_WITH_KEYCLOAK') === true) { if (Cypress.env('SPIFFWORKFLOW_FRONTEND_AUTH_WITH_KEYCLOAK') === true) {
@ -97,7 +102,12 @@ Cypress.Commands.add('createModel', (groupId, modelId, modelDisplayName) => {
Cypress.Commands.add( Cypress.Commands.add(
'runPrimaryBpmnFile', 'runPrimaryBpmnFile',
(expectAutoRedirectToHumanTask = false) => { (expectAutoRedirectToHumanTask = false) => {
cy.contains('Start').click(); // cy.getBySel('start-process-instance').click();
// click on button with text Start
cy.get('button')
.contains(/^Start$/)
.click();
if (expectAutoRedirectToHumanTask) { if (expectAutoRedirectToHumanTask) {
// the url changes immediately, so also make sure we get some content from the next page, "Task:", or else when we try to interact with the page, it'll re-render and we'll get an error with cypress. // the url changes immediately, so also make sure we get some content from the next page, "Task:", or else when we try to interact with the page, it'll re-render and we'll get an error with cypress.
cy.url().should('include', `/tasks/`); cy.url().should('include', `/tasks/`);

View File

@ -8065,7 +8065,7 @@
}, },
"node_modules/bpmn-js-spiffworkflow": { "node_modules/bpmn-js-spiffworkflow": {
"version": "0.0.8", "version": "0.0.8",
"resolved": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#f1f008e3e39be43b016718fca6a38b248ab4ecf7", "resolved": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#82260144f90d9a311155066d637664d9e2a3f02e",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"inherits": "^2.0.4", "inherits": "^2.0.4",
@ -38214,7 +38214,7 @@
} }
}, },
"bpmn-js-spiffworkflow": { "bpmn-js-spiffworkflow": {
"version": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#f1f008e3e39be43b016718fca6a38b248ab4ecf7", "version": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#82260144f90d9a311155066d637664d9e2a3f02e",
"from": "bpmn-js-spiffworkflow@sartography/bpmn-js-spiffworkflow#main", "from": "bpmn-js-spiffworkflow@sartography/bpmn-js-spiffworkflow#main",
"requires": { "requires": {
"inherits": "^2.0.4", "inherits": "^2.0.4",

View File

@ -99,3 +99,6 @@ a:link {
padding-bottom: 70px; padding-bottom: 70px;
min-height: 100%; min-height: 100%;
} }
.djs-palette.two-column.open {
width: 96px !important;
}

View File

@ -126,7 +126,11 @@ export default function ProcessInstanceRun({
if (checkPermissions) { if (checkPermissions) {
return ( return (
<Can I="POST" a={processInstanceCreatePath} ability={ability}> <Can I="POST" a={processInstanceCreatePath} ability={ability}>
<Button onClick={processInstanceCreateAndRun} className={className}> <Button
data-qa="start-process-instance"
onClick={processInstanceCreateAndRun}
className={className}
>
Start Start
</Button> </Button>
</Can> </Can>