Skip to content

Commit d50c8fe

Browse files
Merge pull request #829 from salesforcecli/sm/open-a-flow
feat: open a flow
2 parents 89f19d7 + 27a620e commit d50c8fe

File tree

4 files changed

+45
-1
lines changed

4 files changed

+45
-1
lines changed

messages/open.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ To open in a specific browser, use the --browser flag. Supported browsers are "c
3030

3131
$ <%= config.bin %> <%= command.id %> --source-path force-app/main/default/flexipages/Hello.flexipage-meta.xml
3232

33+
- Open a local Flow in Flow Builder:
34+
35+
$ <%= config.bin %> <%= command.id %> --source-path force-app/main/default/flows/Hello.flow-meta.xml
36+
3337
# flags.browser.summary
3438

3539
Browser where the org opens.
@@ -67,3 +71,12 @@ Waiting to resolve the Lightning Experience-enabled custom domain...
6771
# domainTimeoutError
6872

6973
The Lightning Experience-enabled custom domain is unavailable.
74+
75+
# FlowIdNotFound
76+
77+
No ID not found for Flow %s.
78+
79+
# FlowIdNotFound.actions
80+
81+
- Check that the Flow you want to open is deployed to the org.
82+
- Run `sf org open -p lightning/setup/Flows/home` to open the list of Flows

src/commands/org/open.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,15 @@ export class OrgOpenCommand extends SfCommand<OrgOpenOutput> {
156156
return `/visualEditor/appBuilder.app?pageId=${flexipage.Id}`;
157157
} else if (typeName === 'ApexPage') {
158158
return `/apex/${path.basename(file).replace('.page-meta.xml', '').replace('.page', '')}`;
159+
} else if (typeName === 'Flow') {
160+
return `/builder_platform_interaction/flowBuilder.app?flowId=${await flowFileNameToId(this.conn, file)}`;
159161
} else {
160162
return 'lightning/setup/FlexiPageList/home';
161163
}
162164
} catch (error) {
165+
if (error instanceof Error && error.name === 'FlowIdNotFoundError') {
166+
this.error(error);
167+
}
163168
return 'lightning/setup/FlexiPageList/home';
164169
}
165170
}
@@ -170,3 +175,19 @@ export interface OrgOpenOutput {
170175
username: string;
171176
orgId: string;
172177
}
178+
179+
/** query the tooling API to turn a flow's filepath into a FlowId (starts with 301) */
180+
const flowFileNameToId = async (conn: Connection, filePath: string): Promise<string> => {
181+
const result = await conn.tooling.query<{ Id: string; FullName: string }>(
182+
'select id, MasterLabel, FullName from Flow'
183+
);
184+
const fullName = path.basename(filePath).replace('.flow-meta.xml', '');
185+
// https://developer.salesforce.com/docs/atlas.en-us.api_tooling.meta/api_tooling/tooling_api_objects_flow.htm
186+
// unfortunately, you can't query based on the fullname because `field 'FullName' can not be filtered in a query call`
187+
// so we get all the flows and then filter.
188+
const match = (result.records ?? []).find((r) => r.FullName === fullName)?.Id;
189+
if (match) {
190+
return match;
191+
}
192+
throw messages.createError('FlowIdNotFound', [filePath]);
193+
};

test/nut/open.nut.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ config.truncateThreshold = 0;
2121

2222
describe('test org:open command', () => {
2323
const flexiPagePath = path.join('force-app', 'main', 'default', 'flexipages', 'Property_Explorer.flexipage-meta.xml');
24+
const flowPath = path.join('force-app', 'main', 'default', 'flows', 'Create_property.flow-meta.xml');
2425

2526
before(async () => {
2627
session = await TestSession.create({
@@ -65,6 +66,15 @@ describe('test org:open command', () => {
6566
expect(result.url).to.include('/visualEditor/appBuilder.app?pageId');
6667
});
6768

69+
it('should produce the URL for an existing flow', () => {
70+
const result = execCmd<OrgOpenOutput>(`force:org:open --source-file ${flowPath} --url-only --json`, {
71+
ensureExitCode: 0,
72+
}).jsonOutput?.result;
73+
assert(result);
74+
expect(result).to.include({ orgId: defaultUserOrgId, username: defaultUsername });
75+
expect(result.url).to.include('/builder_platform_interaction/flowBuilder.app?flowId=301');
76+
});
77+
6878
it("should produce the org's frontdoor url when edition of file is not supported", async () => {
6979
const layoutDir = path.join('force-app', 'main', 'default', 'layouts');
7080
const layoutFilePath = path.join(layoutDir, 'MyLayout.layout-meta.xml');

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1361,7 +1361,7 @@
13611361
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.2.tgz#31f6eec1ed7ec23f4f05608d3a2d381df041f564"
13621362
integrity sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==
13631363

1364-
"@types/shelljs@^0.8.12", "@types/shelljs@^0.8.13":
1364+
"@types/shelljs@^0.8.13":
13651365
version "0.8.13"
13661366
resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.13.tgz#a94bf7f2b82b7cd9f4496bbe063c3adb0868a650"
13671367
integrity sha512-++uMLOQSLlse1kCfEOwhgmHuaABZwinkylmUKCpvcEGZUov3TtM+gJZloSkW/W+9pEAEg/VBOwiSR05oqJsa5A==

0 commit comments

Comments
 (0)