Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@transcend-io/cli": minor
"@transcend-io/sdk": major
---

Support per-workflow deletion dependencies in `transcend.yml`. Use a list of titles for the global configuration only, or a list of objects when any per-workflow override is present (`{ titles }` for global, `{ workflow, titles }` or `{ workflow, reset-to-global: true }` for overrides). Mixing titles and objects in the same list is not allowed.

`syncDataSiloDependencies` now takes `[dataSiloId, DependedOnDataSiloInput[]][]` instead of `[dataSiloId, string[]][]` and pushes through `dependedOnDataSilos` rather than the deprecated `dependedOnDataSiloTitles`.
52 changes: 51 additions & 1 deletion packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ A command line interface that allows you to programatically interact with the Tr
- [Changelog](#changelog)
- [Installation](#installation)
- [transcend.yml](#transcendyml)
- [Deletion dependencies](#deletion-dependencies)
- [Usage](#usage)
- [`transcend request approve`](#transcend-request-approve)
- [`transcend request upload`](#transcend-request-upload)
Expand Down Expand Up @@ -168,7 +169,12 @@ data-silos:
- email
- userId
deletion-dependencies:
- Identity Service
- titles:
- Identity Service
- workflow: GDPR Erasure
titles:
- Identity Service
- CRM Warehouse
owners:
- alice@transcend.io
datapoints:
Expand All @@ -192,6 +198,50 @@ data-silos:
description: The email address of the user
```

### Deletion dependencies

When an erasure request runs, `deletion-dependencies` holds off deleting from a data silo until the data silos it lists have finished deleting.

Use a list of objects: `{ titles }` for the global configuration, and `{ workflow, titles }` or `{ workflow, reset-to-global: true }` for each per-workflow override. Mixing titles and objects in the same list is not allowed:

```yaml
data-silos:
- title: Salesforce
integrationName: server
deletion-dependencies:
# Global configuration
- titles:
- Identity Service
# Overrides the global configuration for the "GDPR Erasure" workflow only
- workflow: GDPR Erasure
titles:
- Identity Service
- CRM Warehouse
# Runs with no dependencies at all in the "CCPA Delete" workflow
- workflow: CCPA Delete
titles: []
# Removes a previously configured override so the workflow uses the global configuration again
- workflow: Legacy Erasure
reset-to-global: true
```

**Legacy:** a bare list of titles is still accepted for global-only configuration:

```yaml
data-silos:
- title: Salesforce
integrationName: server
deletion-dependencies:
- Identity Service
- CRM Warehouse
```

`workflow` is the internal name of an erasure workflow, which you can find under [DSR Automation -> Workflows](https://app.transcend.io/privacy-requests/workflows). Overrides are only supported on erasure workflows, and the data silo and everything it depends on must already be part of that workflow.

A workflow that is not listed keeps whatever configuration it already has, so pushing a config that only lists global dependencies never removes existing overrides. Use `reset-to-global: true` to remove one.

Omitting `deletion-dependencies` entirely clears the global configuration, matching how the rest of `transcend.yml` treats omitted fields.

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.

[Not for reviewers, not for Cursor] Even though it says this matches "how the rest of transcend.yml treats omitted fields" this does not seem like obvious behavior to me. If we do want to do it this way, I'm not sure that justifying it by saying "matching how the rest of transcend.yml treats omitted fields." is appropriate within the README.


## Usage

<!-- COMMANDS_START -->
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/examples/invalid.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ data-silos:
- title: Analytics Service
integrationName: database
disabled: 'dog'
deletion-dependencies:
- workflow: GDPR Erasure
15 changes: 14 additions & 1 deletion packages/cli/examples/simple.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,21 @@ data-silos:
# When a data erasure request is being performed, this data silo should not be deleted from
# until all of the following data silos were deleted first. This list can contain other internal
# systems defined in this file, as well as any of the SaaS tools connected in your Transcend instance.
#
# Prefer a list of objects: `{ titles: [...] }` for global, and
# `{ workflow, titles }` (or `{ workflow, reset-to-global: true }`) for each override.
# A bare list of titles is still accepted for global-only configuration.
# Workflows that are not listed keep whatever configuration they already have.
deletion-dependencies:
- Identity Service
- titles:
- Identity Service
- workflow: GDPR Erasure
titles:
- Identity Service
- Analytics Service
# This workflow deletes from the data silo without waiting on anything
- workflow: CCPA Delete
titles: []
# The email addresses of the employees within your company that are the go-to individuals
# for managing this data silo
owners:
Expand Down
55 changes: 51 additions & 4 deletions packages/cli/schema/transcend-yml-schema-latest.json
Original file line number Diff line number Diff line change
Expand Up @@ -28098,10 +28098,57 @@
}
},
"deletion-dependencies": {
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "array",
"items": {
"anyOf": [
{
"allOf": [
{
"type": "object",
"required": ["titles"],
"properties": {
"titles": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
{
"type": "object",
"properties": {
"workflow": {
"type": "string"
}
}
}
]
},
{
"type": "object",
"required": ["workflow", "reset-to-global"],
"properties": {
"workflow": {
"type": "string"
},
"reset-to-global": {
"const": true
}
}
}
]
}
}
]
},
"owners": {
"type": "array",
Expand Down
55 changes: 51 additions & 4 deletions packages/cli/schema/transcend-yml-schema-v10.json
Original file line number Diff line number Diff line change
Expand Up @@ -28098,10 +28098,57 @@
}
},
"deletion-dependencies": {
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "array",
"items": {
"anyOf": [
{
"allOf": [
{
"type": "object",
"required": ["titles"],
"properties": {
"titles": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
{
"type": "object",
"properties": {
"workflow": {
"type": "string"
}
}
}
]
},
{
"type": "object",
"required": ["workflow", "reset-to-global"],
"properties": {
"workflow": {
"type": "string"
},
"reset-to-global": {
"const": true
}
}
}
]
}
}
]
},
"owners": {
"type": "array",
Expand Down
72 changes: 71 additions & 1 deletion packages/cli/src/codecs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,71 @@ export const IntlMessageInput = t.intersection([
/** Type override */
export type IntlMessageInput = t.TypeOf<typeof IntlMessageInput>;

/**
* A set of deletion dependencies, either global (when `workflow` is omitted)
* or scoped to a single DSR workflow.
*
* An empty `titles` list scoped to a workflow is an explicit override, meaning
* that workflow runs the data silo with no dependencies at all.
*/
export const DeletionDependency = t.intersection([
t.type({
/**
* The titles of the data silos that must be deleted from first. This list can contain
* other internal systems defined in this file, as well as any of the SaaS tools connected
* in your Transcend instance.
*/
titles: t.array(t.string),
}),
t.partial({
/**
* The internal name of the DSR workflow that these dependencies override the global
* configuration for. Omit to declare the global configuration.
*/
workflow: t.string,
}),
]);

/** Type override */
export type DeletionDependency = t.TypeOf<typeof DeletionDependency>;

/**
* Removes a workflow's deletion dependency override so that the workflow falls back
* to the global configuration.
*/
export const DeletionDependencyReset = t.type({
/** The internal name of the DSR workflow to remove the override from */
workflow: t.string,
/** Must be `true`; declares that the override should be removed */
'reset-to-global': t.literal(true),
});

/** Type override */
export type DeletionDependencyReset = t.TypeOf<typeof DeletionDependencyReset>;

/**
* A single object entry in a data silo's `deletion-dependencies` list.
* Global dependencies are written as `{ titles: [...] }`; per-workflow overrides
* include a `workflow` key.
*/
export const DeletionDependencyInput = t.union([DeletionDependency, DeletionDependencyReset]);

/** Type override */
export type DeletionDependencyInput = t.TypeOf<typeof DeletionDependencyInput>;

/**
* The `deletion-dependencies` field for a data silo.
*
* Prefer a list of objects: `{ titles: [...] }` for the global configuration and
* `{ workflow, titles }` or `{ workflow, reset-to-global: true }` for each override.
* A bare list of titles is still accepted for global-only configuration. Mixing
* titles and objects in the same list is not allowed.
*/
export const DeletionDependencies = t.union([t.array(t.string), t.array(DeletionDependencyInput)]);

/** Type override */
export type DeletionDependencies = t.TypeOf<typeof DeletionDependencies>;

/**
* Input to define a data silo
*
Expand Down Expand Up @@ -1350,8 +1415,13 @@ export const DataSiloInput = t.intersection([
* When a data erasure request is being performed, this data silo should not be deleted from
* until all of the following data silos were deleted first. This list can contain other internal
* systems defined in this file, as well as any of the SaaS tools connected in your Transcend instance.
*
* Prefer a list of objects: `{ titles: [...] }` for the global configuration and
* `{ workflow, titles }` (or `{ workflow, reset-to-global: true }`) for each override.
* A bare list of titles is still accepted for global-only configuration. Workflows that
* are not listed keep whatever configuration they already have.
*/
'deletion-dependencies': t.array(t.string),
'deletion-dependencies': DeletionDependencies,
/**
* The email addresses of the employees within your company that are the go-to individuals
* for managing this data silo
Expand Down
Loading
Loading