Skip to content

Commit

Permalink
Merge pull request #829 from salesforcecli/sm/open-a-flow
Browse files Browse the repository at this point in the history
feat: open a flow
  • Loading branch information
WillieRuemmele authored Oct 13, 2023
2 parents 89f19d7 + 27a620e commit d50c8fe
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 1 deletion.
13 changes: 13 additions & 0 deletions messages/open.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ To open in a specific browser, use the --browser flag. Supported browsers are "c

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

- Open a local Flow in Flow Builder:

$ <%= config.bin %> <%= command.id %> --source-path force-app/main/default/flows/Hello.flow-meta.xml

# flags.browser.summary

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

The Lightning Experience-enabled custom domain is unavailable.

# FlowIdNotFound

No ID not found for Flow %s.

# FlowIdNotFound.actions

- Check that the Flow you want to open is deployed to the org.
- Run `sf org open -p lightning/setup/Flows/home` to open the list of Flows
21 changes: 21 additions & 0 deletions src/commands/org/open.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,15 @@ export class OrgOpenCommand extends SfCommand<OrgOpenOutput> {
return `/visualEditor/appBuilder.app?pageId=${flexipage.Id}`;
} else if (typeName === 'ApexPage') {
return `/apex/${path.basename(file).replace('.page-meta.xml', '').replace('.page', '')}`;
} else if (typeName === 'Flow') {
return `/builder_platform_interaction/flowBuilder.app?flowId=${await flowFileNameToId(this.conn, file)}`;
} else {
return 'lightning/setup/FlexiPageList/home';
}
} catch (error) {
if (error instanceof Error && error.name === 'FlowIdNotFoundError') {
this.error(error);
}
return 'lightning/setup/FlexiPageList/home';
}
}
Expand All @@ -170,3 +175,19 @@ export interface OrgOpenOutput {
username: string;
orgId: string;
}

/** query the tooling API to turn a flow's filepath into a FlowId (starts with 301) */
const flowFileNameToId = async (conn: Connection, filePath: string): Promise<string> => {
const result = await conn.tooling.query<{ Id: string; FullName: string }>(
'select id, MasterLabel, FullName from Flow'
);
const fullName = path.basename(filePath).replace('.flow-meta.xml', '');
// https://developer.salesforce.com/docs/atlas.en-us.api_tooling.meta/api_tooling/tooling_api_objects_flow.htm
// unfortunately, you can't query based on the fullname because `field 'FullName' can not be filtered in a query call`
// so we get all the flows and then filter.
const match = (result.records ?? []).find((r) => r.FullName === fullName)?.Id;
if (match) {
return match;
}
throw messages.createError('FlowIdNotFound', [filePath]);
};
10 changes: 10 additions & 0 deletions test/nut/open.nut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ config.truncateThreshold = 0;

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

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

it('should produce the URL for an existing flow', () => {
const result = execCmd<OrgOpenOutput>(`force:org:open --source-file ${flowPath} --url-only --json`, {
ensureExitCode: 0,
}).jsonOutput?.result;
assert(result);
expect(result).to.include({ orgId: defaultUserOrgId, username: defaultUsername });
expect(result.url).to.include('/builder_platform_interaction/flowBuilder.app?flowId=301');
});

it("should produce the org's frontdoor url when edition of file is not supported", async () => {
const layoutDir = path.join('force-app', 'main', 'default', 'layouts');
const layoutFilePath = path.join(layoutDir, 'MyLayout.layout-meta.xml');
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1361,7 +1361,7 @@
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.2.tgz#31f6eec1ed7ec23f4f05608d3a2d381df041f564"
integrity sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==

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

0 comments on commit d50c8fe

Please sign in to comment.