Skip to content

Commit 92ddd95

Browse files
authored
Merge pull request #17 from alphamanuscript/add-create-entity-3
Allows creating entities
2 parents afa4a4c + 1de7d9a commit 92ddd95

File tree

16 files changed

+357
-24
lines changed

16 files changed

+357
-24
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@
22

33
The Surix Javascript SDK is a set of libraries and utilities used to interact with the Surix platform in Node.js or browsers using Javascript.
44

5-
5+
| Name | Description |
6+
|--------------|----------------------------------------|
7+
| [client](packages/client/README.md) | An SDK to help interact with surix API |
8+
| [data-helpers](packages/data-helpers/README.md) | Has helper functions for simplicity |

packages/client/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,38 @@ const project = client.project('projectId');
3939

4040
### Entities
4141

42+
#### `project.entities.create(entity: Entity)`
43+
44+
Creates a given entity
45+
46+
```javascript
47+
const entity = {
48+
data: {
49+
name: 'My Awesome Name'
50+
},
51+
tags: []
52+
}
53+
54+
const savedEntity = await project.entities.create(entity)
55+
56+
console.log(savedEntity.get('name'))
57+
```
58+
59+
Note: Before the entity is created, it is expanded (using `expandEntity` function in `@surix/data-helpers`) to a raw entity that includes the types information using the type of the values provided in the data field.
60+
The entity above would be converted to:
61+
62+
```javascript
63+
{
64+
data: {
65+
name: {
66+
type: 'text',
67+
value: 'My Awesome Name'
68+
}
69+
},
70+
tags: []
71+
}
72+
```
73+
Dates are expanded as `text` for now and will correctly be expanded to `datetime` in future versions of `@surix/data-helpers`. Arrays are not supported for now.
4274
#### `project.entities.get(id: string)`
4375

4476
Fetches an entity by ID.

packages/client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
"webpack-cli": "^3.1.2"
6363
},
6464
"dependencies": {
65-
"@surix/data-helpers": "^0.6.0",
65+
"@surix/data-helpers": "^0.7.1",
6666
"axios": "^0.18.0"
6767
}
6868
}
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
1-
import { ApiEntity, wrapEntity, wrapEntityArray, WrappedEntity } from '@surix/data-helpers';
1+
import { ApiEntity, expandEntity, wrapEntity, wrapEntityArray, WrappedEntity} from '@surix/data-helpers';
22
import { AxiosInstance } from 'axios';
3-
import { ProjectEntities, Query, TagList } from '../types';
3+
import { Entity, ProjectEntities, Query } from '../types';
44

