Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support path in the base url #23

Merged
merged 9 commits into from
Dec 2, 2017
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions lib/RestClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ export class RestClient {
/**
* Gets a resource from an endpoint
* Be aware that not found returns a null. Other error conditions reject the promise
* @param {string} requestUrl - fully qualified or relative url
* @param {string} resource - fully qualified url or relative path
* @param {IRequestOptions} requestOptions - (optional) requestOptions object
*/
public async get<T>(requestUrl: string,
public async get<T>(resource: string,
options?: IRequestOptions): Promise<IRestResponse<T>> {

let url: string = util.getUrl(requestUrl, this._baseUrl);
let url: string = util.getUrl(resource, this._baseUrl);
let res: httpm.HttpClientResponse = await this.client.get(url,
this._headersFromOptions(options));
return this._processResponse<T>(res, options);
Expand All @@ -79,13 +79,13 @@ export class RestClient {
/**
* Deletes a resource from an endpoint
* Be aware that not found returns a null. Other error conditions reject the promise
* @param {string} requestUrl - fully qualified or relative url
* @param {string} resource - fully qualified or relative url
* @param {IRequestOptions} requestOptions - (optional) requestOptions object
*/
public async del<T>(requestUrl: string,
public async del<T>(resource: string,
options?: IRequestOptions): Promise<IRestResponse<T>> {

let url: string = util.getUrl(requestUrl, this._baseUrl);
let url: string = util.getUrl(resource, this._baseUrl);
let res: httpm.HttpClientResponse = await this.client.del(url,
this._headersFromOptions(options));
return this._processResponse<T>(res, options);
Expand All @@ -95,14 +95,14 @@ export class RestClient {
* Creates resource(s) from an endpoint
* T type of object returned.
* Be aware that not found returns a null. Other error conditions reject the promise
* @param {string} requestUrl - fully qualified or relative url
* @param {string} resource - fully qualified or relative url
* @param {IRequestOptions} requestOptions - (optional) requestOptions object
*/
public async create<T>(requestUrl: string,
public async create<T>(resource: string,
resources: any,
options?: IRequestOptions): Promise<IRestResponse<T>> {

let url: string = util.getUrl(requestUrl, this._baseUrl);
let url: string = util.getUrl(resource, this._baseUrl);
let headers: ifm.IHeaders = this._headersFromOptions(options, true);

let data: string = JSON.stringify(resources, null, 2);
Expand All @@ -114,14 +114,14 @@ export class RestClient {
* Updates resource(s) from an endpoint
* T type of object returned.
* Be aware that not found returns a null. Other error conditions reject the promise
* @param {string} requestUrl - fully qualified or relative url
* @param {string} resource - fully qualified or relative url
* @param {IRequestOptions} requestOptions - (optional) requestOptions object
*/
public async update<T>(requestUrl: string,
public async update<T>(resource: string,
resources: any,
options?: IRequestOptions): Promise<IRestResponse<T>> {

let url: string = util.getUrl(requestUrl, this._baseUrl);
let url: string = util.getUrl(resource, this._baseUrl);
let headers: ifm.IHeaders = this._headersFromOptions(options, true);

let data: string = JSON.stringify(resources, null, 2);
Expand All @@ -133,14 +133,14 @@ export class RestClient {
* Replaces resource(s) from an endpoint
* T type of object returned.
* Be aware that not found returns a null. Other error conditions reject the promise
* @param {string} requestUrl - fully qualified or relative url
* @param {string} resource - fully qualified or relative url
* @param {IRequestOptions} requestOptions - (optional) requestOptions object
*/
public async replace<T>(requestUrl: string,
public async replace<T>(resource: string,
resources: any,
options?: IRequestOptions): Promise<IRestResponse<T>> {

let url: string = util.getUrl(requestUrl, this._baseUrl);
let url: string = util.getUrl(resource, this._baseUrl);
let headers: ifm.IHeaders = this._headersFromOptions(options, true);

let data: string = JSON.stringify(resources, null, 2);
Expand Down
24 changes: 14 additions & 10 deletions lib/Util.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import * as url from 'url';
import * as path from 'path';

/**
* creates an url from a request url and optional base url (http://server:8080)
* @param {string} requestUrl - a fully qualified url or relative url
* @param {string} resource - a fully qualified url or relative path
* @param {string} baseUrl - an optional baseUrl (http://server:8080)
* @return {string} - resultant url
*/
export function getUrl(requestUrl: string, baseUrl?: string): string {
export function getUrl(resource: string, baseUrl?: string): string {
if (!baseUrl) {
return requestUrl;
return resource;
}

let base: url.Url = url.parse(baseUrl);

// requestUrl (specific per request) always wins
let combined: url.Url = url.parse(requestUrl);
combined.protocol = combined.protocol || base.protocol;
combined.auth = combined.auth || base.auth;
combined.host = combined.host || base.host;
// path from requestUrl always wins
// resource (specific per request) eliments take priority
let resultantUrl: url.Url = url.parse(resource);
resultantUrl.protocol = resultantUrl.protocol || base.protocol;
resultantUrl.auth = resultantUrl.auth || base.auth;
resultantUrl.host = resultantUrl.host || base.host;

let basePathComponent: string = base.pathname === '/' ? '': base.pathname;
resultantUrl.pathname = path.posix.join(basePathComponent, resultantUrl.pathname);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use resolve. drive letter on windows not a problem. If you do something illogical like using a drive letter on windows, you will get a failure which is expected. We're optimizing for the positive rooted and unrooted cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what it's doing now.


let res: string = url.format(resultantUrl);

let res: string = url.format(combined);
return res;
}
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "typed-rest-client",
"version": "0.15.1",
"version": "1.0.0",
"description": "Node Rest and Http Clients for use with TypeScript",
"main": "./RestClient.js",
"scripts": {
Expand Down
117 changes: 115 additions & 2 deletions test/resttests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@

import assert = require('assert');
import * as restm from 'typed-rest-client/RestClient';
import * as util from 'typed-rest-client/Util';
import * as fs from 'fs';
import * as path from 'path';

export interface HttpBinData {
url: string;
data: any;
args?: any
}

describe('Rest Tests', function () {
Expand Down Expand Up @@ -84,7 +86,118 @@ describe('Rest Tests', function () {
assert(err['statusCode'] == 500, "statusCode should be 500");
assert(err.message && err.message.length > 0, "should have error message");
}
});
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add divider

//--------------------------------------------------------
// Path tests
//--------------------------------------------------------

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

//--------------------------------------------------------
// Path in baseUrl tests
//--------------------------------------------------------
it('maintains the path from the base url', async() => {
// Arrange
let rest = new restm.RestClient('typed-rest-client-tests', 'https://httpbin.org/anything');

// Act
let restRes: restm.IRestResponse<HttpBinData> = await rest.get<HttpBinData>('/anythingextra');

// Assert
assert(restRes.statusCode == 200, "statusCode should be 200");
assert(restRes.result.url === 'https://httpbin.org/anything/anythingextra');
});

it('maintains the path from the base url with no slashes', async() => {
// Arrange
let rest = new restm.RestClient('typed-rest-client-tests', 'https://httpbin.org/anything');

// Act
let restRes: restm.IRestResponse<HttpBinData> = await rest.get<HttpBinData>('anythingextra');

// Assert
assert(restRes.statusCode == 200, "statusCode should be 200");
assert(restRes.result.url === 'https://httpbin.org/anything/anythingextra');
});

it('maintains the path from the base url with double slashes', async() => {
// Arrange
let rest = new restm.RestClient('typed-rest-client-tests', 'https://httpbin.org/anything/');

// Act
let restRes: restm.IRestResponse<HttpBinData> = await rest.get<HttpBinData>('/anythingextra');

// Assert
assert(restRes.statusCode == 200, "statusCode should be 200");
assert(restRes.result.url === 'https://httpbin.org/anything/anythingextra');
});

it('maintains the path from the base url with multiple parts', async() => {
// Arrange
let rest = new restm.RestClient('typed-rest-client-tests', 'https://httpbin.org/anything/extrapart');

// Act
let restRes: restm.IRestResponse<HttpBinData> = await rest.get<HttpBinData>('/anythingextra');

// Assert
assert(restRes.statusCode == 200, "statusCode should be 200");
assert(restRes.result.url === 'https://httpbin.org/anything/extrapart/anythingextra');
});

it('maintains the path from the base url where request has multiple parts', async() => {
// Arrange
let rest = new restm.RestClient('typed-rest-client-tests', 'https://httpbin.org/anything');

// Act
let restRes: restm.IRestResponse<HttpBinData> = await rest.get<HttpBinData>('/anythingextra/moreparts');

// Assert
assert(restRes.statusCode == 200, "statusCode should be 200");
assert(restRes.result.url === 'https://httpbin.org/anything/anythingextra/moreparts');
});

it('maintains the path from the base url where both have multiple parts', async() => {
// Arrange
let rest = new restm.RestClient('typed-rest-client-tests', 'https://httpbin.org/anything/multiple');

// Act
let restRes: restm.IRestResponse<HttpBinData> = await rest.get<HttpBinData>('/anythingextra/moreparts');

// Assert
assert(restRes.statusCode == 200, "statusCode should be 200");
assert(restRes.result.url === 'https://httpbin.org/anything/multiple/anythingextra/moreparts');
});

it('maintains the path from the base url where request has query parameters', async() => {
// Arrange
let rest = new restm.RestClient('typed-rest-client-tests', 'https://httpbin.org/anything/multiple');

// Act
let restRes: restm.IRestResponse<HttpBinData> = await rest.get<HttpBinData>('/anythingextra/moreparts?foo=bar&baz=top');

// Assert
assert(restRes.statusCode == 200, "statusCode should be 200");
assert(restRes.result.url === 'https://httpbin.org/anything/multiple/anythingextra/moreparts?foo=bar&baz=top');
assert(restRes.result.args.foo === 'bar');
assert(restRes.result.args.baz === 'top');
});

//
// getUrl path tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're missing this test.

'/get/foo', 'http://httpbin.org/bar'

should result in 'http://httpbin.org/get/foo'

//
it('resolves a just host resource and no baseUrl', async() => {
let res: string = util.getUrl('http://httpbin.org');
assert(res === 'http://httpbin.org', "should be http://httpbin.org");
});

it('resolves a full resource and no baseUrl', async() => {
let res: string = util.getUrl('http://httpbin.org/get?x=y&a=b');
assert(res === 'http://httpbin.org/get?x=y&a=b', `should be http://httpbin.org/get?x=y&a=b but is ${res}`);
});

it('resolves a rooted path resource with host baseUrl', async() => {
let res: string = util.getUrl('/get/foo', 'http://httpbin.org');
assert(res === 'http://httpbin.org/get/foo', `should be http://httpbin.org/get/foo but is ${res}`);
});

it('resolves a relative path resource with pathed baseUrl', async() => {
let res: string = util.getUrl('get/foo', 'http://httpbin.org/bar');
assert(res === 'http://httpbin.org/bar/get/foo', `should be http://httpbin.org/bar/get/foo but is ${res}`);
});
// TODO: more tests here
});
});