Skip to content

Commit

Permalink
feat(http): refactor library to work in dart
Browse files Browse the repository at this point in the history
Mostly internal refactoring needed to make ts2dart and DartAnalyzer happy.

Fixes #2415
  • Loading branch information
jeffbcross committed Jun 30, 2015
1 parent fa7da0c commit 55bf0e5
Show file tree
Hide file tree
Showing 26 changed files with 422 additions and 377 deletions.
1 change: 0 additions & 1 deletion modules/angular2/http.dart

This file was deleted.

13 changes: 9 additions & 4 deletions modules/angular2/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,24 @@ import {Http, HttpFactory} from './src/http/http';
import {XHRBackend, XHRConnection} from 'angular2/src/http/backends/xhr_backend';
import {BrowserXHR} from 'angular2/src/http/backends/browser_xhr';
import {BaseRequestOptions, RequestOptions} from 'angular2/src/http/base_request_options';
import {ConnectionBackend} from 'angular2/src/http/interfaces';

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

export {Http, XHRBackend, XHRConnection, BaseRequestOptions, RequestOptions, HttpFactory};
export {
IHttp,
IRequestOptions,
IRequest,
IResponse,
Connection,
ConnectionBackend
} from 'angular2/src/http/interfaces';

export {BaseRequestOptions, RequestOptions} from 'angular2/src/http/base_request_options';
export {XHRBackend, XHRConnection} from 'angular2/src/http/backends/xhr_backend';
export {Http, HttpFactory} from './src/http/http';

export {Headers} from 'angular2/src/http/headers';

export * from 'angular2/src/http/enums';
Expand All @@ -47,8 +51,9 @@ export {URLSearchParams} from 'angular2/src/http/url_search_params';
*
*/
export var httpInjectables: List<any> = [
bind(BrowserXHR)
.toValue(BrowserXHR),
bind(ConnectionBackend)
.toClass(XHRBackend),
BrowserXHR,
XHRBackend,
BaseRequestOptions,
bind(HttpFactory).toFactory(HttpFactory, [XHRBackend, BaseRequestOptions]),
Expand Down
2 changes: 2 additions & 0 deletions modules/angular2/src/facade/lang.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class Math {
static double random() => _random.nextDouble();
}

int ENUM_INDEX(value) => value.index;

class CONST {
const CONST();
}
Expand Down
5 changes: 5 additions & 0 deletions modules/angular2/src/facade/lang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ _global.assert = function assert(condition) {
_global['assert'].call(condition);
}
};

export function ENUM_INDEX(value: int): int {
return value;
}

