Skip to content

Commit 55bf0e5

Browse files
committed
feat(http): refactor library to work in dart
Mostly internal refactoring needed to make ts2dart and DartAnalyzer happy. Fixes angular#2415
1 parent fa7da0c commit 55bf0e5

26 files changed

+422
-377
lines changed

modules/angular2/http.dart

Lines changed: 0 additions & 1 deletion
This file was deleted.

modules/angular2/http.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,24 @@ import {Http, HttpFactory} from './src/http/http';
1010
import {XHRBackend, XHRConnection} from 'angular2/src/http/backends/xhr_backend';
1111
import {BrowserXHR} from 'angular2/src/http/backends/browser_xhr';
1212
import {BaseRequestOptions, RequestOptions} from 'angular2/src/http/base_request_options';
13+
import {ConnectionBackend} from 'angular2/src/http/interfaces';
1314

1415
export {MockConnection, MockBackend} from 'angular2/src/http/backends/mock_backend';
1516
export {Request} from 'angular2/src/http/static_request';
1617
export {Response} from 'angular2/src/http/static_response';
1718

18-
export {Http, XHRBackend, XHRConnection, BaseRequestOptions, RequestOptions, HttpFactory};
1919
export {
2020
IHttp,
2121
IRequestOptions,
22-
IRequest,
2322
IResponse,
2423
Connection,
2524
ConnectionBackend
2625
} from 'angular2/src/http/interfaces';
26+
27+
export {BaseRequestOptions, RequestOptions} from 'angular2/src/http/base_request_options';
28+
export {XHRBackend, XHRConnection} from 'angular2/src/http/backends/xhr_backend';
29+
export {Http, HttpFactory} from './src/http/http';
30+
2731
export {Headers} from 'angular2/src/http/headers';
2832

