delete internal http and rest client

This commit is contained in:
Bryan MacFarlane 2017-03-28 21:24:15 -04:00
Родитель d42dd40dc0
Коммит b9cecf3e56
10 изменённых файлов: 65 добавлений и 1020 удалений

Просмотреть файл

@ -1,8 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
import restm = require('./RestClient');
import httpm = require('./HttpClient');
import vsom = require('./VsoClient');
import VsoBaseInterfaces = require('./interfaces/common/VsoBaseInterfaces');
import serm = require('./Serialization');
@ -15,11 +13,6 @@ export class ClientApiBase {
http: hm.HttpClient;
rest: rm.RestClient;
// TODO: delete these three after regen
httpClient: httpm.HttpCallbackClient;
restCallbackClient: restm.RestCallbackClient;
restClient: restm.RestClient;
vsoClient: vsom.VsoClient;
constructor(baseUrl: string, handlers: VsoBaseInterfaces.IRequestHandler[], userAgent?: string);
@ -30,36 +23,12 @@ export class ClientApiBase {
this.http = new hm.HttpClient(userAgent, handlers);
this.rest = new rm.RestClient(userAgent, null, handlers);
// TODO: delete these three after regen
this.httpClient = new httpm.HttpCallbackClient(userAgent, handlers);
this.restCallbackClient = new restm.RestCallbackClient(this.httpClient);
this.restClient = new restm.RestClient(userAgent, handlers);
this.vsoClient = new vsom.VsoClient(baseUrl, this.restCallbackClient);
this.vsoClient = new vsom.VsoClient(baseUrl, this.rest);
this.userAgent = userAgent;
}
setUserAgent(userAgent: string) {
this.userAgent = userAgent;
this.httpClient.userAgent = userAgent;
}
public connect(): Promise<any> {
return new Promise((resolve, reject) => {
this.restCallbackClient.get(this.vsoClient.resolveUrl('/_apis/connectionData'), "", (err: any, statusCode: number, obj: any) => {
if (err) {
err.statusCode = statusCode;
reject(err);
}
else {
resolve(obj);
}
}, null);
});
}
public createAcceptHeader(type: string, apiVersion?: string): string {
return this.restCallbackClient.createAcceptHeader(type, apiVersion);
return type + (apiVersion ? (';api-version=' + apiVersion) : '');
}
public createRequestOptions(type: string, apiVersion?: string) {

Просмотреть файл

@ -11,6 +11,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
import stream = require("stream");
import * as restm from 'typed-rest-client/RestClient';
import VsoBaseInterfaces = require('./interfaces/common/VsoBaseInterfaces');
import FileContainerApiBase = require("./FileContainerApiBase");
import FileContainerInterfaces = require("./interfaces/FileContainerInterfaces");
@ -37,6 +38,7 @@ export class FileContainerApi extends FileContainerApiBase.FileContainerApiBase
});
}
// used by ChunkStream
public _createItem(
customHeaders: VsoBaseInterfaces.IHeaders,
contentStream: NodeJS.ReadableStream,
@ -56,15 +58,27 @@ export class FileContainerApi extends FileContainerApiBase.FileContainerApiBase
};
customHeaders = customHeaders || {};
customHeaders["Content-Type"] = "application/octet-stream";
customHeaders["Content-Type"] = "";
this.vsoClient.getVersioningData("2.2-preview.3", "Container", "e4f5c81e-e250-447b-9fef-bd48471bea5e", routeValues, queryValues)
.then((versioningData: vsom.ClientVersioningData) => {
var url: string = versioningData.requestUrl;
var apiVersion: string = versioningData.apiVersion;
var serializationData = { responseTypeMetadata: FileContainerInterfaces.TypeInfo.FileContainerItem, responseIsCollection: false };
this.restCallbackClient.uploadStream('PUT', url, apiVersion, contentStream, customHeaders, onResult, serializationData);
let options: restm.IRequestOptions = this.createRequestOptions('application/octet-stream',
versioningData.apiVersion);
options.additionalHeaders = customHeaders;
this.rest.uploadStream<FileContainerInterfaces.FileContainerItem>('PUT', url, contentStream, options)
.then((res: restm.IRestResponse<FileContainerInterfaces.FileContainerItem>) => {
let ret = this.formatResponse(res.result,
FileContainerInterfaces.TypeInfo.FileContainerItem,
false);
onResult(null, res.statusCode, ret);
})
.catch((err) => {
onResult(err, err.statusCode, null);
});
}, (error) => {
onResult(error, error.statusCode, null);
});

Просмотреть файл

