Skip to content

Commit

Permalink
Merge pull request #146 from ReCodEx/json_everywhere
Browse files Browse the repository at this point in the history
Use JSON instead of urlencoded form data
  • Loading branch information
SemaiCZE committed Dec 4, 2017
2 parents d537017 + 387179a commit 66b6ffb
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 64 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"exenv": "^1.2.1",
"express": "^4.13.4",
"file-saver": "^1.3.3",
"flat": "^2.0.1",
"flat": "^4.0.0",
"flow-bin": "^0.46.0",
"global": "^4.3.1",
"immutable": "^3.8.1",
Expand Down
58 changes: 41 additions & 17 deletions src/redux/helpers/api/tools.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import statusCode from 'statuscode';
import { addNotification } from '../../modules/notifications';
import flatten from 'flat';
import { flatten } from 'flat';

import { logout } from '../../modules/auth';
import { isTokenValid, decode } from '../../helpers/token';
Expand All @@ -14,6 +14,17 @@ export const API_BASE = process.env.API_BASE || 'http://localhost:4000/v1';
const maybeShash = endpoint => (endpoint.indexOf('/') === 0 ? '' : '/');
const getUrl = endpoint => API_BASE + maybeShash(endpoint) + endpoint;

const maybeQuestionMark = (endpoint, query) =>
Object.keys(query).length === 0
? ''
: endpoint.indexOf('?') === -1 ? '?' : '&';

const generateQuery = query =>
!query ? '' : Object.keys(query).map(key => `${key}=${query[key]}`).join('&');

export const assembleEndpoint = (endpoint, query = {}) =>
endpoint + maybeQuestionMark(endpoint, query) + generateQuery(query);

