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

Commit

Permalink
feat(text): add text serializer (#555)
Browse files Browse the repository at this point in the history
  • Loading branch information
patricksmms authored Sep 9, 2020
1 parent 6197f8d commit ec131f1
Show file tree
Hide file tree
Showing 8 changed files with 317 additions and 0 deletions.
1 change: 1 addition & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = {
'remote',
'form',
'json',
'text',
'deps',
'deps-dev',
]],
Expand Down
5 changes: 5 additions & 0 deletions packages/text-serializer/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Text Serializer Changelog

## 0.1.0

Initial release
47 changes: 47 additions & 0 deletions packages/text-serializer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# API Elements: Text Serializer

## Usage

Takes an API Element data structure, and returns Text serialized data
structures, for example:

### Async

```javascript
const { Fury } = require('@apielements/core');
const textSerializer = require('@apielements/text-serializer');

const fury = new Fury();
fury.use(textSerializer);

const api = new fury.minim.elements.String();
api.attributes.set('default', 'Doe');

const mediaType = 'text/plain';
fury.serialize({ api, mediaType }, (error, body) => {
console.log(body);
// "Doe"
});
```


### Sync

```javascript
const { Fury } = require('@apielements/core');
const textSerializer = require('@apielements/text-serializer');

const fury = new Fury();
fury.use(textSerializer);

const api = new fury.minim.elements.String('Doe');
const mediaType = 'text/plain';
try {
const body = fury.serialize({ api, mediaType });
console.log(body);
// Doe
} catch (error) {
console.log(error);
// Only primitive elements can be serialized as text/plain
}
```
36 changes: 36 additions & 0 deletions packages/text-serializer/lib/adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const serializeText = require('./serializeText');

const name = 'text';
const mediaTypes = [
'text/plain',
];

function serialize({ api }) {
return new Promise((resolve, reject) => {
const done = (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
};

serializeText(api, done);
});
}

function serializeSync({ api }) {
const done = (error, result) => {
if (error) {
throw error;
} else {
return result;
}
};

return serializeText(api, done);
}

module.exports = {
name, mediaTypes, serialize, serializeSync,
};
42 changes: 42 additions & 0 deletions packages/text-serializer/lib/serializeText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
function collectElementByIDs(element) {
const dataStructures = {};
const { parents } = element;

if (!parents || parents.isEmpty) {
return dataStructures;
}

const rootElement = parents.get(parents.length - 1);

if (rootElement) {
rootElement.recursiveChildren.forEach((element) => {
// eslint-disable-next-line no-underscore-dangle
const isNotEmptyStringElement = element._meta && element._meta.get('id');

if (isNotEmptyStringElement) {
dataStructures[element.id.toValue()] = element;
}
});
}

return dataStructures;
}

const isPrimitive = value => value !== undefined && (value !== Object(value));

function serializeText(element, done) {
if (element.element === 'dataStructure') {
return serializeText(element.content, done);
}

const dataStructures = collectElementByIDs(element);
const value = element.valueOf(undefined, dataStructures);

if (isPrimitive(value)) {
return done(null, String(value));
}

return done(new Error('Only primitive elements can be serialized as text/plain'));
}

module.exports = serializeText;
35 changes: 35 additions & 0 deletions packages/text-serializer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "@apielements/text-serializer",
"version": "0.1.0",
"description": "Text serializer for API Elements",
"author": "Apiary.io <[email protected]>",
"license": "MIT",
"main": "./lib/adapter.js",
"files": [
"lib/adapter.js",
"lib/serializeText.js"
],
"homepage": "https://github.com/apiaryio/api-elements.js/tree/master/packages/text-serializer",
"repository": {
"type": "git",
"url": "https://github.com/apiaryio/api-elements.js.git",
"directory": "packages/text-serializer"
},
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"test": "mocha test"
},
"peerDependencies": {
"@apielements/core": ">=0.1.0 <0.3.0"
},
"devDependencies": {
"@apielements/core": ">=0.1.0 <0.3.0",
"chai": "^4.2.0",
"eslint": "^5.16.0",
"mocha": "^7.1.1"
},
"engines": {
"node": ">=8"
}
}
58 changes: 58 additions & 0 deletions packages/text-serializer/test/adapter-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const { expect } = require('chai');
const { Fury } = require('@apielements/core');
const adapter = require('../lib/adapter');

