diff --git a/command-snapshot.json b/command-snapshot.json index 6c698b13..111593d8 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -82,16 +82,26 @@ { "command": "org:login:sfdx-url", "plugin": "@salesforce/plugin-auth", - "flags": ["alias", "json", "loglevel", "no-prompt", "set-default", "set-default-dev-hub", "sfdx-url-file"], + "flags": [ + "alias", + "json", + "loglevel", + "no-prompt", + "set-default", + "set-default-dev-hub", + "sfdx-url-file", + "sfdx-url-stdin" + ], "alias": ["force:auth:sfdxurl:store", "auth:sfdxurl:store"], - "flagChars": ["a", "d", "f", "p", "s"], + "flagChars": ["a", "d", "f", "p", "s", "u"], "flagAliases": [ "noprompt", "setalias", "setdefaultdevhub", "setdefaultdevhubusername", "setdefaultusername", - "sfdxurlfile" + "sfdxurlfile", + "sfdxurlstdin" ] }, { diff --git a/messages/sfdxurl.store.md b/messages/sfdxurl.store.md index 706da034..e98711e0 100644 --- a/messages/sfdxurl.store.md +++ b/messages/sfdxurl.store.md @@ -1,6 +1,6 @@ # summary -Authorize an org using a Salesforce DX authorization URL stored in a file. +Authorize an org using a Salesforce DX authorization URL stored in a file or through standard input (stdin). # description @@ -18,10 +18,16 @@ NOTE: The "<%= config.bin %> org display --verbose" command displays the refresh You can also create a JSON file that has a top-level property named sfdxAuthUrl whose value is the authorization URL. Finally, you can create a normal text file that includes just the URL and nothing else. +Alternatively, you can pipe the SFDX authorization URL through standard input by using the --sfdx-url-stdin flag and providing the '-' character as the value. + # flags.sfdx-url-file.summary Path to a file that contains the Salesforce DX authorization URL. +# flags.sfdx-url-stdin.summary + +Specify '-' as this flag's value to pipe the Salesforce DX authorization URL through standard input (stdin). + # examples - Authorize an org using the SFDX authorization URL in the files/authFile.json file: @@ -31,3 +37,7 @@ Path to a file that contains the Salesforce DX authorization URL. - Similar to previous example, but set the org as your default and give it an alias MyDefaultOrg: <%= config.bin %> <%= command.id %> --sfdx-url-file files/authFile.json --set-default --alias MyDefaultOrg + +- Pipe the SFDX authorization URL from stdin by specifying the '-' value. + + <%= "\n $ echo url | " + config.bin %> <%= command.id %> --sfdx-url-stdin - diff --git a/src/commands/org/login/sfdx-url.ts b/src/commands/org/login/sfdx-url.ts index 2cd4cef1..3750bc76 100644 --- a/src/commands/org/login/sfdx-url.ts +++ b/src/commands/org/login/sfdx-url.ts @@ -33,9 +33,19 @@ export default class LoginSfdxUrl extends AuthBaseCommand { 'sfdx-url-file': Flags.file({ char: 'f', summary: messages.getMessage('flags.sfdx-url-file.summary'), - required: true, + required: false, deprecateAliases: true, aliases: ['sfdxurlfile'], + exactlyOne: ['sfdx-url-file', 'sfdx-url-stdin'], + }), + 'sfdx-url-stdin': Flags.file({ + char: 'u', + summary: messages.getMessage('flags.sfdx-url-stdin.summary'), + required: false, + deprecateAliases: true, + aliases: ['sfdxurlstdin'], + allowStdin: 'only', + exactlyOne: ['sfdx-url-file', 'sfdx-url-stdin'], }), 'set-default-dev-hub': Flags.boolean({ char: 'd', @@ -71,15 +81,21 @@ export default class LoginSfdxUrl extends AuthBaseCommand { if (await this.shouldExitCommand(flags['no-prompt'])) return {}; const authFile = flags['sfdx-url-file']; + const authStdin = flags['sfdx-url-stdin']; + let sfdxAuthUrl: string; - const sfdxAuthUrl = authFile.endsWith('.json') - ? await getUrlFromJson(authFile) - : await fs.readFile(authFile, 'utf8'); + if (authFile) { + sfdxAuthUrl = authFile.endsWith('.json') ? await getUrlFromJson(authFile) : await fs.readFile(authFile, 'utf8'); - if (!sfdxAuthUrl) { - throw new Error( - `Error getting the auth URL from file ${authFile}. Please ensure it meets the description shown in the documentation for this command.` - ); + if (!sfdxAuthUrl) { + throw new Error( + `Error getting the auth URL from file ${authFile}. Please ensure it meets the description shown in the documentation for this command.` + ); + } + } else if (authStdin) { + sfdxAuthUrl = authStdin; + } else { + throw new Error('SFDX Auth URL not found.'); } const oauth2Options = AuthInfo.parseSfdxAuthUrl(sfdxAuthUrl); diff --git a/test/commands/org/login/login.sfdx-url.test.ts b/test/commands/org/login/login.sfdx-url.test.ts index bcb94b9a..cdc11ab9 100644 --- a/test/commands/org/login/login.sfdx-url.test.ts +++ b/test/commands/org/login/login.sfdx-url.test.ts @@ -10,7 +10,7 @@ import { AuthFields, AuthInfo, Global, Mode } from '@salesforce/core'; import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup.js'; import { expect } from 'chai'; import { Config } from '@oclif/core'; -import { StubbedType, stubInterface } from '@salesforce/ts-sinon'; +import { StubbedType, stubInterface, stubMethod } from '@salesforce/ts-sinon'; import { SfCommand } from '@salesforce/sf-plugins-core'; import LoginSfdxUrl from '../../../../src/commands/org/login/sfdx-url.js'; @@ -213,4 +213,33 @@ describe('org:login:sfdx-url', () => { await store.run(); expect(authInfoStub.save.called); }); + + it('should error out when neither file or url are provided', async () => { + await prepareStubs(); + const store = new LoginSfdxUrl([], {} as Config); + try { + const response = await store.run(); + expect.fail(`Should have thrown an error. Response: ${JSON.stringify(response)}`); + } catch (e) { + expect((e as Error).message).to.includes( + 'Exactly one of the following must be provided: --sfdx-url-file, --sfdx-url-stdin' + ); + } + }); + + it('should return auth fields when using stdin', async () => { + await prepareStubs(); + const sfdxAuthUrl = 'force://PlatformCLI::CoffeeAndBacon@su0503.my.salesforce.com'; + const flagOutput = { + flags: { + 'no-prompt': false, + 'sfdx-url-file': '', + 'sfdx-url-stdin': sfdxAuthUrl, + }, + }; + const store = new LoginSfdxUrl(['-u', '-'], {} as Config); + stubMethod($$.SANDBOX, store, 'parse').resolves(flagOutput); + const response = await store.run(); + expect(response.username).to.equal(testData.username); + }); });