diff --git a/src/js/ConfigurationApi.ts b/src/js/ConfigurationApi.ts index b8b419d0..7902aeca 100644 --- a/src/js/ConfigurationApi.ts +++ b/src/js/ConfigurationApi.ts @@ -1,6 +1,8 @@ import { XHRFactory } from "./XHRFactory"; import { Promise, PromiseConstructor, ConfigurePromise } from "./Promise"; import { IXHROptions, IXHRApi } from "./Interfaces"; +import { FetchOptions } from 'fetch'; +import { XHRDefault } from './XHRDefault'; export class ConfigurationApi { @@ -8,7 +10,11 @@ export class ConfigurationApi { XHRFactory.xhrHelper = xhrApi; } + static SetXHROptions(fetchOptions: FetchOptions) { + XHRDefault.defaultOptions = fetchOptions; + } + static ConfigurePromise(promise: PromiseConstructor) { ConfigurePromise(promise); } -} \ No newline at end of file +} diff --git a/src/js/ExchangeWebService.ts b/src/js/ExchangeWebService.ts index bf777533..b59ea1a7 100644 --- a/src/js/ExchangeWebService.ts +++ b/src/js/ExchangeWebService.ts @@ -3,6 +3,36 @@ * BootStrap code. to initializes some class to avoid circular reference. */ +/** polyfill */ +if (typeof Object.assign != 'function') { + // Must be writable: true, enumerable: false, configurable: true + Object.defineProperty(Object, "assign", { + value: function assign(target, varArgs) { // .length of function is 2 + 'use strict'; + if (target == null) { // TypeError if undefined or null + throw new TypeError('Cannot convert undefined or null to object'); + } + + var to = Object(target); + + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + + if (nextSource != null) { // Skip over if undefined or null + for (var nextKey in nextSource) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; + }, + writable: true, + configurable: true + }); +} /** Promise type setup */ export { Promise } from "./Promise"; @@ -11,6 +41,7 @@ export { Promise } from "./Promise"; export { IXHRApi, IXHROptions, IXHRProgress, } from "./Interfaces"; export { ConfigurationApi } from "./ConfigurationApi"; export { XHRFactory } from "./XHRFactory"; +export { XHRDefault } from "./XHRDefault"; /**Schema Bootstrapping */ import { Schemas } from "./Core/ServiceObjects/Schemas/Schemas"; diff --git a/src/js/XHRDefaults.ts b/src/js/XHRDefault.ts similarity index 56% rename from src/js/XHRDefaults.ts rename to src/js/XHRDefault.ts index 313acad6..7b3dde99 100644 --- a/src/js/XHRDefaults.ts +++ b/src/js/XHRDefault.ts @@ -1,16 +1,20 @@ -import { FetchStream, fetchUrl } from 'fetch'; +import { FetchStream, fetchUrl, FetchOptions } from 'fetch'; import { Promise } from "./Promise"; import { IXHROptions, IXHRApi, IXHRProgress } from "./Interfaces"; -/** @internal */ -export class XHRDefaults implements IXHRApi { +/** + * Default implementation of XHRApi using fetch + */ +export class XHRDefault implements IXHRApi { static FetchStream: typeof FetchStream = FetchStream; static fetchUrl: typeof fetchUrl = null; + static defaultOptions: FetchOptions = {}; + fetchOptions: FetchOptions = {}; private stream: FetchStream; xhr(xhroptions: IXHROptions, progressDelegate?: (progressData: IXHRProgress) => void): Promise { - if (XHRDefaults.fetchUrl === null) { + if (XHRDefault.fetchUrl === null) { throw new Error("xhrApi - stub method, must be bootstrapped"); } //setup xhr for github.com/andris9/fetch options @@ -26,7 +30,7 @@ export class XHRDefaults implements IXHRApi { // delete xhroptions["type"]; return new Promise((resolve, reject) => { - XHRDefaults.fetchUrl(xhroptions.url, options, (error, meta, body) => { + XHRDefault.fetchUrl(xhroptions.url, this.getOptions(options), (error, meta, body) => { if (error) { if (typeof (error).status === 'undefined') { (error).status = 0; @@ -56,7 +60,7 @@ export class XHRDefaults implements IXHRApi { } xhrStream(xhroptions: IXHROptions, progressDelegate: (progressData: IXHRProgress) => void): Promise { - if (XHRDefaults.FetchStream === null) { + if (XHRDefault.FetchStream === null) { throw new Error("xhrApi - stub method, must be bootstrapped"); } @@ -68,7 +72,7 @@ export class XHRDefaults implements IXHRApi { } return new Promise((resolve, reject) => { - this.stream = new XHRDefaults.FetchStream(xhroptions.url, options); + this.stream = new XHRDefault.FetchStream(xhroptions.url, this.getOptions(options)); this.stream.on("data", (chunk) => { //console.log(chunk.toString()); @@ -105,14 +109,20 @@ export class XHRDefaults implements IXHRApi { return "default"; } - constructor() { + constructor(fetchOptions: FetchOptions = {}) { + this.fetchOptions = fetchOptions; try { let fetch = require("fetch"); - XHRDefaults.FetchStream = fetch.FetchStream; - XHRDefaults.fetchUrl = fetch.fetchUrl; + XHRDefault.FetchStream = fetch.FetchStream; + XHRDefault.fetchUrl = fetch.fetchUrl; } catch (e) { } } + + private getOptions(opts: FetchOptions) { + let headers = Object.assign({}, (XHRDefault.defaultOptions || {}).headers, (this.fetchOptions || {}).headers, (opts || {}).headers) + return Object.assign({}, XHRDefault.defaultOptions, this.fetchOptions, opts, { headers }); + } } /** @internal */ @@ -144,4 +154,47 @@ function setupXhrResponse(xhrResponse: XMLHttpRequest): XMLHttpRequest { } return xhrResponse; -} \ No newline at end of file +} + +export interface xFetchOptions { + /** how many redirects allowed, defaults to 10 */ + maxRedirects: number; + /** set to true if redirects are not allowed, defaults to false */ + disableRedirects: boolean; + /** optional header fields, in the form of {'Header-Field':'value'} */ + headers: { [key: string]: (any) }; + /** maximum allowd length for the file, the remainder is cut off. Defaults to Infinity */ + maxResponseLength: number; + /** defaults to GET */ + method: string; + /** request body */ + payload: string; + /** set to false, to disable content gzipping, needed for Node v0.5.9 which has buggy zlib */ + disableGzip: boolean; + /** an array of cookie definitions in the form of ['name=val'] */ + cookies: any; + /** for sharing cookies between requests, see below */ + cookieJar: any; + /** valid for fetchUrl */ + outputEncoding: string; + /** valid for fetchUrl, set to true to disable automatic charset decoding to utf-8 */ + disableDecoding: boolean; + /** valid for fetchUrl, set input encoding */ + overrideCharset: string; + /** use high performance asyncronous DNS resolution based on c-ares instead of a thread pool calling getaddrinfo(3) */ + asyncDnsLoookup: boolean; + /** set a timeout in ms */ + timeout: number; + /** pass-through http.request agent parameter for https */ + agentHttps: any; + /** pass-through http.request agent parameter for http */ + agentHttp: any; + /** pass-through http.request agent parameter as fallback, if agentHttps or agentHttp are not specified */ + agent: any; + /** whether to reject self-signed certificates (true, default behavior), or ignore and allow them (false) */ + rejectUnauthorized: boolean; + /** is the username for Basic auth */ + user: string; + /** is the password for Basic auth */ + pass: string; +} diff --git a/src/js/XHRFactory.ts b/src/js/XHRFactory.ts index 36e94519..f7a1f7a1 100644 --- a/src/js/XHRFactory.ts +++ b/src/js/XHRFactory.ts @@ -1,18 +1,19 @@ import { Promise } from "./Promise"; import { IXHROptions, IXHRApi } from "./Interfaces"; -import { XHRDefaults } from "./XHRDefaults" +import { XHRDefault } from "./XHRDefault" export class XHRFactory { static xhrHelper: IXHRApi; static get XHRApi() { if (typeof this.xhrHelper === 'undefined' || this.xhrHelper === null) { - this.xhrHelper = new XHRDefaults(); + this.xhrHelper = new XHRDefault(); } return this.xhrHelper; } public static newXHRApi() { - return new XHRDefaults(); + console.warn("depricated, import and use \"new XHRDefault(options?)\" instead") + return new XHRDefault(); } } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index d168a9c9..a199030c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,11 @@ "allowUnreachableCode": true, "pretty": false, "inlineSourceMap": false, - "stripInternal": true + "stripInternal": true, + "lib": [ + "es2015", + "dom" + ] }, "compileOnSave": false, "exclude": [ @@ -22,5 +26,5 @@ "src/js/System.d.ts", "typings/ExchangeWebService.d.ts", "src/js/Microsoft.Exchange.WebServices.d__.ts" - ] -} + ] +} \ No newline at end of file diff --git a/typings/fetch.d.ts b/typings/fetch.d.ts index 2aea8eb9..e616557f 100644 --- a/typings/fetch.d.ts +++ b/typings/fetch.d.ts @@ -7,33 +7,55 @@ declare module 'fetch' { cookieJar: any } - export interface fetchOptions { + export interface FetchOptions { + /** how many redirects allowed, defaults to 10 */ maxRedirects?: number; + /** set to true if redirects are not allowed, defaults to false */ disableRedirects?: boolean; - headers?: any; + /** optional header fields, in the form of {'Header-Field':'value'} */ + headers?: { [key: string]: (any) }; + /** maximum allowd length for the file, the remainder is cut off. Defaults to Infinity */ maxResponseLength?: number | "infinity"; + /** defaults to GET */ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONS'; + /** request body */ payload?: string; + /** set to false, to disable content gzipping, needed for Node v0.5.9 which has buggy zlib */ disableGzip?: boolean; + /** an array of cookie definitions in the form of ['name=val'] */ cookies?: any; + /** for sharing cookies between requests, see below */ cookieJar?: any; + /** valid for fetchUrl */ outputEncoding?: any; + /** valid for fetchUrl, set to true to disable automatic charset decoding to utf-8 */ disableDecoding?: any; + /** valid for fetchUrl, set input encoding */ overrideCharset?: any; + /** use high performance asyncronous DNS resolution based on c-ares instead of a thread pool calling getaddrinfo(3) */ asyncDnsLoookup?: any; + /** set a timeout in ms */ timeout?: number; + /** pass-through http.request agent parameter for https */ agentHttps?: any; + /** pass-through http.request agent parameter for http */ agentHttp?: any; + /** pass-through http.request agent parameter as fallback, if agentHttps or agentHttp are not specified */ agent?: any; + /** whether to reject self-signed certificates (true, default behavior), or ignore and allow them (false) */ rejectUnauthorized?: boolean; + /** is the username for Basic auth */ + user?: string; + /** is the password for Basic auth */ + pass?: string; } export class FetchStream { - constructor(url: string, options: fetchOptions); + constructor(url: string, options: FetchOptions); destroy(): void; on: (event: 'data' | 'meta' | 'end' | 'error', callback?: (data?: string) => void) => void; } export function fetchUrl(url: string, callback: (error: Error, meta: Meta, body: string) => void): void; - export function fetchUrl(url: string, options: fetchOptions, callback: (error: Error, meta: Meta, body: string) => void): void; + export function fetchUrl(url: string, options: FetchOptions, callback: (error: Error, meta: Meta, body: string) => void): void; } \ No newline at end of file