Skip to content

Commit

Permalink
fix: add ability to stop containerless function (#4025)
Browse files Browse the repository at this point in the history
* build: update heroku functions core to v0.3.0

* test: add test for containerless function stop

* refactor: use async/await for containerless function start

* refactor: make container start function as async

Co-authored-by: gbockus-sf <76090802+gbockus-sf@users.noreply.github.com>
  • Loading branch information
mohanraj-r and gbockus-sf authored May 11, 2022
1 parent 5ade0a6 commit 370c1d9
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 22 deletions.
2 changes: 1 addition & 1 deletion packages/salesforcedx-vscode-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,13 @@ export class ForceFunctionContainerStartExecutor extends ForceFunctionStartExecu
});
}

public startFunction(functionName: string): void {
public async startFunction(functionName: string): Promise<void> {
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);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -19,22 +19,27 @@ import {
import { ForceFunctionStartExecutor } from './ForceFunctionStartExecutor';

export class ForceFunctionContainerlessStartExecutor extends ForceFunctionStartExecutor {
private process: LocalRunProcess | undefined | void;

public async setupFunctionListeners(): Promise<void> {
console.log('No listeners for containerless function.');
}

public async cancelFunction(
registeredStartedFunctionDisposable: Disposable
): Promise<void> {
// TODO: how to stop the localRun
if (this.process && !this.process.cancelled) {
this.process.cancel();
this.process = undefined;
}
registeredStartedFunctionDisposable.dispose();
}

public async buildFunction(): Promise<void> {
console.log('No build for containerless function');
}

public startFunction(functionName: string, functionDirPath: string): void {
public async startFunction(functionName: string, functionDirPath: string): Promise<void> {
const functionLanguage = FunctionService.instance.getFunctionType();
channelService.appendLine(
`Starting ${functionName} of type ${functionLanguage}`
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
});
Expand Down Expand Up @@ -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';
Expand Down

0 comments on commit 370c1d9

Please sign in to comment.