Skip to content

Commit

Permalink
feat: support frontend runtime configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
dcoa committed May 31, 2022
1 parent 3492930 commit 94f69d0
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ let config = {
LOGO_TRADEMARK_URL: process.env.LOGO_TRADEMARK_URL,
LOGO_WHITE_URL: process.env.LOGO_WHITE_URL,
FAVICON_URL: process.env.FAVICON_URL,
MFE_CONFIG_API_URL: process.env.MFE_CONFIG_API_URL,
};

/**
Expand Down Expand Up @@ -193,4 +194,5 @@ export function ensureConfig(keys, requester = 'unspecified application code') {
* @property {string} LOGO_TRADEMARK_URL
* @property {string} LOGO_WHITE_URL
* @property {string} FAVICON_URL
* @property {string} MFE_CONFIG_API_URL
*/
29 changes: 27 additions & 2 deletions src/initialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ import {
publish,
} from './pubSub';
// eslint-disable-next-line import/no-cycle
import { getConfig } from './config';
import {
getConfig, mergeConfig, setConfig,
} from './config';
import {
configure as configureLogging, getLoggingService, NewRelicLoggingService, logError,
} from './logging';
Expand All @@ -76,6 +78,7 @@ import {
APP_ANALYTICS_INITIALIZED,
APP_READY, APP_INIT_ERROR,
} from './constants';
import configureCache from './auth/LocalForageCache';

/**
* A browser history or memory history object created by the [history](https://github.com/ReactTraining/history)
Expand Down Expand Up @@ -128,6 +131,28 @@ export async function auth(requireUser, hydrateUser) {
}
}

/*
* Set or overrides configuration through an API.
* This method allows runtime configuration.
* Set a basic configuration when an error happen and allow initError.
*/

export async function runtimeConfig() {
const apiConfig = { headers: { accept: 'application/json' } };
try {
const apiService = await configureCache();
const url = getConfig().MFE_CONFIG_API_URL;
const { data } = await apiService.get(url, apiConfig);
mergeConfig(data);
} catch (error) {
// eslint-disable-next-line no-console
console.error('Error with config API', error.message);
setConfig({
BASE_URL: `${window.location.host}`,
LANGUAGE_PREFERENCE_COOKIE_NAME: 'openedx-language-preference',
});
}
}
/**
* The default handler for the initialization lifecycle's `analytics` phase.
*
Expand Down Expand Up @@ -223,7 +248,7 @@ export async function initialize({
publish(APP_PUBSUB_INITIALIZED);

// Configuration
await handlers.config();
if (getConfig().MFE_CONFIG_API_URL) { await runtimeConfig(); } else { await handlers.config(); }
publish(APP_CONFIG_INITIALIZED);

// Logging
Expand Down
64 changes: 64 additions & 0 deletions src/initialize.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,41 @@ import {
import { configure as configureAnalytics, SegmentAnalyticsService } from './analytics';
import { configure as configureI18n } from './i18n';
import { getConfig } from './config';
import configureCache from './auth/LocalForageCache';

jest.mock('./logging');
jest.mock('./auth');
jest.mock('./analytics');
jest.mock('./i18n');
jest.mock('./auth/LocalForageCache');

let config = null;
const newConfig = {
SITE_NAME: 'Test Case',
LOGO_URL: 'http://test.example.com:18000/theme/logo.png',
LOGO_TRADEMARK_URL: 'http://test.example.com:18000/theme/logo.png',
LOGO_WHITE_URL: 'http://test.example.com:18000/theme/logo.png',
INFO_EMAIL: '[email protected]',
ACCESS_TOKEN_COOKIE_NAME: 'edx-jwt-cookie-header-payload',
FAVICON_URL: 'http://test.example.com:18000/theme/favicon.ico',
CSRF_TOKEN_API_PATH: '/csrf/api/v1/token',
DISCOVERY_API_BASE_URL: 'http://test.example.com:18381',
PUBLISHER_BASE_URL: 'http://test.example.com:18400',
ECOMMERCE_BASE_URL: 'http://test.example.com:18130',
LANGUAGE_PREFERENCE_COOKIE_NAME: 'openedx-language-preference',
LEARNING_BASE_URL: 'http://test.example.com:2000',
LMS_BASE_URL: 'http://test.example.com:18000',
LOGIN_URL: 'http://test.example.com:18000/login',
LOGOUT_URL: 'http://test.example.com:18000/logout',
STUDIO_BASE_URL: 'http://studio.example.com:18010',
MARKETING_SITE_BASE_URL: 'http://test.example.com:18000',
ORDER_HISTORY_URL: 'http://test.example.com:1996/orders',
REFRESH_ACCESS_TOKEN_ENDPOINT: 'http://test.example.com:18000/login_refresh',
SEGMENT_KEY: '',
USER_INFO_COOKIE_NAME: 'edx-user-info',
IGNORED_ERROR_REGEX: '',
CREDENTIALS_BASE_URL: 'http://test.example.com:18150',
};
describe('initialize', () => {
beforeEach(() => {
config = getConfig();
Expand Down Expand Up @@ -240,4 +268,40 @@ describe('initialize', () => {
expect(overrideHandlers.ready).not.toHaveBeenCalled();
expect(overrideHandlers.initError).toHaveBeenCalledWith(new Error('uhoh!'));
});

it('should initialze the app with runtime configuration', async () => {
config.MFE_CONFIG_API_URL = 'http://localhost:18000/api/mfe/v1/config';

configureCache.mockReturnValueOnce(new Promise((resolve) => {
resolve({ get: () => ({ data: newConfig }) });
}));

const messages = { i_am: 'a message' };
await initialize({ messages });

expect(configureCache).toHaveBeenCalled();
expect(configureLogging).toHaveBeenCalledWith(NewRelicLoggingService, { config });
expect(configureAuth).toHaveBeenCalledWith(AxiosJwtAuthService, {
loggingService: getLoggingService(),
config,
middleware: [],
});
expect(configureAnalytics).toHaveBeenCalledWith(SegmentAnalyticsService, {
config,
loggingService: getLoggingService(),
httpClient: getAuthenticatedHttpClient(),
});
expect(configureI18n).toHaveBeenCalledWith({
messages,
config,
loggingService: getLoggingService(),
});

expect(fetchAuthenticatedUser).toHaveBeenCalled();
expect(ensureAuthenticatedUser).not.toHaveBeenCalled();
expect(hydrateAuthenticatedUser).not.toHaveBeenCalled();
expect(logError).not.toHaveBeenCalled();
expect(config.SITE_NAME).toBe(newConfig.SITE_NAME);
expect(config.LOGIN_URL).toBe(newConfig.LOGIN_URL);
});
});

0 comments on commit 94f69d0

Please sign in to comment.