@ -1,460 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
import url = require("url");
import http = require("http");
import https = require("https");
import tunnel = require("tunnel");
import ifm = require('./interfaces/common/VsoBaseInterfaces');
http.globalAgent.maxSockets = 100;
export enum HttpCodes {
OK = 200,
MultipleChoices = 300,
MovedPermanantly = 301,
ResourceMoved = 302,
NotModified = 304,
UseProxy = 305,
SwitchProxy = 306,
TemporaryRedirect = 307,
PermanentRedirect = 308,
BadRequest = 400,
Unauthorized = 401,
PaymentRequired = 402,
Forbidden = 403,
NotFound = 404,
MethodNotAllowed = 405,
NotAcceptable = 406,
ProxyAuthenticationRequired = 407,
RequestTimeout = 408,
Conflict = 409,
Gone = 410,
InternalServerError = 500,
NotImplemented = 501,
BadGateway = 502,
ServiceUnavailable = 503,
GatewayTimeout = 504,
}
export class HttpClientResponse {
constructor(message: http.IncomingMessage) {
this.message = message;
}
public message: http.IncomingMessage;
readBody(): Promise<string> {
return new Promise<string>(async(resolve, reject) => {
let output: string = '';
this.message.on('data', (chunk: string) => {
output += chunk;
});
this.message.on('end', () => {
resolve(output);
});
});
}
}
export interface RequestInfo {
options: http.RequestOptions;
parsedUrl: url.Url;
httpModule: any;
}
export function isHttps(requestUrl: string) {
let parsedUrl: url.Url = url.parse(requestUrl);
return parsedUrl.protocol === 'https:';
}
export class HttpClient {
userAgent: string;
handlers: ifm.IRequestHandler[];
socketTimeout: number;
constructor(userAgent: string, handlers?: ifm.IRequestHandler[], socketTimeout?: number) {
this.userAgent = userAgent;
this.handlers = handlers;
this.socketTimeout = socketTimeout ? socketTimeout : 3 * 60000;
}
public get(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<HttpClientResponse> {
return this.request('GET', requestUrl, null, additionalHeaders || {});
}
public del(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<HttpClientResponse> {
return this.request('DELETE', requestUrl, null, additionalHeaders || {});
}
public post(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise<HttpClientResponse> {
return this.request('POST', requestUrl, data, additionalHeaders || {});
}
public patch(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise<HttpClientResponse> {
return this.request('PATCH', requestUrl, data, additionalHeaders || {});
}
public put(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise<HttpClientResponse> {
return this.request('PUT', requestUrl, data, additionalHeaders || {});
}
public sendStream(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, additionalHeaders?: ifm.IHeaders): Promise<HttpClientResponse> {
return this.request(verb, requestUrl, stream, additionalHeaders);
}
/**
* Makes a raw http request.
* All other methods such as get, post, patch, and request ultimately call this.
* Prefer get, del, post and patch
*/
public request(verb: string, requestUrl: string, data: string | NodeJS.ReadableStream, headers: ifm.IHeaders): Promise<HttpClientResponse> {
return new Promise<HttpClientResponse>(async(resolve, reject) => {
try {
var info: RequestInfo = this._prepareRequest(verb, requestUrl, headers);
let res: HttpClientResponse = await this._requestRaw(info, data);
// TODO: check 401 if handled
// TODO: retry support
resolve(res);
}
catch (err) {
// only throws in truly exceptional cases (connection, can't resolve etc...)
// responses from the server do not throw
reject(err);
}
});
}
private _requestRaw(info: RequestInfo, data: string | NodeJS.ReadableStream): Promise<HttpClientResponse> {
return new Promise<HttpClientResponse>((resolve, reject) => {
let socket;
let isDataString = typeof(data) === 'string';
if (typeof(data) === 'string') {
info.options.headers["Content-Length"] = Buffer.byteLength(data, 'utf8');
}
let req: http.ClientRequest = info.httpModule.request(info.options, (msg: http.IncomingMessage) => {
let res: HttpClientResponse = new HttpClientResponse(msg);
resolve(res);
});
req.on('socket', (sock) => {
socket = sock;
});
// If we ever get disconnected, we want the socket to timeout eventually
req.setTimeout(this.socketTimeout, () => {
if (socket) {
socket.end();
}
reject(new Error('Request timeout: ' + info.options.path));
});
req.on('error', function (err) {
// err has statusCode property
// res should have headers
reject(err);
});
if (data && typeof(data) === 'string') {
req.write(data, 'utf8');
}
if (data && typeof(data) !== 'string') {
data.on('close', function () {
req.end();
});
data.pipe(req);
}
else {
req.end();
}
});
}
private _prepareRequest(method: string, requestUrl: string, headers: any): RequestInfo {
let info: RequestInfo = <RequestInfo>{};
info.parsedUrl = url.parse(requestUrl);
let usingSsl = info.parsedUrl.protocol === 'https:';
info.httpModule = usingSsl ? https : http;
var defaultPort: number = usingSsl ? 443 : 80;
var proxyUrl: url.Url;
if (process.env.HTTPS_PROXY && usingSsl) {
proxyUrl = url.parse(process.env.HTTPS_PROXY);
} else if (process.env.HTTP_PROXY) {
proxyUrl = url.parse(process.env.HTTP_PROXY);
}
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
info.options = <http.RequestOptions>{};
info.options.host = info.parsedUrl.hostname;
info.options.port = info.parsedUrl.port ? parseInt(info.parsedUrl.port) : defaultPort;
info.options.path = (info.parsedUrl.pathname || '') + (info.parsedUrl.search || '');
info.options.method = method;
info.options.headers = headers || {};
info.options.headers["User-Agent"] = this.userAgent;
let useProxy = proxyUrl && proxyUrl.hostname;
if (useProxy) {
var agentOptions: tunnel.TunnelOptions = {
maxSockets: http.globalAgent.maxSockets,
proxy: {
// TODO: support proxy-authorization
//proxyAuth: "user:password",
host: proxyUrl.hostname,
port: proxyUrl.port
}
};
var tunnelAgent: Function;
var overHttps = proxyUrl.protocol === 'https:';
if (usingSsl) {
tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp;
} else {
tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp;
}
info.options.agent = tunnelAgent(agentOptions);
}
// gives handlers an opportunity to participate
if (this.handlers) {
this.handlers.forEach((handler) => {
handler.prepareRequest(info.options);
});
}
return info;
}
}
//
// Legacy callback client. Will delete.
//
export class HttpCallbackClient {
userAgent: string;
handlers: ifm.IRequestHandler[];
socketTimeout: number;
isSsl: boolean;
constructor(userAgent: string, handlers?: ifm.IRequestHandler[], socketTimeout?: number) {
this.userAgent = userAgent;
this.handlers = handlers;
this.socketTimeout = socketTimeout ? socketTimeout : 3 * 60000;
}
get(verb: string, requestUrl: string, headers: ifm.IHeaders, onResult: (err: any, res: http.IncomingMessage, contents: string) => void): void {
var options = this._getOptions(verb, requestUrl, headers);
this.request(options.protocol, options.options, null, onResult);
}
// POST, PATCH, PUT
send(verb: string, requestUrl: string, data: string, headers: ifm.IHeaders, onResult: (err: any, res: http.IncomingMessage, contents: string) => void): void {
var options = this._getOptions(verb, requestUrl, headers);
this.request(options.protocol, options.options, data, onResult);
}
sendStream(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, headers: ifm.IHeaders, onResult: (err: any, res: http.IncomingMessage, contents: string) => void): void {
var options = this._getOptions(verb, requestUrl, headers);
var req = options.protocol.request(options.options, (res) => {
let output: string = '';
res.on('data', function (chunk) {
output += chunk;
});
res.on('end', function () {
// res has statusCode and headers
onResult(null, res, output);
});
});
req.on('error', function (err) {
// err has statusCode property
// res should have headers
onResult(err, null, null);
});
stream.on('close', function () {
req.end();
});
stream.pipe(req);
}
getStream(requestUrl: string, accept: string, onResult: (err: any, statusCode: number, res: NodeJS.ReadableStream) => void): void {
let headers = {};
headers['ACCEPT'] = accept;
var options = this._getOptions('GET', requestUrl, headers);
var req = options.protocol.request(options.options, (res: http.IncomingMessage) => {
onResult(null, res.statusCode, res);
});
req.on('error', (err) => {
onResult(err, err.statusCode, null);
});
req.end();
}
/**
* Makes an http request delegating authentication to handlers.
* returns http result as contents buffer
* All other methods such as get, post, and patch ultimately call this.
*/
request(protocol: any, options: any, data: string, onResult: (err: any, res: http.IncomingMessage, contents: string) => void): void {
// Set up a callback to pass off 401s to an authentication handler that can deal with it
var callback = (err: any, res: http.ClientResponse, contents: string) => {
var authHandler;
if (this.handlers) {
this.handlers.some(function (handler, index, handlers) {
// Find the first one that can handle the auth based on the response
if (handler.canHandleAuthentication(res)) {
authHandler = handler;
return true;
}
return false;
});
}
if (authHandler !== undefined) {
authHandler.handleAuthentication(this, protocol, options, data, onResult);
} else {
// No auth handler found, call onResult normally
onResult(err, res, contents);
}
};
this.requestRaw(protocol, options, data, callback);
}
/**
* Makes a raw http request.
* All other methods such as get, post, patch, and request ultimately call this.
*/
requestRaw(protocol: any, options: any, data: string, onResult: (err: any, res: http.IncomingMessage, contents: string) => void): void {
var socket;
if (data) {
options.headers["Content-Length"] = Buffer.byteLength(data, 'utf8');
}
var callbackCalled: boolean = false;
var handleResult = (err: any, res: http.IncomingMessage, contents: string) => {
if (!callbackCalled) {
callbackCalled = true;
onResult(err, res, contents);
}
};
var req = protocol.request(options, (res) => {
let output: string = '';
res.setEncoding('utf8');
res.on('data', (chunk: string) => {
output += chunk;
});
res.on('end', () => {
// res has statusCode and headers
handleResult(null, res, output);
});
});
req.on('socket', (sock) => {
socket = sock;
});
// If we ever get disconnected, we want the socket to timeout eventually
req.setTimeout(this.socketTimeout, function() {
if (socket) {
socket.end();
}
handleResult(new Error('Request timeout: ' + options.path), null, null);
});
req.on('error', function (err) {
// err has statusCode property
// res should have headers
handleResult(err, null, null);
});
if (data) {
req.write(data, 'utf8');
}
req.end();
}
private _getOptions(method: string, requestUrl: string, headers: any): any {
var parsedUrl: url.Url = url.parse(requestUrl);
var usingSsl = parsedUrl.protocol === 'https:';
var prot: any = usingSsl ? https : http;
var defaultPort = usingSsl ? 443 : 80;
this.isSsl = usingSsl;
var proxyUrl: url.Url;
if (process.env.HTTPS_PROXY && usingSsl) {
proxyUrl = url.parse(process.env.HTTPS_PROXY);
} else if (process.env.HTTP_PROXY) {
proxyUrl = url.parse(process.env.HTTP_PROXY);
}
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var options: any = {
host: parsedUrl.hostname,
port: parsedUrl.port || defaultPort,
path: (parsedUrl.pathname || '') + (parsedUrl.search || ''),
method: method,
headers: headers || {}
};
options.headers["User-Agent"] = this.userAgent;
var useProxy = proxyUrl && proxyUrl.hostname;
if (useProxy) {
var agentOptions: tunnel.TunnelOptions = {
maxSockets: http.globalAgent.maxSockets,
proxy: {
// TODO: support proxy-authorization
//proxyAuth: "user:password",
host: proxyUrl.hostname,
port: proxyUrl.port
}
};
var tunnelAgent: Function;
var overHttps = proxyUrl.protocol === 'https:';
if (usingSsl) {
tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp;
} else {
tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp;
}
options.agent = tunnelAgent(agentOptions);
}
if (this.handlers) {
this.handlers.forEach((handler) => {
handler.prepareRequest(options);
});
}
return {
protocol: prot,
options: options,
};
}
}

Просмотреть файл

@ -1,314 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
import fs = require("fs");
import http = require("http");
import httpm = require('./HttpClient');
import ifm = require("./interfaces/common/VsoBaseInterfaces");
import serm = require('./Serialization');
export interface IRestClientResponse {
statusCode: number,
result: any
}
export class RestClient {
client: httpm.HttpClient;
versionParam: string;
constructor(userAgent: string, handlers?: ifm.IRequestHandler[], socketTimeout?: number, versionParam?: string) {
// TODO: should we really do this?
this.versionParam = versionParam || 'api-version';
this.client = new httpm.HttpClient(userAgent, handlers, socketTimeout);
}
public async get(requestUrl: string,
apiVersion: string,
additionalHeaders?: ifm.IHeaders): Promise<IRestClientResponse> {
var headers = additionalHeaders || {};
headers["Accept"] = this.createAcceptHeader('application/json', apiVersion);
let res: httpm.HttpClientResponse = await this.client.get(requestUrl, headers);
return this._processResponse(res);
}
public async del(requestUrl: string,
apiVersion: string,
additionalHeaders?: ifm.IHeaders): Promise<IRestClientResponse> {
var headers = additionalHeaders || {};
headers["Accept"] = this.createAcceptHeader('application/json', apiVersion);
let res: httpm.HttpClientResponse = await this.client.del(requestUrl, headers);
return this._processResponse(res);
}
public async create(requestUrl: string,
apiVersion: string,
resources: any,
additionalHeaders?: ifm.IHeaders): Promise<IRestClientResponse> {
var headers = additionalHeaders || {};
headers["Accept"] = this.createAcceptHeader('application/json', apiVersion);
headers["Content-Type"] = headers["Content-Type"] || 'application/json; charset=utf-8';
let data: string = JSON.stringify(resources, null, 2);
let res: httpm.HttpClientResponse = await this.client.post(requestUrl, data, headers);
return this._processResponse(res);
}
public async update(requestUrl: string,
apiVersion: string,
resources: any,
additionalHeaders?: ifm.IHeaders): Promise<IRestClientResponse> {
var headers = additionalHeaders || {};
headers["Accept"] = this.createAcceptHeader('application/json', apiVersion);
headers["Content-Type"] = headers["Content-Type"] || 'application/json; charset=utf-8';
let data: string = JSON.stringify(resources, null, 2);
let res: httpm.HttpClientResponse = await this.client.patch(requestUrl, data, headers);
return this._processResponse(res);
}
public async replace(requestUrl: string,
apiVersion: string,
resources: any,
additionalHeaders?: ifm.IHeaders): Promise<IRestClientResponse> {
var headers = additionalHeaders || {};
headers["Accept"] = this.createAcceptHeader('application/json', apiVersion);
headers["Content-Type"] = headers["Content-Type"] || 'application/json; charset=utf-8';
let data: string = JSON.stringify(resources, null, 2);
let res: httpm.HttpClientResponse = await this.client.put(requestUrl, data, headers);
return this._processResponse(res);
}
public async uploadStream(verb: string, requestUrl: string, apiVersion: string, stream: NodeJS.ReadableStream, additionalHeaders: ifm.IHeaders): Promise<IRestClientResponse> {
var headers = additionalHeaders || {};
headers["Accept"] = this.createAcceptHeader('application/json', apiVersion);
let res: httpm.HttpClientResponse = await this.client.sendStream(verb, requestUrl, stream, headers);
return this._processResponse(res);
}
public createAcceptHeader(type: string, apiVersion?: string): string {
return type + (apiVersion ? (';' + this.versionParam + '=' + apiVersion) : '');
}
private async _processResponse(res: httpm.HttpClientResponse): Promise<IRestClientResponse> {
return new Promise<IRestClientResponse>(async(resolve, reject) => {
let rres: IRestClientResponse = <IRestClientResponse>{};
let statusCode: number = res.message.statusCode;
rres.statusCode = statusCode;
// not found leads to null obj returned
if (statusCode == httpm.HttpCodes.NotFound) {
resolve(rres);
}
let obj: any;
// get the result from the body
try {
let contents: string = await res.readBody();
if (contents && contents.length > 0) {
obj = JSON.parse(contents);
rres.result = obj;
}
}
catch (err) {
reject(new Error('Invalid Resource'));
}
if (statusCode > 299) {
let msg: string;
// if exception/error in body, attempt to get better error
if (obj && obj.message) {
msg = obj.message;
} else {
msg = "Failed request: (" + statusCode + ") " + res.message.url;
}
reject(new Error(msg));
} else {
resolve(rres);
}
});
}
}
export class RestCallbackClient {
baseUrl: string;
basePath: string;
httpClient: httpm.HttpCallbackClient;
versionParam: string;
constructor(httpClient: httpm.HttpCallbackClient, versionParam?: string) {
versionParam = versionParam || 'api-version';
this.httpClient = httpClient;
this.versionParam = versionParam;
}
get(url: string, apiVersion: string, customHeaders: ifm.IHeaders, onResult: (err: any, statusCode: number, obj: any) => void): void {
this._getJson('GET', url, apiVersion, customHeaders, onResult);
}
del(url: string, apiVersion: string, customHeaders: ifm.IHeaders, onResult: (err: any, statusCode: number, obj: any) => void): void {
this._getJson('DELETE', url, apiVersion, customHeaders, onResult);
}
create(url: string, apiVersion: string, resources: any, customHeaders: ifm.IHeaders, onResult: (err: any, statusCode: number, obj: any) => void, serializationData?: serm.SerializationData): void {
var headers = customHeaders || {};
headers["Accept"] = this.createAcceptHeader('application/json', apiVersion);
headers["Content-Type"] = headers["Content-Type"] || 'application/json; charset=utf-8';
this._sendJson('POST', url, apiVersion, resources, customHeaders, onResult);
}
update(url: string, apiVersion: string, resources: any, customHeaders: ifm.IHeaders, onResult: (err: any, statusCode: number, obj: any) => void, serializationData?: serm.SerializationData): void {
this._sendJson('PATCH', url, apiVersion, resources, customHeaders, onResult);
}
options(url: string, onResult: (err: any, statusCode: number, obj: any) => void): void {
this._getJson('OPTIONS', url, "", null, onResult);
}
uploadFile(verb: string, url: string, apiVersion: string, filePath: string, customHeaders: ifm.IHeaders, onResult: (err: any, statusCode: number, obj: any) => void, serializationData?: serm.SerializationData): void {
fs.stat(filePath, (err, stats) => {
if (err) {
onResult(err, 400, null);
return;
}
var headers = customHeaders || {};
headers["Content-Length"] = stats.size;
var contentStream: NodeJS.ReadableStream = fs.createReadStream(filePath);
this.uploadStream(verb, url, apiVersion, contentStream, headers, onResult, serializationData);
});
}
uploadStream(verb: string, url: string, apiVersion: string, contentStream: NodeJS.ReadableStream, customHeaders: ifm.IHeaders, onResult: (err: any, statusCode: number, obj: any) => void, serializationData?: serm.SerializationData): void {
var headers = customHeaders || {};
headers["Accept"] = this.createAcceptHeader('application/json', apiVersion);
this.httpClient.sendStream(verb, url, contentStream, headers, (err: any, res: ifm.IHttpResponse, contents: string) => {
if (err) {
onResult(err, err.statusCode, contents);
return;
}
_processResponse(url, res, contents, onResult);
});
}
replace(url: string, apiVersion: string, resources: any, customHeaders: ifm.IHeaders, onResult: (err: any, statusCode: number, obj: any) => void): void {
this._sendJson('PUT', url, apiVersion, resources, customHeaders, onResult);
}
_getJson(verb: string, url: string, apiVersion: string, customHeaders: ifm.IHeaders, onResult: (err: any, statusCode: number, obj: any) => void): void {
var headers = {};
headers["Accept"] = this.createAcceptHeader('application/json', apiVersion);
this.httpClient.get(verb, url, headers, (err: any, res: ifm.IHttpResponse, contents: string) => {
if (err) {
onResult(err, err.statusCode, null);
return;
}
_processResponse(url, res, contents, onResult);
});
}
_sendJson(verb: string, url: string, apiVersion: string, resources: any, customHeaders: ifm.IHeaders, onResult: (err: any, statusCode: number, obj: any) => void): void {
if (!resources) {
throw new Error('invalid resource');
}
var headers = customHeaders || {};
headers["Accept"] = this.createAcceptHeader('application/json', apiVersion);
headers["Content-Type"] = headers["Content-Type"] || 'application/json; charset=utf-8';
let data: string;
data = JSON.stringify(resources, null, 2);
this.httpClient.send(verb, url, data, headers, (err: any, res: ifm.IHttpResponse, contents: string) => {
if (err) {
onResult(err, err.statusCode, null);
return;
}
_processResponse(url, res, contents, onResult);
});
}
public createAcceptHeader(type: string, apiVersion?: string): string {
return type + (apiVersion ? (';' + this.versionParam + '=' + apiVersion) : '');
}
}
var httpCodes = {
300: "Multiple Choices",
301: "Moved Permanantly",
302: "Resource Moved",
304: "Not Modified",
305: "Use Proxy",
306: "Switch Proxy",
307: "Temporary Redirect",
308: "Permanent Redirect",
400: "Bad Request",
401: "Unauthorized",
402: "Payment Required",
403: "Forbidden",
404: "Not Found",
405: "Method Not Allowed",
406: "Not Acceptable",
407: "Proxy Authentication Required",
408: "Request Timeout",
409: "Conflict",
410: "Gone",
500: "Internal Server Error",
501: "Not Implemented",
502: "Bad Gateway",
503: "Service Unavailable",
504: "Gateway Timeout"
}
function _processResponse(url: string,
res: ifm.IHttpResponse,
contents: string,
onResult: (err: any, statusCode: number, obj: any) => void) {
let jsonObj: any;
if (contents && contents.length > 0) {
try {
jsonObj = JSON.parse(contents);
} catch (e) {
onResult(new Error('Invalid Resource'), res.statusCode, null);
return;
}
}
if (res.statusCode > 299) {
// default error message
var msg = httpCodes[res.statusCode] ? "Failed Request: " + httpCodes[res.statusCode] : "Failed Request";
msg += '(' + res.statusCode + ') - ';
// if exception/error in body, attempt to get better error
if (jsonObj && jsonObj.message) {
msg += jsonObj.message;
} else {
msg += url;
}
onResult(new Error(msg), res.statusCode, null);
} else {
onResult(null, res.statusCode, jsonObj);
}
};

Просмотреть файл

@ -5,6 +5,7 @@ import url = require('url');
import vsom = require('./VsoClient');
import TaskAgentInterfaces = require("./interfaces/TaskAgentInterfaces");
import VsoBaseInterfaces = require('./interfaces/common/VsoBaseInterfaces');
import * as restm from 'typed-rest-client/RestClient';
export interface ITaskAgentApi extends taskagentbasem.ITaskAgentApiBase {
uploadTaskDefinition(customHeaders: VsoBaseInterfaces.IHeaders, contentStream: NodeJS.ReadableStream, taskId: string, overwrite: boolean) : Promise<void>;
@ -167,43 +168,7 @@ export class TaskAgentApi extends taskagentbasem.TaskAgentApiBase implements ITa
* @param {boolean} overwrite
* @param onResult callback function
*/
public uploadTaskDefinition(
customHeaders: VsoBaseInterfaces.IHeaders,
contentStream: NodeJS.ReadableStream,
taskId: string,
overwrite: boolean
): Promise<void> {
let promise = this.vsoClient.beginGetLocation("distributedtask", "60aac929-f0cd-4bc8-9ce4-6b30e8f1b1bd")
.then((location: ifm.ApiResourceLocation) => {
if (location) {
// the resource exists at the url we were given. go!
return this._uploadTaskDefinition(customHeaders, contentStream, taskId, overwrite);
}
else {
// this is the case when the server doesn't support collection-level task definitions
var fallbackClient = this._getFallbackClient(this.baseUrl);
if (!fallbackClient) {
// couldn't convert
throw new Error("Failed to find api location for area: distributedtask id: 60aac929-f0cd-4bc8-9ce4-6b30e8f1b1bd");
}
else {
// use the fallback client
return fallbackClient._uploadTaskDefinition(customHeaders, contentStream, taskId, overwrite);
}
}
});
return <Promise<void>>(<any>promise);
}
/**
* @param {NodeJS.ReadableStream} contentStream
* @param {string} taskId
* @param {boolean} overwrite
* @param onResult callback function
*/
private _uploadTaskDefinition(
public async uploadTaskDefinition(
customHeaders: VsoBaseInterfaces.IHeaders,
contentStream: NodeJS.ReadableStream,
taskId: string,
@ -217,27 +182,34 @@ export class TaskAgentApi extends taskagentbasem.TaskAgentApiBase implements ITa
let queryValues: any = {
overwrite: overwrite,
};
customHeaders = customHeaders || {};
customHeaders["Content-Type"] = "application/octet-stream";
return new Promise<void>((resolve, reject) => {
this.vsoClient.getVersioningData("3.0-preview.1", "distributedtask", "60aac929-f0cd-4bc8-9ce4-6b30e8f1b1bd", routeValues, queryValues)
.then((versioningData: vsom.ClientVersioningData) => {
var url: string = versioningData.requestUrl;
var apiVersion: string = versioningData.apiVersion;
var serializationData = { responseIsCollection: false };
return new Promise<void>(async (resolve, reject) => {
let routeValues: any = {
};
this.restCallbackClient.uploadStream('PUT', url, apiVersion, contentStream, customHeaders, (err: any, statusCode: number, obj: any) => {
if (err) {
err.statusCode = statusCode;
reject(err);
}
else {
resolve(null);
}
}, serializationData);
});
customHeaders = customHeaders || {};
customHeaders["Content-Type"] = "application/octet-stream";
try {
let verData: vsom.ClientVersioningData = await this.vsoClient.getVersioningData(
"3.0-preview.1",
"distributedtask",
"60aac929-f0cd-4bc8-9ce4-6b30e8f1b1bd", routeValues, queryValues);
let url: string = verData.requestUrl;
let options: restm.IRequestOptions = this.createRequestOptions('application/json',
verData.apiVersion);
options.additionalHeaders = customHeaders;
let res: restm.IRestResponse<void>;
res = await this.rest.uploadStream<void>("POST", url, contentStream, options);
resolve(res.result);
}
catch (err) {
reject(err);
}
});
}

Просмотреть файл

@ -6,8 +6,8 @@
import url = require("url");
import path = require("path");
/// Import base rest class ///
import restm = require("./RestClient");
import httpm = require("./HttpClient");
import * as restm from 'typed-rest-client/RestClient';
import * as httpm from 'typed-rest-client/HttpClient';
import ifm = require("./interfaces/common/VsoBaseInterfaces");
interface VssApiResourceLocationLookup {
@ -39,17 +39,16 @@ export class InvalidApiResourceVersionError implements Error {
* Base class that should be used (derived from) to make requests to VSS REST apis
*/
export class VsoClient {
private static APIS_RELATIVE_PATH = "_apis";
private static PREVIEW_INDICATOR = "-preview.";
private _locationsByAreaPromises: { [areaName: string]: Promise<VssApiResourceLocationLookup>; };
private _initializationPromise: Promise<any>;
restClient: restm.RestCallbackClient;
restClient: restm.RestClient;
baseUrl: string;
basePath: string;
constructor(baseUrl: string, restClient: restm.RestCallbackClient) {
constructor(baseUrl: string, restClient: restm.RestClient) {
this.baseUrl = baseUrl;
this.basePath = url.parse(baseUrl).pathname;
this.restClient = restClient;
@ -170,25 +169,21 @@ export class VsoClient {
areaLocationsPromise = new Promise<VssApiResourceLocationLookup>((resolve, reject) => {
let requestUrl = this.resolveUrl(VsoClient.APIS_RELATIVE_PATH + "/" + area);
this.restClient.options<any>(requestUrl)
.then((res:restm.IRestResponse<any>) => {
let locationsLookup: VssApiResourceLocationLookup = {};
let resourceLocations: ifm.ApiResourceLocation[] = res.result.value;
this._issueOptionsRequest(requestUrl, (err: any, statusCode: number, locationsResult: any) => {
if (err) {
err.statusCode = statusCode;
reject(err);
let i;
for (i = 0; i < resourceLocations.length; i++) {
let resourceLocation = resourceLocations[i];
locationsLookup[resourceLocation.id.toLowerCase()] = resourceLocation;
}
else {
let locationsLookup: VssApiResourceLocationLookup = {};
let resourceLocations: ifm.ApiResourceLocation[] = locationsResult.value;
let i;
for (i = 0; i < locationsResult.count; i++) {
let resourceLocation = resourceLocations[i];
locationsLookup[resourceLocation.id.toLowerCase()] = resourceLocation;
}
resolve(locationsLookup);
}
resolve(locationsLookup);
})
.catch((err) => {
reject(err);
});
});
@ -202,13 +197,6 @@ export class VsoClient {
return url.resolve(this.baseUrl, path.join(this.basePath, relativeUrl));
}
/**
* Issues an OPTIONS request to get location objects from a location id
*/
public _issueOptionsRequest(requestUrl: string, onResult: (err: any, statusCode: number, locationsResult: any) => void): void {
return this.restClient.options(requestUrl, onResult);
}
private getSerializedObject(object: any): string {
let value:string = "";
let first:boolean = true;

Просмотреть файл

@ -1,7 +1,7 @@
{
"name": "vso-node-api",
"description": "Node client for Visual Studio Online/TFS REST APIs",
"version": "6.1.2-preview",
"version": "6.2.0-preview",
"main": "./WebApi.js",
"typings": "./WebApi.d.ts",
"scripts": {
@ -23,7 +23,7 @@
"dependencies": {
"q": "^1.0.1",
"tunnel": "0.0.4",
"typed-rest-client": "^0.9.0",
"typed-rest-client": "^0.10.0",
"underscore": "^1.8.3"
},
"devDependencies": {

Просмотреть файл

@ -1,122 +0,0 @@
import * as cm from './common';
import * as httpm from 'vso-node-api/HttpClient';
import * as restm from 'vso-node-api/RestClient';
import * as fs from 'fs';
import * as path from 'path';
let sampleFilePath: string = path.join(process.cwd(), 'httpClientStreamSample.txt');
let httpc: httpm.HttpClient = new httpm.HttpClient('vsts-node-api');
let restc: restm.RestClient = new restm.RestClient('vsts-http');
export async function run() {
let restRes: restm.IRestClientResponse;
try {
//--------------------------------------
// HttpClient
//--------------------------------------
//
// Http get. Can await get response.
// The response offers a message (IncomingMessage) and
// an awaitable readBody() which reads the stream to end
//
cm.heading('get request output body');
let res: httpm.HttpClientResponse = await httpc.get('https://httpbin.org/get');
let status: number = res.message.statusCode;
let body: string = await res.readBody();
outputHttpBinResponse(body, status);
//
// Http get request reading body to end in a single line
//
cm.heading('get request in a single line');
body = await (await httpc.get('https://httpbin.org/get')).readBody();
outputHttpBinResponse(body);
//
// Http get piping to another stream
// response message is an IncomingMessage which is a stream
//
cm.heading('get request and pipe stream');
let file: NodeJS.WritableStream = fs.createWriteStream(sampleFilePath);
(await httpc.get('https://httpbin.org/get')).message.pipe(file);
body = fs.readFileSync(sampleFilePath).toString();
outputHttpBinResponse(body);
// DELETE request
cm.heading('delete request');
res = await httpc.del('https://httpbin.org/delete');
body = await res.readBody();
outputHttpBinResponse(body, status);
let b: string = 'Hello World!';
// POST request
cm.heading('post request');
res = await httpc.post('https://httpbin.org/post', b);
body = await res.readBody();
outputHttpBinResponse(body, status);
// PATCH request
cm.heading('patch request');
res = await httpc.patch('https://httpbin.org/patch', b);
body = await res.readBody();
outputHttpBinResponse(body, status);
// GET not found
cm.heading('get not found');
res = await httpc.get('https://httpbin.org/status/404');
body = await res.readBody();
outputHttpBinResponse(body, status);
//--------------------------------------
// RestClient
//--------------------------------------
cm.heading('get rest obj');
restRes = await restc.get('https://httpbin.org/get', '1.0-preview');
outputRestResponse(restRes);
let obj: any = { message: "Hello World!" };
cm.heading('create rest obj');
restRes = await restc.create('https://httpbin.org/post', '1.0-preview', obj);
outputRestResponse(restRes);
cm.heading('update rest obj');
restRes = await restc.update('https://httpbin.org/patch', '1.0-preview', obj);
outputRestResponse(restRes);
}
catch (err) {
console.error('Failed: ' + err.message);
}
}
//
// Utility functions
//
async function outputHttpBinResponse(body: string, status?: number) {
if (status) {
console.log('status', status);
}
if (body) {
let obj = JSON.parse(body.toString());
console.log('response from ' + obj.url);
if (obj.data) {
console.log('data:', obj.data);
}
}
}
function outputRestResponse(res: restm.IRestClientResponse) {
console.log('statusCode:' + res.statusCode);
if (res && res.result) {
console.log('response from ' + res.result.url);
if (res.result.data) {
console.log('data:', res.result.data);
}
}
}

Просмотреть файл

@ -1,5 +1,4 @@
[
"build",
"http",
"task"
]

Просмотреть файл

@ -6,7 +6,6 @@
},
"files": [
"run.ts",
"http.ts",
"build.ts",
"task.ts"
]