55
export function getProjectEntities (projectId: string, apiClient: AxiosInstance): ProjectEntities {
66
return {
7-
async get (entityId: string): Promise<WrappedEntity> {
7+
async get(entityId: string): Promise<WrappedEntity> {
88
const res = await apiClient.get<ApiEntity>(`/projects/${projectId}/entities/${entityId}`);
99
return wrapEntity(res.data);
1010
},
1111
async query(query: Query = {}): Promise<WrappedEntity[]> {
1212
const res = await apiClient.post<ApiEntity[]>(`/projects/${projectId}/entities/query`, query);
1313
const entities = res.data;
1414
return wrapEntityArray(entities);
15+
},
16+
async create(entity: Entity): Promise<WrappedEntity> {
17+
const expandedEntity = expandEntity(entity);
18+
const res = await apiClient.post<ApiEntity>(
19+
`/projects/${projectId}/entities`, expandedEntity);
20+
const returnedEntity = res.data;
21+
return wrapEntity(returnedEntity);
1522
}
1623
};
1724
}

packages/client/src/project/tests/__snapshots__/entities.test.ts.snap

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@ EntityWrapper {
1111
},
1212
"data": Object {
1313
"name": Object {
14-
"label": "Label",
15-
"name": "name",
1614
"type": "text",
17-
"value": "Foo",
15+
"value": "name",
1816
},
1917
},
2018
"tags": Array [
@@ -27,6 +25,33 @@ EntityWrapper {
2725
}
2826
`;
2927

28+
exports[`Project Entities query create should return an error 1`] = `
29+
EntityWrapper {
30+
"_entity": Object {
31+
"entity": Object {
32+
"_id": "e1",
33+
"createdAt": "2017-02-22T09:19:33.885Z",
34+
"createdBy": Object {
35+
"_id": "u1",
36+
"type": "user",
37+
},
38+
"data": Object {
39+
"name": Object {
40+
"type": "text",
41+
"value": "name",
42+
},
43+
},
44+
"tags": Array [
45+
"t1",
46+
],
47+
"updatedAt": "2018-10-22T09:19:33.885Z",
48+
},
49+
},
50+
"createdAt": Date { NaN },
51+
"updatedAt": Date { NaN },
52+
}
53+
`;
54+
3055
exports[`Project Entities query should return the entities transformed to array of WrappedEntity 1`] = `
3156
Array [
3257
EntityWrapper {
@@ -39,10 +64,8 @@ Array [
3964
},
4065
"data": Object {
4166
"name": Object {
42-
"label": "Label",
43-
"name": "name",
4467
"type": "text",
45-
"value": "Foo",
68+
"value": "name",
4669
},
4770
},
4871
"tags": Array [
@@ -63,10 +86,8 @@ Array [
6386
},
6487
"data": Object {
6588
"name": Object {
66-
"label": "Label",
67-
"name": "name",
6889
"type": "text",
69-
"value": "Bar",
90+
"value": "name",
7091
},
7192
},
7293
"tags": Array [

packages/client/src/project/tests/entities.test.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ describe('Project Entities', () => {
1616
data: {
1717
name: {
1818
type: 'text',
19-
value: 'Foo',
20-
name: 'name',
21-
label: 'Label'
19+
value: 'name'
2220
}
2321
}
2422
},
@@ -31,9 +29,7 @@ describe('Project Entities', () => {
3129
data: {
3230
name: {
3331
type: 'text',
34-
value: 'Bar',
35-
name: 'name',
36-
label: 'Label'
32+
value: 'name'
3733
}
3834
}
3935
}
@@ -86,5 +82,36 @@ describe('Project Entities', () => {
8682
expect(apiClient.post).toHaveBeenCalledWith(`/projects/project1/entities/query`, {});
8783
});
8884
});
85+
86+
describe('create', () => {
87+
const userEntity = {
88+
data: {
89+
name: 'someone 1'
90+
},
91+
tags: []
92+
}
93+
let apiClient: AxiosInstance;
94+
async function callMockCreate (entity: any): Promise<dataHelpers.WrappedEntity> {
95+
apiClient = api.getApiClient('http://baseurl', 'somekey');
96+
jest.spyOn(apiClient, 'post').mockReturnValue(Promise.resolve({ data:
97+
{ entity } }));
98+
jest.spyOn(dataHelpers, 'wrapEntity');
99+
const project = getProjectApi('project1', apiClient);
100+
const ent = await project.entities.create(entity);
101+
return ent;
102+
}
103+
it('should call POST /projects/:pid/entities endpoint with entity', async () => {
104+
const entity = userEntity;
105+
const expectedEntity = dataHelpers.expandEntity(entity)
106+
await callMockCreate(entity);
107+
expect(apiClient.post).toHaveBeenCalledWith('/projects/project1/entities', expectedEntity);
108+
});
109+
it('should return an error', async () => {
110+
const ent = apiEntities[0];
111+
const entity = await callMockCreate(ent);
112+
expect(dataHelpers.wrapEntity).toHaveBeenCalledWith(ent);
113+
expect(entity).toMatchSnapshot();
114+
});
115+
});
89116
});
90117
});

packages/client/src/types.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface Project {
99
export interface ProjectEntities {
1010
get(id: string): Promise<WrappedEntity>;
1111
query(query?: Query): Promise<WrappedEntity[]>;
12+
create(entity: any): Promise<WrappedEntity>;
1213
}
1314

1415
export interface ProjectFiles {
@@ -31,4 +32,45 @@ export interface TagListItem {
3132
name: string;
3233
}
3334

34-
export type TagList = TagListItem[];
35+
export type TagList = TagListItem[];
36+
37+
export enum FieldType {
38+
TEXT = 'text',
39+
BOOLEAN = 'boolean',
40+
NUMBER = 'number',
41+
DATETIME = 'datetime',
42+
PHONE = 'phone',
43+
EMAIL = 'email',
44+
FILE = 'file',
45+
OBJECT = 'object',
46+
LIST = 'list'
47+
}
48+
49+
export interface FieldPair {
50+
type: FieldType;
51+
value: RawEntityData | string | RawEntityData[] | number;
52+
}
53+
54+
export interface RawEntityField extends FieldPair {
55+
}
56+
57+
export interface RawEntityData {
58+
[fieldName: string]: RawEntityField;
59+
}
60+
61+
/**
62+
* this interface describe the shape of the entity
63+
* as used within the system and the API
64+
*/
65+
export interface RawEntity {
66+
data: RawEntityData;
67+
tags?: string[];
68+
}
69+
70+
export interface DataField {
71+
[fieldName: string]: string | number | object | boolean;
72+
}
73+
export interface Entity {
74+
data: DataField;
75+
tags?: string[];
76+
}

packages/data-helpers/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,35 @@ const {
3737
} = require('@surix/data-helpers')
3838
```
3939

40+
### `expnadEntity`
41+
42+
Converts a user friendly version of an entity to the raw version of the entity.
43+
*Note: For now, dates are interpreted `text` type but will be updated to `datatime` in future*
44+
```javascript
45+
import { expandEntity } from '@surix/data-helpers';
46+
47+
const entity = {
48+
data: {
49+
name: 'My Name',
50+
age: 12,
51+
children: [
52+
{
53+
name: 'Some name'
54+
}
55+
],
56+
address: {
57+
location: 'Nairobi'
58+
},
59+
isOldEnough: true
60+
},
61+
tags: []
62+
};
63+
64+
const expandedEntity = expandEntity(entity);
65+
```
66+
67+
**Note:** `Date()` will be converted to a string. `new Date()` will be converted to `datetime` so if you want dated treated correctly, you can use `new Date()`
68+
4069
### `wrapEntity`
4170

4271
```javascript

packages/data-helpers/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@surix/data-helpers",
3-
"version": "0.7.0",
3+
"version": "0.7.1",
44
"description": "Utilities for making it easy to work with Surix data",
55
"main": "dist/index.js",
66
"typings": "dist/index.d.ts",
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { DataField, Entity, RawEntity, RawEntityData } from '../types';
2+
3+
function expandEntity (entity: Entity): RawEntity {
4+
const { data } = entity;
5+
const convertedData = expandEntityData(data);
6+
// delete entity.data;
7+
// @ts-ignore
8+
return { ...entity, data: convertedData };
9+
}
10+
11+
function expandEntityData (data: DataField): RawEntityData {
12+
const expandedEntity = Object.keys(data)
13+
.reduce((expanded, key) => {
14+
let converted = {};
15+
switch(typeof data[key]) {
16+
case 'object':
17+
if(Array.isArray(data[key])) {
18+
converted = {
19+
type: 'list',
20+
// @ts-ignore
21+
value: data[key].map(item => expandEntityData(item))
22+
};
23+
} else if (data[key] instanceof Date) {
24+
converted = {
25+
type: 'datetime',
26+
value: data[key]
27+
};
28+
} else {
29+
converted = {
30+
type: 'object',
31+
// @ts-ignore
32+
value: expandEntityData(data[key])
33+
}
34+
}
35+
break;
36+
case 'string':
37+
converted = {
38+
type: 'text',
39+
value: data[key]
40+
};
41+
break;
42+
default:
43+
converted = {
44+
type: typeof data[key],
45+
value: data[key]
46+
};
47+
break;
48+
}
49+
return { ...expanded, [key]: converted };
50+
}, {});
51+
return expandedEntity;
52+
}
53+
54+
export {
55+
expandEntity
56+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { expandEntity } from './expand-entity';

0 commit comments

Comments
 (0)