Skip to content

Commit

Permalink
feat: warn when a flow is deleted (#894)
Browse files Browse the repository at this point in the history
Co-authored-by: Matthias Rolke <mr.amtrack@gmail.com>
  • Loading branch information
scolladon and amtrack committed Jul 31, 2024
1 parent f79d3dc commit 0f186ce
Show file tree
Hide file tree
Showing 9 changed files with 484 additions and 455 deletions.
1 change: 1 addition & 0 deletions .github/linters/.cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
"sgdincludedestructive",
"shellcheck",
"sitedotcom",
"SOQL",
"staticresource",
"staticresources",
"stefanzweifel",
Expand Down
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
- [Generate a comma-separated list of the added and modified Apex classes](#generate-a-comma-separated-list-of-the-added-and-modified-apex-classes)
- [Condition deployment on package.xml and destructiveChange content](#condition-deployment-on-packagexml-and-destructivechange-content)
- [Use the module in your own node application](#use-the-module-in-your-own-node-application)
- [Handle flow deletion](#handle-flow-deletion)
- [Changelog](#changelog)
- [Built With](#built-with)
- [Versioning](#versioning)
Expand Down Expand Up @@ -529,6 +530,64 @@ console.log(JSON.stringify(work))
*/
```
### Handle flow deletion
Deleting a flow cannot be done by adding the flow in the `destructiveChanges.xml` and deploy.
A [known issue](https://issues.salesforce.com/issue/a028c00000gAwixAAC/deletion-of-flow-metadata-through-destructive-changes-not-supported) exist to cover this feature.
Please do not assume committing a flow metadata deletion to the repo, and then run sgd will allow you to delete a flow.
We suggest to deal with flow deletion in one go by following those steps (it requires the `FlowDefinition` metadata which is not available in API `v44+`)
1. Set the `FlowDefinition` `activeVersionNumber` to `0`
2. List the `FlowDefinition` in a `package.xml`
3. List all the existing version of the `Flow` in a `destructiveChangesPost.xml` (can be fetch via SOQL using this query : `SELECT FlowDefinitionView.ApiName, VersionNumber, Status FROM FlowVersionView WHERE FlowDefinitionView.ApiName='<FLOW_API_NAME>'`)
4. Deploy this `FlowDefinition` with a `package.xml` and post delete all the `Flow` versions with a post `destructiveChangesPost.xml`
Example to delete the Flow `Set_Account_Description` :
1. Set the `FlowDefinition` `activeVersionNumber` to `0`
```xml
<!--Set_Account_Description.flowDefinition-meta.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<FlowDefinition xmlns="http://soap.sforce.com/2006/04/metadata">
<activeVersionNumber>0</activeVersionNumber>
</FlowDefinition>
```
2. List the `FlowDefinition` in a `package.xml`
```xml
<!--package.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>Set_Account_Description</members>
<name>FlowDefinition</name>
</types>
<version>61.0</version>
</Package>
```
3. List all the existing version of the `Flow` in a `destructiveChangesPost.xml`
```xml
<!--destructiveChangesPost.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>Set_Account_Description-1</members>
<members>Set_Account_Description-2</members>
<members>Set_Account_Description-...</members>
<members>Set_Account_Description-n</members>
<name>Flow</name>
</types>
</Package>
```
4. Deploy this `package.xml`, `destructiveChangesPost.xml` and `FlowDefinition`
```sh
# add `--ignore-warnings` parameter if you listed a deleted Flow version in the destructiveChangesPost.xml
sf project deploy start -x package.xml --post-destructive-changes destructiveChangesPost.xml
```
## Changelog
[changelog.md](CHANGELOG.md) is available for consultation.
Expand Down
49 changes: 49 additions & 0 deletions __tests__/unit/lib/service/flowHandler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict'
import { expect, jest, describe, it } from '@jest/globals'

import { DELETION } from '../../../../src/constant/gitConstants'
import { MetadataRepository } from '../../../../src/metadata/MetadataRepository'
import FlowHandler from '../../../../src/service/flowHandler'
import type { Work } from '../../../../src/types/work'
import { getGlobalMetadata, getWork } from '../../../__utils__/globalTestHelper'

jest.mock('../../../../src/utils/fsHelper')

const objectType = {
directoryName: 'flows',
inFolder: false,
metaFile: false,
suffix: 'flow',
xmlName: 'Flow',
}
const basePath = `force-app/main/default/${objectType.directoryName}`
let work: Work
beforeEach(() => {
jest.clearAllMocks()
work = getWork()
})

describe('flowHandler', () => {
let globalMetadata: MetadataRepository
beforeAll(async () => {
globalMetadata = await getGlobalMetadata()
})
describe('when a flow is deleted', () => {
it('warns the user not to', async () => {
// Arrange
const sut = new FlowHandler(
`${DELETION} ${basePath}/MyFlow.${objectType.suffix}-meta.xml`,
objectType,
work,
globalMetadata
)
expect(work.warnings.length).toBe(0)

// Act
await sut.handle()

// Assert
expect(work.warnings.length).toBe(1)
})
})
})
9 changes: 9 additions & 0 deletions __tests__/unit/lib/service/typeHandlerFactory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { expect, describe, it } from '@jest/globals'
import { MetadataRepository } from '../../../../src/metadata/MetadataRepository'
import CustomField from '../../../../src/service/customFieldHandler'
import Decomposed from '../../../../src/service/decomposedHandler'
import FlowHandler from '../../../../src/service/flowHandler'
import InFolder from '../../../../src/service/inFolderHandler'
import InResource from '../../../../src/service/inResourceHandler'
import SharedFolder from '../../../../src/service/sharedFolderHandler'
Expand Down Expand Up @@ -74,6 +75,14 @@ describe('the type handler factory', () => {
).toBeInstanceOf(InFolder)
})

it('can handle Flow', () => {
expect(
typeHandlerFactory.getTypeHandler(
`Z force-app/main/default/flows/MyFlow.flow-meta.xml`
)
).toBeInstanceOf(FlowHandler)
})

it.each([
'force-app/main/default/TestClass.cls',
'force-app/main/default/TestClass.cls-meta.xml',
Expand Down
34 changes: 17 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@
"author": "Sebastien Colladon <colladonsebastien@gmail.com>",
"dependencies": {
"@salesforce/command": "^5.3.9",
"@salesforce/core": "^8.1.1",
"@salesforce/core": "^8.2.6",
"async": "^3.2.5",
"fast-xml-parser": "^4.4.0",
"fast-xml-parser": "^4.4.1",
"fs-extra": "^11.2.0",
"ignore": "^5.3.1",
"isomorphic-git": "^1.27.0",
"isomorphic-git": "^1.27.1",
"lodash": "^4.17.21",
"simple-git": "^3.25.0",
"xmlbuilder2": "^3.1.1"
Expand Down Expand Up @@ -224,38 +224,38 @@
"@jest/globals": "^29.7.0",
"@ls-lint/ls-lint": "^2.2.3",
"@oclif/dev-cli": "^1.26.10",
"@salesforce/cli-plugins-testkit": "^5.3.18",
"@salesforce/cli-plugins-testkit": "^5.3.20",
"@salesforce/dev-config": "^4.1.0",
"@salesforce/ts-sinon": "^1.4.22",
"@salesforce/ts-sinon": "^1.4.23",
"@stryker-mutator/core": "^8.2.6",
"@stryker-mutator/jest-runner": "^8.2.6",
"@swc/core": "^1.6.13",
"@swc/core": "^1.7.3",
"@types/async": "^3.2.24",
"@types/jest": "^29.5.12",
"@types/mocha": "^10.0.7",
"@types/node": "^20.14.10",
"@typescript-eslint/eslint-plugin": "^7.15.0",
"@typescript-eslint/parser": "^7.15.0",
"@types/node": "^22.0.0",
"@typescript-eslint/eslint-plugin": "^7.17.0",
"@typescript-eslint/parser": "^7.17.0",
"benchmark": "^2.1.4",
"chai": "^4.3.10",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"husky": "^9.0.11",
"eslint-plugin-prettier": "^5.2.1",
"husky": "^9.1.4",
"jest": "^29.7.0",
"knip": "^5.24.1",
"knip": "^5.27.0",
"lint-staged": "^15.2.7",
"mocha": "^10.6.0",
"mocha": "^10.7.0",
"nyc": "^17.0.0",
"prettier": "^3.3.2",
"prettier": "^3.3.3",
"shx": "^0.3.4",
"sinon": "^18.0.0",
"ts-jest": "^29.1.5",
"ts-jest": "^29.2.3",
"ts-node": "^10.9.2",
"tslib": "^2.6.3",
"typescript": "^5.5.3",
"wireit": "^0.14.4",
"typescript": "^5.5.4",
"wireit": "^0.14.5",
"yarn-audit-fix": "^10.0.7",
"yarn-upgrade-all": "^0.7.2"
},
Expand Down
1 change: 1 addition & 0 deletions src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const message = {
errorPathIsNotFile: `'%s' file does not exist`,
errorPathIsNotGit: `'%s' is not a git repository`,
warningApiVersionNotSupported: `API version not found or not supported, using '%s' instead`,
warningFlowDeleted: `Attempt to delete the flow '%s' via destructiveChanges.xml may not work as expected (see https://github.com/scolladon/sfdx-git-delta#handle-flow-deletion)`,
}

export default message
19 changes: 19 additions & 0 deletions src/service/flowHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict'
import { format } from 'util'

import messages from '../locales/en'

import StandardHandler from './standardHandler'

export default class FlowHandler extends StandardHandler {
public override async handleDeletion() {
await super.handleDeletion()
this.warnFlowDeleted()
}

private warnFlowDeleted() {
this.work.warnings.push(
new Error(format(messages.warningFlowDeleted, this._getElementName()))
)
}
}
2 changes: 2 additions & 0 deletions src/service/typeHandlerFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import CustomFieldHandler from './customFieldHandler'
import CustomLabel from './customLabelHandler'
import CustomObject from './customObjectHandler'
import Decomposed from './decomposedHandler'
import FlowHandler from './flowHandler'
import InBundle from './inBundleHandler'
import InFile from './inFileHandler'
import InFolder from './inFolderHandler'
Expand Down Expand Up @@ -35,6 +36,7 @@ const handlerMap = {
EscalationRules: InFile,
ExperienceBundle: InResource,
FieldSet: Decomposed,
Flow: FlowHandler,
GlobalValueSetTranslation: InFile,
Index: Decomposed,
LightningComponentBundle: Lwc,
Expand Down
Loading

0 comments on commit 0f186ce

Please sign in to comment.