Commit 7ed0a2f9 authored by Alejandro De Maria Antolinos's avatar Alejandro De Maria Antolinos

Merge branch 'master' of https://gitlab.esrf.fr/icat/E-DataPortal

parents 4b3f90ac a65168ca
......@@ -10,5 +10,10 @@ EXTEND_ESLINT=true
# To override one of these variables in development, create a file called `.env.local`.
#####
# ICAT+ SERVER URL
# ICAT+ server URL
REACT_APP_ICATPLUS_URL=http://localhost:8000
# Authentication methods
REACT_APP_SSO_AUTH_ENABLED=true
REACT_APP_ANONYMOUS_AUTH_ENABLED=true
REACT_APP_DB_AUTH_ENABLED=true
# ICAT+ SERVER URL
# ICAT+ production server URL
REACT_APP_ICATPLUS_URL=https://icatplus.esrf.fr
# ICAT+ SERVER URL
# Use fake ICAT+ SERVER URL
REACT_APP_ICATPLUS_URL=https://icatplus.test
# Disable SSO authentication
REACT_APP_SSO_AUTH_ENABLED=false
# Configurations files
/src/config/icat/icat.js
# Local environment files
.env.local
.env.development.local
......
......@@ -11,7 +11,6 @@ Lint:
image: node:12.14.1
script:
- npm ci
- npm run configure
- npm run lint
Test:
......@@ -19,7 +18,6 @@ Test:
image: node:12.14.1
script:
- npm ci
- npm run configure
- npm run test
.docker-template:
......
......@@ -3,6 +3,7 @@
"editorconfig.editorconfig",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"firefox-devtools.vscode-firefox-debug"
"firefox-devtools.vscode-firefox-debug",
"mikestead.dotenv"
]
}
{
"version": "0.2.0",
"configurations": [
{
"type": "firefox",
"request": "launch",
"name": "Launch Firefox against locaholst",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}"
}
]
}
......@@ -15,27 +15,21 @@ Currently, datahub depends on:
# Menu
1. [Installation](#installation)
2. [Configuration](#configuration)
3. [Developments](#developments)
- [Installation](#installation)
- [Configuration](#configuration)
- [Development](#development)
# Installation
## Prerequisite:
1. Make sure the dependencies mentioned above are installed and properly configured.
## Installation procedure
1. Clone or download the project from [GitLab](https://gitlab.esrf.fr/icat/E-DataPortal)
2. Install the dependencies
1. Install the dependencies
```bash
npm install
```
3. [Configure the app](#configuration)
4. Start the app
1. Start the app
```bash
npm start
......@@ -45,96 +39,29 @@ Currently, datahub depends on:
Datahub configuration is spread among different files located in <kbd>src/config</kbd>. These files are:
- [icat.js](#icatjs) : configure access to the metadata catalogue.
- [icatPlus.js](#icatplusjs) : configure access to the ICAT+ server
- [techniques.js](#techniquesjs) : configure the known techniques
- [gui.config.js](#guiconfigjs) : configuration file for the logbook (to be moved to config.js)
- [config.js](#configjs) : UI general configuration of the GUI
- `icat.js` - configure access to metadata catalogue
- `icatPlus.js` - configure access to ICAT+ server
- `ids.js` - configure IDs server for downloading datasets
- [`techniques.js`](#techniquesjs) - configure known techniques
- [`ui.js`](#uijs) - general UI configuration
Some of the configuration options in these files are stored in [environment variables](#environment-variables).
Example file for [icat.js](src/config/icat/icat.example.js) is provided and needs to be copied in configuration file:
You can copy it manually:
```bash
cp src/config/icat/icat.example.js src/config/icat/icat.js
```
or run:
```
npm run-script configure
```
## Environment variables
Create React App loads environment variables from [environment files](https://create-react-app.dev/docs/adding-custom-environment-variables/#what-other-env-files-can-be-used) (<kbd>.env</kbd>, <kbd>.env.test</kbd>, etc.) according to the Node environment (`NODE_ENV`).
File <kbd>.env</kbd> is loaded in every environment with the lowest priority and is therefore used to declare default values and development fallbacks.
The following portal configuration variable is currently declared in <kbd>.env</kbd>:
The following portal configuration variables are declared in <kbd>.env</kbd>:
- `REACT_APP_ICATPLUS_URL` - the URL of the ICAT+ server (used in <kbd>src/config/icatPlus.js</kbd>)
- `REACT_APP_SSO_AUTH_ENABLED` - whether to allow users to sign-in to ICAT with SSO (used in <kbd>src/config/icat.js</kbd>)
- `REACT_APP_ANONYMOUS_AUTH_ENABLED` - whether to allow users to sign-in to ICAT as anonymous (used in <kbd>src/config/icat.js</kbd>)
- `REACT_APP_DB_AUTH_ENABLED` - whether to allow users to sign-in to ICAT with database credentials (used in <kbd>src/config/icat.js</kbd>)
In development, you can override this variable by creating a file called `.env.local`. This file is ignored from version control.
## icat.js
Edit the file **icat.js** with your favorite text editor and set the configuration to access the metadata catalogue following the indications below.
<!--START configurationICAT -->
```js
/**
* icat.example.js configuration file
*/
var ICAT = {
authentication: {
/** Single sign-on configuration */
sso: {
enabled: true,
/** ICAT authentication plugin for json web tokens */
plugin: 'esrf',
/** Configuration to be passed to keycloak.js (https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/javascript-adapter.adoc) */
configuration: {
realm: 'ESRF',
url: 'https://websso.esrf.fr/auth/',
clientId: 'icat',
},
},
/** Anonymous user's credentials*/
anonymous: {
enabled: true,
/** ICAT plugin used for authentication. */
plugin: 'db',
/** Username for the anonymous user */
username: 'reader',
/** Password for the anonymous user */
password: 'reader',
},
authenticators: [
{
plugin: 'esrf',
/** title will be displayed as the title of the Tab at the login Form */
title: 'ESRF',
/** If will be hidden in the login form */
hidden: false,
},
{
plugin: 'db',
title: 'Database',
hidden: false,
},
],
},
};
export default ICAT;
```
<!--END configurationICAT -->
In development, you can override any of these variables by creating a file called `.env.local`. This file is ignored from version control.
## techniques.js
## `techniques.js`
The file [techniques.js](src/config/techniques/techniques.js) complements short named techniques as stored in ICAT metadata catalogue. It maps a short name to a description and display settings.
......@@ -154,23 +81,12 @@ color: "#97E0FE"
},
```
## gui.config.js
Edit the file [gui.config.js](src/config/gui.config.js) with your favorite text editor and set the GUI configuration following the indications below.
<!--START configurationGUI -->
```js
```
<!--END configurationGUI -->
## ui.config.js
## `ui.js`
<!--START config -->
<!--START ui -->
```js
var UI = {
const UI = {
status: {
offline: {
enabled: false,
......@@ -178,20 +94,21 @@ var UI = {
},
},
menu: {
applicationTittle: 'Data Portal',
applicationTitle: 'Data Portal',
isSearchVisible: true,
isClosedDataVisible: true,
isOpenDataVisible: true,
isMySelectionVisible: true,
},
sampleTracking: {
//** Makes visible or invisible the menus and tabs related to sample tracking */
enabled: true,
},
loginForm: {
header: '',
/** Text displayed on top of the username sigin form. Someone can customize the text as: User office account, ESRF Account, Umbrella account, etc... */
usernameLabel: 'Username',
sso: {
title: 'Sign in with SSO',
buttonText: 'ESRF SSO',
},
ssoBtnLabel: 'Sign in with ESRF SSO',
},
investigationContainer: {
isDatasetListVisible: true,
......@@ -220,19 +137,22 @@ var UI = {
* When it is set to false, the event list is not refreshed but the amount of new events which arriived is shown.*/
AUTOREFRESH_EVENTLIST: true,
/* Time interval between requests checking whether new events arrived. (in milliseconds) */
AUTOREFRESH_DELAY: 20000,
AUTOREFRESH_DELAY: 60000,
},
dangerousGoodsHelp:
'Please ensure you reply correctly to this question after consultation with your lab safety referent and/or the transport company. This question is for ESRF internal use only - you must correctly declare your parcel in the official transport documents',
footer: {
text: 'European Synchrotron Radiation Facility',
},
samplePageTemplateURL: 'https://smis.esrf.fr/misapps/SMISWebClient/protected/samplesheet/view.do?pk=',
};
export default UI;
```
<!--END config -->
<!--END ui -->
# Developments
# Development
- [Tests](#tests)
- [Code quality](#code-quality)
......
......@@ -14176,6 +14176,155 @@
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
"integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
},
"replace": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/replace/-/replace-1.2.0.tgz",
"integrity": "sha512-e3AP5GkRk+N/Qm1MUBaMhEHr4X3sHNI44a8m4ww6/qShJphTsStxSezbYtFNTFGCXZtWrwz4McVvCEwBv+ebAw==",
"dev": true,
"requires": {
"chalk": "2.4.2",
"minimatch": "3.0.4",
"yargs": "^15.3.1"
},
"dependencies": {
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"dev": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"dev": true,
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
}
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"requires": {
"p-locate": "^4.1.0"
}
},
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"requires": {
"p-limit": "^2.2.0"
}
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true
},
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
"dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
},
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"dev": true,
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
}
},
"yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"dev": true,
"requires": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
}
},
"yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
},
"request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
......
......@@ -8,15 +8,12 @@
"npm": "6.x"
},
"scripts": {
"build": "npm run configure && react-scripts build",
"configure": "echo 'Copying config/icat/icat.example.js' && cp src/config/icat/icat.example.js src/config/icat/icat.js",
"build": "react-scripts build",
"start": "react-scripts start",
"test": "react-scripts test --env=jsdom-fourteen",
"doc": "npm run Readme:ConfigDoc:ICAT && npm run Readme:ConfigDoc:ICAT:ReplaceCodeSection && npm run Readme:ConfigDoc:ICATPlus && npm run Readme:ConfigDoc:ICATPlus:ReplaceCodeSection && npm run Readme:ConfigDoc:Config && npm run Readme:ConfigDoc:Config:ReplaceCodeSection",
"Readme:ConfigDoc:ICAT": "node ./node_modules/jscat/bin/jscat src/config/icat/icat.example.js | node ./node_modules/injectmd/bin/cli.js -t configurationICAT -i Readme.md",
"Readme:ConfigDoc:ICAT:ReplaceCodeSection": "./node_modules/replace/bin/replace.js '<!--START configurationICAT -->' '<!--START configurationICAT -->\n```js' Readme.md && ./node_modules/replace/bin/replace.js '<!--END configurationICAT -->' '```\n<!--END configurationICAT -->' Readme.md",
"Readme:ConfigDoc:Config": "node ./node_modules/jscat/bin/jscat src/config/ui/config.js | node ./node_modules/injectmd/bin/cli.js -t config -i Readme.md",
"Readme:ConfigDoc:Config:ReplaceCodeSection": "./node_modules/replace/bin/replace.js '<!--START config -->' '<!--START config -->\n```js' Readme.md && ./node_modules/replace/bin/replace.js '<!--END config -->' '```\n<!--END config -->' Readme.md",
"doc": "run-s doc:*",
"doc:inject": "node ./node_modules/jscat/bin/jscat src/config/ui.js | node ./node_modules/injectmd/bin/cli.js -t config -i README.md",
"doc:replace": "./node_modules/replace/bin/replace.js '<!--START ui -->\n```js' '<!--START ui -->\n```js' README.md && ./node_modules/replace/bin/replace.js '```\n<!--END ui -->' '```\n<!--END ui -->' README.md",
"complexity-report": "./node_modules/.bin/es6-plato -r -d ./public/report src",
"lint": "run-p lint:*",
"lint:eslint": "eslint \"**/*.{js,jsx}\" --max-warnings=0",
......@@ -85,6 +82,7 @@
"prettier": "2.0.5",
"prop-types": "^15.7.2",
"react-doc-generator": "^1.2.5",
"replace": "^1.2.0",
"typescript": "^3.9.7"
},
"jest": {
......
......@@ -2,7 +2,7 @@ import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Route, Switch, Redirect, useLocation } from 'react-router-dom';
import Footer from './components/Footer';
import UI from './config/ui/config';
import UI from './config/ui';
import AddressesPage from './containers/AddressesPage';
import LoginPage from './containers/LoginPage';
import MyDataPage from './containers/MyDataPage';
......@@ -25,12 +25,12 @@ import MintSelectionPage from './containers/Selection/MintSelectionPage';
import BeamlineDataPage from './containers/BeamlineDataPage';
import { getRemainingSessionTime } from './helpers/auth';
import { doLogOut, doSilentRefreshFromSSO } from './actions/login';
import keycloak from './config/sso/keycloak';
import keycloak from './keycloak';
import Menu from './components/Menu/Menu';
import { fetchAllInvestigations } from './actions/investigations';
import { fetchDataCollections } from './actions/datacollections';
import { useQuery } from './helpers/hooks';
import PageNotFound from './containers/PageNotFound';
import LoadingBoundary from './components/LoadingBoundary';
function App() {
const user = useSelector((state) => state.user);
......@@ -46,7 +46,6 @@ function App() {
useEffect(() => {
if (user.sessionId) {
dispatch(fetchAllInvestigations(user.sessionId));
dispatch(fetchDataCollections(user.sessionId));
}
}, [dispatch, user.sessionId]);
......@@ -134,42 +133,40 @@ function App() {
<Route exact path="/addresses" component={AddressesPage} />
)}
<Route
exact
path="/investigation/:investigationId/datasets"
render={({ match }) =>
UI.investigationContainer.isDatasetListVisible ? (
<DatasetsPage />
) : (
<Redirect
to={`/investigation/${match.params.investigationId}/events`}
/>
)
}
/>
<Route
exact
path="/investigation/:investigationId/events/tagManager"
component={EventTagPage}
/>
<Route
exact
path="/investigation/:investigationId/events"
component={EventsPage}
/>
{isSampleTrackingEnabled && (
<Route
exact
path="/investigation/:investigationId/shipping"
component={ShippingPage}
/>
)}
<Route
exact
path="/investigation/:investigationId/parcel/:parcelId"
component={ParcelPage}
/>
<Route path="/investigation">
<LoadingBoundary message="Loading investigation..." spacedOut>
<Route
path="/investigation/:investigationId/datasets"
render={({ match }) =>
UI.investigationContainer.isDatasetListVisible ? (
<DatasetsPage />
) : (
<Redirect
to={`/investigation/${match.params.investigationId}/events`}
/>
)
}
/>
<Route path="/investigation/:investigationId/events/tagManager">
<EventTagPage />
</Route>
<Route path="/investigation/:investigationId/events">
<EventsPage />
</Route>
{isSampleTrackingEnabled && (
<Route path="/investigation/:investigationId/shipping">
<ShippingPage />
</Route>
)}
<Route path="/investigation/:investigationId/parcel/:parcelId">
<ParcelPage />
</Route>
</LoadingBoundary>
</Route>
<Route exact path="/selection" component={SelectionPage} />
<Route exact path="/selection/mint" component={MintSelectionPage} />
......
import axios from 'axios';
import { getDataCollections } from '../api/icat/icatPlus';
import { getDataCollections } from '../api/icat-plus/catalogue';
import { FETCH_DATACOLLECTIONS } from '../constants/actionTypes';
export function fetchDataCollections(sessionId) {
return {
type: FETCH_DATACOLLECTIONS,
payload: axios.get(getDataCollections(sessionId)),
payload: getDataCollections(sessionId),
};
}
import axios from 'axios';
import {
getDatasetByDOI,
getDatasetsByInvestigationId,
} from '../api/icat/icatPlus';
import { getDatasetsByInvestigationId } from '../api/icat-plus/catalogue';
import {
FETCH_DATASETS_BY_DOI,
FETCH_DATASETS_BY_INVESTIGATION,
} from '../constants/actionTypes';
import { getDatasetByDOI } from '../api/icat-plus/doi';
export function fetchDatasetsByDOI(sessionId, doi) {
return function (dispatch) {
......
import axios from 'axios';
import { getIn