Skip to content

Commit

Permalink
refactor: Extract common http methods
Browse files Browse the repository at this point in the history
  • Loading branch information
vakrilov committed Oct 3, 2017
1 parent cc5f210 commit 557510e
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 123 deletions.
52 changes: 52 additions & 0 deletions nativescript-angular/http-client/http-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { NSFileSystem } from "../file-system/ns-file-system";

import { Observable } from "rxjs/Observable";
import { Observer } from "rxjs/Observer";
import { path } from "tns-core-modules/file-system/file-system";

export type httpResponseFactory<T> = (url: string, body: any, status: number) => T;
export type httpErrorFactory = (url: string, body: any, status: number) => any;

export function isLocalRequest(url: string): boolean {
return url.indexOf("~") === 0 || url.indexOf("/") === 0;
}

export function getAbsolutePath(url: string, nsFileSystem: NSFileSystem): string {
url = url.replace("~", "").replace("/", "");
url = path.join(nsFileSystem.currentApp().path, url);
return url;
}

export function handleLocalRequest<T>(
url: string,
nsFileSystem: NSFileSystem,
successResponse: httpResponseFactory<T>,
errorResponse: httpErrorFactory): Observable<T> {

url = getAbsolutePath(url, nsFileSystem);

// request from local app resources
return new Observable((observer: Observer<T>) => {
if (nsFileSystem.fileExists(url)) {
const localFile = nsFileSystem.fileFromPath(url);
localFile.readText()
.then((data) => {
try {
const json = JSON.parse(data);
observer.next(successResponse(url, json, 200));
observer.complete();
} catch (error) {
// Even though the response status was 2xx, this is still an error.
// The parse error contains the text of the body that failed to parse.
const errorResult = { error, text: data };
observer.error(errorResponse(url, errorResult, 200));
}
}, (err: Object) => {
observer.error(errorResponse(url, err, 400));

});
} else {
observer.error(errorResponse(url, "Not Found", 404));
}
});
}
55 changes: 15 additions & 40 deletions nativescript-angular/http-client/ns-http-backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import {
HttpErrorResponse, HttpXhrBackend
} from "@angular/common/http";
import { Observable } from "rxjs/Observable";
import { Observer } from "rxjs/Observer";

import { NSFileSystem } from "../file-system/ns-file-system";
import { path } from "tns-core-modules/file-system";
import { isLocalRequest, handleLocalRequest } from "./http-utils";

@Injectable()
export class NsHttpBackEnd extends HttpXhrBackend {
Expand All @@ -28,47 +27,20 @@ export class NsHttpBackEnd extends HttpXhrBackend {
return result;
}

private getAbsolutePath(url: string): string {
url = url.replace("~", "").replace("/", "");
url = path.join(this.nsFileSystem.currentApp().path, url);
return url;
}

private handleLocalRequest(url: string): Observable<HttpEvent<any>> {
url = this.getAbsolutePath(url);

// request from local app resources
return new Observable((observer: Observer<HttpEvent<any>>) => {
if (this.nsFileSystem.fileExists(url)) {
const localFile = this.nsFileSystem.fileFromPath(url);
localFile.readText()
.then((data) => {
try {
const json = JSON.parse(data);
observer.next(createSuccessResponse(url, json, 200));
observer.complete();
} catch (error) {
// Even though the response status was 2xx, this is still an error.
// The parse error contains the text of the body that failed to parse.
const errorResult = { error, text: data };
observer.error(createErrorResponse(url, errorResult, 200));
}
}, (err: Object) => {
observer.error(createErrorResponse(url, err, 400));

});
} else {
observer.error(createErrorResponse(url, "Not Found", 404));
}
});
return handleLocalRequest(
url,
this.nsFileSystem,
createSuccessResponse,
createErrorResponse
);
}
}

function isLocalRequest(url: string): boolean {
return url.indexOf("~") === 0 || url.indexOf("/") === 0;
}

