Skip to content

Instantly share code, notes, and snippets.

@mdciotti
Created April 26, 2019 14:45
Show Gist options
  • Save mdciotti/3780358ddbef792f9c09d04d238c08e0 to your computer and use it in GitHub Desktop.
Save mdciotti/3780358ddbef792f9c09d04d238c08e0 to your computer and use it in GitHub Desktop.
Mock API for testing
/**
* Simulates asynchronous data loading by wrapping setTimeout in a Promise.
* @param {Number} ms the delay in milliseconds to wait before resolving
* @param {any} data the data to resolve with
* @returns {Promise} a promise that resolves with `data` in `ms` milliseconds
*/
async function asyncData(ms, data) {
await new Promise(r => setTimeout(r, ms));
return data;
}
function has(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
export default class FakeAPI {
constructor(delay = 1000) {
this.delay = delay;
this.routes = {
GET: {},
POST: {},
PUT: {},
DELETE: {},
PATCH: {},
};
this.errors = {
network: false,
server: false,
};
}
routeExists(route, method = 'GET') {
const m = method.toUpperCase();
return has(this.routes, m) && has(this.routes[m], route);
}
routeImplemented(route, method = 'GET') {
const m = method.toUpperCase();
return this.routeExists(route, m) && this.routes[m][route];
}
/**
* Creates an error Response of the specified type.
* @param {Number} status the HTTP status code
* @param {String} statusText the HTTP status text
* @param {*} data the body JSON that explains the error
*/
async errorResponse(status, statusText, data) {
const body = await asyncData(this.delay, JSON.stringify(data));
const headers = { 'Content-Length': body.length };
return new Response(body, { status, statusText, headers });
}
/**
* Defines a route that responds to requests.
* @param {String} method the HTTP method to use
* @param {String} route the URL path for this resource
* @param {*} data the data to be returned by this route
*/
addRoute(method, route, data) {
if (data === null) {
this.routes[method][route] = null;
return;
}
let getter;
switch (typeof data) {
case 'function': getter = data; break;
case 'string': getter = () => data; break;
case 'undefined': getter = () => null; break;
default: getter = () => JSON.stringify(data);
}
this.routes[method][route] = getter;
}
/**
* Defines a route that responds to GET requests.
* @param {String} route the URL path for this resource
* @param {*} data the data to be returned by this route
*/
get(route, data) {
this.addRoute('GET', route, data);
}
/**
* Defines a route that responds to POST requests.
* @param {String} route the URL path for this resource
* @param {*} data the data to be returned by this route
*/
post(route, data) {
this.addRoute('POST', route, data);
}
/**
* Defines a route that responds to DELETE requests.
* @param {String} route the URL path for this resource
* @param {*} data the data to be returned by this route
*/
delete(route, data) {
this.addRoute('DELETE', route, data);
}
/**
* Defines a route that responds to PUT requests.
* @param {String} route the URL path for this resource
* @param {*} data the data to be returned by this route
*/
put(route, data) {
this.addRoute('PUT', route, data);
}
/**
* Defines a route that responds to PATCH requests.
* @param {String} route the URL path for this resource
* @param {*} data the data to be returned by this route
*/
patch(route, data) {
this.addRoute('PATCH', route, data);
}
async fetch(route, init) {
// return fetch(route, init);
const opts = Object.assign({
method: 'GET',
headers: {
Accept: 'text/plain;charset=UTF-8',
'Content-Type': 'text/plain;charset=UTF-8',
},
}, init);
const m = opts.method.toUpperCase();
if (this.errors.network) {
throw new Error('Network error (no connection).');
}
if (!this.routeExists(route, m)) {
return this.errorResponse(404, 'Not found', {
error: { code: 'routenotfound', message: 'The request URL did not match any known routes.' },
});
}
if (this.errors.server) {
return this.errorResponse(500, 'Internal Server Error', {
error: { code: 'unknown', message: 'An unknown error occured.' },
});
}
if (!this.routeImplemented(route, m)) {
return this.errorResponse(501, 'Not Implemented', {
error: { code: 'routenotimplemented', message: 'This route has not yet been defined.' },
});
}
const origin = window.location.origin;
const req = new Request(`${origin}${route}`, opts);
const body = await asyncData(this.delay, this.routes[m][route](req));
return new Response(body, {
status: 200,
statusText: 'OK',
headers: { 'Content-Length': body.length },
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment