-
Notifications
You must be signed in to change notification settings - Fork 64
/
config.js
327 lines (318 loc) · 11.8 KB
/
config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
/**
* #### Import members from **@edx/frontend-platform**
*
* The configuration module provides utilities for working with an application's configuration
* document (ConfigDocument). Configuration variables can be supplied to the
* application in four different ways. They are applied in the following order:
*
* - Build-time Configuration
* - Environment Variables
* - JavaScript File
* - Runtime Configuration
*
* Last one in wins. Variables with the same name defined via the later methods will override any
* defined using an earlier method. i.e., if a variable is defined in Runtime Configuration, that
* will override the same variable defined in either Build-time Configuration method (environment
* variables or JS file). Configuration defined in a JS file will override environment variables.
*
* ##### Build-time Configuration
*
* Build-time configuration methods add config variables into the app when it is built by webpack.
* This saves the app an API call and means it has all the information it needs to initialize right
* away. There are two methods of supplying build-time configuration: environment variables and a
* JavaScript file.
*
* ###### Environment Variables
*
* A set list of required config variables can be supplied as
* command-line environment variables during the build process.
*
* As a simple example, these are supplied on the command-line before invoking `npm run build`:
*
* ```
* LMS_BASE_URL=http://localhost:18000 npm run build
* ```
*
* Note that additional variables _cannot_ be supplied via this method without using the `config`
* initialization handler. The app won't pick them up and they'll appear `undefined`.
*
* This configuration method is being deprecated in favor of JavaScript File Configuration.
*
* ###### JavaScript File Configuration
*
* Configuration variables can be supplied in an optional file named env.config.js. This file must
* export either an Object containing configuration variables or a function. The function must
* return an Object containing configuration variables or, alternately, a promise which resolves to
* an Object.
*
* Using a function or async function allows the configuration to be resolved at runtime (because
* the function will be executed at runtime). This is not common, and the capability is included
* for the sake of flexibility.
*
* JavaScript File Configuration is well-suited to extensibility use cases or component overrides,
* in that the configuration file can depend on any installed JavaScript module. It is also the
* preferred way of doing build-time configuration if runtime configuration isn't used by your
* deployment of the platform.
*
* Exporting a config object:
* ```
* const config = {
* LMS_BASE_URL: 'http://localhost:18000'
* };
*
* export default config;
* ```
*
* Exporting a function that returns an object:
* ```
* function getConfig() {
* return {
* LMS_BASE_URL: 'http://localhost:18000'
* };
* }
* ```
*
* Exporting a function that returns a promise that resolves to an object:
* ```
* function getAsyncConfig() {
* return new Promise((resolve, reject) => {
* resolve({
* LMS_BASE_URL: 'http://localhost:18000'
* });
* });
* }
*
* export default getAsyncConfig;
* ```
*
* ##### Runtime Configuration
*
* Configuration variables can also be supplied using the "runtime configuration" method, taking
* advantage of the Micro-frontend Config API in edx-platform. More information on this API can be
* found in the ADR which introduced it:
*
* https://github.com/openedx/edx-platform/blob/master/lms/djangoapps/mfe_config_api/docs/decisions/0001-mfe-config-api.rst
*
* The runtime configuration method can be enabled by supplying a MFE_CONFIG_API_URL via one of the other
* two configuration methods above.
*
* Runtime configuration is particularly useful if you need to supply different configurations to
* a single deployment of a micro-frontend, for instance. It is also a perfectly valid alternative
* to build-time configuration, though it introduces an additional API call to edx-platform on MFE
* initialization.
*
* ##### Initialization Config Handler
*
* The configuration document can be extended by
* applications at run-time using a `config` initialization handler. Please see the Initialization
* documentation for more information on handlers and initialization phases.
*
* ```
* initialize({
* handlers: {
* config: () => {
* mergeConfig({
* CUSTOM_VARIABLE: 'custom value',
* LMS_BASE_URL: 'http://localhost:18001' // You can override variables, but this is uncommon.
* }, 'App config override handler');
* },
* },
* });
* ```
*
* @module Config
*/
import { APP_CONFIG_INITIALIZED, CONFIG_CHANGED } from './constants';
import { publish, subscribe } from './pubSub';
import { ensureDefinedConfig } from './utils';
function extractRegex(envVar) {
// Convert the environment variable string to a regex, while guarding
// against a non-string and an empty/whitespace-only string.
if (typeof envVar === 'string' && envVar.trim() !== '') {
return new RegExp(envVar);
}
return undefined;
}
const ENVIRONMENT = process.env.NODE_ENV;
let config = {
ACCESS_TOKEN_COOKIE_NAME: process.env.ACCESS_TOKEN_COOKIE_NAME,
ACCOUNT_PROFILE_URL: process.env.ACCOUNT_PROFILE_URL,
ACCOUNT_SETTINGS_URL: process.env.ACCOUNT_SETTINGS_URL,
BASE_URL: process.env.BASE_URL,
PUBLIC_PATH: process.env.PUBLIC_PATH || '/',
CREDENTIALS_BASE_URL: process.env.CREDENTIALS_BASE_URL,
CSRF_TOKEN_API_PATH: process.env.CSRF_TOKEN_API_PATH,
DISCOVERY_API_BASE_URL: process.env.DISCOVERY_API_BASE_URL,
PUBLISHER_BASE_URL: process.env.PUBLISHER_BASE_URL,
ECOMMERCE_BASE_URL: process.env.ECOMMERCE_BASE_URL,
ENVIRONMENT,
IGNORED_ERROR_REGEX: extractRegex(process.env.IGNORED_ERROR_REGEX),
LANGUAGE_PREFERENCE_COOKIE_NAME: process.env.LANGUAGE_PREFERENCE_COOKIE_NAME,
LEARNING_BASE_URL: process.env.LEARNING_BASE_URL,
LMS_BASE_URL: process.env.LMS_BASE_URL,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
STUDIO_BASE_URL: process.env.STUDIO_BASE_URL,
MARKETING_SITE_BASE_URL: process.env.MARKETING_SITE_BASE_URL,
ORDER_HISTORY_URL: process.env.ORDER_HISTORY_URL,
REFRESH_ACCESS_TOKEN_ENDPOINT: process.env.REFRESH_ACCESS_TOKEN_ENDPOINT,
SECURE_COOKIES: ENVIRONMENT !== 'development',
SEGMENT_KEY: process.env.SEGMENT_KEY,
SITE_NAME: process.env.SITE_NAME,
USER_INFO_COOKIE_NAME: process.env.USER_INFO_COOKIE_NAME,
LOGO_URL: process.env.LOGO_URL,
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,
APP_ID: process.env.APP_ID,
SUPPORT_URL: process.env.SUPPORT_URL,
};
/**
* Getter for the application configuration document. This is synchronous and merely returns a
* reference to an existing object, and is thus safe to call as often as desired.
*
* Example:
*
* ```
* import { getConfig } from '@edx/frontend-platform';
*
* const {
* LMS_BASE_URL,
* } = getConfig();
* ```
*
* @returns {ConfigDocument}
*/
export function getConfig() {
return config;
}
/**
* Replaces the existing ConfigDocument. This is not commonly used, but can be helpful for tests.
*
* The supplied config document will be tested with `ensureDefinedConfig` to ensure it does not
* have any `undefined` keys.
*
* Example:
*
* ```
* import { setConfig } from '@edx/frontend-platform';
*
* setConfig({
* LMS_BASE_URL, // This is overriding the ENTIRE document - this is not merged in!
* });
* ```
*
* @param {ConfigDocument} newConfig
*/
export function setConfig(newConfig) {
ensureDefinedConfig(config, 'config');
config = newConfig;
publish(CONFIG_CHANGED);
}
/**
* Merges additional configuration values into the ConfigDocument returned by `getConfig`. Will
* override any values that exist with the same keys.
*
* ```
* mergeConfig({
* NEW_KEY: 'new value',
* OTHER_NEW_KEY: 'other new value',
* });
*
* If any of the key values are `undefined`, an error will be logged to 'warn'.
*
* @param {Object} newConfig
*/
export function mergeConfig(newConfig) {
ensureDefinedConfig(newConfig, 'ProcessEnvConfigService');
config = Object.assign(config, newConfig);
publish(CONFIG_CHANGED);
}
/**
* A method allowing application code to indicate that particular ConfigDocument keys are required
* for them to function. This is useful for diagnosing development/deployment issues, primarily,
* by surfacing misconfigurations early. For instance, if the build process fails to supply an
* environment variable on the command-line, it's possible that one of the `process.env` variables
* will be undefined. Should be used in conjunction with `mergeConfig` for custom `ConfigDocument`
* properties. Requester is for informational/error reporting purposes only.
*
* ```
* ensureConfig(['LMS_BASE_URL', 'LOGIN_URL'], 'MySpecialComponent');
*
* // Will log a warning with:
* // "App configuration error: LOGIN_URL is required by MySpecialComponent."
* // if LOGIN_URL is undefined, for example.
* ```
*
* *NOTE*: `ensureConfig` waits until `APP_CONFIG_INITIALIZED` is published to verify the existence
* of the specified properties. This means that this function is compatible with custom `config`
* phase handlers responsible for loading additional configuration data in the initialization
* sequence.
*
* @param {Array} keys
* @param {string} [requester='unspecified application code']
*/
export function ensureConfig(keys, requester = 'unspecified application code') {
subscribe(APP_CONFIG_INITIALIZED, () => {
keys.forEach((key) => {
if (config[key] === undefined) {
// eslint-disable-next-line no-console
console.warn(`App configuration error: ${key} is required by ${requester}.`);
}
});
});
}
/**
* An object describing the current application configuration.
*
* In its most basic form, the initialization process loads this document via `process.env`
* variables. There are other ways to add configuration variables to the ConfigDocument as
* documented above (JavaScript File Configuration, Runtime Configuration, and the Initialization
* Config Handler)
*
* ```
* {
* BASE_URL: process.env.BASE_URL,
* // ... other vars
* }
* ```
*
* When using Webpack (i.e., normal usage), the build process is responsible for supplying these
* variables via command-line environment variables. That means they must be supplied at build
* time.
*
* @name ConfigDocument
* @memberof module:Config
* @property {string} ACCESS_TOKEN_COOKIE_NAME
* @property {string} ACCOUNT_PROFILE_URL
* @property {string} ACCOUNT_SETTINGS_URL
* @property {string} BASE_URL The URL of the current application.
* @property {string} CREDENTIALS_BASE_URL
* @property {string} CSRF_TOKEN_API_PATH
* @property {string} DISCOVERY_API_BASE_URL
* @property {string} PUBLISHER_BASE_URL
* @property {string} ECOMMERCE_BASE_URL
* @property {string} ENVIRONMENT This is one of: development, production, or test.
* @property {string} IGNORED_ERROR_REGEX
* @property {string} LANGUAGE_PREFERENCE_COOKIE_NAME
* @property {string} LEARNING_BASE_URL
* @property {string} LMS_BASE_URL
* @property {string} LOGIN_URL
* @property {string} LOGOUT_URL
* @property {string} STUDIO_BASE_URL
* @property {string} MARKETING_SITE_BASE_URL
* @property {string} ORDER_HISTORY_URL
* @property {string} REFRESH_ACCESS_TOKEN_ENDPOINT
* @property {boolean} SECURE_COOKIES
* @property {string} SEGMENT_KEY
* @property {string} SITE_NAME
* @property {string} USER_INFO_COOKIE_NAME
* @property {string} LOGO_URL
* @property {string} LOGO_TRADEMARK_URL
* @property {string} LOGO_WHITE_URL
* @property {string} FAVICON_URL
* @property {string} MFE_CONFIG_API_URL
* @property {string} APP_ID
* @property {string} SUPPORT_URL
*/