procesio-cli 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- procesio_cli/__init__.py +18 -0
- procesio_cli/_cli.py +71 -0
- procesio_cli/agents/__init__.py +0 -0
- procesio_cli/agents/_lib/__init__.py +0 -0
- procesio_cli/agents/_lib/actiondef.py +24 -0
- procesio_cli/agents/_lib/errors.py +40 -0
- procesio_cli/agents/_lib/reversibility.py +236 -0
- procesio_cli/agents/_lib/toolrunner.py +141 -0
- procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-PLAYBOOK.md +62 -0
- procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-PROCESIO-INTEROP.md +76 -0
- procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-TROUBLESHOOTING.md +60 -0
- procesio_cli/agents/connector-builder/README.md +51 -0
- procesio_cli/agents/connector-builder/agent.yaml +61 -0
- procesio_cli/agents/connector-builder/context.py +19 -0
- procesio_cli/agents/connector-builder/handlers/__init__.py +1 -0
- procesio_cli/agents/connector-builder/handlers/checklist.py +71 -0
- procesio_cli/agents/connector-builder/handlers/guidance.py +37 -0
- procesio_cli/agents/connector-builder/handlers/nextstep.py +221 -0
- procesio_cli/agents/connector-builder/handlers/status.py +35 -0
- procesio_cli/agents/connector-builder/knowledge.py +47 -0
- procesio_cli/agents/connector-builder/main.py +93 -0
- procesio_cli/agents/connector-builder/tests/conftest.py +95 -0
- procesio_cli/agents/connector-builder/tests/test_cba_agent.py +148 -0
- procesio_cli/agents/orchestrator/__init__.py +0 -0
- procesio_cli/agents/orchestrator/bootstraplib.py +166 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/00-INDEX.md +60 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/01-PROCESSES.md +230 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/02-FORMS.md +287 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/03-INTEGRATIONS.md +384 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/04-DATABASE.md +293 -0
- procesio_cli/agents/procesio/PROCESIO-API-RELIABILITY-DOCTRINE.md +113 -0
- procesio_cli/agents/procesio/PROCESIO-BEST-PRACTICES.md +316 -0
- procesio_cli/agents/procesio/PROCESIO-BUILD-AND-TEST-PLAYBOOK.md +217 -0
- procesio_cli/agents/procesio/PROCESIO-DATASTORE.md +72 -0
- procesio_cli/agents/procesio/PROCESIO-ENVIRONMENTS.md +62 -0
- procesio_cli/agents/procesio/PROCESIO-SCHEDULING.md +47 -0
- procesio_cli/agents/procesio/PROCESIO-VISUAL-ORGANIZATION.md +175 -0
- procesio_cli/agents/procesio/README.md +97 -0
- procesio_cli/agents/procesio/__init__.py +0 -0
- procesio_cli/agents/procesio/agent.yaml +73 -0
- procesio_cli/agents/procesio/audit.py +226 -0
- procesio_cli/agents/procesio/checklist.py +135 -0
- procesio_cli/agents/procesio/context.py +19 -0
- procesio_cli/agents/procesio/handlers/__init__.py +0 -0
- procesio_cli/agents/procesio/handlers/audit.py +39 -0
- procesio_cli/agents/procesio/handlers/checklist.py +34 -0
- procesio_cli/agents/procesio/handlers/guidance.py +36 -0
- procesio_cli/agents/procesio/handlers/status.py +33 -0
- procesio_cli/agents/procesio/handlers/verify.py +39 -0
- procesio_cli/agents/procesio/knowledge.py +115 -0
- procesio_cli/agents/procesio/main.py +89 -0
- procesio_cli/agents/procesio/tests/__init__.py +0 -0
- procesio_cli/agents/procesio/tests/conftest.py +62 -0
- procesio_cli/agents/procesio/tests/test_audit.py +91 -0
- procesio_cli/agents/procesio/tests/test_checklist.py +28 -0
- procesio_cli/agents/procesio/tests/test_guidance.py +78 -0
- procesio_cli/agents/procesio/tests/test_manifest_sync.py +48 -0
- procesio_cli/agents/procesio/tests/test_verify.py +134 -0
- procesio_cli/agents/procesio/tests/test_workspace_scope.py +97 -0
- procesio_cli/agents/procesio/verifylib.py +222 -0
- procesio_cli/dashboard/README.md +117 -0
- procesio_cli/dashboard/__init__.py +18 -0
- procesio_cli/dashboard/serve.py +98 -0
- procesio_cli/dashboard/server/__init__.py +1 -0
- procesio_cli/dashboard/server/config_schemas.py +92 -0
- procesio_cli/dashboard/server/google.py +103 -0
- procesio_cli/dashboard/server/handler.py +160 -0
- procesio_cli/dashboard/server/inventory.py +213 -0
- procesio_cli/dashboard/server/jobs.py +118 -0
- procesio_cli/dashboard/server/llmtest.py +181 -0
- procesio_cli/dashboard/server/procesio_env.py +94 -0
- procesio_cli/dashboard/server/routes.py +74 -0
- procesio_cli/dashboard/server/runner.py +150 -0
- procesio_cli/dashboard/server/security.py +29 -0
- procesio_cli/dashboard/server/setup.py +211 -0
- procesio_cli/dashboard/server/validate.py +152 -0
- procesio_cli/dashboard/tests/conftest.py +16 -0
- procesio_cli/dashboard/tests/test_dashboard.py +225 -0
- procesio_cli/dashboard/tests/test_procesio_env.py +159 -0
- procesio_cli/dashboard/web/actions.js +274 -0
- procesio_cli/dashboard/web/api.js +79 -0
- procesio_cli/dashboard/web/app.js +385 -0
- procesio_cli/dashboard/web/copilot.js +72 -0
- procesio_cli/dashboard/web/google.js +121 -0
- procesio_cli/dashboard/web/index.html +67 -0
- procesio_cli/dashboard/web/procesio_env.js +187 -0
- procesio_cli/dashboard/web/styles.css +207 -0
- procesio_cli/registry.py +370 -0
- procesio_cli/scripts/build-router.py +249 -0
- procesio_cli/scripts/build-tool-skill.py +202 -0
- procesio_cli/scripts/check-requirements.py +131 -0
- procesio_cli/scripts/check-skill-governance.py +174 -0
- procesio_cli/scripts/evaluate-skill-routing.py +217 -0
- procesio_cli/scripts/evaluate-skills.py +316 -0
- procesio_cli/scripts/get-skill.py +122 -0
- procesio_cli/scripts/list-skills.py +68 -0
- procesio_cli/scripts/list-tools.py +73 -0
- procesio_cli/scripts/run-agent.py +59 -0
- procesio_cli/scripts/run-tool.py +123 -0
- procesio_cli/scripts/secret_scan.py +584 -0
- procesio_cli/scripts/set-credential.py +126 -0
- procesio_cli/scripts/validate-skills.py +530 -0
- procesio_cli/skills/PORTFOLIO.md +134 -0
- procesio_cli/skills/__init__.py +6 -0
- procesio_cli/skills/agent-skill-engineer/SKILL.md +226 -0
- procesio_cli/skills/agent-skill-engineer/evals/evals.json +422 -0
- procesio_cli/skills/agent-skill-engineer/references/authoring-standard.md +178 -0
- procesio_cli/skills/agent-skill-engineer/references/decision-framework.md +107 -0
- procesio_cli/skills/agent-skill-engineer/references/domain-evidence-protocol.md +248 -0
- procesio_cli/skills/agent-skill-engineer/references/evaluation-standard.md +191 -0
- procesio_cli/skills/agent-skill-engineer/references/expert-lenses.md +167 -0
- procesio_cli/skills/agent-skill-engineer/references/field-gate-standard.md +231 -0
- procesio_cli/skills/agent-skill-engineer/references/optimization-protocol.md +233 -0
- procesio_cli/skills/agent-skill-engineer/references/optimizer-contract.md +268 -0
- procesio_cli/skills/agent-skill-engineer/references/portability-standard.md +99 -0
- procesio_cli/skills/agent-skill-engineer/references/research-basis.md +128 -0
- procesio_cli/skills/agent-skill-engineer/references/review-standard.md +143 -0
- procesio_cli/skills/agent-skill-engineer/scripts/audit_skill.py +959 -0
- procesio_cli/skills/agent-skill-engineer/scripts/optimize_skill.py +891 -0
- procesio_cli/skills/agent-skill-engineer/scripts/scaffold_skill.py +397 -0
- procesio_cli/skills/evals/baseline-catalog.json +14 -0
- procesio_cli/skills/evals/baseline-routing-v2.json +38 -0
- procesio_cli/skills/evals/baseline.json +37 -0
- procesio_cli/skills/evals/behavioral.json +367 -0
- procesio_cli/skills/evals/field-boundary-development-v1.json +126 -0
- procesio_cli/skills/evals/field-closeout-correction-development-v1.json +23 -0
- procesio_cli/skills/evals/field-closeout-development-v1.json +89 -0
- procesio_cli/skills/evals/portfolio-pressure-training-v1.json +83 -0
- procesio_cli/skills/evals/routing.json +363 -0
- procesio_cli/skills/evals/validation-baseline.json +4 -0
- procesio_cli/skills/procesio-cli/SKILL.md +134 -0
- procesio_cli/skills/procesio-cli/evals/evals.json +111 -0
- procesio_cli/skills/procesio-cli/evals/workflows.json +16 -0
- procesio_cli/skills/procesio-cli/references/connector-lifecycle.md +47 -0
- procesio_cli/skills/procesio-cli/references/credentials-admin.md +113 -0
- procesio_cli/skills/procesio-cli/references/data-verification.md +71 -0
- procesio_cli/skills/procesio-cli/references/documents-files.md +44 -0
- procesio_cli/skills/procesio-cli/references/form-e2e.md +57 -0
- procesio_cli/skills/procesio-cli/references/operation-contract.md +138 -0
- procesio_cli/skills/procesio-cli/references/process-debugging.md +65 -0
- procesio_cli/skills/procesio-cli/references/process-lifecycle.md +88 -0
- procesio_cli/skills/procesio-cli/references/schedules-webhooks.md +58 -0
- procesio_cli/skills/procesio-cli/references/transport-environments.md +53 -0
- procesio_cli/skills/procesio-cli-maintainer/SKILL.md +84 -0
- procesio_cli/skills/procesio-cli-maintainer/evals/evals.json +116 -0
- procesio_cli/skills/procesio-cli-maintainer/references/change-contract.md +34 -0
- procesio_cli/skills/procesio-cli-maintainer/references/mcp-compatibility.md +13 -0
- procesio_cli/skills/procesio-cli-maintainer/references/skill-authoring.md +51 -0
- procesio_cli/skills/procesio-platform-advisor/SKILL.md +82 -0
- procesio_cli/skills/procesio-platform-advisor/evals/evals.json +116 -0
- procesio_cli/skills/procesio-platform-advisor/references/capability-map.md +22 -0
- procesio_cli/skills/procesio-platform-advisor/references/fit-framework.md +35 -0
- procesio_cli/skills/procesio-platform-advisor/references/freshness-policy.md +25 -0
- procesio_cli/skills/procesio-platform-advisor/references/sizing-method.md +70 -0
- procesio_cli/skills/procesio-platform-advisor/scripts/estimate_capacity.py +99 -0
- procesio_cli/skills/procesio-setup/SKILL.md +180 -0
- procesio_cli/skills/sql-server-optimizer/SKILL.md +86 -0
- procesio_cli/skills/sql-server-optimizer/evals/evals.json +116 -0
- procesio_cli/skills/sql-server-optimizer/references/db-context.md +44 -0
- procesio_cli/skills/sql-server-optimizer/references/generic-mode.md +32 -0
- procesio_cli/skills/sql-server-optimizer/references/procesio-mode.md +40 -0
- procesio_cli/skills/sql-server-optimizer/references/sql-rules.md +43 -0
- procesio_cli/skills/sql-server-optimizer/scripts/export-indexes.sql +178 -0
- procesio_cli/skills/sql-server-optimizer/scripts/export-procs-and-functions.sql +118 -0
- procesio_cli/skills/sql-server-optimizer/scripts/export-tables.sql +367 -0
- procesio_cli/skills/tests/__init__.py +0 -0
- procesio_cli/skills/tests/test_agent_skill_engineer.py +240 -0
- procesio_cli/skills/tests/test_auth_failure_stop.py +25 -0
- procesio_cli/skills/tests/test_capacity_estimator.py +85 -0
- procesio_cli/skills/tests/test_field_boundary_learning.py +100 -0
- procesio_cli/skills/tests/test_field_closeout_learning.py +123 -0
- procesio_cli/skills/tests/test_field_gate_standard.py +76 -0
- procesio_cli/skills/tests/test_operational_playbooks.py +51 -0
- procesio_cli/skills/tests/test_portfolio_pressure_training.py +35 -0
- procesio_cli/skills/tests/test_process_lifecycle_single_run.py +25 -0
- procesio_cli/skills/tests/test_skill_evaluator.py +67 -0
- procesio_cli/skills/tests/test_skill_optimizer.py +473 -0
- procesio_cli/skills/tests/test_skill_portfolio_review.py +70 -0
- procesio_cli/skills/tests/test_skill_release_binding.py +204 -0
- procesio_cli/skills/tests/test_skill_routing.py +74 -0
- procesio_cli/skills/tests/test_skill_scaffold_v2.py +116 -0
- procesio_cli/skills/tests/test_validate_skills.py +115 -0
- procesio_cli/tools/__init__.py +0 -0
- procesio_cli/tools/_lib/README.md +119 -0
- procesio_cli/tools/_lib/__init__.py +0 -0
- procesio_cli/tools/_lib/accounting.py +120 -0
- procesio_cli/tools/_lib/contacts.py +544 -0
- procesio_cli/tools/_lib/creds.py +83 -0
- procesio_cli/tools/_lib/creds_backends.py +578 -0
- procesio_cli/tools/_lib/dbprofiles.py +147 -0
- procesio_cli/tools/_lib/dbquery.py +286 -0
- procesio_cli/tools/_lib/dbschema.py +225 -0
- procesio_cli/tools/_lib/io.py +54 -0
- procesio_cli/tools/_lib/manifest.py +297 -0
- procesio_cli/tools/_lib/skill_release.py +127 -0
- procesio_cli/tools/_lib/skill_resources.py +118 -0
- procesio_cli/tools/_lib/userdata.py +228 -0
- procesio_cli/tools/_lib/webserialize/__init__.py +16 -0
- procesio_cli/tools/_lib/webserialize/admin.py +61 -0
- procesio_cli/tools/_lib/webserialize/client.py +264 -0
- procesio_cli/tools/_lib/webserialize/config.py +96 -0
- procesio_cli/tools/_lib/webserialize/errors.py +27 -0
- procesio_cli/tools/_lib/webserialize/framing.py +45 -0
- procesio_cli/tools/_lib/webserialize/registry.py +47 -0
- procesio_cli/tools/_lib/webserialize/runtime.py +61 -0
- procesio_cli/tools/_lib/webserialize/server.py +510 -0
- procesio_cli/tools/_lib/websession.py +194 -0
- procesio_cli/tools/connector-builder/CONNECTOR-BUILDER-API-NOTES.md +91 -0
- procesio_cli/tools/connector-builder/README.md +101 -0
- procesio_cli/tools/connector-builder/SKILL.md +248 -0
- procesio_cli/tools/connector-builder/__init__.py +7 -0
- procesio_cli/tools/connector-builder/actiondef.py +24 -0
- procesio_cli/tools/connector-builder/client.py +251 -0
- procesio_cli/tools/connector-builder/errors.py +44 -0
- procesio_cli/tools/connector-builder/gen_manifest.py +142 -0
- procesio_cli/tools/connector-builder/handlers/__init__.py +1 -0
- procesio_cli/tools/connector-builder/handlers/admin.py +182 -0
- procesio_cli/tools/connector-builder/handlers/auth.py +74 -0
- procesio_cli/tools/connector-builder/handlers/builds.py +396 -0
- procesio_cli/tools/connector-builder/handlers/common.py +104 -0
- procesio_cli/tools/connector-builder/handlers/files.py +111 -0
- procesio_cli/tools/connector-builder/handlers/raw.py +38 -0
- procesio_cli/tools/connector-builder/handlers/telemetry.py +39 -0
- procesio_cli/tools/connector-builder/main.py +94 -0
- procesio_cli/tools/connector-builder/tests/conftest.py +146 -0
- procesio_cli/tools/connector-builder/tests/test_cb_client.py +86 -0
- procesio_cli/tools/connector-builder/tests/test_cb_dispatch.py +105 -0
- procesio_cli/tools/connector-builder/tests/test_cb_manifest_sync.py +76 -0
- procesio_cli/tools/connector-builder/tests/test_cb_wait.py +123 -0
- procesio_cli/tools/connector-builder/tool.yaml +671 -0
- procesio_cli/tools/framework-map/.gitattributes +2 -0
- procesio_cli/tools/framework-map/README.md +98 -0
- procesio_cli/tools/framework-map/SKILL.md +30 -0
- procesio_cli/tools/framework-map/app.js +188 -0
- procesio_cli/tools/framework-map/fm_builder.py +476 -0
- procesio_cli/tools/framework-map/fm_catalog.py +19 -0
- procesio_cli/tools/framework-map/fm_ro_catalog.py +94 -0
- procesio_cli/tools/framework-map/fm_strings.py +101 -0
- procesio_cli/tools/framework-map/main.py +95 -0
- procesio_cli/tools/framework-map/styles.css +214 -0
- procesio_cli/tools/framework-map/tests/conftest.py +33 -0
- procesio_cli/tools/framework-map/tests/test_fm_build.py +126 -0
- procesio_cli/tools/framework-map/tests/test_fm_manifest_sync.py +63 -0
- procesio_cli/tools/framework-map/tests/test_fm_store_tree.py +75 -0
- procesio_cli/tools/framework-map/tool.yaml +61 -0
- procesio_cli/tools/mysql/MOVED-TO-CENTRAL.md +29 -0
- procesio_cli/tools/mysql/README.md +196 -0
- procesio_cli/tools/mysql/SKILL.md +37 -0
- procesio_cli/tools/mysql/__init__.py +0 -0
- procesio_cli/tools/mysql/connection.py +191 -0
- procesio_cli/tools/mysql/main.py +487 -0
- procesio_cli/tools/mysql/profiles.template.json +1 -0
- procesio_cli/tools/mysql/schema.py +434 -0
- procesio_cli/tools/mysql/tests/__init__.py +0 -0
- procesio_cli/tools/mysql/tests/conftest.py +21 -0
- procesio_cli/tools/mysql/tests/test_manifest_sync.py +68 -0
- procesio_cli/tools/mysql/tests/test_mysql.py +201 -0
- procesio_cli/tools/mysql/tests/test_mysql_schema.py +337 -0
- procesio_cli/tools/mysql/tool.yaml +112 -0
- procesio_cli/tools/procesio/DTO-SUBTOOLS-NOTE.md +360 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/00-INDEX.md +67 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/01-ANATOMY.md +209 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/02-CODE-INJECTION.md +309 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/03-DOM-CONTRACT.md +370 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/04-INTERACTION-RECIPES.md +600 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/05-STEPPER-AND-MOTION.md +426 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/06-PROCESS-INTEGRATION.md +347 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/07-DEPLOY-WORKFLOW.md +294 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/08-PITFALLS.md +569 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/09-NAVIGATION-AND-HOSTING.md +308 -0
- procesio_cli/tools/procesio/PHASE4-E2E-NOTES.md +594 -0
- procesio_cli/tools/procesio/PROCESIO-API-CORRECTIONS.md +295 -0
- procesio_cli/tools/procesio/PROCESIO-API-NOTES.md +6976 -0
- procesio_cli/tools/procesio/PROCESIO-AUTH-NOTES.md +226 -0
- procesio_cli/tools/procesio/PROCESIO-CARD-BUILD-NOTES.md +183 -0
- procesio_cli/tools/procesio/PROCESIO-CONNECTORS-NOTES.md +141 -0
- procesio_cli/tools/procesio/PROCESIO-CUSTOM-ACTION-NOTES.md +179 -0
- procesio_cli/tools/procesio/PROCESIO-DOCS-FIX-REPORT.md +123 -0
- procesio_cli/tools/procesio/PROCESIO-ENVIRONMENTS-NOTES.md +94 -0
- procesio_cli/tools/procesio/PROCESIO-FE-VALIDATION-NOTES.md +107 -0
- procesio_cli/tools/procesio/PROCESIO-FOREACH-NOTES.md +199 -0
- procesio_cli/tools/procesio/PROCESIO-FORM-API-HANG-NOTE.md +101 -0
- procesio_cli/tools/procesio/PROCESIO-FORM-SUBMISSION-NOTES.md +172 -0
- procesio_cli/tools/procesio/PROCESIO-HTML-TO-DOCX-NOTES.md +110 -0
- procesio_cli/tools/procesio/PROCESIO-INSTANCE-HISTORY-NOTES.md +242 -0
- procesio_cli/tools/procesio/PROCESIO-METERING-NOTES.md +201 -0
- procesio_cli/tools/procesio/PROCESIO-NODE-CODE-NOTES.md +279 -0
- procesio_cli/tools/procesio/PROCESIO-NODE-MODULE-WHITELIST.md +113 -0
- procesio_cli/tools/procesio/PROCESIO-PYTHON-ACTION-NOTES.md +165 -0
- procesio_cli/tools/procesio/PROCESIO-QUERY-STORE-NOTES.md +826 -0
- procesio_cli/tools/procesio/PROCESIO-RECONCILIATION-PATTERNS.md +104 -0
- procesio_cli/tools/procesio/PROCESIO-RESOURCE-MODEL-NOTES.md +1100 -0
- procesio_cli/tools/procesio/PROCESIO-SEND-EMAIL-CRED-OPTIONALITY.md +45 -0
- procesio_cli/tools/procesio/PROCESIO-SEND-EMAIL-NOTES.md +252 -0
- procesio_cli/tools/procesio/PROCESIO-SQL-ACTIONS-NOTES.md +284 -0
- procesio_cli/tools/procesio/PROCESIO-USAGE-GUIDE.md +219 -0
- procesio_cli/tools/procesio/README.md +341 -0
- procesio_cli/tools/procesio/SCHEDULE-INPUT-SECURITY-NOTES.md +63 -0
- procesio_cli/tools/procesio/SKILL.md +694 -0
- procesio_cli/tools/procesio/__init__.py +1 -0
- procesio_cli/tools/procesio/actiondef.py +27 -0
- procesio_cli/tools/procesio/assets/html2docx/html2docx.js +2155 -0
- procesio_cli/tools/procesio/auth.py +354 -0
- procesio_cli/tools/procesio/autogen.py +121 -0
- procesio_cli/tools/procesio/client.py +355 -0
- procesio_cli/tools/procesio/config.py +96 -0
- procesio_cli/tools/procesio/data/endpoints.json +2901 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/01-authentication.md +180 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/02-conventions.md +116 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/ANONYMOUS-SURFACE.md +51 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/README.md +104 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/SYMBOL-INDEX.md +444 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/03-auth-users-workspaces.md +731 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/04-processes.md +913 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/05-actions.md +605 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/06-files.md +237 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/07-forms-documents.md +985 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/08-credentials.md +532 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/09-datastore-datatypes.md +830 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/10-custom-urls.md +319 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/11-subscriptions-resources-analytics.md +455 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/12-webhooks-notifications-misc.md +519 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/13-realtime-signalr.md +153 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/99-internal-and-other-services.md +52 -0
- procesio_cli/tools/procesio/docs_info/FormBuilder mock.ts +5725 -0
- procesio_cli/tools/procesio/docs_info/Procesio Platform Actions.json +30219 -0
- procesio_cli/tools/procesio/docs_info/form-config-enums.ts +92 -0
- procesio_cli/tools/procesio/docs_info/form-theme.ts +1603 -0
- procesio_cli/tools/procesio/docs_info/process-validation-reference-1.md +2739 -0
- procesio_cli/tools/procesio/dto/__init__.py +13 -0
- procesio_cli/tools/procesio/dto/credential/builder.py +179 -0
- procesio_cli/tools/procesio/dto/credential/config.schema.json +19 -0
- procesio_cli/tools/procesio/dto/credential/description.md +193 -0
- procesio_cli/tools/procesio/dto/credential/fixtures/rest.config.json +11 -0
- procesio_cli/tools/procesio/dto/credential/fixtures/rest.dto.json +28 -0
- procesio_cli/tools/procesio/dto/credential/fixtures/rest.template.json +44 -0
- procesio_cli/tools/procesio/dto/credential/template.dto.json +16 -0
- procesio_cli/tools/procesio/dto/data/_refresh.py +147 -0
- procesio_cli/tools/procesio/dto/data/action_catalog.json +30825 -0
- procesio_cli/tools/procesio/dto/data/action_families.json +55 -0
- procesio_cli/tools/procesio/dto/data/platform_types.json +182 -0
- procesio_cli/tools/procesio/dto/datatype/builder.py +223 -0
- procesio_cli/tools/procesio/dto/datatype/config.schema.json +47 -0
- procesio_cli/tools/procesio/dto/datatype/description.md +85 -0
- procesio_cli/tools/procesio/dto/datatype/fixtures/customer.config.json +41 -0
- procesio_cli/tools/procesio/dto/datatype/fixtures/customer.dto.json +164 -0
- procesio_cli/tools/procesio/dto/datatype/template.dto.json +37 -0
- procesio_cli/tools/procesio/dto/document/builder.py +193 -0
- procesio_cli/tools/procesio/dto/document/config.schema.json +33 -0
- procesio_cli/tools/procesio/dto/document/description.md +124 -0
- procesio_cli/tools/procesio/dto/document/fixtures/invoice.config.json +16 -0
- procesio_cli/tools/procesio/dto/document/fixtures/invoice.dto.json +32 -0
- procesio_cli/tools/procesio/dto/document/template.dto.json +14 -0
- procesio_cli/tools/procesio/dto/form/FORM-STYLING-NOTES.md +142 -0
- procesio_cli/tools/procesio/dto/form/GOLDEN-DRIFT-NOTES.md +72 -0
- procesio_cli/tools/procesio/dto/form/RUN-PROCESS-RESULT-NOTES.md +80 -0
- procesio_cli/tools/procesio/dto/form/addelement.py +277 -0
- procesio_cli/tools/procesio/dto/form/builder.py +1009 -0
- procesio_cli/tools/procesio/dto/form/capture_goldens.py +106 -0
- procesio_cli/tools/procesio/dto/form/code_cipher.py +67 -0
- procesio_cli/tools/procesio/dto/form/config.schema.json +76 -0
- procesio_cli/tools/procesio/dto/form/data_shell.json +1 -0
- procesio_cli/tools/procesio/dto/form/description.md +346 -0
- procesio_cli/tools/procesio/dto/form/elements/approval.json +259 -0
- procesio_cli/tools/procesio/dto/form/elements/assignee.json +137 -0
- procesio_cli/tools/procesio/dto/form/elements/button.json +125 -0
- procesio_cli/tools/procesio/dto/form/elements/chart.json +341 -0
- procesio_cli/tools/procesio/dto/form/elements/chat.json +199 -0
- procesio_cli/tools/procesio/dto/form/elements/checkbox.json +153 -0
- procesio_cli/tools/procesio/dto/form/elements/column.json +77 -0
- procesio_cli/tools/procesio/dto/form/elements/columns.json +78 -0
- procesio_cli/tools/procesio/dto/form/elements/datetime-input.json +304 -0
- procesio_cli/tools/procesio/dto/form/elements/divider.json +54 -0
- procesio_cli/tools/procesio/dto/form/elements/dropdown.json +130 -0
- procesio_cli/tools/procesio/dto/form/elements/dynamic-table-row.json +253 -0
- procesio_cli/tools/procesio/dto/form/elements/file-upload.json +196 -0
- procesio_cli/tools/procesio/dto/form/elements/file-viewer.json +169 -0
- procesio_cli/tools/procesio/dto/form/elements/heading.json +79 -0
- procesio_cli/tools/procesio/dto/form/elements/icon.json +107 -0
- procesio_cli/tools/procesio/dto/form/elements/image.json +174 -0
- procesio_cli/tools/procesio/dto/form/elements/input.json +206 -0
- procesio_cli/tools/procesio/dto/form/elements/list.json +202 -0
- procesio_cli/tools/procesio/dto/form/elements/number-input.json +202 -0
- procesio_cli/tools/procesio/dto/form/elements/paragraph.json +79 -0
- procesio_cli/tools/procesio/dto/form/elements/radiobox.json +174 -0
- procesio_cli/tools/procesio/dto/form/elements/section.json +114 -0
- procesio_cli/tools/procesio/dto/form/elements/select.json +217 -0
- procesio_cli/tools/procesio/dto/form/elements/side-panel.json +146 -0
- procesio_cli/tools/procesio/dto/form/elements/signature-pad.json +141 -0
- procesio_cli/tools/procesio/dto/form/elements/static-table-row.json +84 -0
- procesio_cli/tools/procesio/dto/form/elements/step.json +105 -0
- procesio_cli/tools/procesio/dto/form/elements/stepper.json +126 -0
- procesio_cli/tools/procesio/dto/form/elements/tab.json +77 -0
- procesio_cli/tools/procesio/dto/form/elements/table.json +279 -0
- procesio_cli/tools/procesio/dto/form/elements/tabs.json +114 -0
- procesio_cli/tools/procesio/dto/form/elements/textarea.json +213 -0
- procesio_cli/tools/procesio/dto/form/enum_map.json +68 -0
- procesio_cli/tools/procesio/dto/form/fieldpath.py +284 -0
- procesio_cli/tools/procesio/dto/form/fixtures/reg.config.json +32 -0
- procesio_cli/tools/procesio/dto/form/fixtures/reg.elements.json +638 -0
- procesio_cli/tools/procesio/dto/form/sync_from_mock.py +386 -0
- procesio_cli/tools/procesio/dto/framework.py +125 -0
- procesio_cli/tools/procesio/dto/process/builder.py +1991 -0
- procesio_cli/tools/procesio/dto/process/config.schema.json +159 -0
- procesio_cli/tools/procesio/dto/process/description.md +218 -0
- procesio_cli/tools/procesio/dto/process/fixtures/greet.config.json +32 -0
- procesio_cli/tools/procesio/dto/process/fixtures/greet.dto.json +317 -0
- procesio_cli/tools/procesio/dto/process/naming.py +305 -0
- procesio_cli/tools/procesio/dto/process/normalize.py +74 -0
- procesio_cli/tools/procesio/dto/process/template.dto.json +32 -0
- procesio_cli/tools/procesio/dto/refdata.py +87 -0
- procesio_cli/tools/procesio/dto/registry.py +27 -0
- procesio_cli/tools/procesio/dto/webhook/builder.py +142 -0
- procesio_cli/tools/procesio/dto/webhook/config.schema.json +25 -0
- procesio_cli/tools/procesio/dto/webhook/description.md +92 -0
- procesio_cli/tools/procesio/dto/webhook/fixtures/order.config.json +10 -0
- procesio_cli/tools/procesio/dto/webhook/fixtures/order.dto.json +25 -0
- procesio_cli/tools/procesio/dto/webhook/template.dto.json +14 -0
- procesio_cli/tools/procesio/environments.py +264 -0
- procesio_cli/tools/procesio/errors.py +166 -0
- procesio_cli/tools/procesio/flowmodel/README.md +37 -0
- procesio_cli/tools/procesio/flowmodel/__init__.py +14 -0
- procesio_cli/tools/procesio/flowmodel/digest.py +223 -0
- procesio_cli/tools/procesio/flowmodel/families.py +34 -0
- procesio_cli/tools/procesio/flowmodel/fevalidation.py +1098 -0
- procesio_cli/tools/procesio/flowmodel/formdigest.py +232 -0
- procesio_cli/tools/procesio/flowmodel/inspect.py +78 -0
- procesio_cli/tools/procesio/flowmodel/model.py +82 -0
- procesio_cli/tools/procesio/flowmodel/nodeparam.py +871 -0
- procesio_cli/tools/procesio/flowmodel/reader.py +191 -0
- procesio_cli/tools/procesio/flowmodel/sqlparam.py +410 -0
- procesio_cli/tools/procesio/form_code_keys.py +50 -0
- procesio_cli/tools/procesio/formlint.py +292 -0
- procesio_cli/tools/procesio/gen_manifest.py +118 -0
- procesio_cli/tools/procesio/handlers/__init__.py +1 -0
- procesio_cli/tools/procesio/handlers/auth_actions.py +103 -0
- procesio_cli/tools/procesio/handlers/catalog.py +147 -0
- procesio_cli/tools/procesio/handlers/common.py +90 -0
- procesio_cli/tools/procesio/handlers/customaction_ops.py +146 -0
- procesio_cli/tools/procesio/handlers/datastore_ops.py +482 -0
- procesio_cli/tools/procesio/handlers/datatype_ops.py +275 -0
- procesio_cli/tools/procesio/handlers/dto_actions.py +258 -0
- procesio_cli/tools/procesio/handlers/environment_admin.py +146 -0
- procesio_cli/tools/procesio/handlers/fevalidate.py +236 -0
- procesio_cli/tools/procesio/handlers/files.py +141 -0
- procesio_cli/tools/procesio/handlers/flowgraph.py +229 -0
- procesio_cli/tools/procesio/handlers/flowlint.py +269 -0
- procesio_cli/tools/procesio/handlers/form_chains.py +336 -0
- procesio_cli/tools/procesio/handlers/form_code.py +277 -0
- procesio_cli/tools/procesio/handlers/form_element.py +199 -0
- procesio_cli/tools/procesio/handlers/form_events.py +441 -0
- procesio_cli/tools/procesio/handlers/form_update.py +270 -0
- procesio_cli/tools/procesio/handlers/formprocess.py +211 -0
- procesio_cli/tools/procesio/handlers/generic.py +135 -0
- procesio_cli/tools/procesio/handlers/layout.py +137 -0
- procesio_cli/tools/procesio/handlers/nodeparams.py +1018 -0
- procesio_cli/tools/procesio/handlers/process_layout.py +189 -0
- procesio_cli/tools/procesio/handlers/process_naming.py +213 -0
- procesio_cli/tools/procesio/handlers/processes.py +416 -0
- procesio_cli/tools/procesio/handlers/profile_admin.py +184 -0
- procesio_cli/tools/procesio/handlers/resource_ops.py +447 -0
- procesio_cli/tools/procesio/handlers/schedules.py +320 -0
- procesio_cli/tools/procesio/handlers/sqlactions.py +232 -0
- procesio_cli/tools/procesio/handlers/transport.py +463 -0
- procesio_cli/tools/procesio/handlers/usageguide.py +222 -0
- procesio_cli/tools/procesio/layout/README.md +196 -0
- procesio_cli/tools/procesio/layout/__init__.py +11 -0
- procesio_cli/tools/procesio/layout/adapter.py +736 -0
- procesio_cli/tools/procesio/layout/cycles.py +186 -0
- procesio_cli/tools/procesio/layout/dispatch.py +39 -0
- procesio_cli/tools/procesio/layout/elk/elk_runner.mjs +50 -0
- procesio_cli/tools/procesio/layout/elk/package-lock.json +21 -0
- procesio_cli/tools/procesio/layout/elk/package.json +11 -0
- procesio_cli/tools/procesio/layout/elk_engine.py +568 -0
- procesio_cli/tools/procesio/layout/engine.py +1311 -0
- procesio_cli/tools/procesio/layout/report.py +187 -0
- procesio_cli/tools/procesio/main.py +179 -0
- procesio_cli/tools/procesio/profiles.py +193 -0
- procesio_cli/tools/procesio/reliability.py +193 -0
- procesio_cli/tools/procesio/sqlformat.py +167 -0
- procesio_cli/tools/procesio/swagger.py +51 -0
- procesio_cli/tools/procesio/tests/__init__.py +0 -0
- procesio_cli/tools/procesio/tests/conftest.py +129 -0
- procesio_cli/tools/procesio/tests/test_auth.py +200 -0
- procesio_cli/tools/procesio/tests/test_autogen.py +78 -0
- procesio_cli/tools/procesio/tests/test_builder_attrpath_sqlbind.py +180 -0
- procesio_cli/tools/procesio/tests/test_builder_custom_action_catalog.py +72 -0
- procesio_cli/tools/procesio/tests/test_client.py +114 -0
- procesio_cli/tools/procesio/tests/test_client_reliability.py +139 -0
- procesio_cli/tools/procesio/tests/test_customaction.py +103 -0
- procesio_cli/tools/procesio/tests/test_customaction_test.py +80 -0
- procesio_cli/tools/procesio/tests/test_cycles.py +69 -0
- procesio_cli/tools/procesio/tests/test_datastore.py +378 -0
- procesio_cli/tools/procesio/tests/test_datastore_node.py +94 -0
- procesio_cli/tools/procesio/tests/test_dispatch.py +170 -0
- procesio_cli/tools/procesio/tests/test_document_page_size.py +19 -0
- procesio_cli/tools/procesio/tests/test_dto.py +734 -0
- procesio_cli/tools/procesio/tests/test_edit_dryrun_preview.py +96 -0
- procesio_cli/tools/procesio/tests/test_elk_layout.py +221 -0
- procesio_cli/tools/procesio/tests/test_environments.py +214 -0
- procesio_cli/tools/procesio/tests/test_fevalidate_gate.py +104 -0
- procesio_cli/tools/procesio/tests/test_fevalidation.py +317 -0
- procesio_cli/tools/procesio/tests/test_fieldpath_variables.py +87 -0
- procesio_cli/tools/procesio/tests/test_files.py +72 -0
- procesio_cli/tools/procesio/tests/test_flow_digest.py +202 -0
- procesio_cli/tools/procesio/tests/test_flowlint.py +114 -0
- procesio_cli/tools/procesio/tests/test_flowmodel.py +158 -0
- procesio_cli/tools/procesio/tests/test_form_add_element.py +256 -0
- procesio_cli/tools/procesio/tests/test_form_approval_gaps.py +319 -0
- procesio_cli/tools/procesio/tests/test_form_auto_names.py +60 -0
- procesio_cli/tools/procesio/tests/test_form_chains.py +217 -0
- procesio_cli/tools/procesio/tests/test_form_code_actions.py +188 -0
- procesio_cli/tools/procesio/tests/test_form_code_keys.py +92 -0
- procesio_cli/tools/procesio/tests/test_form_code_style.py +178 -0
- procesio_cli/tools/procesio/tests/test_form_delete_element.py +99 -0
- procesio_cli/tools/procesio/tests/test_form_element_config.py +141 -0
- procesio_cli/tools/procesio/tests/test_form_event_maps.py +135 -0
- procesio_cli/tools/procesio/tests/test_form_event_ops.py +376 -0
- procesio_cli/tools/procesio/tests/test_form_event_replace.py +86 -0
- procesio_cli/tools/procesio/tests/test_form_events.py +92 -0
- procesio_cli/tools/procesio/tests/test_form_parity.py +171 -0
- procesio_cli/tools/procesio/tests/test_form_select_source.py +74 -0
- procesio_cli/tools/procesio/tests/test_form_sync.py +35 -0
- procesio_cli/tools/procesio/tests/test_form_update.py +133 -0
- procesio_cli/tools/procesio/tests/test_formlint.py +155 -0
- procesio_cli/tools/procesio/tests/test_formlint_css_scope.py +38 -0
- procesio_cli/tools/procesio/tests/test_formlint_field_names.py +83 -0
- procesio_cli/tools/procesio/tests/test_guards.py +110 -0
- procesio_cli/tools/procesio/tests/test_hidden_setting_types.py +39 -0
- procesio_cli/tools/procesio/tests/test_html2docx_asset.py +132 -0
- procesio_cli/tools/procesio/tests/test_key_handle_terminology.py +45 -0
- procesio_cli/tools/procesio/tests/test_layout.py +574 -0
- procesio_cli/tools/procesio/tests/test_layout_report.py +88 -0
- procesio_cli/tools/procesio/tests/test_manifest_sync.py +68 -0
- procesio_cli/tools/procesio/tests/test_nodedelete.py +151 -0
- procesio_cli/tools/procesio/tests/test_nodeparam.py +826 -0
- procesio_cli/tools/procesio/tests/test_normalize.py +70 -0
- procesio_cli/tools/procesio/tests/test_payload_docs.py +86 -0
- procesio_cli/tools/procesio/tests/test_process_action_naming.py +170 -0
- procesio_cli/tools/procesio/tests/test_process_call_api_seed.py +46 -0
- procesio_cli/tools/procesio/tests/test_process_ext.py +373 -0
- procesio_cli/tools/procesio/tests/test_process_invariants.py +183 -0
- procesio_cli/tools/procesio/tests/test_process_layout.py +117 -0
- procesio_cli/tools/procesio/tests/test_process_naming.py +188 -0
- procesio_cli/tools/procesio/tests/test_process_query_store.py +172 -0
- procesio_cli/tools/procesio/tests/test_processinfo_variable.py +56 -0
- procesio_cli/tools/procesio/tests/test_profiles.py +127 -0
- procesio_cli/tools/procesio/tests/test_put_projects.py +41 -0
- procesio_cli/tools/procesio/tests/test_reliability.py +107 -0
- procesio_cli/tools/procesio/tests/test_removal_gate.py +95 -0
- procesio_cli/tools/procesio/tests/test_resource_ops.py +173 -0
- procesio_cli/tools/procesio/tests/test_save_validity.py +207 -0
- procesio_cli/tools/procesio/tests/test_schedule_cron_shape.py +111 -0
- procesio_cli/tools/procesio/tests/test_schedules.py +309 -0
- procesio_cli/tools/procesio/tests/test_sqlconvert.py +234 -0
- procesio_cli/tools/procesio/tests/test_sqlformat.py +83 -0
- procesio_cli/tools/procesio/tests/test_sqlparam.py +187 -0
- procesio_cli/tools/procesio/tests/test_superseded_actions.py +54 -0
- procesio_cli/tools/procesio/tests/test_surgical_concurrency.py +156 -0
- procesio_cli/tools/procesio/tests/test_tier4.py +123 -0
- procesio_cli/tools/procesio/tests/test_transport.py +147 -0
- procesio_cli/tools/procesio/tests/test_transport_data_stores.py +168 -0
- procesio_cli/tools/procesio/tests/test_transport_mapper_repair.py +150 -0
- procesio_cli/tools/procesio/tests/test_transport_webhook_strip.py +253 -0
- procesio_cli/tools/procesio/tests/test_usage_guide.py +96 -0
- procesio_cli/tools/procesio/tool.yaml +12486 -0
- procesio_cli/tools/sqlserver/MOVED-TO-CENTRAL.md +29 -0
- procesio_cli/tools/sqlserver/README.md +192 -0
- procesio_cli/tools/sqlserver/SKILL.md +37 -0
- procesio_cli/tools/sqlserver/SQLSERVER-SCHEMA-NOTES.md +284 -0
- procesio_cli/tools/sqlserver/__init__.py +0 -0
- procesio_cli/tools/sqlserver/connection.py +242 -0
- procesio_cli/tools/sqlserver/main.py +521 -0
- procesio_cli/tools/sqlserver/profiles.template.json +1 -0
- procesio_cli/tools/sqlserver/schema.py +600 -0
- procesio_cli/tools/sqlserver/tests/__init__.py +0 -0
- procesio_cli/tools/sqlserver/tests/conftest.py +21 -0
- procesio_cli/tools/sqlserver/tests/test_connection.py +75 -0
- procesio_cli/tools/sqlserver/tests/test_dbprofiles.py +92 -0
- procesio_cli/tools/sqlserver/tests/test_dbquery.py +220 -0
- procesio_cli/tools/sqlserver/tests/test_manifest_sync.py +68 -0
- procesio_cli/tools/sqlserver/tests/test_sqlserver.py +326 -0
- procesio_cli/tools/sqlserver/tests/test_sqlserver_schema.py +488 -0
- procesio_cli/tools/sqlserver/tool.yaml +123 -0
- procesio_cli/tools/web/README.md +381 -0
- procesio_cli/tools/web/SKILL.md +37 -0
- procesio_cli/tools/web/WEB-AUTOMATION-NOTES.md +277 -0
- procesio_cli/tools/web/__init__.py +21 -0
- procesio_cli/tools/web/actiondef.py +24 -0
- procesio_cli/tools/web/api.py +444 -0
- procesio_cli/tools/web/brokered.py +107 -0
- procesio_cli/tools/web/driver.py +839 -0
- procesio_cli/tools/web/drivercontract.py +44 -0
- procesio_cli/tools/web/errors.py +54 -0
- procesio_cli/tools/web/handlers/__init__.py +1 -0
- procesio_cli/tools/web/handlers/archive.py +367 -0
- procesio_cli/tools/web/handlers/browse.py +182 -0
- procesio_cli/tools/web/handlers/contacts.py +74 -0
- procesio_cli/tools/web/handlers/sessmgmt.py +120 -0
- procesio_cli/tools/web/main.py +128 -0
- procesio_cli/tools/web/sessions.py +253 -0
- procesio_cli/tools/web/steps.py +162 -0
- procesio_cli/tools/web/tests/__init__.py +0 -0
- procesio_cli/tools/web/tests/conftest.py +197 -0
- procesio_cli/tools/web/tests/test_api.py +330 -0
- procesio_cli/tools/web/tests/test_archive.py +190 -0
- procesio_cli/tools/web/tests/test_brokered.py +138 -0
- procesio_cli/tools/web/tests/test_dispatch.py +132 -0
- procesio_cli/tools/web/tests/test_errors.py +60 -0
- procesio_cli/tools/web/tests/test_manifest_sync.py +76 -0
- procesio_cli/tools/web/tests/test_page_contacts.py +68 -0
- procesio_cli/tools/web/tests/test_sessions.py +145 -0
- procesio_cli/tools/web/tests/test_steps.py +210 -0
- procesio_cli/tools/web/tool.yaml +184 -0
- procesio_cli/tools/xlsx/README.md +34 -0
- procesio_cli/tools/xlsx/SKILL.md +28 -0
- procesio_cli/tools/xlsx/__init__.py +0 -0
- procesio_cli/tools/xlsx/main.py +129 -0
- procesio_cli/tools/xlsx/reader.py +91 -0
- procesio_cli/tools/xlsx/tests/__init__.py +0 -0
- procesio_cli/tools/xlsx/tests/conftest.py +39 -0
- procesio_cli/tools/xlsx/tests/test_xlsx.py +103 -0
- procesio_cli/tools/xlsx/tool.yaml +30 -0
- procesio_cli/webplatform/aat_mcp/AAT-MCP-NOTES.md +244 -0
- procesio_cli/webplatform/aat_mcp/README.md +78 -0
- procesio_cli/webplatform/aat_mcp/bridge.py +705 -0
- procesio_cli/webplatform/aat_mcp/chat_server.py +194 -0
- procesio_cli/webplatform/aat_mcp/chat_surface.snapshot.json +668 -0
- procesio_cli/webplatform/aat_mcp/chat_surface.yaml +372 -0
- procesio_cli/webplatform/aat_mcp/gate.py +68 -0
- procesio_cli/webplatform/aat_mcp/http_server.py +139 -0
- procesio_cli/webplatform/aat_mcp/protocol.py +175 -0
- procesio_cli/webplatform/aat_mcp/server.py +306 -0
- procesio_cli/webplatform/aat_mcp/tests/conftest.py +8 -0
- procesio_cli/webplatform/aat_mcp/tests/test_aat_mcp.py +255 -0
- procesio_cli/webplatform/aat_mcp/tests/test_chat_surface.py +247 -0
- procesio_cli/webplatform/aat_mcp/tests/test_discovery.py +489 -0
- procesio_cli/webplatform/aat_mcp/tests/test_multiuser.py +86 -0
- procesio_cli/webplatform/aat_mcp/tests/test_progressive_discovery.py +89 -0
- procesio_cli/webplatform/aat_mcp/tests/test_protocol.py +180 -0
- procesio_cli/webplatform/aat_mcp/tests/test_skill_discovery.py +23 -0
- procesio_cli-0.1.1.dist-info/METADATA +467 -0
- procesio_cli-0.1.1.dist-info/RECORD +647 -0
- procesio_cli-0.1.1.dist-info/WHEEL +4 -0
- procesio_cli-0.1.1.dist-info/entry_points.txt +7 -0
- procesio_cli-0.1.1.dist-info/licenses/LICENSE +201 -0
- procesio_cli-0.1.1.dist-info/licenses/NOTICE +5 -0
|
@@ -0,0 +1,826 @@
|
|
|
1
|
+
"""Tests for the surgical node-parameter patcher (flowmodel/nodeparam.py).
|
|
2
|
+
|
|
3
|
+
Guards the two things that make a one-literal edit safe: the string-only scope (a structured
|
|
4
|
+
parameter must be refused, not silently text-patched) and the placeholder contract (the `<%N%>`
|
|
5
|
+
set binds `variable[]` positionally, so it may not drift without an explicit opt-in).
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from tools.procesio.flowmodel import nodeparam
|
|
12
|
+
|
|
13
|
+
ENDPOINT = "45a7bbde-7e2a-4bd4-a6d3-8420f7790002"
|
|
14
|
+
PAYLOAD = "45a7bbde-7e2a-4bd4-a6d3-8420f7790003"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _flow():
|
|
18
|
+
"""A Call API node shaped like the live DTO: a string Endpoint bound to one variable, plus a
|
|
19
|
+
structured Request Parameters tab, mirrored in the designer side-pannel."""
|
|
20
|
+
return {
|
|
21
|
+
"id": "flow-1", "title": "Rates",
|
|
22
|
+
"variables": [{"id": "var-year", "name": "year", "type": 20}],
|
|
23
|
+
"actions": [
|
|
24
|
+
{"id": "act-1", "actionName": "Get all by year", "actionTemplateName": "Call API",
|
|
25
|
+
"parameters": [
|
|
26
|
+
{"tabPropertyId": ENDPOINT, "value": "https://old.example/x<%0%>.xml",
|
|
27
|
+
"variable": [{"id": 0, "variableId": "var-year", "attribute": None}]},
|
|
28
|
+
{"tabPropertyId": PAYLOAD, "value": {"headers": [], "queryParams": []},
|
|
29
|
+
"variable": []},
|
|
30
|
+
],
|
|
31
|
+
"customData": {"configuration": [{"settings": [
|
|
32
|
+
{"id": "side", "type": "side-pannel", "label": "Configure Request", "value": [
|
|
33
|
+
{"id": ENDPOINT, "label": "Endpoint", "value": "https://old.example/xvar-year.xml"},
|
|
34
|
+
{"id": PAYLOAD, "label": "Request Parameters", "value": {}},
|
|
35
|
+
]},
|
|
36
|
+
]}]}},
|
|
37
|
+
],
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_find_node_by_id_and_by_label():
|
|
42
|
+
f = _flow()
|
|
43
|
+
assert nodeparam.find_node(f, "act-1")["id"] == "act-1"
|
|
44
|
+
assert nodeparam.find_node(f, "Get all by year")["id"] == "act-1"
|
|
45
|
+
assert nodeparam.find_node(f, "nope") is None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_find_param_by_label_and_by_id():
|
|
49
|
+
n = _flow()["actions"][0]
|
|
50
|
+
assert nodeparam.find_param(n, "Endpoint")["tabPropertyId"] == ENDPOINT
|
|
51
|
+
assert nodeparam.find_param(n, "endpoint")["tabPropertyId"] == ENDPOINT # case-insensitive
|
|
52
|
+
assert nodeparam.find_param(n, ENDPOINT)["tabPropertyId"] == ENDPOINT
|
|
53
|
+
assert nodeparam.find_param(n, "Missing") is None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_describe_node_labels_binds_and_editability():
|
|
57
|
+
f = _flow()
|
|
58
|
+
d = nodeparam.describe_node(f, f["actions"][0])
|
|
59
|
+
by_label = {p["label"]: p for p in d["parameters"]}
|
|
60
|
+
assert by_label["Endpoint"]["editable"] is True
|
|
61
|
+
assert by_label["Endpoint"]["binds"] == [{"index": 0, "variable": "year", "id": "var-year"}]
|
|
62
|
+
assert by_label["Request Parameters"]["editable"] is False
|
|
63
|
+
assert by_label["Request Parameters"]["value"] == "<structured>"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_set_param_value_rewrites_host_and_keeps_binding():
|
|
67
|
+
f = _flow()
|
|
68
|
+
n = f["actions"][0]
|
|
69
|
+
p = nodeparam.find_param(n, "Endpoint")
|
|
70
|
+
r = nodeparam.set_param_value(n, p, "https://new.example/x<%0%>.xml")
|
|
71
|
+
assert r["changed"] is True
|
|
72
|
+
assert p["value"] == "https://new.example/x<%0%>.xml"
|
|
73
|
+
assert p["variable"] == [{"id": 0, "variableId": "var-year", "attribute": None}]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_set_param_value_is_noop_when_identical():
|
|
77
|
+
f = _flow()
|
|
78
|
+
n = f["actions"][0]
|
|
79
|
+
p = nodeparam.find_param(n, "Endpoint")
|
|
80
|
+
assert nodeparam.set_param_value(n, p, p["value"])["changed"] is False
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_structured_parameter_is_refused():
|
|
84
|
+
f = _flow()
|
|
85
|
+
n = f["actions"][0]
|
|
86
|
+
p = nodeparam.find_param(n, "Request Parameters")
|
|
87
|
+
with pytest.raises(ValueError, match="not text"):
|
|
88
|
+
nodeparam.set_param_value(n, p, "anything")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_dropping_a_placeholder_is_refused_unless_opted_in():
|
|
92
|
+
f = _flow()
|
|
93
|
+
n = f["actions"][0]
|
|
94
|
+
p = nodeparam.find_param(n, "Endpoint")
|
|
95
|
+
with pytest.raises(ValueError, match="placeholder set"):
|
|
96
|
+
nodeparam.set_param_value(n, p, "https://new.example/fixed.xml")
|
|
97
|
+
nodeparam.set_param_value(n, p, "https://new.example/fixed.xml", allow_binding_change=True)
|
|
98
|
+
assert p["value"] == "https://new.example/fixed.xml"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_placeholders_reports_the_binding_contract():
|
|
102
|
+
assert nodeparam.placeholders("a<%1%>b<%0%>c<%1%>") == [0, 1]
|
|
103
|
+
assert nodeparam.placeholders({"not": "a string"}) == []
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _map_node():
|
|
107
|
+
"""A Map Data node shaped like the live DTO: the runtime row holds `<%1%>-12-01`, the designer
|
|
108
|
+
mirror the same literal appended to the variable GUID. The literal is identical in both."""
|
|
109
|
+
return {
|
|
110
|
+
"id": "map-1", "actionName": "set start currentDate", "actionTemplateName": "Map Data",
|
|
111
|
+
"parameters": [{"tabPropertyId": "map-pid", "variable": [], "value": [
|
|
112
|
+
{"id": 0, "slugId": "map-process-data",
|
|
113
|
+
"source": {"value": "<%1%>-12-01", "variable": [{"id": 1, "variableId": "var-year"}]},
|
|
114
|
+
"destination": {"id": 0, "variableId": "var-cur"}},
|
|
115
|
+
{"id": 1, "slugId": "map-process-data",
|
|
116
|
+
"source": {"value": "<%3%>-12-01", "variable": [{"id": 3, "variableId": "var-year"}]},
|
|
117
|
+
"destination": {"id": 2, "variableId": "var-last"}},
|
|
118
|
+
]}],
|
|
119
|
+
"customData": {"configuration": [{"settings": [
|
|
120
|
+
{"id": "side", "type": "side-pannel", "label": "Map Process Data", "value": [
|
|
121
|
+
{"id": "map-pid", "label": "Map Process Variables", "type": "map-process-data",
|
|
122
|
+
"value": [{"id": 0, "destination": "var-cur", "source": "var-year-12-01"},
|
|
123
|
+
{"id": 1, "destination": "var-last", "source": "var-year-12-01"}]},
|
|
124
|
+
]},
|
|
125
|
+
]}]},
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_replace_text_reaches_both_layers_of_a_structured_parameter():
|
|
130
|
+
n = _map_node()
|
|
131
|
+
hits = nodeparam.replace_text(n, "-12-01", "-01-01")
|
|
132
|
+
assert sum(h["count"] for h in hits) == 4
|
|
133
|
+
assert {h["layer"] for h in hits} == {"runtime", "designer"}
|
|
134
|
+
rows = n["parameters"][0]["value"]
|
|
135
|
+
assert rows[0]["source"]["value"] == "<%1%>-01-01"
|
|
136
|
+
assert rows[1]["source"]["value"] == "<%3%>-01-01"
|
|
137
|
+
designer = n["customData"]["configuration"][0]["settings"][0]["value"][0]["value"]
|
|
138
|
+
assert [r["source"] for r in designer] == ["var-year-01-01", "var-year-01-01"]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_replace_text_keeps_variable_bindings_untouched():
|
|
142
|
+
n = _map_node()
|
|
143
|
+
nodeparam.replace_text(n, "-12-01", "-01-01")
|
|
144
|
+
assert n["parameters"][0]["value"][0]["source"]["variable"] == [{"id": 1, "variableId": "var-year"}]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_replace_text_reports_nothing_when_the_literal_is_absent():
|
|
148
|
+
n = _map_node()
|
|
149
|
+
assert nodeparam.replace_text(n, "-99-99", "-01-01") == []
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def test_replace_text_can_be_narrowed_to_one_property():
|
|
153
|
+
f = _flow()
|
|
154
|
+
n = f["actions"][0]
|
|
155
|
+
hits = nodeparam.replace_text(n, "old.example", "new.example", property_key="Endpoint")
|
|
156
|
+
assert sum(h["count"] for h in hits) == 2 # runtime param + designer mirror
|
|
157
|
+
assert n["parameters"][0]["value"] == "https://new.example/x<%0%>.xml"
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_replace_text_rejects_an_empty_needle():
|
|
161
|
+
with pytest.raises(ValueError, match="non-empty"):
|
|
162
|
+
nodeparam.replace_text(_map_node(), "", "x")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _typed_flow():
|
|
166
|
+
return {"id": "f", "title": "T", "actions": [], "variables": [
|
|
167
|
+
{"id": "v-list", "name": "exchangeRateList", "type": 20,
|
|
168
|
+
"dataType": "0317bfee-b2f5-4bde-bfe8-121212121220", "isList": True},
|
|
169
|
+
{"id": "v-out", "name": "exchangeRate", "type": 30,
|
|
170
|
+
"dataType": "0317bfee-b2f5-4bde-bfe8-121212121220", "isList": False},
|
|
171
|
+
{"id": "v-in", "name": "payload", "type": 10,
|
|
172
|
+
"dataType": "0317bfee-b2f5-4bde-bfe8-121212121214", "isList": False},
|
|
173
|
+
]}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
OBJECT = "0317bfee-b2f5-4bde-bfe8-121212121221"
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def test_find_variable_by_name_and_id():
|
|
180
|
+
f = _typed_flow()
|
|
181
|
+
assert nodeparam.find_variable(f, "exchangeRateList")["id"] == "v-list"
|
|
182
|
+
assert nodeparam.find_variable(f, "v-out")["name"] == "exchangeRate"
|
|
183
|
+
assert nodeparam.find_variable(f, "ghost") is None
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def test_retyping_an_internal_variable_is_allowed():
|
|
187
|
+
f = _typed_flow()
|
|
188
|
+
v = nodeparam.find_variable(f, "exchangeRateList")
|
|
189
|
+
r = nodeparam.set_variable_type(f, v, OBJECT)
|
|
190
|
+
assert r["changed"] is True and r["direction"] == "process"
|
|
191
|
+
assert v["dataType"] == OBJECT and v["isList"] is True # isList untouched when not passed
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def test_retyping_an_output_variable_needs_the_contract_override():
|
|
195
|
+
f = _typed_flow()
|
|
196
|
+
v = nodeparam.find_variable(f, "exchangeRate")
|
|
197
|
+
with pytest.raises(ValueError, match="public contract"):
|
|
198
|
+
nodeparam.set_variable_type(f, v, OBJECT)
|
|
199
|
+
nodeparam.set_variable_type(f, v, OBJECT, allow_contract_change=True)
|
|
200
|
+
assert v["dataType"] == OBJECT
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def test_retyping_an_input_variable_needs_the_contract_override():
|
|
204
|
+
f = _typed_flow()
|
|
205
|
+
v = nodeparam.find_variable(f, "payload")
|
|
206
|
+
with pytest.raises(ValueError, match="public contract"):
|
|
207
|
+
nodeparam.set_variable_type(f, v, OBJECT)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def test_retype_is_a_noop_when_already_that_type():
|
|
211
|
+
f = _typed_flow()
|
|
212
|
+
v = nodeparam.find_variable(f, "exchangeRateList")
|
|
213
|
+
assert nodeparam.set_variable_type(f, v, v["dataType"], is_list=True)["changed"] is False
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def test_clearing_required_on_an_input_needs_no_override():
|
|
217
|
+
"""Loosening cannot break a caller: every payload that was valid stays valid."""
|
|
218
|
+
f = _typed_flow()
|
|
219
|
+
v = nodeparam.find_variable(f, "payload")
|
|
220
|
+
v["isRequired"] = True
|
|
221
|
+
r = nodeparam.set_variable_required(f, v, False)
|
|
222
|
+
assert r["changed"] is True and r["direction"] == "input"
|
|
223
|
+
assert v["isRequired"] is False
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_making_an_input_required_needs_the_contract_override():
|
|
227
|
+
"""Tightening breaks every caller that legitimately omitted the field."""
|
|
228
|
+
f = _typed_flow()
|
|
229
|
+
v = nodeparam.find_variable(f, "payload")
|
|
230
|
+
v["isRequired"] = False
|
|
231
|
+
with pytest.raises(ValueError, match="public contract"):
|
|
232
|
+
nodeparam.set_variable_required(f, v, True)
|
|
233
|
+
nodeparam.set_variable_required(f, v, True, allow_contract_change=True)
|
|
234
|
+
assert v["isRequired"] is True
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def test_isrequired_is_refused_on_a_non_input_variable():
|
|
238
|
+
"""Only an input carries a caller-supplied value, so the flag means nothing elsewhere."""
|
|
239
|
+
f = _typed_flow()
|
|
240
|
+
for name in ("exchangeRate", "exchangeRateList"):
|
|
241
|
+
with pytest.raises(ValueError, match="only an input variable"):
|
|
242
|
+
nodeparam.set_variable_required(f, nodeparam.find_variable(f, name), False)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def test_setting_required_to_its_current_value_is_a_noop():
|
|
246
|
+
f = _typed_flow()
|
|
247
|
+
v = nodeparam.find_variable(f, "payload")
|
|
248
|
+
v["isRequired"] = True
|
|
249
|
+
assert nodeparam.set_variable_required(f, v, True)["changed"] is False
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def test_add_variable_appends_and_is_addressable_by_name_and_id():
|
|
253
|
+
f = _typed_flow()
|
|
254
|
+
before = len(f["variables"])
|
|
255
|
+
v = nodeparam.add_variable(f, "historyText", "string", "output")
|
|
256
|
+
assert len(f["variables"]) == before + 1
|
|
257
|
+
assert v["type"] == 30 and v["isList"] is False and v["isRequired"] is False
|
|
258
|
+
assert nodeparam.find_variable(f, "historyText")["id"] == v["id"]
|
|
259
|
+
assert nodeparam.find_variable(f, v["id"])["name"] == "historyText"
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def test_add_variable_refuses_a_duplicate_name():
|
|
263
|
+
"""The form-event tooling resolves names to ids, so two same-named variables are ambiguous."""
|
|
264
|
+
f = _typed_flow()
|
|
265
|
+
with pytest.raises(ValueError, match="already exists"):
|
|
266
|
+
nodeparam.add_variable(f, "payload", "string", "input")
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def test_add_variable_refuses_an_unknown_direction_or_type():
|
|
270
|
+
f = _typed_flow()
|
|
271
|
+
with pytest.raises(ValueError, match="direction must be"):
|
|
272
|
+
nodeparam.add_variable(f, "x", "string", "sideways")
|
|
273
|
+
with pytest.raises(ValueError, match="unknown data type"):
|
|
274
|
+
nodeparam.add_variable(f, "y", "stringy", "output")
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _chain_flow():
|
|
278
|
+
"""start -> a -> b -> stop, ports living on the SOURCE action."""
|
|
279
|
+
def port(src, dst):
|
|
280
|
+
return {"id": f"p-{src}-{dst}", "flowId": "f", "sourceId": src, "destinationId": dst}
|
|
281
|
+
return {"id": "f", "title": "T", "variables": [], "actions": [
|
|
282
|
+
{"id": "a", "actionName": "a", "actionTemplateName": "Node", "ports": [port("a", "b")]},
|
|
283
|
+
{"id": "b", "actionName": "b", "actionTemplateName": "Node", "ports": [port("b", "stop")]},
|
|
284
|
+
]}
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def test_insert_node_takes_the_successor_and_repoints_the_anchor():
|
|
288
|
+
"""Both edits, or the graph is broken in a way that still validates."""
|
|
289
|
+
f = _chain_flow()
|
|
290
|
+
anchor = f["actions"][0]
|
|
291
|
+
new = {"id": "n", "actionName": "mid", "actionTemplateName": "Node"}
|
|
292
|
+
ok, msg = nodeparam.insert_node(f, anchor, new)
|
|
293
|
+
assert ok and "between" in msg
|
|
294
|
+
assert anchor["ports"][0]["destinationId"] == "n" # anchor now points at the new node
|
|
295
|
+
assert new["ports"][0]["destinationId"] == "b" # new node inherited the successor
|
|
296
|
+
assert [a["id"] for a in f["actions"]] == ["a", "b", "n"]
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def test_insert_node_after_a_tail_node_needs_no_successor():
|
|
300
|
+
f = _chain_flow()
|
|
301
|
+
tail = f["actions"][1]
|
|
302
|
+
tail["ports"] = [] # nothing downstream
|
|
303
|
+
ok, msg = nodeparam.insert_node(f, tail, {"id": "n", "actionName": "mid"})
|
|
304
|
+
assert ok and "no successor" in msg
|
|
305
|
+
assert f["actions"][-1]["id"] == "n"
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def test_insert_node_refuses_a_branching_anchor():
|
|
309
|
+
"""Which branch the new node belongs on is a design decision, not a default."""
|
|
310
|
+
f = _chain_flow()
|
|
311
|
+
a = f["actions"][0]
|
|
312
|
+
a["ports"].append({"id": "p2", "sourceId": "a", "destinationId": "b"})
|
|
313
|
+
ok, msg = nodeparam.insert_node(f, a, {"id": "n"})
|
|
314
|
+
assert ok is False and "outgoing ports" in msg
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def test_insert_node_refuses_an_anchor_from_another_flow():
|
|
318
|
+
f = _chain_flow()
|
|
319
|
+
ok, msg = nodeparam.insert_node(f, {"id": "ghost", "actionName": "ghost"}, {"id": "n"})
|
|
320
|
+
assert ok is False and "not in this flow" in msg
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def test_to_live_action_lowercases_the_dto_and_its_parameter_rows():
|
|
324
|
+
"""The builder emits the CREATE shape; a live flow is camelCase. Splicing one into the
|
|
325
|
+
other unconverted is accepted by the API and renders inconsistently."""
|
|
326
|
+
dto = {"Id": "n", "FlowId": "f", "ActionName": "mid", "CustomData": {"name": "mid"},
|
|
327
|
+
"Parameters": [{"TabPropertyId": "t", "Value": "v", "Variable": []}]}
|
|
328
|
+
live = nodeparam.to_live_action(dto)
|
|
329
|
+
assert set(live) == {"id", "flowId", "actionName", "customData", "parameters"}
|
|
330
|
+
assert live["parameters"][0] == {"tabPropertyId": "t", "value": "v", "variable": []}
|
|
331
|
+
assert live["customData"] == {"name": "mid"}
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def test_add_variable_resolves_aliases_and_passes_guids_through():
|
|
335
|
+
f = _typed_flow()
|
|
336
|
+
assert nodeparam.add_variable(f, "a", "string", "process")["dataType"].endswith("121214")
|
|
337
|
+
assert nodeparam.add_variable(f, "b", "file", "input")["dataType"].endswith("121219")
|
|
338
|
+
assert nodeparam.add_variable(f, "c", OBJECT, "output")["dataType"] == OBJECT
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def test_defaulting_a_process_variable_needs_no_override():
|
|
342
|
+
"""A process variable has no caller and no signature, so stamping its default breaks nobody."""
|
|
343
|
+
f = _typed_flow()
|
|
344
|
+
v = nodeparam.find_variable(f, "exchangeRateList")
|
|
345
|
+
r = nodeparam.set_variable_default(f, v, [1, 2])
|
|
346
|
+
assert r["changed"] is True and r["direction"] == "process"
|
|
347
|
+
assert r["before"] == {"defaultValue": None} and v["defaultValue"] == [1, 2]
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def test_defaulting_an_input_needs_the_contract_override():
|
|
351
|
+
"""Changing what a run does when the caller supplies nothing is a contract change, even
|
|
352
|
+
though the signature is untouched - so the refusal has to come before the write."""
|
|
353
|
+
f = _typed_flow()
|
|
354
|
+
v = nodeparam.find_variable(f, "payload")
|
|
355
|
+
with pytest.raises(ValueError, match="allow-contract-change"):
|
|
356
|
+
nodeparam.set_variable_default(f, v, {"a": 1})
|
|
357
|
+
assert "defaultValue" not in v
|
|
358
|
+
nodeparam.set_variable_default(f, v, {"a": 1}, allow_contract_change=True)
|
|
359
|
+
assert v["defaultValue"] == {"a": 1}
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def test_the_output_direction_is_guarded_too():
|
|
363
|
+
"""Both signature-bearing directions are behind the flag, not just the input one."""
|
|
364
|
+
f = _typed_flow()
|
|
365
|
+
with pytest.raises(ValueError, match="is an output variable"):
|
|
366
|
+
nodeparam.set_variable_default(f, nodeparam.find_variable(f, "exchangeRate"), 1.5)
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def test_clearing_a_default_is_guarded_as_well_unlike_isrequired():
|
|
370
|
+
"""The asymmetry between this and set_variable_required: loosening `isRequired` can never
|
|
371
|
+
break a caller, so it is allowed outright - but REMOVING a default is as much a contract
|
|
372
|
+
change as adding one, because a caller that omitted the field now gets nothing instead of
|
|
373
|
+
the value it silently relied on. The guard is on the DIRECTION, so it fires either way."""
|
|
374
|
+
f = _typed_flow()
|
|
375
|
+
v = nodeparam.find_variable(f, "payload")
|
|
376
|
+
v["defaultValue"] = {"a": 1}
|
|
377
|
+
with pytest.raises(ValueError, match="allow-contract-change"):
|
|
378
|
+
nodeparam.set_variable_default(f, v, None)
|
|
379
|
+
assert v["defaultValue"] == {"a": 1}
|
|
380
|
+
assert nodeparam.set_variable_default(f, v, None, allow_contract_change=True)["changed"] is True
|
|
381
|
+
assert v["defaultValue"] is None
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def test_setting_a_default_to_its_current_value_is_a_noop():
|
|
385
|
+
"""`changed` drives whether the caller PUTs at all; a false positive is a pointless write."""
|
|
386
|
+
f = _typed_flow()
|
|
387
|
+
v = nodeparam.find_variable(f, "exchangeRateList")
|
|
388
|
+
v["defaultValue"] = [1, 2]
|
|
389
|
+
assert nodeparam.set_variable_default(f, v, [1, 2])["changed"] is False
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def test_the_default_is_written_into_the_flow_not_a_copy():
|
|
393
|
+
"""The patch is in place: the caller PUTs the flow it passed in, so a write to a detached
|
|
394
|
+
copy would validate, upload, and change nothing."""
|
|
395
|
+
f = _typed_flow()
|
|
396
|
+
nodeparam.set_variable_default(f, nodeparam.find_variable(f, "exchangeRateList"), "x")
|
|
397
|
+
assert [v.get("defaultValue") for v in f["variables"]] == ["x", None, None]
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def test_set_variable_default_changes_process_var():
|
|
401
|
+
f = _typed_flow()
|
|
402
|
+
f['variables'][0]['defaultValue'] = 'old-event-id'
|
|
403
|
+
v = nodeparam.find_variable(f, 'exchangeRateList')
|
|
404
|
+
r = nodeparam.set_variable_default(f, v, 'new-event-id')
|
|
405
|
+
assert r['changed'] is True and r['direction'] == 'process'
|
|
406
|
+
assert r['before'] == {'defaultValue': 'old-event-id'}
|
|
407
|
+
assert r['after'] == {'defaultValue': 'new-event-id'}
|
|
408
|
+
assert v['defaultValue'] == 'new-event-id'
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def test_set_variable_default_is_a_noop_when_already_that_value():
|
|
412
|
+
f = _typed_flow()
|
|
413
|
+
f['variables'][0]['defaultValue'] = 'same'
|
|
414
|
+
v = nodeparam.find_variable(f, 'exchangeRateList')
|
|
415
|
+
assert nodeparam.set_variable_default(f, v, 'same')['changed'] is False
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def test_set_variable_default_allowed_on_input_and_output_vars():
|
|
419
|
+
f = _typed_flow()
|
|
420
|
+
# settable on both directions - but it changes what a run does when the caller says
|
|
421
|
+
# nothing, so unlike a process var it is an EXPLICIT contract change, not a free edit
|
|
422
|
+
for name in ('payload', 'exchangeRate'):
|
|
423
|
+
v = nodeparam.find_variable(f, name)
|
|
424
|
+
r = nodeparam.set_variable_default(f, v, {'k': 1}, allow_contract_change=True)
|
|
425
|
+
assert r['changed'] is True and v['defaultValue'] == {'k': 1}
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def test_set_process_title_changes_and_is_noop_when_same():
|
|
429
|
+
f = _typed_flow()
|
|
430
|
+
r = nodeparam.set_process_title(f, 'New Name')
|
|
431
|
+
assert r['changed'] is True and r['before'] == 'T' and f['title'] == 'New Name'
|
|
432
|
+
assert nodeparam.set_process_title(f, 'New Name')['changed'] is False
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def test_bind_param_var_turns_literal_token_into_a_bound_placeholder():
|
|
436
|
+
node = {"actionName": "Send Email",
|
|
437
|
+
"parameters": [{"tabPropertyId": "body", "value": "Hey <%firstName%>, welcome", "variable": []}]}
|
|
438
|
+
param = node["parameters"][0]
|
|
439
|
+
r = nodeparam.bind_param_var(node, param, {0: "v-first"}, find="<%firstName%>", replace="<%0%>")
|
|
440
|
+
assert r["value_changed"] is True
|
|
441
|
+
assert param["value"] == "Hey <%0%>, welcome"
|
|
442
|
+
assert param["variable"] == [{"id": 0, "variableId": "v-first", "attribute": None}]
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def test_bind_param_var_refuses_mismatched_placeholder_and_binding_sets():
|
|
446
|
+
node = {"actionName": "N", "parameters": [{"tabPropertyId": "body", "value": "Hey <%0%>", "variable": []}]}
|
|
447
|
+
param = node["parameters"][0]
|
|
448
|
+
import pytest as _pt
|
|
449
|
+
with _pt.raises(ValueError, match="each"):
|
|
450
|
+
nodeparam.bind_param_var(node, param, {0: "a", 1: "b"}) # value has only <%0%>
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def test_bind_param_var_refuses_a_structured_value():
|
|
454
|
+
node = {"actionName": "N", "parameters": [{"tabPropertyId": "body", "value": {"x": 1}, "variable": []}]}
|
|
455
|
+
import pytest as _pt
|
|
456
|
+
with _pt.raises(ValueError, match="structured"):
|
|
457
|
+
nodeparam.bind_param_var(node, node["parameters"][0], {0: "a"})
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def test_handler_variable_set_default_accepts_clear_alone(monkeypatch):
|
|
461
|
+
"""Regression on the HANDLER's argument plumbing, which the library tests above never
|
|
462
|
+
reach. `--clear` alone (no --value / --value-file) must clear a process variable's
|
|
463
|
+
default. The guard used to raise 'nothing to set' one line before the --clear branch,
|
|
464
|
+
so --clear was refused by an error that named --clear as the remedy and only worked as
|
|
465
|
+
`--clear --value ""`. The validate/lint/save tail is stubbed so this asserts on the
|
|
466
|
+
plumbing, not the network."""
|
|
467
|
+
from types import SimpleNamespace
|
|
468
|
+
from tools.procesio.handlers import nodeparams
|
|
469
|
+
|
|
470
|
+
flow = {"id": "p1", "title": "T", "actions": [], "variables": [
|
|
471
|
+
{"id": "v", "name": "eventId", "type": 20,
|
|
472
|
+
"dataType": "0317bfee-b2f5-4bde-bfe8-121212121220",
|
|
473
|
+
"isList": False, "defaultValue": "stale"}]}
|
|
474
|
+
|
|
475
|
+
class _Client:
|
|
476
|
+
def get(self, path):
|
|
477
|
+
return flow
|
|
478
|
+
|
|
479
|
+
monkeypatch.setattr(nodeparams, "_validate", lambda c, f: (True, None))
|
|
480
|
+
monkeypatch.setattr(nodeparams, "_lint", lambda c, f: [])
|
|
481
|
+
args = SimpleNamespace(id="p1", variable="eventId", value=None, value_file=None,
|
|
482
|
+
clear=True, json=False, allow_contract_change=False, dry_run=True)
|
|
483
|
+
|
|
484
|
+
res = nodeparams.variable_set_default(_Client(), args) # must not raise
|
|
485
|
+
assert res["variable"] == "eventId"
|
|
486
|
+
assert res["changed"] is True
|
|
487
|
+
assert flow["variables"][0]["defaultValue"] is None # the default was actually cleared
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def test_handler_variable_set_default_rejects_clear_with_a_value():
|
|
491
|
+
"""The other half of the fix: --clear combined with --value hides which one wins, so it
|
|
492
|
+
is refused up front rather than silently letting one shadow the other."""
|
|
493
|
+
from types import SimpleNamespace
|
|
494
|
+
from tools.procesio.handlers import nodeparams
|
|
495
|
+
from tools.procesio.errors import UsageError
|
|
496
|
+
|
|
497
|
+
flow = {"id": "p1", "title": "T", "actions": [], "variables": [
|
|
498
|
+
{"id": "v", "name": "eventId", "type": 20, "defaultValue": "stale"}]}
|
|
499
|
+
|
|
500
|
+
class _Client:
|
|
501
|
+
def get(self, path):
|
|
502
|
+
return flow
|
|
503
|
+
|
|
504
|
+
args = SimpleNamespace(id="p1", variable="eventId", value="x", value_file=None,
|
|
505
|
+
clear=True, json=False, allow_contract_change=False, dry_run=True)
|
|
506
|
+
with pytest.raises(UsageError, match="do not combine it with"):
|
|
507
|
+
nodeparams.variable_set_default(_Client(), args)
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
# --------------------------------------------------------------------------- error ports
|
|
512
|
+
|
|
513
|
+
def _err_flow():
|
|
514
|
+
"""Two nodes in a line: a Call API that can fault, and the Node that consumes its response.
|
|
515
|
+
|
|
516
|
+
Shaped like the live DTO on the detail that matters here - ports live on the SOURCE action and
|
|
517
|
+
carry `destinationId` - because an error port written on the wrong side validates and never
|
|
518
|
+
fires.
|
|
519
|
+
"""
|
|
520
|
+
return {
|
|
521
|
+
"id": "flow-e", "title": "Bootstrap",
|
|
522
|
+
"variables": [{"id": "var-res", "name": "response", "type": 20}],
|
|
523
|
+
"actions": [
|
|
524
|
+
{"id": "call-1", "actionName": "List the workspace credentials",
|
|
525
|
+
"actionTemplateName": "Call API", "variableErrorId": None,
|
|
526
|
+
"parameters": [], "customData": {},
|
|
527
|
+
"ports": [{"id": "p1", "flowId": "flow-e", "sourceId": "call-1",
|
|
528
|
+
"destinationId": "node-1", "type": 0, "state": 1, "data": {}}]},
|
|
529
|
+
{"id": "node-1", "actionName": "Keep only the calendar ones",
|
|
530
|
+
"actionTemplateName": "Node", "parameters": [], "customData": {}, "ports": []},
|
|
531
|
+
{"id": "join-1", "actionName": "Join", "actionTemplateName": "Join",
|
|
532
|
+
"parameters": [], "customData": {}, "ports": []},
|
|
533
|
+
],
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def test_an_error_port_is_written_as_all_three_parts():
|
|
538
|
+
flow = _err_flow()
|
|
539
|
+
node = nodeparam.find_node(flow, "List the workspace credentials")
|
|
540
|
+
handler = nodeparam.find_node(flow, "Keep only the calendar ones")
|
|
541
|
+
report = nodeparam.set_error_port(flow, node, handler)
|
|
542
|
+
|
|
543
|
+
err = [p for p in node["ports"] if p["type"] == 1]
|
|
544
|
+
assert len(err) == 1
|
|
545
|
+
assert err[0]["destinationId"] == "node-1"
|
|
546
|
+
assert err[0]["data"] == {"isDefault": "error"} # the port
|
|
547
|
+
var = next(v for v in flow["variables"] if v["id"] == node["variableErrorId"])
|
|
548
|
+
assert var["isError"] is True # the variable
|
|
549
|
+
assert var["dataType"] == nodeparam.ERROR_DATA_MODEL # of the only accepted datatype
|
|
550
|
+
assert report["handler"] == "Keep only the calendar ones"
|
|
551
|
+
assert report["variable_created"] is True
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def test_the_normal_port_is_left_alone():
|
|
555
|
+
flow = _err_flow()
|
|
556
|
+
node = nodeparam.find_node(flow, "List the workspace credentials")
|
|
557
|
+
nodeparam.set_error_port(flow, node, nodeparam.find_node(flow, "Keep only the calendar ones"))
|
|
558
|
+
normal = [p for p in node["ports"] if p["type"] == 0]
|
|
559
|
+
assert len(normal) == 1 and normal[0]["id"] == "p1" and normal[0]["destinationId"] == "node-1"
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
def test_rewiring_replaces_the_port_rather_than_adding_a_second():
|
|
563
|
+
flow = _err_flow()
|
|
564
|
+
node = nodeparam.find_node(flow, "List the workspace credentials")
|
|
565
|
+
nodeparam.set_error_port(flow, node, nodeparam.find_node(flow, "Keep only the calendar ones"))
|
|
566
|
+
first_var = node["variableErrorId"]
|
|
567
|
+
report = nodeparam.set_error_port(flow, node, nodeparam.find_node(flow, "Join"))
|
|
568
|
+
assert len([p for p in node["ports"] if p["type"] == 1]) == 1
|
|
569
|
+
assert report["handler_before"] == "node-1"
|
|
570
|
+
assert node["variableErrorId"] == first_var # the variable is reused, not duplicated
|
|
571
|
+
assert len([v for v in flow["variables"] if v.get("isError")]) == 1
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def test_a_half_wired_error_variable_is_repaired_not_trusted():
|
|
575
|
+
"""A plain string variable in that slot validates and the port never fires, so the shape is
|
|
576
|
+
corrected on the way past rather than left as the caller found it."""
|
|
577
|
+
flow = _err_flow()
|
|
578
|
+
flow["variables"].append({"id": "var-bad", "name": "oops", "type": 20,
|
|
579
|
+
"dataType": "0317bfee-b2f5-4bde-bfe8-121212121214", "isError": False})
|
|
580
|
+
node = nodeparam.find_node(flow, "List the workspace credentials")
|
|
581
|
+
node["variableErrorId"] = "var-bad"
|
|
582
|
+
nodeparam.set_error_port(flow, node, nodeparam.find_node(flow, "Keep only the calendar ones"))
|
|
583
|
+
var = next(v for v in flow["variables"] if v["id"] == "var-bad")
|
|
584
|
+
assert var["isError"] is True and var["dataType"] == nodeparam.ERROR_DATA_MODEL
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def test_clear_removes_the_port_and_its_now_unused_variable():
|
|
588
|
+
flow = _err_flow()
|
|
589
|
+
node = nodeparam.find_node(flow, "List the workspace credentials")
|
|
590
|
+
nodeparam.set_error_port(flow, node, nodeparam.find_node(flow, "Keep only the calendar ones"))
|
|
591
|
+
report = nodeparam.set_error_port(flow, node, None, clear=True)
|
|
592
|
+
assert [p for p in node["ports"] if p["type"] == 1] == []
|
|
593
|
+
assert node["variableErrorId"] is None
|
|
594
|
+
assert [v for v in flow["variables"] if v.get("isError")] == []
|
|
595
|
+
assert report["cleared"] is True and report["handler_before"] == "node-1"
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
def test_a_template_without_an_error_port_is_refused():
|
|
599
|
+
flow = _err_flow()
|
|
600
|
+
with pytest.raises(ValueError) as e:
|
|
601
|
+
nodeparam.set_error_port(flow, nodeparam.find_node(flow, "Join"),
|
|
602
|
+
nodeparam.find_node(flow, "Keep only the calendar ones"))
|
|
603
|
+
assert "no error port" in str(e.value)
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
def test_a_node_cannot_handle_its_own_error():
|
|
607
|
+
flow = _err_flow()
|
|
608
|
+
node = nodeparam.find_node(flow, "List the workspace credentials")
|
|
609
|
+
with pytest.raises(ValueError) as e:
|
|
610
|
+
nodeparam.set_error_port(flow, node, node)
|
|
611
|
+
assert "its own error" in str(e.value)
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def test_a_handler_outside_the_flow_is_refused():
|
|
615
|
+
flow = _err_flow()
|
|
616
|
+
node = nodeparam.find_node(flow, "List the workspace credentials")
|
|
617
|
+
with pytest.raises(ValueError) as e:
|
|
618
|
+
nodeparam.set_error_port(flow, node, {"id": "elsewhere", "actionName": "Other"})
|
|
619
|
+
assert "not in this flow" in str(e.value)
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
# --------------------------------------------------------------------------- Query Store nodes
|
|
623
|
+
|
|
624
|
+
def _qs_node(statement="SELECT <%0%>\nFROM <%1%>", chips=None, pmap=None):
|
|
625
|
+
return {"id": "qs-1", "actionName": "Read the meetings 1",
|
|
626
|
+
"actionTemplateName": "Query Store", "customData": {}, "ports": [],
|
|
627
|
+
"parameters": [
|
|
628
|
+
{"tabPropertyId": "02577ada-0000-0200-0000-00000000b102", "value": statement,
|
|
629
|
+
"variable": chips if chips is not None else [{"id": 0}, {"id": 1}]},
|
|
630
|
+
{"tabPropertyId": "02577ada-0000-0200-0000-00000000b103", "value": pmap or [],
|
|
631
|
+
"variable": []},
|
|
632
|
+
{"tabPropertyId": "02577ada-0000-0200-0000-00000000b104", "value": "1800",
|
|
633
|
+
"variable": []},
|
|
634
|
+
]}
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def test_the_statement_and_its_params_map_are_found_by_property_suffix():
|
|
638
|
+
node = _qs_node()
|
|
639
|
+
query, pmap = nodeparam.find_query_params(node)
|
|
640
|
+
assert query["value"].startswith("SELECT")
|
|
641
|
+
assert pmap["value"] == []
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
def test_a_node_with_no_statement_reports_none():
|
|
645
|
+
node = {"id": "n", "actionName": "Some node", "actionTemplateName": "Node",
|
|
646
|
+
"parameters": [{"tabPropertyId": "whatever-else", "value": "x"}]}
|
|
647
|
+
assert nodeparam.find_query_params(node) == (None, None)
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
def test_a_non_empty_params_map_is_visible_to_the_caller():
|
|
651
|
+
"""The guard that protects a bound @param lives in the handler, so the model has to hand the
|
|
652
|
+
map back rather than hide it."""
|
|
653
|
+
node = _qs_node(pmap=[{"id": 0, "source": {"value": "<%7%>"},
|
|
654
|
+
"destination": {"value": "e", "variable": []}}])
|
|
655
|
+
_, pmap = nodeparam.find_query_params(node)
|
|
656
|
+
assert pmap["value"] and pmap["value"][0]["destination"]["value"] == "e"
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def _qs_full():
|
|
660
|
+
"""A Query Store node shaped like the live release step: statement chips 0-4, a parameters map
|
|
661
|
+
on 5-7, and an output binding on 8 - one sequence across three parameters."""
|
|
662
|
+
return {"id": "qs", "actionName": "Update the meetings 3", "actionTemplateName": "Query Store",
|
|
663
|
+
"customData": {}, "ports": [], "parameters": [
|
|
664
|
+
{"tabPropertyId": "x-b102",
|
|
665
|
+
"value": "UPDATE <%0%>\nSET <%1%> = 'Cancelled', <%2%> = NULL\n"
|
|
666
|
+
"WHERE <%3%> = @mid\nAND <%4%> = 'Confirmed'",
|
|
667
|
+
"variable": [{"id": i} for i in range(5)]},
|
|
668
|
+
{"tabPropertyId": "x-b103", "value": [
|
|
669
|
+
{"id": 0, "source": {"value": "<%5%>", "variable": [{"id": 5, "variableId": "v-row"}]},
|
|
670
|
+
"destination": {"value": "mid", "variable": []}},
|
|
671
|
+
{"id": 1, "source": {"value": "<%6%>", "variable": [{"id": 6, "variableId": "v-st"}]},
|
|
672
|
+
"destination": {"value": "status", "variable": []}},
|
|
673
|
+
], "variable": []},
|
|
674
|
+
{"tabPropertyId": "x-b104", "value": "1800", "variable": []},
|
|
675
|
+
{"tabPropertyId": "x-b106", "value": "<%7%>",
|
|
676
|
+
"variable": [{"id": 7, "variableId": "v-out"}]},
|
|
677
|
+
]}
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
def test_renumbering_keeps_one_sequence_across_every_parameter():
|
|
681
|
+
node = _qs_full()
|
|
682
|
+
query, pmap = nodeparam.find_query_params(node)
|
|
683
|
+
nxt = nodeparam.renumber_node_placeholders(node, [query, pmap])
|
|
684
|
+
assert nxt == 8
|
|
685
|
+
assert node["parameters"][0]["value"].startswith("UPDATE <%0%>")
|
|
686
|
+
rows = node["parameters"][1]["value"]
|
|
687
|
+
assert [r["source"]["value"] for r in rows] == ["<%5%>", "<%6%>"]
|
|
688
|
+
assert node["parameters"][3]["value"] == "<%7%>" # the output keeps its place at the end
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
def test_growing_the_map_pushes_the_output_binding_along():
|
|
692
|
+
"""The failure this exists to stop: two more map rows and the output index moves, so an output
|
|
693
|
+
left at its old number is written back to the wrong variable."""
|
|
694
|
+
node = _qs_full()
|
|
695
|
+
query, pmap = nodeparam.find_query_params(node)
|
|
696
|
+
pmap["value"].append({"id": 2, "source": {"value": "<%99%>",
|
|
697
|
+
"variable": [{"id": 99, "variableId": "v-new"}]},
|
|
698
|
+
"destination": {"value": "event", "variable": []}})
|
|
699
|
+
nodeparam.renumber_node_placeholders(node, [query, pmap])
|
|
700
|
+
rows = node["parameters"][1]["value"]
|
|
701
|
+
assert [r["source"]["value"] for r in rows] == ["<%5%>", "<%6%>", "<%7%>"]
|
|
702
|
+
assert [r["source"]["variable"][0]["id"] for r in rows] == [5, 6, 7]
|
|
703
|
+
out = node["parameters"][3]
|
|
704
|
+
assert out["value"] == "<%8%>" and out["variable"][0]["id"] == 8
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
def test_binding_ids_and_their_value_stay_in_step():
|
|
708
|
+
node = _qs_full()
|
|
709
|
+
query, pmap = nodeparam.find_query_params(node)
|
|
710
|
+
nodeparam.renumber_node_placeholders(node, [query, pmap])
|
|
711
|
+
for p in node["parameters"]:
|
|
712
|
+
for b in p.get("variable") or []:
|
|
713
|
+
if isinstance(b.get("id"), int) and isinstance(p.get("value"), str):
|
|
714
|
+
assert ("<%%%d%%>" % b["id"]) in p["value"] or p["tabPropertyId"].endswith("b102")
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
# --------------------------------------------------------------------------- structured bindings
|
|
718
|
+
|
|
719
|
+
def _body_node():
|
|
720
|
+
"""A Call API node whose Request Parameters is a dict holding a JSON template - the shape that
|
|
721
|
+
bind_param_var refuses and that an HTTP body always has."""
|
|
722
|
+
return {"id": "api-1", "actionName": "Create the calendar event",
|
|
723
|
+
"actionTemplateName": "Call API", "customData": {}, "ports": [],
|
|
724
|
+
"parameters": [{
|
|
725
|
+
"tabPropertyId": "x-0003",
|
|
726
|
+
"value": {"body": {"type": "RAW", "value": {"RAW": {
|
|
727
|
+
"format": "json",
|
|
728
|
+
"value": '{\n "summary": "<%4%>",\n "start": {\n "dateTime": "<%8%>"\n }\n}'}}},
|
|
729
|
+
"headers": [], "queryParams": []},
|
|
730
|
+
"variable": [{"id": 4, "variableId": "v-row",
|
|
731
|
+
"attribute": {"attributeId": "a-name", "nextAttribute": None}},
|
|
732
|
+
{"id": 8, "variableId": "v-row",
|
|
733
|
+
"attribute": {"attributeId": "a-start", "nextAttribute": None}}],
|
|
734
|
+
}]}
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
def test_a_binding_can_be_added_inside_a_structured_parameter():
|
|
738
|
+
node = _body_node()
|
|
739
|
+
p = node["parameters"][0]
|
|
740
|
+
report = nodeparam.add_param_bindings(
|
|
741
|
+
node, p, {12: {"variableId": "v-row", "attributeId": "a-zone"}},
|
|
742
|
+
find='"dateTime": "<%8%>"',
|
|
743
|
+
replace='"dateTime": "<%8%>",\n "timeZone": "<%12%>"')
|
|
744
|
+
raw = p["value"]["body"]["value"]["RAW"]["value"]
|
|
745
|
+
assert '"timeZone": "<%12%>"' in raw
|
|
746
|
+
assert report["replacements"] == 1 and report["bindings_total"] == 3
|
|
747
|
+
new = next(b for b in p["variable"] if b["id"] == 12)
|
|
748
|
+
assert new["attribute"] == {"attributeId": "a-zone", "nextAttribute": None}
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
def test_the_existing_bindings_are_kept_with_their_attribute_paths():
|
|
752
|
+
node = _body_node()
|
|
753
|
+
p = node["parameters"][0]
|
|
754
|
+
nodeparam.add_param_bindings(node, p, {12: {"variableId": "v-row", "attributeId": "a-zone"}},
|
|
755
|
+
find='"dateTime": "<%8%>"',
|
|
756
|
+
replace='"dateTime": "<%8%>",\n "timeZone": "<%12%>"')
|
|
757
|
+
by_id = {b["id"]: b for b in p["variable"]}
|
|
758
|
+
assert by_id[4]["attribute"]["attributeId"] == "a-name"
|
|
759
|
+
assert by_id[8]["attribute"]["attributeId"] == "a-start"
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
def test_a_placeholder_left_unbound_is_refused():
|
|
763
|
+
node = _body_node()
|
|
764
|
+
p = node["parameters"][0]
|
|
765
|
+
with pytest.raises(ValueError) as e:
|
|
766
|
+
nodeparam.add_param_bindings(node, p, {}, find='"<%8%>"',
|
|
767
|
+
replace='"<%8%>", "timeZone": "<%12%>"')
|
|
768
|
+
assert "exactly one binding" in str(e.value)
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
def test_a_binding_with_no_placeholder_is_refused():
|
|
772
|
+
node = _body_node()
|
|
773
|
+
p = node["parameters"][0]
|
|
774
|
+
with pytest.raises(ValueError) as e:
|
|
775
|
+
nodeparam.add_param_bindings(node, p, {99: {"variableId": "v-row", "attributeId": "a-x"}})
|
|
776
|
+
assert "exactly one binding" in str(e.value)
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
def test_a_binding_with_no_attribute_is_written_as_the_whole_variable():
|
|
780
|
+
node = _body_node()
|
|
781
|
+
p = node["parameters"][0]
|
|
782
|
+
nodeparam.add_param_bindings(node, p, {12: {"variableId": "v-other"}},
|
|
783
|
+
find='"dateTime": "<%8%>"',
|
|
784
|
+
replace='"dateTime": "<%8%>",\n "tz": "<%12%>"')
|
|
785
|
+
assert next(b for b in p["variable"] if b["id"] == 12)["attribute"] is None
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
def test_find_text_that_is_absent_is_refused():
|
|
789
|
+
node = _body_node()
|
|
790
|
+
p = node["parameters"][0]
|
|
791
|
+
with pytest.raises(ValueError) as e:
|
|
792
|
+
nodeparam.add_param_bindings(node, p, {}, find="nothing like this", replace="x")
|
|
793
|
+
assert "not present" in str(e.value)
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
def _engine_owned_flow():
|
|
797
|
+
return {"id": "f", "title": "T", "actions": [], "variables": [
|
|
798
|
+
{"id": "v-sys", "name": "processInfo", "type": 40,
|
|
799
|
+
"dataType": "0317bfee-b2f5-4bde-bfe8-121212121220", "isList": False},
|
|
800
|
+
{"id": "v-auto", "name": "Call_Subprocess_ChildInstanceId", "type": 50,
|
|
801
|
+
"dataType": "0317bfee-b2f5-4bde-bfe8-121212121222", "isList": False,
|
|
802
|
+
"defaultValue": None},
|
|
803
|
+
]}
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
@pytest.mark.parametrize("name", ["processInfo", "Call_Subprocess_ChildInstanceId"])
|
|
807
|
+
def test_an_engine_owned_variable_cannot_be_retyped_even_with_the_override(name):
|
|
808
|
+
f = _engine_owned_flow()
|
|
809
|
+
v = nodeparam.find_variable(f, name)
|
|
810
|
+
with pytest.raises(ValueError, match="the engine writes it"):
|
|
811
|
+
nodeparam.set_variable_type(f, v, OBJECT, allow_contract_change=True)
|
|
812
|
+
assert v["dataType"] != OBJECT
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
@pytest.mark.parametrize("name", ["processInfo", "Call_Subprocess_ChildInstanceId"])
|
|
816
|
+
def test_an_engine_owned_variable_cannot_be_given_a_default(name):
|
|
817
|
+
f = _engine_owned_flow()
|
|
818
|
+
v = nodeparam.find_variable(f, name)
|
|
819
|
+
before = v.get("defaultValue")
|
|
820
|
+
with pytest.raises(ValueError, match="the engine writes it"):
|
|
821
|
+
nodeparam.set_variable_default(f, v, "x", allow_contract_change=True)
|
|
822
|
+
assert v.get("defaultValue") == before
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
def test_auto_is_a_known_direction():
|
|
826
|
+
assert nodeparam.VAR_DIRECTION[50] == "auto"
|