diff --git a/packages/salesforcedx-vscode-core/package.json b/packages/salesforcedx-vscode-core/package.json index 8bec15954c..499918902e 100644 --- a/packages/salesforcedx-vscode-core/package.json +++ b/packages/salesforcedx-vscode-core/package.json @@ -25,7 +25,7 @@ "Other" ], "dependencies": { - "@heroku/functions-core": "^0.2.7", + "@heroku/functions-core": "^0.3.0", "@salesforce/core": "^2.35.0", "@salesforce/salesforcedx-sobjects-faux-generator": "54.12.0", "@salesforce/salesforcedx-utils-vscode": "54.12.0", diff --git a/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionContainerlessStartCommand.ts b/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionContainerlessStartCommand.ts index ac7c59667a..9afadfebf2 100644 --- a/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionContainerlessStartCommand.ts +++ b/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionContainerlessStartCommand.ts @@ -21,7 +21,7 @@ export const CONTAINER_START_TEXT_KEY = export const FUNCTION_CONTAINER_LOG_NAME = 'force_function_containerless_start'; /** - * Executes sfdx run:function:start:local --verbose + * Starts a local run of the function which can then be invoked with payloads. * @param sourceUri */ export const forceFunctionContainerlessStartCommand = async ( diff --git a/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionStart/ForceFunctionContainerStartExecutor.ts b/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionStart/ForceFunctionContainerStartExecutor.ts index 4a87a10d13..d8b778f208 100644 --- a/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionStart/ForceFunctionContainerStartExecutor.ts +++ b/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionStart/ForceFunctionContainerStartExecutor.ts @@ -135,13 +135,13 @@ export class ForceFunctionContainerStartExecutor extends ForceFunctionStartExecu }); } - public startFunction(functionName: string): void { + public async startFunction(functionName: string): Promise { channelService.appendLine(`Starting ${functionName} in container`); if (!this.functionsBinary) { throw new Error('Unable to start function with no binary.'); } - this.functionsBinary.run(functionName, {}).catch(err => { + await this.functionsBinary.run(functionName, {}).catch(err => { console.log(err); }); } diff --git a/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionStart/ForceFunctionContainerlessStartExecutor.ts b/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionStart/ForceFunctionContainerlessStartExecutor.ts index 8c01b05265..c2bdbadf5e 100644 --- a/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionStart/ForceFunctionContainerlessStartExecutor.ts +++ b/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionStart/ForceFunctionContainerlessStartExecutor.ts @@ -5,7 +5,7 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import { LocalRun } from '@heroku/functions-core'; +import { LocalRun, LocalRunProcess } from '@heroku/functions-core'; import { Disposable } from 'vscode'; import { channelService } from '../../../channels'; import { nls } from '../../../messages'; @@ -19,6 +19,8 @@ import { import { ForceFunctionStartExecutor } from './ForceFunctionStartExecutor'; export class ForceFunctionContainerlessStartExecutor extends ForceFunctionStartExecutor { + private process: LocalRunProcess | undefined | void; + public async setupFunctionListeners(): Promise { console.log('No listeners for containerless function.'); } @@ -26,7 +28,10 @@ export class ForceFunctionContainerlessStartExecutor extends ForceFunctionStartE public async cancelFunction( registeredStartedFunctionDisposable: Disposable ): Promise { - // TODO: how to stop the localRun + if (this.process && !this.process.cancelled) { + this.process.cancel(); + this.process = undefined; + } registeredStartedFunctionDisposable.dispose(); } @@ -34,7 +39,7 @@ export class ForceFunctionContainerlessStartExecutor extends ForceFunctionStartE console.log('No build for containerless function'); } - public startFunction(functionName: string, functionDirPath: string): void { + public async startFunction(functionName: string, functionDirPath: string): Promise { const functionLanguage = FunctionService.instance.getFunctionType(); channelService.appendLine( `Starting ${functionName} of type ${functionLanguage}` @@ -49,13 +54,7 @@ export class ForceFunctionContainerlessStartExecutor extends ForceFunctionStartE const debugType = functionLanguage === functionType.JAVA ? 'java' : 'node'; FunctionService.instance.updateFunction(functionDirPath, debugType, true); - localRun - .exec() - .then(msg => { - console.log( - `localRun resolved in ForceFunctionContainerlessStartExecutor with message: ${msg}` - ); - }) + this.process = await localRun.exec() .catch((err: Error) => { const errorNotificationMessage = nls.localize( this.UNEXPECTED_ERROR_KEY diff --git a/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionStart/ForceFunctionStartExecutor.ts b/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionStart/ForceFunctionStartExecutor.ts index d24fe9e28d..6435283d6a 100644 --- a/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionStart/ForceFunctionStartExecutor.ts +++ b/packages/salesforcedx-vscode-core/src/commands/functions/forceFunctionStart/ForceFunctionStartExecutor.ts @@ -110,7 +110,7 @@ export abstract class ForceFunctionStartExecutor extends LibraryCommandletExecut this.buildFunction(functionName, functionDirPath); channelService.appendLine(`Starting ${functionName}`); - this.startFunction(functionName, functionDirPath); + await this.startFunction(functionName, functionDirPath); return true; } diff --git a/packages/salesforcedx-vscode-core/test/vscode-integration/commands/functions/forceFunctionStart/ForceFunctionContainerlessStartExecutor.test.ts b/packages/salesforcedx-vscode-core/test/vscode-integration/commands/functions/forceFunctionStart/ForceFunctionContainerlessStartExecutor.test.ts index 4e1316dc7b..00c56c06d7 100644 --- a/packages/salesforcedx-vscode-core/test/vscode-integration/commands/functions/forceFunctionStart/ForceFunctionContainerlessStartExecutor.test.ts +++ b/packages/salesforcedx-vscode-core/test/vscode-integration/commands/functions/forceFunctionStart/ForceFunctionContainerlessStartExecutor.test.ts @@ -5,6 +5,7 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ +import { LocalRunProcess } from '@heroku/functions-core'; import { vscodeStub } from '@salesforce/salesforcedx-utils-vscode/out/test/unit/commands/mocks'; import { expect } from 'chai'; import * as proxyquire from 'proxyquire'; @@ -216,19 +217,14 @@ describe('ForceFunctionContainerlessStartExecutor unit tests', () => { }); it('Should be able to call methods that are no-ops for containerless mode.', async () => { - const disposable = { dispose: stub() }; const executor = new ForceFunctionContainerlessStartExecutor( START_KEY, LOG_NAME ); const listenerResult = await executor.setupFunctionListeners( - 'funDirPath', - disposable + 'funDirPath' ); expect(listenerResult).to.equal(undefined); - const cancelResult = await executor.cancelFunction(disposable); - expect(cancelResult).to.equal(undefined); - assert.calledOnce(disposable.dispose); const buildResult = await executor.buildFunction('nameMe', 'funDirPath'); expect(buildResult).to.equal(undefined); }); @@ -260,7 +256,32 @@ describe('ForceFunctionContainerlessStartExecutor unit tests', () => { assert.calledOnce(fakeLocalRunInst.exec); }); - it('Should call telementry when localRun fails.', async () => { + it('Should be able to cancel running function.', async () => { + const fakeProcess = { cancel: stub().resolves() }; + const fakeLocalRunInst = { + exec: stub().resolves(fakeProcess) + }; + const disposable = { + dispose: stub() + }; + const executor = new ForceFunctionContainerlessStartExecutor( + START_KEY, + LOG_NAME + ); + localRunConstructorStub.returns(fakeLocalRunInst); + + // Sets the local process + await executor.startFunction('foo', 'bar'); + + // have to wait for the unawaited promise in exec().then() to resolve + await Promise.resolve(); + + await executor.cancelFunction(disposable); + assert.calledOnce(fakeProcess.cancel); + assert.calledOnce(disposable.dispose); + }); + + it('Should call telemetry when localRun fails.', async () => { const fakeType = 'typescript'; getFunctionTypeStub.returns(fakeType); const errMessage = 'oh noes. FAIL';