feat(common): two missing features in HttpClient (#17996)

- Add params to HttpRequest API
- Add optional description to testing APIs
This commit is contained in:
Alex Rickabaugh
2017-07-07 14:56:36 -07:00
committed by Jason Aden
parent 37797e2b4e
commit c81ad9d19d
14 changed files with 487 additions and 198 deletions

View File

@ -7,6 +7,7 @@
*/
import {HttpHeaders} from './headers';
import {HttpParams} from './params';
/**
* Construction interface for `HttpRequest`s.
@ -14,7 +15,7 @@ import {HttpHeaders} from './headers';
* All values are optional and will override default values if provided.
*/
interface HttpRequestInit {
headers?: HttpHeaders, reportProgress?: boolean,
headers?: HttpHeaders, reportProgress?: boolean, params?: HttpParams,
responseType?: 'arraybuffer'|'blob'|'json'|'text', withCredentials?: boolean,
}
@ -61,10 +62,6 @@ function isFormData(value: any): value is FormData {
return typeof FormData !== 'undefined' && value instanceof FormData;
}
function isUrlEncodedBody(value: any): value is Object {
return typeof value === 'object' && value['__HttpUrlEncodedBody'];
}
/**
* An outgoing HTTP request with an optional typed body.
*
@ -116,34 +113,49 @@ export class HttpRequest<T> {
*/
readonly method: string;
/**
* Outgoing URL parameters.
*/
readonly params: HttpParams;
/**
* The outgoing URL with all URL parameters set.
*/
readonly urlWithParams: string;
constructor(method: 'DELETE'|'GET'|'HEAD'|'JSONP'|'OPTIONS', url: string, init?: {
headers?: HttpHeaders,
reportProgress?: boolean,
params?: HttpParams,
responseType?: 'arraybuffer'|'blob'|'json'|'text',
withCredentials?: boolean,
});
constructor(method: 'POST'|'PUT'|'PATCH', url: string, body: T|null, init?: {
headers?: HttpHeaders,
reportProgress?: boolean,
params?: HttpParams,
responseType?: 'arraybuffer'|'blob'|'json'|'text',
withCredentials?: boolean,
});
constructor(method: string, url: string, body: T|null, init?: {
headers?: HttpHeaders,
reportProgress?: boolean,
params?: HttpParams,
responseType?: 'arraybuffer'|'blob'|'json'|'text',
withCredentials?: boolean,
});
constructor(
method: string, public url: string, third?: T|{
method: string, readonly url: string, third?: T|{
headers?: HttpHeaders,
reportProgress?: boolean,
params?: HttpParams,
responseType?: 'arraybuffer'|'blob'|'json'|'text',
withCredentials?: boolean,
}|null,
fourth?: {
headers?: HttpHeaders,
reportProgress?: boolean,
params?: HttpParams,
responseType?: 'arraybuffer'|'blob'|'json'|'text',
withCredentials?: boolean,
}) {
@ -178,12 +190,41 @@ export class HttpRequest<T> {
if (!!options.headers) {
this.headers = options.headers;
}
if (!!options.params) {
this.params = options.params;
}
}
// If no headers have been passed in, construct a new HttpHeaders instance.
if (!this.headers) {
this.headers = new HttpHeaders();
}
// If no parameters have been passed in, construct a new HttpUrlEncodedParams instance.
if (!this.params) {
this.params = new HttpParams();
this.urlWithParams = url;
} else {
// Encode the parameters to a string in preparation for inclusion in the URL.
const params = this.params.toString();
if (params.length === 0) {
// No parameters, the visible URL is just the URL given at creation time.
this.urlWithParams = url;
} else {
// Does the URL already have query parameters? Look for '?'.
const qIdx = url.indexOf('?');
// There are 3 cases to handle:
// 1) No existing parameters -> append '?' followed by params.
// 2) '?' exists and is followed by existing query string ->
// append '&' followed by params.
// 3) '?' exists at the end of the url -> append params directly.
// This basically amounts to determining the character, if any, with
// which to join the URL and parameters.
const sep: string = qIdx === -1 ? '?' : (qIdx < url.length - 1 ? '&' : '');
this.urlWithParams = url + sep + params;
}
}
}
/**
@ -201,9 +242,8 @@ export class HttpRequest<T> {
typeof this.body === 'string') {
return this.body;
}
// Check whether the body is an instance of HttpUrlEncodedBody, avoiding any direct
// references to the class in order to permit it being tree-shaken.
if (isUrlEncodedBody(this.body)) {
// Check whether the body is an instance of HttpUrlEncodedParams.
if (this.body instanceof HttpParams) {
return this.body.toString();
}
// Check whether the body is an object or array, and serialize with JSON if so.
@ -244,9 +284,8 @@ export class HttpRequest<T> {
if (typeof this.body === 'string') {
return 'text/plain';
}
// `HttpUrlEncodedBody` is detected specially so as to allow it to be
// tree-shaken.
if (isUrlEncodedBody(this.body)) {
// `HttpUrlEncodedParams` has its own content-type.
if (this.body instanceof HttpParams) {
return 'application/x-www-form-urlencoded;charset=UTF-8';
}
// Arrays, objects, and numbers will be encoded as JSON.
@ -262,28 +301,38 @@ export class HttpRequest<T> {
clone(update: {
headers?: HttpHeaders,
reportProgress?: boolean,
params?: HttpParams,
responseType?: 'arraybuffer'|'blob'|'json'|'text',
withCredentials?: boolean,
body?: T|null,
method?: string,
url?: string,
setHeaders?: {[name: string]: string | string[]},
setParams?: {[param: string]: string},
}): HttpRequest<T>;
clone<V>(update: {
headers?: HttpHeaders,
reportProgress?: boolean,
params?: HttpParams,
responseType?: 'arraybuffer'|'blob'|'json'|'text',
withCredentials?: boolean,
body?: V|null,
method?: string,
url?: string,
setHeaders?: {[name: string]: string | string[]},
setParams?: {[param: string]: string},
}): HttpRequest<V>;
clone(update: {
headers?: HttpHeaders,
reportProgress?: boolean,
params?: HttpParams,
responseType?: 'arraybuffer'|'blob'|'json'|'text',
withCredentials?: boolean,
body?: any|null,
method?: string,
url?: string,
setHeaders?: {[name: string]: string | string[]},
setParams?: {[param: string]: string};
} = {}): HttpRequest<any> {
// For method, url, and responseType, take the current value unless
// it is overridden in the update hash.
@ -304,9 +353,10 @@ export class HttpRequest<T> {
const reportProgress =
(update.reportProgress !== undefined) ? update.reportProgress : this.reportProgress;
// Headers may need to be cloned later if they're sealed, but being
// appended to.
// Headers and params may be appended to if `setHeaders` or
// `setParams` are used.
let headers = update.headers || this.headers;
let params = update.params || this.params;
// Check whether the caller has asked to add headers.
if (update.setHeaders !== undefined) {
@ -316,10 +366,17 @@ export class HttpRequest<T> {
.reduce((headers, name) => headers.set(name, update.setHeaders ![name]), headers);
}
// Check whether the caller has asked to set params.
if (update.setParams) {
// Set every requested param.
params = Object.keys(update.setParams)
.reduce((params, param) => params.set(param, update.setParams ![param]), params);
}
// Finally, construct the new HttpRequest using the pieces from above.
return new HttpRequest(
method, url, body, {
headers, reportProgress, responseType, withCredentials,
params, headers, reportProgress, responseType, withCredentials,
});
}
}