function createSuccessResponse(url: string, body: any, status: number): HttpEvent<any> {
function createSuccessResponse(
url: string,
body: any,
status: number): HttpEvent<any> {
return new HttpResponse({
url,
body,
Expand All @@ -77,7 +49,10 @@ function createSuccessResponse(url: string, body: any, status: number): HttpEven
});
}

function createErrorResponse(url: string, body: any, status: number): HttpErrorResponse {
function createErrorResponse(
url: string,
body: any,
status: number): HttpErrorResponse {
return new HttpErrorResponse({
url,
error: body,
Expand Down
45 changes: 13 additions & 32 deletions nativescript-angular/http/ns-http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import {
} from "@angular/http";
import { Observable } from "rxjs/Observable";
import "rxjs/add/observable/fromPromise";

import { isLocalRequest, handleLocalRequest } from "../http-client/http-utils";

import { NSFileSystem } from "../file-system/ns-file-system";

export class NSXSRFStrategy {
Expand All @@ -31,7 +34,7 @@ export class NSHttp extends Http {
request(req: string | Request, options?: RequestOptionsArgs): Observable<Response> {
const urlString = typeof req === "string" ? req : req.url;
if (isLocalRequest(urlString)) {
return this._requestLocalUrl(urlString);
return this.handleLocalRequest(urlString);
} else {
return super.request(req, options);
}
Expand All @@ -42,45 +45,23 @@ export class NSHttp extends Http {
*/
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
if (isLocalRequest(url)) {
return this._requestLocalUrl(url);
return this.handleLocalRequest(url);
} else {
return super.get(url, options);
}
}

/**
* Uses a local file if `~/` resource is requested.
* @param url
*/
private _requestLocalUrl(url: string): Observable<Response> {
url = normalizeLocalUrl(url);

// request from local app resources
return Observable.fromPromise<Response>(new Promise((resolve, reject) => {
let app = this.nsFileSystem.currentApp();
let localFile = app.getFile(url);
if (localFile) {
localFile.readText().then((data) => {
resolve(responseOptions(data, 200, url));
}, (err: Object) => {
reject(responseOptions(err, 400, url));
});
} else {
reject(responseOptions("Not Found", 404, url));
}
}));
private handleLocalRequest(url: string): Observable<Response> {
return handleLocalRequest(
url,
this.nsFileSystem,
createResponse,
createResponse
);
}
}

function isLocalRequest(url: string): boolean {
return url.indexOf("~") === 0 || url.indexOf("/") === 0;
}

function normalizeLocalUrl(url: string): string {
return url.replace("~", "").replace("/", "");
}

function responseOptions(body: string | Object, status: number, url: string): Response {
function createResponse(url: string, body: string | Object, status: number): Response {
return new Response(new ResponseOptions({
body: body,
status: status,
Expand Down
53 changes: 38 additions & 15 deletions tests/app/tests/http.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
// make sure you import mocha-config before @angular/core
import {assert} from "./test-config";
import { assert } from "./test-config";
import {
async,
inject,
} from "@angular/core/testing";
import {ReflectiveInjector} from "@angular/core";
import {Request, BaseRequestOptions, ConnectionBackend, Http, Response, ResponseOptions} from "@angular/http";
import { ReflectiveInjector, Injectable } from "@angular/core";
import { Request, BaseRequestOptions, ConnectionBackend, Http, Response, ResponseOptions } from "@angular/http";
import "rxjs/add/operator/map";
import {MockBackend} from "@angular/http/testing";
import {NSHttp} from "nativescript-angular/http/ns-http";
import {NSFileSystem} from "nativescript-angular/file-system/ns-file-system";
import {NSFileSystemMock, FileResponses} from "./mocks/ns-file-system.mock";
import { MockBackend } from "@angular/http/testing";
import { NSHttp } from "nativescript-angular/http/ns-http";
import { NSFileSystem } from "nativescript-angular/file-system/ns-file-system";

const AWESOME_TEAM: string = '[{"name":"Alex"}, {"name":"Rosen"}, {"name":"Panayot"}]';

// Filesystem mock
@Injectable()
export class NSFileSystemMock implements NSFileSystem {
public currentApp(): any {
return { path: "/app/dir" };
}

public fileFromPath(path: string): any {
if (path === "/app/dir/test.json") {
return {
readText: () => { return Promise.resolve(AWESOME_TEAM); }
};
}
throw new Error("Opening non-existing file");
}

public fileExists(path: string): boolean {
return path === "/app/dir/test.json";
}

}

describe("Http", () => {
let http: Http;
Expand All @@ -22,15 +45,15 @@ describe("Http", () => {
MockBackend,
{ provide: NSFileSystem, useClass: NSFileSystemMock },
{
provide: Http,
useFactory: function (
connectionBackend: ConnectionBackend,
defaultOptions: BaseRequestOptions,
nsFileSystem: NSFileSystem) {
provide: Http,
useFactory: function (
connectionBackend: ConnectionBackend,
defaultOptions: BaseRequestOptions,
nsFileSystem: NSFileSystem) {
// HACK: cast backend to any to work around an angular typings problem
return new NSHttp(<any>connectionBackend, defaultOptions, nsFileSystem);
},
deps: [MockBackend, BaseRequestOptions, NSFileSystem]
},
deps: [MockBackend, BaseRequestOptions, NSFileSystem]
}
]);

Expand Down Expand Up @@ -78,6 +101,6 @@ describe("Http", () => {
assert.strictEqual(3, response.length);
assert.strictEqual("Rosen", response[1].name);
});
connection.mockRespond(new Response(new ResponseOptions({ body: FileResponses.AWESOME_TEAM })));
connection.mockRespond(new Response(new ResponseOptions({ body: AWESOME_TEAM })));
});
});
36 changes: 0 additions & 36 deletions tests/app/tests/mocks/ns-file-system.mock.ts

This file was deleted.

0 comments on commit 557510e

Please sign in to comment.