Skip to content

feat(cli): add workflow-configs pull and push#282

Open
michaelfarrell76 wants to merge 15 commits into
mainfrom
michaelfarrell76/pull-workflow-configs
Open

feat(cli): add workflow-configs pull and push#282
michaelfarrell76 wants to merge 15 commits into
mainfrom
michaelfarrell76/pull-workflow-configs

Conversation

@michaelfarrell76

@michaelfarrell76 michaelfarrell76 commented Jul 8, 2026

Copy link
Copy Markdown
Member

Ref WAL-10250

Summary

Adds top-level workflow-configs for DSR workflow settings.

  • Update-only — workflow configs must be created in the Admin Dashboard first. Sync never creates configs; pushing an unknown internal name fails with a clear error.

  • YAML entries are matched to existing configs by internal-name (required, must be unique across the org; set it in the Admin Dashboard first). Workflows without an internal name are skipped on pull with a warning.

  • action-type is required. Changing a workflow's action type creates a new workflow version server-side — treat it as a deliberate, versioned change, not a cosmetic edit.

  • Pull is filtered to workflowConfigType: DSR; preference-management workflows are excluded on pull and rejected on push (they have purpose-based semantics and must be managed in the Admin Dashboard).

  • Client-side validation: expiry-time, when set, must include a region: default entry and all values must be > 0 (mirrors the backend RegionExpiryMap validation, but fails before any mutation is sent).

  • SDK: fetchAllWorkflowConfigs, syncWorkflowConfigs (+ unit tests for the negative paths)

  • CLI: WorkflowConfigInput codec, pull resource, push sync

  • Example: packages/cli/examples/workflow-configs.yml

Recent updates

  • Added a dedicated View Workflows (viewWorkflows) scope for the DSR Automation product so pull no longer requires a manage/write scope. It depends on View Data Subject Request Settings + View Global Attributes (needed to resolve action / data-subject / attribute-key IDs to names).
  • Manage Workflows now depends on View Workflows + those two view scopes, so push only needs Manage Workflows.
  • Regenerated the CLI YAML schema to include the new viewWorkflows scope enum value.

Required scopes

Operation Scopes Why
Pull View Workflows The workflows GraphQL query is granted by the read-only ViewWorkflows scope, which inherits ViewDataSubjectRequestSettings + ViewGlobalAttributes to resolve action / data-subject / attribute-key IDs to names
Push Manage Workflows updateWorkflowConfig + the workflows query need ManageWorkflows, which inherits ViewWorkflows, ViewDataSubjectRequestSettings, and ViewGlobalAttributes (used to resolve names to IDs)

Example YAML

workflow-configs:
  - internal-name: customer-access-workflow
    action-type: ACCESS
    title: Customer Data Access Request
    subtitle: Submit a request to access your personal data
    data-subject-type: Customer
    visibility: PUBLISHED
    region-list:
      - US
      - CA
    expiry-time:
      - region: default   # required whenever expiry-time is set
        value: 45
      - region: CA
        value: 30

Region codes must be real ISO country / subdivision codes (GB, DE, US-CA, ...). EU is not a valid code and is rejected by both the CLI codec and the backend.

Test plan

