Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
dzwiedziu-nkg committed Apr 21, 2017
1 parent af174eb commit 3d921de
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 16 deletions.
101 changes: 101 additions & 0 deletions src/apiClient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// This class is a reference implementation. If you desire additional
// functionality, you can work with the config options here, or copy this code
// into your project and customize the ApiClient to your needs.

// You can pass in config using `action.payload.fetchConfig`, or using passedConfig
// when you first initialize your ApiClient. Some keys will be used internally, and
// all other config keys will be passed on to the fetch `init` parameter. See
// https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch for details.
//
// See below for how the three config objects are merged: fetchConfig takes precedence
// over passedConfig, which takes precedence over baseConfig. The headers key of
// the three objects is merged, to allow more fine-grained header setup.
//
// `methods` will be ignored if passed to fetchConfig. Pass an array to passedConfig
// to allow more HTTP methods to be used via the fetch API.
//
// `basePath` is the basePath of your API. It must be passed to passedConfig, and can
// be overwritten in fetchConfig.
//
// `format` is the format to be requested from the Response. It can be any of arrayBuffer,
// blob, formData, json (the default), or text.
//
// `bodyEncoder` is the function that encodes the data parameter before passing to fetch
//
// All other keys are passed directly to the fetch `init` parameter.

class ApiClient {
constructor(passedConfig) {
const baseConfig = {
bodyEncoder: JSON.stringify,
credentials: 'same-origin',
format: 'json',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
methods: ['get', 'post', 'put', 'patch', 'delete']
}

if (!passedConfig.basePath) {
// e.g. 'https://example.com/api/v3'
throw new Error('You must pass a base path to the ApiClient')
}

const methods = passedConfig.methods || baseConfig.methods
methods.forEach(method => {
this[method] = (path, { params, data, fetchConfig } = {}) => {
const config = {
...baseConfig,
...passedConfig,
...fetchConfig,
headers: {
...baseConfig.headers,
...(passedConfig ? passedConfig.headers : {}),
...(fetchConfig ? fetchConfig.headers : {})
}
}
const {
methods: _methods, basePath, headers, format, bodyEncoder,
...otherConfig
} = config
const requestPath = basePath + path + this.queryString(params)
const body = data ? bodyEncoder(data) : undefined

return fetch(requestPath, {
...otherConfig,
method,
headers,
body
}).then(response => ({ response, format }))
.then(this.handleErrors)
.then(response => response[format]())
}
})
}

// thanks http://stackoverflow.com/a/12040639/5332286
queryString(params) {
const s = Object.keys(params).map(key => (
[key, params[key]].map(encodeURIComponent).join('=')
)).join('&')
return s ? `?${s}` : ''
}

handleErrors({ response, format }) {
if (!response.ok) {
return response[format]()
// if response parsing failed send back the entire response object
.catch(() => { throw response })
// else send back the parsed error
.then(parsedErr => { throw parsedErr })
}
// fix of "Unexpected end of JSON input" when HTTP response is 204 No Content
if (response.status === 204) {
return { json: () => () => null }
}
return response
}
}

export default ApiClient;
5 changes: 0 additions & 5 deletions src/containers/RoomForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,6 @@ class RoomForm extends React.Component {
const { pending, error, response } = action;
const done = ('error' in action && error !== null) || ('response' in action && response !== null);
let ok = done && (!('error' in action) || error === null);
// TODO: modify ApiClient to support 204 as success of delete
if (this.state.action === 'delete' && done && !ok && 'message' in error && 'stack' in error && error.message === "Unexpected end of JSON input" && error.stack === "SyntaxError: Unexpected end of JSON input") {
ok = true;
}
return { pending, done, ok, error, response };
}
return { pending: false, done: false, ok: false };
Expand All @@ -130,7 +126,6 @@ class RoomForm extends React.Component {
text = `Usunięto pokój o nazwie: ${name}`;
}
dispatch(setNotify(text, "success"));
dispatch(clearModelData('rooms')); // TODO: remove after modify ApiClient to support 204 as success of delete
}
}
}
Expand Down
10 changes: 0 additions & 10 deletions src/routes.js

This file was deleted.

3 changes: 2 additions & 1 deletion src/stores/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import createSagaMiddleware from 'redux-saga';
import { createStore, applyMiddleware, compose } from 'redux'
import { crudSaga, ApiClient } from 'redux-crud-store'
import { crudSaga } from 'redux-crud-store'
import ApiClient from '../apiClient'
import reducers from '../reducers';

const client = new ApiClient({ basePath: '/api' });
Expand Down

0 comments on commit 3d921de

Please sign in to comment.