Permalink
Browse files

feat(project): replace defaults, auto-convert objects for x-ww-form-u…

…rlencoded and json. allow form and multi-form data

BREAKING CHANGE: registerEndpoint - defaults will now completely replace standard deafults. Needed  to submit multipart/form-data.
  • Loading branch information...
doktordirk committed May 30, 2016
1 parent 840b3c5 commit c68faffc64dc62205ec0218e24579ac7d152b2fe
Showing with 111 additions and 22 deletions.
  1. +5 −3 README.md
  2. +16 −0 build/tasks/server.js
  3. +4 −5 src/config.js
  4. +9 −7 src/rest.js
  5. +1 −2 test/config.spec.js
  6. +3 −2 test/resources/inject-test.js
  7. +73 −3 test/rest.spec.js
@@ -46,8 +46,8 @@ aurelia.use
.plugin('aurelia-api', config => {
// Register hosts
config.registerEndpoint('api');
config.registerEndpoint('other-api');
config.registerEndpoint('api', '/mypath');
config.registerEndpoint('other-api', '/otherpath', {headers: {'Content-Type': 'x-www-form-urlencoded'}});
})
```

@@ -66,7 +66,7 @@ export class MyClass {
this.apiEndpoint.find('users')
.then(users => {
// use your recieved users.json
// use your received users.json
})
.catch(console.error);
}
@@ -75,6 +75,8 @@ export class MyClass {

## Quick Rest api overview

All methods will when the body passed as an object stringify it if the `Content-Type` is `application/json`, resp. convert it to a querystring format if the `Content-Type` is `application/x-www-form-urlencoded`.

````js
endpoint
.client // the httpClient instance
@@ -3,9 +3,25 @@ var bodyParser = require('body-parser');
var cors = require('cors');
var server;

var multer = require('multer')
var storage = multer.memoryStorage()
var upload = multer({ storage: storage })

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());

app.post('/uploads', upload.single(), function(req, res) {
res.send({
path: req.path,
query: req.query,
body: req.body,
method: req.method,
contentType: req.header('content-type'),
Authorization: req.header('Authorization')
});
});

app.all('*', function(req, res) {
res.send({
path: req.path,
@@ -1,6 +1,5 @@
import {HttpClient} from 'aurelia-fetch-client';
import {Rest} from './rest';
import extend from 'extend';

export class Config {
endpoints = {};
@@ -11,17 +10,17 @@ export class Config {
*
* @param {string} name The name of the new endpoint.
* @param {function|string} [configureMethod] Configure method or endpoint.
* @param {{}} [defaults] Defaults for the HttpClient
* @param {{}} [defaults] New defaults for the HttpClient
*
* @see http://aurelia.io/docs.html#/aurelia/fetch-client/latest/doc/api/class/HttpClientConfiguration
* @return {Config}
*/
registerEndpoint(name, configureMethod, defaults = {}) {
registerEndpoint(name, configureMethod, defaults) {
let newClient = new HttpClient();
this.endpoints[name] = new Rest(newClient, name);

// add custom defaults to Rest
extend(true, this.endpoints[name].defaults, defaults);
// set custom defaults to Rest
if (defaults !== undefined) this.endpoints[name].defaults = defaults;

// Manual configure of client.
if (typeof configureMethod === 'function') {
@@ -1,4 +1,3 @@
import {json} from 'aurelia-fetch-client';
import qs from 'qs';
import extend from 'extend';

@@ -33,12 +32,15 @@ export class Rest {
* @return {Promise}
*/
request(method, path, body, options = {}) {
let requestOptions = extend(true, {}, this.defaults, options);

requestOptions.method = method;

if (typeof body === 'object') {
requestOptions.body = json(body);
let requestOptions = extend(true, {}, this.defaults, options, {method, body});

if (typeof body === 'object'
&& !(typeof FormData === 'function' && body instanceof FormData)) {
if (requestOptions.headers['Content-Type'].search('application/x-www-form-urlencoded') !== -1) {
requestOptions.body = qs.stringify(body);
} else if (requestOptions.headers['Content-Type'].search('application/json') !== -1) {
requestOptions.body = JSON.stringify(body);
}
}

return this.client.fetch(path, requestOptions).then(response => {
@@ -1,6 +1,5 @@
import {HttpClient} from 'aurelia-fetch-client';
import {Config, Rest} from '../src/aurelia-api';
import extend from 'extend';

describe('Config', function() {
describe('.registerEndpoint()', function() {
@@ -38,7 +37,7 @@ describe('Config', function() {
let config = new Config;
let returned = config.registerEndpoint('api', baseUrls.api, userOptions);

expect(config.endpoints.api.defaults).toEqual(extend(true, {}, defaultOptions, userOptions));
expect(config.endpoints.api.defaults).toEqual(userOptions);
expect(config.endpoints.api.client.baseUrl).toEqual(baseUrls.api);
expect(returned).toBe(config);
});
@@ -1,10 +1,11 @@
import {inject} from 'aurelia-dependency-injection';
import {Endpoint} from '../../src/aurelia-api';

@inject(Endpoint.of('api'), Endpoint.of('github'))
@inject(Endpoint.of('api'), Endpoint.of('github'), Endpoint.of('form'))
export class InjectTest {
constructor(apiEndpoint, githubEndpoint) {
constructor(apiEndpoint, githubEndpoint, formEndpoint) {
this.apiEndpoint = apiEndpoint;
this.githubEndpoint = githubEndpoint;
this.formEndpoint = formEndpoint;
}
}
@@ -11,6 +11,7 @@ let baseUrls = {

config.registerEndpoint('api', baseUrls.api);
config.registerEndpoint('github', baseUrls.github);
config.registerEndpoint('form', baseUrls.api, null);

let criteria = {user: 'john', comment: 'last'};
let body = {message: 'some'};
@@ -65,6 +66,18 @@ describe('Rest', function() {
});

describe('.update()', function() {
it('Should update with body (as json).', function(done) {
let injectTest = container.get(InjectTest);

injectTest.apiEndpoint.update('posts', null, body)
.then(y => {
expect(y.method).toBe('PUT');
expect(y.path).toBe('/posts');
expect(y.contentType).toMatch('application/json');
done();
});
});

it('Should update with body (as json), criteria and options.', function(done) {
let injectTest = container.get(InjectTest);

@@ -73,14 +86,26 @@ describe('Rest', function() {
expect(y.method).toBe('PUT');
expect(y.path).toBe('/posts');
expect(JSON.stringify(y.query)).toBe(JSON.stringify(criteria));
expect(y.contentType).toBe('application/json');
expect(y.contentType).toMatch(options.headers['Content-Type']);
expect(y.Authorization).toBe(options.headers['Authorization']);
done();
});
});
});

describe('.patch()', function() {
it('Should patch with body (as json).', function(done) {
let injectTest = container.get(InjectTest);

injectTest.apiEndpoint.patch('post', null, body)
.then(y => {
expect(y.method).toBe('PATCH');
expect(y.path).toBe('/post');
expect(y.contentType).toMatch('application/json');
done();
});
});

it('Should patch with body (as json), criteria and options.', function(done) {
let injectTest = container.get(InjectTest);

@@ -89,7 +114,7 @@ describe('Rest', function() {
expect(y.method).toBe('PATCH');
expect(y.path).toBe('/post');
expect(JSON.stringify(y.query)).toBe(JSON.stringify(criteria));
expect(y.contentType).toBe('application/json');
expect(y.contentType).toMatch(options.headers['Content-Type']);
expect(y.Authorization).toBe(options.headers['Authorization']);
done();
});
@@ -112,17 +137,62 @@ describe('Rest', function() {
});

describe('.create()', function() {
it('Should create body (as json).', function(done) {
let injectTest = container.get(InjectTest);

injectTest.apiEndpoint.create('posts', body)
.then(y => {
expect(y.method).toBe('POST');
expect(y.path).toBe('/posts');
expect(y.contentType).toMatch('application/json');
done();
});
});

it('Should create body (as json) and options.', function(done) {
let injectTest = container.get(InjectTest);

injectTest.apiEndpoint.create('posts', body, options)
.then(y => {
expect(y.method).toBe('POST');
expect(y.path).toBe('/posts');
expect(y.contentType).toBe('application/json');
expect(y.contentType).toMatch(options.headers['Content-Type']);
expect(y.Authorization).toBe(options.headers['Authorization']);
done();
});
});
});

describe('.post()', function() {
it('Should post body (as urlencoded).', function(done) {
let injectTest = container.get(InjectTest);

injectTest.apiEndpoint.post('posts', body, options)
.then(y => {
expect(JSON.stringify(y.body)).toBe(JSON.stringify(y.body));
expect(y.method).toBe('POST');
expect(y.path).toBe('/posts');
expect(y.contentType).toMatch(options.headers['Content-Type']);
expect(y.Authorization).toBe(options.headers['Authorization']);
done();
});
});

it('Should post body (as FormData) and options.', function(done) {
let injectTest = container.get(InjectTest);

let data = new FormData();
data.append('message', 'some');

injectTest.formEndpoint.post('uploads', data, {headers: {'Authorization': 'Bearer aToken'}})
.then(y => {
expect(y.method).toBe('POST');
expect(y.path).toBe('/uploads');
expect(y.contentType).toMatch('multipart/form-data');
expect(y.Authorization).toBe('Bearer aToken');
expect(y.body.message).toBe('some');
done();
});
});
});
});

0 comments on commit c68faff

Please sign in to comment.