Skip to content

Commit

Permalink
fix: metadata API commands hand indefinitely due to jsforce post-proc…
Browse files Browse the repository at this point in the history
…essing of SOAP responses
  • Loading branch information
Codeneos committed Jan 26, 2023
1 parent 0bca5fc commit 44c6291
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 6 deletions.
3 changes: 1 addition & 2 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@
}
}
}
],
"dependsOn": ["watch-vlocity-deploy"]
]
},
{
"type": "shell",
Expand Down
78 changes: 74 additions & 4 deletions packages/salesforce/src/connection/salesforceConnection.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { Connection, ConnectionOptions, RequestInfo, OAuth2, OAuth2Options } from 'jsforce';
import { Connection, ConnectionOptions, RequestInfo, OAuth2, Metadata, Callback, DeployResult, DescribeMetadataResult } from 'jsforce';
import { HttpApiOptions } from 'jsforce/http-api';

import { Logger, LogLevel, LogManager } from '@vlocode/core';
import { CustomError, decorate, lazy, wait } from '@vlocode/util';
import { HttpResponse, HttpTransport } from './httpTransport';
import { CustomError, decorate, wait } from '@vlocode/util';
import { HttpTransport } from './httpTransport';

/**
* Promise with a `thenCall` callback method compatible with JSforce promises
*/
type CallbackPromise<T> = Promise<T> & { thenCall(callback: Callback<T> | undefined): CallbackPromise<T> };

/**
* Salesforce connection decorator that extends the base JSForce connection
Expand Down Expand Up @@ -63,14 +68,17 @@ export class SalesforceConnection extends Connection {
* WHen the prototype is changed of connection local variables aren't re-initialized; this method sets the defaults for all private and public variables.
*/
private initializeLocalVariables() {
this.disableFeedTracking = true;
this.disableFeedTracking = true;

// Setup transport
this['_transport'] = new HttpTransport({ instanceUrl: this.instanceUrl, baseUrl: this._baseUrl() }, LogManager.get('HttpTransport'));
if (this.oauth2) {
this.oauth2 = new SalesforceOAuth2(this.oauth2, this);
}

// Decorate metadata API
this.metadata = new SalesforceMetadata(this.metadata);

// Configure logger
this.logger = LogManager.get(SalesforceConnection)
this['_logger'] = new JsForceLogAdapter(this.logger);
Expand All @@ -80,6 +88,8 @@ export class SalesforceConnection extends Connection {
if (this['_refreshDelegate']) {
this['_refreshDelegate']['_refreshFn'] = SalesforceConnection.refreshAccessToken;
}

this.patchAsyncResultLocator();
}

private static refreshAccessToken(_this: SalesforceConnection, callback: (err: any, accessToken?: string, response?: any) => void) : Promise<string> {
Expand Down Expand Up @@ -174,6 +184,30 @@ export class SalesforceConnection extends Connection {
)
);
}

/**
* Monkey-patch jsforce async AsyncResultLocator to support `res.done` as boolean type
*/
private patchAsyncResultLocator() {
const asyncResultLocatorPrototype = Object.getPrototypeOf(this.metadata.checkStatus(''));
if (typeof asyncResultLocatorPrototype.then === 'function') {
const convertType = function(res: any) {
if (res?.$["xsi:nil"] === 'true' || res?.$["xsi:nil"] === true) {
return null;
}
return res;
};
asyncResultLocatorPrototype.then = function(onResolve, onReject) {
return this._results.then(results => {
results = Array.isArray(results) ? results.map(convertType) : convertType(results);
if (this._isArray && !Array.isArray(results)) {
results = [ results ];
}
return onResolve?.(results);
}, onReject);
};
}
}
}

class JsForceLogAdapter {
Expand All @@ -200,6 +234,8 @@ class JsForceLogAdapter {
public debug(...args: any[]): void { this.logger.write(LogLevel.debug, ...args); }
}

//Metadata.prototype.checkDeployStatus

class SalesforceOAuth2 extends decorate(OAuth2) {

private transport: HttpTransport;
Expand Down Expand Up @@ -249,3 +285,37 @@ class SalesforceOAuth2 extends decorate(OAuth2) {
return response.body;
}
}

/**
* Patches Metadata access container that which fixes issues with `checkDeployStatus` and `describe` results
*/
class SalesforceMetadata extends decorate(Metadata) {

private get apiVersion(): string {
return this.connection.version;
}

private get connection(): SalesforceConnection {
return this['_conn'];
}

public checkDeployStatus(id: string, includeDetails?: boolean, callback?: Callback<DeployResult>): Promise<DeployResult> {
if (typeof includeDetails === 'function') {
callback = includeDetails;
}
return this['_invoke']('checkDeployStatus', {
asyncProcessId: id,
includeDetails: includeDetails === true
}).thenCall(callback);
}

public describe(version?: string, callback?: Callback<DescribeMetadataResult>): Promise<DescribeMetadataResult> {
if (typeof version !== 'string') {
callback = version;
version = this.apiVersion;
}
return this['_invoke']("describeMetadata", {
asOfVersion: version
}).thenCall(callback);
}
}

0 comments on commit 44c6291

Please sign in to comment.