-
Notifications
You must be signed in to change notification settings - Fork 398
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Retrieve default org's metadata types (#1237)
@W-5980289@
- Loading branch information
Showing
6 changed files
with
314 additions
and
1 deletion.
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,13 @@ | ||
/* | ||
* Copyright (c) 2019, 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 { | ||
onUsernameChange, | ||
forceDescribeMetadata, | ||
ForceDescribeMetadataExecutor, | ||
getMetadataTypesPath, | ||
buildTypesList | ||
} from './orgMetadata'; |
154 changes: 154 additions & 0 deletions
154
packages/salesforcedx-vscode-core/src/orgBrowser/orgMetadata.ts
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,154 @@ | ||
/* | ||
* Copyright (c) 2019, 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 | ||
*/ | ||
|
||
import { | ||
CliCommandExecutor, | ||
Command, | ||
SfdxCommandBuilder | ||
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli'; | ||
import { ContinueResponse } from '@salesforce/salesforcedx-utils-vscode/out/src/types'; | ||
import * as fs from 'fs'; | ||
import * as path from 'path'; | ||
import { Observable } from 'rxjs/Observable'; | ||
import { isNullOrUndefined } from 'util'; | ||
import * as vscode from 'vscode'; | ||
import { channelService } from '../channels'; | ||
import { | ||
EmptyParametersGatherer, | ||
SfdxCommandlet, | ||
SfdxCommandletExecutor, | ||
SfdxWorkspaceChecker | ||
} from '../commands'; | ||
import { nls } from '../messages'; | ||
import { notificationService, ProgressNotification } from '../notifications'; | ||
import { taskViewService } from '../statuses'; | ||
import { telemetryService } from '../telemetry'; | ||
import { getRootWorkspacePath, hasRootWorkspace, OrgAuthInfo } from '../util'; | ||
|
||
export class ForceDescribeMetadataExecutor extends SfdxCommandletExecutor< | ||
string | ||
> { | ||
private outputPath: string; | ||
|
||
public constructor(outputPath: string) { | ||
super(); | ||
this.outputPath = outputPath; | ||
} | ||
|
||
public build(data: {}): Command { | ||
return new SfdxCommandBuilder() | ||
.withArg('force:mdapi:describemetadata') | ||
.withJson() | ||
.withFlag('-f', this.outputPath) | ||
.withLogName('force_describe_metadata') | ||
.build(); | ||
} | ||
|
||
public execute(response: ContinueResponse<string>): void { | ||
const startTime = process.hrtime(); | ||
const cancellationTokenSource = new vscode.CancellationTokenSource(); | ||
const cancellationToken = cancellationTokenSource.token; | ||
|
||
const execution = new CliCommandExecutor(this.build(response.data), { | ||
cwd: getRootWorkspacePath() | ||
}).execute(cancellationToken); | ||
|
||
execution.processExitSubject.subscribe(async data => { | ||
this.logMetric(execution.command.logName, startTime); | ||
buildTypesList(this.outputPath); | ||
}); | ||
notificationService.reportExecutionError( | ||
execution.command.toString(), | ||
(execution.stderrSubject as any) as Observable<Error | undefined> | ||
); | ||
channelService.streamCommandOutput(execution); | ||
ProgressNotification.show(execution, cancellationTokenSource); | ||
taskViewService.addCommandExecution(execution, cancellationTokenSource); | ||
} | ||
} | ||
|
||
const workspaceChecker = new SfdxWorkspaceChecker(); | ||
const parameterGatherer = new EmptyParametersGatherer(); | ||
|
||
export async function forceDescribeMetadata(outputPath?: string) { | ||
if (isNullOrUndefined(outputPath)) { | ||
outputPath = await getMetadataTypesPath(); | ||
} | ||
const describeExecutor = new ForceDescribeMetadataExecutor(outputPath!); | ||
const commandlet = new SfdxCommandlet( | ||
workspaceChecker, | ||
parameterGatherer, | ||
describeExecutor | ||
); | ||
await commandlet.run(); | ||
} | ||
|
||
export async function getMetadataTypesPath(): Promise<string | undefined> { | ||
if (hasRootWorkspace()) { | ||
const workspaceRootPath = getRootWorkspacePath(); | ||
const defaultUsernameOrAlias = await OrgAuthInfo.getDefaultUsernameOrAlias(); | ||
const defaultUsernameIsSet = typeof defaultUsernameOrAlias !== 'undefined'; | ||
|
||
if (defaultUsernameIsSet) { | ||
const username = await OrgAuthInfo.getUsername(defaultUsernameOrAlias!); | ||
const metadataTypesPath = path.join( | ||
workspaceRootPath, | ||
'.sfdx', | ||
'orgs', | ||
username, | ||
'metadata', | ||
'metadataTypes.json' | ||
); | ||
return metadataTypesPath; | ||
} else { | ||
const err = nls.localize('error_no_default_username'); | ||
telemetryService.sendError(err); | ||
throw new Error(err); | ||
} | ||
} else { | ||
const err = nls.localize('cannot_determine_workspace'); | ||
telemetryService.sendError(err); | ||
throw new Error(err); | ||
} | ||
} | ||
|
||
export type MetadataObject = { | ||
directoryName: string; | ||
inFolder: boolean; | ||
metaFile: boolean; | ||
suffix: string; | ||
xmlName: string; | ||
}; | ||
|
||
export function buildTypesList(metadataTypesPath: string): string[] { | ||
try { | ||
const fileData = JSON.parse(fs.readFileSync(metadataTypesPath, 'utf8')); | ||
const metadataObjects = fileData.metadataObjects as MetadataObject[]; | ||
const metadataTypes = []; | ||
for (const metadataObject of metadataObjects) { | ||
if (!isNullOrUndefined(metadataObject.xmlName)) { | ||
metadataTypes.push(metadataObject.xmlName); | ||
} | ||
} | ||
telemetryService.sendEventData('Metadata Types Quantity', undefined, { | ||
metadataTypes: metadataTypes.length | ||
}); | ||
return metadataTypes; | ||
} catch (e) { | ||
throw e; | ||
} | ||
} | ||
|
||
export async function onUsernameChange() { | ||
const metadataTypesPath = await getMetadataTypesPath(); | ||
if ( | ||
!isNullOrUndefined(metadataTypesPath) && | ||
!fs.existsSync(metadataTypesPath) | ||
) { | ||
await forceDescribeMetadata(metadataTypesPath); | ||
} | ||
} |
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
122 changes: 122 additions & 0 deletions
122
packages/salesforcedx-vscode-core/test/vscode-integration/orgBrowser/orgMetadata.test.ts
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,122 @@ | ||
/* | ||
* Copyright (c) 2019, 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 | ||
*/ | ||
|
||
import { expect } from 'chai'; | ||
import * as fs from 'fs'; | ||
import * as path from 'path'; | ||
import { SinonStub, stub } from 'sinon'; | ||
import { isNullOrUndefined } from 'util'; | ||
import { nls } from '../../../src/messages'; | ||
import { | ||
buildTypesList, | ||
ForceDescribeMetadataExecutor, | ||
getMetadataTypesPath | ||
} from '../../../src/orgBrowser'; | ||
import { | ||
getRootWorkspacePath, | ||
hasRootWorkspace, | ||
OrgAuthInfo | ||
} from '../../../src/util'; | ||
|
||
describe('Force Describe Metadata', () => { | ||
it('Should build describe metadata command', async () => { | ||
const outputPath = 'outputPath'; | ||
const forceDescribeMetadataExec = new ForceDescribeMetadataExecutor( | ||
outputPath | ||
); | ||
const forceDescribeMetadataCmd = forceDescribeMetadataExec.build({}); | ||
expect(forceDescribeMetadataCmd.toCommand()).to.equal( | ||
`sfdx force:mdapi:describemetadata --json --loglevel fatal -f ${outputPath}` | ||
); | ||
}); | ||
}); | ||
|
||
// tslint:disable:no-unused-expression | ||
describe('getMetadataTypesPath', () => { | ||
let getDefaultUsernameStub: SinonStub; | ||
let getUsernameStub: SinonStub; | ||
const rootWorkspacePath = getRootWorkspacePath(); | ||
beforeEach(() => { | ||
getDefaultUsernameStub = stub(OrgAuthInfo, 'getDefaultUsernameOrAlias'); | ||
getUsernameStub = stub(OrgAuthInfo, 'getUsername'); | ||
}); | ||
afterEach(() => { | ||
getDefaultUsernameStub.restore(); | ||
getUsernameStub.restore(); | ||
}); | ||
|
||
it('should return the path for a given username', async () => { | ||
getDefaultUsernameStub.returns('defaultAlias'); | ||
getUsernameStub.returns('test-username1@example.com'); | ||
const filePath = path.join( | ||
rootWorkspacePath, | ||
'.sfdx', | ||
'orgs', | ||
'test-username1@example.com', | ||
'metadata', | ||
'metadataTypes.json' | ||
); | ||
expect(await getMetadataTypesPath()).to.equal(filePath); | ||
}); | ||
|
||
it('should throw an error if default username is not set', async () => { | ||
getDefaultUsernameStub.returns(undefined); | ||
let errorWasThrown = false; | ||
try { | ||
await getMetadataTypesPath(); | ||
} catch (e) { | ||
errorWasThrown = true; | ||
expect(e.message).to.equal(nls.localize('error_no_default_username')); | ||
} finally { | ||
expect(getUsernameStub.called).to.be.false; | ||
expect(errorWasThrown).to.be.true; | ||
} | ||
}); | ||
}); | ||
|
||
describe('build metadata types list', () => { | ||
let readFileStub: SinonStub; | ||
let fileExistStub: SinonStub; | ||
beforeEach(() => { | ||
readFileStub = stub(fs, 'readFileSync'); | ||
fileExistStub = stub(fs, 'existsSync'); | ||
}); | ||
afterEach(() => { | ||
readFileStub.restore(); | ||
fileExistStub.restore(); | ||
}); | ||
it('should return a list of xmlNames when given a list of metadata objects', async () => { | ||
const metadataTypesPath = 'metadataTypesPath'; | ||
fileExistStub.returns(true); | ||
const fileData = JSON.stringify({ | ||
metadataObjects: [ | ||
{ xmlName: 'fakeName1', suffix: 'fakeSuffix1' }, | ||
{ xmlName: 'fakeName2', suffix: 'fakeSuffix2' } | ||
], | ||
extraField1: 'extraData1', | ||
extraField2: 'extraData2' | ||
}); | ||
readFileStub.returns(fileData); | ||
const xmlNames = buildTypesList(metadataTypesPath); | ||
if (!isNullOrUndefined(xmlNames)) { | ||
expect(xmlNames[0]).to.equal('fakeName1'); | ||
expect(xmlNames[1]).to.equal('fakeName2'); | ||
} | ||
}); | ||
it('should throw an error if the file does not exist yet', async () => { | ||
const metadataTypesPath = 'invalidPath'; | ||
fileExistStub.returns(false); | ||
let errorWasThrown = false; | ||
try { | ||
buildTypesList(metadataTypesPath); | ||
} catch (e) { | ||
errorWasThrown = true; | ||
} finally { | ||
expect(errorWasThrown).to.be.true; | ||
} | ||
}); | ||
}); |
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