Skip to content

Commit

Permalink
Retrieve default org's metadata types (#1237)
Browse files Browse the repository at this point in the history
@W-5980289@
  • Loading branch information
AnanyaJha committed Apr 10, 2019
1 parent 3a4362a commit f9049c0
Show file tree
Hide file tree
Showing 6 changed files with 314 additions and 1 deletion.
13 changes: 13 additions & 0 deletions packages/salesforcedx-vscode-core/src/orgBrowser/index.ts
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 packages/salesforcedx-vscode-core/src/orgBrowser/orgMetadata.ts
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);
}
}
10 changes: 10 additions & 0 deletions packages/salesforcedx-vscode-core/src/telemetry/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,16 @@ export class TelemetryService {
}
}

public sendEventData(
eventName: string,
properties?: { [key: string]: string },
measures?: { [key: string]: number }
): void {
if (this.reporter !== undefined && this.isTelemetryEnabled) {
this.reporter.sendTelemetryEvent(eventName, properties, measures);
}
}

public sendErrorEvent(errorMsg: string, callstack: string): void {
if (this.reporter !== undefined && this.isTelemetryEnabled) {
this.reporter.sendTelemetryEvent('error', {
Expand Down
2 changes: 1 addition & 1 deletion packages/salesforcedx-vscode-core/src/util/authInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from '@salesforce/core';
import * as path from 'path';
import { nls } from '../messages';
import { getRootWorkspacePath, hasRootWorkspace } from './index';
import { getRootWorkspacePath } from './index';
export class OrgAuthInfo {
public static async getDefaultUsernameOrAlias(): Promise<string | undefined> {
try {
Expand Down
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;
}
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -230,5 +230,19 @@ describe('Telemetry', () => {
};
assert.calledWith(reporter, 'commandExecution', match(expectedData));
});

it('should send correct data format on sendEventData', async () => {
mockContext = new MockContext(true);

const telemetryService = TelemetryService.getInstance();
telemetryService.initializeService(mockContext, machineId);

const eventName = 'eventName';
const property = { property: 'property for event' };
const measure = { measure: 123456 };
telemetryService.sendEventData(eventName, property, measure);

assert.calledWith(reporter, eventName, property, measure);
});
});
});

0 comments on commit f9049c0

Please sign in to comment.