outsource Token namespace in seperate file to release dependency to GlobalInfos
This commit is contained in:
		@@ -9,7 +9,7 @@ import style from './App.module.css';
 | 
			
		||||
 | 
			
		||||
import SettingsPage from './pages/SettingsPage/SettingsPage';
 | 
			
		||||
import CategoryPage from './pages/CategoryPage/CategoryPage';
 | 
			
		||||
import {APINode, callAPI, token} from './utils/Api';
 | 
			
		||||
import {APINode, callAPI} from './utils/Api';
 | 
			
		||||
 | 
			
		||||
import {BrowserRouter as Router, NavLink, Route, Switch} from 'react-router-dom';
 | 
			
		||||
import Player from './pages/Player/Player';
 | 
			
		||||
@@ -20,6 +20,7 @@ import AuthenticationPage from './pages/AuthenticationPage/AuthenticationPage';
 | 
			
		||||
import TVShowPage from './pages/TVShowPage/TVShowPage';
 | 
			
		||||
import TVPlayer from './pages/TVShowPage/TVPlayer';
 | 
			
		||||
import {CookieTokenStore} from './utils/TokenStore/CookieTokenStore';
 | 
			
		||||
import {token} from './utils/TokenHandler';
 | 
			
		||||
 | 
			
		||||
interface state {
 | 
			
		||||
    password: boolean | null; // null if uninitialized - true if pwd needed false if not needed
 | 
			
