From d6c9d5df27f3f13da66729dde5b33501158dcd50 Mon Sep 17 00:00:00 2001 From: Allan Oricil Date: Fri, 28 Jul 2023 11:47:16 -0300 Subject: [PATCH] feat: add --sfdx-url-stdin flag --- command-snapshot.json | 11 +++++++- messages/sfdxurl.store.md | 8 ++++++ src/commands/org/login/sfdx-url.ts | 24 ++++++++++++++---- src/stdin.ts | 25 +++++++++++++++++++ test/commands/org/login/login.sfdx-url.nut.ts | 8 ++++++ .../commands/org/login/login.sfdx-url.test.ts | 22 +++++++++++++++- 6 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 src/stdin.ts diff --git a/command-snapshot.json b/command-snapshot.json index 6c698b13..bb48f2de 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -82,8 +82,17 @@ { "command": "org:login:sfdx-url", "plugin": "@salesforce/plugin-auth", - "flags": ["alias", "json", "loglevel", "no-prompt", "set-default", "set-default-dev-hub", "sfdx-url-file"], "alias": ["force:auth:sfdxurl:store", "auth:sfdxurl:store"], + "flags": [ + "alias", + "json", + "loglevel", + "no-prompt", + "set-default", + "set-default-dev-hub", + "sfdx-url-file", + "sfdx-url-stdin" + ], "flagChars": ["a", "d", "f", "p", "s"], "flagAliases": [ "noprompt", diff --git a/messages/sfdxurl.store.md b/messages/sfdxurl.store.md index 706da034..af8e3d02 100644 --- a/messages/sfdxurl.store.md +++ b/messages/sfdxurl.store.md @@ -22,6 +22,10 @@ You can also create a JSON file that has a top-level property named sfdxAuthUrl Path to a file that contains the Salesforce DX authorization URL. +# flags.sfdx-url-stdin.summary + +Read sfdx auth url from stdin + # examples - Authorize an org using the SFDX authorization URL in the files/authFile.json file: @@ -31,3 +35,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 + +- Authorize an org reading the SFDX authorization URL from stdin: + + echo 'force://PlatformCLI::CoffeeAndBacon@su0503.my.salesforce.com' | <%= config.bin %> <%= command.id %> --sfdx-url-stdin --set-default --alias MyDefaultOrg diff --git a/src/commands/org/login/sfdx-url.ts b/src/commands/org/login/sfdx-url.ts index 003b4e9e..87240247 100644 --- a/src/commands/org/login/sfdx-url.ts +++ b/src/commands/org/login/sfdx-url.ts @@ -11,6 +11,7 @@ import { AuthFields, AuthInfo, Messages } from '@salesforce/core'; import { AnyJson } from '@salesforce/ts-types'; import { parseJson } from '@salesforce/kit'; import { AuthBaseCommand } from '../../../authBaseCommand'; +import { read } from '../../../stdin'; Messages.importMessagesDirectory(__dirname); const messages = Messages.loadMessages('@salesforce/plugin-auth', 'sfdxurl.store'); @@ -22,6 +23,7 @@ type AuthJson = AnyJson & { result?: AnyJson & { sfdxAuthUrl: string }; sfdxAuthUrl: string; }; + export default class LoginSfdxUrl extends AuthBaseCommand { public static readonly summary = messages.getMessage('summary'); public static readonly description = messages.getMessage('description', [AUTH_URL_FORMAT]); @@ -29,12 +31,18 @@ export default class LoginSfdxUrl extends AuthBaseCommand { public static aliases = ['force:auth:sfdxurl:store', 'auth:sfdxurl:store']; public static readonly flags = { + 'sfdx-url-stdin': Flags.boolean({ + summary: messages.getMessage('flags.sfdx-url-stdin.summary'), + exclusive: ['sfdx-url-file'], + exactlyOne: ['sfdx-url-file'], + }), 'sfdx-url-file': Flags.file({ char: 'f', summary: messages.getMessage('flags.sfdx-url-file.summary'), - required: true, deprecateAliases: true, aliases: ['sfdxurlfile'], + exclusive: ['sfdx-url-stdin'], + exactlyOne: ['sfdx-url-stdin'], }), 'set-default-dev-hub': Flags.boolean({ char: 'd', @@ -67,15 +75,21 @@ export default class LoginSfdxUrl extends AuthBaseCommand { public async run(): Promise { const { flags } = await this.parse(LoginSfdxUrl); - if (await this.shouldExitCommand(flags['no-prompt'])) return {}; - const authFile = flags['sfdx-url-file']; + if (await this.shouldExitCommand(flags['no-prompt'])) return {}; - const sfdxAuthUrl = authFile.endsWith('.json') ? await getUrlFromJson(authFile) : await readFile(authFile, 'utf8'); + const sfdxUrlFile = flags['sfdx-url-file']; + const sfdxAuthUrl = flags['sfdx-url-stdin'] + ? await read() + : sfdxUrlFile + ? sfdxUrlFile.endsWith('.json') + ? await getUrlFromJson(sfdxUrlFile) + : await readFile(sfdxUrlFile, 'utf8') + : null; 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.` + 'Error retrieving the auth URL. Please ensure it meets the description shown in the documentation for this command.' ); } diff --git a/src/stdin.ts b/src/stdin.ts new file mode 100644 index 00000000..342d2273 --- /dev/null +++ b/src/stdin.ts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ +export function read(): Promise { + return new Promise((resolve) => { + const stdin = process.openStdin(); + stdin.setEncoding('utf-8'); + + let data = ''; + stdin.on('data', (chunk) => { + data += chunk; + }); + + stdin.on('end', () => { + resolve(data); + }); + + if (stdin.isTTY) { + resolve(''); + } + }); +} diff --git a/test/commands/org/login/login.sfdx-url.nut.ts b/test/commands/org/login/login.sfdx-url.nut.ts index 63d2f6bd..47590429 100644 --- a/test/commands/org/login/login.sfdx-url.nut.ts +++ b/test/commands/org/login/login.sfdx-url.nut.ts @@ -56,4 +56,12 @@ describe('org:login:sfdx-url NUTs', () => { const output = getString(result, 'shellOutput.stdout'); expect(output).to.include(`Successfully authorized ${username} with org ID`); }); + + it('should authorize an org using sfdx-url (human readable)', () => { + env.setString('TESTKIT_EXECUTABLE_PATH', `echo '${authUrl}' | ${env.getString('TESTKIT_EXECUTABLE_PATH')}`); + const command = 'org:login:sfdx-url -d --sfdx-url-stdin'; + const result = execCmd(command, { ensureExitCode: 0 }); + const output = getString(result, 'shellOutput.stdout'); + expect(output).to.include(`Successfully authorized ${username} with org ID`); + }); }); diff --git a/test/commands/org/login/login.sfdx-url.test.ts b/test/commands/org/login/login.sfdx-url.test.ts index a6081303..4749ac4d 100644 --- a/test/commands/org/login/login.sfdx-url.test.ts +++ b/test/commands/org/login/login.sfdx-url.test.ts @@ -13,6 +13,7 @@ import { Config } from '@oclif/core'; import { StubbedType, stubInterface } from '@salesforce/ts-sinon'; import { SfCommand } from '@salesforce/sf-plugins-core'; import LoginSfdxUrl from '../../../../src/commands/org/login/sfdx-url'; +import * as stdin from '../../../../src/stdin'; interface Options { authInfoCreateFails?: boolean; @@ -91,7 +92,7 @@ describe('org:login:sfdx-url', () => { 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('Error getting the auth URL from file'); + expect((e as Error).message).to.includes('Error retrieving the auth URL'); } }); @@ -213,4 +214,23 @@ describe('org:login:sfdx-url', () => { await store.run(); expect(authInfoStub.save.callCount).to.equal(1); }); + + it('should return auth fields when reading auth url from stdin', async () => { + await prepareStubs({ fileDoesNotExist: true }); + $$.SANDBOX.stub(stdin, 'read').resolves('force://PlatformCLI::CoffeeAndBacon@su0503.my.salesforce.com'); + const store = new LoginSfdxUrl(['--sfdx-url-stdin', '--json'], {} as Config); + const response = await store.run(); + expect(response.username).to.equal(testData.username); + }); + + it('should throw error when passing both sfdx-url-stdin and sfdx-url-file', async () => { + const store = new LoginSfdxUrl(['--sfdx-url-stdin', '--sfdx-url-file', 'path/to/key.txt', '--json'], {} 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('--sfdx-url-file cannot also be provided when using --sfdx-url-stdin'); + } + }); });