@coorpacademy/app-review 0.0.13 → 0.0.14-alpha.3
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/es/actions/courses.d.ts +7 -0
- package/es/actions/courses.d.ts.map +1 -0
- package/es/actions/courses.js +5 -0
- package/es/actions/index.d.ts +6 -0
- package/es/actions/index.d.ts.map +1 -1
- package/es/actions/index.js +2 -0
- package/es/actions/navigation.d.ts +7 -0
- package/es/actions/navigation.d.ts.map +1 -1
- package/es/actions/navigation.js +5 -0
- package/es/actions/token.d.ts +7 -0
- package/es/actions/token.d.ts.map +1 -0
- package/es/actions/token.js +5 -0
- package/es/configure-store.d.ts +11 -3
- package/es/configure-store.d.ts.map +1 -1
- package/es/configure-store.js +4 -2
- package/es/index.d.ts +9 -3
- package/es/index.d.ts.map +1 -1
- package/es/index.js +4 -4
- package/es/middlewares/on-start-app.d.ts +7 -0
- package/es/middlewares/on-start-app.d.ts.map +1 -0
- package/es/middlewares/on-start-app.js +17 -0
- package/es/reducers/courses.d.ts +6 -0
- package/es/reducers/courses.d.ts.map +1 -0
- package/es/reducers/courses.js +14 -0
- package/es/reducers/index.d.ts +2 -0
- package/es/reducers/index.d.ts.map +1 -1
- package/es/reducers/index.js +5 -1
- package/es/reducers/navigation.d.ts.map +1 -1
- package/es/reducers/navigation.js +3 -0
- package/es/reducers/token.d.ts +7 -0
- package/es/reducers/token.d.ts.map +1 -0
- package/es/reducers/token.js +16 -0
- package/es/services/fetch-demo.d.ts +6 -0
- package/es/services/fetch-demo.d.ts.map +1 -0
- package/es/services/fetch-demo.js +25 -0
- package/es/services/fetch-demo.test.d.ts +2 -0
- package/es/services/fetch-demo.test.d.ts.map +1 -0
- package/es/services/fetch-demo.test.js +13 -0
- package/es/services/fetch-responses.d.ts +20 -0
- package/es/services/fetch-responses.d.ts.map +1 -0
- package/es/services/fetch-responses.js +33 -0
- package/es/services/fetch.test.d.ts +10 -0
- package/es/services/fetch.test.d.ts.map +1 -0
- package/es/services/fetch.test.js +12 -0
- package/lib/actions/courses.d.ts +7 -0
- package/lib/actions/courses.d.ts.map +1 -0
- package/lib/actions/courses.js +9 -0
- package/lib/actions/index.d.ts +6 -0
- package/lib/actions/index.d.ts.map +1 -1
- package/lib/actions/index.js +2 -0
- package/lib/actions/navigation.d.ts +7 -0
- package/lib/actions/navigation.d.ts.map +1 -1
- package/lib/actions/navigation.js +7 -1
- package/lib/actions/token.d.ts +7 -0
- package/lib/actions/token.d.ts.map +1 -0
- package/lib/actions/token.js +9 -0
- package/lib/configure-store.d.ts +11 -3
- package/lib/configure-store.d.ts.map +1 -1
- package/lib/configure-store.js +4 -2
- package/lib/index.d.ts +9 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -3
- package/lib/middlewares/on-start-app.d.ts +7 -0
- package/lib/middlewares/on-start-app.d.ts.map +1 -0
- package/lib/middlewares/on-start-app.js +22 -0
- package/lib/reducers/courses.d.ts +6 -0
- package/lib/reducers/courses.d.ts.map +1 -0
- package/lib/reducers/courses.js +27 -0
- package/lib/reducers/index.d.ts +2 -0
- package/lib/reducers/index.d.ts.map +1 -1
- package/lib/reducers/index.js +5 -1
- package/lib/reducers/navigation.d.ts.map +1 -1
- package/lib/reducers/navigation.js +3 -0
- package/lib/reducers/token.d.ts +7 -0
- package/lib/reducers/token.d.ts.map +1 -0
- package/lib/reducers/token.js +21 -0
- package/lib/services/fetch-demo.d.ts +6 -0
- package/lib/services/fetch-demo.d.ts.map +1 -0
- package/lib/services/fetch-demo.js +75 -0
- package/lib/services/fetch-demo.test.d.ts +2 -0
- package/lib/services/fetch-demo.test.d.ts.map +1 -0
- package/lib/services/fetch-demo.test.js +60 -0
- package/lib/services/fetch-responses.d.ts +20 -0
- package/lib/services/fetch-responses.d.ts.map +1 -0
- package/lib/services/fetch-responses.js +118 -0
- package/lib/services/fetch.test.d.ts +10 -0
- package/lib/services/fetch.test.d.ts.map +1 -0
- package/lib/services/fetch.test.js +31 -0
- package/package.json +8 -3
- package/readme.md +23 -0
- package/src/actions/courses.ts +11 -0
- package/src/actions/index.ts +8 -0
- package/src/actions/navigation.ts +12 -0
- package/src/actions/token.ts +11 -0
- package/src/configure-store.ts +15 -2
- package/src/index.tsx +12 -3
- package/src/middlewares/on-start-app.ts +30 -0
- package/src/reducers/courses.ts +22 -0
- package/src/reducers/index.ts +6 -1
- package/src/reducers/navigation.ts +9 -1
- package/src/reducers/token.ts +26 -0
- package/src/services/fetch-demo.test.ts +15 -0
- package/src/services/fetch-demo.ts +49 -0
- package/src/services/fetch-responses.ts +53 -0
- package/src/services/fetch.test.ts +26 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coorpacademy/app-review",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14-alpha.3+31066cb07",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"clean:commonjs": "rm -rf lib",
|
|
18
18
|
"clean:es": "rm -rf es",
|
|
19
19
|
"clean": "concurrently \"npm run clean:commonjs\" \"npm run clean:es\"",
|
|
20
|
-
"test:unit": "
|
|
20
|
+
"test:unit": "ava --config ava.config.cjs"
|
|
21
21
|
},
|
|
22
22
|
"author": "CoorpAcademy",
|
|
23
23
|
"files": [
|
|
@@ -37,16 +37,21 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@coorpacademy/components": "10.19.5",
|
|
40
|
+
"cross-fetch": "^3.1.5",
|
|
41
|
+
"jwt-decode": "^3.1.2",
|
|
40
42
|
"react": "^17.0.2",
|
|
41
43
|
"react-native": "0.68.1",
|
|
42
44
|
"react-redux": "^8.0.1",
|
|
43
45
|
"redux": "^4.2.0"
|
|
44
46
|
},
|
|
45
47
|
"devDependencies": {
|
|
48
|
+
"@ava/typescript": "^3.0.1",
|
|
46
49
|
"@storybook/react-native": "^5.3.25",
|
|
50
|
+
"ava": "^4.2.0",
|
|
47
51
|
"chalk": "^5.0.1",
|
|
48
52
|
"concurrently": "^5.2.0",
|
|
49
53
|
"connect-history-api-fallback": "^1.6.0",
|
|
54
|
+
"dotenv": "^16.0.1",
|
|
50
55
|
"esbuild": "^0.14.39",
|
|
51
56
|
"esbuild-css-modules-plugin": "^2.2.16",
|
|
52
57
|
"esbuild-plugin-svgr": "^1.0.1",
|
|
@@ -55,5 +60,5 @@
|
|
|
55
60
|
"redux-devtools-extension": "^2.13.9",
|
|
56
61
|
"typescript": "^4.6.4"
|
|
57
62
|
},
|
|
58
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "31066cb07260432d142efe025060a4a7cdae2b74"
|
|
59
64
|
}
|
package/readme.md
CHANGED
|
@@ -14,6 +14,22 @@ npm run static
|
|
|
14
14
|
|
|
15
15
|
(This script is generated by lerna to publish the static web app)
|
|
16
16
|
|
|
17
|
+
## unit tests
|
|
18
|
+
|
|
19
|
+
(requirements: duplicate `.env.sample` to `.env` and ask for the tokens)
|
|
20
|
+
|
|
21
|
+
Open a terminal and run a compilation in watch mode:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm run build:es -- --watch
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Now open a 2nd terminal and run ava in watch mode:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
npx ava --watch
|
|
31
|
+
```
|
|
32
|
+
|
|
17
33
|
## dependencies notes
|
|
18
34
|
|
|
19
35
|
`react-native` must be the same version on this package and the rootApp
|
|
@@ -25,3 +41,10 @@ npm run static
|
|
|
25
41
|
### tmp notes
|
|
26
42
|
|
|
27
43
|
export: <https://stackoverflow.com/a/70020984/959219>
|
|
44
|
+
|
|
45
|
+
css modules:
|
|
46
|
+
|
|
47
|
+
- <https://github.com/evanw/esbuild/issues/20>
|
|
48
|
+
- <https://github.com/indooorsman/esbuild-css-modules-plugin/issues/35>
|
|
49
|
+
- <https://github.com/princed/postcss-modules-values-replace>
|
|
50
|
+
- <https://github.com/CoorpAcademy/coorpacademy-lambda/blob/master/teams/postcss.config.js#L4>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const RECEIVED_COURSE = '@@api/RECEIVED_COURSE';
|
|
2
|
+
|
|
3
|
+
export type $RECEIVED_COURSE = {
|
|
4
|
+
type: '@@api/RECEIVED_COURSE';
|
|
5
|
+
payload: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const receivedCourse = (course: any): $RECEIVED_COURSE => ({
|
|
9
|
+
type: RECEIVED_COURSE,
|
|
10
|
+
payload: course
|
|
11
|
+
});
|
package/src/actions/index.ts
CHANGED
|
@@ -1 +1,9 @@
|
|
|
1
|
+
import {$RECEIVED_COURSE} from './courses';
|
|
2
|
+
import {$NAVIGATE_BACK, $NAVIGATE_TO, $START_APP} from './navigation';
|
|
3
|
+
import {$STORE_TOKEN} from './token';
|
|
4
|
+
|
|
5
|
+
export * from './courses';
|
|
1
6
|
export * from './navigation';
|
|
7
|
+
export * from './token';
|
|
8
|
+
|
|
9
|
+
export type Action = $START_APP | $STORE_TOKEN | $NAVIGATE_BACK | $NAVIGATE_TO | $RECEIVED_COURSE;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {ActionCreator} from 'redux';
|
|
2
|
+
import {AppOptions} from '..';
|
|
2
3
|
|
|
3
4
|
export const NAVIGATE_TO = '@@navigation/NAVIGATE_TO';
|
|
4
5
|
export const NAVIGATE_BACK = '@@navigation/NAVIGATE_BACK';
|
|
6
|
+
export const START_APP = '@@navigation/START_APP';
|
|
5
7
|
|
|
6
8
|
export type $NAVIGATE_TO = {
|
|
7
9
|
type: '@@navigation/NAVIGATE_TO';
|
|
@@ -12,6 +14,11 @@ export type $NAVIGATE_BACK = {
|
|
|
12
14
|
type: '@@navigation/NAVIGATE_BACK';
|
|
13
15
|
};
|
|
14
16
|
|
|
17
|
+
export type $START_APP = {
|
|
18
|
+
type: '@@navigation/START_APP';
|
|
19
|
+
payload: AppOptions;
|
|
20
|
+
};
|
|
21
|
+
|
|
15
22
|
export const navigateTo: ActionCreator<$NAVIGATE_TO> = (newPath: string) => ({
|
|
16
23
|
type: NAVIGATE_TO,
|
|
17
24
|
payload: newPath
|
|
@@ -20,3 +27,8 @@ export const navigateTo: ActionCreator<$NAVIGATE_TO> = (newPath: string) => ({
|
|
|
20
27
|
export const navigateBack: ActionCreator<$NAVIGATE_BACK> = () => ({
|
|
21
28
|
type: NAVIGATE_BACK
|
|
22
29
|
});
|
|
30
|
+
|
|
31
|
+
export const startApp = (options: AppOptions): $START_APP => ({
|
|
32
|
+
type: START_APP,
|
|
33
|
+
payload: options
|
|
34
|
+
});
|
package/src/configure-store.ts
CHANGED
|
@@ -4,9 +4,15 @@ import {composeWithDevTools} from 'redux-devtools-extension';
|
|
|
4
4
|
|
|
5
5
|
import rootReducer from './reducers';
|
|
6
6
|
|
|
7
|
+
import {State as NavigationState} from './reducers/navigation';
|
|
8
|
+
import {State as TokenState} from './reducers/token';
|
|
9
|
+
import onStartApp from './middlewares/on-start-app';
|
|
10
|
+
|
|
7
11
|
const preloadedState = undefined;
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
// -----------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
export default function configureStore() {
|
|
10
16
|
const _compose =
|
|
11
17
|
process.env.NODE_ENV !== 'production'
|
|
12
18
|
? composeWithDevTools({
|
|
@@ -16,8 +22,15 @@ export default function configureStore(services) {
|
|
|
16
22
|
})
|
|
17
23
|
: compose;
|
|
18
24
|
|
|
19
|
-
const enhancer = _compose(applyMiddleware(thunk));
|
|
25
|
+
const enhancer = _compose(applyMiddleware(thunk, onStartApp));
|
|
20
26
|
const store = createStore(rootReducer, preloadedState, enhancer);
|
|
21
27
|
|
|
22
28
|
return store;
|
|
23
29
|
}
|
|
30
|
+
|
|
31
|
+
// -----------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
export type StoreState = {
|
|
34
|
+
navigation: NavigationState;
|
|
35
|
+
token: TokenState;
|
|
36
|
+
};
|
package/src/index.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import {connect, Provider} from 'react-redux';
|
|
|
3
3
|
import AppReviewRootView from '@coorpacademy/components/es/template/app-review/root-view/root-view';
|
|
4
4
|
|
|
5
5
|
import configureStore from './configure-store';
|
|
6
|
-
import {navigateTo, navigateBack} from './actions/navigation';
|
|
6
|
+
import {navigateTo, navigateBack, startApp} from './actions/navigation';
|
|
7
7
|
import {getCurrentViewName} from './reducers/navigation';
|
|
8
8
|
|
|
9
9
|
// -----------------------------------------------------------------------------
|
|
@@ -17,11 +17,12 @@ const App = connect(mapStateToProps, mapDispatchToProps)(AppReviewRootView);
|
|
|
17
17
|
|
|
18
18
|
// -----------------------------------------------------------------------------
|
|
19
19
|
|
|
20
|
-
const AppRevision = ({
|
|
20
|
+
const AppRevision = ({options}: Props) => {
|
|
21
21
|
const [store, setStore] = useState(null);
|
|
22
22
|
|
|
23
23
|
useEffect(() => {
|
|
24
|
-
const store = configureStore(
|
|
24
|
+
const store = configureStore();
|
|
25
|
+
store.dispatch(startApp(options));
|
|
25
26
|
setStore(store);
|
|
26
27
|
}, []);
|
|
27
28
|
|
|
@@ -36,4 +37,12 @@ const AppRevision = ({services}) => {
|
|
|
36
37
|
|
|
37
38
|
// -----------------------------------------------------------------------------
|
|
38
39
|
|
|
40
|
+
type Props = {options: AppOptions};
|
|
41
|
+
|
|
42
|
+
export type AppOptions = {
|
|
43
|
+
token: string;
|
|
44
|
+
theme?: any;
|
|
45
|
+
translations?: any;
|
|
46
|
+
};
|
|
47
|
+
|
|
39
48
|
export default AppRevision;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import fetch from 'cross-fetch';
|
|
2
|
+
import type {Middleware} from 'redux';
|
|
3
|
+
import {Dispatch} from 'redux';
|
|
4
|
+
|
|
5
|
+
import {Action, receivedCourse, START_APP, storeToken} from '../actions';
|
|
6
|
+
import {StoreState} from '../configure-store';
|
|
7
|
+
import fetchCourse from '../services/fetch-demo';
|
|
8
|
+
|
|
9
|
+
const onStartApp: Middleware<{}, StoreState, Dispatch<Action>> = ({dispatch}) => next => (
|
|
10
|
+
action: Action
|
|
11
|
+
) => {
|
|
12
|
+
if (action.type === START_APP) {
|
|
13
|
+
const {token} = action.payload;
|
|
14
|
+
dispatch(storeToken(token));
|
|
15
|
+
|
|
16
|
+
// --------------
|
|
17
|
+
// demo service
|
|
18
|
+
|
|
19
|
+
const language = 'fr';
|
|
20
|
+
const universalRef = 'dis_xxx15';
|
|
21
|
+
|
|
22
|
+
fetchCourse(token, language, universalRef).then(course => {
|
|
23
|
+
dispatch(receivedCourse(course));
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return next(action);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default onStartApp;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {$RECEIVED_COURSE, RECEIVED_COURSE} from '../actions';
|
|
2
|
+
|
|
3
|
+
// -----------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
export type State = Array<any>;
|
|
6
|
+
export const initialState: State = [];
|
|
7
|
+
|
|
8
|
+
// -----------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
const reducer = (state: State = initialState, action: $RECEIVED_COURSE): State => {
|
|
11
|
+
switch (action.type) {
|
|
12
|
+
case RECEIVED_COURSE: {
|
|
13
|
+
return [...state, action.payload];
|
|
14
|
+
}
|
|
15
|
+
default:
|
|
16
|
+
return state;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// -----------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
export default reducer;
|
package/src/reducers/index.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import {combineReducers} from 'redux';
|
|
2
|
+
|
|
3
|
+
import courses from './courses';
|
|
2
4
|
import navigation from './navigation';
|
|
5
|
+
import token from './token';
|
|
3
6
|
|
|
4
7
|
const rootReducer = combineReducers({
|
|
5
|
-
|
|
8
|
+
courses,
|
|
9
|
+
navigation,
|
|
10
|
+
token
|
|
6
11
|
});
|
|
7
12
|
|
|
8
13
|
export default rootReducer;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {NAVIGATE_BACK, NAVIGATE_TO} from '../actions/navigation';
|
|
2
2
|
import type {$NAVIGATE_TO, $NAVIGATE_BACK} from '../actions/navigation';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// -----------------------------------------------------------------------------
|
|
5
5
|
|
|
6
|
+
export type State = Array<string>;
|
|
6
7
|
export const initialState: State = ['home'];
|
|
7
8
|
|
|
9
|
+
// -----------------------------------------------------------------------------
|
|
10
|
+
|
|
8
11
|
const reducer = (state: State = initialState, action: $NAVIGATE_TO | $NAVIGATE_BACK): State => {
|
|
9
12
|
switch (action.type) {
|
|
10
13
|
case NAVIGATE_TO: {
|
|
@@ -20,5 +23,10 @@ const reducer = (state: State = initialState, action: $NAVIGATE_TO | $NAVIGATE_B
|
|
|
20
23
|
}
|
|
21
24
|
};
|
|
22
25
|
|
|
26
|
+
// -----------------------------------------------------------------------------
|
|
27
|
+
|
|
23
28
|
export const getCurrentViewName = appState => appState.navigation[appState.navigation.length - 1];
|
|
29
|
+
|
|
30
|
+
// -----------------------------------------------------------------------------
|
|
31
|
+
|
|
24
32
|
export default reducer;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {$STORE_TOKEN, STORE_TOKEN} from '../actions';
|
|
2
|
+
|
|
3
|
+
// -----------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
export type State = string;
|
|
6
|
+
export const initialState: State = null;
|
|
7
|
+
|
|
8
|
+
// -----------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
const reducer = (state: State = initialState, action: $STORE_TOKEN): State => {
|
|
11
|
+
switch (action.type) {
|
|
12
|
+
case STORE_TOKEN: {
|
|
13
|
+
return action.payload;
|
|
14
|
+
}
|
|
15
|
+
default:
|
|
16
|
+
return state;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// -----------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
export const getToken = appState => appState.token;
|
|
23
|
+
|
|
24
|
+
// -----------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
export default reducer;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import {fetchCourse as _fetchCourse} from './fetch-demo.js';
|
|
3
|
+
import {okJSONResponse} from './fetch.test';
|
|
4
|
+
import dotenv from 'dotenv';
|
|
5
|
+
dotenv.config();
|
|
6
|
+
|
|
7
|
+
test('should fetch a demo course with mobile-staging dev token', async t => {
|
|
8
|
+
const token = 'foo';
|
|
9
|
+
const language = 'fr';
|
|
10
|
+
const universalRef = 'bar';
|
|
11
|
+
|
|
12
|
+
const fetchCourse = _fetchCourse(okJSONResponse({title: 'Digital IQ test'}));
|
|
13
|
+
const course = await fetchCourse(token, language, universalRef);
|
|
14
|
+
t.is(course.title, 'Digital IQs test');
|
|
15
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import decode from 'jwt-decode';
|
|
2
|
+
import {Fetch, toJSON} from './fetch-responses';
|
|
3
|
+
|
|
4
|
+
// -----------------------------------------------------------------------------
|
|
5
|
+
|
|
6
|
+
type JWT = {
|
|
7
|
+
exp: number;
|
|
8
|
+
iat: number;
|
|
9
|
+
grants: unknown;
|
|
10
|
+
iss: string;
|
|
11
|
+
user: string;
|
|
12
|
+
usage: string;
|
|
13
|
+
host: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type QueryParams = {
|
|
17
|
+
[key: string]: string | number | boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const buildUrlQueryParams = (params: QueryParams) =>
|
|
21
|
+
Object.keys(params)
|
|
22
|
+
.map(key => {
|
|
23
|
+
const value = params[key].toString();
|
|
24
|
+
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
|
25
|
+
})
|
|
26
|
+
.join('&');
|
|
27
|
+
|
|
28
|
+
// -----------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
const fetchCourse = (fetch: Fetch) => async (token, language, universalRef) => {
|
|
31
|
+
const jwt: JWT = decode(token);
|
|
32
|
+
const query = {
|
|
33
|
+
type: 'course',
|
|
34
|
+
universalRef,
|
|
35
|
+
lang: language
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const response = await fetch(`${jwt.host}/api/v2/contents?${buildUrlQueryParams(query)}`, {
|
|
39
|
+
headers: {authorization: token}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const {hits} = (await toJSON(response)) as {hits};
|
|
43
|
+
return hits?.[0];
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// -----------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
export {fetchCourse};
|
|
49
|
+
export default fetchCourse(fetch);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type Response = {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
status: number;
|
|
4
|
+
statusText: string;
|
|
5
|
+
json: () => Promise<unknown>;
|
|
6
|
+
text: () => Promise<string>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type Fetch = (
|
|
10
|
+
url?: string,
|
|
11
|
+
options?: {headers: {authorization: string}}
|
|
12
|
+
) => Promise<Response>;
|
|
13
|
+
|
|
14
|
+
export class ResponseError extends Error {
|
|
15
|
+
public statusCode: number;
|
|
16
|
+
|
|
17
|
+
public constructor(status: number, statusText: string, body = '') {
|
|
18
|
+
super(`${statusText} ${body}`.trim());
|
|
19
|
+
this.name = 'ResponseError';
|
|
20
|
+
this.statusCode = status;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const toJSON = async (response: Response): Promise<unknown> => {
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
const body = await response.text();
|
|
27
|
+
throw new ResponseError(response.status, response.statusText, body);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
return await response.json();
|
|
32
|
+
} catch (err) {
|
|
33
|
+
throw new ResponseError(response.status, response.statusText);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const toText = async (response: Response): Promise<string> => {
|
|
38
|
+
const body = await response.text();
|
|
39
|
+
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new ResponseError(response.status, response.statusText, body);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return body;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const toVoid = async (response: Response): Promise<void> => {
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
const body = await response.text();
|
|
50
|
+
throw new ResponseError(response.status, response.statusText, body);
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {Response} from './fetch-responses.js';
|
|
2
|
+
|
|
3
|
+
export const toResponse = (status: number, statusText: string, body: string): Promise<Response> =>
|
|
4
|
+
Promise.resolve({
|
|
5
|
+
ok: status < 400,
|
|
6
|
+
status,
|
|
7
|
+
statusText,
|
|
8
|
+
json: (): Promise<unknown> => Promise.resolve(body).then(JSON.parse),
|
|
9
|
+
text: (): Promise<string> => Promise.resolve(body)
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
type FetchFunction = () => Promise<Response>;
|
|
13
|
+
|
|
14
|
+
export const okJSONResponse = (body: unknown): FetchFunction => () =>
|
|
15
|
+
toResponse(200, 'OK', JSON.stringify(body));
|
|
16
|
+
|
|
17
|
+
export const internalServerErrorJSONResponse = (body: unknown): FetchFunction => () =>
|
|
18
|
+
toResponse(500, 'InternalServerError', JSON.stringify(body));
|
|
19
|
+
|
|
20
|
+
export const notFoundJSONResponse = (body: unknown): FetchFunction => () =>
|
|
21
|
+
toResponse(404, 'Not Found', JSON.stringify(body));
|
|
22
|
+
|
|
23
|
+
export const okTextResponse = (body: string): FetchFunction => () => toResponse(200, 'OK', body);
|
|
24
|
+
|
|
25
|
+
export const internalServerErrorTextResponse = (body: string): FetchFunction => () =>
|
|
26
|
+
toResponse(500, 'InternalServerError', body);
|