-
Notifications
You must be signed in to change notification settings - Fork 25.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This implementation only works in JavaScript, while the Observable transpilation story gets worked out. Right now, the service just makes a simple request, and returns an Observable of Response. Additional functionality will be captured in separate issues. Fixes #2028
- Loading branch information
1 parent
363b9ba
commit 2156810
Showing
35 changed files
with
1,054 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import {bind, Binding} from 'angular2/di'; | ||
import {Http, HttpFactory} from './src/http/http'; | ||
import {XHRBackend} from 'angular2/src/http/backends/xhr_backend'; | ||
import {BrowserXHR} from 'angular2/src/http/backends/browser_xhr'; | ||
|
||
export {Http}; | ||
export var httpInjectables: List<any> = [ | ||
XHRBackend, | ||
bind(BrowserXHR).toValue(BrowserXHR), | ||
bind(Http).toFactory(HttpFactory, [XHRBackend]) | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
library angular2.src.http.backends.browser_xhr; | ||
|
||
/// import 'dart:html' show HttpRequest; | ||
/// import 'package:angular2/di.dart'; | ||
/// @Injectable() | ||
/// class BrowserXHR { | ||
/// factory BrowserXHR() => new HttpRequest(); | ||
/// } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
declare var window; | ||
|
||
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()); } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import {Injectable} from 'angular2/di'; | ||
import {Request} from 'angular2/src/http/static_request'; | ||
import {Response} from 'angular2/src/http/static_response'; | ||
import {ReadyStates} from 'angular2/src/http/enums'; | ||
import * as Rx from 'rx'; | ||
|
||
/** | ||
* Connection represents a request and response for an underlying transport, like XHR or mock. | ||
* The mock implementation contains helper methods to respond to connections within tests. | ||
* API subject to change and expand. | ||
**/ | ||
export class Connection { | ||
/** | ||
* Observer to call on download progress, if provided in config. | ||
**/ | ||
downloadObserver: Rx.Observer<Response>; | ||
|
||
/** | ||
* TODO | ||
* Name `readyState` should change to be more generic, and states could be made to be more | ||
* descriptive than XHR states. | ||
**/ | ||
|
||
readyState: ReadyStates; | ||
request: Request; | ||
response: Rx.Subject<Response>; | ||
|
||
constructor(req: Request) { | ||
// State | ||
if (Rx.hasOwnProperty('default')) { | ||
this.response = new ((<any>Rx).default.Rx.Subject)(); | ||
} else { | ||
this.response = new Rx.Subject<Response>(); | ||
} | ||
|
||
this.readyState = ReadyStates.OPEN; | ||
this.request = req; | ||
this.dispose = this.dispose.bind(this); | ||
} | ||
|
||
dispose() { | ||
if (this.readyState !== ReadyStates.DONE) { | ||
this.readyState = ReadyStates.CANCELLED; | ||
} | ||
} | ||
|
||
/** | ||
* Called after a connection has been established. | ||
**/ | ||
mockRespond(res: Response) { | ||
if (this.readyState >= ReadyStates.DONE) { | ||
throw new Error('Connection has already been resolved'); | ||
} | ||
this.readyState = ReadyStates.DONE; | ||
this.response.onNext(res); | ||
this.response.onCompleted(); | ||
} | ||
|
||
mockDownload(res: Response) { | ||
this.downloadObserver.onNext(res); | ||
if (res.bytesLoaded === res.totalBytes) { | ||
this.downloadObserver.onCompleted(); | ||
} | ||
} | ||
|
||
mockError(err?) { | ||
// Matches XHR semantics | ||
this.readyState = ReadyStates.DONE; | ||
this.response.onError(err); | ||
this.response.onCompleted(); | ||
} | ||
} | ||
|
||
@Injectable() | ||
export class MockBackend { | ||
connections: Rx.Subject<Connection>; | ||
connectionsArray: Array<Connection>; | ||
pendingConnections: Rx.Observable<Connection>; | ||
constructor() { | ||
this.connectionsArray = []; | ||
if (Rx.hasOwnProperty('default')) { | ||
this.connections = new (<any>Rx).default.Rx.Subject(); | ||
} else { | ||
this.connections = new Rx.Subject<Connection>(); | ||
} | ||
this.connections.subscribe(connection => this.connectionsArray.push(connection)); | ||
this.pendingConnections = this.connections.filter((c) => c.readyState < ReadyStates.DONE); | ||
} | ||
|
||
verifyNoPendingRequests() { | ||
let pending = 0; | ||
this.pendingConnections.subscribe((c) => pending++); | ||
if (pending > 0) throw new Error(`${pending} pending connections to be resolved`); | ||
} | ||
|
||
resolveAllConnections() { this.connections.subscribe((c) => c.readyState = 4); } | ||
|
||
createConnection(req: Request) { | ||
if (!req || !(req instanceof Request)) { | ||
throw new Error(`createConnection requires an instance of Request, got ${req}`); | ||
} | ||
let connection = new Connection(req); | ||
this.connections.onNext(connection); | ||
return connection; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import {ConnectionBackend, Connection} from '../interfaces'; | ||
import {ReadyStates, RequestMethods} 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'; | ||
|
||
export class XHRConnection implements Connection { | ||
request: Request; | ||
response: Rx.Subject<Response>; | ||
readyState: ReadyStates; | ||
private _xhr; | ||
constructor(req: Request, NativeConstruct: any) { | ||
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._xhr.open(RequestMethods[req.method], req.url); | ||
this._xhr.addEventListener( | ||
'load', | ||
() => {this.response.onNext(new Response(this._xhr.response || this._xhr.responseText))}); | ||
// TODO(jeffbcross): make this more dynamic based on body type | ||
this._xhr.send(this.request.text()); | ||
} | ||
|
||
dispose(): void { this._xhr.abort(); } | ||
} | ||
|
||
@Injectable() | ||
export class XHRBackend implements ConnectionBackend { | ||
constructor(private _NativeConstruct: BrowserXHR) {} | ||
createConnection(request: Request): XHRConnection { | ||
return new XHRConnection(request, this._NativeConstruct); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import {CONST_EXPR, CONST} from 'angular2/src/facade/lang'; | ||
import {Headers} from './headers'; | ||
import {URLSearchParams} from './url_search_params'; | ||
import {RequestModesOpts, RequestMethods, RequestCacheOpts, RequestCredentialsOpts} from './enums'; | ||
import {RequestOptions} from './interfaces'; | ||
import {Injectable} from 'angular2/di'; | ||
|
||
@Injectable() | ||
export class BaseRequestOptions implements RequestOptions { | ||
method: RequestMethods; | ||
headers: Headers; | ||
body: URLSearchParams | FormData | string; | ||
mode: RequestModesOpts; | ||
credentials: RequestCredentialsOpts; | ||
cache: RequestCacheOpts; | ||
|
||
constructor() { | ||
this.method = RequestMethods.GET; | ||
this.mode = RequestModesOpts.Cors; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import {Headers} from './headers'; | ||
import {ResponseTypes} from './enums'; | ||
import {ResponseOptions} from './interfaces'; | ||
|
||
export class BaseResponseOptions implements ResponseOptions { | ||
status: number; | ||
headers: Headers | Object; | ||
statusText: string; | ||
type: ResponseTypes; | ||
url: string; | ||
|
||
constructor({status = 200, statusText = 'Ok', type = ResponseTypes.Default, | ||
headers = new Headers(), url = ''}: ResponseOptions = {}) { | ||
this.status = status; | ||
this.statusText = statusText; | ||
this.type = type; | ||
this.headers = headers; | ||
this.url = url; | ||
} | ||
} | ||
; | ||
|
||
export var baseResponseOptions = Object.freeze(new BaseResponseOptions()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
|
||
export enum RequestModesOpts { Cors, NoCors, SameOrigin }; | ||
|
||
export enum RequestCacheOpts { Default, NoStore, Reload, NoCache, ForceCache, OnlyIfCached }; | ||
|
||
export enum RequestCredentialsOpts { Omit, SameOrigin, Include }; | ||
|
||
export enum RequestMethods { GET, POST, PUT, DELETE, OPTIONS, HEAD }; | ||
|
||
export enum ReadyStates { UNSENT, OPEN, HEADERS_RECEIVED, LOADING, DONE, CANCELLED }; | ||
|
||
export enum ResponseTypes { Basic, Cors, Default, Error, Opaque } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { | ||
isPresent, | ||
isBlank, | ||
isJsObject, | ||
isType, | ||
StringWrapper, | ||
BaseException | ||
} from 'angular2/src/facade/lang'; | ||
import { | ||
isListLikeIterable, | ||
List, | ||
Map, | ||
MapWrapper, | ||
ListWrapper | ||
} from 'angular2/src/facade/collection'; | ||
|
||
// (@jeffbcross): This is implemented mostly to spec, except that the entries method has been | ||
// removed because it doesn't exist in dart, and it doesn't seem worth adding it to the facade. | ||
|
||
export class Headers { | ||
_headersMap: Map<string, List<string>>; | ||
constructor(headers?: Headers | Object) { | ||
if (isBlank(headers)) { | ||
this._headersMap = MapWrapper.create(); | ||
return; | ||
} | ||
|
||
if (isPresent((<Headers>headers)._headersMap)) { | ||
this._headersMap = (<Headers>headers)._headersMap; | ||
} else if (isJsObject(headers)) { | ||
this._headersMap = MapWrapper.createFromStringMap(headers); | ||
MapWrapper.forEach(this._headersMap, (v, k) => { | ||
if (!isListLikeIterable(v)) { | ||
var list = ListWrapper.create(); | ||
ListWrapper.push(list, v); | ||
MapWrapper.set(this._headersMap, k, list); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
append(name: string, value: string): void { | ||
var list = MapWrapper.get(this._headersMap, name) || ListWrapper.create(); | ||
ListWrapper.push(list, value); | ||
MapWrapper.set(this._headersMap, name, list); | ||
} | ||
|
||
delete (name: string): void { MapWrapper.delete(this._headersMap, name); } | ||
|
||
forEach(fn: Function) { return MapWrapper.forEach(this._headersMap, fn); } | ||
|
||
get(header: string): string { | ||
return ListWrapper.first(MapWrapper.get(this._headersMap, header)); | ||
} | ||
|
||
has(header: string) { return MapWrapper.contains(this._headersMap, header); } | ||
|
||
keys() { return MapWrapper.keys(this._headersMap); } | ||
|
||
// TODO: this implementation seems wrong. create list then check if it's iterable? | ||
set(header: string, value: string | List<string>): void { | ||
var list = ListWrapper.create(); | ||
if (!isListLikeIterable(value)) { | ||
ListWrapper.push(list, value); | ||
} else { | ||
ListWrapper.push(list, ListWrapper.toString((<List<string>>value))); | ||
} | ||
|
||
MapWrapper.set(this._headersMap, header, list); | ||
} | ||
|
||
values() { return MapWrapper.values(this._headersMap); } | ||
|
||
getAll(header: string): Array<string> { | ||
return MapWrapper.get(this._headersMap, header) || ListWrapper.create(); | ||
} | ||
|
||
entries() { throw new BaseException('"entries" method is not implemented on Headers class'); } | ||
} |
Oops, something went wrong.
2156810
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 :)
2156810
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<3