@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.
Files changed (105) hide show
  1. package/es/actions/courses.d.ts +7 -0
  2. package/es/actions/courses.d.ts.map +1 -0
  3. package/es/actions/courses.js +5 -0
  4. package/es/actions/index.d.ts +6 -0
  5. package/es/actions/index.d.ts.map +1 -1
  6. package/es/actions/index.js +2 -0
  7. package/es/actions/navigation.d.ts +7 -0
  8. package/es/actions/navigation.d.ts.map +1 -1
  9. package/es/actions/navigation.js +5 -0
  10. package/es/actions/token.d.ts +7 -0
  11. package/es/actions/token.d.ts.map +1 -0
  12. package/es/actions/token.js +5 -0
  13. package/es/configure-store.d.ts +11 -3
  14. package/es/configure-store.d.ts.map +1 -1
  15. package/es/configure-store.js +4 -2
  16. package/es/index.d.ts +9 -3
  17. package/es/index.d.ts.map +1 -1
  18. package/es/index.js +4 -4
  19. package/es/middlewares/on-start-app.d.ts +7 -0
  20. package/es/middlewares/on-start-app.d.ts.map +1 -0
  21. package/es/middlewares/on-start-app.js +17 -0
  22. package/es/reducers/courses.d.ts +6 -0
  23. package/es/reducers/courses.d.ts.map +1 -0
  24. package/es/reducers/courses.js +14 -0
  25. package/es/reducers/index.d.ts +2 -0
  26. package/es/reducers/index.d.ts.map +1 -1
  27. package/es/reducers/index.js +5 -1
  28. package/es/reducers/navigation.d.ts.map +1 -1
  29. package/es/reducers/navigation.js +3 -0
  30. package/es/reducers/token.d.ts +7 -0
  31. package/es/reducers/token.d.ts.map +1 -0
  32. package/es/reducers/token.js +16 -0
  33. package/es/services/fetch-demo.d.ts +6 -0
  34. package/es/services/fetch-demo.d.ts.map +1 -0
  35. package/es/services/fetch-demo.js +25 -0
  36. package/es/services/fetch-demo.test.d.ts +2 -0
  37. package/es/services/fetch-demo.test.d.ts.map +1 -0
  38. package/es/services/fetch-demo.test.js +13 -0
  39. package/es/services/fetch-responses.d.ts +20 -0
  40. package/es/services/fetch-responses.d.ts.map +1 -0
  41. package/es/services/fetch-responses.js +33 -0
  42. package/es/services/fetch.test.d.ts +10 -0
  43. package/es/services/fetch.test.d.ts.map +1 -0
  44. package/es/services/fetch.test.js +12 -0
  45. package/lib/actions/courses.d.ts +7 -0
  46. package/lib/actions/courses.d.ts.map +1 -0
  47. package/lib/actions/courses.js +9 -0
  48. package/lib/actions/index.d.ts +6 -0
  49. package/lib/actions/index.d.ts.map +1 -1
  50. package/lib/actions/index.js +2 -0
  51. package/lib/actions/navigation.d.ts +7 -0
  52. package/lib/actions/navigation.d.ts.map +1 -1
  53. package/lib/actions/navigation.js +7 -1
  54. package/lib/actions/token.d.ts +7 -0
  55. package/lib/actions/token.d.ts.map +1 -0
  56. package/lib/actions/token.js +9 -0
  57. package/lib/configure-store.d.ts +11 -3
  58. package/lib/configure-store.d.ts.map +1 -1
  59. package/lib/configure-store.js +4 -2
  60. package/lib/index.d.ts +9 -3
  61. package/lib/index.d.ts.map +1 -1
  62. package/lib/index.js +3 -3
  63. package/lib/middlewares/on-start-app.d.ts +7 -0
  64. package/lib/middlewares/on-start-app.d.ts.map +1 -0
  65. package/lib/middlewares/on-start-app.js +22 -0
  66. package/lib/reducers/courses.d.ts +6 -0
  67. package/lib/reducers/courses.d.ts.map +1 -0
  68. package/lib/reducers/courses.js +27 -0
  69. package/lib/reducers/index.d.ts +2 -0
  70. package/lib/reducers/index.d.ts.map +1 -1
  71. package/lib/reducers/index.js +5 -1
  72. package/lib/reducers/navigation.d.ts.map +1 -1
  73. package/lib/reducers/navigation.js +3 -0
  74. package/lib/reducers/token.d.ts +7 -0
  75. package/lib/reducers/token.d.ts.map +1 -0
  76. package/lib/reducers/token.js +21 -0
  77. package/lib/services/fetch-demo.d.ts +6 -0
  78. package/lib/services/fetch-demo.d.ts.map +1 -0
  79. package/lib/services/fetch-demo.js +75 -0
  80. package/lib/services/fetch-demo.test.d.ts +2 -0
  81. package/lib/services/fetch-demo.test.d.ts.map +1 -0
  82. package/lib/services/fetch-demo.test.js +60 -0
  83. package/lib/services/fetch-responses.d.ts +20 -0
  84. package/lib/services/fetch-responses.d.ts.map +1 -0
  85. package/lib/services/fetch-responses.js +118 -0
  86. package/lib/services/fetch.test.d.ts +10 -0
  87. package/lib/services/fetch.test.d.ts.map +1 -0
  88. package/lib/services/fetch.test.js +31 -0
  89. package/package.json +8 -3
  90. package/readme.md +23 -0
  91. package/src/actions/courses.ts +11 -0
  92. package/src/actions/index.ts +8 -0
  93. package/src/actions/navigation.ts +12 -0
  94. package/src/actions/token.ts +11 -0
  95. package/src/configure-store.ts +15 -2
  96. package/src/index.tsx +12 -3
  97. package/src/middlewares/on-start-app.ts +30 -0
  98. package/src/reducers/courses.ts +22 -0
  99. package/src/reducers/index.ts +6 -1
  100. package/src/reducers/navigation.ts +9 -1
  101. package/src/reducers/token.ts +26 -0
  102. package/src/services/fetch-demo.test.ts +15 -0
  103. package/src/services/fetch-demo.ts +49 -0
  104. package/src/services/fetch-responses.ts +53 -0
  105. 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.13",
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": "echo @todo setup unit tests"
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": "ba829b703d71b8606e85f3cd6fb932049525e684"
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
+ });
@@ -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
+ });
@@ -0,0 +1,11 @@
1
+ export const STORE_TOKEN = '@@token/STORE_TOKEN';
2
+
3
+ export type $STORE_TOKEN = {
4
+ type: '@@token/STORE_TOKEN';
5
+ payload: string;
6
+ };
7
+
8
+ export const storeToken = (token: string): $STORE_TOKEN => ({
9
+ type: STORE_TOKEN,
10
+ payload: token
11
+ });
@@ -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
- export default function configureStore(services) {
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 = ({services}) => {
20
+ const AppRevision = ({options}: Props) => {
21
21
  const [store, setStore] = useState(null);
22
22
 
23
23
  useEffect(() => {
24
- const store = configureStore(services);
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;
@@ -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
- navigation
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
- export type State = Array<string>;
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);