Skip to content

Instantly share code, notes, and snippets.

@paulorsbrito
Created September 18, 2017 14:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paulorsbrito/8ce201b07a3c4519dc01484186c3ec94 to your computer and use it in GitHub Desktop.
Save paulorsbrito/8ce201b07a3c4519dc01484186c3ec94 to your computer and use it in GitHub Desktop.
Rest client for admin-on-rest talk to django rest framework
import { stringify } from 'query-string';
import {
GET_LIST,
GET_ONE,
GET_MANY,
GET_MANY_REFERENCE,
CREATE,
UPDATE,
DELETE,
} from 'admin-on-rest';
export default (apiUrl, httpClient = null) => {
class HttpError extends Error {
constructor(message, status, body = null) {
super(message);
this.message = message;
this.status = status;
this.body = body;
this.name = this.constructor.name;
if (typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(this, this.constructor);
} else {
this.stack = new Error(message).stack;
}
this.stack = new Error().stack;
}
}
const fetchJson = (url, options = {}) => {
const requestHeaders =
options.headers ||
new Headers({
Accept: 'application/json',
});
if (
!requestHeaders.has('Content-Type') &&
!(options && options.body && options.body instanceof FormData)
) {
requestHeaders.set('Content-Type', 'application/json');
}
if (options.user && options.user.authenticated && options.user.token) {
requestHeaders.set('Authorization', options.user.token);
}
return fetch(url, { ...options, headers: requestHeaders })
.then(response =>
response.text().then(text => ({
status: response.status,
statusText: response.statusText,
headers: response.headers,
body: text,
}))
)
.then(({ status, statusText, headers, body }) => {
let json;
try {
json = JSON.parse(body);
} catch (e) {
// not json, no big deal
}
if (status < 200 || status >= 300) {
return Promise.reject(
new HttpError(
(json && json.message) || statusText,
status,
json
)
);
}
return { status, headers, body, json };
});
};
const queryParameters = stringify;
const isValidObject = value => {
if (!value) {
return false;
}
const isArray = Array.isArray(value);
const isBuffer = Buffer.isBuffer(value);
const isObject =
Object.prototype.toString.call(value) === '[object Object]';
const hasKeys = !!Object.keys(value).length;
return !isArray && !isBuffer && isObject && hasKeys;
};
const flattenObject = (value, path = []) => {
if (isValidObject(value)) {
return Object.assign(
{},
...Object.keys(value).map(key =>
flattenObject(value[key], path.concat([key]))
)
);
} else {
return path.length ? { [path.join('.')]: value } : value;
}
};
/**
* @param {String} type One of the constants appearing at the top if this file, e.g. 'UPDATE'
* @param {String} resource Name of the resource to fetch, e.g. 'posts'
* @param {Object} params The REST request params, depending on the type
* @returns {Object} { url, options } The HTTP request parameters
*/
const convertRESTRequestToHTTP = (type, resource, params) => {
let url = '';
const options = {};
console.log('filters', params.filter)
switch (type) {
case GET_LIST: {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
...flattenObject(params.filter),
ordering: (order == 'DESC' ? '-' : '') + field,
offset: (page - 1) * perPage,
limit: page * perPage,
};
console.log(5)
url = `${apiUrl}/${resource}?${stringify(query)}`;
break;
}
case GET_ONE:
url = `${apiUrl}/${resource}/${params.id}`;
break;
case GET_MANY_REFERENCE: {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
...flattenObject(params.filter),
[params.target]: params.id,
ordering: (order == 'DESC' ? '-' : '') + field,
offset: (page - 1) * perPage,
limit: page * perPage,
};
url = `${apiUrl}/${resource}?${stringify(query)}`;
break;
}
case UPDATE:
url = `${apiUrl}/${resource}/${params.id}`;
options.method = 'PUT';
options.body = JSON.stringify(params.data);
break;
case CREATE:
url = `${apiUrl}/${resource}`;
options.method = 'POST';
options.body = JSON.stringify(params.data);
break;
case DELETE:
url = `${apiUrl}/${resource}/${params.id}`;
options.method = 'DELETE';
break;
default:
throw new Error(`Unsupported fetch action type ${type}`);
}
return { url, options };
};
/**
* @param {Object} response HTTP response from fetch()
* @param {String} type One of the constants appearing at the top if this file, e.g. 'UPDATE'
* @param {String} resource Name of the resource to fetch, e.g. 'posts'
* @param {Object} params The REST request params, depending on the type
* @returns {Object} REST response
*/
const convertHTTPResponseToREST = (response, type, resource, params) => {
const { headers, json } = response;
switch (type) {
case GET_LIST:
case GET_MANY_REFERENCE:
if (!headers.has('x-total-count')) {
throw new Error(
'The X-Total-Count header is missing in the HTTP Response. The jsonServer REST client expects responses for lists of resources to contain this header with the total number of results to build the pagination. If you are using CORS, did you declare X-Total-Count in the Access-Control-Expose-Headers header?'
);
}
return {
data: json,
total: parseInt(
headers
.get('x-total-count')
.split('/')
.pop(),
10
),
};
case CREATE:
return { data: { ...params.data, id: json.id } };
default:
return { data: json };
}
};
/**
* @param {string} type Request type, e.g GET_LIST
* @param {string} resource Resource name, e.g. "posts"
* @param {Object} payload Request parameters. Depends on the request type
* @returns {Promise} the Promise for a REST response
*/
return (type, resource, params) => {
// json-server doesn't handle WHERE IN requests, so we fallback to calling GET_ONE n times instead
if (!httpClient)
httpClient = fetchJson
if (type === GET_MANY) {
return Promise.all(
params.ids.map(id => httpClient(`${apiUrl}/${resource}/${id}`))
).then(responses => ({
data: responses.map(response => response.json),
}));
}
const { url, options } = convertRESTRequestToHTTP(
type,
resource,
params
);
return httpClient(url, options).then(response =>
convertHTTPResponseToREST(response, type, resource, params)
);
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment