Skip to content

Commit

Permalink
Merge pull request #390 from forcedotcom/wr/deploy
Browse files Browse the repository at this point in the history
Wr/deploy
  • Loading branch information
shetzel authored Apr 2, 2021
2 parents 107070d + 4cda33f commit 9aef985
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 4 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"postcompile": "tsc -p test; tsc -p typedocExamples",
"prepack": "sf-build",
"pretest": "sf-compile-test",
"test": "sf-test --require ts-node/register"
"test": "sf-test"
},
"keywords": [
"force",
Expand Down
66 changes: 65 additions & 1 deletion src/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import { URL } from 'url';
import { AsyncResult, DeployOptions, DeployResultLocator } from 'jsforce/api/metadata';
import { Callback } from 'jsforce/connection';
import { Duration, maxBy, merge, env } from '@salesforce/kit';
import {
asString,
Expand All @@ -26,9 +28,12 @@ import {
RequestInfo,
Tooling as JSForceTooling,
} from 'jsforce';
// no types for Transport
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
import * as Transport from 'jsforce/lib/transport';
import { AuthFields, AuthInfo } from './authInfo';
import { MyDomainResolver } from './status/myDomainResolver';

import { ConfigAggregator } from './config/configAggregator';
import { Logger } from './logger';
import { SfdxError } from './sfdxError';
Expand All @@ -49,6 +54,7 @@ export const SFDX_HTTP_HEADERS = {
};

export const DNS_ERROR_NAME = 'Domain Not Found';
export type DeployOptionsWithRest = DeployOptions & { rest?: boolean };

// This interface is so we can add the autoFetchQuery method to both the Connection
// and Tooling classes and get nice typing info for it within editors. JSForce is
Expand Down Expand Up @@ -108,6 +114,7 @@ export class Connection extends JSForceConnection {

this.options = options;
}

/**
* Creates an instance of a Connection. Performs additional async initializations.
*
Expand Down Expand Up @@ -168,6 +175,63 @@ export class Connection extends JSForceConnection {
this.logger = this.tooling._logger = await Logger.child('connection');
}

/**
* TODO: This should be moved into JSForce V2 once ready
* this is only a temporary solution to support both REST and SOAP APIs
*
* deploy a zipped buffer from the SDRL with REST or SOAP
*
* @param zipInput data to deploy
* @param options JSForce deploy options + a boolean for rest
* @param callback
*/
public async deploy(
zipInput: Buffer,
options: DeployOptionsWithRest,
callback?: Callback<AsyncResult>
): Promise<DeployResultLocator<AsyncResult>> {
const rest = options.rest;
// neither API expects this option
delete options.rest;
if (rest) {
this.logger.debug('deploy with REST');
const headers = {
Authorization: this && `OAuth ${this.accessToken}`,
clientId: this.oauth2 && this.oauth2.clientId,
'Sforce-Call-Options': 'client=sfdx-core',
};
const url = `${this.baseUrl()}/metadata/deployRequest`;
const request = Transport.prototype._getHttpRequestModule();

return new Promise((resolve, reject) => {
const req = request.post(url, { headers }, (err: Error, httpResponse: { statusCode: number }, body: string) => {
let res;
try {
res = JSON.parse(body);
} catch (e) {
reject(SfdxError.wrap(body));
}
resolve(res);
});
const form = req.form();

// Add the zip file
form.append('file', zipInput, {
contentType: 'application/zip',
filename: 'package.xml',
});

// Add the deploy options
form.append('entity_content', JSON.stringify({ deployOptions: options }), {
contentType: 'application/json',
});
});
} else {
this.logger.debug('deploy with SOAP');
return this.metadata.deploy(zipInput, options, callback);
}
}

/**
* Send REST API request with given HTTP request info, with connected session information
* and SFDX headers.
Expand Down
2 changes: 1 addition & 1 deletion src/keyChainImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export class KeychainAccess implements PasswordStore {
}

// eslint-disable-next-line @typescript-eslint/no-misused-promises
credManager.on('close', async (code) => {
credManager.on('close', async (code: number) => {
try {
return await this.osImpl.onGetCommandClose(code, stdout, stderr, opts, fn);
} catch (e) {
Expand Down
44 changes: 43 additions & 1 deletion test/unit/connectionTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
* 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 { get, JsonMap } from '@salesforce/ts-types';
import { get, getObject, JsonMap } from '@salesforce/ts-types';

import { assert, expect } from 'chai';
import * as jsforce from 'jsforce';
import { fromStub, stubInterface, StubbedType } from '@salesforce/ts-sinon';
import { Duration } from '@salesforce/kit';
// no types for Transport
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
import * as Transport from 'jsforce/lib/transport';
import { AuthInfo } from '../../src/authInfo';
import { MyDomainResolver } from '../../src/status/myDomainResolver';
import { ConfigAggregator, ConfigInfo } from '../../src/config/configAggregator';
Expand Down Expand Up @@ -186,6 +190,44 @@ describe('Connection', () => {
expect(response).to.deep.equal(testResponse);
});

describe('deploy', () => {
it('deploy() will work with REST', async () => {
const conn = await Connection.create({
authInfo: fromStub(testAuthInfoWithDomain),
});

const req = Transport.prototype._getHttpRequestModule();
const postStub = $$.SANDBOX.stub(req, 'post').yields(null, null, '{"done": true}', {
form: () => {
return { append: () => {} };
},
});

await conn.deploy(new Buffer('test data'), { rest: true }, () => {});

const headers = getObject<{ Authorization: string; 'Sforce-Call-Options': string }>(
postStub.args[0][1],
'headers',
{ Authorization: '', 'Sforce-Call-Options': '' }
);

expect(postStub.callCount).to.equal(1);
expect(postStub.args[0][0]).to.equal('/services/data/v42.0/metadata/deployRequest');
expect(headers.Authorization).to.include('OAuth');
expect(headers['Sforce-Call-Options']).to.equal('client=sfdx-core');
});

it('deploy() will work with SOAP', async () => {
const conn = await Connection.create({
authInfo: fromStub(testAuthInfoWithDomain),
});
const soapDeployStub = $$.SANDBOX.stub(conn.metadata, 'deploy').resolves();

await conn.deploy(new Buffer('test data'), { rest: false }, () => {});
expect(soapDeployStub.callCount).to.equal(1);
});
});

it('autoFetchQuery() should call this.query with proper args', async () => {
const records = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }, { id: 6 }];
const queryResponse = { totalSize: records.length, done: true, records };
Expand Down

0 comments on commit 9aef985

Please sign in to comment.