// This function is needed only to properly support Dart's const expressions
// see https://github.com/angular/ts2dart/pull/151 for more info
export function CONST_EXPR<T>(expr: T): T {
Expand Down
14 changes: 8 additions & 6 deletions modules/angular2/src/http/backends/browser_xhr.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
library angular2.src.http.backends.browser_xhr;

/// import 'dart:html' show HttpRequest;
/// import 'package:angular2/di.dart';
import 'dart:html' show HttpRequest;
import 'package:angular2/di.dart';

/// @Injectable()
/// class BrowserXHR {
/// factory BrowserXHR() => new HttpRequest();
/// }
@Injectable()
class BrowserXHR {
HttpRequest build() {
return new HttpRequest();
}
}
3 changes: 2 additions & 1 deletion modules/angular2/src/http/backends/browser_xhr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ import {Injectable} from 'angular2/di';
// Make sure not to evaluate this in a non-browser environment!
@Injectable()
export class BrowserXHR {
constructor() { return <any>(new window.XMLHttpRequest()); }
constructor() {}
build(): any { return <any>(new window.XMLHttpRequest()); }
}
66 changes: 29 additions & 37 deletions modules/angular2/src/http/backends/mock_backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import {Request} from 'angular2/src/http/static_request';
import {Response} from 'angular2/src/http/static_response';
import {ReadyStates} from 'angular2/src/http/enums';
import {Connection, ConnectionBackend} from 'angular2/src/http/interfaces';
import * as Rx from 'rx';
import {ObservableWrapper, EventEmitter} from 'angular2/src/facade/async';
import {isPresent} from 'angular2/src/facade/lang';
import {IMPLEMENTS, BaseException} from 'angular2/src/facade/lang';

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

constructor(req: Request) {
if (Rx.hasOwnProperty('default')) {
this.response = new ((<any>Rx).default.Rx.Subject)();
} else {
this.response = new Rx.Subject<Response>();
}

this.response = new EventEmitter();
this.readyState = ReadyStates.OPEN;
this.request = req;
this.dispose = this.dispose.bind(this);
}

/**
Expand All @@ -71,12 +68,12 @@ export class MockConnection implements Connection {
*
*/
mockRespond(res: Response) {
if (this.readyState >= ReadyStates.DONE) {
throw new Error('Connection has already been resolved');
if (this.readyState === ReadyStates.DONE || this.readyState === ReadyStates.CANCELLED) {
throw new BaseException('Connection has already been resolved');
}
this.readyState = ReadyStates.DONE;
this.response.onNext(res);
this.response.onCompleted();
ObservableWrapper.callNext(this.response, res);
ObservableWrapper.callReturn(this.response);
}

/**
Expand All @@ -100,8 +97,8 @@ export class MockConnection implements Connection {
mockError(err?) {
// Matches XHR semantics
this.readyState = ReadyStates.DONE;
this.response.onError(err);
this.response.onCompleted();
ObservableWrapper.callThrow(this.response, err);
ObservableWrapper.callReturn(this.response);
}
}

Expand Down Expand Up @@ -137,7 +134,8 @@ export class MockConnection implements Connection {
* This method only exists in the mock implementation, not in real Backends.
**/
@Injectable()
export class MockBackend implements ConnectionBackend {
@IMPLEMENTS(ConnectionBackend)
export class MockBackend {
/**
* [RxJS
* Subject](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/subject.md)
Expand Down Expand Up @@ -171,7 +169,7 @@ export class MockBackend implements ConnectionBackend {
*
* This property only exists in the mock implementation, not in real Backends.
*/
connections: Rx.Subject<MockConnection>;
connections: EventEmitter; //<MockConnection>

/**
* An array representation of `connections`. This array will be updated with each connection that
Expand All @@ -188,20 +186,14 @@ export class MockBackend implements ConnectionBackend {
*
* This property only exists in the mock implementation, not in real Backends.
*/
pendingConnections: Rx.Observable<MockConnection>;
pendingConnections: EventEmitter; //<MockConnection>
constructor() {
var Observable;
this.connectionsArray = [];
if (Rx.hasOwnProperty('default')) {
this.connections = new (<any>Rx).default.Rx.Subject();
Observable = (<any>Rx).default.Rx.Observable;
} else {
this.connections = new Rx.Subject<MockConnection>();
Observable = Rx.Observable;
}
this.connections.subscribe(connection => this.connectionsArray.push(connection));
this.pendingConnections =
Observable.fromArray(this.connectionsArray).filter((c) => c.readyState < ReadyStates.DONE);
this.connections = new EventEmitter();
ObservableWrapper.subscribe(this.connections,
connection => this.connectionsArray.push(connection));
this.pendingConnections = new EventEmitter();
// Observable.fromArray(this.connectionsArray).filter((c) => c.readyState < ReadyStates.DONE);
}

/**
Expand All @@ -211,8 +203,8 @@ export class MockBackend implements ConnectionBackend {
*/
verifyNoPendingRequests() {
let pending = 0;
this.pendingConnections.subscribe((c) => pending++);
if (pending > 0) throw new Error(`${pending} pending connections to be resolved`);
ObservableWrapper.subscribe(this.pendingConnections, c => pending++);
if (pending > 0) throw new BaseException(`${pending} pending connections to be resolved`);
}

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

/**
* Creates a new {@link MockConnection}. This is equivalent to calling `new
* 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
* against the framework itself, not by end-users.
*/
createConnection(req: Request): Connection {
if (!req || !(req instanceof Request)) {
throw new Error(`createConnection requires an instance of Request, got ${req}`);
createConnection(req: Request) {
if (!isPresent(req) || !(req instanceof Request)) {
throw new BaseException(`createConnection requires an instance of Request, got ${req}`);
}
let connection = new MockConnection(req);
this.connections.onNext(connection);
ObservableWrapper.callNext(this.connections, connection);
return connection;
}
}
32 changes: 17 additions & 15 deletions modules/angular2/src/http/backends/xhr_backend.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {ConnectionBackend, Connection} from '../interfaces';
import {ReadyStates, RequestMethods} from '../enums';
import {ReadyStates, RequestMethods, RequestMethodsMap} from '../enums';
import {Request} from '../static_request';
import {Response} from '../static_response';
import {Inject} from 'angular2/di';
import {Injectable} from 'angular2/di';
import {BrowserXHR} from './browser_xhr';
import * as Rx from 'rx';
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
import {isPresent, ENUM_INDEX} from 'angular2/src/facade/lang';

/**
* Creates connections using `XMLHttpRequest`. Given a fully-qualified
Expand All @@ -22,22 +22,24 @@ export class XHRConnection implements Connection {
* [Subject](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/subject.md)
* which emits a single {@link Response} value on load event of `XMLHttpRequest`.
*/
response: Rx.Subject<Response>;
response: EventEmitter; //<Response>;
readyState: ReadyStates;
private _xhr;
constructor(req: Request, NativeConstruct: any) {
constructor(req: Request, browserXHR: BrowserXHR) {
// TODO: get rid of this when enum lookups are available in ts2dart
// https://github.com/angular/ts2dart/issues/221
var requestMethodsMap = new RequestMethodsMap();
this.request = req;
if (Rx.hasOwnProperty('default')) {
this.response = new (<any>Rx).default.Rx.Subject();
} else {
this.response = new Rx.Subject<Response>();
}
this._xhr = new NativeConstruct();
this.response = new EventEmitter();
this._xhr = browserXHR.build();
// TODO(jeffbcross): implement error listening/propagation
this._xhr.open(RequestMethods[req.method], req.url);
this._xhr.open(requestMethodsMap.getMethod(ENUM_INDEX(req.method)), req.url);
this._xhr.addEventListener(
'load',
() => {this.response.onNext(new Response(this._xhr.response || this._xhr.responseText))});
(_) => {ObservableWrapper.callNext(
this.response, new Response({
body: isPresent(this._xhr.response) ? this._xhr.response : this._xhr.responseText
}))});
// TODO(jeffbcross): make this more dynamic based on body type
this._xhr.send(this.request.text());
}
Expand Down Expand Up @@ -76,8 +78,8 @@ export class XHRConnection implements Connection {
**/
@Injectable()
export class XHRBackend implements ConnectionBackend {
constructor(private _NativeConstruct: BrowserXHR) {}
constructor(private _browserXHR: BrowserXHR) {}
createConnection(request: Request): XHRConnection {
return new XHRConnection(request, this._NativeConstruct);
return new XHRConnection(request, this._browserXHR);
}
}
43 changes: 32 additions & 11 deletions modules/angular2/src/http/base_request_options.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {CONST_EXPR, CONST, isPresent} from 'angular2/src/facade/lang';
import {Headers} from './headers';
import {URLSearchParams} from './url_search_params';
import {RequestModesOpts, RequestMethods, RequestCacheOpts, RequestCredentialsOpts} from './enums';
import {IRequestOptions} from './interfaces';
import {Injectable} from 'angular2/di';
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
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
* Spec](https://fetch.spec.whatwg.org/#requestinit) to be optionally provided when instantiating a
Expand All @@ -28,28 +28,49 @@ export class RequestOptions implements IRequestOptions {
/**
* Body to be used when creating the request.
*/
body: URLSearchParams | FormData | Blob | string;
// TODO: support FormData, Blob, URLSearchParams, JSON
body: string;
mode: RequestModesOpts = RequestModesOpts.Cors;
credentials: RequestCredentialsOpts;
cache: RequestCacheOpts;
constructor({method, headers, body, mode, credentials, cache}: IRequestOptions = {
method: RequestMethods.GET,
mode: RequestModesOpts.Cors
}) {
this.method = method;
url: string;
constructor({method, headers, body, mode, credentials, cache, url}: IRequestOptions = {}) {
this.method = isPresent(method) ? method : RequestMethods.GET;
this.headers = headers;
this.body = body;
this.mode = mode;
this.mode = isPresent(mode) ? mode : RequestModesOpts.Cors;
this.credentials = credentials;
this.cache = cache;
this.url = url;
}

/**
* Creates a copy of the `RequestOptions` instance, using the optional input as values to override
* existing values.
*/
merge(opts: IRequestOptions = {}): RequestOptions {
return new RequestOptions(StringMapWrapper.merge(this, opts));
merge({url = null, method = null, headers = null, body = null, mode = null, credentials = null,
cache = null}: any = {}): RequestOptions {
return new RequestOptions({
method: isPresent(method) ? method : this.method,
headers: isPresent(headers) ? headers : this.headers,
body: isPresent(body) ? body : this.body,
mode: isPresent(mode) ? mode : this.mode,
credentials: isPresent(credentials) ? credentials : this.credentials,
cache: isPresent(cache) ? cache : this.cache,
url: isPresent(url) ? 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')
})
}
}

Expand Down
Loading

0 comments on commit 55bf0e5

Please sign in to comment.