		||||
@@ -33,7 +34,7 @@ class App extends React.Component<{}, state> {
 | 
			
		||||
    constructor(props: {}) {
 | 
			
		||||
        super(props);
 | 
			
		||||
 | 
			
		||||
        token.setTokenStore(new CookieTokenStore());
 | 
			
		||||
        token.init(new CookieTokenStore());
 | 
			
		||||
 | 
			
		||||
        let pwdneeded: boolean | null = null;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import React from 'react';
 | 
			
		||||
import {Button} from '../../elements/GPElements/Button';
 | 
			
		||||
import style from './AuthenticationPage.module.css';
 | 
			
		||||
import {addKeyHandler, removeKeyHandler} from '../../utils/ShortkeyHandler';
 | 
			
		||||
import {token} from '../../utils/Api';
 | 
			
		||||
import {token} from '../../utils/TokenHandler';
 | 
			
		||||
import {faTimes} from '@fortawesome/free-solid-svg-icons';
 | 
			
		||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,8 @@ import '@testing-library/jest-dom/extend-expect';
 | 
			
		||||
 | 
			
		||||
import {configure} from 'enzyme';
 | 
			
		||||
import Adapter from 'enzyme-adapter-react-16';
 | 
			
		||||
import GlobalInfos from './utils/GlobalInfos';
 | 
			
		||||
import {CookieTokenStore} from "./utils/TokenStore/CookieTokenStore";
 | 
			
		||||
import {token} from "./utils/Api";
 | 
			
		||||
import {token} from "./utils/TokenHandler";
 | 
			
		||||
 | 
			
		||||
configure({adapter: new Adapter()});
 | 
			
		||||
 | 
			
		||||
@@ -46,7 +45,7 @@ global.callAPIMock = (resonse) => {
 | 
			
		||||
global.beforeEach(() => {
 | 
			
		||||
    // empty fetch response implementation for each test
 | 
			
		||||
    global.fetch = prepareFetchApi({});
 | 
			
		||||
    token.setTokenStore(new CookieTokenStore());
 | 
			
		||||
    token.init(new CookieTokenStore());
 | 
			
		||||
    // todo with callAPIMock
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										126
									
								
								src/utils/Api.ts
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								src/utils/Api.ts
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
import GlobalInfos from './GlobalInfos';
 | 
			
		||||
import {TokenStore} from './TokenStore/TokenStore';
 | 
			
		||||
import {token} from './TokenHandler';
 | 
			
		||||
 | 
			
		||||
const APIPREFIX: string = '/api/';
 | 
			
		||||
 | 
			
		||||
@@ -12,130 +12,6 @@ interface ApiBaseRequest {
 | 
			
		||||
    [_: string]: string | number | boolean | object;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export namespace token {
 | 
			
		||||
    // store api token - empty if not set
 | 
			
		||||
    let apiToken = '';
 | 
			
		||||
 | 
			
		||||
    // a callback que to be called after api token refresh
 | 
			
		||||
    let callQue: ((error: string) => void)[] = [];
 | 
			
		||||
    // flag to check wheter a api refresh is currently pending
 | 
			
		||||
    let refreshInProcess = false;
 | 
			
		||||
    // store the expire seconds of token
 | 
			
		||||
    let expireSeconds = -1;
 | 
			
		||||
 | 
			
		||||
    let tokenStore: TokenStore;
 | 
			
		||||
 | 
			
		||||
    export function setTokenStore(ts: TokenStore): void {
 | 
			
		||||
        tokenStore = ts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * refresh the api token or use that one in cookie if still valid
 | 
			
		||||
     * @param callback to be called after successful refresh
 | 
			
		||||
     * @param password
 | 
			
		||||
     * @param force
 | 
			
		||||
     */
 | 
			
		||||
    export function refreshAPIToken(callback: (error: string) => void, force?: boolean, password?: string): void {
 | 
			
		||||
        callQue.push(callback);
 | 
			
		||||
 | 
			
		||||
        // check if already is a token refresh is in process
 | 
			
		||||
        if (refreshInProcess) {
 | 
			
		||||
            // if yes return
 | 
			
		||||
            return;
 | 
			
		||||
        } else {
 | 
			
		||||
            // if not set flat
 | 
			
		||||
            refreshInProcess = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (apiTokenValid() && !force) {
 | 
			
		||||
            console.log('token still valid...');
 | 
			
		||||
            callFuncQue('');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const formData = new FormData();
 | 
			
		||||
        formData.append('grant_type', 'client_credentials');
 | 
			
		||||
        formData.append('client_id', 'openmediacenter');
 | 
			
		||||
        formData.append('client_secret', password ? password : 'openmediacenter');
 | 
			
		||||
        formData.append('scope', 'all');
 | 
			
		||||
 | 
			
		||||
        interface APIToken {
 | 
			
		||||
            error?: string;
 | 
			
		||||
            // eslint-disable-next-line camelcase
 | 
			
		||||
            access_token: string; // no camel case allowed because of backendlib
 | 
			
		||||
            // eslint-disable-next-line camelcase
 | 
			
		||||
            expires_in: number; // no camel case allowed because of backendlib
 | 
			
		||||
            scope: string;
 | 
			
		||||
            // eslint-disable-next-line camelcase
 | 
			
		||||
            token_type: string; // no camel case allowed because of backendlib
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fetch('/token', {method: 'POST', body: formData}).then((response) =>
 | 
			
		||||
            response.json().then((result: APIToken) => {
 | 
			
		||||
                if (result.error) {
 | 
			
		||||
                    callFuncQue(result.error);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                // set api token
 | 
			
		||||
                apiToken = result.access_token;
 | 
			
		||||
                // set expire time
 | 
			
		||||
                expireSeconds = new Date().getTime() / 1000 + result.expires_in;
 | 
			
		||||
                // setTokenCookie(apiToken, expireSeconds);
 | 
			
		||||
                tokenStore.setToken({accessToken: apiToken, expireTime: expireSeconds, tokenType: '', scope: ''});
 | 
			
		||||
                // call all handlers and release flag
 | 
			
		||||
                callFuncQue('');
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    export function apiTokenValid(): boolean {
 | 
			
		||||
        // check if a cookie with token is available
 | 
			
		||||
        // const token = getTokenCookie();
 | 
			
		||||
        const tmptoken = tokenStore.loadToken();
 | 
			
		||||
        if (tmptoken !== null) {
 | 
			
		||||
            // check if token is at least valid for the next minute
 | 
			
		||||
            if (tmptoken.expireTime > new Date().getTime() / 1000 + 60) {
 | 
			
		||||
                apiToken = tmptoken.accessToken;
 | 
			
		||||
                expireSeconds = tmptoken.expireTime;
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * call all qued callbacks
 | 
			
		||||
     */
 | 
			
		||||
    function callFuncQue(error: string): void {
 | 
			
		||||
        // call all pending handlers
 | 
			
		||||
        callQue.map((func) => {
 | 
			
		||||
            return func(error);
 | 
			
		||||
        });
 | 
			
		||||
        // reset pending que
 | 
			
		||||
        callQue = [];
 | 
			
		||||
        // release flag to be able to start new refresh
 | 
			
		||||
        refreshInProcess = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * check if api token is valid -- if not request new one
 | 
			
		||||
     * when finished call callback
 | 
			
		||||
     * @param callback function to be called afterwards
 | 
			
		||||
     */
 | 
			
		||||
    export function checkAPITokenValid(callback: (mytoken: string) => void): void {
 | 
			
		||||
        // check if token is valid and set
 | 
			
		||||
        if (apiToken === '' || expireSeconds <= new Date().getTime() / 1000) {
 | 
			
		||||
            console.log('token not valid...');
 | 
			
		||||
            refreshAPIToken(() => {
 | 
			
		||||
                callback(apiToken);
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            callback(apiToken);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A backend api call
 | 
			
		||||
 * @param apinode which api backend handler to call
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										131
									
								
								src/utils/TokenHandler.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/utils/TokenHandler.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
import {TokenStore} from './TokenStore/TokenStore';
 | 
			
		||||
 | 
			
		||||
export namespace token {
 | 
			
		||||
    // store api token - empty if not set
 | 
			
		||||
    let apiToken = '';
 | 
			
		||||
 | 
			
		||||
    // a callback que to be called after api token refresh
 | 
			
		||||
    let callQue: ((error: string) => void)[] = [];
 | 
			
		||||
    // flag to check wheter a api refresh is currently pending
 | 
			
		||||
    let refreshInProcess = false;
 | 
			
		||||
    // store the expire seconds of token
 | 
			
		||||
    let expireSeconds = -1;
 | 
			
		||||
 | 
			
		||||
    let tokenStore: TokenStore;
 | 
			
		||||
    let APiHost: string = '/';
 | 
			
		||||
 | 
			
		||||
    export function init(ts: TokenStore, apiHost?: string): void {
 | 
			
		||||
        tokenStore = ts;
 | 
			
		||||
        if (apiHost) {
 | 
			
		||||
            APiHost = apiHost;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * refresh the api token or use that one in cookie if still valid
 | 
			
		||||
     * @param callback to be called after successful refresh
 | 
			
		||||
     * @param password
 | 
			
		||||
     * @param force
 | 
			
		||||
     */
 | 
			
		||||
    export function refreshAPIToken(callback: (error: string) => void, force?: boolean, password?: string): void {
 | 
			
		||||
        callQue.push(callback);
 | 
			
		||||
 | 
			
		||||
        // check if already is a token refresh is in process
 | 
			
		||||
        if (refreshInProcess) {
 | 
			
		||||
            // if yes return
 | 
			
		||||
            return;
 | 
			
		||||
        } else {
 | 
			
		||||
            // if not set flat
 | 
			
		||||
            refreshInProcess = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (apiTokenValid() && !force) {
 | 
			
		||||
            console.log('token still valid...');
 | 
			
		||||
            callFuncQue('');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const formData = new FormData();
 | 
			
		||||
        formData.append('grant_type', 'client_credentials');
 | 
			
		||||
        formData.append('client_id', 'openmediacenter');
 | 
			
		||||
        formData.append('client_secret', password ? password : 'openmediacenter');
 | 
			
		||||
        formData.append('scope', 'all');
 | 
			
		||||
 | 
			
		||||
        interface APIToken {
 | 
			
		||||
            error?: string;
 | 
			
		||||
            // eslint-disable-next-line camelcase
 | 
			
		||||
            access_token: string; // no camel case allowed because of backendlib
 | 
			
		||||
            // eslint-disable-next-line camelcase
 | 
			
		||||
            expires_in: number; // no camel case allowed because of backendlib
 | 
			
		||||
            scope: string;
 | 
			
		||||
            // eslint-disable-next-line camelcase
 | 
			
		||||
            token_type: string; // no camel case allowed because of backendlib
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        console.log(APiHost);
 | 
			
		||||
 | 
			
		||||
        fetch(APiHost + 'token', {method: 'POST', body: formData}).then((response) =>
 | 
			
		||||
            response.json().then((result: APIToken) => {
 | 
			
		||||
                if (result.error) {
 | 
			
		||||
                    callFuncQue(result.error);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                // set api token
 | 
			
		||||
                apiToken = result.access_token;
 | 
			
		||||
                // set expire time
 | 
			
		||||
                expireSeconds = new Date().getTime() / 1000 + result.expires_in;
 | 
			
		||||
                // setTokenCookie(apiToken, expireSeconds);
 | 
			
		||||
                tokenStore.setToken({accessToken: apiToken, expireTime: expireSeconds, tokenType: '', scope: ''});
 | 
			
		||||
                // call all handlers and release flag
 | 
			
		||||
                callFuncQue('');
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    export function apiTokenValid(): boolean {
 | 
			
		||||
        // check if a cookie with token is available
 | 
			
		||||
        // const token = getTokenCookie();
 | 
			
		||||
        const tmptoken = tokenStore.loadToken();
 | 
			
		||||
        if (tmptoken !== null) {
 | 
			
		||||
            // check if token is at least valid for the next minute
 | 
			
		||||
            if (tmptoken.expireTime > new Date().getTime() / 1000 + 60) {
 | 
			
		||||
                apiToken = tmptoken.accessToken;
 | 
			
		||||
                expireSeconds = tmptoken.expireTime;
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * call all qued callbacks
 | 
			
		||||
     */
 | 
			
		||||
    function callFuncQue(error: string): void {
 | 
			
		||||
        // call all pending handlers
 | 
			
		||||
        callQue.map((func) => {
 | 
			
		||||
            return func(error);
 | 
			
		||||
        });
 | 
			
		||||
        // reset pending que
 | 
			
		||||
        callQue = [];
 | 
			
		||||
        // release flag to be able to start new refresh
 | 
			
		||||
        refreshInProcess = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * check if api token is valid -- if not request new one
 | 
			
		||||
     * when finished call callback
 | 
			
		||||
     * @param callback function to be called afterwards
 | 
			
		||||
     */
 | 
			
		||||
    export function checkAPITokenValid(callback: (mytoken: string) => void): void {
 | 
			
		||||
        // check if token is valid and set
 | 
			
		||||
        if (apiToken === '' || expireSeconds <= new Date().getTime() / 1000) {
 | 
			
		||||
            console.log('token not valid...');
 | 
			
		||||
            refreshAPIToken(() => {
 | 
			
		||||
                callback(apiToken);
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            callback(apiToken);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user