refactor(http): remove default settings from RequestOptions
constructor
The BaseRequestOptions class is responsible for declaring default values, while the RequestOptions class is merely responsible for setting values based on values provided in the constructor.
This commit is contained in:
parent
146dbf1270
commit
b3d98cba77
@ -17,5 +17,6 @@ export * from './src/core/compiler/dynamic_component_loader';
|
|||||||
export {ViewRef, ProtoViewRef} from './src/core/compiler/view_ref';
|
export {ViewRef, ProtoViewRef} from './src/core/compiler/view_ref';
|
||||||
export {ViewContainerRef} from './src/core/compiler/view_container_ref';
|
export {ViewContainerRef} from './src/core/compiler/view_container_ref';
|
||||||
export {ElementRef} from './src/core/compiler/element_ref';
|
export {ElementRef} from './src/core/compiler/element_ref';
|
||||||
|
export {EventEmitter} from './src/facade/async';
|
||||||
|
|
||||||
export {NgZone} from './src/core/zone/ng_zone';
|
export {NgZone} from './src/core/zone/ng_zone';
|
@ -6,30 +6,39 @@
|
|||||||
* class.
|
* class.
|
||||||
*/
|
*/
|
||||||
import {bind, Binding} from 'angular2/di';
|
import {bind, Binding} from 'angular2/di';
|
||||||
import {Http} from './src/http/http';
|
import {Http} from 'angular2/src/http/http';
|
||||||
import {XHRBackend, XHRConnection} from 'angular2/src/http/backends/xhr_backend';
|
import {XHRBackend, XHRConnection} from 'angular2/src/http/backends/xhr_backend';
|
||||||
import {BrowserXHR} from 'angular2/src/http/backends/browser_xhr';
|
import {BrowserXhr} from 'angular2/src/http/backends/browser_xhr';
|
||||||
import {BaseRequestOptions, RequestOptions} from 'angular2/src/http/base_request_options';
|
import {BaseRequestOptions, RequestOptions} from 'angular2/src/http/base_request_options';
|
||||||
import {ConnectionBackend} from 'angular2/src/http/interfaces';
|
import {ConnectionBackend} from 'angular2/src/http/interfaces';
|
||||||
|
|
||||||
export {MockConnection, MockBackend} from 'angular2/src/http/backends/mock_backend';
|
export {MockConnection, MockBackend} from 'angular2/src/http/backends/mock_backend';
|
||||||
export {Request} from 'angular2/src/http/static_request';
|
export {Request} from 'angular2/src/http/static_request';
|
||||||
export {Response} from 'angular2/src/http/static_response';
|
export {Response} from 'angular2/src/http/static_response';
|
||||||
|
import {BaseResponseOptions, ResponseOptions} from 'angular2/src/http/base_response_options';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
IRequestOptions,
|
IRequestOptions,
|
||||||
IResponse,
|
IResponseOptions,
|
||||||
Connection,
|
Connection,
|
||||||
ConnectionBackend
|
ConnectionBackend
|
||||||
} from 'angular2/src/http/interfaces';
|
} from 'angular2/src/http/interfaces';
|
||||||
|
|
||||||
export {BaseRequestOptions, RequestOptions} from 'angular2/src/http/base_request_options';
|
export {BaseRequestOptions, RequestOptions} from 'angular2/src/http/base_request_options';
|
||||||
|
export {BaseResponseOptions, ResponseOptions} from 'angular2/src/http/base_response_options';
|
||||||
export {XHRBackend, XHRConnection} from 'angular2/src/http/backends/xhr_backend';
|
export {XHRBackend, XHRConnection} from 'angular2/src/http/backends/xhr_backend';
|
||||||
export {Http} from './src/http/http';
|
export {Http} from 'angular2/src/http/http';
|
||||||
|
|
||||||
export {Headers} from 'angular2/src/http/headers';
|
export {Headers} from 'angular2/src/http/headers';
|
||||||
|
|
||||||
export * from 'angular2/src/http/enums';
|
export {
|
||||||
|
ResponseTypes,
|
||||||
|
ReadyStates,
|
||||||
|
RequestMethods,
|
||||||
|
RequestCredentialsOpts,
|
||||||
|
RequestCacheOpts,
|
||||||
|
RequestModesOpts
|
||||||
|
} from 'angular2/src/http/enums';
|
||||||
export {URLSearchParams} from 'angular2/src/http/url_search_params';
|
export {URLSearchParams} from 'angular2/src/http/url_search_params';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,5 +58,11 @@ export {URLSearchParams} from 'angular2/src/http/url_search_params';
|
|||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export var httpInjectables: List<any> =
|
export var httpInjectables: List<any> = [
|
||||||
[bind(ConnectionBackend).toClass(XHRBackend), BrowserXHR, XHRBackend, BaseRequestOptions, Http];
|
bind(ConnectionBackend)
|
||||||
|
.toClass(XHRBackend),
|
||||||
|
BrowserXhr,
|
||||||
|
bind(RequestOptions).toClass(BaseRequestOptions),
|
||||||
|
bind(ResponseOptions).toClass(BaseResponseOptions),
|
||||||
|
Http
|
||||||
|
];
|
||||||
|
@ -4,7 +4,7 @@ import 'dart:html' show HttpRequest;
|
|||||||
import 'package:angular2/di.dart';
|
import 'package:angular2/di.dart';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class BrowserXHR {
|
class BrowserXhr {
|
||||||
HttpRequest build() {
|
HttpRequest build() {
|
||||||
return new HttpRequest();
|
return new HttpRequest();
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
declare var window;
|
|
||||||
|
|
||||||
import {Injectable} from 'angular2/di';
|
import {Injectable} from 'angular2/di';
|
||||||
|
|
||||||
// Make sure not to evaluate this in a non-browser environment!
|
// Make sure not to evaluate this in a non-browser environment!
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class BrowserXHR {
|
export class BrowserXhr {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
build(): any { return <any>(new window.XMLHttpRequest()); }
|
build(): any { return <any>(new XMLHttpRequest()); }
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,7 @@ import {IMPLEMENTS, BaseException} from 'angular2/src/facade/lang';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Connection class used by MockBackend
|
* Mock Connection to represent a {@link Connection} for tests.
|
||||||
*
|
|
||||||
* This class is typically not instantiated directly, but instances can be retrieved by subscribing
|
|
||||||
*to the `connections` Observable of
|
|
||||||
* {@link MockBackend} in order to mock responses to requests.
|
|
||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
@IMPLEMENTS(Connection)
|
@IMPLEMENTS(Connection)
|
||||||
@ -32,9 +28,8 @@ export class MockConnection {
|
|||||||
request: Request;
|
request: Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [RxJS
|
* {@link EventEmitter} of {@link Response}. Can be subscribed to in order to be notified when a
|
||||||
* Observable](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md)
|
* response is available.
|
||||||
* of {@link Response}. Can be subscribed to in order to be notified when a response is available.
|
|
||||||
*/
|
*/
|
||||||
response: EventEmitter;
|
response: EventEmitter;
|
||||||
|
|
||||||
@ -55,7 +50,7 @@ export class MockConnection {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a mock response to the connection. This response is the value that is emitted to the
|
* Sends a mock response to the connection. This response is the value that is emitted to the
|
||||||
* `Observable` returned by {@link Http}.
|
* {@link EventEmitter} returned by {@link Http}.
|
||||||
*
|
*
|
||||||
* #Example
|
* #Example
|
||||||
*
|
*
|
||||||
@ -91,7 +86,8 @@ export class MockConnection {
|
|||||||
|
|
||||||
// TODO(jeffbcross): consider using Response type
|
// TODO(jeffbcross): consider using Response type
|
||||||
/**
|
/**
|
||||||
* Emits the provided error object as an error to the {@link Response} observable returned
|
* Emits the provided error object as an error to the {@link Response} {@link EventEmitter}
|
||||||
|
* returned
|
||||||
* from {@link Http}.
|
* from {@link Http}.
|
||||||
*/
|
*/
|
||||||
mockError(err?) {
|
mockError(err?) {
|
||||||
@ -137,8 +133,7 @@ export class MockConnection {
|
|||||||
@IMPLEMENTS(ConnectionBackend)
|
@IMPLEMENTS(ConnectionBackend)
|
||||||
export class MockBackend {
|
export class MockBackend {
|
||||||
/**
|
/**
|
||||||
* [RxJS
|
* {@link EventEmitter}
|
||||||
* Subject](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/subject.md)
|
|
||||||
* of {@link MockConnection} instances that have been created by this backend. Can be subscribed
|
* of {@link MockConnection} instances that have been created by this backend. Can be subscribed
|
||||||
* to in order to respond to connections.
|
* to in order to respond to connections.
|
||||||
*
|
*
|
||||||
@ -162,7 +157,7 @@ export class MockBackend {
|
|||||||
* http.request('something.json').subscribe(res => {
|
* http.request('something.json').subscribe(res => {
|
||||||
* text = res.text();
|
* text = res.text();
|
||||||
* });
|
* });
|
||||||
* connection.mockRespond(new Response('Something'));
|
* connection.mockRespond(new Response({body: 'Something'}));
|
||||||
* expect(text).toBe('Something');
|
* expect(text).toBe('Something');
|
||||||
* });
|
* });
|
||||||
* ```
|
* ```
|
||||||
@ -179,8 +174,8 @@ export class MockBackend {
|
|||||||
*/
|
*/
|
||||||
connectionsArray: Array<MockConnection>;
|
connectionsArray: Array<MockConnection>;
|
||||||
/**
|
/**
|
||||||
* [Observable](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md)
|
* {@link EventEmitter} of {@link MockConnection} instances that haven't yet been resolved (i.e.
|
||||||
* of {@link MockConnection} instances that haven't yet been resolved (i.e. with a `readyState`
|
* with a `readyState`
|
||||||
* less than 4). Used internally to verify that no connections are pending via the
|
* less than 4). Used internally to verify that no connections are pending via the
|
||||||
* `verifyNoPendingRequests` method.
|
* `verifyNoPendingRequests` method.
|
||||||
*
|
*
|
||||||
@ -193,7 +188,6 @@ export class MockBackend {
|
|||||||
ObservableWrapper.subscribe(this.connections,
|
ObservableWrapper.subscribe(this.connections,
|
||||||
connection => this.connectionsArray.push(connection));
|
connection => this.connectionsArray.push(connection));
|
||||||
this.pendingConnections = new EventEmitter();
|
this.pendingConnections = new EventEmitter();
|
||||||
// Observable.fromArray(this.connectionsArray).filter((c) => c.readyState < ReadyStates.DONE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -218,10 +212,10 @@ export class MockBackend {
|
|||||||
/**
|
/**
|
||||||
* Creates a new {@link MockConnection}. This is equivalent to calling `new
|
* Creates a new {@link MockConnection}. This is equivalent to calling `new
|
||||||
* MockConnection()`, except that it also will emit the new `Connection` to the `connections`
|
* MockConnection()`, except that it also will emit the new `Connection` to the `connections`
|
||||||
* observable of this `MockBackend` instance. This method will usually only be used by tests
|
* emitter of this `MockBackend` instance. This method will usually only be used by tests
|
||||||
* against the framework itself, not by end-users.
|
* against the framework itself, not by end-users.
|
||||||
*/
|
*/
|
||||||
createConnection(req: Request) {
|
createConnection(req: Request): Connection {
|
||||||
if (!isPresent(req) || !(req instanceof Request)) {
|
if (!isPresent(req) || !(req instanceof Request)) {
|
||||||
throw new BaseException(`createConnection requires an instance of Request, got ${req}`);
|
throw new BaseException(`createConnection requires an instance of Request, got ${req}`);
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,9 @@ import {ConnectionBackend, Connection} from '../interfaces';
|
|||||||
import {ReadyStates, RequestMethods, RequestMethodsMap} from '../enums';
|
import {ReadyStates, RequestMethods, RequestMethodsMap} from '../enums';
|
||||||
import {Request} from '../static_request';
|
import {Request} from '../static_request';
|
||||||
import {Response} from '../static_response';
|
import {Response} from '../static_response';
|
||||||
|
import {ResponseOptions, BaseResponseOptions} from '../base_response_options';
|
||||||
import {Injectable} from 'angular2/di';
|
import {Injectable} from 'angular2/di';
|
||||||
import {BrowserXHR} from './browser_xhr';
|
import {BrowserXhr} from './browser_xhr';
|
||||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
import {isPresent, ENUM_INDEX} from 'angular2/src/facade/lang';
|
import {isPresent, ENUM_INDEX} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
@ -18,14 +19,14 @@ import {isPresent, ENUM_INDEX} from 'angular2/src/facade/lang';
|
|||||||
export class XHRConnection implements Connection {
|
export class XHRConnection implements Connection {
|
||||||
request: Request;
|
request: Request;
|
||||||
/**
|
/**
|
||||||
* Response
|
* Response {@link EventEmitter} which emits a single {@link Response} value on load event of
|
||||||
* [Subject](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/subject.md)
|
* `XMLHttpRequest`.
|
||||||
* which emits a single {@link Response} value on load event of `XMLHttpRequest`.
|
|
||||||
*/
|
*/
|
||||||
response: EventEmitter; //<Response>;
|
response: EventEmitter; // TODO: Make generic of <Response>;
|
||||||
readyState: ReadyStates;
|
readyState: ReadyStates;
|
||||||
private _xhr;
|
private _xhr; // TODO: make type XMLHttpRequest, pending resolution of
|
||||||
constructor(req: Request, browserXHR: BrowserXHR) {
|
// https://github.com/angular/ts2dart/issues/230
|
||||||
|
constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions?: ResponseOptions) {
|
||||||
// TODO: get rid of this when enum lookups are available in ts2dart
|
// TODO: get rid of this when enum lookups are available in ts2dart
|
||||||
// https://github.com/angular/ts2dart/issues/221
|
// https://github.com/angular/ts2dart/issues/221
|
||||||
var requestMethodsMap = new RequestMethodsMap();
|
var requestMethodsMap = new RequestMethodsMap();
|
||||||
@ -34,12 +35,15 @@ export class XHRConnection implements Connection {
|
|||||||
this._xhr = browserXHR.build();
|
this._xhr = browserXHR.build();
|
||||||
// TODO(jeffbcross): implement error listening/propagation
|
// TODO(jeffbcross): implement error listening/propagation
|
||||||
this._xhr.open(requestMethodsMap.getMethod(ENUM_INDEX(req.method)), req.url);
|
this._xhr.open(requestMethodsMap.getMethod(ENUM_INDEX(req.method)), req.url);
|
||||||
this._xhr.addEventListener(
|
this._xhr.addEventListener('load', (_) => {
|
||||||
'load',
|
var responseOptions = new ResponseOptions(
|
||||||
(_) => {ObservableWrapper.callNext(
|
{body: isPresent(this._xhr.response) ? this._xhr.response : this._xhr.responseText});
|
||||||
this.response, new Response({
|
if (isPresent(baseResponseOptions)) {
|
||||||
body: isPresent(this._xhr.response) ? this._xhr.response : this._xhr.responseText
|
responseOptions = baseResponseOptions.merge(responseOptions);
|
||||||
}))});
|
}
|
||||||
|
|
||||||
|
ObservableWrapper.callNext(this.response, new Response(responseOptions))
|
||||||
|
});
|
||||||
// TODO(jeffbcross): make this more dynamic based on body type
|
// TODO(jeffbcross): make this more dynamic based on body type
|
||||||
this._xhr.send(this.request.text());
|
this._xhr.send(this.request.text());
|
||||||
}
|
}
|
||||||
@ -78,8 +82,8 @@ export class XHRConnection implements Connection {
|
|||||||
**/
|
**/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class XHRBackend implements ConnectionBackend {
|
export class XHRBackend implements ConnectionBackend {
|
||||||
constructor(private _browserXHR: BrowserXHR) {}
|
constructor(private _browserXHR: BrowserXhr, private _baseResponseOptions: ResponseOptions) {}
|
||||||
createConnection(request: Request): XHRConnection {
|
createConnection(request: Request): XHRConnection {
|
||||||
return new XHRConnection(request, this._browserXHR);
|
return new XHRConnection(request, this._browserXHR, this._baseResponseOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,14 @@ import {Headers} from './headers';
|
|||||||
import {RequestModesOpts, RequestMethods, RequestCacheOpts, RequestCredentialsOpts} from './enums';
|
import {RequestModesOpts, RequestMethods, RequestCacheOpts, RequestCredentialsOpts} from './enums';
|
||||||
import {IRequestOptions} from './interfaces';
|
import {IRequestOptions} from './interfaces';
|
||||||
import {Injectable} from 'angular2/di';
|
import {Injectable} from 'angular2/di';
|
||||||
import {ListWrapper, StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
|
|
||||||
|
|
||||||
const INITIALIZER = CONST_EXPR({});
|
|
||||||
/**
|
/**
|
||||||
* Creates a request options object with default properties as described in the [Fetch
|
* Creates a request options object similar to the `RequestInit` description
|
||||||
|
* in the [Fetch
|
||||||
* Spec](https://fetch.spec.whatwg.org/#requestinit) to be optionally provided when instantiating a
|
* Spec](https://fetch.spec.whatwg.org/#requestinit) to be optionally provided when instantiating a
|
||||||
* {@link Request}. This class is used implicitly by {@link Http} to merge in provided request
|
* {@link Request}.
|
||||||
* options with the default options specified here. These same default options are injectable via
|
*
|
||||||
* the {@link BaseRequestOptions} class.
|
* All values are null by default.
|
||||||
*/
|
*/
|
||||||
export class RequestOptions implements IRequestOptions {
|
export class RequestOptions implements IRequestOptions {
|
||||||
/**
|
/**
|
||||||
@ -19,7 +18,7 @@ export class RequestOptions implements IRequestOptions {
|
|||||||
*
|
*
|
||||||
* Defaults to "GET".
|
* Defaults to "GET".
|
||||||
*/
|
*/
|
||||||
method: RequestMethods = RequestMethods.GET;
|
method: RequestMethods;
|
||||||
/**
|
/**
|
||||||
* Headers object based on the `Headers` class in the [Fetch
|
* Headers object based on the `Headers` class in the [Fetch
|
||||||
* Spec](https://fetch.spec.whatwg.org/#headers-class).
|
* Spec](https://fetch.spec.whatwg.org/#headers-class).
|
||||||
@ -28,54 +27,42 @@ export class RequestOptions implements IRequestOptions {
|
|||||||
/**
|
/**
|
||||||
* Body to be used when creating the request.
|
* Body to be used when creating the request.
|
||||||
*/
|
*/
|
||||||
// TODO: support FormData, Blob, URLSearchParams, JSON
|
// TODO: support FormData, Blob, URLSearchParams
|
||||||
body: string;
|
body: string;
|
||||||
mode: RequestModesOpts = RequestModesOpts.Cors;
|
mode: RequestModesOpts;
|
||||||
credentials: RequestCredentialsOpts;
|
credentials: RequestCredentialsOpts;
|
||||||
cache: RequestCacheOpts;
|
cache: RequestCacheOpts;
|
||||||
url: string;
|
url: string;
|
||||||
constructor({method, headers, body, mode, credentials, cache, url}: IRequestOptions = {}) {
|
constructor({method, headers, body, mode, credentials, cache, url}: IRequestOptions = {}) {
|
||||||
this.method = isPresent(method) ? method : RequestMethods.GET;
|
this.method = isPresent(method) ? method : null;
|
||||||
this.headers = headers;
|
this.headers = isPresent(headers) ? headers : null;
|
||||||
this.body = body;
|
this.body = isPresent(body) ? body : null;
|
||||||
this.mode = isPresent(mode) ? mode : RequestModesOpts.Cors;
|
this.mode = isPresent(mode) ? mode : null;
|
||||||
this.credentials = credentials;
|
this.credentials = isPresent(credentials) ? credentials : null;
|
||||||
this.cache = cache;
|
this.cache = isPresent(cache) ? cache : null;
|
||||||
this.url = url;
|
this.url = isPresent(url) ? url : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a copy of the `RequestOptions` instance, using the optional input as values to override
|
* Creates a copy of the `RequestOptions` instance, using the optional input as values to override
|
||||||
* existing values.
|
* existing values.
|
||||||
*/
|
*/
|
||||||
merge({url = null, method = null, headers = null, body = null, mode = null, credentials = null,
|
merge(options?: IRequestOptions): RequestOptions {
|
||||||
cache = null}: any = {}): RequestOptions {
|
|
||||||
return new RequestOptions({
|
return new RequestOptions({
|
||||||
method: isPresent(method) ? method : this.method,
|
method: isPresent(options) && isPresent(options.method) ? options.method : this.method,
|
||||||
headers: isPresent(headers) ? headers : this.headers,
|
headers: isPresent(options) && isPresent(options.headers) ? options.headers : this.headers,
|
||||||
body: isPresent(body) ? body : this.body,
|
body: isPresent(options) && isPresent(options.body) ? options.body : this.body,
|
||||||
mode: isPresent(mode) ? mode : this.mode,
|
mode: isPresent(options) && isPresent(options.mode) ? options.mode : this.mode,
|
||||||
credentials: isPresent(credentials) ? credentials : this.credentials,
|
credentials: isPresent(options) && isPresent(options.credentials) ? options.credentials :
|
||||||
cache: isPresent(cache) ? cache : this.cache,
|
this.credentials,
|
||||||
url: isPresent(url) ? url : this.url
|
cache: isPresent(options) && isPresent(options.cache) ? options.cache : this.cache,
|
||||||
|
url: isPresent(options) && isPresent(options.url) ? options.url : this.url
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromStringWrapper(map: StringMap<string, any>): RequestOptions {
|
|
||||||
return new RequestOptions({
|
|
||||||
method: StringMapWrapper.get(map, 'method'),
|
|
||||||
headers: StringMapWrapper.get(map, 'headers'),
|
|
||||||
body: StringMapWrapper.get(map, 'body'),
|
|
||||||
mode: StringMapWrapper.get(map, 'mode'),
|
|
||||||
credentials: StringMapWrapper.get(map, 'credentials'),
|
|
||||||
cache: StringMapWrapper.get(map, 'cache'),
|
|
||||||
url:<string>StringMapWrapper.get(map, 'url')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injectable version of {@link RequestOptions}.
|
* Injectable version of {@link RequestOptions}, with overridable default values.
|
||||||
*
|
*
|
||||||
* #Example
|
* #Example
|
||||||
*
|
*
|
||||||
@ -84,8 +71,8 @@ export class RequestOptions implements IRequestOptions {
|
|||||||
* ...
|
* ...
|
||||||
* class MyComponent {
|
* class MyComponent {
|
||||||
* constructor(baseRequestOptions:BaseRequestOptions, http:Http) {
|
* constructor(baseRequestOptions:BaseRequestOptions, http:Http) {
|
||||||
* var options = baseRequestOptions.merge({body: 'foobar'});
|
* var options = baseRequestOptions.merge({body: 'foobar', url: 'https://foo'});
|
||||||
* var request = new Request('https://foo', options);
|
* var request = new Request(options);
|
||||||
* http.request(request).subscribe(res => this.bars = res.json());
|
* http.request(request).subscribe(res => this.bars = res.json());
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
@ -94,5 +81,7 @@ export class RequestOptions implements IRequestOptions {
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class BaseRequestOptions extends RequestOptions {
|
export class BaseRequestOptions extends RequestOptions {
|
||||||
constructor() { super(); }
|
constructor() {
|
||||||
|
super({method: RequestMethods.GET, headers: new Headers(), mode: RequestModesOpts.Cors});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,54 @@
|
|||||||
|
import {Injectable} from 'angular2/di';
|
||||||
|
import {isPresent, isJsObject} from 'angular2/src/facade/lang';
|
||||||
import {Headers} from './headers';
|
import {Headers} from './headers';
|
||||||
import {ResponseTypes} from './enums';
|
import {ResponseTypes} from './enums';
|
||||||
import {ResponseOptions} from './interfaces';
|
import {IResponseOptions} from './interfaces';
|
||||||
|
|
||||||
export class BaseResponseOptions implements ResponseOptions {
|
/**
|
||||||
|
* Creates a response options object similar to the
|
||||||
|
* [ResponseInit](https://fetch.spec.whatwg.org/#responseinit) description
|
||||||
|
* in the Fetch
|
||||||
|
* Spec to be optionally provided when instantiating a
|
||||||
|
* {@link Response}.
|
||||||
|
*
|
||||||
|
* All values are null by default.
|
||||||
|
*/
|
||||||
|
export class ResponseOptions implements IResponseOptions {
|
||||||
|
// TODO: ArrayBuffer | FormData | Blob
|
||||||
|
body: string | Object;
|
||||||
|
status: number;
|
||||||
|
headers: Headers;
|
||||||
|
statusText: string;
|
||||||
|
type: ResponseTypes;
|
||||||
|
url: string;
|
||||||
|
constructor({body, status, headers, statusText, type, url}: IResponseOptions = {}) {
|
||||||
|
this.body = isPresent(body) ? body : null;
|
||||||
|
this.status = isPresent(status) ? status : null;
|
||||||
|
this.headers = isPresent(headers) ? headers : null;
|
||||||
|
this.statusText = isPresent(statusText) ? statusText : null;
|
||||||
|
this.type = isPresent(type) ? type : null;
|
||||||
|
this.url = isPresent(url) ? url : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
merge(options?: IResponseOptions): ResponseOptions {
|
||||||
|
return new ResponseOptions({
|
||||||
|
body: isPresent(options) && isPresent(options.body) ? options.body : this.body,
|
||||||
|
status: isPresent(options) && isPresent(options.status) ? options.status : this.status,
|
||||||
|
headers: isPresent(options) && isPresent(options.headers) ? options.headers : this.headers,
|
||||||
|
statusText: isPresent(options) && isPresent(options.statusText) ? options.statusText :
|
||||||
|
this.statusText,
|
||||||
|
type: isPresent(options) && isPresent(options.type) ? options.type : this.type,
|
||||||
|
url: isPresent(options) && isPresent(options.url) ? options.url : this.url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injectable version of {@link ResponseOptions}, with overridable default values.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class BaseResponseOptions extends ResponseOptions {
|
||||||
|
// TODO: Object | ArrayBuffer | JSON | FormData | Blob
|
||||||
body: string | Object | ArrayBuffer | JSON | FormData | Blob;
|
body: string | Object | ArrayBuffer | JSON | FormData | Blob;
|
||||||
status: number;
|
status: number;
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
@ -11,11 +57,6 @@ export class BaseResponseOptions implements ResponseOptions {
|
|||||||
url: string;
|
url: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.status = 200;
|
super({status: 200, statusText: 'Ok', type: ResponseTypes.Default, headers: new Headers()});
|
||||||
this.statusText = 'Ok';
|
|
||||||
this.type = ResponseTypes.Default;
|
|
||||||
this.headers = new Headers();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export var baseResponseOptions = new BaseResponseOptions();
|
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
import {StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
|
import {StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acceptable origin modes to be associated with a {@link Request}, based on
|
||||||
|
* [RequestMode](https://fetch.spec.whatwg.org/#requestmode) from the Fetch spec.
|
||||||
|
*/
|
||||||
export enum RequestModesOpts {
|
export enum RequestModesOpts {
|
||||||
Cors,
|
Cors,
|
||||||
NoCors,
|
NoCors,
|
||||||
SameOrigin
|
SameOrigin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acceptable cache option to be associated with a {@link Request}, based on
|
||||||
|
* [RequestCache](https://fetch.spec.whatwg.org/#requestcache) from the Fetch spec.
|
||||||
|
*/
|
||||||
export enum RequestCacheOpts {
|
export enum RequestCacheOpts {
|
||||||
Default,
|
Default,
|
||||||
NoStore,
|
NoStore,
|
||||||
@ -15,12 +23,19 @@ export enum RequestCacheOpts {
|
|||||||
OnlyIfCached
|
OnlyIfCached
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acceptable credentials option to be associated with a {@link Request}, based on
|
||||||
|
* [RequestCredentials](https://fetch.spec.whatwg.org/#requestcredentials) from the Fetch spec.
|
||||||
|
*/
|
||||||
export enum RequestCredentialsOpts {
|
export enum RequestCredentialsOpts {
|
||||||
Omit,
|
Omit,
|
||||||
SameOrigin,
|
SameOrigin,
|
||||||
Include
|
Include
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported http methods.
|
||||||
|
*/
|
||||||
export enum RequestMethods {
|
export enum RequestMethods {
|
||||||
GET,
|
GET,
|
||||||
POST,
|
POST,
|
||||||
@ -38,7 +53,11 @@ export class RequestMethodsMap {
|
|||||||
constructor() { this._methods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD', 'PATCH']; }
|
constructor() { this._methods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD', 'PATCH']; }
|
||||||
getMethod(method: int): string { return this._methods[method]; }
|
getMethod(method: int): string { return this._methods[method]; }
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* All possible states in which a connection can be, based on
|
||||||
|
* [States](http://www.w3.org/TR/XMLHttpRequest/#states) from the `XMLHttpRequest` spec, but with an
|
||||||
|
* additional "CANCELLED" state.
|
||||||
|
*/
|
||||||
export enum ReadyStates {
|
export enum ReadyStates {
|
||||||
UNSENT,
|
UNSENT,
|
||||||
OPEN,
|
OPEN,
|
||||||
@ -48,6 +67,10 @@ export enum ReadyStates {
|
|||||||
CANCELLED
|
CANCELLED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acceptable response types to be associated with a {@link Response}, based on
|
||||||
|
* [ResponseType](https://fetch.spec.whatwg.org/#responsetype) from the Fetch spec.
|
||||||
|
*/
|
||||||
export enum ResponseTypes {
|
export enum ResponseTypes {
|
||||||
Basic,
|
Basic,
|
||||||
Cors,
|
Cors,
|
||||||
|
@ -42,6 +42,9 @@ export class Headers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a header to existing list of header values for a given header name.
|
||||||
|
*/
|
||||||
append(name: string, value: string): void {
|
append(name: string, value: string): void {
|
||||||
var mapName = this._headersMap.get(name);
|
var mapName = this._headersMap.get(name);
|
||||||
var list = isListLikeIterable(mapName) ? mapName : [];
|
var list = isListLikeIterable(mapName) ? mapName : [];
|
||||||
@ -49,26 +52,36 @@ export class Headers {
|
|||||||
this._headersMap.set(name, list);
|
this._headersMap.set(name, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all header values for the given name.
|
||||||
|
*/
|
||||||
delete (name: string): void { MapWrapper.delete(this._headersMap, name); }
|
delete (name: string): void { MapWrapper.delete(this._headersMap, name); }
|
||||||
|
|
||||||
forEach(fn: Function) { MapWrapper.forEach(this._headersMap, fn); }
|
forEach(fn: Function) { MapWrapper.forEach(this._headersMap, fn); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns first header that matches given name.
|
||||||
|
*/
|
||||||
get(header: string): string { return ListWrapper.first(this._headersMap.get(header)); }
|
get(header: string): string { return ListWrapper.first(this._headersMap.get(header)); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for existence of header by given name.
|
||||||
|
*/
|
||||||
has(header: string): boolean { return this._headersMap.has(header); }
|
has(header: string): boolean { return this._headersMap.has(header); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides names of set headers
|
||||||
|
*/
|
||||||
keys(): List<string> { return MapWrapper.keys(this._headersMap); }
|
keys(): List<string> { return MapWrapper.keys(this._headersMap); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets or overrides header value for given name.
|
||||||
|
*/
|
||||||
set(header: string, value: string | List<string>): void {
|
set(header: string, value: string | List<string>): void {
|
||||||
var list = [];
|
var list = [];
|
||||||
var isDart = false;
|
|
||||||
// Dart hack
|
|
||||||
if (list.toString().length === 2) {
|
|
||||||
isDart = true;
|
|
||||||
}
|
|
||||||
if (isListLikeIterable(value)) {
|
if (isListLikeIterable(value)) {
|
||||||
var pushValue = (<List<string>>value).toString();
|
var pushValue = (<List<string>>value).join(',');
|
||||||
if (isDart) pushValue = pushValue.substring(1, pushValue.length - 1);
|
|
||||||
list.push(pushValue);
|
list.push(pushValue);
|
||||||
} else {
|
} else {
|
||||||
list.push(value);
|
list.push(value);
|
||||||
@ -77,12 +90,21 @@ export class Headers {
|
|||||||
this._headersMap.set(header, list);
|
this._headersMap.set(header, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns values of all headers.
|
||||||
|
*/
|
||||||
values(): List<List<string>> { return MapWrapper.values(this._headersMap); }
|
values(): List<List<string>> { return MapWrapper.values(this._headersMap); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns list of header values for a given name.
|
||||||
|
*/
|
||||||
getAll(header: string): Array<string> {
|
getAll(header: string): Array<string> {
|
||||||
var headers = this._headersMap.get(header);
|
var headers = this._headersMap.get(header);
|
||||||
return isListLikeIterable(headers) ? headers : [];
|
return isListLikeIterable(headers) ? headers : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is not implemented.
|
||||||
|
*/
|
||||||
entries() { throw new BaseException('"entries" method is not implemented on Headers class'); }
|
entries() { throw new BaseException('"entries" method is not implemented on Headers class'); }
|
||||||
}
|
}
|
||||||
|
@ -13,12 +13,21 @@ function httpRequest(backend: ConnectionBackend, request: Request): EventEmitter
|
|||||||
function mergeOptions(defaultOpts, providedOpts, method, url): RequestOptions {
|
function mergeOptions(defaultOpts, providedOpts, method, url): RequestOptions {
|
||||||
var newOptions = defaultOpts;
|
var newOptions = defaultOpts;
|
||||||
if (isPresent(providedOpts)) {
|
if (isPresent(providedOpts)) {
|
||||||
newOptions = newOptions.merge(providedOpts);
|
// Hack so Dart can used named parameters
|
||||||
|
newOptions = newOptions.merge(new RequestOptions({
|
||||||
|
method: providedOpts.method,
|
||||||
|
url: providedOpts.url,
|
||||||
|
headers: providedOpts.headers,
|
||||||
|
body: providedOpts.body,
|
||||||
|
mode: providedOpts.mode,
|
||||||
|
credentials: providedOpts.credentials,
|
||||||
|
cache: providedOpts.cache
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
if (isPresent(method)) {
|
if (isPresent(method)) {
|
||||||
return newOptions.merge({method: method, url: url});
|
return newOptions.merge(new RequestOptions({method: method, url: url}));
|
||||||
} else {
|
} else {
|
||||||
return newOptions.merge({url: url});
|
return newOptions.merge(new RequestOptions({url: url}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,9 +35,8 @@ function mergeOptions(defaultOpts, providedOpts, method, url): RequestOptions {
|
|||||||
* Performs http requests using `XMLHttpRequest` as the default backend.
|
* Performs http requests using `XMLHttpRequest` as the default backend.
|
||||||
*
|
*
|
||||||
* `Http` is available as an injectable class, with methods to perform http requests. Calling
|
* `Http` is available as an injectable class, with methods to perform http requests. Calling
|
||||||
* `request` returns an
|
* `request` returns an {@link EventEmitter} which will emit a single {@link Response} when a
|
||||||
* [Observable](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md),
|
*response is
|
||||||
* which will emit a single {@link Response} when a response is
|
|
||||||
* received.
|
* received.
|
||||||
*
|
*
|
||||||
* #Example
|
* #Example
|
||||||
@ -73,7 +81,7 @@ function mergeOptions(defaultOpts, providedOpts, method, url): RequestOptions {
|
|||||||
**/
|
**/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Http {
|
export class Http {
|
||||||
constructor(private _backend: ConnectionBackend, private _defaultOptions: BaseRequestOptions) {}
|
constructor(private _backend: ConnectionBackend, private _defaultOptions: RequestOptions) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs any type of http request. First argument is required, and can either be a url or
|
* Performs any type of http request. First argument is required, and can either be a url or
|
||||||
@ -96,7 +104,7 @@ export class Http {
|
|||||||
/**
|
/**
|
||||||
* Performs a request with `get` http method.
|
* Performs a request with `get` http method.
|
||||||
*/
|
*/
|
||||||
get(url: string, options?: IRequestOptions) {
|
get(url: string, options?: IRequestOptions): EventEmitter {
|
||||||
return httpRequest(this._backend, new Request(mergeOptions(this._defaultOptions, options,
|
return httpRequest(this._backend, new Request(mergeOptions(this._defaultOptions, options,
|
||||||
RequestMethods.GET, url)));
|
RequestMethods.GET, url)));
|
||||||
}
|
}
|
||||||
@ -104,25 +112,27 @@ export class Http {
|
|||||||
/**
|
/**
|
||||||
* Performs a request with `post` http method.
|
* Performs a request with `post` http method.
|
||||||
*/
|
*/
|
||||||
post(url: string, body: string, options?: IRequestOptions) {
|
post(url: string, body: string, options?: IRequestOptions): EventEmitter {
|
||||||
return httpRequest(this._backend,
|
return httpRequest(
|
||||||
new Request(mergeOptions(this._defaultOptions.merge({body: body}), options,
|
this._backend,
|
||||||
RequestMethods.POST, url)));
|
new Request(mergeOptions(this._defaultOptions.merge(new RequestOptions({body: body})),
|
||||||
|
options, RequestMethods.POST, url)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a request with `put` http method.
|
* Performs a request with `put` http method.
|
||||||
*/
|
*/
|
||||||
put(url: string, body: string, options?: IRequestOptions) {
|
put(url: string, body: string, options?: IRequestOptions): EventEmitter {
|
||||||
return httpRequest(this._backend,
|
return httpRequest(
|
||||||
new Request(mergeOptions(this._defaultOptions.merge({body: body}), options,
|
this._backend,
|
||||||
RequestMethods.PUT, url)));
|
new Request(mergeOptions(this._defaultOptions.merge(new RequestOptions({body: body})),
|
||||||
|
options, RequestMethods.PUT, url)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a request with `delete` http method.
|
* Performs a request with `delete` http method.
|
||||||
*/
|
*/
|
||||||
delete (url: string, options?: IRequestOptions) {
|
delete (url: string, options?: IRequestOptions): EventEmitter {
|
||||||
return httpRequest(this._backend, new Request(mergeOptions(this._defaultOptions, options,
|
return httpRequest(this._backend, new Request(mergeOptions(this._defaultOptions, options,
|
||||||
RequestMethods.DELETE, url)));
|
RequestMethods.DELETE, url)));
|
||||||
}
|
}
|
||||||
@ -130,16 +140,17 @@ export class Http {
|
|||||||
/**
|
/**
|
||||||
* Performs a request with `patch` http method.
|
* Performs a request with `patch` http method.
|
||||||
*/
|
*/
|
||||||
patch(url: string, body: string, options?: IRequestOptions) {
|
patch(url: string, body: string, options?: IRequestOptions): EventEmitter {
|
||||||
return httpRequest(this._backend,
|
return httpRequest(
|
||||||
new Request(mergeOptions(this._defaultOptions.merge({body: body}), options,
|
this._backend,
|
||||||
RequestMethods.PATCH, url)));
|
new Request(mergeOptions(this._defaultOptions.merge(new RequestOptions({body: body})),
|
||||||
|
options, RequestMethods.PATCH, url)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a request with `head` http method.
|
* Performs a request with `head` http method.
|
||||||
*/
|
*/
|
||||||
head(url: string, options?: IRequestOptions) {
|
head(url: string, options?: IRequestOptions): EventEmitter {
|
||||||
return httpRequest(this._backend, new Request(mergeOptions(this._defaultOptions, options,
|
return httpRequest(this._backend, new Request(mergeOptions(this._defaultOptions, options,
|
||||||
RequestMethods.HEAD, url)));
|
RequestMethods.HEAD, url)));
|
||||||
}
|
}
|
||||||
|
@ -13,18 +13,31 @@ import {BaseException} from 'angular2/src/facade/lang';
|
|||||||
import {EventEmitter} from 'angular2/src/facade/async';
|
import {EventEmitter} from 'angular2/src/facade/async';
|
||||||
import {Request} from './static_request';
|
import {Request} from './static_request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class from which real backends are derived.
|
||||||
|
*
|
||||||
|
* The primary purpose of a `ConnectionBackend` is to create new connections to fulfill a given
|
||||||
|
* {@link Request}.
|
||||||
|
*/
|
||||||
export class ConnectionBackend {
|
export class ConnectionBackend {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
createConnection(request: any): Connection { throw new BaseException('Abstract!'); }
|
createConnection(request: any): Connection { throw new BaseException('Abstract!'); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class from which real connections are derived.
|
||||||
|
*/
|
||||||
export class Connection {
|
export class Connection {
|
||||||
readyState: ReadyStates;
|
readyState: ReadyStates;
|
||||||
request: Request;
|
request: Request;
|
||||||
response: EventEmitter; //<IResponse>;
|
response: EventEmitter; // TODO: generic of <Response>;
|
||||||
dispose(): void { throw new BaseException('Abstract!'); }
|
dispose(): void { throw new BaseException('Abstract!'); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for options to construct a Request, based on
|
||||||
|
* [RequestInit](https://fetch.spec.whatwg.org/#requestinit) from the Fetch spec.
|
||||||
|
*/
|
||||||
export interface IRequestOptions {
|
export interface IRequestOptions {
|
||||||
url?: string;
|
url?: string;
|
||||||
method?: RequestMethods;
|
method?: RequestMethods;
|
||||||
@ -36,27 +49,16 @@ export interface IRequestOptions {
|
|||||||
cache?: RequestCacheOpts;
|
cache?: RequestCacheOpts;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResponseOptions {
|
/**
|
||||||
|
* Interface for options to construct a Response, based on
|
||||||
|
* [ResponseInit](https://fetch.spec.whatwg.org/#responseinit) from the Fetch spec.
|
||||||
|
*/
|
||||||
|
export interface IResponseOptions {
|
||||||
// TODO: Support Blob, ArrayBuffer, JSON
|
// TODO: Support Blob, ArrayBuffer, JSON
|
||||||
body?: string | Object | FormData;
|
body?: string | Object | FormData;
|
||||||
status?: number;
|
status?: number;
|
||||||
statusText?: string;
|
statusText?: string;
|
||||||
headers?: Headers | Object;
|
headers?: Headers;
|
||||||
type?: ResponseTypes;
|
type?: ResponseTypes;
|
||||||
url?: string;
|
url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IResponse {
|
|
||||||
headers: Headers;
|
|
||||||
ok: boolean;
|
|
||||||
status: number;
|
|
||||||
statusText: string;
|
|
||||||
type: ResponseTypes;
|
|
||||||
url: string;
|
|
||||||
totalBytes: number;
|
|
||||||
bytesLoaded: number;
|
|
||||||
blob(): any; // TODO: Blob
|
|
||||||
arrayBuffer(): any; // TODO: ArrayBuffer
|
|
||||||
text(): string;
|
|
||||||
json(): Object;
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
import {RequestMethods, RequestModesOpts, RequestCredentialsOpts, RequestCacheOpts} from './enums';
|
import {RequestMethods, RequestModesOpts, RequestCredentialsOpts, RequestCacheOpts} from './enums';
|
||||||
import {RequestOptions} from './base_request_options';
|
import {RequestOptions} from './base_request_options';
|
||||||
import {IRequestOptions} from './interfaces';
|
|
||||||
import {Headers} from './headers';
|
import {Headers} from './headers';
|
||||||
import {BaseException, RegExpWrapper, CONST_EXPR, isPresent} from 'angular2/src/facade/lang';
|
import {
|
||||||
import {StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
|
BaseException,
|
||||||
|
RegExpWrapper,
|
||||||
|
CONST_EXPR,
|
||||||
|
isPresent,
|
||||||
|
isJsObject
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
// TODO(jeffbcross): properly implement body accessors
|
// TODO(jeffbcross): properly implement body accessors
|
||||||
/**
|
/**
|
||||||
* Creates `Request` instances with default values.
|
* Creates `Request` instances from provided values.
|
||||||
*
|
*
|
||||||
* The Request's interface is inspired by the Request constructor defined in the [Fetch
|
* The Request's interface is inspired by the Request constructor defined in the [Fetch
|
||||||
* Spec](https://fetch.spec.whatwg.org/#request-class),
|
* Spec](https://fetch.spec.whatwg.org/#request-class),
|
||||||
@ -33,12 +37,8 @@ export class Request {
|
|||||||
// TODO: support URLSearchParams | FormData | Blob | ArrayBuffer
|
// TODO: support URLSearchParams | FormData | Blob | ArrayBuffer
|
||||||
private _body: string;
|
private _body: string;
|
||||||
cache: RequestCacheOpts;
|
cache: RequestCacheOpts;
|
||||||
// TODO(jeffbcross): determine way to add type to destructured args
|
constructor(requestOptions: RequestOptions) {
|
||||||
constructor(options?: IRequestOptions) {
|
// TODO: assert that url is present
|
||||||
var requestOptions: RequestOptions = options instanceof
|
|
||||||
StringMap ? RequestOptions.fromStringWrapper(<StringMap<string, any>>options) :
|
|
||||||
<RequestOptions>options;
|
|
||||||
|
|
||||||
this.url = requestOptions.url;
|
this.url = requestOptions.url;
|
||||||
this._body = requestOptions.body;
|
this._body = requestOptions.body;
|
||||||
this.method = requestOptions.method;
|
this.method = requestOptions.method;
|
||||||
@ -47,10 +47,11 @@ export class Request {
|
|||||||
// Defaults to 'omit', consistent with browser
|
// Defaults to 'omit', consistent with browser
|
||||||
// TODO(jeffbcross): implement behavior
|
// TODO(jeffbcross): implement behavior
|
||||||
this.credentials = requestOptions.credentials;
|
this.credentials = requestOptions.credentials;
|
||||||
this.headers = requestOptions.headers;
|
this.headers = new Headers(requestOptions.headers);
|
||||||
this.cache = requestOptions.cache;
|
this.cache = requestOptions.cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the request's body as string, assuming that body exists. If body is undefined, return
|
* Returns the request's body as string, assuming that body exists. If body is undefined, return
|
||||||
* empty
|
* empty
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
import {IResponse, ResponseOptions} from './interfaces';
|
|
||||||
import {ResponseTypes} from './enums';
|
import {ResponseTypes} from './enums';
|
||||||
import {baseResponseOptions} from './base_response_options';
|
import {
|
||||||
import {BaseException, isJsObject, isString, isPresent, Json} from 'angular2/src/facade/lang';
|
BaseException,
|
||||||
|
CONST_EXPR,
|
||||||
|
isJsObject,
|
||||||
|
isString,
|
||||||
|
isPresent,
|
||||||
|
Json
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
import {Headers} from './headers';
|
import {Headers} from './headers';
|
||||||
|
import {ResponseOptions} from './base_response_options';
|
||||||
|
|
||||||
// TODO: make this injectable so baseResponseOptions can be overridden, mostly for the benefit of
|
|
||||||
// headers merging.
|
|
||||||
/**
|
/**
|
||||||
* Creates `Response` instances with default values.
|
* Creates `Response` instances from provided values.
|
||||||
*
|
*
|
||||||
* Though this object isn't
|
* Though this object isn't
|
||||||
* usually instantiated by end-users, it is the primary object interacted with when it comes time to
|
* usually instantiated by end-users, it is the primary object interacted with when it comes time to
|
||||||
@ -19,12 +23,12 @@ import {Headers} from './headers';
|
|||||||
* http.request('my-friends.txt').subscribe(response => this.friends = response.text());
|
* http.request('my-friends.txt').subscribe(response => this.friends = response.text());
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* The Response's interface is inspired by the Request constructor defined in the [Fetch
|
* The Response's interface is inspired by the Response constructor defined in the [Fetch
|
||||||
* Spec](https://fetch.spec.whatwg.org/#response-class), but is considered a static value whose body
|
* Spec](https://fetch.spec.whatwg.org/#response-class), but is considered a static value whose body
|
||||||
* can be accessed many times. There are other differences in the implementation, but this is the
|
* can be accessed many times. There are other differences in the implementation, but this is the
|
||||||
* most significant.
|
* most significant.
|
||||||
*/
|
*/
|
||||||
export class Response implements IResponse {
|
export class Response {
|
||||||
/**
|
/**
|
||||||
* One of "basic", "cors", "default", "error, or "opaque".
|
* One of "basic", "cors", "default", "error, or "opaque".
|
||||||
*
|
*
|
||||||
@ -74,16 +78,13 @@ export class Response implements IResponse {
|
|||||||
headers: Headers;
|
headers: Headers;
|
||||||
// TODO: Support ArrayBuffer, JSON, FormData, Blob
|
// TODO: Support ArrayBuffer, JSON, FormData, Blob
|
||||||
private _body: string | Object;
|
private _body: string | Object;
|
||||||
constructor({body, status, statusText, headers, type, url}: ResponseOptions = {}) {
|
constructor(responseOptions: ResponseOptions) {
|
||||||
if (isJsObject(headers)) {
|
this._body = responseOptions.body;
|
||||||
headers = new Headers(headers);
|
this.status = responseOptions.status;
|
||||||
}
|
this.statusText = responseOptions.statusText;
|
||||||
this._body = isPresent(body) ? body : baseResponseOptions.body;
|
this.headers = responseOptions.headers;
|
||||||
this.status = isPresent(status) ? status : baseResponseOptions.status;
|
this.type = responseOptions.type;
|
||||||
this.statusText = isPresent(statusText) ? statusText : baseResponseOptions.statusText;
|
this.url = responseOptions.url;
|
||||||
this.headers = isPresent(headers) ? <Headers>headers : baseResponseOptions.headers;
|
|
||||||
this.type = isPresent(type) ? type : baseResponseOptions.type;
|
|
||||||
this.url = isPresent(url) ? url : baseResponseOptions.url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,6 +21,11 @@ function paramParser(rawParams: string): Map<string, List<string>> {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map-like representation of url search parameters, based on
|
||||||
|
* [URLSearchParams](https://url.spec.whatwg.org/#urlsearchparams) in the url living standard.
|
||||||
|
*
|
||||||
|
*/
|
||||||
export class URLSearchParams {
|
export class URLSearchParams {
|
||||||
paramsMap: Map<string, List<string>>;
|
paramsMap: Map<string, List<string>>;
|
||||||
constructor(public rawParams: string) { this.paramsMap = paramParser(rawParams); }
|
constructor(public rawParams: string) { this.paramsMap = paramParser(rawParams); }
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
AsyncTestCompleter,
|
AsyncTestCompleter,
|
||||||
|
afterEach,
|
||||||
beforeEach,
|
beforeEach,
|
||||||
ddescribe,
|
ddescribe,
|
||||||
describe,
|
describe,
|
||||||
@ -10,35 +11,47 @@ import {
|
|||||||
xit,
|
xit,
|
||||||
SpyObject
|
SpyObject
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
import {BrowserXHR} from 'angular2/src/http/backends/browser_xhr';
|
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
|
import {BrowserXhr} from 'angular2/src/http/backends/browser_xhr';
|
||||||
import {XHRConnection, XHRBackend} from 'angular2/src/http/backends/xhr_backend';
|
import {XHRConnection, XHRBackend} from 'angular2/src/http/backends/xhr_backend';
|
||||||
import {bind, Injector} from 'angular2/di';
|
import {bind, Injector} from 'angular2/di';
|
||||||
import {Request} from 'angular2/src/http/static_request';
|
import {Request} from 'angular2/src/http/static_request';
|
||||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
import {Map} from 'angular2/src/facade/collection';
|
||||||
import {RequestOptions} from 'angular2/src/http/base_request_options';
|
import {RequestOptions, BaseRequestOptions} from 'angular2/src/http/base_request_options';
|
||||||
|
import {BaseResponseOptions, ResponseOptions} from 'angular2/src/http/base_response_options';
|
||||||
|
import {ResponseTypes} from 'angular2/src/http/enums';
|
||||||
|
|
||||||
var abortSpy;
|
var abortSpy;
|
||||||
var sendSpy;
|
var sendSpy;
|
||||||
var openSpy;
|
var openSpy;
|
||||||
var addEventListenerSpy;
|
var addEventListenerSpy;
|
||||||
|
var existingXHRs = [];
|
||||||
|
|
||||||
class MockBrowserXHR extends BrowserXHR {
|
class MockBrowserXHR extends BrowserXhr {
|
||||||
abort: any;
|
abort: any;
|
||||||
send: any;
|
send: any;
|
||||||
open: any;
|
open: any;
|
||||||
addEventListener: any;
|
|
||||||
response: any;
|
response: any;
|
||||||
responseText: string;
|
responseText: string;
|
||||||
|
callbacks: Map<string, Function>;
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
var spy = new SpyObject();
|
var spy = new SpyObject();
|
||||||
this.abort = abortSpy = spy.spy('abort');
|
this.abort = abortSpy = spy.spy('abort');
|
||||||
this.send = sendSpy = spy.spy('send');
|
this.send = sendSpy = spy.spy('send');
|
||||||
this.open = openSpy = spy.spy('open');
|
this.open = openSpy = spy.spy('open');
|
||||||
this.addEventListener = addEventListenerSpy = spy.spy('addEventListener');
|
this.callbacks = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
build() { return new MockBrowserXHR(); }
|
addEventListener(type: string, cb: Function) { this.callbacks.set(type, cb); }
|
||||||
|
|
||||||
|
dispatchEvent(type: string) { this.callbacks.get(type)({}); }
|
||||||
|
|
||||||
|
build() {
|
||||||
|
var xhr = new MockBrowserXHR();
|
||||||
|
existingXHRs.push(xhr);
|
||||||
|
return xhr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
@ -47,17 +60,35 @@ export function main() {
|
|||||||
var sampleRequest;
|
var sampleRequest;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
var injector =
|
var injector = Injector.resolveAndCreate([
|
||||||
Injector.resolveAndCreate([bind(BrowserXHR).toClass(MockBrowserXHR), XHRBackend]);
|
bind(ResponseOptions)
|
||||||
|
.toClass(BaseResponseOptions),
|
||||||
|
bind(BrowserXhr).toClass(MockBrowserXHR),
|
||||||
|
XHRBackend
|
||||||
|
]);
|
||||||
backend = injector.get(XHRBackend);
|
backend = injector.get(XHRBackend);
|
||||||
sampleRequest = new Request(new RequestOptions({url: 'https://google.com'}));
|
var base = new BaseRequestOptions();
|
||||||
|
sampleRequest = new Request(base.merge(new RequestOptions({url: 'https://google.com'})));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => { existingXHRs = []; });
|
||||||
|
|
||||||
it('should create a connection',
|
it('should create a connection',
|
||||||
() => { expect(() => backend.createConnection(sampleRequest)).not.toThrow(); });
|
() => { expect(() => backend.createConnection(sampleRequest)).not.toThrow(); });
|
||||||
|
|
||||||
|
|
||||||
describe('XHRConnection', () => {
|
describe('XHRConnection', () => {
|
||||||
|
it('should use the injected BaseResponseOptions to create the response',
|
||||||
|
inject([AsyncTestCompleter], async => {
|
||||||
|
var connection = new XHRConnection(sampleRequest, new MockBrowserXHR(),
|
||||||
|
new ResponseOptions({type: ResponseTypes.Error}));
|
||||||
|
ObservableWrapper.subscribe(connection.response, res => {
|
||||||
|
expect(res.type).toBe(ResponseTypes.Error);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
existingXHRs[0].dispatchEvent('load');
|
||||||
|
}));
|
||||||
|
|
||||||
it('should call abort when disposed', () => {
|
it('should call abort when disposed', () => {
|
||||||
var connection = new XHRConnection(sampleRequest, new MockBrowserXHR());
|
var connection = new XHRConnection(sampleRequest, new MockBrowserXHR());
|
||||||
connection.dispose();
|
connection.dispose();
|
||||||
@ -73,7 +104,9 @@ export function main() {
|
|||||||
|
|
||||||
it('should automatically call send on the backend with request body', () => {
|
it('should automatically call send on the backend with request body', () => {
|
||||||
var body = 'Some body to love';
|
var body = 'Some body to love';
|
||||||
new XHRConnection(new Request(new RequestOptions({body: body})), new MockBrowserXHR());
|
var base = new BaseRequestOptions();
|
||||||
|
new XHRConnection(new Request(base.merge(new RequestOptions({body: body}))),
|
||||||
|
new MockBrowserXHR());
|
||||||
expect(sendSpy).toHaveBeenCalledWith(body);
|
expect(sendSpy).toHaveBeenCalledWith(body);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -9,22 +9,22 @@ import {
|
|||||||
it,
|
it,
|
||||||
xit
|
xit
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
import {BaseRequestOptions} from 'angular2/src/http/base_request_options';
|
import {BaseRequestOptions, RequestOptions} from 'angular2/src/http/base_request_options';
|
||||||
import {RequestMethods, RequestModesOpts} from 'angular2/src/http/enums';
|
import {RequestMethods, RequestModesOpts} from 'angular2/src/http/enums';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('BaseRequestOptions', () => {
|
describe('BaseRequestOptions', () => {
|
||||||
it('should create a new object when calling merge', () => {
|
it('should create a new object when calling merge', () => {
|
||||||
var options1 = new BaseRequestOptions();
|
var options1 = new BaseRequestOptions();
|
||||||
var options2 = options1.merge({method: RequestMethods.DELETE});
|
var options2 = options1.merge(new RequestOptions({method: RequestMethods.DELETE}));
|
||||||
expect(options2).not.toBe(options1);
|
expect(options2).not.toBe(options1);
|
||||||
expect(options2.method).toBe(RequestMethods.DELETE);
|
expect(options2.method).toBe(RequestMethods.DELETE);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should retain previously merged values when merging again', () => {
|
it('should retain previously merged values when merging again', () => {
|
||||||
var options1 = new BaseRequestOptions();
|
var options1 = new BaseRequestOptions();
|
||||||
var options2 = options1.merge({method: RequestMethods.DELETE});
|
var options2 = options1.merge(new RequestOptions({method: RequestMethods.DELETE}));
|
||||||
var options3 = options2.merge({mode: RequestModesOpts.NoCors});
|
var options3 = options2.merge(new RequestOptions({mode: RequestModesOpts.NoCors}));
|
||||||
expect(options3.mode).toBe(RequestModesOpts.NoCors);
|
expect(options3.mode).toBe(RequestModesOpts.NoCors);
|
||||||
expect(options3.method).toBe(RequestMethods.DELETE);
|
expect(options3.method).toBe(RequestMethods.DELETE);
|
||||||
});
|
});
|
||||||
|
@ -17,6 +17,7 @@ import {MockBackend} from 'angular2/src/http/backends/mock_backend';
|
|||||||
import {Response} from 'angular2/src/http/static_response';
|
import {Response} from 'angular2/src/http/static_response';
|
||||||
import {RequestMethods} from 'angular2/src/http/enums';
|
import {RequestMethods} from 'angular2/src/http/enums';
|
||||||
import {BaseRequestOptions, RequestOptions} from 'angular2/src/http/base_request_options';
|
import {BaseRequestOptions, RequestOptions} from 'angular2/src/http/base_request_options';
|
||||||
|
import {ResponseOptions} from 'angular2/src/http/base_response_options';
|
||||||
import {Request} from 'angular2/src/http/static_request';
|
import {Request} from 'angular2/src/http/static_request';
|
||||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
import {ConnectionBackend} from 'angular2/src/http/interfaces';
|
import {ConnectionBackend} from 'angular2/src/http/interfaces';
|
||||||
@ -52,7 +53,7 @@ export function main() {
|
|||||||
]);
|
]);
|
||||||
http = injector.get(Http);
|
http = injector.get(Http);
|
||||||
backend = injector.get(MockBackend);
|
backend = injector.get(MockBackend);
|
||||||
baseResponse = new Response({body: 'base response'});
|
baseResponse = new Response(new ResponseOptions({body: 'base response'}));
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => backend.verifyNoPendingRequests());
|
afterEach(() => backend.verifyNoPendingRequests());
|
||||||
@ -67,7 +68,7 @@ export function main() {
|
|||||||
inject([AsyncTestCompleter], (async) => {
|
inject([AsyncTestCompleter], (async) => {
|
||||||
ObservableWrapper.subscribe(backend.connections, c => {
|
ObservableWrapper.subscribe(backend.connections, c => {
|
||||||
expect(c.request.url).toBe('https://google.com');
|
expect(c.request.url).toBe('https://google.com');
|
||||||
c.mockRespond(new Response({body: 'Thank you'}));
|
c.mockRespond(new Response(new ResponseOptions({body: 'Thank you'})));
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
ObservableWrapper.subscribe(
|
ObservableWrapper.subscribe(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {bootstrap, Component, View, NgFor, Inject} from 'angular2/angular2';
|
import {Component, View, NgFor} from 'angular2/angular2';
|
||||||
|
import {Http} from 'angular2/http';
|
||||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
import {Http, httpInjectables} from 'angular2/http';
|
|
||||||
|
|
||||||
@Component({selector: 'http-app'})
|
@Component({selector: 'http-app'})
|
||||||
@View({
|
@View({
|
||||||
@ -19,4 +19,4 @@ export class HttpCmp {
|
|||||||
constructor(http: Http) {
|
constructor(http: Http) {
|
||||||
ObservableWrapper.subscribe(http.get('./people.json'), res => this.people = res.json());
|
ObservableWrapper.subscribe(http.get('./people.json'), res => this.people = res.json());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
/// <reference path="../../../angular2/typings/rx/rx.all.d.ts" />
|
/// <reference path="../../../angular2/typings/rx/rx.all.d.ts" />
|
||||||
|
|
||||||
import {
|
import {bootstrap} from 'angular2/angular2';
|
||||||
bootstrap,
|
|
||||||
} from 'angular2/angular2';
|
|
||||||
import {reflector} from 'angular2/src/reflection/reflection';
|
import {reflector} from 'angular2/src/reflection/reflection';
|
||||||
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
|
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
|
||||||
import {httpInjectables} from 'angular2/http';
|
import {httpInjectables} from 'angular2/http';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user