@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
346 lines (344 loc) • 12.5 kB
JavaScript
import { Observable, throwError } from 'rxjs';
import { ajax } from 'rxjs/ajax';
import { catchError, mergeMap, switchMap } from 'rxjs/operators';
import { Cookie } from './cookie';
import { ErrorExtended } from './error-extended';
import { headerConstants } from './http-constants';
/**
* Retry options for an http request
*/
export class HttpRetryOptions {
maxRetry = 0;
handlers = [];
}
/**
* Enum for http method types
*/
export var HttpMethod;
(function (HttpMethod) {
HttpMethod["Get"] = "GET";
HttpMethod["Post"] = "POST";
HttpMethod["Put"] = "PUT";
HttpMethod["Delete"] = "DELETE";
HttpMethod["Patch"] = "PATCH";
HttpMethod["Head"] = "HEAD";
HttpMethod["Options"] = "OPTIONS";
})(HttpMethod || (HttpMethod = {}));
/**
* The Http observable based class.
*/
export class Http {
/**
* Default ajax options must be used for CORS.
*/
static defaultHttpOptions = { withCredentials: true, crossDomain: true };
/**
* The collection of set of monitors.
*/
static monitorSets = [];
/**
* The default retry options.
*/
defaultRetryOptions = new HttpRetryOptions();
/**
* Register the set of monitors.
*
* @param monitorSet The set of monitors.
*/
static registerMonitorSet(monitorSet) {
const found = Http.monitorSets.find(monitors => monitors.name === monitorSet.name);
if (found) {
return;
}
Http.monitorSets.push(monitorSet);
}
/**
* Unregister the set of monitors.
*
* @param name The name of set of monitors.
* @returns boolean true if unregistered the named set.
*/
static unregisterMonitors(name) {
const found = Http.monitorSets.find(monitors => monitors.name === name);
if (found) {
Http.monitorSets.remove(found);
return true;
}
return false;
}
/**
* The common request method.
* Adds default responseType, contentType, Accept values if they are not already included in the request
*
* @param request the request options.
* @param options the retry options.
*/
request(request, retryOptions) {
if (!request) {
request = {};
}
if (!retryOptions) {
retryOptions = this.defaultRetryOptions;
}
if (!request.headers) {
request.headers = {};
}
if (!request.responseType) {
request.responseType = 'json';
}
if (request.headers && !request.headers[headerConstants.CONTENT_TYPE]) {
request.headers[headerConstants.CONTENT_TYPE] = 'application/json; charset=utf-8';
}
if (request.method === HttpMethod.Get || request.method === HttpMethod.Delete || request.method === HttpMethod.Head) {
delete request.headers[headerConstants.CONTENT_TYPE];
request.body = undefined;
}
if (request.headers && !request.headers[headerConstants.ACCEPT]) {
request.headers[headerConstants.ACCEPT] = 'application/json, text/plain, */*';
}
if (request.headers && !request.headers[headerConstants.CROSS_SITE_REQUEST_FORGERY_TOKEN]) {
const token = Cookie.getCrossSiteRequestForgeryToken();
if (token) {
request.headers[headerConstants.CROSS_SITE_REQUEST_FORGERY_TOKEN] = token;
}
}
if (retryOptions.maxRetry > 0 && retryOptions.handlers && retryOptions.handlers.length > 0) {
return this.requestWithHandlers(request, retryOptions);
}
return this.monitorAjax(request);
}
/**
* Performs a request without modification.
* If the result is an error, we will retry with the handlers in options
*
* @param request the request options.
* @param options the retry options.
* @param count the current iteration of the retry cycle.
*/
requestWithHandlers(request, retryOptions, count = 0) {
return this.monitorAjax(request)
.pipe(catchError((error, caught) => {
// original request is replaced with latest instance. it must take current error request.
const caughtRequest = caught.source.request || (caught.source.source && caught.source.source.value) || request;
if (++count > retryOptions.maxRetry) {
return throwError(() => error);
}
const handler = retryOptions.handlers.find(handler2 => handler2.canHandle(error.status, error));
if (handler) {
if (handler.handleNoRetry) {
return handler.handleNoRetry(error.status, caughtRequest, error)
.pipe(mergeMap(() => throwError(() => error)));
}
else if (handler.handle) {
return handler.handle(error.status, caughtRequest, error)
.pipe(catchError(handlerError => {
// if the handler throws, return the original error with an inserted
// property for the handler error
error['handlerError'] = handlerError;
return throwError(() => error);
}), switchMap(() => this.monitorAjax(caughtRequest)));
}
}
return throwError(() => error);
}));
}
/**
* Performs a request with `get` http method.
*
* @param url the url.
* @param request the request options.
* @param options the retry options.
*/
get(url, request, options) {
request = request ? request : {};
request.url = url;
request.method = HttpMethod.Get;
return this.request(request, options);
}
/**
* Performs a request with `post` http method.
*
* @param url the url.
* @param body the body content.
* @param request the request options.
* @param options the retry options.
*/
post(url, body, request, options) {
request = request ? request : {};
request.url = url;
request.method = HttpMethod.Post;
request.body = body;
return this.request(request, options);
}
/**
* Performs a request with `put` http method.
*
* @param url the url.
* @param body the body content.
* @param request the request options.
* @param options the retry options.
*/
put(url, body, request, options) {
request = request ? request : {};
request.url = url;
request.method = HttpMethod.Put;
request.body = body;
return this.request(request, options);
}
/**
* Performs a request with `delete` http method.
*
* @param url the url.
* @param request the request options.
* @param options the retry options.
*/
delete(url, request, options) {
request = request ? request : {};
request.url = url;
request.method = HttpMethod.Delete;
return this.request(request, options);
}
/**
* Performs a request with `patch` http method.
*
* @param url the url.
* @param body the body content.
* @param request the request options.
* @param options the retry options.
*/
patch(url, body, request, options) {
request = request ? request : {};
request.url = url;
request.method = HttpMethod.Patch;
request.body = body;
return this.request(request, options);
}
/**
* Performs a request with `head` http method.
*
* @param url the url.
* @param request the request options.
* @param options the retry options.
*/
head(url, request, options) {
request = request ? request : {};
request.url = url;
request.method = HttpMethod.Head;
return this.request(request, options);
}
/**
* Performs a request with `options` http method.
*
* @param url the url.
* @param request the request options.
* @param options the retry options.
*/
options(url, request, options) {
request = request ? request : {};
request.url = url;
request.method = HttpMethod.Options;
return this.request(request, options);
}
/**
* Performs a request with 'get' http method with cache control.
*
* @param url the uri for GET call.
* @return the observable for GET result data.
*/
getNoCache(url, noCache = true, responseType = '', withCredentials = true) {
const publish = new Observable(observer => {
const request = new XMLHttpRequest();
const handler = () => {
if (request.readyState === XMLHttpRequest.DONE) {
if (request.status === 200) {
try {
let response;
if (responseType === '') {
response = JSON.parse(request.response);
}
else {
response = request.response;
}
observer.next({
status: request.status,
response
});
observer.complete();
}
catch (e) {
observer.error(e);
}
}
else {
const error = new ErrorExtended(request.statusText);
error.extendedSource = ErrorExtended.sources.getNoCache;
error.extended = { status: request.status, url };
observer.error(error);
}
}
};
request.open('Get', url);
request.withCredentials = withCredentials;
request.responseType = responseType;
request.setRequestHeader(headerConstants.ACCEPT, 'application/json, text/plain, */*');
if (noCache) {
request.setRequestHeader('Cache-control', 'no-cache');
}
request.onreadystatechange = handler;
request.send();
});
return publish;
}
/**
* Performs a request with 'delete' http method without waiting for the response.
*
* @param url the uri for GET call.
*/
deleteQuick(url, headers) {
const request = new XMLHttpRequest();
request.open('Delete', url);
request.withCredentials = true;
request.responseType = 'json';
request.setRequestHeader(headerConstants.ACCEPT, 'application/json, text/plain, */*');
request.setRequestHeader(headerConstants.CONTENT_TYPE, 'application/json; charset=utf-8');
request.setRequestHeader(headerConstants.CACHE_CONTROL, 'no-cache');
let xsrf = false;
if (headers) {
for (const key in headers) {
if (key) {
request.setRequestHeader(key, headers[key]);
if (key.toLowerCase() === headerConstants.CROSS_SITE_REQUEST_FORGERY_TOKEN.toLowerCase()) {
xsrf = true;
}
}
}
}
const token = Cookie.getCrossSiteRequestForgeryToken();
if (token && !xsrf) {
request.setRequestHeader(headerConstants.CROSS_SITE_REQUEST_FORGERY_TOKEN, token);
}
// not watching any response.
request.send();
}
monitorAjax(request) {
let monitored = ajax;
for (const monitorSet of Http.monitorSets) {
monitored = this.monitor(monitored, monitorSet);
}
return monitored(request);
}
monitor(target, monitorSet) {
return function (request) {
return monitorSet.preMonitor(request)
.pipe(switchMap(monitoredRequest => {
if (monitoredRequest instanceof Error) {
return throwError(() => monitoredRequest);
}
return target(monitoredRequest).pipe(catchError((error) => {
return monitorSet.errorMonitor(error);
}), switchMap(response => monitorSet.successMonitor(response)));
}));
};
}
}
//# sourceMappingURL=http.js.map