-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
NEW (Extension) @W-16096256@ Send class content as request to ApexGur…
…u and get requestId back (#114)
- Loading branch information
Showing
7 changed files
with
422 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/* | ||
* Copyright (c) 2024, Salesforce, Inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
*/ | ||
|
||
import * as vscode from 'vscode'; | ||
import * as fspromises from 'fs/promises'; | ||
import { CoreExtensionService } from '../lib/core-extension-service'; | ||
import * as Constants from '../lib/constants'; | ||
|
||
export async function isApexGuruEnabledInOrg(outputChannel: vscode.LogOutputChannel): Promise<boolean> { | ||
try { | ||
const connection = await CoreExtensionService.getConnection(); | ||
const response:ApexGuruAuthResponse = await connection.request({ | ||
method: 'GET', | ||
url: Constants.APEX_GURU_AUTH_ENDPOINT, | ||
body: '' | ||
}); | ||
return response.status == 'Success'; | ||
} catch(e) { | ||
// This could throw an error for a variety of reasons. The API endpoint has not been deployed to the instance, org has no perms, timeouts etc,. | ||
// In all of these scenarios, we return false. | ||
const errMsg = e instanceof Error ? e.message : e as string; | ||
outputChannel.error('***ApexGuru perm check failed with error:***' + errMsg); | ||
outputChannel.show(); | ||
return false; | ||
} | ||
} | ||
|
||
export async function runApexGuruOnFile(selection: vscode.Uri, outputChannel: vscode.LogOutputChannel) { | ||
try { | ||
const requestId = await initiateApexGuruRequest(selection, outputChannel); | ||
// TODO: Logging the request Id for easy QA. Future stories will use this requestId to poll and retrieve the Apex Guru report. | ||
outputChannel.appendLine('***Apex Guru request Id:***' + requestId); | ||
} catch (e) { | ||
const errMsg = e instanceof Error ? e.message : e as string; | ||
outputChannel.appendLine('***Apex Guru initiate request failed***'); | ||
outputChannel.appendLine(errMsg); | ||
} | ||
} | ||
|
||
export async function initiateApexGuruRequest(selection: vscode.Uri, outputChannel: vscode.LogOutputChannel): Promise<string> { | ||
const fileContent = await fileSystem.readFile(selection.fsPath); | ||
const base64EncodedContent = Buffer.from(fileContent).toString('base64'); | ||
const connection = await CoreExtensionService.getConnection(); | ||
const response: ApexGuruInitialResponse = await connection.request({ | ||
method: 'POST', | ||
url: Constants.APEX_GURU_REQUEST, | ||
body: JSON.stringify({ | ||
classContent: base64EncodedContent | ||
}) | ||
}); | ||
|
||
if (response.status != 'new' && response.status != 'success') { | ||
outputChannel.warn('***Apex Guru returned unexpected response:***' + response.status); | ||
throw Error('***Apex Guru returned unexpected response:***' + response.status); | ||
} | ||
|
||
const requestId = response.requestId; | ||
return requestId; | ||
} | ||
|
||
export const fileSystem = { | ||
readFile: (path: string) => fspromises.readFile(path, 'utf8') | ||
}; | ||
|
||
export type ApexGuruAuthResponse = { | ||
status: string; | ||
} | ||
|
||
export type ApexGuruInitialResponse = { | ||
status: string; | ||
requestId: string; | ||
message: string; | ||
} | ||
|
||
export type ApexGuruQueryResponse = { | ||
status: string; | ||
message: string; | ||
report: string; | ||
} | ||
|
||
export type ApexGuruProperty = { | ||
name: string; | ||
value: string; | ||
}; | ||
|
||
export type ApexGuruReport = { | ||
id: string; | ||
type: string; | ||
value: string; | ||
properties: ApexGuruProperty[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
/* | ||
* Copyright (c) 2024, Salesforce, Inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
*/ | ||
|
||
import {expect} from 'chai'; | ||
import Sinon = require('sinon'); | ||
import {CoreExtensionService} from '../../../lib/core-extension-service'; | ||
import * as Constants from '../../../lib/constants'; | ||
import * as ApexGuruFunctions from '../../../apexguru/apex-guru-service' | ||
|
||
// You can import and use all API from the 'vscode' module | ||
// as well as import your extension to test it | ||
import * as vscode from 'vscode'; | ||
|
||
suite('Apex Guru Test Suite', () => { | ||
let outputChannel = vscode.window.createOutputChannel('Salesforce Code Analyzer', {log: true}); | ||
|
||
suite('#_isApexGuruEnabledInOrg', () => { | ||
let getConnectionStub: Sinon.SinonStub; | ||
let requestStub: Sinon.SinonStub; | ||
|
||
setup(() => { | ||
getConnectionStub = Sinon.stub(CoreExtensionService, 'getConnection'); | ||
requestStub = Sinon.stub(); | ||
}); | ||
|
||
teardown(() => { | ||
Sinon.restore(); | ||
}); | ||
|
||
test('Returns true if response status is Success', async () => { | ||
// ===== SETUP ===== | ||
getConnectionStub.resolves({ | ||
request: requestStub.resolves({ status: 'Success' }) | ||
}); | ||
|
||
// ===== TEST ===== | ||
const result = await ApexGuruFunctions.isApexGuruEnabledInOrg(outputChannel); | ||
|
||
// ===== ASSERTIONS ===== | ||
expect(result).to.be.true; | ||
Sinon.assert.calledOnce(getConnectionStub); | ||
Sinon.assert.calledOnce(requestStub); | ||
Sinon.assert.calledWith(requestStub, { | ||
method: 'GET', | ||
url: Constants.APEX_GURU_AUTH_ENDPOINT, | ||
body: '' | ||
}); | ||
}); | ||
|
||
test('Returns false if response status is not Success', async () => { | ||
// ===== SETUP ===== | ||
getConnectionStub.resolves({ | ||
request: requestStub.resolves({ status: 'Failure' }) | ||
}); | ||
|
||
// ===== TEST ===== | ||
const result = await ApexGuruFunctions.isApexGuruEnabledInOrg(outputChannel); | ||
|
||
// ===== ASSERTIONS ===== | ||
expect(result).to.be.false; | ||
Sinon.assert.calledOnce(getConnectionStub); | ||
Sinon.assert.calledOnce(requestStub); | ||
Sinon.assert.calledWith(requestStub, { | ||
method: 'GET', | ||
url: Constants.APEX_GURU_AUTH_ENDPOINT, | ||
body: '' | ||
}); | ||
}); | ||
|
||
test('Returns false if an error is thrown', async () => { | ||
// ===== SETUP ===== | ||
getConnectionStub.resolves({ | ||
request: requestStub.rejects(new Error('Resource not found')) | ||
}); | ||
|
||
// ===== TEST ===== | ||
const result = await ApexGuruFunctions.isApexGuruEnabledInOrg(outputChannel); | ||
|
||
// ===== ASSERTIONS ===== | ||
expect(result).to.be.false; | ||
Sinon.assert.calledOnce(getConnectionStub); | ||
Sinon.assert.calledOnce(requestStub); | ||
Sinon.assert.calledWith(requestStub, { | ||
method: 'GET', | ||
url: Constants.APEX_GURU_AUTH_ENDPOINT, | ||
body: '' | ||
}); | ||
}); | ||
}); | ||
suite('#initiateApexGuruRequest', () => { | ||
let getConnectionStub: Sinon.SinonStub; | ||
let requestStub: Sinon.SinonStub; | ||
let readFileStub: Sinon.SinonStub; | ||
|
||
setup(() => { | ||
getConnectionStub = Sinon.stub(CoreExtensionService, 'getConnection'); | ||
requestStub = Sinon.stub(); | ||
readFileStub = Sinon.stub(ApexGuruFunctions.fileSystem, 'readFile'); | ||
}); | ||
|
||
teardown(() => { | ||
Sinon.restore(); | ||
}); | ||
|
||
test('Returns requestId if response status is new', async () => { | ||
// ===== SETUP ===== | ||
getConnectionStub.resolves({ | ||
request: requestStub.resolves({ status: 'new', requestId: '12345' }) | ||
}); | ||
readFileStub.resolves('console.log("Hello World");'); | ||
|
||
// ===== TEST ===== | ||
const result = await ApexGuruFunctions.initiateApexGuruRequest(vscode.Uri.file('dummyPath'), outputChannel); | ||
|
||
// ===== ASSERTIONS ===== | ||
expect(result).to.equal('12345'); | ||
Sinon.assert.calledOnce(getConnectionStub); | ||
Sinon.assert.calledOnce(requestStub); | ||
Sinon.assert.calledOnce(readFileStub); | ||
Sinon.assert.calledWith(requestStub, Sinon.match({ | ||
method: 'POST', | ||
url: Constants.APEX_GURU_REQUEST, | ||
body: Sinon.match.string | ||
})); | ||
}); | ||
|
||
test('Logs warning if response status is not new', async () => { | ||
// ===== SETUP ===== | ||
getConnectionStub.resolves({ | ||
request: requestStub.resolves({ status: 'failed' }) | ||
}); | ||
readFileStub.resolves('console.log("Hello World");'); | ||
const outputChannelSpy = Sinon.spy(outputChannel, 'warn'); | ||
|
||
// ===== TEST ===== | ||
try { | ||
await ApexGuruFunctions.initiateApexGuruRequest(vscode.Uri.file('dummyPath'), outputChannel); | ||
} catch (e) { | ||
// ===== ASSERTIONS ===== | ||
Sinon.assert.calledOnce(outputChannelSpy); | ||
Sinon.assert.calledWith(outputChannelSpy, Sinon.match.string); | ||
} | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.