Skip to content

Commit

Permalink
Fixed add to profiles does not work when selecting a single field
Browse files Browse the repository at this point in the history
Fixed issue with refresh and open didn't work for certain datapacks without matching keys
Added multi select for exporting salesforce metadata
Bump version 0.16.17
  • Loading branch information
Codeneos committed Nov 25, 2021
1 parent 2e762ec commit 399ce5c
Show file tree
Hide file tree
Showing 12 changed files with 84 additions and 80 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog Vlocity/Salesforce Integration for VSCode

## Version 0.16.17 - 2021-25-11
- Fixed add to profiles does not work when selecting a single field
- Fixed issue with refresh and open didn't work for certain datapacks without matching keys
- Added multi select for exporting salesforce metadata

## Version 0.16.16 - 2021-11-07
- Fixed #362; issue with tsconfig-paths-webpack-plugin causing relative imports in modules to be incorrectly resolved to local files if they could be resolved locally

Expand Down
2 changes: 1 addition & 1 deletion build/loaders/yaml.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ module.exports = function (source) {
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');

return `module.exports = ${value};`;
return `module.exports = Object.freeze(${value});`;
};
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "vlocode",
"displayName": "Salesforce Vlocity Integration",
"description": "Salesforce and Vlocity development and deployment extension for VSCode",
"version": "0.16.16",
"version": "0.16.17",
"license": "MIT",
"icon": "resources/icon.png",
"author": {
Expand Down
10 changes: 5 additions & 5 deletions src/commands/exportDatapackCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,13 @@ export default class ExportDatapackCommand extends DatapackCommand {
}

protected async showDatapackTypeSelection() : Promise<string | undefined> {
const datapackOptions = Object.values(exportQueryDefinitions).filter(queryDef => queryDef.query).map(
queryDef => ({
label: queryDef.VlocityDataPackType,
const datapackOptions = Object.entries(exportQueryDefinitions)
.filter(([,queryDef]) => queryDef.query && !queryDef.requiredSetting)
.map(([key, queryDef]) => ({
label: key,
detail: queryDef.query.replace(constants.NAMESPACE_PLACEHOLDER_PATTERN, 'vlocity'),
datapackType: queryDef.VlocityDataPackType
})
);
}));

const datapackToExport = await vscode.window.showQuickPick(datapackOptions, {
matchOnDetail: true,
Expand Down
25 changes: 11 additions & 14 deletions src/commands/salesforce/retrieveMetadataCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default class RetrieveMetadataCommand extends MetadataCommand {
}

// query available records
const components = await this.getExportableComponents(metadataType);
const components = await this.vlocode.withProgress(`Query ${metadataType.nameForMsgsPlural}...`, this.getExportableComponents(metadataType));
if (components.length == 0) {
void vscode.window.showWarningMessage(`No exportable records for ${metadataType}`);
return;
Expand All @@ -37,14 +37,10 @@ export default class RetrieveMetadataCommand extends MetadataCommand {
return; // selection cancelled;
}

return this.retrieveMetadata([{
fullname: this.getManifestName(metadataType, componentToExport),
return this.retrieveMetadata(componentToExport.map(item => ({
fullname: item.fullName,
componentType: metadataType.xmlName
}]);
}

private getManifestName(metadataType: MetadataType, component: { fullName: string }) : string {
return component.fullName;
})));
}

protected async getExportableObjectLikeTypes(nameFilter: RegExp) : Promise<{ fullName: string }[]>
Expand All @@ -60,7 +56,7 @@ export default class RetrieveMetadataCommand extends MetadataCommand {
}));
}

protected async getExportableComponents(metadataType : MetadataType) : Promise<{ fullName: string }[]> {
protected async getExportableComponents(metadataType : MetadataType) : Promise<FileProperties[]> {
// query available records
const connection = await this.salesforce.getJsForceConnection();
const components = await connection.metadata.list({ type: metadataType.xmlName });
Expand Down Expand Up @@ -147,20 +143,21 @@ export default class RetrieveMetadataCommand extends MetadataCommand {
});
}

protected async showComponentSelection<T extends { fullName: string; label?: string }>(records: T[]) : Promise<T | undefined> {
protected async showComponentSelection<T extends FileProperties>(records: T[]) : Promise<Array<T> | undefined> {
const objectOptions = records.map(record => ({
label: record.label ?? record.fullName,
description: record.label ? record.fullName : undefined,
label: record.fullName,
description: `last modified: ${record.lastModifiedByName} (${record.lastModifiedDate})`,
record
})).sort((a, b) => a.label.localeCompare(b.label));

const objectSelection = await vscode.window.showQuickPick(objectOptions, {
placeHolder: 'Select metadata object to export'
placeHolder: 'Select metadata object to export',
canPickMany: true
});
if (!objectSelection) {
return; // selection cancelled;
}
return objectSelection.record;
return objectSelection.map(item => item.record);
}

protected async showExportPathSelection() : Promise<string | undefined> {
Expand Down
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ class Vlocode {
private async deactivate() {
// Log to debug as other output channels will be disposed
this.service.dispose();
console.debug('Vlocode extension deactivated');
}

static activate(context: vscode.ExtensionContext) {
Expand All @@ -206,5 +205,6 @@ export function activate(context: vscode.ExtensionContext) {
}

export function deactivate() {
console.log('deactivate');
return Vlocode.deactivate();
}
2 changes: 1 addition & 1 deletion src/lib/vlocity/datapackInfoService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default class DatapackInfoService {
const regex = new RegExp(`${removeNamespacePrefix(sobjectType)}`,'ig');
const datapackInfo = (await this.getDatapacks()).find(dataPack => dataPack.sobjectType && regex.test(removeNamespacePrefix(dataPack.sobjectType)));
if (!datapackInfo) {
this.logger.warn(`No Datapack with SObject '${sobjectType}' configured in Salesforce (see VlocityDataPackConfiguration object)`);
this.logger.verbose(`No Datapack with SObject '${sobjectType}' configured in Salesforce (see VlocityDataPackConfiguration object)`);
}
return datapackInfo?.datapackType;
}
Expand Down
16 changes: 11 additions & 5 deletions src/lib/vlocity/vlocityMatchingKeyService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Logger , injectable } from '@vlocode/core';
import SalesforceService from '@lib/salesforce/salesforceService';
import { stringEquals , cache , removeNamespacePrefix } from '@vlocode/util';

import * as ExportQueryDefinitions from 'exportQueryDefinitions.yaml';
import { VlocityNamespaceService } from './vlocityNamespaceService';
import DatapackInfoService from './datapackInfoService';
import { QueryDefinitions } from './types';
Expand All @@ -24,7 +25,6 @@ export default class VlocityMatchingKeyService {
private readonly vlocityNamespace: VlocityNamespaceService,
private readonly datapackInfo: DatapackInfoService,
private readonly salesforce: SalesforceService) {
this.exportQueryDefinitions = require('exportQueryDefinitions.yaml')?.default ?? {};
}

@cache(-1)
Expand All @@ -33,7 +33,7 @@ export default class VlocityMatchingKeyService {
}

private get queryDefinitions() {
return this.exportQueryDefinitions;
return ExportQueryDefinitions;
}

/**
Expand Down Expand Up @@ -61,11 +61,17 @@ export default class VlocityMatchingKeyService {
sobject.fields.find(field => stringEquals(field.name, this.vlocityNamespace.updateNamespace(fieldName), true))?.type;

// Append matching key fields
baseQuery += / where /gi.test(baseQuery) ? ' AND ' : ' WHERE ';
baseQuery += matchingKey.fields.filter(field => entry[field])
.map(field => `${field} = ${this.formatValue(entry[field], getFieldType(field))}`).join(' and ');
if (matchingKey.fields.length) {
baseQuery += / where /gi.test(baseQuery) ? ' AND ' : ' WHERE ';
baseQuery += matchingKey.fields.filter(field => entry[field])
.map(field => `${field} = ${this.formatValue(entry[field], getFieldType(field))}`).join(' and ');
}
baseQuery += ' ORDER BY LastModifiedDate DESC LIMIT 1';

if (!/ where /gi.test(baseQuery)) {
throw new Error(`The specified ${type} does not have a matching key`);
}

return baseQuery;
}

Expand Down
1 change: 1 addition & 0 deletions src/lib/vlocodeService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export default class VlocodeService implements vscode.Disposable, JsForceConnect
}

public dispose() {
console.log('dispose service');
this.disposables.forEach(disposable => disposable.dispose());
this.disposables = [];
if (this._datapackService) {
Expand Down
72 changes: 33 additions & 39 deletions src/lib/workspaceContextDetector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface FileFilterFunction {
@injectable({ lifecycle: LifecyclePolicy.transient })
export class WorkspaceContextDetector implements vscode.Disposable {

private contextFiles = new Array<string>();
private contextFiles: { [file: string]: boolean } = {};
private workspaceFolderWatcher: vscode.Disposable;
private workspaceFileWatcher: vscode.FileSystemWatcher;
private scheduledContextUpdate?: NodeJS.Timeout;
Expand All @@ -38,12 +38,13 @@ export class WorkspaceContextDetector implements vscode.Disposable {
}

public dispose() {
if (this.workspaceFolderWatcher) {
this.workspaceFolderWatcher.dispose();
}
if (this.workspaceFileWatcher) {
this.workspaceFileWatcher.dispose();
this.contextFiles = {};
this.workspaceFolderWatcher?.dispose();
this.workspaceFileWatcher?.dispose();
if (this.scheduledContextUpdate) {
clearTimeout(this.scheduledContextUpdate);
}
void vscode.commands.executeCommand('setContext', `${constants.CONTEXT_PREFIX}.${this.editorContextKey}`, null);
}

public async initialize() {
Expand All @@ -52,36 +53,44 @@ export class WorkspaceContextDetector implements vscode.Disposable {
this.remove(removeWorkspace.uri.fsPath);
}
for (const addedWorkspace of e.added) {
this.contextFiles.push(...await this.getApplicableFiles(addedWorkspace.uri.fsPath));
this.add(await this.getApplicableFiles(addedWorkspace.uri.fsPath));
}
this.scheduleContextUpdate();
});

this.workspaceFileWatcher = vscode.workspace.createFileSystemWatcher('**/*', false, true, false);
this.workspaceFileWatcher.onDidCreate(async newFile => {
const fsPath = newFile.fsPath;
const newFiles = new Array<string>();

if (await this.fs.isDirectory(fsPath)) {
newFiles.push(...await this.getApplicableFiles(fsPath));
} else if (this.isApplicableFile(fsPath)) {
newFiles.push(fsPath);
}

if (newFiles.length > 0) {
this.contextFiles.push(...newFiles);
const folderFiles = await this.getApplicableFiles(fsPath);
if (folderFiles.length) {
this.add(fsPath.split(/\\|\//).map((v,i,p) => [...p.slice(0, i), v].join(path.sep)));
this.add(folderFiles);
this.scheduleContextUpdate();
}
} else if (this.isApplicableFile(fsPath) ) {
this.add(fsPath.split(/\\|\//).map((v,i,p) => [...p.slice(0, i), v].join(path.sep)));
this.scheduleContextUpdate();
}
});

this.contextFiles.push(...await this.getApplicableFoldersInWorkspace());
this.add(await this.getApplicableFoldersInWorkspace());
this.scheduleContextUpdate();

return this;
}

private add(filePath: string | string[]) {
if (Array.isArray(filePath)) {
filePath.forEach(p => this.add(p));
} else {
this.contextFiles[filePath] = true;
}
}

private remove(filePath: string) {
this.contextFiles = this.contextFiles.filter(file => file.startsWith(filePath));
// eslint-disable-next-line @typescript-eslint/tslint/config
delete this.contextFiles[filePath];
}

private scheduleContextUpdate() {
Expand All @@ -94,8 +103,7 @@ export class WorkspaceContextDetector implements vscode.Disposable {
private async updateContext() {
this.scheduledContextUpdate = undefined;
const timer = new Timer();
const folders = this.contextFiles.reduce((map, fullPath) => Object.assign(map, { [fullPath]: true }), {});
await vscode.commands.executeCommand('setContext', `${constants.CONTEXT_PREFIX}.${this.editorContextKey}`, folders);
await vscode.commands.executeCommand('setContext', `${constants.CONTEXT_PREFIX}.${this.editorContextKey}`, this.contextFiles);
this.logger.verbose(`Updated context ${constants.CONTEXT_PREFIX}.${this.editorContextKey} [${timer.stop()}]`);
}

Expand All @@ -113,39 +121,25 @@ export class WorkspaceContextDetector implements vscode.Disposable {

public async getApplicableFiles(folder: string) : Promise<string[]> {
const files = new Array<string>();
const dirEntries = await fs.readdir(folder, { withFileTypes: true });
const hasApplicableFiles = dirEntries.some(entry => entry.isFile() && this.isApplicableFile(entry.name));
const entries = await fs.readdir(folder, { withFileTypes: true });

for (const entry of dirEntries) {
for (const entry of entries) {
if (entry.name.startsWith('.') || entry.name == 'node_modules') {
continue;
}
const fullPath = path.join(folder, entry.name);
if (entry.isDirectory()) {
files.push(...await this.getApplicableFiles(fullPath));
} else if (hasApplicableFiles) {
} else if (this.isApplicableFile(entry.name)) {
files.push(fullPath);
}
}

if (files.length > 0) {
// Include the parent folder when it has any files applicable
if (files.length) {
// Add folder when there are files in this folder
files.push(folder);
}

return files;
}

public async isApplicableFolder(folder: vscode.Uri) {
try {
for (const [file, type] of await vscode.workspace.fs.readDirectory(folder)) {
if (type == vscode.FileType.File && this.isApplicableFile(file)) {
return true;
}
}
} catch(err) {
console.error(err);
}
return false;
}
}
25 changes: 13 additions & 12 deletions types/yaml-files.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,22 @@
*/
declare module 'exportQueryDefinitions.yaml' {
interface DatapackQueryDefinition {
VlocityDataPackType: string
query: string
name: string
groupKey?: string
groupName?: string
groupDescription?: string
description?: string
VlocityDataPackType: string;
query: string;
name: string;
requiredSetting: string;
groupKey?: string;
groupName?: string;
groupDescription?: string;
description?: string;
matchingKey?: {
fields: string[],
returnField?: string
fields: string[];
returnField?: string;
}
salesforceUrl?: {
namespace?: string,
path: string
} | string
namespace?: string;
path: string;
} | string;
}
const exportQueryDefinitions : {
[datapackType: string] : DatapackQueryDefinition
Expand Down

0 comments on commit 399ce5c

Please sign in to comment.