2933
export * from 'angular2/src/http/enums';
@@ -47,8 +51,9 @@ export {URLSearchParams} from 'angular2/src/http/url_search_params';
4751
*
4852
*/
4953
export var httpInjectables: List<any> = [
50-
bind(BrowserXHR)
51-
.toValue(BrowserXHR),
54+
bind(ConnectionBackend)
55+
.toClass(XHRBackend),
56+
BrowserXHR,
5257
XHRBackend,
5358
BaseRequestOptions,
5459
bind(HttpFactory).toFactory(HttpFactory, [XHRBackend, BaseRequestOptions]),

modules/angular2/src/facade/lang.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ class Math {
1111
static double random() => _random.nextDouble();
1212
}
1313

14+
int ENUM_INDEX(value) => value.index;
15+
1416
class CONST {
1517
const CONST();
1618
}

modules/angular2/src/facade/lang.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ _global.assert = function assert(condition) {
3131
_global['assert'].call(condition);
3232
}
3333
};
34+
35+
export function ENUM_INDEX(value: int): int {
36+
return value;
37+
}
38+
3439
// This function is needed only to properly support Dart's const expressions
3540
// see https://github.com/angular/ts2dart/pull/151 for more info
3641
export function CONST_EXPR<T>(expr: T): T {
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
library angular2.src.http.backends.browser_xhr;
22

3-
/// import 'dart:html' show HttpRequest;
4-
/// import 'package:angular2/di.dart';
3+
import 'dart:html' show HttpRequest;
4+
import 'package:angular2/di.dart';
55

6-
/// @Injectable()
7-
/// class BrowserXHR {
8-
/// factory BrowserXHR() => new HttpRequest();
9-
/// }
6+
@Injectable()
7+
class BrowserXHR {
8+
HttpRequest build() {
9+
return new HttpRequest();
10+
}
11+
}

modules/angular2/src/http/backends/browser_xhr.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ import {Injectable} from 'angular2/di';
55
// Make sure not to evaluate this in a non-browser environment!
66
@Injectable()
77
export class BrowserXHR {
8-
constructor() { return <any>(new window.XMLHttpRequest()); }
8+
constructor() {}
9+
build(): any { return <any>(new window.XMLHttpRequest()); }
910
}

modules/angular2/src/http/backends/mock_backend.ts

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import {Request} from 'angular2/src/http/static_request';
33
import {Response} from 'angular2/src/http/static_response';
44
import {ReadyStates} from 'angular2/src/http/enums';
55
import {Connection, ConnectionBackend} from 'angular2/src/http/interfaces';
6-
import * as Rx from 'rx';
6+
import {ObservableWrapper, EventEmitter} from 'angular2/src/facade/async';
7+
import {isPresent} from 'angular2/src/facade/lang';
8+
import {IMPLEMENTS, BaseException} from 'angular2/src/facade/lang';
79

810
/**
911
*
@@ -14,7 +16,8 @@ import * as Rx from 'rx';
1416
* {@link MockBackend} in order to mock responses to requests.
1517
*
1618
**/
17-
export class MockConnection implements Connection {
19+
@IMPLEMENTS(Connection)
20+
export class MockConnection {
1821
// TODO Name `readyState` should change to be more generic, and states could be made to be more
1922
// descriptive than XHR states.
2023
/**
@@ -33,18 +36,12 @@ export class MockConnection implements Connection {
3336
* Observable](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md)
3437
* of {@link Response}. Can be subscribed to in order to be notified when a response is available.
3538
*/
36-
response: Rx.Subject<Response>;
39+
response: EventEmitter;
3740

3841
constructor(req: Request) {
39-
if (Rx.hasOwnProperty('default')) {
40-
this.response = new ((<any>Rx).default.Rx.Subject)();
41-
} else {
42-
this.response = new Rx.Subject<Response>();
43-
}
44-
42+
this.response = new EventEmitter();
4543
this.readyState = ReadyStates.OPEN;
4644
this.request = req;
47-
this.dispose = this.dispose.bind(this);
4845
}
4946

5047
/**
@@ -71,12 +68,12 @@ export class MockConnection implements Connection {
7168
*
7269
*/
7370
mockRespond(res: Response) {
74-
if (this.readyState >= ReadyStates.DONE) {
75-
throw new Error('Connection has already been resolved');
71+
if (this.readyState === ReadyStates.DONE || this.readyState === ReadyStates.CANCELLED) {
72+
throw new BaseException('Connection has already been resolved');
7673
}
7774
this.readyState = ReadyStates.DONE;
78-
this.response.onNext(res);
79-
this.response.onCompleted();
75+
ObservableWrapper.callNext(this.response, res);
76+
ObservableWrapper.callReturn(this.response);
8077
}
8178

8279
/**
@@ -100,8 +97,8 @@ export class MockConnection implements Connection {
10097
mockError(err?) {
10198
// Matches XHR semantics
10299
this.readyState = ReadyStates.DONE;
103-
this.response.onError(err);
104-
this.response.onCompleted();
100+
ObservableWrapper.callThrow(this.response, err);
101+
ObservableWrapper.callReturn(this.response);
105102
}
106103
}
107104

@@ -137,7 +134,8 @@ export class MockConnection implements Connection {
137134
* This method only exists in the mock implementation, not in real Backends.
138135
**/
139136
@Injectable()
140-
export class MockBackend implements ConnectionBackend {
137+
@IMPLEMENTS(ConnectionBackend)
138+
export class MockBackend {
141139
/**
142140
* [RxJS
143141
* Subject](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/subject.md)
@@ -171,7 +169,7 @@ export class MockBackend implements ConnectionBackend {
171169
*
172170
* This property only exists in the mock implementation, not in real Backends.
173171
*/
174-
connections: Rx.Subject<MockConnection>;
172+
connections: EventEmitter; //<MockConnection>
175173

176174
/**
177175
* An array representation of `connections`. This array will be updated with each connection that
@@ -188,20 +186,14 @@ export class MockBackend implements ConnectionBackend {
188186
*
189187
* This property only exists in the mock implementation, not in real Backends.
190188
*/
191-
pendingConnections: Rx.Observable<MockConnection>;
189+
pendingConnections: EventEmitter; //<MockConnection>
192190
constructor() {
193-
var Observable;
194191
this.connectionsArray = [];
195-
if (Rx.hasOwnProperty('default')) {
196-
this.connections = new (<any>Rx).default.Rx.Subject();
197-
Observable = (<any>Rx).default.Rx.Observable;
198-
} else {
199-
this.connections = new Rx.Subject<MockConnection>();
200-
Observable = Rx.Observable;
201-
}
202-
this.connections.subscribe(connection => this.connectionsArray.push(connection));
203-
this.pendingConnections =
204-
Observable.fromArray(this.connectionsArray).filter((c) => c.readyState < ReadyStates.DONE);
192+
this.connections = new EventEmitter();
193+
ObservableWrapper.subscribe(this.connections,
194+
connection => this.connectionsArray.push(connection));
195+
this.pendingConnections = new EventEmitter();
196+
// Observable.fromArray(this.connectionsArray).filter((c) => c.readyState < ReadyStates.DONE);
205197
}
206198

207199
/**
@@ -211,8 +203,8 @@ export class MockBackend implements ConnectionBackend {
211203
*/
212204
verifyNoPendingRequests() {
213205
let pending = 0;
214-
this.pendingConnections.subscribe((c) => pending++);
215-
if (pending > 0) throw new Error(`${pending} pending connections to be resolved`);
206+
ObservableWrapper.subscribe(this.pendingConnections, c => pending++);
207+
if (pending > 0) throw new BaseException(`${pending} pending connections to be resolved`);
216208
}
217209

218210
/**
@@ -221,20 +213,20 @@ export class MockBackend implements ConnectionBackend {
221213
*
222214
* This method only exists in the mock implementation, not in real Backends.
223215
*/
224-
resolveAllConnections() { this.connections.subscribe((c) => c.readyState = 4); }
216+
resolveAllConnections() { ObservableWrapper.subscribe(this.connections, c => c.readyState = 4); }
225217

226218
/**
227219
* Creates a new {@link MockConnection}. This is equivalent to calling `new
228220
* MockConnection()`, except that it also will emit the new `Connection` to the `connections`
229221
* observable of this `MockBackend` instance. This method will usually only be used by tests
230222
* against the framework itself, not by end-users.
231223
*/
232-
createConnection(req: Request): Connection {
233-
if (!req || !(req instanceof Request)) {
234-
throw new Error(`createConnection requires an instance of Request, got ${req}`);
224+
createConnection(req: Request) {
225+
if (!isPresent(req) || !(req instanceof Request)) {
226+
throw new BaseException(`createConnection requires an instance of Request, got ${req}`);
235227
}
236228
let connection = new MockConnection(req);
237-
this.connections.onNext(connection);
229+
ObservableWrapper.callNext(this.connections, connection);
238230
return connection;
239231
}
240232
}

modules/angular2/src/http/backends/xhr_backend.ts

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import {ConnectionBackend, Connection} from '../interfaces';
2-
import {ReadyStates, RequestMethods} from '../enums';
2+
import {ReadyStates, RequestMethods, RequestMethodsMap} from '../enums';
33
import {Request} from '../static_request';
44
import {Response} from '../static_response';
5-
import {Inject} from 'angular2/di';
65
import {Injectable} from 'angular2/di';
76
import {BrowserXHR} from './browser_xhr';
8-
import * as Rx from 'rx';
7+
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
8+
import {isPresent, ENUM_INDEX} from 'angular2/src/facade/lang';
99

1010
/**
1111
* Creates connections using `XMLHttpRequest`. Given a fully-qualified
@@ -22,22 +22,24 @@ export class XHRConnection implements Connection {
2222
* [Subject](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/subject.md)
2323
* which emits a single {@link Response} value on load event of `XMLHttpRequest`.
2424
*/
25-
response: Rx.Subject<Response>;
25+
response: EventEmitter; //<Response>;
2626
readyState: ReadyStates;
2727
private _xhr;
28-
constructor(req: Request, NativeConstruct: any) {
28+
constructor(req: Request, browserXHR: BrowserXHR) {
29+
// TODO: get rid of this when enum lookups are available in ts2dart
30+
// https://github.com/angular/ts2dart/issues/221
31+
var requestMethodsMap = new RequestMethodsMap();
2932
this.request = req;
30-
if (Rx.hasOwnProperty('default')) {
31-
this.response = new (<any>Rx).default.Rx.Subject();
32-
} else {
33-
this.response = new Rx.Subject<Response>();
34-
}
35-
this._xhr = new NativeConstruct();
33+
this.response = new EventEmitter();
34+
this._xhr = browserXHR.build();
3635
// TODO(jeffbcross): implement error listening/propagation
37-
this._xhr.open(RequestMethods[req.method], req.url);
36+
this._xhr.open(requestMethodsMap.getMethod(ENUM_INDEX(req.method)), req.url);
3837
this._xhr.addEventListener(
3938
'load',
40-
() => {this.response.onNext(new Response(this._xhr.response || this._xhr.responseText))});
39+
(_) => {ObservableWrapper.callNext(
40+
this.response, new Response({
41+
body: isPresent(this._xhr.response) ? this._xhr.response : this._xhr.responseText
42+
}))});
4143
// TODO(jeffbcross): make this more dynamic based on body type
4244
this._xhr.send(this.request.text());
4345
}
@@ -76,8 +78,8 @@ export class XHRConnection implements Connection {
7678
**/
7779
@Injectable()
7880
export class XHRBackend implements ConnectionBackend {
79-
constructor(private _NativeConstruct: BrowserXHR) {}
81+
constructor(private _browserXHR: BrowserXHR) {}
8082
createConnection(request: Request): XHRConnection {
81-
return new XHRConnection(request, this._NativeConstruct);
83+
return new XHRConnection(request, this._browserXHR);
8284
}
8385
}

modules/angular2/src/http/base_request_options.ts

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import {CONST_EXPR, CONST, isPresent} from 'angular2/src/facade/lang';
22
import {Headers} from './headers';
3-
import {URLSearchParams} from './url_search_params';
43
import {RequestModesOpts, RequestMethods, RequestCacheOpts, RequestCredentialsOpts} from './enums';
54
import {IRequestOptions} from './interfaces';
65
import {Injectable} from 'angular2/di';
7-
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
6+
import {ListWrapper, StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
87

8+
const INITIALIZER = CONST_EXPR({});
99
/**
1010
* Creates a request options object with default properties as described in the [Fetch
1111
* Spec](https://fetch.spec.whatwg.org/#requestinit) to be optionally provided when instantiating a
@@ -28,28 +28,49 @@ export class RequestOptions implements IRequestOptions {
2828
/**
2929
* Body to be used when creating the request.
3030
*/
31-
body: URLSearchParams | FormData | Blob | string;
31+
// TODO: support FormData, Blob, URLSearchParams, JSON
32+
body: string;
3233
mode: RequestModesOpts = RequestModesOpts.Cors;
3334
credentials: RequestCredentialsOpts;
3435
cache: RequestCacheOpts;
35-
constructor({method, headers, body, mode, credentials, cache}: IRequestOptions = {
36-
method: RequestMethods.GET,
37-
mode: RequestModesOpts.Cors
38-
}) {
39-
this.method = method;
36+
url: string;
37+
constructor({method, headers, body, mode, credentials, cache, url}: IRequestOptions = {}) {
38+
this.method = isPresent(method) ? method : RequestMethods.GET;
4039
this.headers = headers;
4140
this.body = body;
42-
this.mode = mode;
41+
this.mode = isPresent(mode) ? mode : RequestModesOpts.Cors;
4342
this.credentials = credentials;
4443
this.cache = cache;
44+
this.url = url;
4545
}
4646

4747
/**
4848
* Creates a copy of the `RequestOptions` instance, using the optional input as values to override
4949
* existing values.
5050
*/
51-
merge(opts: IRequestOptions = {}): RequestOptions {
52-
return new RequestOptions(StringMapWrapper.merge(this, opts));
51+
merge({url = null, method = null, headers = null, body = null, mode = null, credentials = null,
52+
cache = null}: any = {}): RequestOptions {
53+
return new RequestOptions({
54+
method: isPresent(method) ? method : this.method,
55+
headers: isPresent(headers) ? headers : this.headers,
56+
body: isPresent(body) ? body : this.body,
57+
mode: isPresent(mode) ? mode : this.mode,
58+
credentials: isPresent(credentials) ? credentials : this.credentials,
59+
cache: isPresent(cache) ? cache : this.cache,
60+
url: isPresent(url) ? url : this.url
61+
});
62+
}
63+
64+
static fromStringWrapper(map: StringMap<string, any>): RequestOptions {
65+
return new RequestOptions({
66+
method: StringMapWrapper.get(map, 'method'),
67+
headers: StringMapWrapper.get(map, 'headers'),
68+
body: StringMapWrapper.get(map, 'body'),
69+
mode: StringMapWrapper.get(map, 'mode'),
70+
credentials: StringMapWrapper.get(map, 'credentials'),
71+
cache: StringMapWrapper.get(map, 'cache'),
72+
url:<string>StringMapWrapper.get(map, 'url')
73+
})
5374
}
5475
}
5576

0 commit comments

Comments
 (0)