@laboratoria/sdk-js 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/core.js +0 -15
- package/lib/model.js +66 -9
- package/package.json +7 -7
- package/.github/workflows/node.js.yml +0 -31
- package/index.spec.js +0 -112
- package/lib/client.spec.js +0 -77
- package/lib/currencies.js +0 -3
- package/lib/curriculum.spec.js +0 -194
- package/lib/model.spec.js +0 -242
package/lib/core.js
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
import md5 from 'blueimp-md5';
|
2
|
-
import currencies from './currencies';
|
3
|
-
import roles from './roles';
|
4
2
|
import { createModels, extendSchemaDefinitions } from './model';
|
5
3
|
import schema from '../schemas/core.json';
|
6
4
|
|
@@ -135,9 +133,6 @@ const extended = {
|
|
135
133
|
countryCode: {
|
136
134
|
enum: ['BR', 'CL', 'CO', 'MX', 'PE'],
|
137
135
|
},
|
138
|
-
currency: {
|
139
|
-
enum: currencies,
|
140
|
-
},
|
141
136
|
},
|
142
137
|
},
|
143
138
|
Gig: {
|
@@ -150,11 +145,6 @@ const extended = {
|
|
150
145
|
'start',
|
151
146
|
'end',
|
152
147
|
],
|
153
|
-
properties: {
|
154
|
-
role: {
|
155
|
-
enum: roles,
|
156
|
-
},
|
157
|
-
},
|
158
148
|
},
|
159
149
|
};
|
160
150
|
|
@@ -162,8 +152,3 @@ export const createAPI = (url, state) => createModels(url, state, {
|
|
162
152
|
...schema,
|
163
153
|
definitions: extendSchemaDefinitions(schema, extended),
|
164
154
|
});
|
165
|
-
|
166
|
-
// const {
|
167
|
-
// delete: _,
|
168
|
-
// ...userAPI,
|
169
|
-
// } = createModel(coreApiUrl, state, 'users', schemas.user);
|
package/lib/model.js
CHANGED
@@ -1,6 +1,38 @@
|
|
1
1
|
import { createClient } from './client.js';
|
2
2
|
|
3
3
|
|
4
|
+
const isRequiredOneToOneRelation = (schema, key) => (
|
5
|
+
!!schema.properties
|
6
|
+
&& !!schema.properties[key]
|
7
|
+
&& !!schema.properties[key].$ref
|
8
|
+
);
|
9
|
+
|
10
|
+
const isOptionalOneToOneRelation = (schema, key) => (
|
11
|
+
!!schema.properties
|
12
|
+
&& !!schema.properties[key]
|
13
|
+
&& Array.isArray(schema.properties[key].anyOf)
|
14
|
+
&& !!schema.properties[key].anyOf[0]?.$ref
|
15
|
+
&& schema.properties[key].anyOf[1]?.type === 'null'
|
16
|
+
);
|
17
|
+
|
18
|
+
const isOneToOneRelation = (schema, key) => (
|
19
|
+
isRequiredOneToOneRelation(schema, key)
|
20
|
+
|| isOptionalOneToOneRelation(schema, key)
|
21
|
+
);
|
22
|
+
|
23
|
+
const isOneToManyRelation = (schema, key) => (
|
24
|
+
schema.properties
|
25
|
+
&& schema.properties[key]
|
26
|
+
&& schema.properties[key].type === 'array'
|
27
|
+
&& !!schema.properties[key].items.$ref
|
28
|
+
);
|
29
|
+
|
30
|
+
// const isRelation = (schema, key) => (
|
31
|
+
// isOneToOneRelation(schema, key)
|
32
|
+
// || isOneToManyRelation(schema, key)
|
33
|
+
// );
|
34
|
+
|
35
|
+
|
4
36
|
const createValidator = (schema) => {
|
5
37
|
const properties = schema.properties || {};
|
6
38
|
const inputProps = schema.inputProps || Object.keys(properties);
|
@@ -78,15 +110,7 @@ const serializeData = (data, schema) => {
|
|
78
110
|
if (hasInputProps && !schema.inputProps.includes(key)) {
|
79
111
|
return memo;
|
80
112
|
}
|
81
|
-
|
82
|
-
const isOptionalRelation = (
|
83
|
-
prop
|
84
|
-
&& Array.isArray(prop.anyOf)
|
85
|
-
&& prop.anyOf[0].$ref
|
86
|
-
&& prop.anyOf[1]
|
87
|
-
&& prop.anyOf[1].type === 'null'
|
88
|
-
);
|
89
|
-
if (isOptionalRelation && data[key] === null) {
|
113
|
+
if (isOptionalOneToOneRelation(schema, key) && data[key] === null) {
|
90
114
|
return memo;
|
91
115
|
}
|
92
116
|
return {
|
@@ -144,10 +168,43 @@ export const createModel = (baseUrl, state, collectionName, schema = {}) => {
|
|
144
168
|
);
|
145
169
|
};
|
146
170
|
|
171
|
+
const relations = Object.keys(schema.properties || {}).reduce(
|
172
|
+
(memo, key) => (
|
173
|
+
isRequiredOneToOneRelation(schema, key)
|
174
|
+
? Object.assign(memo, {
|
175
|
+
all: memo.all.concat(key),
|
176
|
+
oneToOne: memo.oneToOne.concat(key),
|
177
|
+
requiredOneToOne: memo.requiredOneToOne.concat(key),
|
178
|
+
})
|
179
|
+
: isOptionalOneToOneRelation(schema, key)
|
180
|
+
? Object.assign(memo, {
|
181
|
+
all: memo.all.concat(key),
|
182
|
+
oneToOne: memo.oneToOne.concat(key),
|
183
|
+
optionalOneToOne: memo.optionalOneToOne.concat(key),
|
184
|
+
})
|
185
|
+
: isOneToManyRelation(schema, key)
|
186
|
+
? Object.assign(memo, {
|
187
|
+
all: memo.all.concat(key),
|
188
|
+
oneToMany: memo.oneToMany.concat(key),
|
189
|
+
})
|
190
|
+
: memo
|
191
|
+
),
|
192
|
+
{
|
193
|
+
all: [],
|
194
|
+
oneToOne: [],
|
195
|
+
requiredOneToOne: [],
|
196
|
+
optionalOneToOne: [],
|
197
|
+
oneToMany: [],
|
198
|
+
},
|
199
|
+
);
|
200
|
+
|
147
201
|
return {
|
148
202
|
get schema() {
|
149
203
|
return schema;
|
150
204
|
},
|
205
|
+
get relations() {
|
206
|
+
return relations;
|
207
|
+
},
|
151
208
|
validateAttr: validator.validateAttr,
|
152
209
|
validate: validator.validate,
|
153
210
|
findMany: q => req(buildURL(null, q))
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@laboratoria/sdk-js",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.1.0",
|
4
4
|
"description": "Laboratoria JavaScript (browser) SDK",
|
5
5
|
"scripts": {
|
6
6
|
"test": "jest --verbose --coverage",
|
@@ -9,25 +9,25 @@
|
|
9
9
|
"license": "MIT",
|
10
10
|
"dependencies": {
|
11
11
|
"blueimp-md5": "^2.19.0",
|
12
|
-
"firebase": "^9.6.
|
12
|
+
"firebase": "^9.6.5"
|
13
13
|
},
|
14
14
|
"devDependencies": {
|
15
15
|
"@babel/core": "^7.16.12",
|
16
16
|
"@babel/plugin-transform-modules-commonjs": "^7.16.8",
|
17
17
|
"babel-jest": "^27.4.6",
|
18
18
|
"jest": "^27.4.7",
|
19
|
-
"webpack": "^5.
|
19
|
+
"webpack": "^5.68.0",
|
20
20
|
"webpack-cli": "^4.9.2"
|
21
21
|
},
|
22
22
|
"jest": {
|
23
23
|
"testEnvironment": "jsdom",
|
24
24
|
"coverageThreshold": {
|
25
25
|
"global": {
|
26
|
-
"statements":
|
27
|
-
"branches":
|
26
|
+
"statements": 97,
|
27
|
+
"branches": 92,
|
28
28
|
"functions": 98,
|
29
|
-
"lines":
|
29
|
+
"lines": 97
|
30
30
|
}
|
31
31
|
}
|
32
32
|
}
|
33
|
-
}
|
33
|
+
}
|
@@ -1,31 +0,0 @@
|
|
1
|
-
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
2
|
-
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
3
|
-
|
4
|
-
name: Node.js CI
|
5
|
-
|
6
|
-
on:
|
7
|
-
push:
|
8
|
-
branches: [ main ]
|
9
|
-
pull_request:
|
10
|
-
branches: [ main ]
|
11
|
-
|
12
|
-
jobs:
|
13
|
-
build:
|
14
|
-
|
15
|
-
runs-on: ubuntu-latest
|
16
|
-
|
17
|
-
strategy:
|
18
|
-
matrix:
|
19
|
-
node-version: [14.x, 16.x]
|
20
|
-
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
21
|
-
|
22
|
-
steps:
|
23
|
-
- uses: actions/checkout@v2
|
24
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
25
|
-
uses: actions/setup-node@v2
|
26
|
-
with:
|
27
|
-
node-version: ${{ matrix.node-version }}
|
28
|
-
cache: 'npm'
|
29
|
-
- run: npm ci
|
30
|
-
- run: npm run build --if-present
|
31
|
-
- run: npm test
|
package/index.spec.js
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
import { initializeApp } from 'firebase/app';
|
2
|
-
import {
|
3
|
-
getAuth,
|
4
|
-
onAuthStateChanged,
|
5
|
-
signInWithEmailAndPassword,
|
6
|
-
signOut,
|
7
|
-
} from 'firebase/auth';
|
8
|
-
import { createAPI as createCoreAPI } from './lib/core';
|
9
|
-
import { createApp } from './index.js';
|
10
|
-
|
11
|
-
jest.mock('./lib/core.js', () => ({
|
12
|
-
createAPI: jest.fn().mockReturnValue({
|
13
|
-
user: {
|
14
|
-
findById: jest.fn().mockResolvedValue({}),
|
15
|
-
},
|
16
|
-
}),
|
17
|
-
}));
|
18
|
-
|
19
|
-
beforeEach(() => {
|
20
|
-
initializeApp.mockClear();
|
21
|
-
onAuthStateChanged.mockClear();
|
22
|
-
signInWithEmailAndPassword.mockClear();
|
23
|
-
});
|
24
|
-
|
25
|
-
describe('createApp', () => {
|
26
|
-
it('should invoke firebase\'s initializaApp', () => {
|
27
|
-
createApp();
|
28
|
-
expect(initializeApp).toHaveBeenCalledTimes(1);
|
29
|
-
expect(initializeApp.mock.calls[0]).toMatchSnapshot();
|
30
|
-
});
|
31
|
-
});
|
32
|
-
|
33
|
-
describe('app.auth.onChange', () => {
|
34
|
-
it('should listen to firebase\'s onAuthStateChanged and notify subscribers when not authenticated', (done) => {
|
35
|
-
const { auth } = createApp();
|
36
|
-
|
37
|
-
onAuthStateChanged.mockImplementationOnce((_, cb) => {
|
38
|
-
cb();
|
39
|
-
return () => { };
|
40
|
-
});
|
41
|
-
|
42
|
-
auth.onChange(({ authUser, user }) => {
|
43
|
-
expect(authUser).toBeNull();
|
44
|
-
expect(user).toBeNull();
|
45
|
-
done();
|
46
|
-
});
|
47
|
-
});
|
48
|
-
|
49
|
-
it('should fetch user from db when authenticated using coreAPI', (done) => {
|
50
|
-
const { auth } = createApp();
|
51
|
-
const mockAuthUser = { uid: 'xxx', getIdToken: () => 'token' };
|
52
|
-
const userMock = { uid: 'xxx', email: 'foo@bar.baz' };
|
53
|
-
|
54
|
-
onAuthStateChanged.mockImplementationOnce((_, cb) => {
|
55
|
-
cb(mockAuthUser);
|
56
|
-
return () => { };
|
57
|
-
});
|
58
|
-
|
59
|
-
createCoreAPI().user.findById.mockResolvedValue(userMock);
|
60
|
-
|
61
|
-
auth.onChange(({ authUser, user }) => {
|
62
|
-
expect(authUser).toEqual(mockAuthUser);
|
63
|
-
expect(user).toEqual(userMock);
|
64
|
-
done();
|
65
|
-
});
|
66
|
-
});
|
67
|
-
|
68
|
-
it('should log error and reset state (authUser = null, user = null) when fetch user from db fails', (done) => {
|
69
|
-
const { auth } = createApp();
|
70
|
-
const mockAuthUser = { uid: 'xxx', getIdToken: () => 'token' };
|
71
|
-
const spy = jest.spyOn(console, 'error').mockImplementationOnce(() => { })
|
72
|
-
const error = new Error('OMG');
|
73
|
-
|
74
|
-
onAuthStateChanged.mockImplementationOnce((_, cb) => {
|
75
|
-
cb(mockAuthUser);
|
76
|
-
return () => { };
|
77
|
-
});
|
78
|
-
|
79
|
-
createCoreAPI().user.findById.mockRejectedValueOnce(error);
|
80
|
-
|
81
|
-
auth.onChange(({ authUser, user }) => {
|
82
|
-
expect(authUser).toBeNull();
|
83
|
-
expect(user).toBeNull();
|
84
|
-
expect(spy).toHaveBeenCalledWith(error);
|
85
|
-
spy.mockRestore();
|
86
|
-
done();
|
87
|
-
});
|
88
|
-
});
|
89
|
-
});
|
90
|
-
|
91
|
-
describe('app.auth.signIn', () => {
|
92
|
-
it('should delegate to firebase\'s signInWithEmailAndPassword', async () => {
|
93
|
-
const { auth } = createApp();
|
94
|
-
const email = 'foo@bar.baz';
|
95
|
-
const pass = 'secret';
|
96
|
-
|
97
|
-
await auth.signIn(email, pass);
|
98
|
-
|
99
|
-
expect(signInWithEmailAndPassword).toHaveBeenCalledTimes(1);
|
100
|
-
expect(signInWithEmailAndPassword).toHaveBeenCalledWith({}, email, pass);
|
101
|
-
});
|
102
|
-
});
|
103
|
-
|
104
|
-
describe('app.auth.signOut', () => {
|
105
|
-
it('should delegate to firebase\'s signOut', async () => {
|
106
|
-
const { auth } = createApp();
|
107
|
-
|
108
|
-
await auth.signOut();
|
109
|
-
|
110
|
-
expect(signOut).toHaveBeenCalledTimes(1);
|
111
|
-
});
|
112
|
-
});
|
package/lib/client.spec.js
DELETED
@@ -1,77 +0,0 @@
|
|
1
|
-
import { createClient } from './client.js';
|
2
|
-
|
3
|
-
describe('client', () => {
|
4
|
-
|
5
|
-
beforeAll(() => window.fetch = jest.fn());
|
6
|
-
|
7
|
-
afterEach(() => {
|
8
|
-
window.fetch.mockClear();
|
9
|
-
jest.restoreAllMocks();
|
10
|
-
});
|
11
|
-
|
12
|
-
it('should delegate to global fetch and add mode:cors and parse JSON resp when OK', async () => {
|
13
|
-
window.fetch.mockResolvedValue({
|
14
|
-
json: jest.fn().mockResolvedValue({ ok: true }),
|
15
|
-
});
|
16
|
-
const baseUrl = 'http://foo.bar';
|
17
|
-
const client = createClient(baseUrl);
|
18
|
-
expect(await client('/')).toEqual({ ok: true });
|
19
|
-
expect(window.fetch).toHaveBeenCalledTimes(1);
|
20
|
-
expect(window.fetch).toHaveBeenCalledWith(`${baseUrl}/`, {
|
21
|
-
mode: 'cors',
|
22
|
-
headers: {},
|
23
|
-
});
|
24
|
-
});
|
25
|
-
|
26
|
-
it('should reject when response.status > 202', async () => {
|
27
|
-
window.fetch.mockResolvedValue({
|
28
|
-
status: 400,
|
29
|
-
json: jest.fn().mockResolvedValue({ ok: false }),
|
30
|
-
});
|
31
|
-
const baseUrl = 'http://foo.bar';
|
32
|
-
const client = createClient(baseUrl);
|
33
|
-
await expect(client('/')).rejects.toThrow('HTTP Error 400');
|
34
|
-
expect(window.fetch).toHaveBeenCalledTimes(1);
|
35
|
-
expect(window.fetch).toHaveBeenCalledWith(`${baseUrl}/`, {
|
36
|
-
mode: 'cors',
|
37
|
-
headers: {},
|
38
|
-
});
|
39
|
-
});
|
40
|
-
|
41
|
-
it('should add token when authUser present', async () => {
|
42
|
-
window.fetch.mockResolvedValue({
|
43
|
-
json: jest.fn().mockResolvedValue({ ok: true }),
|
44
|
-
});
|
45
|
-
const baseUrl = 'http://foo.bar';
|
46
|
-
const user = { getIdToken: jest.fn().mockReturnValueOnce('xxx') };
|
47
|
-
const client = createClient(baseUrl, user);
|
48
|
-
expect(await client('/')).toEqual({ ok: true });
|
49
|
-
expect(window.fetch).toHaveBeenCalledTimes(1);
|
50
|
-
expect(window.fetch).toHaveBeenCalledWith(`${baseUrl}/`, {
|
51
|
-
mode: 'cors',
|
52
|
-
headers: {
|
53
|
-
authorization: 'Bearer xxx',
|
54
|
-
},
|
55
|
-
});
|
56
|
-
expect(user.getIdToken).toHaveBeenCalledTimes(1);
|
57
|
-
});
|
58
|
-
|
59
|
-
it('should add content-type header and stringify body when necessary', async () => {
|
60
|
-
window.fetch.mockResolvedValue({
|
61
|
-
json: jest.fn().mockResolvedValue({ ok: true }),
|
62
|
-
});
|
63
|
-
const baseUrl = 'http://foo.bar';
|
64
|
-
const client = createClient(baseUrl);
|
65
|
-
const body = { foo: 'bar' };
|
66
|
-
expect(await client('/', { method: 'POST', body })).toEqual({ ok: true });
|
67
|
-
expect(window.fetch).toHaveBeenCalledTimes(1);
|
68
|
-
expect(window.fetch).toHaveBeenCalledWith(`${baseUrl}/`, {
|
69
|
-
mode: 'cors',
|
70
|
-
headers: {
|
71
|
-
'content-type': 'application/json',
|
72
|
-
},
|
73
|
-
method: 'POST',
|
74
|
-
body: JSON.stringify(body),
|
75
|
-
});
|
76
|
-
});
|
77
|
-
});
|
package/lib/currencies.js
DELETED
package/lib/curriculum.spec.js
DELETED
@@ -1,194 +0,0 @@
|
|
1
|
-
import { createAPI } from './curriculum';
|
2
|
-
|
3
|
-
describe('projects', () => {
|
4
|
-
describe('findMany', () => {
|
5
|
-
// TODO: errores HTTP, GitHub caido, repo borrado, etc...
|
6
|
-
|
7
|
-
it('sends request to get latest version and then corresponding json', () => {
|
8
|
-
global.fetch = jest.fn().mockImplementation(async url => ({
|
9
|
-
status: 200,
|
10
|
-
json: jest.fn().mockResolvedValue(
|
11
|
-
url.startsWith('https://api.')
|
12
|
-
? { tag_name: 'v4.0.0' }
|
13
|
-
: []
|
14
|
-
),
|
15
|
-
}));
|
16
|
-
|
17
|
-
return createAPI().projects.findMany()
|
18
|
-
.then((projects) => {
|
19
|
-
expect(projects).toEqual([]);
|
20
|
-
expect(global.fetch).toHaveBeenCalledTimes(2);
|
21
|
-
expect(global.fetch).toHaveBeenNthCalledWith(
|
22
|
-
1,
|
23
|
-
'https://api.github.com/repos/Laboratoria/bootcamp/releases/latest',
|
24
|
-
);
|
25
|
-
expect(global.fetch).toHaveBeenNthCalledWith(
|
26
|
-
2,
|
27
|
-
'https://raw.githubusercontent.com/Laboratoria/bootcamp/v4.0.0/dist/projects.json',
|
28
|
-
);
|
29
|
-
});
|
30
|
-
});
|
31
|
-
|
32
|
-
it('gets projects in specific tag when passed as option', () => {
|
33
|
-
global.fetch = jest.fn().mockImplementation(async url => ({
|
34
|
-
status: 200,
|
35
|
-
json: jest.fn().mockResolvedValue(
|
36
|
-
url.startsWith('https://api.')
|
37
|
-
? { tag_name: 'v4.0.0' }
|
38
|
-
: []
|
39
|
-
),
|
40
|
-
}));
|
41
|
-
|
42
|
-
return createAPI().projects.findMany({ tag: 'v3.0.0' })
|
43
|
-
.then((projects) => {
|
44
|
-
expect(projects).toEqual([]);
|
45
|
-
expect(global.fetch).toHaveBeenCalledTimes(1);
|
46
|
-
expect(global.fetch).toHaveBeenCalledWith(
|
47
|
-
'https://raw.githubusercontent.com/Laboratoria/bootcamp/v3.0.0/dist/projects.json',
|
48
|
-
);
|
49
|
-
});
|
50
|
-
});
|
51
|
-
|
52
|
-
it('gets projects in specific tag when passed as option', () => {
|
53
|
-
global.fetch = jest.fn().mockImplementation(async url => ({
|
54
|
-
status: 200,
|
55
|
-
json: jest.fn().mockResolvedValue(
|
56
|
-
url.startsWith('https://api.')
|
57
|
-
? { tag_name: 'v4.0.0' }
|
58
|
-
: [
|
59
|
-
{ locale: 'es-ES', track: 'ux' },
|
60
|
-
{ locale: 'es-ES', track: 'js' },
|
61
|
-
{ locale: 'pt-BR', track: 'ux' },
|
62
|
-
]
|
63
|
-
),
|
64
|
-
}));
|
65
|
-
|
66
|
-
return createAPI().projects.findMany({
|
67
|
-
tag: 'v3.0.0',
|
68
|
-
locale: 'es-ES',
|
69
|
-
track: 'ux',
|
70
|
-
})
|
71
|
-
.then((projects) => {
|
72
|
-
expect(projects).toEqual([{ locale: 'es-ES', track: 'ux' }]);
|
73
|
-
expect(global.fetch).toHaveBeenCalledTimes(1);
|
74
|
-
expect(global.fetch).toHaveBeenCalledWith(
|
75
|
-
'https://raw.githubusercontent.com/Laboratoria/bootcamp/v3.0.0/dist/projects.json',
|
76
|
-
);
|
77
|
-
});
|
78
|
-
});
|
79
|
-
|
80
|
-
// it should filter by tag, locale and track...
|
81
|
-
});
|
82
|
-
|
83
|
-
describe('findById', () => {
|
84
|
-
it('sends request to get latest version and then corresponding json', () => {
|
85
|
-
global.fetch = jest.fn().mockImplementation(async url => ({
|
86
|
-
status: 200,
|
87
|
-
json: jest.fn().mockResolvedValue(
|
88
|
-
url.startsWith('https://api.')
|
89
|
-
? { tag_name: 'v4.0.0' }
|
90
|
-
: {}
|
91
|
-
),
|
92
|
-
}));
|
93
|
-
|
94
|
-
return createAPI().projects.findById('cipher')
|
95
|
-
.then((project) => {
|
96
|
-
expect(project).toEqual({});
|
97
|
-
expect(global.fetch).toHaveBeenCalledTimes(2);
|
98
|
-
expect(global.fetch).toHaveBeenNthCalledWith(
|
99
|
-
1,
|
100
|
-
'https://api.github.com/repos/Laboratoria/bootcamp/releases/latest',
|
101
|
-
);
|
102
|
-
expect(global.fetch).toHaveBeenNthCalledWith(
|
103
|
-
2,
|
104
|
-
'https://raw.githubusercontent.com/Laboratoria/bootcamp/v4.0.0/dist/projects/cipher.json',
|
105
|
-
);
|
106
|
-
});
|
107
|
-
});
|
108
|
-
});
|
109
|
-
});
|
110
|
-
|
111
|
-
describe('topics', () => {
|
112
|
-
describe('findMany', () => {
|
113
|
-
it('sends request to get latest version and then corresponding json', () => {
|
114
|
-
global.fetch = jest.fn().mockImplementation(async url => ({
|
115
|
-
status: 200,
|
116
|
-
json: jest.fn().mockResolvedValue(
|
117
|
-
url.startsWith('https://api.')
|
118
|
-
? { tag_name: 'v4.0.0' }
|
119
|
-
: []
|
120
|
-
),
|
121
|
-
}));
|
122
|
-
|
123
|
-
return createAPI().topics.findMany()
|
124
|
-
.then((topics) => {
|
125
|
-
expect(topics).toEqual([]);
|
126
|
-
expect(global.fetch).toHaveBeenCalledTimes(2);
|
127
|
-
expect(global.fetch).toHaveBeenNthCalledWith(
|
128
|
-
1,
|
129
|
-
'https://api.github.com/repos/Laboratoria/bootcamp/releases/latest',
|
130
|
-
);
|
131
|
-
expect(global.fetch).toHaveBeenNthCalledWith(
|
132
|
-
2,
|
133
|
-
'https://raw.githubusercontent.com/Laboratoria/bootcamp/v4.0.0/dist/topics.json',
|
134
|
-
);
|
135
|
-
});
|
136
|
-
});
|
137
|
-
});
|
138
|
-
|
139
|
-
describe('findById', () => {
|
140
|
-
it('sends request to get latest version and then corresponding json', () => {
|
141
|
-
global.fetch = jest.fn().mockImplementation(async url => ({
|
142
|
-
status: 200,
|
143
|
-
json: jest.fn().mockResolvedValue(
|
144
|
-
url.startsWith('https://api.')
|
145
|
-
? { tag_name: 'v4.0.0' }
|
146
|
-
: {}
|
147
|
-
),
|
148
|
-
}));
|
149
|
-
|
150
|
-
return createAPI().topics.findById('javascript')
|
151
|
-
.then((topic) => {
|
152
|
-
expect(topic).toEqual({});
|
153
|
-
expect(global.fetch).toHaveBeenCalledTimes(2);
|
154
|
-
expect(global.fetch).toHaveBeenNthCalledWith(
|
155
|
-
1,
|
156
|
-
'https://api.github.com/repos/Laboratoria/bootcamp/releases/latest',
|
157
|
-
);
|
158
|
-
expect(global.fetch).toHaveBeenNthCalledWith(
|
159
|
-
2,
|
160
|
-
'https://raw.githubusercontent.com/Laboratoria/bootcamp/v4.0.0/dist/topics/javascript.json',
|
161
|
-
);
|
162
|
-
});
|
163
|
-
});
|
164
|
-
});
|
165
|
-
});
|
166
|
-
|
167
|
-
describe('learningObjectives', () => {
|
168
|
-
describe('findMany', () => {
|
169
|
-
it('sends request to get latest version and then corresponding json', () => {
|
170
|
-
global.fetch = jest.fn().mockImplementation(async url => ({
|
171
|
-
status: 200,
|
172
|
-
json: jest.fn().mockResolvedValue(
|
173
|
-
url.startsWith('https://api.')
|
174
|
-
? { tag_name: 'v4.0.0' }
|
175
|
-
: []
|
176
|
-
),
|
177
|
-
}));
|
178
|
-
|
179
|
-
return createAPI().learningObjectives.findMany()
|
180
|
-
.then((learningObjectives) => {
|
181
|
-
expect(learningObjectives).toEqual([]);
|
182
|
-
expect(global.fetch).toHaveBeenCalledTimes(2);
|
183
|
-
expect(global.fetch).toHaveBeenNthCalledWith(
|
184
|
-
1,
|
185
|
-
'https://api.github.com/repos/Laboratoria/bootcamp/releases/latest',
|
186
|
-
);
|
187
|
-
expect(global.fetch).toHaveBeenNthCalledWith(
|
188
|
-
2,
|
189
|
-
'https://raw.githubusercontent.com/Laboratoria/bootcamp/v4.0.0/dist/learning-objectives.json',
|
190
|
-
);
|
191
|
-
});
|
192
|
-
});
|
193
|
-
});
|
194
|
-
});
|
package/lib/model.spec.js
DELETED
@@ -1,242 +0,0 @@
|
|
1
|
-
import { createModel } from './model.js';
|
2
|
-
import { createClient } from './client.js';
|
3
|
-
|
4
|
-
jest.mock('./client.js');
|
5
|
-
|
6
|
-
beforeEach(() => createClient().mockClear());
|
7
|
-
|
8
|
-
describe('model.schema', () => {
|
9
|
-
it('should be empty object by default', () => {
|
10
|
-
const model = createModel('http://1.2.3.4', {}, 'foo');
|
11
|
-
expect(model.schema).toEqual({});
|
12
|
-
});
|
13
|
-
|
14
|
-
it('should get the model\'s schema', () => {
|
15
|
-
const schema = {
|
16
|
-
properties: {
|
17
|
-
a: { type: 'string' },
|
18
|
-
},
|
19
|
-
};
|
20
|
-
const model = createModel('http://1.2.3.4', {}, 'foo', schema);
|
21
|
-
expect(model.schema).toEqual(schema);
|
22
|
-
});
|
23
|
-
});
|
24
|
-
|
25
|
-
describe('model.findMany(options)', () => {
|
26
|
-
it('should send GET request', async () => {
|
27
|
-
const schema = {
|
28
|
-
properties: {
|
29
|
-
id: { type: 'integer' },
|
30
|
-
createdAt: { type: 'string', format: 'date-time' },
|
31
|
-
},
|
32
|
-
};
|
33
|
-
const now = new Date();
|
34
|
-
const client = createClient().mockResolvedValue([{
|
35
|
-
id: 1,
|
36
|
-
createdAt: now.toISOString(),
|
37
|
-
}]);
|
38
|
-
const model = createModel('http://1.2.3.4', {}, 'foo', schema);
|
39
|
-
const results = await model.findMany();
|
40
|
-
expect(results).toEqual([{ id: 1, createdAt: now }]);
|
41
|
-
expect(results[0].createdAt instanceof Date).toBe(true);
|
42
|
-
expect(client).toHaveBeenCalledTimes(1);
|
43
|
-
expect(client).toHaveBeenCalledWith('/foo');
|
44
|
-
});
|
45
|
-
});
|
46
|
-
|
47
|
-
describe('model.findById(id, options)', () => {
|
48
|
-
it('should send GET request', async () => {
|
49
|
-
const client = createClient().mockResolvedValue({});
|
50
|
-
const model = createModel('http://1.2.3.4', {}, 'foo');
|
51
|
-
expect(await model.findById(1)).toEqual({});
|
52
|
-
expect(client).toHaveBeenCalledTimes(1);
|
53
|
-
expect(client).toHaveBeenCalledWith('/foo/1');
|
54
|
-
});
|
55
|
-
});
|
56
|
-
|
57
|
-
describe('model.create(options)', () => {
|
58
|
-
it('should send POST request', async () => {
|
59
|
-
const client = createClient().mockResolvedValue({});
|
60
|
-
const model = createModel('http://1.2.3.4', {}, 'foo');
|
61
|
-
expect(await model.create({ data: { ok: true } })).toEqual({});
|
62
|
-
expect(client).toHaveBeenCalledTimes(1);
|
63
|
-
expect(client).toHaveBeenCalledWith('/foo', {
|
64
|
-
body: { data: { ok: true } },
|
65
|
-
method: 'POST',
|
66
|
-
});
|
67
|
-
});
|
68
|
-
});
|
69
|
-
|
70
|
-
describe('model.update(options)', () => {
|
71
|
-
it('should send POST request', async () => {
|
72
|
-
const client = createClient().mockResolvedValue({});
|
73
|
-
const model = createModel('http://1.2.3.4', {}, 'foo');
|
74
|
-
expect(await model.update({ where: { id: 1 }, data: { ok: true } })).toEqual({});
|
75
|
-
expect(client).toHaveBeenCalledTimes(1);
|
76
|
-
expect(client).toHaveBeenCalledWith('/foo/1', {
|
77
|
-
body: { where: { id: 1 }, data: { ok: true } },
|
78
|
-
method: 'PUT',
|
79
|
-
});
|
80
|
-
});
|
81
|
-
|
82
|
-
it('should exclude null values for optional relation props', async () => {
|
83
|
-
const client = createClient().mockResolvedValue({});
|
84
|
-
const schema = {
|
85
|
-
properties: {
|
86
|
-
signupCohort: {
|
87
|
-
anyOf: [
|
88
|
-
{ '$ref': '#/definitions/Cohort' },
|
89
|
-
{ type: 'null' },
|
90
|
-
],
|
91
|
-
},
|
92
|
-
},
|
93
|
-
};
|
94
|
-
const model = createModel('http://1.2.3.4', {}, 'foo', schema);
|
95
|
-
expect(await model.update({
|
96
|
-
where: { id: 1 },
|
97
|
-
data: { signupCohort: null },
|
98
|
-
})).toEqual({});
|
99
|
-
expect(client).toHaveBeenCalledTimes(1);
|
100
|
-
expect(client).toHaveBeenCalledWith('/foo/1', {
|
101
|
-
body: {
|
102
|
-
where: { id: 1 },
|
103
|
-
data: {},
|
104
|
-
},
|
105
|
-
method: 'PUT',
|
106
|
-
});
|
107
|
-
});
|
108
|
-
});
|
109
|
-
|
110
|
-
describe('model.upsert(options)', () => {
|
111
|
-
it('should send POST request when new row', async () => {
|
112
|
-
const client = createClient().mockResolvedValue({});
|
113
|
-
const model = createModel('http://1.2.3.4', {}, 'foo');
|
114
|
-
const result = await model.upsert({
|
115
|
-
where: {},
|
116
|
-
create: { ok: true },
|
117
|
-
});
|
118
|
-
expect(result).toEqual({});
|
119
|
-
expect(client).toHaveBeenCalledTimes(1);
|
120
|
-
expect(client).toHaveBeenCalledWith('/foo', {
|
121
|
-
body: { data: { ok: true } },
|
122
|
-
method: 'POST',
|
123
|
-
});
|
124
|
-
});
|
125
|
-
|
126
|
-
it('should send PUT request when existing row', async () => {
|
127
|
-
const client = createClient().mockResolvedValue({});
|
128
|
-
const model = createModel('http://1.2.3.4', {}, 'foo');
|
129
|
-
const result = await model.upsert({
|
130
|
-
where: { id: 1 },
|
131
|
-
update: { ok: true },
|
132
|
-
});
|
133
|
-
expect(result).toEqual({});
|
134
|
-
expect(client).toHaveBeenCalledTimes(1);
|
135
|
-
expect(client).toHaveBeenCalledWith('/foo/1', {
|
136
|
-
body: {
|
137
|
-
where: { id: 1 },
|
138
|
-
data: { ok: true },
|
139
|
-
},
|
140
|
-
method: 'PUT',
|
141
|
-
});
|
142
|
-
});
|
143
|
-
});
|
144
|
-
|
145
|
-
describe('model.delete(id)', () => {
|
146
|
-
it('should send POST request', async () => {
|
147
|
-
const client = createClient().mockResolvedValue({});
|
148
|
-
const model = createModel('http://1.2.3.4', {}, 'foo');
|
149
|
-
expect(await model.delete(1)).toEqual({});
|
150
|
-
expect(client).toHaveBeenCalledTimes(1);
|
151
|
-
expect(client).toHaveBeenCalledWith('/foo/1', {
|
152
|
-
method: 'DELETE',
|
153
|
-
});
|
154
|
-
});
|
155
|
-
});
|
156
|
-
|
157
|
-
describe('model.validateAttr(key, value)', () => {
|
158
|
-
it('should not validate when no schema', () => {
|
159
|
-
const model = createModel('http://1.2.3.4', {}, 'foo');
|
160
|
-
expect(model.validateAttr('a', 1)).toBeUndefined();
|
161
|
-
});
|
162
|
-
|
163
|
-
it('should validate text when required', () => {
|
164
|
-
const schema = {
|
165
|
-
properties: {
|
166
|
-
a: { type: 'string' },
|
167
|
-
},
|
168
|
-
};
|
169
|
-
const model = createModel('http://1.2.3.4', {}, 'foo', schema);
|
170
|
-
expect(model.validateAttr('a', '')).toBe('Field is required');
|
171
|
-
});
|
172
|
-
|
173
|
-
it('should validate number when required', () => {
|
174
|
-
const schema = {
|
175
|
-
properties: {
|
176
|
-
a: { type: 'integer', required: true },
|
177
|
-
},
|
178
|
-
};
|
179
|
-
const model = createModel('http://1.2.3.4', {}, 'foo', schema);
|
180
|
-
expect(model.validateAttr('a', 'foo')).toBe('Invalid number format');
|
181
|
-
});
|
182
|
-
|
183
|
-
it('should validate enum', () => {
|
184
|
-
const schema = {
|
185
|
-
properties: {
|
186
|
-
a: { type: 'integer', enum: [1, 2, 3] },
|
187
|
-
},
|
188
|
-
};
|
189
|
-
const model = createModel('http://1.2.3.4', {}, 'foo', schema);
|
190
|
-
expect(model.validateAttr('a', 'foo')).toBe('foo is not one of: 1,2,3');
|
191
|
-
});
|
192
|
-
|
193
|
-
it('should allow empty enum when not required', () => {
|
194
|
-
const schema = {
|
195
|
-
properties: {
|
196
|
-
a: { type: ['integer', 'null'], enum: [1, 2, 3] },
|
197
|
-
},
|
198
|
-
};
|
199
|
-
const model = createModel('http://1.2.3.4', {}, 'foo', schema);
|
200
|
-
expect(model.validateAttr('a', null)).toBeUndefined();
|
201
|
-
expect(model.validateAttr('a', undefined)).toBeUndefined();
|
202
|
-
expect(model.validateAttr('a', '')).toBeUndefined();
|
203
|
-
});
|
204
|
-
});
|
205
|
-
|
206
|
-
describe('model.validate(attributes)', () => {
|
207
|
-
it('should not validate when no schema', () => {
|
208
|
-
const model = createModel('http://1.2.3.4', {}, 'foo');
|
209
|
-
expect(model.validate({ a: 1 })).toEqual({});
|
210
|
-
});
|
211
|
-
|
212
|
-
it('should validate when schema present', () => {
|
213
|
-
const schema = {
|
214
|
-
properties: {
|
215
|
-
a: { type: 'string' },
|
216
|
-
b: { type: 'integer' },
|
217
|
-
},
|
218
|
-
};
|
219
|
-
const model = createModel('http://1.2.3.4', {}, 'foo', schema);
|
220
|
-
expect(model.validate({ a: '', b: 1 })).toEqual({ a: 'Field is required' });
|
221
|
-
});
|
222
|
-
|
223
|
-
it('should validate number when required', () => {
|
224
|
-
const schema = {
|
225
|
-
properties: {
|
226
|
-
a: { type: 'integer' },
|
227
|
-
},
|
228
|
-
};
|
229
|
-
const model = createModel('http://1.2.3.4', {}, 'foo', schema);
|
230
|
-
expect(model.validateAttr('a', 'foo')).toBe('Invalid number format');
|
231
|
-
});
|
232
|
-
|
233
|
-
it('should validate enum', () => {
|
234
|
-
const schema = {
|
235
|
-
properties: {
|
236
|
-
a: { type: 'integer', enum: [1, 2, 3] },
|
237
|
-
},
|
238
|
-
};
|
239
|
-
const model = createModel('http://1.2.3.4', {}, 'foo', schema);
|
240
|
-
expect(model.validateAttr('a', 'foo')).toBe('foo is not one of: 1,2,3');
|
241
|
-
});
|
242
|
-
});
|