Skip to content
This repository was archived by the owner on Nov 8, 2024. It is now read-only.

Commit 96fbce7

Browse files
author
Natalie Mikešová
authored
feat(form): add a form serializer (#509)
* feat(form): add multipart/form-data serializer * test(form): add multipart/form-data serializer tests * test(form): add data structures tests * chore(form): change fury to apielements/core and mocha version * feat(form): add application/x-www-form-urlencoded support * chore(form): add engine specification * chore(form): change eslint version
1 parent 62dff8c commit 96fbce7

File tree

7 files changed

+226
-0
lines changed

7 files changed

+226
-0
lines changed

commitlint.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
'apib',
1111
'apiaryb',
1212
'remote',
13+
'form',
1314
'deps',
1415
'deps-dev',
1516
]],

packages/form-serializer/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# API Elements: Multipart / Form data serializer
2+
3+
## Usage
4+
5+
```js
6+
const formSerializer = require('@apielements/form-serializer');
7+
fury.use(formSerializer);
8+
9+
const mediaType = 'multipart/form-data';
10+
fury.serialize({ api, mediatype }, (error, body) => {
11+
console.log(body);
12+
});
13+
```
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const serializeForm = require('./serializeForm');
2+
3+
const name = 'form';
4+
const mediaTypes = ['multipart/form-data', 'application/x-www-form-urlencoded'];
5+
6+
function serialize({ api, mediaType }) {
7+
return new Promise(resolve => resolve(serializeForm({ api, mediaType })));
8+
}
9+
10+
module.exports = { name, mediaTypes, serialize };
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
const _ = require('lodash');
2+
const querystring = require('querystring');
3+
4+
function collectElementByIDs(element) {
5+
const dataStructures = {};
6+
7+
if (typeof element.content === 'string') {
8+
dataStructures.undefined = element.content;
9+
return dataStructures;
10+
}
11+
12+
const { parents } = element;
13+
if (parents) {
14+
const rootElement = parents.get(0);
15+
16+
if (rootElement) {
17+
rootElement.recursiveChildren.forEach((element) => {
18+
if (element.id) {
19+
dataStructures[element.id.toValue()] = element;
20+
}
21+
});
22+
}
23+
}
24+
25+
return dataStructures;
26+
}
27+
28+
function serializeForm({ api, mediaType }) {
29+
const dataStructures = collectElementByIDs(api);
30+
let values = api.valueOf(undefined, dataStructures);
31+
32+
if (mediaType === 'multipart/form-data') {
33+
const boundary = 'BOUNDARY';
34+
35+
let content = '';
36+
37+
if (typeof values === 'string') {
38+
content += `--${boundary}\r\n`;
39+
content += 'Content-Disposition: form-data; name="undefined"\r\n\r\n';
40+
content += `${values}\r\n`;
41+
} else {
42+
_.forOwn(values, (value, key) => {
43+
content += `--${boundary}\r\n`;
44+
content += `Content-Disposition: form-data; name="${key}"\r\n\r\n`;
45+
content += `${value}\r\n`;
46+
});
47+
}
48+
49+
content += `--${boundary}--\r\n`;
50+
return content;
51+
}
52+
if (typeof values === 'string') {
53+
values = { undefined: values };
54+
}
55+
return querystring.encode(values);
56+
}
57+
58+
module.exports = serializeForm;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "@apielements/form-serializer",
3+
"version": "0.1.0",
4+
"description": "Multipart/form-data serializer for API Elements",
5+
"author": "Apiary.io <support@apiary.io>",
6+
"license": "MIT",
7+
"main": "./lib/adapter.js",
8+
"files": [
9+
"lib/adapter.js",
10+
"lib/serializeForm.js"
11+
],
12+
"homepage": "https://github.com/apiaryio/api-elements.js/tree/master/packages/form-serializer",
13+
"repository": {
14+
"type": "git",
15+
"url": "https://github.com/apiaryio/api-elements.js.git",
16+
"directory": "packages/form-serializer"
17+
},
18+
"scripts": {
19+
"lint": "eslint",
20+
"lint:fix": "eslint . --fix",
21+
"test": "mocha test"
22+
},
23+
"devDependencies": {
24+
"chai": "^4.2.0",
25+
"eslint": "^5.16.0",
26+
"@apielements/core": "^0.1.0",
27+
"mocha": "^7.1.1"
28+
},
29+
"peerDependencies": {
30+
"@apielements/core": "^0.1.0"
31+
},
32+
"dependencies": {
33+
"lodash": "^4.17.15"
34+
},
35+
"engines": {
36+
"node": ">=8"
37+
}
38+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
const { Fury } = require('@apielements/core');
2+
const { expect } = require('chai');
3+
const adapter = require('../lib/adapter');
4+
5+
describe('Form Serializer Adapter', () => {
6+
let fury;
7+
8+
before(() => {
9+
fury = new Fury();
10+
fury.use(adapter);
11+
});
12+
13+
describe('Multipart/form-data mediaType', () => {
14+
it('has a name', () => {
15+
expect(adapter.name).to.equal('form');
16+
});
17+
18+
it('has a multipart/form-data media type', () => {
19+
expect(adapter.mediaTypes).to.include('multipart/form-data');
20+
});
21+
22+
it('can serialize an object element', (done) => {
23+
const element = new fury.minim.elements.Object({ message: 'Hello world' });
24+
25+
fury.serialize({ api: element, mediaType: 'multipart/form-data' }, (error, result) => {
26+
expect(error).to.be.null;
27+
expect(result).to.equal('--BOUNDARY\r\nContent-Disposition: form-data; name="message"\r\n\r\nHello world\r\n--BOUNDARY--\r\n');
28+
done();
29+
});
30+
});
31+
32+
it('can serialize a string element', (done) => {
33+
const element = new fury.minim.elements.String('Hello world');
34+
35+
fury.serialize({ api: element, mediaType: 'multipart/form-data' }, (error, result) => {
36+
expect(error).to.be.null;
37+
expect(result).to.equal('--BOUNDARY\r\nContent-Disposition: form-data; name="undefined"\r\n\r\nHello world\r\n--BOUNDARY--\r\n');
38+
done();
39+
});
40+
});
41+
});
42+
43+
describe('Application/x-www-form-urlencoded mediaType', () => {
44+
it('has a name', () => {
45+
expect(adapter.name).to.equal('form');
46+
});
47+
48+
it('has an application/x-www-form-urlencoded media type', () => {
49+
expect(adapter.mediaTypes).to.include('application/x-www-form-urlencoded');
50+
});
51+
52+
it('can serialize an object element', (done) => {
53+
const element = new fury.minim.elements.Object({ message: 'Hello world' });
54+
55+
fury.serialize({ api: element, mediaType: 'application/x-www-form-urlencoded' }, (error, result) => {
56+
expect(error).to.be.null;
57+
expect(result).to.equal('message=Hello%20world');
58+
done();
59+
});
60+
});
61+
62+
it('can serialize a string element', (done) => {
63+
const element = new fury.minim.elements.String('Hello world');
64+
65+
fury.serialize({ api: element, mediaType: 'application/x-www-form-urlencoded' }, (error, result) => {
66+
expect(error).to.be.null;
67+
expect(result).to.equal('undefined=Hello%20world');
68+
done();
69+
});
70+
});
71+
});
72+
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const { Fury } = require('@apielements/core');
2+
const { expect } = require('chai');
3+
const serializeForm = require('../lib/serializeForm.js');
4+
5+
const { minim: namespace } = new Fury();
6+
7+
describe('#serializeForm', () => {
8+
it('can serialize a primitive element with value', () => {
9+
const element = new namespace.elements.String('Hello world');
10+
11+
expect(serializeForm({ api: element, mediaType: 'multipart/form-data' })).to.equal('--BOUNDARY\r\nContent-Disposition: form-data; name="undefined"\r\n\r\nHello world\r\n--BOUNDARY--\r\n');
12+
});
13+
14+
it('can serialize a primitive element with a default value', () => {
15+
const element = new namespace.elements.String();
16+
element.attributes.set('default', 'Hello world');
17+
18+
expect(serializeForm({ api: element, mediaType: 'multipart/form-data' })).to.equal('--BOUNDARY\r\nContent-Disposition: form-data; name="undefined"\r\n\r\nHello world\r\n--BOUNDARY--\r\n');
19+
});
20+
21+
it('can serialize an element with references via parent tree', () => {
22+
const element = new namespace.elements.Element();
23+
element.element = 'message';
24+
25+
new namespace.elements.Category([
26+
new namespace.elements.Category([
27+
new namespace.elements.String('Hello world', { id: 'message' }),
28+
], { classes: ['dataStructures'] }),
29+
element,
30+
]).freeze();
31+
32+
expect(serializeForm({ api: element, mediaType: 'multipart/form-data' })).to.equal('--BOUNDARY\r\nContent-Disposition: form-data; name="undefined"\r\n\r\nHello world\r\n--BOUNDARY--\r\n');
33+
});
34+
});

0 commit comments

Comments
 (0)