describe('Text Serializer Adapter', () => {
let fury;

before(() => {
fury = new Fury();
fury.use(adapter);
});

it('has a name', () => {
expect(adapter.name).to.equal('text');
});

it('has a text/plain media type', () => {
expect(adapter.mediaTypes).to.deep.equal(['text/plain']);
});

describe('using serialize', () => {
it('can serialize a primitive element', (done) => {
const element = new fury.minim.elements.String('hello world');

fury.serialize({ api: element, mediaType: 'text/plain' }, (error, result) => {
expect(error).to.be.null;
expect(result).to.equal('hello world');
done();
});
});

it('errors with a non-primitive element', (done) => {
const element = new fury.minim.elements.Object({ message: 'Hello world' });

fury.serialize({ api: element, mediaType: 'text/plain' }, (error, result) => {
expect(error.message).to.equal('Only primitive elements can be serialized as text/plain');
expect(result).to.be.undefined;
done();
});
});
});

describe('using serializeSync', () => {
it('can serialize a primitive element', () => {
const element = new fury.minim.elements.String('hello world');
const result = fury.serializeSync({ api: element, mediaType: 'text/plain' });

expect(result).to.equal('hello world');
});

it('errors with a non-primitive element', () => {
const element = new fury.minim.elements.Object({ message: 'Hello world' });

expect(() => fury.serializeSync({ api: element, mediaType: 'text/plain' }))
.to.throw('Only primitive elements can be serialized as text/plain');
});
});
});
93 changes: 93 additions & 0 deletions packages/text-serializer/test/serializeText-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const { expect } = require('chai');
const { Fury } = require('@apielements/core');
const serializeText = require('../lib/serializeText');

const { minim: namespace } = new Fury();

const done = (error, result) => {
if (error) {
throw error;
} else {
return result;
}
};

describe('#serializeText', () => {
it('can serialize a primitive element with value', () => {
const stringElement = new namespace.elements.String('hello world');
const numberElement = new namespace.elements.Number(1);
const booleanElement = new namespace.elements.Boolean(true);
const nullElement = new namespace.elements.Null();

expect(serializeText(stringElement, done)).to.equal('hello world');
expect(serializeText(numberElement, done)).to.equal('1');
expect(serializeText(booleanElement, done)).to.equal('true');
expect(serializeText(nullElement, done)).to.equal('null');
});

it('can serialize an enum element with primitive values', () => {
const enumElement = new namespace.elements.Enum();
enumElement.enumerations = ['north', 'east', 'south', 'west'];

expect(serializeText(enumElement, done)).to.equal('north');
});

it('can serialize a primitive element with default value', () => {
const element = new namespace.elements.String();
element.attributes.set('default', 'hello world');

expect(serializeText(element, done)).to.equal('hello world');
});

it('can serialize an element with references via parent tree', () => {
const element = new namespace.elements.Element();
element.element = 'error';

const error = new namespace.elements.Element();
error.element = 'message';
error.id = 'error';

new namespace.elements.Category([
new namespace.elements.Category([
new namespace.elements.String('error message', { id: 'message' }),
error,
], { classes: ['dataStructures'] }),
new namespace.elements.Category([
element,
]),
]).freeze();

expect(serializeText(element, done)).to.equal('error message');
});

it('can serialize a dataStructure element', () => {
const element = new namespace.elements.DataStructure(
new namespace.elements.String('hello world')
);

expect(serializeText(element, done)).to.equal('hello world');
});

it('can serialize from referenced element', () => {
const element = new namespace.elements.Element();
element.element = 'ref';
element.content = 'message';

new namespace.elements.Category([
new namespace.elements.String('hello world', { id: 'message' }),
element,
]).freeze();

expect(serializeText(element, done)).to.equal('hello world');
});

it('errors with a non primitive element', () => {
const objectElement = new namespace.elements.Object({ message: 'Hello world' });
const arrayElement = new namespace.elements.Array(['Hello', 'Doe']);
const emptyEnumElement = new namespace.elements.Enum();

expect(() => serializeText(objectElement, done)).to.throw('Only primitive elements can be serialized as text/plain');
expect(() => serializeText(arrayElement, done)).to.throw('Only primitive elements can be serialized as text/plain');
expect(() => serializeText(emptyEnumElement, done)).to.throw('Only primitive elements can be serialized as text/plain');
});
});

0 comments on commit ec131f1

Please sign in to comment.