Prod test against the Rideshare demo org (rideshare-dating-demo on https://app.transcend.io).

Setup

  • Create an API key in the Rideshare org with the Manage Workflows scope (it grants View Workflows + the required view scopes via dependencies).
  • export TRANSCEND_URL=https://api.transcend.io and export TRANSCEND_API_KEY=...

Test 1 — pull

pnpm exec transcend inventory pull \
  --resources workflowConfigs \
  --file ./rideshare-workflows.yml \
  --auth "$TRANSCEND_API_KEY" \
  --transcendUrl "$TRANSCEND_URL"

Verify:

  • Only DSR-type workflows appear (no PREFERENCE_MANAGEMENT).
  • Every entry has internal-name and action-type.
  • Any workflow missing an internal name is logged as skipped with a warning.

Test 2 — scope negative test

Repeat the pull with an API key that has only View Data Subject Request Settings (i.e. missing View Workflows). Expect an access-denied error naming the View Workflows scope (confirms the scope table is honest).

Test 3 — push a safe, reversible edit

  • In the pulled file, pick one workflow and change only its subtitle (append " (cli test)").
pnpm exec transcend inventory push \
  --file ./rideshare-workflows.yml \
  --auth "$TRANSCEND_API_KEY" \
  --transcendUrl "$TRANSCEND_URL"
  • Verify in Admin Dashboard → DSR Automation → Workflows that the subtitle changed, then revert by restoring the original value and pushing again. Do NOT change visibility or action-type on this org.

Test 4 — negative tests

  • Push an entry with internal-name: does-not-exist-cli-test → expect a clear "must be created in the Admin Dashboard" error and non-zero exit.
  • Push an entry with region-list: [EU] → expect a codec decode error before any network call.
  • Push an expiry-time list without a default region entry → expect the new client-side validation error.

Cleanup

Delete the test API keys and confirm the org's workflows match their pre-test state via a final pull + diff.

Made with Cursor

@pkg-pr-new

pkg-pr-new Bot commented Jul 8, 2026

Copy link
Copy Markdown

Open in StackBlitz

@transcend-io/airgap.js-types

pnpm add https://pkg.pr.new/@transcend-io/airgap.js-types@282
yarn add https://pkg.pr.new/@transcend-io/airgap.js-types@282.tgz

@transcend-io/cli

pnpm add https://pkg.pr.new/@transcend-io/cli@282
yarn add https://pkg.pr.new/@transcend-io/cli@282.tgz

@transcend-io/internationalization

pnpm add https://pkg.pr.new/@transcend-io/internationalization@282
yarn add https://pkg.pr.new/@transcend-io/internationalization@282.tgz

@transcend-io/privacy-types

pnpm add https://pkg.pr.new/@transcend-io/privacy-types@282
yarn add https://pkg.pr.new/@transcend-io/privacy-types@282.tgz

@transcend-io/sdk

pnpm add https://pkg.pr.new/@transcend-io/sdk@282
yarn add https://pkg.pr.new/@transcend-io/sdk@282.tgz

@transcend-io/type-utils

pnpm add https://pkg.pr.new/@transcend-io/type-utils@282
yarn add https://pkg.pr.new/@transcend-io/type-utils@282.tgz

@transcend-io/utils

pnpm add https://pkg.pr.new/@transcend-io/utils@282
yarn add https://pkg.pr.new/@transcend-io/utils@282.tgz

@transcend-io/mcp

pnpm add https://pkg.pr.new/@transcend-io/mcp@282
yarn add https://pkg.pr.new/@transcend-io/mcp@282.tgz

@transcend-io/mcp-server-admin

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-admin@282
yarn add https://pkg.pr.new/@transcend-io/mcp-server-admin@282.tgz

@transcend-io/mcp-server-assessment

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-assessment@282
yarn add https://pkg.pr.new/@transcend-io/mcp-server-assessment@282.tgz

@transcend-io/mcp-server-base

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-base@282
yarn add https://pkg.pr.new/@transcend-io/mcp-server-base@282.tgz

@transcend-io/mcp-server-consent

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-consent@282
yarn add https://pkg.pr.new/@transcend-io/mcp-server-consent@282.tgz

@transcend-io/mcp-server-discovery

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-discovery@282
yarn add https://pkg.pr.new/@transcend-io/mcp-server-discovery@282.tgz

@transcend-io/mcp-server-docs

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-docs@282
yarn add https://pkg.pr.new/@transcend-io/mcp-server-docs@282.tgz

@transcend-io/mcp-server-dsr

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-dsr@282
yarn add https://pkg.pr.new/@transcend-io/mcp-server-dsr@282.tgz

@transcend-io/mcp-server-inventory

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-inventory@282
yarn add https://pkg.pr.new/@transcend-io/mcp-server-inventory@282.tgz

@transcend-io/mcp-server-preferences

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-preferences@282
yarn add https://pkg.pr.new/@transcend-io/mcp-server-preferences@282.tgz

@transcend-io/mcp-server-workflows

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-workflows@282
yarn add https://pkg.pr.new/@transcend-io/mcp-server-workflows@282.tgz

commit: 83c4da5

Base automatically changed from michaelfarrell76/push-sync-result to main July 8, 2026 04:38
michaelfarrell76 and others added 2 commits July 7, 2026 21:42
Add workflow-configs top-level field for DSR workflow settings with
SDK fetch/sync, tr-pull resource, and update-only tr-push sync.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@michaelfarrell76 michaelfarrell76 force-pushed the michaelfarrell76/pull-workflow-configs branch from 13ad1a8 to 94aa588 Compare July 8, 2026 04:44
@michaelfarrell76 michaelfarrell76 marked this pull request as ready for review July 8, 2026 04:50
giacaglia
giacaglia previously approved these changes Jul 8, 2026
Comment thread packages/cli/examples/workflow-configs.yml Outdated
Comment thread packages/sdk/src/dsr-automation/gqls/workflowConfig.ts
@linear-code

linear-code Bot commented Jul 8, 2026

Copy link
Copy Markdown

WAL-10250

Address PR review feedback:
- Configs no longer carry opaque IDs; they sync by their unique
  human-readable title (update-only lookup, errors on missing/ambiguous).
- Support more fields: collect-data-subject-regions, per-region
  expiry-time, and attribute-keys (by name), plus a CollectDataSubjectRegions
  enum in privacy-types.
Comment thread packages/cli/src/codecs.ts Outdated
Comment thread packages/cli/src/codecs.ts Outdated
Comment thread packages/cli/src/codecs.ts Outdated
Comment thread packages/cli/README.md Outdated
Comment thread packages/sdk/src/dsr-automation/syncWorkflowConfigs.ts Outdated
Comment thread packages/sdk/src/dsr-automation/fetchAllWorkflowConfigs.ts
Comment thread packages/sdk/src/dsr-automation/fetchAllWorkflowConfigs.ts Outdated
Use title-based sync without IDs in examples, proper enum codecs, typed
WorkflowConfigNode from GraphQL, indexBy for lookups, and README nav fix.
Comment thread packages/cli/README.md Outdated
Comment thread packages/cli/README.md Outdated
Include @transcend-io/type-utils in the PR changeset and run oxfmt on
README and syncWorkflowConfigs files that failed format:check:root.
Comment thread packages/type-utils/src/tests/indexBy.test.ts Outdated
Comment thread packages/cli/schema/transcend-yml-schema-latest.json Outdated
Use pull/push-specific workflow nav labels in docgen, revert indexBy in
favor of lodash keyBy, and drop the type-utils changeset entry.
Comment thread packages/cli/src/lib/docgen/createPullResourceScopesTable.ts Outdated
Comment thread packages/cli/src/lib/graphql/pullTranscendConfiguration.ts Outdated
Comment thread packages/cli/src/codecs.ts Outdated
Use internal-name + action-type as required YAML fields (matching create
semantics), pull all workflow config types, and simplify docgen nav to a
single Workflows -> DSR Workflows link.
Comment thread packages/cli/src/lib/docgen/createPullResourceScopesTable.ts Outdated
Comment thread packages/sdk/src/dsr-automation/syncWorkflowConfigs.ts Outdated
michaelfarrell76 and others added 4 commits July 8, 2026 00:24
Inline workflow configs docgen link and remove type casts in sync fetch
logic.
Resolve conflicts by keeping both workflow-configs and preference-options
pull/push support.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Pull scope: ManageWorkflows (the workflows query is not granted by
  ViewDataSubjectRequestSettings); push scopes now include
  ViewDataSubjectRequestSettings + ViewGlobalAttributes for lookups
- Filter pull to DSR workflows and reject non-DSR configs on push
- Validate expiry-time client-side (default entry required, values > 0)
- Replace invalid EU region code in examples with GB/DE/FR
- Add syncWorkflowConfigs unit tests and regenerate README scope tables
Comment thread packages/sdk/src/dsr-automation/tests/syncWorkflowConfigs.test.ts Outdated
michaelfarrell76 and others added 3 commits July 8, 2026 03:08
Add ViewWorkflows as a view-only scope for workflow-configs pull, with
ManageWorkflows inheriting ViewWorkflows plus the data-subject and
attribute view scopes needed for push sync. Simplify CLI scope mappings,
pull all workflow config types, remove syncWorkflowConfigs unit tests per
review, and regenerate README scope tables.

Co-authored-by: Cursor <cursoragent@cursor.com>
The ViewWorkflows scope was added to privacy-types but the generated
CLI YAML schema was not regenerated, causing the "Verify generated CLI
files are up to date" CI check to fail.
Comment on lines +15 to +22
region-list:
- US
- CA
expiry-time:
# A `region: default` entry is required whenever expiry-time is set
- region: default
value: 45
- region: CA

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to include a subdivision code example here for completeness? Could make this US-CA.

Suggested change
region-list:
- US
- CA
expiry-time:
# A `region: default` entry is required whenever expiry-time is set
- region: default
value: 45
- region: CA
region-list:
- US
- US-CA
expiry-time:
# A `region: default` entry is required whenever expiry-time is set
- region: default
value: 45
- region: US-CA

Comment on lines +1461 to +1462
visibility: config.workflowConfigVisibility,
type: config.workflowConfigType,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we change so many of these names and structures? It seems like it would be easier for the user to follow if they were as close to the GQL schema as possible. It also seems easier for developers to follow and keep these in sync as we add more properties.

},
[TranscendPullResource.WorkflowConfigs]: {
description:
'Workflow config settings (update-only — workflow configs are created in the Admin Dashboard).',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does this comment get shown? "(update-only — workflow configs are created in the Admin Dashboard)" feels only relevant to the "push" action, so I'm wondering if there's a different place we could put this note where it is more targeted. But that's just a nit.

Comment on lines +112 to +117
if (duplicates.length > 0) {
throw new Error(
`Found "${matches.length}" workflow configs with internal name: "${internalName}". ` +
'Internal names must be unique to sync workflow configs.',
);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should just make these unique in the db

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ya i was thinking same thing....

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool if i make that change?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants