refactor config filename and convert to yaml

This commit is contained in:
William Welling
2021-12-08 09:38:52 -06:00
parent bc999d0b5f
commit 10622008c4
16 changed files with 330 additions and 45 deletions

View File

@@ -104,10 +104,10 @@ Installing
Default configuration file is located in `config/` folder. Default configuration file is located in `config/` folder.
To change the default configuration values, create local files that override the parameters you need to change. You can use `appConfig.json` as a starting point. To override the default configuration values, create local files that override the parameters you need to change. You can use `config.example.yml` as a starting point.
- Create a new `appConfig.(dev or development).json` file in `config/` for a `development` environment; - Create a new `config.(dev or development).yml` file in `config/` for a `development` environment;
- Create a new `appConfig.(prod or production).ts` file in `config/` for a `production` environment; - Create a new `config.(prod or production).yml` file in `config/` for a `production` environment;
The settings can also be overwritten using an environment file or environment variables. The settings can also be overwritten using an environment file or environment variables.
@@ -144,9 +144,9 @@ The same settings can also be overwritten by setting system environment variable
export DSPACE_HOST=api7.dspace.org export DSPACE_HOST=api7.dspace.org
``` ```
The priority works as follows: **environment variable** overrides **variable in `.env` file** overrides external config set by `APP_CONFIG_PATH` overrides **`appConfig.(prod or dev).json`** The priority works as follows: **environment variable** overrides **variable in `.env` file** overrides external config set by `APP_CONFIG_PATH` overrides **`config.(prod or dev).yml`**
These configuration sources are collected **at run time**, and written to `dist/browser/assets/appConfig.json` for production and `src/app/assets/appConfig.json` for development. These configuration sources are collected **at run time**, and written to `dist/browser/assets/config.json` for production and `src/app/assets/config.json` for development.
The configuration file can be externalized by using environment variable `APP_CONFIG_PATH`. The configuration file can be externalized by using environment variable `APP_CONFIG_PATH`.
@@ -251,7 +251,7 @@ E2E tests (aka integration tests) use [Cypress.io](https://www.cypress.io/). Con
The test files can be found in the `./cypress/integration/` folder. The test files can be found in the `./cypress/integration/` folder.
Before you can run e2e tests, you MUST have a running backend (i.e. REST API). By default, the e2e tests look for this at http://localhost:8080/server/ or whatever `rest` backend is defined in your `appConfig.prod.json` or `appConfig.json`. You may override this using env variables, see [Configuring](#configuring). Before you can run e2e tests, you MUST have a running backend (i.e. REST API). By default, the e2e tests look for this at http://localhost:8080/server/ or whatever `rest` backend is defined in your `config.prod.yml` or `config.yml`. You may override this using env variables, see [Configuring](#configuring).
Run `ng e2e` to kick off the tests. This will start Cypress and allow you to select the browser you wish to use, as well as whether you wish to run all tests or an individual test file. Once you click run on test(s), this opens the [Cypress Test Runner](https://docs.cypress.io/guides/core-concepts/test-runner) to run your test(s) and show you the results. Run `ng e2e` to kick off the tests. This will start Cypress and allow you to select the browser you wish to use, as well as whether you wish to run all tests or an individual test file. Once you click run on test(s), this opens the [Cypress Test Runner](https://docs.cypress.io/guides/core-concepts/test-runner) to run your test(s) and show you the results.
@@ -331,7 +331,7 @@ File Structure
``` ```
dspace-angular dspace-angular
├── config * ├── config *
│ └── appConfig.json * Default app config │ └── config.yml * Default app config
├── cypress * Folder for Cypress (https://cypress.io/) / e2e tests ├── cypress * Folder for Cypress (https://cypress.io/) / e2e tests
│ ├── downloads * │ ├── downloads *
│ ├── fixtures * Folder for e2e/integration test files │ ├── fixtures * Folder for e2e/integration test files

3
config/.gitignore vendored
View File

@@ -1 +1,2 @@
appConfig.*.json config.*.yml
!config.example.yml

View File

@@ -1,8 +0,0 @@
{
"rest": {
"ssl": true,
"host": "api7.dspace.org",
"port": 443,
"nameSpace": "/server"
}
}

231
config/config.example.yml Normal file
View File

@@ -0,0 +1,231 @@
# NOTE: will log all redux actions and transfers in console
debug: false
# Angular Universal server settings
# NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg.
ui:
ssl: false
host: localhost
port: 4000
# NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
nameSpace: /
# The rateLimiter settings limit each IP to a 'max' of 500 requests per 'windowMs' (1 minute).
rateLimiter:
windowMs: 60000 # 1 minute
max: 500 # limit each IP to 500 requests per windowMs
# The REST API server settings
# NOTE: these must be 'synced' with the 'dspace.server.url' setting in your backend's local.cfg.
rest:
ssl: true
host: api7.dspace.org
port: 443
# NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
nameSpace: /server
# Caching settings
cache:
# NOTE: how long should objects be cached for by default
msToLive:
default: 900000 # 15 minutes
control: max-age=60 # revalidate browser
autoSync:
defaultTime: 0
maxBufferSize: 100
timePerMethod:
PATCH: 3 # time in seconds
# Authentication settings
auth:
# Authentication UI settings
ui:
# the amount of time before the idle warning is shown
timeUntilIdle: 900000 # 15 minutes
# the amount of time the user has to react after the idle warning is shown before they are logged out.
idleGracePeriod: 300000 # 5 minutes
# Authentication REST settings
rest:
# If the rest token expires in less than this amount of time, it will be refreshed automatically.
# This is independent from the idle warning.
timeLeftBeforeTokenRefresh: 120000 # 2 minutes
# Form settings
form:
# NOTE: Map server-side validators to comparative Angular form validators
validatorMap:
required: required
regex: pattern
# Notification settings
notifications:
rtl: false
position:
- top
- right
maxStack: 8
# NOTE: after how many seconds notification is closed automatically. If set to zero notifications are not closed automatically
timeOut: 5000 # 5 second
clickToClose: true
# NOTE: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale'
animate: scale
# Submission settings
submission:
autosave:
# NOTE: which metadata trigger an autosave
metadata: []
# NOTE: after how many time (milliseconds) submission is saved automatically
# eg. timer: 5 * (1000 * 60); // 5 minutes
timer: 0
icons:
metadata:
# NOTE: example of configuration
# # NOTE: metadata name
# - name: dc.author
# # NOTE: fontawesome (v5.x) icon classes and bootstrap utility classes can be used
# style: fas fa-user
- name: dc.author
style: fas fa-user
# default configuration
- name: default
style: ''
authority:
confidence:
# NOTE: example of configuration
# # NOTE: confidence value
# - name: dc.author
# # NOTE: fontawesome (v5.x) icon classes and bootstrap utility classes can be used
# style: fa-user
- value: 600
style: text-success
- value: 500
style: text-info
- value: 400
style: text-warning
# default configuration
- value: default
style: text-muted
# Default Language in which the UI will be rendered if the user's browser language is not an active language
defaultLanguage: en
# Languages. DSpace Angular holds a message catalog for each of the following languages.
# When set to active, users will be able to switch to the use of this language in the user interface.
languages:
- code: en
label: English
active: true
- code: cs
label: Čeština
active: true
- code: de
label: Deutsch
active: true
- code: es
label: Español
active: true
- code: fr
label: Français
active: true
- code: lv
label: Latviešu
active: true
- code: hu
label: Magyar
active: true
- code: nl
label: Nederlands
active: true
- code: pt-PT
label: Português
active: true
- code: pt-BR
label: Português do Brasil
active: true
- code: fi
label: Suomi
active: true
# Browse-By Pages
browseBy:
# Amount of years to display using jumps of one year (current year - oneYearLimit)
oneYearLimit: 10
# Limit for years to display using jumps of five years (current year - fiveYearLimit)
fiveYearLimit: 30
# The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items)
defaultLowerLimit: 1900
# List of all the active Browse-By types
# Adding a type will activate their Browse-By page and add them to the global navigation menu,
# as well as community and collection pages
# Allowed fields and their purpose:
# id: The browse id to use for fetching info from the rest api
# type: The type of Browse-By page to display
# metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date')
types:
- id: title
type: title
- id: dateissued
type: date
metadataField: dc.date.issued
- id: author
type: metadata
- id: subject
type: metadata
# Item Page Config
item:
edit:
undoTimeout: 10000 # 10 seconds
# Collection Page Config
collection:
edit:
undoTimeout: 10000 # 10 seconds
# Theme Config
themes:
# Add additional themes here. In the case where multiple themes match a route, the first one
# in this list will get priority. It is advisable to always have a theme that matches
# every route as the last one
#
# # A theme with a handle property will match the community, collection or item with the given
# # handle, and all collections and/or items within it
# - name: 'custom',
# handle: '10673/1233'
#
# # A theme with a regex property will match the route using a regular expression. If it
# # matches the route for a community or collection it will also apply to all collections
# # and/or items within it
# - name: 'custom',
# regex: 'collections\/e8043bc2.*'
#
# # A theme with a uuid property will match the community, collection or item with the given
# # ID, and all collections and/or items within it
# - name: 'custom',
# uuid: '0958c910-2037-42a9-81c7-dca80e3892b4'
#
# # The extends property specifies an ancestor theme (by name). Whenever a themed component is not found
# # in the current theme, its ancestor theme(s) will be checked recursively before falling back to default.
# - name: 'custom-A',
# extends: 'custom-B',
# # Any of the matching properties above can be used
# handle: '10673/34'
#
# - name: 'custom-B',
# extends: 'custom',
# handle: '10673/12'
#
# # A theme with only a name will match every route
# name: 'custom'
#
# # This theme will use the default bootstrap styling for DSpace components
# - name: BASE_THEME_NAME
#
- name: dspace
# Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with 'image' or 'video').
# For images, this enables a gallery viewer where you can zoom or page through images.
# For videos, this enables embedded video streaming
mediaViewer:
image: false
video: false

5
config/config.yml Normal file
View File

@@ -0,0 +1,5 @@
rest:
ssl: true
host: api7.dspace.org
port: 443
nameSpace: /server

View File

@@ -1,15 +1,15 @@
# Configuration # Configuration
Default configuration file is located in `config/` folder. All configuration options should be listed in the default configuration file `src/config/default-app-config.ts`. Please do not change this file directly! To change the default configuration values, create local files that override the parameters you need to change. You can use `appConfig.json` as a starting point. Default configuration file is located at `config/config.yml`. All configuration options should be listed in the default typescript file `src/config/default-app-config.ts`. Please do not change this file directly! To override the default configuration values, create local files that override the parameters you need to change. You can use `config.example.yml` as a starting point.
- Create a new `appConfig.(dev or development).json` file in `config/` for `development` environment; - Create a new `config.(dev or development).yml` file in `config/` for `development` environment;
- Create a new `appConfig.(prod or production).json` file in `config/` for `production` environment; - Create a new `config.(prod or production).yml` file in `config/` for `production` environment;
Alternatively, create a desired app config file at an external location and set the path as environment variable `APP_CONFIG_PATH`. Alternatively, create a desired app config file at an external location and set the path as environment variable `APP_CONFIG_PATH`.
e.g. e.g.
``` ```
APP_CONFIG_PATH=/usr/local/dspace/config/appConfig.json APP_CONFIG_PATH=/usr/local/dspace/config/config.yml
``` ```
Configuration options can be overridden by setting environment variables. Configuration options can be overridden by setting environment variables.

View File

@@ -22,7 +22,7 @@
"lint": "ng lint", "lint": "ng lint",
"lint-fix": "ng lint --fix=true", "lint-fix": "ng lint --fix=true",
"e2e": "ng e2e", "e2e": "ng e2e",
"clean:dev:config": "rimraf src/assets/appConfig.json", "clean:dev:config": "rimraf src/assets/config.json",
"clean:coverage": "rimraf coverage", "clean:coverage": "rimraf coverage",
"clean:dist": "rimraf dist", "clean:dist": "rimraf dist",
"clean:doc": "rimraf doc", "clean:doc": "rimraf doc",
@@ -35,7 +35,8 @@
"build:mirador": "webpack --config webpack/webpack.mirador.config.ts", "build:mirador": "webpack --config webpack/webpack.mirador.config.ts",
"merge-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/merge-i18n-files.ts", "merge-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/merge-i18n-files.ts",
"cypress:open": "cypress open", "cypress:open": "cypress open",
"cypress:run": "cypress run" "cypress:run": "cypress run",
"env:yaml": "ts-node --project ./tsconfig.ts-node.json scripts/env-to-yaml.ts"
}, },
"browser": { "browser": {
"fs": false, "fs": false,
@@ -90,6 +91,7 @@
"http-proxy-middleware": "^1.0.5", "http-proxy-middleware": "^1.0.5",
"https": "1.0.0", "https": "1.0.0",
"js-cookie": "2.2.1", "js-cookie": "2.2.1",
"js-yaml": "^4.1.0",
"json5": "^2.1.3", "json5": "^2.1.3",
"jsonschema": "1.4.0", "jsonschema": "1.4.0",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",

32
scripts/env-to-yaml.ts Normal file
View File

@@ -0,0 +1,32 @@
import * as fs from 'fs';
import * as yaml from 'js-yaml';
import { join } from 'path';
const args = process.argv.slice(2);
if (args[0] === undefined) {
console.log(`Usage:\n\tyarn env:yaml [relative path to environment.ts file] (optional relative path to write yaml file)\n`);
process.exit(0);
}
const envFullPath = join(process.cwd(), args[0]);
if (!fs.existsSync(envFullPath)) {
console.error(`Error:\n${envFullPath} does not exist\n`);
process.exit(1);
}
try {
const env = require(envFullPath);
const config = yaml.dump(env);
if (args[1]) {
const ymlFullPath = join(process.cwd(), args[1]);
fs.writeFileSync(ymlFullPath, config);
} else {
console.log(config);
}
} catch (e) {
console.error(e);
}

View File

@@ -58,7 +58,7 @@ const indexHtml = existsSync(join(DIST_FOLDER, 'index.html')) ? 'index.html' : '
const cookieParser = require('cookie-parser'); const cookieParser = require('cookie-parser');
const appConfig: AppConfig = buildAppConfig(join(DIST_FOLDER, 'assets/appConfig.json')); const appConfig: AppConfig = buildAppConfig(join(DIST_FOLDER, 'assets/config.json'));
// extend environment with app config for server // extend environment with app config for server
extendEnvironmentWithAppConfig(environment, appConfig); extendEnvironmentWithAppConfig(environment, appConfig);

View File

@@ -109,8 +109,6 @@ export class AppComponent implements OnInit, AfterViewInit {
@Optional() private googleAnalyticsService: GoogleAnalyticsService, @Optional() private googleAnalyticsService: GoogleAnalyticsService,
) { ) {
console.log(this.appConfig);
if (!isEqual(environment, this.appConfig)) { if (!isEqual(environment, this.appConfig)) {
throw new Error('environment does not match app config!'); throw new Error('environment does not match app config!');
} }

View File

@@ -1 +1 @@
appConfig.json config.json

View File

@@ -1,5 +1,6 @@
import * as colors from 'colors'; import * as colors from 'colors';
import * as fs from 'fs'; import * as fs from 'fs';
import * as yaml from 'js-yaml';
import { join } from 'path'; import { join } from 'path';
import { AppConfig } from './app-config.interface'; import { AppConfig } from './app-config.interface';
@@ -44,8 +45,12 @@ const getEnvironment = (): Environment => {
}; };
const getLocalConfigPath = (env: Environment) => { const getLocalConfigPath = (env: Environment) => {
// default to config/appConfig.json // default to config/config.yml
let localConfigPath = join(CONFIG_PATH, 'appConfig.json'); let localConfigPath = join(CONFIG_PATH, 'config.yml');
if (!fs.existsSync(localConfigPath)) {
localConfigPath = join(CONFIG_PATH, 'config.yaml');
}
// determine app config filename variations // determine app config filename variations
let envVariations; let envVariations;
@@ -63,9 +68,16 @@ const getLocalConfigPath = (env: Environment) => {
// check if any environment variations of app config exist // check if any environment variations of app config exist
for (const envVariation of envVariations) { for (const envVariation of envVariations) {
const envLocalConfigPath = join(CONFIG_PATH, `appConfig.${envVariation}.json`); let envLocalConfigPath = join(CONFIG_PATH, `config.${envVariation}.yml`);
if (fs.existsSync(envLocalConfigPath)) { if (fs.existsSync(envLocalConfigPath)) {
localConfigPath = envLocalConfigPath; localConfigPath = envLocalConfigPath;
break;
} else {
envLocalConfigPath = join(CONFIG_PATH, `config.${envVariation}.yaml`);
if (fs.existsSync(envLocalConfigPath)) {
localConfigPath = envLocalConfigPath;
break;
}
} }
} }
@@ -76,7 +88,7 @@ const overrideWithConfig = (config: Config, pathToConfig: string) => {
try { try {
console.log(`Overriding app config with ${pathToConfig}`); console.log(`Overriding app config with ${pathToConfig}`);
const externalConfig = fs.readFileSync(pathToConfig, 'utf8'); const externalConfig = fs.readFileSync(pathToConfig, 'utf8');
mergeConfig(config, JSON.parse(externalConfig)); mergeConfig(config, yaml.load(externalConfig));
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} }
@@ -190,7 +202,7 @@ export const buildAppConfig = (destConfigPath?: string): AppConfig => {
if (isNotEmpty(destConfigPath)) { if (isNotEmpty(destConfigPath)) {
fs.writeFileSync(destConfigPath, JSON.stringify(appConfig, null, 2)); fs.writeFileSync(destConfigPath, JSON.stringify(appConfig, null, 2));
console.log(`Angular ${colors.bold('appConfig.json')} file generated correctly at ${colors.bold(destConfigPath)} \n`); console.log(`Angular ${colors.bold('config.json')} file generated correctly at ${colors.bold(destConfigPath)} \n`);
} }
return appConfig; return appConfig;

View File

@@ -20,7 +20,17 @@ import { UniversalConfig } from './universal-config.interface';
export class DefaultAppConfig implements AppConfig { export class DefaultAppConfig implements AppConfig {
production = false; production = false;
// Angular Universal server settings. // Angular Universal settings
universal: UniversalConfig = {
preboot: true,
async: true,
time: false
};
// NOTE: will log all redux actions and transfers in console
debug = false;
// Angular Universal server settings
// NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg. // NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg.
ui: UIServerConfig = { ui: UIServerConfig = {
ssl: false, ssl: false,
@@ -36,7 +46,7 @@ export class DefaultAppConfig implements AppConfig {
} }
}; };
// The REST API server settings. // The REST API server settings
// NOTE: these must be 'synced' with the 'dspace.server.url' setting in your backend's local.cfg. // NOTE: these must be 'synced' with the 'dspace.server.url' setting in your backend's local.cfg.
rest: ServerConfig = { rest: ServerConfig = {
ssl: false, ssl: false,
@@ -164,16 +174,6 @@ export class DefaultAppConfig implements AppConfig {
} }
}; };
// Angular Universal settings
universal: UniversalConfig = {
preboot: true,
async: true,
time: false
};
// NOTE: will log all redux actions and transfers in console
debug = false;
// Default Language in which the UI will be rendered if the user's browser language is not an active language // Default Language in which the UI will be rendered if the user's browser language is not an active language
defaultLanguage = 'en'; defaultLanguage = 'en';

View File

@@ -34,7 +34,7 @@ const main = () => {
return bootstrap(); return bootstrap();
} else { } else {
return fetch('assets/appConfig.json') return fetch('assets/config.json')
.then((response) => response.json()) .then((response) => response.json())
.then((appConfig: AppConfig) => { .then((appConfig: AppConfig) => {

View File

@@ -10,7 +10,7 @@ module.exports = Object.assign({}, commonExports, {
}, },
devServer: { devServer: {
before(app, server) { before(app, server) {
buildAppConfig(join(process.cwd(), 'src/assets/appConfig.json')); buildAppConfig(join(process.cwd(), 'src/assets/config.json'));
} }
} }
}); });

View File

@@ -2753,6 +2753,11 @@ argparse@^1.0.7:
dependencies: dependencies:
sprintf-js "~1.0.2" sprintf-js "~1.0.2"
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
aria-query@^3.0.0: aria-query@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc"
@@ -8012,6 +8017,13 @@ js-yaml@^3.13.1:
argparse "^1.0.7" argparse "^1.0.7"
esprima "^4.0.0" esprima "^4.0.0"
js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies:
argparse "^2.0.1"
jsbn@~0.1.0: jsbn@~0.1.0:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"