export const flattenBody = body => {
const flattened = flatten(body, { delimiter: ':' });
body = {};
Expand All @@ -36,33 +47,45 @@ const createFormData = body => {
return data;
};

const maybeQuestionMark = (endpoint, query) =>
Object.keys(query).length === 0
? ''
: endpoint.indexOf('?') === -1 ? '?' : '&';

const generateQuery = query =>
!query ? '' : Object.keys(query).map(key => `${key}=${query[key]}`).join('&');
const encodeBody = (body, method, encodeAsMultipart) => {
if (method.toUpperCase() !== 'POST') {
return undefined;
}

export const assembleEndpoint = (endpoint, query = {}) =>
endpoint + maybeQuestionMark(endpoint, query) + generateQuery(query);
if (encodeAsMultipart) {
return body ? createFormData(body) : undefined;
} else {
// otherwise we encode in JSON
return JSON.stringify(body || []);
}
};

export const createRequest = (endpoint, query = {}, method, headers, body) =>
export const createRequest = (
endpoint,
query = {},
method,
headers,
body,
uploadFiles
) =>
fetch(getUrl(assembleEndpoint(endpoint, query)), {
method,
headers,
body: body ? createFormData(body) : undefined
body: encodeBody(body, method, uploadFiles)
});

export const getHeaders = (headers, accessToken) => {
export const getHeaders = (headers, accessToken, skipContentType) => {
const usedHeaders = skipContentType
? headers
: { 'Content-Type': 'application/json', ...headers };
if (accessToken) {
return {
Authorization: `Bearer ${accessToken}`,
...headers
...usedHeaders
};
}

return headers;
return usedHeaders;
};

/**
Expand All @@ -79,11 +102,12 @@ export const createApiCallPromise = (
accessToken = '',
body = undefined,
wasSuccessful = () => true,
doNotProcess = false
doNotProcess = false,
uploadFiles = false
},
dispatch = undefined
) => {
let call = createRequest(endpoint, query, method, headers, body)
let call = createRequest(endpoint, query, method, headers, body, uploadFiles)
.catch(err => detectUnreachableServer(err, dispatch))
.then(res => {
if (
Expand Down
8 changes: 5 additions & 3 deletions src/redux/middleware/apiMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export const apiCall = (
body = undefined,
meta = undefined,
wasSuccessful = isTwoHundredCode,
doNotProcess = false
doNotProcess = false,
uploadFiles = false
},
dispatch = undefined
) => ({
Expand All @@ -29,10 +30,11 @@ export const apiCall = (
endpoint,
query,
method,
headers: getHeaders(headers, accessToken),
headers: getHeaders(headers, accessToken, uploadFiles),
body,
wasSuccessful,
doNotProcess
doNotProcess,
uploadFiles
},
dispatch
),
Expand Down
3 changes: 2 additions & 1 deletion src/redux/modules/additionalExerciseFiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ export const addAdditionalExerciseFiles = (exerciseId, files) =>
tmpId: Math.random().toString(),
file: uploaded.file
}))
}
},
uploadFiles: true
});

export const removeAdditionalExerciseFile = (exerciseId, fileId) =>
Expand Down
3 changes: 2 additions & 1 deletion src/redux/modules/pipelineFiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export const addPipelineFiles = (pipelineId, files) =>
tmpId: Math.random().toString(),
file: uploaded.file
}))
}
},
uploadFiles: true
});

/**
Expand Down
3 changes: 2 additions & 1 deletion src/redux/modules/supplementaryFiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ export const addSupplementaryFiles = (exerciseId, files) =>
tmpId: Math.random().toString(),
file: uploaded.file
}))
}
},
uploadFiles: true
});

export const removeSupplementaryFile = (exerciseId, fileId) =>
Expand Down
3 changes: 2 additions & 1 deletion src/redux/modules/upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export const uploadFile = (id, file, endpoint = '/uploaded-files') =>
method: 'POST',
endpoint,
body: { [file.name]: file },
meta: { id, fileName: file.name }
meta: { id, fileName: file.name },
uploadFiles: true
});

const wrapWithName = (id, file) => ({ [file.name]: file });
Expand Down
37 changes: 3 additions & 34 deletions test/redux/helpers/api/tools-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import { expect } from 'chai';

import {
getHeaders,
assembleEndpoint,
flattenBody
assembleEndpoint
} from '../../../../src/redux/helpers/api/tools';

describe('API middleware and helper functions', () => {
describe('(Helper functions)', () => {
it('must add access token to the headers', () => {
expect(getHeaders({ a: 'b' }, 'abcd')).to.eql({
a: 'b',
Authorization: 'Bearer abcd'
Authorization: 'Bearer abcd',
'Content-Type': 'application/json'
});
});

Expand All @@ -33,36 +33,5 @@ describe('API middleware and helper functions', () => {
'http://www.blabla.com/abcd'
);
});

it('must flatten the body object to urlencoded format', () => {
expect(flattenBody({})).to.eql({});
expect(flattenBody([])).to.eql({});
expect(flattenBody({ a: 'b' })).to.eql({ a: 'b' });
expect(flattenBody({ a: { b: 'c' } })).to.eql({ 'a[b]': 'c' });
expect(flattenBody({ a: { b: { c: 'd' } } })).to.eql({ 'a[b][c]': 'd' });
expect(flattenBody({ a: { b: ['c', 'd'] } })).to.eql({
'a[b][0]': 'c',
'a[b][1]': 'd'
});
expect(
flattenBody({
a: [
{
b: [{ c: 'A' }, { d: 'B' }]
},
[{ f: 'C', g: 'D' }, { h: 'E' }]
]
})
).to.eql({
'a[0][b][0][c]': 'A',
'a[0][b][1][d]': 'B',
'a[1][0][f]': 'C',
'a[1][0][g]': 'D',
'a[1][1][h]': 'E'
});

// not a POJO
expect(() => flattenBody({ a: new MouseEvent('click') })).to.throw();
});
});
});
14 changes: 9 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2991,11 +2991,11 @@ flat-cache@^1.2.1:
graceful-fs "^4.1.2"
write "^0.2.1"

flat@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/flat/-/flat-2.0.1.tgz#70e29188a74be0c3c89409eed1fa9577907ae32f"
flat@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/flat/-/flat-4.0.0.tgz#3abc7f3b588e64ce77dc42fd59aa35806622fea8"
dependencies:
is-buffer "~1.1.2"
is-buffer "~1.1.5"

flatten@^1.0.2:
version "1.0.2"
Expand Down Expand Up @@ -3861,10 +3861,14 @@ is-binary-path@^1.0.0:
dependencies:
binary-extensions "^1.0.0"

is-buffer@^1.1.5, is-buffer@~1.1.2:
is-buffer@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc"

is-buffer@~1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"

is-builtin-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
Expand Down

0 comments on commit 66b6ffb

Please sign in to comment.