@laboratoria/sdk-js 5.3.2 → 6.0.0-beta.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,101 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2017 Google LLC
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- */
17
-
18
- /**
19
- * @license
20
- * Copyright 2019 Google LLC
21
- *
22
- * Licensed under the Apache License, Version 2.0 (the "License");
23
- * you may not use this file except in compliance with the License.
24
- * You may obtain a copy of the License at
25
- *
26
- * http://www.apache.org/licenses/LICENSE-2.0
27
- *
28
- * Unless required by applicable law or agreed to in writing, software
29
- * distributed under the License is distributed on an "AS IS" BASIS,
30
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31
- * See the License for the specific language governing permissions and
32
- * limitations under the License.
33
- */
34
-
35
- /**
36
- * @license
37
- * Copyright 2020 Google LLC
38
- *
39
- * Licensed under the Apache License, Version 2.0 (the "License");
40
- * you may not use this file except in compliance with the License.
41
- * You may obtain a copy of the License at
42
- *
43
- * http://www.apache.org/licenses/LICENSE-2.0
44
- *
45
- * Unless required by applicable law or agreed to in writing, software
46
- * distributed under the License is distributed on an "AS IS" BASIS,
47
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
48
- * See the License for the specific language governing permissions and
49
- * limitations under the License.
50
- */
51
-
52
- /**
53
- * @license
54
- * Copyright 2020 Google LLC.
55
- *
56
- * Licensed under the Apache License, Version 2.0 (the "License");
57
- * you may not use this file except in compliance with the License.
58
- * You may obtain a copy of the License at
59
- *
60
- * http://www.apache.org/licenses/LICENSE-2.0
61
- *
62
- * Unless required by applicable law or agreed to in writing, software
63
- * distributed under the License is distributed on an "AS IS" BASIS,
64
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
65
- * See the License for the specific language governing permissions and
66
- * limitations under the License.
67
- */
68
-
69
- /**
70
- * @license
71
- * Copyright 2021 Google LLC
72
- *
73
- * Licensed under the Apache License, Version 2.0 (the "License");
74
- * you may not use this file except in compliance with the License.
75
- * You may obtain a copy of the License at
76
- *
77
- * http://www.apache.org/licenses/LICENSE-2.0
78
- *
79
- * Unless required by applicable law or agreed to in writing, software
80
- * distributed under the License is distributed on an "AS IS" BASIS,
81
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
82
- * See the License for the specific language governing permissions and
83
- * limitations under the License.
84
- */
85
-
86
- /**
87
- * @license
88
- * Copyright 2022 Google LLC
89
- *
90
- * Licensed under the Apache License, Version 2.0 (the "License");
91
- * you may not use this file except in compliance with the License.
92
- * You may obtain a copy of the License at
93
- *
94
- * http://www.apache.org/licenses/LICENSE-2.0
95
- *
96
- * Unless required by applicable law or agreed to in writing, software
97
- * distributed under the License is distributed on an "AS IS" BASIS,
98
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99
- * See the License for the specific language governing permissions and
100
- * limitations under the License.
101
- */
package/examples/esm.html DELETED
@@ -1,4 +0,0 @@
1
- <script type="module">
2
- import { createApp } from '../dist/laboratoria-sdk-esm.js';
3
- console.log(createApp());
4
- </script>
package/examples/umd.html DELETED
@@ -1,4 +0,0 @@
1
- <script src="../dist/laboratoria-sdk-umd.js"></script>
2
- <script>
3
- console.log(laboratoria.createApp());
4
- </script>
package/index.js DELETED
@@ -1,64 +0,0 @@
1
- import { initializeApp } from 'firebase/app';
2
- import {
3
- getAuth,
4
- onAuthStateChanged,
5
- signInWithEmailAndPassword,
6
- signOut,
7
- sendPasswordResetEmail,
8
- } from 'firebase/auth';
9
- import { createAPI as createCoreAPI } from './lib/core';
10
- import { createAPI as createCurriculumAPI } from './lib/curriculum';
11
-
12
- export * from './lib/util';
13
-
14
- export const createApp = ({
15
- firebaseApiKey = 'AIzaSyAXbaEbpq8NOfn0r8mIrcoHvoGRkJThwdc',
16
- firebaseProject = 'laboratoria-la',
17
- coreApiUrl = 'https://us-central1-outpost-272720.cloudfunctions.net/core-api',
18
- } = {}) => {
19
- const firebaseApp = initializeApp({
20
- apiKey: firebaseApiKey,
21
- authDomain: `${firebaseProject}.firebaseapp.com`,
22
- databaseURL: `https://${firebaseProject}.firebaseio.com`,
23
- projectId: firebaseProject,
24
- });
25
-
26
- const firebaseAuth = getAuth(firebaseApp);
27
- const state = { authUser: undefined, user: undefined };
28
- const coreAPI = createCoreAPI(coreApiUrl, state);
29
- const curriculumAPI = createCurriculumAPI();
30
-
31
- const authAPI = {
32
- onChange: fn => onAuthStateChanged(firebaseAuth, (authUser) => {
33
- if (!authUser) {
34
- Object.assign(state, { authUser: null, user: null });
35
- return fn(state);
36
- }
37
- Object.assign(state, { authUser });
38
- coreAPI.user.findById(authUser.uid)
39
- .then((user) => {
40
- Object.assign(state, { user });
41
- fn(state);
42
- })
43
- .catch((err) => {
44
- console.error(err);
45
- Object.assign(state, { authUser: null, user: null });
46
- fn(state);
47
- });
48
- }),
49
- signIn: (email, password) => signInWithEmailAndPassword(
50
- firebaseAuth,
51
- email,
52
- password,
53
- ),
54
- signOut: () => signOut(firebaseAuth),
55
- sendPasswordResetEmail: email => sendPasswordResetEmail(firebaseAuth, email),
56
- };
57
-
58
- return {
59
- ...curriculumAPI,
60
- ...coreAPI,
61
- auth: authAPI,
62
- firebaseApp,
63
- };
64
- };
package/lib/client.js DELETED
@@ -1,25 +0,0 @@
1
- export const createClient = (baseUrl, authUser) => async (url, opts = {}) => {
2
- const token = authUser ? await authUser.getIdToken() : null;
3
- const resp = await fetch(`${baseUrl}${url}`, {
4
- mode: 'cors',
5
- ...opts,
6
- headers: {
7
- ...(token && { authorization: `Bearer ${token}` }),
8
- ...(opts.body && { 'content-type': 'application/json' }),
9
- ...opts.headers,
10
- },
11
- ...(opts.body && { body: JSON.stringify(opts.body) }),
12
- });
13
-
14
- const body = await resp.json();
15
-
16
- if (resp.status > 202) {
17
- throw Object.assign(new Error(body?.message || `HTTP Error ${resp.status}`), {
18
- status: resp.status,
19
- code: body?.code,
20
- body,
21
- });
22
- }
23
-
24
- return body;
25
- };
package/lib/core.js DELETED
@@ -1,373 +0,0 @@
1
- import md5 from 'blueimp-md5';
2
- import { createModels, extendSchemaDefinitions } from './model';
3
- import { createClient } from './client.js';
4
- import schema from '../schemas/core.json';
5
-
6
- const ONE_HOUR = 60 * 60 * 1000;
7
- const ONE_DAY = 24 * ONE_HOUR;
8
-
9
- const extended = {
10
- Country: {
11
- primaryKey: 'code',
12
- plural: 'countries',
13
- searchProps: ['code', 'name', "timeZone"],
14
- getOptionLabel: ({ code, name }) => `${code} / ${name}`,
15
- },
16
- ActivityLogEntry: {
17
- plural: 'activityLog',
18
- },
19
- User: {
20
- primaryKey: 'uid',
21
- inputProps: [
22
- 'firstName',
23
- 'lastName',
24
- 'email',
25
- 'password',
26
- 'lang',
27
- 'identifiesAsFemale',
28
- 'dateOfBirth',
29
- 'country',
30
- 'identificationNumber',
31
- 'mobileNumber',
32
- 'github',
33
- 'linkedin',
34
- 'bio',
35
- 'role',
36
- 'signupCohort',
37
- 'disabled',
38
- 'referralSource',
39
- ],
40
- searchProps: ['firstName', 'lastName', 'email'],
41
- getOptionLabel: ({ firstName, lastName, email }) => `${firstName} ${lastName} (${email})`,
42
- properties: {
43
- email: {
44
- inputType: 'email',
45
- },
46
- mobileNumber: {
47
- inputType: 'phone',
48
- },
49
- bio: {
50
- inputType: 'textarea',
51
- },
52
- },
53
- parse: (props) => {
54
- const avatar = (
55
- props.github
56
- ? `https://github.com/${props.github}.png?size=`
57
- : `https://www.gravatar.com/avatar/${md5(props.email)}?s=`
58
- );
59
- return {
60
- ...props,
61
- fullName: `${props.firstName || ''}${props.lastName ? ` ${props.lastName}` : ''}`,
62
- avatar: size => `${avatar}${size}`,
63
- isStaff: ['staff', 'manager', 'finance', 'admin'].includes(props.role),
64
- isManager: ['manager', 'finance', 'admin'].includes(props.role),
65
- isFinance: ['finance', 'admin'].includes(props.role),
66
- isAdmin: props.role === 'admin',
67
- };
68
- },
69
- },
70
- Track: {
71
- searchProps: ['name'],
72
- },
73
- Program: {
74
- searchProps: ['name'],
75
- },
76
- Stage: {
77
- searchProps: ['name'],
78
- },
79
- Cohort: {
80
- inputProps: [
81
- 'program',
82
- 'stage',
83
- 'lang',
84
- 'name',
85
- 'start',
86
- 'end',
87
- 'timeZone',
88
- 'startTime',
89
- 'endTime',
90
- 'vacancies',
91
- 'admissionStart',
92
- 'admissionEnd',
93
- 'placementStart',
94
- 'admissionCountries',
95
- ],
96
- searchProps: ['name'],
97
- getOptionLabel: ({ id, name, stage }) => (
98
- `${name}${!stage ? '' : ` - ${stage.name}`} (id: ${id})`
99
- ),
100
- properties: {},
101
- // Status:
102
- // - `draft`: El cohort todavía no tiene fecha de inicio o fin de admisión.
103
- // - `planned`: El cohort ya tiene fechas de inicio y fin de admisión, pero el
104
- // período de admisiones todavía no ha comenzado.
105
- // - `admission_started`: El periodo de admisión está abierto.
106
- // - `admission_ended`: El período de admisión ya terminó, pero todavía no
107
- // comienza el bootcamp.
108
- // - `started`: El cohort está en período de bootcamp.
109
- // - `ended`: El cohort ya terminó su bootcamp.
110
- // - `placement_started`: El cohort se encuentra en período de placement (aka
111
- // _placement_season_).
112
- // - `placement_ended`: El período de placement ya terminó.
113
- parse: (props) => {
114
- const now = Date.now();
115
-
116
- let status;
117
-
118
- if (props.placementStart) {
119
- const placementDuration = props.placementDuration || 270;
120
- const placementEnd = new Date(
121
- +props.placementStart
122
- + placementDuration * 24 * 60 * 60 * 1000
123
- )
124
-
125
- if (placementEnd < now) {
126
- status = 'placement_ended';
127
- } else if (props.placementStart < now) {
128
- status = 'placement_started';
129
- }
130
- }
131
-
132
- if (!status && props.end < now) {
133
- status = 'ended';
134
- }
135
-
136
- if (!status && props.start < now) {
137
- status = 'started';
138
- }
139
-
140
- // NOTA: La fecha de cierre de admisiones se guarda por defecto con la
141
- // hora 00:00:00, pero semánticamente en realidad queremos expersar que
142
- // las postulaciones están abiertas inclusive ese día, hasta las 23:59:59,
143
- // con lo cual sumamos un día a la fecha de cierre para no excluir el
144
- // último día.
145
- const admissionEnd = (
146
- props.admissionEnd
147
- ? +props.admissionEnd + ONE_DAY
148
- : false
149
- );
150
- if (!status && admissionEnd && admissionEnd < now) {
151
- status = 'admission_ended';
152
- }
153
-
154
- if (!status && props.admissionStart && props.admissionStart < now) {
155
- status = 'admission_started';
156
- }
157
-
158
- if (!status && props.admissionStart && props.admissionEnd) {
159
- status = 'planned';
160
- }
161
-
162
- if (!status) {
163
- status = 'draft'
164
- }
165
-
166
- const size = (
167
- props.start > now && props.vacancies
168
- ? props.vacancies
169
- : props._count?.students || props.students?.length || 0
170
- );
171
-
172
- return {
173
- ...props,
174
- status,
175
- size,
176
- };
177
- },
178
- },
179
- Student: {
180
- inputProps: [
181
- 'cohort',
182
- 'user',
183
- ],
184
- searchProps: [],
185
- },
186
- Dropout: {
187
- inputProps: [
188
- 'cohort',
189
- 'user',
190
- 'date',
191
- 'reason',
192
- 'reasonDetail',
193
- 'lastProject',
194
- 'completedProjects',
195
- 'notes',
196
- 'staffSad',
197
- 'covidRelated',
198
- ],
199
- searchProps: [
200
- 'user.uid',
201
- 'user.firstName',
202
- 'user.lastName',
203
- 'user.email',
204
- 'cohort.name',
205
- ],
206
- },
207
- Contract: {
208
- inputProps: [
209
- 'user',
210
- 'country',
211
- 'isEmployment',
212
- 'feeAmount',
213
- 'hoursPerWeek',
214
- 'start',
215
- 'end',
216
- 'isTrial',
217
- 'checkPilot',
218
- 'source',
219
- ],
220
- searchProps: [
221
- 'user.firstName',
222
- 'user.lastName',
223
- 'user.email',
224
- ],
225
- getOptionLabel: ({ id, user }) => `${user?.firstName} (id: ${id})`,
226
- },
227
- Gig: {
228
- inputProps: [
229
- 'cohort',
230
- 'user',
231
- 'contract',
232
- 'role',
233
- 'hoursPerWeek',
234
- 'start',
235
- 'end',
236
- ],
237
- },
238
- Application: {
239
- inputProps: [
240
- 'user',
241
- 'cohort',
242
- 'dateOfBirth',
243
- 'country',
244
- 'locality',
245
- 'nationality',
246
- 'isAllowedToWork',
247
- 'hasValidIdDocument',
248
- 'householdSize',
249
- 'children',
250
- 'householdIncome',
251
- 'racialIdentity',
252
- 'educationLevel',
253
- 'schoolType',
254
- 'educationSubject',
255
- 'university',
256
- 'isStudying',
257
- 'englishLevel',
258
- 'workStatus',
259
- 'isLookingForWork',
260
- 'monthsLookingForWork',
261
- 'monthsUnemployed',
262
- 'workRole',
263
- 'freelance',
264
- 'workFormal',
265
- 'workIncome',
266
- 'isReady',
267
- 'bypassFilters',
268
- ],
269
- properties: {
270
- locality: {
271
- isRequired: true,
272
- },
273
- englishLevel: {
274
- isRequired: true,
275
- },
276
- isReady: {
277
- isRequired: false,
278
- },
279
- bypassFilters: {
280
- isRequired: false,
281
- },
282
- },
283
- // Status:
284
- // - `active`: Postulación en progreso.
285
- // - `rejected`: Postulación rechazada por filtros (ingresos y/o educación).
286
- // - `ready`: Retos completados y postulación _enviada_ (confirmada). Llegado a
287
- // este estado se considera que la postulación está lista para el siguiente
288
- // paso, por ejemplo entrevista.
289
- // - `admission_ended`: Período de admisión del cohort seleccionado ya ha
290
- // terminado, pero la postulación todavía no ha expirado, así que la postulante
291
- // todavía puede continuar cambiando de cohort, siempre y cuando haya uno
292
- // abierto dentro del período de validez de 90 días desde la creación de la
293
- // postulación.
294
- // - `expired`: Se pasaron los 90 días. La postulación ya no puede ser
295
- // completada.
296
- // - `cancelled`: La postulante canceló su propia postulación. (Todavía no
297
- // implementado).
298
- parse: (props) => {
299
- const hasFailedFilters = (
300
- typeof props.hasFailedFilters === 'boolean'
301
- ? props.hasFailedFilters
302
- : typeof props.failedFilters?.length === 'number'
303
- ? props.failedFilters.length > 0
304
- : undefined
305
- );
306
-
307
- if (!props.cohort?.admissionEnd || typeof hasFailedFilters === 'undefined') {
308
- return props;
309
- }
310
-
311
- const now = Date.now();
312
- let status;
313
-
314
- if (props.isReady) {
315
- status = 'ready';
316
- } else if (hasFailedFilters && !props.bypassFilters) {
317
- status = 'rejected';
318
- } else if (props.createdAt < (now - 90 * ONE_DAY)) {
319
- status = 'expired';
320
- } else if (props.cohort?.status !== 'admission_started') {
321
- status = 'admission_ended';
322
- } else {
323
- status = 'active';
324
- }
325
-
326
- return {
327
- ...props,
328
- status,
329
- };
330
- },
331
- },
332
- IscedLevel: {
333
- parse: (props) => {
334
- const level = Math.floor(props.id / 10);
335
- return {
336
- ...props,
337
- level,
338
- completed: (level * 10) < props.id,
339
- };
340
- },
341
- },
342
- EducationLevel: {
343
- searchProps: [
344
- 'label',
345
- ],
346
- },
347
- University: {
348
- plural: 'universities',
349
- searchProps: ['name'],
350
- },
351
- Job: {
352
- // inputProps: [],
353
- // properties: {},
354
- },
355
- Company: {
356
- plural: 'companies',
357
- // inputProps: [],
358
- // properties: {},
359
- },
360
- };
361
-
362
- export const createAPI = (url, state) => ({
363
- ...createModels(url, state, {
364
- ...schema,
365
- definitions: extendSchemaDefinitions(schema, extended),
366
- }),
367
- gcpLog: {
368
- getEntries: async (q) => {
369
- const req = createClient(url, state.authUser);
370
- return await req(`/gcp-logs?q=${JSON.stringify(q)}`);
371
- },
372
- },
373
- });
package/lib/curriculum.js DELETED
@@ -1,59 +0,0 @@
1
- const releasesUrl = 'https://api.github.com/repos/Laboratoria/bootcamp/releases';
2
- const rawUrl = 'https://raw.githubusercontent.com/Laboratoria/bootcamp';
3
-
4
- const getLatestVersion = () => fetch(`${releasesUrl}/latest`)
5
- .then(resp => resp.json())
6
- .then(({ tag_name }) => tag_name);
7
-
8
- const fetchProjects = async tag => (
9
- fetch(`${rawUrl}/${tag || await getLatestVersion()}/dist/projects.json`)
10
- .then(resp => resp.json())
11
- );
12
-
13
- const fetchProject = async (slug, tag) => (
14
- fetch(`${rawUrl}/${tag || await getLatestVersion()}/dist/projects/${slug}.json`)
15
- .then(resp => resp.json())
16
- );
17
-
18
- const fetchTopics = async tag => (
19
- fetch(`${rawUrl}/${tag || await getLatestVersion()}/dist/topics.json`)
20
- .then(resp => resp.json())
21
- );
22
-
23
- const fetchTopic = async (slug, tag) => (
24
- fetch(`${rawUrl}/${tag || await getLatestVersion()}/dist/topics/${slug}.json`)
25
- .then(resp => resp.json())
26
- );
27
-
28
- const fetchLearningObjectives = async tag => (
29
- fetch(`${rawUrl}/${tag || await getLatestVersion()}/dist/learning-objectives.json`)
30
- .then(resp => resp.json())
31
- );
32
-
33
- const filterByLocaleAndTrack = opts => arr => arr.filter((obj) => {
34
- if (opts.locale && opts.locale !== obj.locale) {
35
- return false;
36
- }
37
- if (opts.track && opts.track !== obj.track) {
38
- return false;
39
- }
40
- return true;
41
- });
42
-
43
- export const createAPI = () => {
44
- return {
45
- project: {
46
- findMany: (opts = {}) => fetchProjects(opts.tag)
47
- .then(filterByLocaleAndTrack(opts)),
48
- findById: (slug, opts = {}) => fetchProject(slug, opts.tag),
49
- },
50
- topic: {
51
- findMany: (opts = {}) => fetchTopics(opts.tag)
52
- .then(filterByLocaleAndTrack(opts)),
53
- findById: (slug, opts = {}) => fetchTopic(slug, opts.tag),
54
- },
55
- learningObjective: {
56
- findMany: (opts = {}) => fetchLearningObjectives(opts.tag),
57
- },
58
- };
59
- };