Skip to content

Commit 39490d9

Browse files
committed
Adds ReduxObject denormalization with tests
1 parent 59db50d commit 39490d9

15 files changed

+1042
-347
lines changed

README.md

+97-197
Original file line numberDiff line numberDiff line change
@@ -1,224 +1,124 @@
11
# Jsona
2-
Framework agnostic library that provide systemized way to work with JSON API [v1.0 spetification](http://jsonapi.org/format/1.0/) in your JavaScript / TypeScript code.
2+
Framework agnostic library that provide data formatters to simplify work with JSON API [v1.0 specification](http://jsonapi.org/format/1.0/).
33

44
[![NPM](https://nodei.co/npm/jsona.png?compact=true)](https://www.npmjs.com/package/jsona/)
55

6-
### Why you may need it?
7-
8-
If you use or build API, that specified by *json:api* and:
9-
- want to use more comfortable interface, than plain json:api object gives (such as array in `included`) to work with data in code;
10-
- dont't want to think how to build correct json in accordance with standard for POST/PUT/PATH requests;
11-
- like to have deal with typed data and you want to map API's entities to objects, that instantiated with unique constructors (classes);
6+
*NOTE:* This README describes 1.x.x version. You can read [README for old versions 0.2.x here](https://github.com/olosegres/jsona/README_0_2.md)
127

138
### What it gives?
9+
- converter from json to simplified objects (some denormalized structure)
10+
- converter from "reduxObject" to simplified objects (`reduxObject` is a result object of [json-api-normalizer](https://github.com/yury-dymov/json-api-normalizer)
11+
- converter from simplified objects to json (json in according with JSON API specification)
1412

15-
Ability to automatically convert request body (json, formatted in accordance with specification *json:api*) to instances of predefined by you classes and back to correct json, easy.
13+
### How to use
14+
You need to instantiate Jsona ones, then use it's public methods to convert data.
15+
```
16+
const dataFormatter = new Jsona();
17+
```
1618

17-
**Simple example**
19+
##### deserialize - creates simplified object(s) from json
1820
```javascript
19-
import {Jsona} from 'jsona';
20-
21-
// it suppose that we already defined special classes, such as data models
22-
// and EntitiesFactory, that will help Jsona to map json:api entities to our data models and back
23-
import MyEntitiesFactory from './MyEntitiesFactory';
24-
import TestEntity1 from './entities/TestEntity1';
25-
import TestEntity2 from './entities/TestEntity2';
26-
27-
const testJson = {
28-
"data": {
29-
"id": 123,
30-
"type": "testentity1",
31-
"attributes": {
32-
"foo1": "bar1"
21+
const json = {
22+
data: {
23+
type: 'town',
24+
id: '123',
25+
attributes: {
26+
name: 'Barcelona',
27+
},
28+
relationships: {
29+
country: {
30+
data: {
31+
type: 'country',
32+
id: '32',
33+
}
34+
}
35+
}
3336
},
34-
"relationships": {
35-
"testrelation": {
36-
"data": {
37-
"id": 321,
38-
"type": "testentity2"
37+
included: [{
38+
type: 'country',
39+
id: '32',
40+
attributes: {
41+
name: 'Spain',
3942
}
40-
}
41-
}
42-
},
43-
"included": [{
44-
"id": 321,
45-
"type": "testentity2",
46-
"attributes": {
47-
"foo2": "bar2"
48-
}
49-
}]
43+
}]
5044
};
5145

52-
const dataFormatter = new Jsona(new MyEntitiesFactory());
53-
54-
// testJson may be stringified json or plain object
55-
const deserialized = dataFormatter.deserialize(testJson);
56-
57-
console.log(deserialized); // will output something similar to:
58-
// {
59-
// hasCollection: false
60-
// hasItem: true
61-
// item: TestEntity1 {
62-
// foo1: "bar1"
63-
// id: 123
64-
// type: "testentity1"
65-
// testrelation: TestEntity2 {
66-
// foo2: "bar2"
67-
// id: 321
68-
// type: "testentity2"
69-
// }
70-
// }
71-
// collection: null
72-
// }
73-
46+
const model = dataFormatter.deserialize(json);
47+
console.log(model); // will output:
48+
/* {
49+
type: 'town',
50+
id: '21',
51+
name: 'Shanghai',
52+
country: {
53+
type: 'country',
54+
id: '34',
55+
name: 'Spain'
56+
}
57+
} */
7458
```
7559

76-
### Examples of helpers and models that you may change and use in your project
77-
Library written in TypeScript, but examples below uses ES6-7 and partly Flow.
78-
79-
**MyEntitiesFactory.js**
60+
##### serialize - creates json from simplified object(s)
8061
```javascript
81-
import Town from './entities/Town';
82-
import Country from './entities/Country';
83-
import Region from './entities/Region';
84-
85-
class MyEntitiesFactory {
86-
87-
constructor() {
88-
this.map = {
89-
'town': Town,
90-
'country': Country,
91-
'region': Region,
92-
};
93-
}
94-
95-
getClass(entityType) {
96-
return this.map[entityType];
97-
}
98-
99-
getModel(entityType) {
100-
var mappedClass = this.map[entityType];
101-
102-
if (!mappedClass) {
103-
throw new Error(`MyEntitiesFactory dont know about entity with type [${entityType}]`);
104-
}
105-
106-
return new mappedClass();
107-
}
108-
}
109-
110-
export default MyEntitiesFactory;
62+
const newJson = dataFormatter.serialize({
63+
stuff: model,
64+
includeNames: 'country'
65+
});
66+
console.log(newJson); // will output:
67+
/* {
68+
data: {
69+
type: 'town',
70+
id: '123',
71+
attributes: {
72+
name: 'Barcelona',
73+
},
74+
relationships: {
75+
country: {
76+
data: {
77+
type: 'country',
78+
id: '32',
79+
}
80+
}
81+
}
82+
},
83+
included: [{
84+
type: 'country',
85+
id: '32',
86+
attributes: {
87+
name: 'Spain',
88+
}
89+
}]
90+
}*/
11191
```
11292

113-
**jsonaHelpers.js**
114-
```javascript
115-
import {Jsona} from 'jsona';
116-
import MyEntitiesFactory from './MyEntitiesFactory';
117-
118-
/**
119-
* @var {MyEntitiesFactory} entitiesFactory - factory, that will using in Jsona for instantiate entities for each defined type
120-
*/
121-
const entitiesFactory = new MyEntitiesFactory();
122-
const dataFormatter = new Jsona(entitiesFactory);
123-
124-
export function fromJsonToItem(jsonApiBody) {
125-
const deserialized = dataFormatter.deserialize(jsonApiBody);
126-
127-
if (deserialized.hasItem) {
128-
return {
129-
item: deserialized.item,
130-
meta: deserialized.meta,
131-
};
132-
}
133-
134-
console.error('fromJsonToItem cant deserialize object', jsonApiBody);
135-
return {};
136-
}
137-
138-
export function fromJsonToCollection(jsonApiBody) {
139-
const deserialized = dataFormatter.deserialize(jsonApiBody);
140-
141-
if (deserialized.hasCollection) {
142-
return {
143-
collection: deserialized.collection,
144-
meta: deserialized.meta,
145-
};
146-
}
147-
148-
console.error('fromJsonToCollection cant deserialize object', jsonApiBody);
149-
return {};
150-
}
151-
152-
export function fromCollectionToJson(collection, requestedIncludes, withAllIncludes = false) {
153-
return dataFormatter.serialize({collection, requestedIncludes, withAllIncludes});
154-
}
155-
156-
export function fromItemToJson(item, requestedIncludes, withAllIncludes = false) {
157-
return dataFormatter.serialize({item, requestedIncludes, withAllIncludes});
158-
}
159-
160-
export function fromJsonToItemOrCollection(jsonApiBody) {
161-
const deserialized = dataFormatter.deserialize(jsonApiBody);
162-
163-
return {
164-
item: deserialized.item || null,
165-
collection: deserialized.collection || null,
166-
meta: deserialized.meta || null,
167-
};
168-
}
93+
##### denormalizeReduxObject - creates simplified object(s) from reduxObject
94+
"reduxObject" - result object of [json-api-normalizer](https://github.com/yury-dymov/json-api-normalizer)
16995

170-
export default {
171-
fromJsonToCollection,
172-
fromJsonToItem,
173-
fromCollectionToJson,
174-
fromItemToJson,
175-
fromJsonToItemOrCollection,
176-
};
177-
```
178-
179-
**Town.js** (example of model)
18096
```javascript
181-
import MyBaseEntity from './MyBaseEntity';
182-
import Region from './Region';
183-
import Country from './Country';
184-
185-
class Town extends MyBaseEntity {
186-
id: string;
187-
type: string;
188-
189-
name: string;
190-
nameGenitive: string;
191-
timeZone: string;
192-
193-
region: Region;
194-
country: Country;
195-
196-
getRelationships() {
197-
return {
198-
region: this.region,
199-
country: this.country
200-
}
201-
}
202-
}
203-
204-
export default Town;
97+
const reduxObject = reduxStore.entities; // depends on where you store it
98+
const model = dataFormatter.denormalizeReduxObject({reduxObject, 'town', '123');
99+
console.log(newJson); // if there is such town and country in reduxObject, it will output:
100+
/* {
101+
type: 'town',
102+
id: '21',
103+
name: 'Shanghai',
104+
country: {
105+
type: 'country',
106+
id: '34',
107+
name: 'Spain'
108+
}
109+
} */
205110
```
206111
207-
**MyBaseEntity.js**
112+
*NOTE:* You can control process of building this objects, just use your own [propertyMappers](https://github.com/olosegres/jsona/src/simplePropertyMappers.js) when Jsona instantiates.
113+
So, it may be easier to use, if you will create a proxy module in your project, something like this:
208114
```javascript
209-
import {BaseJsonaModel} from 'jsona';
210-
211-
class MyBaseEntity extends BaseJsonaModel {
212-
constructor(props) {
213-
super();
214-
215-
Object.keys(params).forEach((k) => {
216-
this[k] = params[k];
217-
});
218-
}
219-
}
115+
import Jsona from 'jsona';
116+
import {MyModelPropertiesMapper, MyJsonPropertiesMapper} from 'myPropertyMappers';
220117

221-
export default MyBaseEntity;
118+
export default const dataFormatter = new Jsona({
119+
modelPropertiesMapper: MyModelPropertiesMapper,
120+
jsonPropertiesMapper: MyJsonPropertiesMapper
121+
});
222122
```
223123
224124
### License

0 commit comments

Comments
 (0)