diff --git a/.gitignore b/.gitignore index b5f8abf7bbb1596d85af3b087183575a4cdfb403..408e05823a09530dab2c0824536641bb1b6acae7 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ openbis/source/core-plugins **/.pydevproject **/.shredder ivy-repository +**/.idea diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/DefaultSessionManager.java b/authentication/source/java/ch/systemsx/cisd/authentication/DefaultSessionManager.java index 41f9aeaaea898d05cce102206d3a6a461d1a526b..778350bcd3b519591fe40bb8235560537cb6b812 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/DefaultSessionManager.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/DefaultSessionManager.java @@ -19,11 +19,15 @@ package ch.systemsx.cisd.authentication; import java.io.File; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.Collection; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.Set; import javax.annotation.Resource; @@ -32,6 +36,7 @@ import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DurationFormatUtils; import org.apache.log4j.Logger; +import ch.systemsx.cisd.common.collection.SimpleComparator; import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; import ch.systemsx.cisd.common.exceptions.InvalidSessionException; import ch.systemsx.cisd.common.exceptions.UserFailureException; @@ -55,6 +60,11 @@ import ch.systemsx.cisd.common.spring.ExposablePropertyPlaceholderConfigurer; */ public class DefaultSessionManager<T extends BasicSession> implements ISessionManager<T> { + private enum SessionClosingReason + { + LOGOUT, SESSION_EXPIRATION, SESSIONS_LIMIT + } + public static final File NO_LOGIN_FILE = new File("./etc/nologin.html"); private static final String LOGOUT_PREFIX = "LOGOUT: "; @@ -143,6 +153,8 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa private final boolean tryEmailAsUserName; + private final Set<ISessionActionListener> listeners = new LinkedHashSet<>(); + public DefaultSessionManager(final ISessionFactory<T> sessionFactory, final ILogMessagePrefixGenerator<T> prefixGenerator, final IAuthenticationService authenticationService, @@ -212,11 +224,11 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa int maxNumberOfSessions = getMaxNumberOfSessionsFor(user); if (maxNumberOfSessions > 0) { - int numberOfOpenSessions = getNumberOfOpenSessionsFor(user); - if (numberOfOpenSessions >= maxNumberOfSessions) + List<FullSession<T>> openSessions = getOpenSessionsFor(user); + while (openSessions.size() >= maxNumberOfSessions) { - throw new UserFailureException(numberOfOpenSessions + " open session(s) but only " - + maxNumberOfSessions + " session(s) are allowed for user " + user + "."); + FullSession<T> session = openSessions.remove(0); + closeSession(session.getSession(), SessionClosingReason.SESSIONS_LIMIT); } } @@ -232,18 +244,25 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa } } - private int getNumberOfOpenSessionsFor(String user) + private List<FullSession<T>> getOpenSessionsFor(String user) { - int count = 0; - Collection<FullSession<T>> values = sessions.values(); - for (FullSession<T> session : values) + List<FullSession<T>> userSessions = new ArrayList<>(); + for (FullSession<T> session : sessions.values()) { if (session.getSession().getUserName().equals(user)) { - count++; + userSessions.add(session); } } - return count; + Collections.sort(userSessions, new SimpleComparator<FullSession<T>, Long>() + { + @Override + public Long evaluate(FullSession<T> session) + { + return session.lastActiveTime; + } + }); + return userSessions; } protected int getMaxNumberOfSessionsFor(String user) @@ -453,6 +472,18 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa } } + private void logLimitedNumberOfSessions(final T session) + { + final String prefix = prefixGenerator.createPrefix(session); + authenticationLog.info(prefix + ": session closed because limit of open session has been reached."); + if (operationLog.isInfoEnabled()) + { + final String user = session.getUserName(); + operationLog.info(LOGOUT_PREFIX + "Session '" + session.getSessionToken() + + "' of user '" + user + "' has been closed because limit of open session has been reached."); + } + } + @Override public boolean isAWellFormedSessionToken(String sessionTokenOrNull) { @@ -540,7 +571,7 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa } if (checkAndTouch && doSessionExpiration(session)) { - closeSession(session.getSession(), false); + closeSession(session.getSession(), SessionClosingReason.SESSION_EXPIRATION); } if (checkAndTouch && isSessionUnavailable(session)) { @@ -615,7 +646,7 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa synchronized (sessions) { final T session = getSession(sessionToken, false); - closeSession(session, true); + closeSession(session, SessionClosingReason.LOGOUT); } } @@ -623,22 +654,32 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa public void expireSession(String sessionToken) throws InvalidSessionException { final T session = getSession(sessionToken, false); - closeSession(session, false); + closeSession(session, SessionClosingReason.SESSION_EXPIRATION); } - private void closeSession(final T session, final boolean regularLogout) + private void closeSession(final T session, SessionClosingReason reason) throws InvalidSessionException { synchronized (sessions) { session.cleanup(); - sessions.remove(session.getSessionToken()); - if (regularLogout) + String sessionToken = session.getSessionToken(); + sessions.remove(sessionToken); + switch (reason) { - logLogout(session); - } else + case LOGOUT: + logLogout(session); + break; + case SESSION_EXPIRATION: + logSessionExpired(session); + break; + case SESSIONS_LIMIT: + logLimitedNumberOfSessions(session); + break; + } + for (ISessionActionListener listener : listeners) { - logSessionExpired(session); + listener.sessionClosed(sessionToken); } } } @@ -659,4 +700,11 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa } return p; } + + @Override + public void addListener(ISessionActionListener listener) + { + listeners.add(listener); + } + } diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/ISessionActionListener.java b/authentication/source/java/ch/systemsx/cisd/authentication/ISessionActionListener.java new file mode 100644 index 0000000000000000000000000000000000000000..9c8803b0a94e6c5c93b56d4346bbd2dd260decdb --- /dev/null +++ b/authentication/source/java/ch/systemsx/cisd/authentication/ISessionActionListener.java @@ -0,0 +1,26 @@ +/* + * Copyright 2019 ETH Zuerich, SIS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.authentication; + +/** + * @author Franz-Josef Elmer + * + */ +public interface ISessionActionListener +{ + public void sessionClosed(String sessionToken); +} diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/ISessionManager.java b/authentication/source/java/ch/systemsx/cisd/authentication/ISessionManager.java index e08049acc0321e52542b78df54f3fc4da79e8c16..30aa716f670e58a0e70abc6dbacbf9a504ac7274 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/ISessionManager.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/ISessionManager.java @@ -75,5 +75,7 @@ public interface ISessionManager<T extends BasicSession> extends IRemoteHostProv * </p> */ public T tryGetSession(final String sessionToken); + + public void addListener(ISessionActionListener listener); } \ No newline at end of file diff --git a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/DefaultSessionManagerTest.java b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/DefaultSessionManagerTest.java index 78ed2c035dfb0d7a263d661c437f97504bc80f4a..8704bf905bc52b5709c2d374c306bad517d9c637 100644 --- a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/DefaultSessionManagerTest.java +++ b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/DefaultSessionManagerTest.java @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Level; @@ -40,6 +41,7 @@ import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; import ch.systemsx.cisd.common.exceptions.UserFailureException; import ch.systemsx.cisd.common.logging.BufferedAppender; import ch.systemsx.cisd.common.server.IRemoteHostProvider; +import ch.systemsx.cisd.common.test.RecordingMatcher; import ch.systemsx.cisd.common.test.RetryTen; import ch.systemsx.cisd.common.test.TestReportCleaner; @@ -78,6 +80,8 @@ public class DefaultSessionManagerTest private Sequence sequence; + private ISessionActionListener sessionActionListener; + private void assertExceptionMessageForInvalidSessionToken(final UserFailureException ex) { final String message = ex.getMessage(); @@ -94,6 +98,7 @@ public class DefaultSessionManagerTest authenticationService = context.mock(IAuthenticationService.class); remoteHostProvider = context.mock(IRemoteHostProvider.class); principalProvider = context.mock(IPrincipalProvider.class); + sessionActionListener = context.mock(ISessionActionListener.class); sequence = context.sequence("sequence"); context.checking(new Expectations() { @@ -232,6 +237,7 @@ public class DefaultSessionManagerTest public void testLimitedNumberOfSession() { final String user = "bla"; + RecordingMatcher<BasicSession> recordingSessionMatcher = new RecordingMatcher<BasicSession>(); context.checking(new Expectations() { { @@ -243,7 +249,7 @@ public class DefaultSessionManagerTest exactly(3).of(authenticationService).tryGetAndAuthenticateUser(user, "blub"); will(returnValue(principal)); - for (int i = 0; i < 2; i++) + for (int i = 0; i < 3; i++) { one(sessionFactory).create(with(any(String.class)), with(equal(user)), with(equal(principal)), with(equal(REMOTE_HOST)), @@ -253,26 +259,24 @@ public class DefaultSessionManagerTest will(returnValue(session)); inSequence(sequence); - one(prefixGenerator).createPrefix(session); + one(prefixGenerator).createPrefix(with(recordingSessionMatcher)); will(returnValue("[USER:'" + user + "', HOST:'remote-host']")); } - - one(prefixGenerator).createPrefix("bla", REMOTE_HOST); - will(returnValue("[USER:'bla', HOST:'remote-host']")); + one(prefixGenerator).createPrefix(with(recordingSessionMatcher)); + will(returnValue("[USER:'" + user + "', HOST:'remote-host']")); + + one(sessionActionListener).sessionClosed("bla-1"); } }); sessionManager = createSessionManager(SESSION_EXPIRATION_PERIOD_MINUTES, Collections.singletonMap("bla", 2)); + sessionManager.addListener(sessionActionListener); assertEquals("bla-1", sessionManager.tryToOpenSession("bla", "blub")); assertEquals("bla-2", sessionManager.tryToOpenSession("bla", "blub")); - try - { - assertEquals("bla-3", sessionManager.tryToOpenSession("bla", "blub")); - fail("UserFailureException expected"); - } catch (UserFailureException e) - { - assertEquals("2 open session(s) but only 2 session(s) are allowed for user bla.", e.getMessage()); - } + assertEquals("bla-3", sessionManager.tryToOpenSession("bla", "blub")); + assertEquals("[bla-1, bla-2, bla-1, bla-3]", + recordingSessionMatcher.getRecordedObjects().stream().map(s -> s.getSessionToken()) + .collect(Collectors.toList()).toString()); context.assertIsSatisfied(); } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/AbstractClientService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/AbstractClientService.java index c1d3ce70d524a95cd0c556d0eeaccf5a62273470..0976d7a0f467a11a7718dcacdf30e4fb5776250d 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/AbstractClientService.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/AbstractClientService.java @@ -36,6 +36,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import ch.rinn.restrictions.Private; +import ch.systemsx.cisd.authentication.ISessionActionListener; import ch.systemsx.cisd.common.exceptions.UserFailureException; import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogFactory; @@ -73,8 +74,10 @@ import ch.systemsx.cisd.openbis.generic.client.web.server.translator.ResultSetTr import ch.systemsx.cisd.openbis.generic.client.web.server.translator.UserFailureExceptionTranslator; import ch.systemsx.cisd.openbis.generic.client.web.server.util.TableModelUtils; import ch.systemsx.cisd.openbis.generic.client.web.server.util.XMLPropertyTransformer; +import ch.systemsx.cisd.openbis.generic.server.ComponentNames; import ch.systemsx.cisd.openbis.generic.server.SessionConstants; import ch.systemsx.cisd.openbis.generic.shared.Constants; +import ch.systemsx.cisd.openbis.generic.shared.IOpenBisSessionManager; import ch.systemsx.cisd.openbis.generic.shared.IServer; import ch.systemsx.cisd.openbis.generic.shared.WebClientConfigurationProvider; import ch.systemsx.cisd.openbis.generic.shared.basic.IEntityInformationHolder; @@ -116,6 +119,29 @@ public abstract class AbstractClientService implements IClientService, @Resource(name = ExposablePropertyPlaceholderConfigurer.PROPERTY_CONFIGURER_BEAN_NAME) private ExposablePropertyPlaceholderConfigurer configurer; + @Resource(name = ComponentNames.SESSION_MANAGER) + private IOpenBisSessionManager sessionManager; + + private final Map<String, HttpSession> httpSessionsBySessionToken = new HashMap<>(); + + private ISessionActionListener sessionActionListener = new ISessionActionListener() + { + @Override + public void sessionClosed(String sessionToken) + { + synchronized (httpSessionsBySessionToken) + { + HttpSession httpSession = httpSessionsBySessionToken.remove(sessionToken); + logout(null, true, httpSession, sessionToken); + if (httpSession != null) + { + operationLog.info("Session " + sessionToken + " closed. " + + "httpSessionsBySessionToken.size() = " + httpSessionsBySessionToken.size()); + } + } + } + }; + @Autowired private TableDataCache<String, Object> tableDataCache; @@ -653,6 +679,12 @@ public abstract class AbstractClientService implements IClientService, return null; } final HttpSession httpSession = createHttpSession(); + synchronized (httpSessionsBySessionToken) + { + httpSessionsBySessionToken.put(session.getSessionToken(), httpSession); + operationLog.info("httpSessionsBySessionToken.size() = " + httpSessionsBySessionToken.size()); + } + // Expiration time of httpSession is 10 seconds less than of session final int sessionExpirationTimeInMillis = session.getSessionExpirationTime(); final int sessionExpirationTimeInSeconds = sessionExpirationTimeInMillis / 1000; @@ -772,26 +804,31 @@ public abstract class AbstractClientService implements IClientService, try { final HttpSession httpSession = getHttpSession(); - if (httpSession != null) + String sessionToken = getSessionToken(); + logout(displaySettings, simpleViewMode, httpSession, sessionToken); + } catch (Exception e) + { + operationLog.info("logout exception: " + e); + } + } + + private void logout(DisplaySettings displaySettings, boolean simpleViewMode, final HttpSession httpSession, String sessionToken) + { + if (httpSession != null) + { + httpSession.removeAttribute(SessionConstants.OPENBIS_SESSION_TOKEN_ATTRIBUTE_KEY); + httpSession.removeAttribute(SessionConstants.OPENBIS_SERVER_ATTRIBUTE_KEY); + httpSession.removeAttribute(SessionConstants.OPENBIS_RESULT_SET_MANAGER); + httpSession.removeAttribute(SessionConstants.OPENBIS_EXPORT_MANAGER); + httpSession.invalidate(); + IServer server = getServer(); + if (simpleViewMode == false) { - String sessionToken = getSessionToken(); - httpSession.removeAttribute(SessionConstants.OPENBIS_SESSION_TOKEN_ATTRIBUTE_KEY); - httpSession.removeAttribute(SessionConstants.OPENBIS_SERVER_ATTRIBUTE_KEY); - httpSession.removeAttribute(SessionConstants.OPENBIS_RESULT_SET_MANAGER); - httpSession.removeAttribute(SessionConstants.OPENBIS_EXPORT_MANAGER); - httpSession.invalidate(); - IServer server = getServer(); - if (simpleViewMode == false) - { - // only save settings for "normal" view - int maxEntityVisits = getWebClientConfiguration().getMaxEntityVisits(); - server.saveDisplaySettings(sessionToken, displaySettings, maxEntityVisits); - } - server.logout(sessionToken); + // only save settings for "normal" view + int maxEntityVisits = getWebClientConfiguration().getMaxEntityVisits(); + server.saveDisplaySettings(sessionToken, displaySettings, maxEntityVisits); } - } catch (final UserFailureException e) - { - // Just ignore it because we are logging out anyway. + server.logout(sessionToken); } } @@ -832,5 +869,6 @@ public abstract class AbstractClientService implements IClientService, public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; + sessionManager.addListener(sessionActionListener); } } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java index 5952c837f6beff9e6fb95c9f45147a930ab5e996..3ce90cd554e611f71540f4cc2fe9b94742041c05 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java @@ -29,6 +29,7 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import javax.annotation.PostConstruct; import javax.annotation.Resource; import org.apache.commons.lang3.StringUtils; @@ -39,6 +40,7 @@ import org.springframework.transaction.annotation.Transactional; import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi; import ch.systemsx.cisd.authentication.DefaultSessionManager; import ch.systemsx.cisd.authentication.IPrincipalProvider; +import ch.systemsx.cisd.authentication.ISessionActionListener; import ch.systemsx.cisd.authentication.ISessionManager; import ch.systemsx.cisd.authentication.Principal; import ch.systemsx.cisd.common.action.IDelegatedActionWithResult; @@ -178,6 +180,15 @@ public abstract class AbstractServer<T> extends AbstractServiceWithLogger<T> imp private IApplicationServerApi v3Api; protected String CISDHelpdeskEmail; + + private ISessionActionListener sessionActionListener = new ISessionActionListener() + { + @Override + public void sessionClosed(String sessionToken) + { + logout(sessionToken); + } + }; protected AbstractServer() { @@ -204,6 +215,12 @@ public abstract class AbstractServer<T> extends AbstractServiceWithLogger<T> imp this.sampleTypeSlaveServerPlugin = sampleTypeSlaveServerPlugin; this.dataSetTypeSlaveServerPlugin = dataSetTypeSlaveServerPlugin; } + + @PostConstruct + public void registerAtSessionManager() + { + sessionManager.addListener(sessionActionListener); + } // For unit tests - in production Spring will inject this object. public void setDisplaySettingsProvider(DisplaySettingsProvider displaySettingsProvider) diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/jython/api/v1/impl/MasterDataRegistrationHelper.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/jython/api/v1/impl/MasterDataRegistrationHelper.java index 8d5e02c3dee9b580ff0fcfc4c79a2ec9a6a53c08..9d642c3f4903ae4f33d7df972a4b07a95c487e96 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/jython/api/v1/impl/MasterDataRegistrationHelper.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/jython/api/v1/impl/MasterDataRegistrationHelper.java @@ -17,6 +17,8 @@ package ch.systemsx.cisd.openbis.generic.server.jython.api.v1.impl; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -31,31 +33,24 @@ import ch.systemsx.cisd.common.logging.LogFactory; /** * Helper class to be used in initialize-master-data.py. - * + * * @author Franz-Josef Elmer */ -public class MasterDataRegistrationHelper -{ +public class MasterDataRegistrationHelper { private static Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, MasterDataRegistrationHelper.class); - + private File masterDataFolder; - public MasterDataRegistrationHelper(Collection<?> systemPaths) - { - for (Object systemPath : systemPaths) - { - if (systemPath != null) - { + public MasterDataRegistrationHelper(Collection<?> systemPaths) { + for (Object systemPath : systemPaths) { + if (systemPath != null) { String systemPathString = String.valueOf(systemPath); - if (systemPathString.contains("core-plugins")) - { + if (systemPathString.contains("core-plugins")) { masterDataFolder = new File(new File(systemPathString), "master-data"); - if (masterDataFolder.exists() == false) - { + if (masterDataFolder.exists() == false) { throw new IllegalArgumentException("Folder does not exist: " + masterDataFolder.getAbsolutePath()); } - if (masterDataFolder.isFile()) - { + if (masterDataFolder.isFile()) { throw new IllegalArgumentException("Is not a folder but a file: " + masterDataFolder.getAbsolutePath()); } operationLog.info("Master data folder: " + masterDataFolder.getAbsolutePath()); @@ -66,14 +61,11 @@ public class MasterDataRegistrationHelper throw new IllegalArgumentException("Does not contain path to the core plugin: " + systemPaths); } - public List<byte[]> listXlsByteArrays() - { + public List<byte[]> listXlsByteArrays() { List<byte[]> result = new ArrayList<>(); - for (File file : masterDataFolder.listFiles()) - { + for (File file : masterDataFolder.listFiles()) { String name = file.getName(); - if (name.endsWith(".xls") || name.endsWith(".xlsx")) - { + if (name.endsWith(".xls") || name.endsWith(".xlsx")) { operationLog.info("load master data " + file.getName()); result.add(FileUtilities.loadToByteArray(file)); } @@ -81,34 +73,39 @@ public class MasterDataRegistrationHelper return result; } - public Map<String, String> getAllScripts() - { + public List<byte[]> listCsvByteArrays() throws IOException { + List<byte[]> result = new ArrayList<>(); + for (File file : masterDataFolder.listFiles()) { + String name = file.getName(); + if (name.endsWith(".csv")) { + operationLog.info("load master data " + file.getName()); + result.add(Files.readAllBytes(file.toPath())); + } + } + return result; + } + + public Map<String, String> getAllScripts() { Map<String, String> result = new TreeMap<>(); File scriptsFolder = new File(masterDataFolder, "scripts"); - if (scriptsFolder.isDirectory()) - { + if (scriptsFolder.isDirectory()) { gatherScripts(result, scriptsFolder, scriptsFolder); } return result; } - private void gatherScripts(Map<String, String> scripts, File rootFolder, File file) - { - if (file.isFile()) - { + private void gatherScripts(Map<String, String> scripts, File rootFolder, File file) { + if (file.isFile()) { String scriptPath = FileUtilities.getRelativeFilePath(rootFolder, file); scripts.put(scriptPath, FileUtilities.loadToString(file)); operationLog.info("Script " + scriptPath + " loaded"); } - if (file.isDirectory()) - { + if (file.isDirectory()) { File[] files = file.listFiles(); - for (File child : files) - { + for (File child : files) { gatherScripts(scripts, rootFolder, child); } } } - } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js index 6fe6ce85a34339948828f0fc37526e39efc73ac1..672ac400beb4e5427f8154a07f98d97679c906e2 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js +++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js @@ -2045,6 +2045,17 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria }); } + this.isSessionActive = function() { + var thisFacade = this; + return thisFacade._private.ajaxRequest({ + url : openbisUrl, + data : { + "method" : "isSessionActive", + "params" : [ thisFacade._private.sessionToken ] + } + }); + } + this.getDataStoreFacade = function() { var dataStoreCodes = []; for (var i = 0; i < arguments.length; i++) { diff --git a/openbis_ng_ui/.eslintrc b/openbis_ng_ui/.eslintrc new file mode 100644 index 0000000000000000000000000000000000000000..5b842c72c51ed8b5a6b52cb77356abc8d5fe9c79 --- /dev/null +++ b/openbis_ng_ui/.eslintrc @@ -0,0 +1,41 @@ +{ + "root": true, + "parser": "babel-eslint", + "parserOptions": { + "ecmaVersion": 2017, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + } + }, + "env": { + "browser": true, + "jest": true, + "es6": true + }, + "extends": [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:prettier/recommended" + ], + "plugins": ["react", "prettier"], + "settings": { + "react": { + "createClass": "createReactClass", + "pragma": "React", + "version": "16.4.2" + }, + "propWrapperFunctions": ["forbidExtraProps"] + }, + "rules": { + "react/jsx-uses-react": "error", + "react/jsx-uses-vars": "error", + "react/prop-types": "off", + "prettier/prettier": "error", + + "eqeqeq": ["error", "always"], + + // override default options for rules from base configurations + "no-cond-assign": ["error", "always"] + } +} diff --git a/openbis_ng_ui/.eslintrc.js b/openbis_ng_ui/.eslintrc.js deleted file mode 100644 index 4a2639476b096e99ca1da0a2920b18f6bc15564f..0000000000000000000000000000000000000000 --- a/openbis_ng_ui/.eslintrc.js +++ /dev/null @@ -1,47 +0,0 @@ -module.exports = { - root: true, - parser: 'babel-eslint', - parserOptions: { - ecmaVersion: 2017, - sourceType: 'module', - ecmaFeatures: { - jsx: true - } - }, - env: { - browser: true, - jest: true, - es6: true - }, - extends: [ - 'eslint:recommended', - 'plugin:react/recommended' - ], - plugins: [ - 'react' - ], - settings: { - react: { - createClass: 'createReactClass', - pragma: 'React', - version: '16.4.2' - }, - propWrapperFunctions: ['forbidExtraProps'] - }, - rules: { - 'react/jsx-uses-react': 'error', - 'react/jsx-uses-vars': 'error', - - 'indent': ['error', 2, { 'SwitchCase': 1 }], - 'key-spacing': ["error", { "mode": "strict" }], - 'linebreak-style': ['error', 'unix'], - 'quotes': ['error', 'single'], - 'semi': ['error', 'never'], - 'eqeqeq': ['error', 'always'], - - 'react/prop-types': 'off', - - // override default options for rules from base configurations - 'no-cond-assign': ['error', 'always'], - } -} diff --git a/openbis_ng_ui/.prettierrc b/openbis_ng_ui/.prettierrc new file mode 100644 index 0000000000000000000000000000000000000000..b10e63c106124b2ee980fbee8fef8a829005b55b --- /dev/null +++ b/openbis_ng_ui/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": false, + "endOfLine": "lf", + "singleQuote": true, + "jsxSingleQuote": true +} diff --git a/openbis_ng_ui/README.md b/openbis_ng_ui/README.md index 963b4be6c126e37ee27356c89579c16d4c0d9860..a8bd209911a38c2cf38b3fbe28afb62fb01b3f9c 100644 --- a/openbis_ng_ui/README.md +++ b/openbis_ng_ui/README.md @@ -20,6 +20,10 @@ 1. Under "IntelliJ IDEA" -> "Preferences" -> "Languages and Frameworks" -> Javascript, set the language version to ECMAScript 6. +## Setting up Visual Studio Code (alternative to IntelliJ Idea) + +Install "ESLint" and "Prettier - Code formatter" extensions. + ## Login into Vagrant to see the dev server output 1. vagrant ssh @@ -30,28 +34,30 @@ On Ubuntu (and maybe on other Linux distributions, too), you might get errors like this when running "vagrant up": - terminate called after throwing an instance of 'std::runtime_error' - what(): Could not add watch +terminate called after throwing an instance of 'std::runtime_error' +what(): Could not add watch This situation will prevent the development environment from working properly. The reason for the error is https://github.com/mhallin/vagrant-notify-forwarder/issues/5. It can be fixed by increasing the maximum number of watches on the host system. On Ubuntu, this is done like this: - echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p +echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p ## "Jest" automated tests Running tests - npm run test +npm run test Debugging tests - 1. execute in command line: +1. execute in command line: + node --inspect-brk ./node_modules/.bin/jest --runInBand - 2. put the following snippet of code in a line where the debugger should stop: +2. put the following snippet of code in a line where the debugger should stop: + /*eslint no-debugger: 0*/ debugger - 3. open "chrome://inspect" in Chrome and choose "Open dedicated DevTools for Node" to start debugging +3. open "chrome://inspect" in Chrome and choose "Open dedicated DevTools for Node" to start debugging diff --git a/openbis_ng_ui/babel.config.js b/openbis_ng_ui/babel.config.js index 8f5ca5e35a7fba4fd2b08c31cd02abf0d8b83509..6db8530ff2a276006956c90296140bcb41a5f853 100644 --- a/openbis_ng_ui/babel.config.js +++ b/openbis_ng_ui/babel.config.js @@ -1,11 +1,8 @@ /* eslint-disable-next-line no-undef */ -module.exports = function (api) { +module.exports = function(api) { api.cache(true) - const presets = [ - '@babel/preset-env', - '@babel/preset-react', - ] + const presets = ['@babel/preset-env', '@babel/preset-react'] const plugins = [ '@babel/plugin-transform-runtime', diff --git a/openbis_ng_ui/index.html b/openbis_ng_ui/index.html index 4580046bc8c92ce9ea841e9aa87705b4184d01d1..8d6d4ada6e5414d2764a2559b5414a4574c6d966 100644 --- a/openbis_ng_ui/index.html +++ b/openbis_ng_ui/index.html @@ -2,18 +2,19 @@ <html lang="en" style="height: 100%;"> <head> <title>openBIS / React</title> - <meta charset="utf-8"> + <meta charset="utf-8" /> <link rel="shortcut icon" href="#" /> <style> body { height: 100%; - min-height:100%; + min-height: 100%; margin: 0px 0px 0px 0px; } #app { height: 100%; } - #app > div, #app > div > div { + #app > div, + #app > div > div { height: 100%; } #error { @@ -23,15 +24,18 @@ <script> function loadError() { window.onload = function() { - let text = "Error: Could not connect to openBIS." - let h2 = document.createElement("h2") + let text = 'Error: Could not connect to openBIS.' + let h2 = document.createElement('h2') var textnode = document.createTextNode(text) h2.appendChild(textnode) - document.getElementById("error").appendChild(h2) + document.getElementById('error').appendChild(h2) } } </script> - <script src="/openbis/resources/api/v3/config.js" onerror="loadError()"></script> + <script + src="/openbis/resources/api/v3/config.js" + onerror="loadError()" + ></script> <script src="/openbis/resources/api/v3/require.js"></script> </head> <body> diff --git a/openbis_ng_ui/jest.config.js b/openbis_ng_ui/jest.config.js index 82e9d638d412a254a477bad383680cb442fdd40e..79dfc00c8043c5af573a3d0488e48b532c95c9a8 100644 --- a/openbis_ng_ui/jest.config.js +++ b/openbis_ng_ui/jest.config.js @@ -1,10 +1,5 @@ /* eslint-disable-next-line no-undef */ module.exports = { - reporters: [ - 'default', - 'jest-junit' - ], - setupFilesAfterEnv: [ - '<rootDir>srcTest/setupTests.js' - ] + reporters: ['default', 'jest-junit'], + setupFilesAfterEnv: ['<rootDir>srcTest/setupTests.js'] } diff --git a/openbis_ng_ui/package.json b/openbis_ng_ui/package.json index ac896db494e247fdf5b28971219e8ce5eef1593c..77bd25d834a7a673400e987cbd0b022d04481d5d 100644 --- a/openbis_ng_ui/package.json +++ b/openbis_ng_ui/package.json @@ -39,17 +39,18 @@ "enzyme": "^3.10.0", "enzyme-adapter-react-16": "^1.14.0", "eslint": "^6.0.1", - "eslint-config-standard": "^12.0.0", + "eslint-config-prettier": "^6.2.0", "eslint-plugin-import": "^2.18.0", "eslint-plugin-jest": "^22.7.2", "eslint-plugin-node": "^9.1.0", + "eslint-plugin-prettier": "^3.1.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-react": "^7.14.2", - "eslint-plugin-standard": "^4.0.0", "file-loader": "^4.0.0", "html-webpack-plugin": "^3.2.0", "jest": "^24.8.0", "jest-junit": "^6.4.0", + "prettier": "^1.18.2", "raw-loader": "^3.0.0", "react-hot-loader": "^4.12.5", "react-loader": "^2.4.5", diff --git a/openbis_ng_ui/src/common/consts/routes.js b/openbis_ng_ui/src/common/consts/routes.js index a71bb3cbe20efb1a0ff988f92d57d3a9d6d9209b..c678fe62fda14759532a7ed7c850072fd1ac115b 100644 --- a/openbis_ng_ui/src/common/consts/routes.js +++ b/openbis_ng_ui/src/common/consts/routes.js @@ -4,56 +4,56 @@ import pathToRegexp from 'path-to-regexp' import * as pages from './pages.js' import * as objectTypes from './objectType.js' -function doFormat(actualParams, pattern, requiredParams){ - if(_.isMatch(actualParams, requiredParams)){ +function doFormat(actualParams, pattern, requiredParams) { + if (_.isMatch(actualParams, requiredParams)) { let toPath = pathToRegexp.compile(pattern) return { path: toPath(actualParams), match: Object.keys(requiredParams).length } - }else{ + } else { return null } } -function doParse(path, pattern, extraParams){ +function doParse(path, pattern, extraParams) { let match = matchPath(path, { path: pattern, exact: true, strict: false }) - if(match){ + if (match) { return { path: match.url, ...match.params, ...extraParams } - }else{ + } else { return null } } const routes = { TYPES: { - format: (params) => { + format: params => { return doFormat(params, '/types', { page: pages.TYPES }) }, - parse: (path) => { + parse: path => { return doParse(path, '/types', { page: pages.TYPES }) } }, TYPES_SEARCH: { - format: (params) => { + format: params => { return doFormat(params, '/typesSearch/:id', { page: pages.TYPES, type: objectTypes.SEARCH }) }, - parse: (path) => { + parse: path => { return doParse(path, '/typesSearch/:id', { page: pages.TYPES, type: objectTypes.SEARCH @@ -61,13 +61,13 @@ const routes = { } }, OBJECT_TYPE: { - format: (params) => { + format: params => { return doFormat(params, '/objectType/:id', { page: pages.TYPES, type: objectTypes.OBJECT_TYPE }) }, - parse: (path) => { + parse: path => { return doParse(path, '/objectType/:id', { page: pages.TYPES, type: objectTypes.OBJECT_TYPE @@ -75,13 +75,13 @@ const routes = { } }, COLLECTION_TYPE: { - format: (params) => { + format: params => { return doFormat(params, '/collectionType/:id', { page: pages.TYPES, type: objectTypes.COLLECTION_TYPE }) }, - parse: (path) => { + parse: path => { return doParse(path, '/collectionType/:id', { page: pages.TYPES, type: objectTypes.COLLECTION_TYPE @@ -89,13 +89,13 @@ const routes = { } }, DATA_SET_TYPE: { - format: (params) => { + format: params => { return doFormat(params, '/datasetType/:id', { page: pages.TYPES, type: objectTypes.DATA_SET_TYPE }) }, - parse: (path) => { + parse: path => { return doParse(path, '/datasetType/:id', { page: pages.TYPES, type: objectTypes.DATA_SET_TYPE @@ -103,13 +103,13 @@ const routes = { } }, MATERIAL_TYPE: { - format: (params) => { + format: params => { return doFormat(params, '/materialType/:id', { page: pages.TYPES, type: objectTypes.MATERIAL_TYPE }) }, - parse: (path) => { + parse: path => { return doParse(path, '/materialType/:id', { page: pages.TYPES, type: objectTypes.MATERIAL_TYPE @@ -117,25 +117,25 @@ const routes = { } }, USERS: { - format: (params) => { + format: params => { return doFormat(params, '/users', { page: pages.USERS }) }, - parse: (path) => { + parse: path => { return doParse(path, '/users', { page: pages.USERS }) } }, USERS_SEARCH: { - format: (params) => { + format: params => { return doFormat(params, '/usersSearch/:id', { page: pages.USERS, type: objectTypes.SEARCH }) }, - parse: (path) => { + parse: path => { return doParse(path, '/usersSearch/:id', { page: pages.USERS, type: objectTypes.SEARCH @@ -143,13 +143,13 @@ const routes = { } }, USER: { - format: (params) => { + format: params => { return doFormat(params, '/user/:id', { page: pages.USERS, type: objectTypes.USER }) }, - parse: (path) => { + parse: path => { return doParse(path, '/user/:id', { page: pages.USERS, type: objectTypes.USER @@ -157,13 +157,13 @@ const routes = { } }, GROUP: { - format: (params) => { + format: params => { return doFormat(params, '/group/:id', { page: pages.USERS, type: objectTypes.GROUP }) }, - parse: (path) => { + parse: path => { return doParse(path, '/group/:id', { page: pages.USERS, type: objectTypes.GROUP @@ -171,13 +171,13 @@ const routes = { } }, DEFAULT: { - format: (params) => { + format: () => { return { path: '/', match: 0 } }, - parse: (path) => { + parse: () => { return { path: '/', page: pages.TYPES @@ -186,26 +186,30 @@ const routes = { } } -function format(params){ +function format(params) { let keys = Object.keys(routes) let bestPath = null let bestMatch = 0 - for(let i = 0; i < keys.length; i++){ + for (let i = 0; i < keys.length; i++) { let route = routes[keys[i]] - let result = route.format(params) - if(result && result.match > bestMatch){ - bestPath = result.path + try { + let result = route.format(params) + if (result && result.match > bestMatch) { + bestPath = result.path + } + } catch (err) { + // ignore problems with incorrect params } } return bestPath } -function parse(path){ +function parse(path) { let keys = Object.keys(routes) - for(let i = 0; i < keys.length; i++){ + for (let i = 0; i < keys.length; i++) { let route = routes[keys[i]] let params = route.parse(path) - if(params){ + if (params) { return params } } diff --git a/openbis_ng_ui/src/common/logger.js b/openbis_ng_ui/src/common/logger.js index c6ace92add51173033f875fbb0576bc7e2e21861..7d792c6b9522a1cff6045ab281b5baf64b1e427e 100644 --- a/openbis_ng_ui/src/common/logger.js +++ b/openbis_ng_ui/src/common/logger.js @@ -1,35 +1,42 @@ -export const ERROR = {name: 'ERROR', importance: 4} -export const INFO = {name: 'INFO', importance: 3} -export const DEBUG = {name: 'DEBUG', importance: 2} -export const TRACE = {name: 'TRACE', importance: 1} +export const ERROR = { name: 'ERROR', importance: 4 } +export const INFO = { name: 'INFO', importance: 3 } +export const DEBUG = { name: 'DEBUG', importance: 2 } +export const TRACE = { name: 'TRACE', importance: 1 } let currentLevel = INFO -let isLevelEnabled = (level) => { +let isLevelEnabled = level => { return level.importance >= currentLevel.importance } let log = (level, message, ...params) => { - if(isLevelEnabled(level)){ + if (isLevelEnabled(level)) { // eslint-disable-next-line no-console console.log(message, ...params) } } let group = (level, message, ...params) => { - if(isLevelEnabled(level)){ + if (isLevelEnabled(level)) { // eslint-disable-next-line no-console console.group(message, ...params) } } -let groupEnd = (level) => { - if(isLevelEnabled(level)){ +let groupEnd = level => { + if (isLevelEnabled(level)) { // eslint-disable-next-line no-console console.groupEnd() } } export default { - ERROR, INFO, DEBUG, TRACE, log, group, groupEnd, isLevelEnabled + ERROR, + INFO, + DEBUG, + TRACE, + log, + group, + groupEnd, + isLevelEnabled } diff --git a/openbis_ng_ui/src/common/util.js b/openbis_ng_ui/src/common/util.js index 22c7644df4d3d0fa3f51f2fccba36fbf275a4ed5..0970182dc5ffa99fa42242011acd2cf71e5fc37a 100644 --- a/openbis_ng_ui/src/common/util.js +++ b/openbis_ng_ui/src/common/util.js @@ -1,3 +1,3 @@ -export function classNames(...classNames){ +export function classNames(...classNames) { return classNames.filter(className => className).join(' ') } diff --git a/openbis_ng_ui/src/components/App.jsx b/openbis_ng_ui/src/components/App.jsx index 3f19e6a3d8777dba14e4573e6d820ca00c19674f..50d7eae97bbcad388db671ecfdbce379a3d84d54 100644 --- a/openbis_ng_ui/src/components/App.jsx +++ b/openbis_ng_ui/src/components/App.jsx @@ -1,7 +1,7 @@ import React from 'react' import _ from 'lodash' -import {connect} from 'react-redux' -import {withStyles} from '@material-ui/core/styles' +import { connect } from 'react-redux' +import { withStyles } from '@material-ui/core/styles' import logger from '../common/logger.js' import * as util from '../common/util.js' import * as pages from '../common/consts/pages.js' @@ -31,16 +31,16 @@ const styles = { display: 'flex' }, hidden: { - display: 'none', + display: 'none' } } const pageToComponent = { [pages.TYPES]: Types, - [pages.USERS]: Users, + [pages.USERS]: Users } -function mapStateToProps(state){ +function mapStateToProps(state) { return { loading: selectors.getLoading(state), session: selectors.getSession(state), @@ -49,16 +49,19 @@ function mapStateToProps(state){ } } -function mapDispatchToProps(dispatch){ +function mapDispatchToProps(dispatch) { return { - init: () => { dispatch(actions.init()) }, - errorClosed: () => { dispatch(actions.errorChange(null)) } + init: () => { + dispatch(actions.init()) + }, + errorClosed: () => { + dispatch(actions.errorChange(null)) + } } } class App extends React.Component { - - componentDidMount(){ + componentDidMount() { this.props.init() } @@ -74,32 +77,39 @@ class App extends React.Component { ) } - renderPage(){ + renderPage() { const classes = this.props.classes - if(this.props.session){ + if (this.props.session) { return ( <div className={classes.container}> <Menu page={this.props.currentPage} /> - { - _.map(pageToComponent, (PageComponent, page) => { - let visible = this.props.currentPage === page - return ( - <div key={page} className={util.classNames(classes.page, visible ? classes.visible : classes.hidden)}> - <PageComponent /> - </div> - ) - }) - } + {_.map(pageToComponent, (PageComponent, page) => { + let visible = this.props.currentPage === page + return ( + <div + key={page} + className={util.classNames( + classes.page, + visible ? classes.visible : classes.hidden + )} + > + <PageComponent /> + </div> + ) + })} </div> ) - }else{ - return <Login/> + } else { + return <Login /> } } } export default _.flow( - connect(mapStateToProps, mapDispatchToProps), + connect( + mapStateToProps, + mapDispatchToProps + ), withStyles(styles) )(App) diff --git a/openbis_ng_ui/src/components/common/browser/Browser.jsx b/openbis_ng_ui/src/components/common/browser/Browser.jsx index 2db0eba9cf295468381f4af8bffff69c1ed03025..cdd22813f33ea8617638f59385fb57a4f92dd6cb 100644 --- a/openbis_ng_ui/src/components/common/browser/Browser.jsx +++ b/openbis_ng_ui/src/components/common/browser/Browser.jsx @@ -1,7 +1,7 @@ import React from 'react' import _ from 'lodash' -import {connect} from 'react-redux' -import {withStyles} from '@material-ui/core/styles' +import { connect } from 'react-redux' +import { withStyles } from '@material-ui/core/styles' import logger from '../../../common/logger.js' import * as selectors from '../../../store/selectors/selectors.js' import * as actions from '../../../store/actions/actions.js' @@ -17,7 +17,7 @@ const styles = { } } -function mapStateToProps(){ +function mapStateToProps() { const getBrowserNodes = selectors.createGetBrowserNodes() return (state, ownProps) => { return { @@ -27,19 +27,28 @@ function mapStateToProps(){ } } -function mapDispatchToProps(dispatch, ownProps){ +function mapDispatchToProps(dispatch, ownProps) { return { - init: () => { dispatch(actions.browserInit(ownProps.page)) }, - filterChange: (filter) => { dispatch(actions.browserFilterChange(ownProps.page, filter)) }, - nodeSelect: (id) => { dispatch(actions.browserNodeSelect(ownProps.page, id)) }, - nodeExpand: (id) => { dispatch(actions.browserNodeExpand(ownProps.page, id)) }, - nodeCollapse: (id) => { dispatch(actions.browserNodeCollapse(ownProps.page, id)) } + init: () => { + dispatch(actions.browserInit(ownProps.page)) + }, + filterChange: filter => { + dispatch(actions.browserFilterChange(ownProps.page, filter)) + }, + nodeSelect: id => { + dispatch(actions.browserNodeSelect(ownProps.page, id)) + }, + nodeExpand: id => { + dispatch(actions.browserNodeExpand(ownProps.page, id)) + }, + nodeCollapse: id => { + dispatch(actions.browserNodeCollapse(ownProps.page, id)) + } } } class Browser extends React.PureComponent { - - componentDidMount(){ + componentDidMount() { this.props.init() } @@ -64,10 +73,12 @@ class Browser extends React.PureComponent { </div> ) } - } export default _.flow( - connect(mapStateToProps, mapDispatchToProps), + connect( + mapStateToProps, + mapDispatchToProps + ), withStyles(styles) )(Browser) diff --git a/openbis_ng_ui/src/components/common/browser/BrowserNode.jsx b/openbis_ng_ui/src/components/common/browser/BrowserNode.jsx index 2844eecc2a71beaf7eb266494f8d66853333adc3..0bee6b7e4702664df364b990cd8bcd1429acd8ee 100644 --- a/openbis_ng_ui/src/components/common/browser/BrowserNode.jsx +++ b/openbis_ng_ui/src/components/common/browser/BrowserNode.jsx @@ -1,6 +1,6 @@ import React from 'react' import _ from 'lodash' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import ListItem from '@material-ui/core/ListItem' import ListItemIcon from '@material-ui/core/ListItemIcon' import ListItemText from '@material-ui/core/ListItemText' @@ -18,71 +18,90 @@ const styles = { } class BrowserNode extends React.Component { - render() { logger.log(logger.DEBUG, 'BrowserNode.render') - const {node, level} = this.props + const { node, level } = this.props - return (<div> - <ListItem - button - selected={node.selected} - onClick={() => this.props.nodeSelect(node.id)} - style={{paddingLeft: level * 24 + 'px'}}> - {this.renderIcon(node)} - {this.renderText(node)} - </ListItem> - {node.children && node.children.length > 0 && - <Collapse in={node.expanded} mountOnEnter={true} unmountOnExit={true}> - <BrowserNodes - nodes={node.children} - nodeSelect={this.props.nodeSelect} - nodeCollapse={this.props.nodeCollapse} - nodeExpand={this.props.nodeExpand} - level={level + 1} /> - </Collapse>} - </div>) + return ( + <div> + <ListItem + button + selected={node.selected} + onClick={() => this.props.nodeSelect(node.id)} + style={{ paddingLeft: level * 24 + 'px' }} + > + {this.renderIcon(node)} + {this.renderText(node)} + </ListItem> + {node.children && node.children.length > 0 && ( + <Collapse in={node.expanded} mountOnEnter={true} unmountOnExit={true}> + <BrowserNodes + nodes={node.children} + nodeSelect={this.props.nodeSelect} + nodeCollapse={this.props.nodeCollapse} + nodeExpand={this.props.nodeExpand} + level={level + 1} + /> + </Collapse> + )} + </div> + ) } - renderIcon(node){ + renderIcon(node) { logger.log(logger.DEBUG, 'BrowserNode.renderIcon') const classes = this.props.classes - if(node.children && node.children.length > 0){ + if (node.children && node.children.length > 0) { let icon = null - if(node.expanded){ - icon = <ExpandMoreIcon onClick={(e) => { - e.stopPropagation() - this.props.nodeCollapse(node.id) - }}/> - }else{ - icon = <ChevronRightIcon onClick={(e) => { - e.stopPropagation() - this.props.nodeExpand(node.id) - }}/> + if (node.expanded) { + icon = ( + <ExpandMoreIcon + onClick={e => { + e.stopPropagation() + this.props.nodeCollapse(node.id) + }} + /> + ) + } else { + icon = ( + <ChevronRightIcon + onClick={e => { + e.stopPropagation() + this.props.nodeExpand(node.id) + }} + /> + ) } - return <ListItemIcon classes={{ - root: classes.icon - }}>{icon}</ListItemIcon> - }else{ - return <ListItemIcon classes={{ - root: classes.icon - }}><span></span></ListItemIcon> + return ( + <ListItemIcon + classes={{ + root: classes.icon + }} + > + {icon} + </ListItemIcon> + ) + } else { + return ( + <ListItemIcon + classes={{ + root: classes.icon + }} + > + <span></span> + </ListItemIcon> + ) } } - renderText(node){ + renderText(node) { logger.log(logger.DEBUG, 'BrowserNode.renderText "' + node.text + '"') - return <ListItemText - primary={node.text} - /> + return <ListItemText primary={node.text} /> } - } -export default _.flow( - withStyles(styles) -)(BrowserNode) +export default _.flow(withStyles(styles))(BrowserNode) diff --git a/openbis_ng_ui/src/components/common/browser/BrowserNodes.jsx b/openbis_ng_ui/src/components/common/browser/BrowserNodes.jsx index 299d1f385af0eecfd2b527fb5dd20d0e62336e99..626479e75e2bf70431c7bf63e199988fca71c79c 100644 --- a/openbis_ng_ui/src/components/common/browser/BrowserNodes.jsx +++ b/openbis_ng_ui/src/components/common/browser/BrowserNodes.jsx @@ -1,5 +1,5 @@ import React from 'react' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import List from '@material-ui/core/List' import BrowserNode from './BrowserNode.jsx' import logger from '../../../common/logger.js' @@ -12,22 +12,26 @@ const styles = () => ({ }, list: { paddingTop: '0', - paddingBottom: '0', - }, + paddingBottom: '0' + } }) class BrowserNodes extends React.Component { - render() { logger.log(logger.DEBUG, 'BrowserNodes.render') const classes = this.props.classes return ( - <List className={util.classNames(classes.list, this.props.level === 0 ? classes.container : null)}> - { - this.props.nodes.map(node => { - return <BrowserNode + <List + className={util.classNames( + classes.list, + this.props.level === 0 ? classes.container : null + )} + > + {this.props.nodes.map(node => { + return ( + <BrowserNode key={node.id} node={node} nodeSelect={this.props.nodeSelect} @@ -35,12 +39,11 @@ class BrowserNodes extends React.Component { nodeExpand={this.props.nodeExpand} level={this.props.level} /> - }) - } + ) + })} </List> ) } - } export default withStyles(styles)(BrowserNodes) diff --git a/openbis_ng_ui/src/components/common/content/Content.jsx b/openbis_ng_ui/src/components/common/content/Content.jsx index 5b760ed0277d536df0c955b3e73ca386cd87d749..edd4e403a6c009b7bc72664b86529002c6d92301 100644 --- a/openbis_ng_ui/src/components/common/content/Content.jsx +++ b/openbis_ng_ui/src/components/common/content/Content.jsx @@ -1,7 +1,7 @@ import React from 'react' import _ from 'lodash' -import {connect} from 'react-redux' -import {withStyles} from '@material-ui/core/styles' +import { connect } from 'react-redux' +import { withStyles } from '@material-ui/core/styles' import logger from '../../../common/logger.js' import * as util from '../../../common/util.js' import * as selectors from '../../../store/selectors/selectors.js' @@ -24,11 +24,11 @@ const styles = { display: 'block' }, hidden: { - display: 'none', + display: 'none' } } -function mapStateToProps(){ +function mapStateToProps() { const getSelectedObject = selectors.createGetSelectedObject() return (state, ownProps) => { return { @@ -39,15 +39,18 @@ function mapStateToProps(){ } } -function mapDispatchToProps(dispatch, ownProps){ +function mapDispatchToProps(dispatch, ownProps) { return { - objectSelect: (type, id) => { dispatch(actions.objectOpen(ownProps.page, type, id)) }, - objectClose: (type, id) => { dispatch(actions.objectClose(ownProps.page, type, id)) } + objectSelect: (type, id) => { + dispatch(actions.objectOpen(ownProps.page, type, id)) + }, + objectClose: (type, id) => { + dispatch(actions.objectClose(ownProps.page, type, id)) + } } } class Content extends React.Component { - render() { logger.log(logger.DEBUG, 'Content.render') @@ -60,27 +63,35 @@ class Content extends React.Component { changedObjects={this.props.changedObjects} selectedObject={this.props.selectedObject} objectSelect={this.props.objectSelect} - objectClose={this.props.objectClose} /> - { - this.props.openObjects.map(object => { - let ObjectComponent = this.props.objectTypeToComponent[object.type] - if(ObjectComponent){ - let key = object.type + '/' + object.id - let visible = _.isEqual(object, this.props.selectedObject) - return ( - <div key={key} className={util.classNames(classes.component, visible ? classes.visible : classes.hidden)}> - <ObjectComponent objectId={object.id} /> - </div> - ) - } - }) - } + objectClose={this.props.objectClose} + /> + {this.props.openObjects.map(object => { + let ObjectComponent = this.props.objectTypeToComponent[object.type] + if (ObjectComponent) { + let key = object.type + '/' + object.id + let visible = _.isEqual(object, this.props.selectedObject) + return ( + <div + key={key} + className={util.classNames( + classes.component, + visible ? classes.visible : classes.hidden + )} + > + <ObjectComponent objectId={object.id} /> + </div> + ) + } + })} </div> ) } } export default _.flow( - connect(mapStateToProps, mapDispatchToProps), + connect( + mapStateToProps, + mapDispatchToProps + ), withStyles(styles) )(Content) diff --git a/openbis_ng_ui/src/components/common/content/ContentTabs.jsx b/openbis_ng_ui/src/components/common/content/ContentTabs.jsx index d6eec630b753b230250f13d86a7ff549929463f1..6c2487d701de40b612d1ba1a4a6d0f84a2b23c2a 100644 --- a/openbis_ng_ui/src/components/common/content/ContentTabs.jsx +++ b/openbis_ng_ui/src/components/common/content/ContentTabs.jsx @@ -1,6 +1,6 @@ import _ from 'lodash' import React from 'react' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import Tabs from '@material-ui/core/Tabs' import Tab from '@material-ui/core/Tab' import CloseIcon from '@material-ui/icons/Close' @@ -12,16 +12,15 @@ const styles = { textTransform: 'none' }, iconRoot: { - marginLeft: '6px', + marginLeft: '6px' }, tabLabel: { display: 'inline-flex', - alignItems: 'center', + alignItems: 'center' } } class ContentTabs extends React.Component { - handleTabChange = (event, value) => { let object = this.props.objects[value] this.props.objectSelect(object.type, object.id) @@ -40,27 +39,28 @@ class ContentTabs extends React.Component { return ( <Tabs value={_.findIndex(this.props.objects, this.props.selectedObject)} - variant="scrollable" - scrollButtons="on" + variant='scrollable' + scrollButtons='on' onChange={this.handleTabChange} > - {this.props.objects.map(object => - <Tab key={`${object.type}/${object.id}`} + {this.props.objects.map(object => ( + <Tab + key={`${object.type}/${object.id}`} label={this.renderLabel(object)} classes={{ root: classes.tabRoot }} /> - )} + ))} </Tabs> ) } - renderLabel(object){ + renderLabel(object) { let changed = _.find(this.props.changedObjects, object) ? '*' : '' let label = null - switch(object.type){ + switch (object.type) { case objectTypes.SEARCH: label = 'search: ' + object.id break @@ -69,18 +69,24 @@ class ContentTabs extends React.Component { break } - return <span className={this.props.classes.tabLabel}>{label}{this.renderIcon(object)}</span> + return ( + <span className={this.props.classes.tabLabel}> + {label} + {this.renderIcon(object)} + </span> + ) } - renderIcon(object){ - return <CloseIcon - onClick={(event) => this.handleTabClose(event, object)} - classes={{ - root: this.props.classes.iconRoot - }} - /> + renderIcon(object) { + return ( + <CloseIcon + onClick={event => this.handleTabClose(event, object)} + classes={{ + root: this.props.classes.iconRoot + }} + /> + ) } - } export default withStyles(styles)(ContentTabs) diff --git a/openbis_ng_ui/src/components/common/dnd/DragAndDropProvider.jsx b/openbis_ng_ui/src/components/common/dnd/DragAndDropProvider.jsx index 33c3e32565b54d4334052b0cd55c318db18806a8..7adbbf9c87d0c039fbd269dcf6ed6721a79ceb02 100644 --- a/openbis_ng_ui/src/components/common/dnd/DragAndDropProvider.jsx +++ b/openbis_ng_ui/src/components/common/dnd/DragAndDropProvider.jsx @@ -4,7 +4,6 @@ import HTML5Backend from 'react-dnd-html5-backend' import logger from '../../../common/logger.js' class DragAndDropProvider extends React.Component { - render() { logger.log(logger.DEBUG, 'DragAndDropProvider.render') diff --git a/openbis_ng_ui/src/components/common/error/Error.jsx b/openbis_ng_ui/src/components/common/error/Error.jsx index 881a2777d845bc2a4a555a9f998b0bf70d7f0985..8cc2b851f1149b029e929bf3407614a56adf53a4 100644 --- a/openbis_ng_ui/src/components/common/error/Error.jsx +++ b/openbis_ng_ui/src/components/common/error/Error.jsx @@ -3,16 +3,17 @@ import ErrorDialog from './ErrorDialog.jsx' import logger from '../../../common/logger.js' class Error extends React.Component { - render() { logger.log(logger.DEBUG, 'Error.render') return ( <div> - { - this.props.error && - <ErrorDialog error={this.props.error} onClose={this.props.errorClosed}/> - } + {this.props.error && ( + <ErrorDialog + error={this.props.error} + onClose={this.props.errorClosed} + /> + )} {this.props.children} </div> ) diff --git a/openbis_ng_ui/src/components/common/error/ErrorDialog.jsx b/openbis_ng_ui/src/components/common/error/ErrorDialog.jsx index f8d504134de996ad6ce90ea6c47a5a0af8c2d2ae..ea24379f71fc4712582289e9e444ee31211e8a0f 100644 --- a/openbis_ng_ui/src/components/common/error/ErrorDialog.jsx +++ b/openbis_ng_ui/src/components/common/error/ErrorDialog.jsx @@ -13,8 +13,7 @@ import Slide from '@material-ui/core/Slide' import logger from '../../../common/logger.js' import profile from '../../../profile.js' - -const dialogStyles = (theme) => ({ +const dialogStyles = theme => ({ paper: { backgroundColor: theme.palette.error.main } @@ -25,31 +24,45 @@ const StyledDialog = withStyles(dialogStyles)(Dialog) const ANIMATION_TIME_MS = 250 function Transition(props) { - return <Slide - direction="up" - timeout={{ enter: ANIMATION_TIME_MS, exit: ANIMATION_TIME_MS }} - {...props} - /> + return ( + <Slide + direction='up' + timeout={{ enter: ANIMATION_TIME_MS, exit: ANIMATION_TIME_MS }} + {...props} + /> + ) } class ErrorDialog extends React.Component { - state = { - open: true, + open: true } getErrorMailtoHref() { let report = - 'agent: ' + navigator.userAgent + '%0D%0A' + - 'domain: ' + location.hostname + '%0D%0A' + - 'timestamp: ' + new Date() + '%0D%0A' + - 'href: ' + location.href.replace(new RegExp('&', 'g'), ' - ') + '%0D%0A' + - 'error: ' + JSON.stringify(this.props.error['data']) + 'agent: ' + + navigator.userAgent + + '%0D%0A' + + 'domain: ' + + location.hostname + + '%0D%0A' + + 'timestamp: ' + + new Date() + + '%0D%0A' + + 'href: ' + + location.href.replace(new RegExp('&', 'g'), ' - ') + + '%0D%0A' + + 'error: ' + + JSON.stringify(this.props.error['data']) let href = - 'mailto:' + profile.devEmail + - '?subject=openBIS Error Report [' + location.hostname + ']' + - '&body=' + report + 'mailto:' + + profile.devEmail + + '?subject=openBIS Error Report [' + + location.hostname + + ']' + + '&body=' + + report return href } @@ -63,25 +76,23 @@ class ErrorDialog extends React.Component { return ( <StyledDialog - open={ this.state.open } - onClose={ this.props.closeError } - scroll="paper" - aria-labelledby="error-dialog-title" - fullWidth={ true } - maxWidth="md" + open={this.state.open} + onClose={this.props.closeError} + scroll='paper' + aria-labelledby='error-dialog-title' + fullWidth={true} + maxWidth='md' TransitionComponent={Transition} > - <DialogTitle id="error-dialog-title">Error</DialogTitle> + <DialogTitle id='error-dialog-title'>Error</DialogTitle> <DialogContent> - <DialogContentText> - { this.props.error.message } - </DialogContentText> + <DialogContentText>{this.props.error.message}</DialogContentText> </DialogContent> <DialogActions> - <Button onClick={ this.close.bind(this) } color="primary"> + <Button onClick={this.close.bind(this)} color='primary'> Dismiss </Button> - <Button color="primary" href={this.getErrorMailtoHref()}> + <Button color='primary' href={this.getErrorMailtoHref()}> Send error report </Button> </DialogActions> @@ -91,7 +102,7 @@ class ErrorDialog extends React.Component { } ErrorDialog.propTypes = { - error: PropTypes.any.isRequired, + error: PropTypes.any.isRequired } export default ErrorDialog diff --git a/openbis_ng_ui/src/components/common/form/EditableField.jsx b/openbis_ng_ui/src/components/common/form/EditableField.jsx index c6959a2d8f0d3a8b06fce1d0fb1b78b992e95488..858f08fbebacc0772f0e24101fb5613def8260ec 100644 --- a/openbis_ng_ui/src/components/common/form/EditableField.jsx +++ b/openbis_ng_ui/src/components/common/form/EditableField.jsx @@ -1,11 +1,11 @@ import _ from 'lodash' import React from 'react' import EditIcon from '@material-ui/icons/EditOutlined' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import * as util from '../../../common/util.js' import logger from '../../../common/logger.js' -const styles = (theme) => ({ +const styles = theme => ({ container: { display: 'inline-flex', margin: '1px 30px 1px 1px', @@ -20,12 +20,11 @@ const styles = (theme) => ({ margin: '0px', border: '1px solid', borderColor: theme.palette.action.selected - }, + } }) class EditableField extends React.Component { - - constructor(props){ + constructor(props) { super(props) this.state = { edited: false, @@ -38,32 +37,35 @@ class EditableField extends React.Component { this.handleBlur = this.handleBlur.bind(this) } - handleMouseEnter(){ + handleMouseEnter() { this.setState(() => ({ hovered: true })) } - handleMouseLeave(){ + handleMouseLeave() { this.setState(() => ({ hovered: false })) } - handleClick(event){ + handleClick(event) { event.stopPropagation() - if(!this.state.edited){ - this.setState(() => ({ - edited: true - }), () => { - this.ref.current.focus() - }) + if (!this.state.edited) { + this.setState( + () => ({ + edited: true + }), + () => { + this.ref.current.focus() + } + ) } } - handleBlur(){ - if(this.state.edited){ + handleBlur() { + if (this.state.edited) { this.setState(() => ({ edited: false, hovered: false @@ -71,24 +73,29 @@ class EditableField extends React.Component { } } - render(){ + render() { logger.log(logger.DEBUG, 'EditableField.render') - const {classes} = this.props - const {edited, hovered} = this.state + const { classes } = this.props + const { edited, hovered } = this.state - let classNames = util.classNames(classes.container, hovered ? classes.hovered : null, edited ? classes.edited : null) + let classNames = util.classNames( + classes.container, + hovered ? classes.hovered : null, + edited ? classes.edited : null + ) - if(edited){ + if (edited) { return ( - <span - className={classNames} - onClick={this.handleClick} - > - {this.props.renderField({edited: true, handleBlur: this.handleBlur, ref: this.ref})} + <span className={classNames} onClick={this.handleClick}> + {this.props.renderField({ + edited: true, + handleBlur: this.handleBlur, + ref: this.ref + })} </span> ) - }else{ + } else { return ( <span className={classNames} @@ -96,17 +103,16 @@ class EditableField extends React.Component { onMouseLeave={this.handleMouseLeave} onClick={this.handleClick} > - {this.props.renderField({edited: false, handleBlur: this.handleBlur, ref: this.ref})} - {hovered && - <EditIcon className={classes.icon} /> - } + {this.props.renderField({ + edited: false, + handleBlur: this.handleBlur, + ref: this.ref + })} + {hovered && <EditIcon className={classes.icon} />} </span> ) } } - } -export default _.flow( - withStyles(styles), -)(EditableField) +export default _.flow(withStyles(styles))(EditableField) diff --git a/openbis_ng_ui/src/components/common/form/FilterField.jsx b/openbis_ng_ui/src/components/common/form/FilterField.jsx index c37cecaf60ad8e30358e52c7093e0c28020c49f7..02ae14d516f1da0fb23724b623de267aadbc7603 100644 --- a/openbis_ng_ui/src/components/common/form/FilterField.jsx +++ b/openbis_ng_ui/src/components/common/form/FilterField.jsx @@ -1,5 +1,5 @@ import React from 'react' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import InputAdornment from '@material-ui/core/InputAdornment' import TextField from '@material-ui/core/TextField' import IconButton from '@material-ui/core/IconButton' @@ -20,19 +20,18 @@ const styles = () => ({ }) class FilterField extends React.Component { - - constructor(props){ + constructor(props) { super(props) this.filterRef = React.createRef() this.handleFilterChange = this.handleFilterChange.bind(this) this.handleFilterClear = this.handleFilterClear.bind(this) } - handleFilterChange(event){ + handleFilterChange(event) { this.props.filterChange(event.target.value) } - handleFilterClear(event){ + handleFilterClear(event) { event.preventDefault() this.props.filterChange('') this.filterRef.current.focus() @@ -46,7 +45,7 @@ class FilterField extends React.Component { return ( <TextField className={classes.field} - placeholder="Filter" + placeholder='Filter' value={this.props.filter} onChange={this.handleFilterChange} InputProps={{ @@ -56,39 +55,45 @@ class FilterField extends React.Component { classes: { input: classes.input } - }}/> + }} + /> ) } - renderFilterIcon(){ + renderFilterIcon() { const classes = this.props.classes return ( - <InputAdornment position="start" classes={{ - root: classes.adornment - }}> + <InputAdornment + position='start' + classes={{ + root: classes.adornment + }} + > <FilterIcon /> </InputAdornment> ) } - renderFilterClearIcon(){ + renderFilterClearIcon() { const classes = this.props.classes - if(this.props.filter){ + if (this.props.filter) { return ( - <InputAdornment position="end" classes={{ - root: classes.adornment - }}> + <InputAdornment + position='end' + classes={{ + root: classes.adornment + }} + > <IconButton onMouseDown={this.handleFilterClear}> <CloseIcon /> </IconButton> </InputAdornment> ) - }else{ - return (<React.Fragment></React.Fragment>) + } else { + return <React.Fragment></React.Fragment> } } - } export default withStyles(styles)(FilterField) diff --git a/openbis_ng_ui/src/components/common/grid/ColumnConfig.jsx b/openbis_ng_ui/src/components/common/grid/ColumnConfig.jsx index 5b6e6584aa470926603e8e881f4ad6edde98c9d8..6829948807a088b433538cd5ce76fbe436ecf372 100644 --- a/openbis_ng_ui/src/components/common/grid/ColumnConfig.jsx +++ b/openbis_ng_ui/src/components/common/grid/ColumnConfig.jsx @@ -1,11 +1,10 @@ import _ from 'lodash' import React from 'react' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import IconButton from '@material-ui/core/IconButton' import SettingsIcon from '@material-ui/icons/Settings' import Popover from '@material-ui/core/Popover' -import FormControlLabel from '@material-ui/core/FormControlLabel' -import Checkbox from '@material-ui/core/Checkbox' +import ColumnConfigRow from './ColumnConfigRow.jsx' import logger from '../../../common/logger.js' const styles = () => ({ @@ -21,46 +20,31 @@ const styles = () => ({ }) class ColumnConfig extends React.Component { - - constructor(props){ + constructor(props) { super(props) this.state = { el: null } this.handleOpen = this.handleOpen.bind(this) this.handleClose = this.handleClose.bind(this) - this.handleChange = this.handleChange.bind(this) } - handleOpen(event){ + handleOpen(event) { this.setState({ el: event.currentTarget }) } - handleClose(){ + handleClose() { this.setState({ el: null }) } - handleChange(event){ - let columns = [...this.props.visibleColumns] - let column = event.target.value - - if(columns.includes(column)){ - _.pull(columns, column) - }else{ - columns.push(column) - } - - this.props.onColumnsChange(columns) - } - render() { logger.log(logger.DEBUG, 'ColumnConfig.render') - const { classes, allColumns, visibleColumns } = this.props + const { classes, columns, onVisibleChange, onOrderChange } = this.props const { el } = this.state return ( @@ -74,25 +58,20 @@ class ColumnConfig extends React.Component { onClose={this.handleClose} anchorOrigin={{ vertical: 'top', - horizontal: 'center', + horizontal: 'center' }} transformOrigin={{ vertical: 'bottom', - horizontal: 'center', + horizontal: 'center' }} > <ol className={classes.columns}> - {allColumns.map(column => ( + {columns.map(column => ( <li key={column.field}> - <FormControlLabel - control={ - <Checkbox - value={column.field} - checked={visibleColumns.includes(column.field)} - onChange={this.handleChange} - /> - } - label={column.label || column.field} + <ColumnConfigRow + column={column} + onVisibleChange={onVisibleChange} + onOrderChange={onOrderChange} /> </li> ))} @@ -101,9 +80,6 @@ class ColumnConfig extends React.Component { </div> ) } - } -export default _.flow( - withStyles(styles) -)(ColumnConfig) +export default _.flow(withStyles(styles))(ColumnConfig) diff --git a/openbis_ng_ui/src/components/common/grid/ColumnConfigRow.jsx b/openbis_ng_ui/src/components/common/grid/ColumnConfigRow.jsx new file mode 100644 index 0000000000000000000000000000000000000000..0ff58ae3755fd4835bd99b91356628fc72c6da03 --- /dev/null +++ b/openbis_ng_ui/src/components/common/grid/ColumnConfigRow.jsx @@ -0,0 +1,111 @@ +import _ from 'lodash' +import React from 'react' +import { DragSource } from 'react-dnd' +import { DropTarget } from 'react-dnd' +import FormControlLabel from '@material-ui/core/FormControlLabel' +import Checkbox from '@material-ui/core/Checkbox' +import DragHandleIcon from '@material-ui/icons/DragHandle' +import RootRef from '@material-ui/core/RootRef' +import { withStyles } from '@material-ui/core/styles' +import logger from '../../../common/logger.js' + +const styles = () => ({ + row: { + display: 'flex', + alignItems: 'center' + }, + label: { + marginLeft: 0 + }, + drag: { + display: 'flex', + cursor: 'grab' + } +}) + +const source = { + beginDrag(props) { + return { column: props.column.field } + }, + endDrag(props, monitor) { + if (monitor.getItem() && monitor.getDropResult()) { + const { column: sourceColumn } = monitor.getItem() + const { column: targetColumn } = monitor.getDropResult() + props.onOrderChange(sourceColumn, targetColumn) + } + } +} + +function sourceCollect(connect, monitor) { + return { + connectDragSource: connect.dragSource(), + connectDragPreview: connect.dragPreview(), + isDragging: monitor.isDragging() + } +} + +const target = { + drop(props) { + return { column: props.column.field } + } +} + +function targetCollect(connect, monitor) { + return { + connectDropTarget: connect.dropTarget(), + isDragging: monitor.getItem() !== null + } +} + +class ColumnConfigRow extends React.Component { + constructor(props) { + super(props) + this.handleRef = React.createRef() + this.rowRef = React.createRef() + this.handleVisibleChange = this.handleVisibleChange.bind(this) + } + + componentDidMount() { + this.props.connectDragSource(this.handleRef.current) + this.props.connectDragPreview(this.rowRef.current) + this.props.connectDropTarget(this.rowRef.current) + } + + handleVisibleChange() { + this.props.onVisibleChange(this.props.column.field) + } + + render() { + logger.log(logger.DEBUG, 'ColumnConfigRow.render') + + const { classes, column } = this.props + + return ( + <RootRef rootRef={this.rowRef}> + <div className={classes.row}> + <RootRef rootRef={this.handleRef}> + <div className={classes.drag}> + <DragHandleIcon /> + </div> + </RootRef> + <FormControlLabel + classes={{ root: classes.label }} + control={ + <Checkbox + checked={column.visible} + onChange={this.handleVisibleChange} + /> + } + label={column.label || column.field} + /> + </div> + </RootRef> + ) + } +} + +export default _.flow( + DragSource('column', source, sourceCollect), + DropTarget('column', target, targetCollect), + withStyles(styles) +)(ColumnConfigRow) diff --git a/openbis_ng_ui/src/components/common/grid/Grid.jsx b/openbis_ng_ui/src/components/common/grid/Grid.jsx index dc9f18dea94ce3c49e4c9096c2a6f3ec6f1bd34d..2ad7ca827ed744c98c0d4168c2c6c2efd914b480 100644 --- a/openbis_ng_ui/src/components/common/grid/Grid.jsx +++ b/openbis_ng_ui/src/components/common/grid/Grid.jsx @@ -1,7 +1,7 @@ import _ from 'lodash' import React from 'react' -import {connect} from 'react-redux' -import {withStyles} from '@material-ui/core/styles' +import { connect } from 'react-redux' +import { withStyles } from '@material-ui/core/styles' import Table from '@material-ui/core/Table' import TableBody from '@material-ui/core/TableBody' import TableCell from '@material-ui/core/TableCell' @@ -13,20 +13,15 @@ import ColumnConfig from '../../common/grid/ColumnConfig.jsx' import PageConfig from '../../common/grid/PageConfig.jsx' import * as ids from '../../../common/consts/ids.js' import * as selectors from '../../../store/selectors/selectors.js' -import {facade, dto} from '../../../services/openbis.js' +import { facade, dto } from '../../../services/openbis.js' import logger from '../../../common/logger.js' -const styles = (theme) => ({ +const styles = theme => ({ container: { display: 'flex', flexDirection: 'column', height: '100%' }, - headerContainer: { - flexGrow: 0, - padding: theme.spacing(2), - paddingBottom: 0 - }, footerContainer: { display: 'flex', alignItems: 'center', @@ -60,47 +55,47 @@ const styles = (theme) => ({ } }) -function mapStateToProps(state){ +function mapStateToProps(state) { return { session: selectors.getSession(state) } } class Grid extends React.Component { - - constructor(props){ + constructor(props) { super(props) const sortDefault = _.isFunction(props.data) ? false : true - this.columnsArray = props.columns.map(column => ({ + let columns = props.columns.map(column => ({ ...column, label: column.label || _.upperFirst(column.field), render: column.render || (row => _.get(row, column.field)), - sort: column.sort === undefined ? sortDefault : Boolean(column.sort) + sort: column.sort === undefined ? sortDefault : Boolean(column.sort), + visible: true })) - this.columnsMap = _.keyBy(props.columns, 'field') this.state = { loaded: false, - filter: this.props.filter || '', + filters: this.props.filters || {}, page: 0, pageSize: 10, - visibleColumns: Object.keys(this.columnsMap), + columns: columns, columnConfigEl: null } this.handleFilterChange = this.handleFilterChange.bind(this) - this.handleColumnsChange = this.handleColumnsChange.bind(this) + this.handleColumnVisibleChange = this.handleColumnVisibleChange.bind(this) + this.handleColumnOrderChange = this.handleColumnOrderChange.bind(this) this.handlePageChange = this.handlePageChange.bind(this) this.handlePageSizeChange = this.handlePageSizeChange.bind(this) } - componentDidMount(){ + componentDidMount() { this.load() } - load(){ + load() { this.loadSettings().then(() => { this.loadData().then(() => { this.setState(() => ({ @@ -110,10 +105,10 @@ class Grid extends React.Component { }) } - loadData(){ - if(_.isFunction(this.props.data)){ + loadData() { + if (_.isFunction(this.props.data)) { let loadConfig = { - filter: this.state.filter, + filters: this.state.filters, page: this.state.page, pageSize: this.state.pageSize, sort: this.state.sort, @@ -125,7 +120,7 @@ class Grid extends React.Component { totalCount })) }) - }else if(!this.state.loaded){ + } else if (!this.state.loaded) { this.setState(() => ({ objects: this.props.data })) @@ -133,7 +128,7 @@ class Grid extends React.Component { return Promise.resolve() } - loadSettings(){ + loadSettings() { let id = new dto.PersonPermId(this.props.session.userName) let fo = new dto.PersonFetchOptions() fo.withWebAppSettings(ids.WEB_APP_ID).withAllSettings() @@ -141,13 +136,31 @@ class Grid extends React.Component { return facade.getPersons([id], fo).then(map => { let person = map[id] let webAppSettings = person.webAppSettings[ids.WEB_APP_ID] - if(webAppSettings && webAppSettings.settings){ + if (webAppSettings && webAppSettings.settings) { let gridSettings = webAppSettings.settings[this.props.id] - if(gridSettings){ + if (gridSettings) { let settings = JSON.parse(gridSettings.value) - if(settings){ + if (settings) { + let newColumns = [...this.state.columns] + newColumns.sort((c1, c2) => { + let index1 = _.findIndex(settings.columns, ['field', c1.field]) + let index2 = _.findIndex(settings.columns, ['field', c2.field]) + return index1 - index2 + }) + newColumns = newColumns.map(column => { + let setting = _.find(settings.columns, ['field', column.field]) + if (setting) { + return { + ...column, + visible: setting.visible + } + } else { + return column + } + }) this.setState(() => ({ - ...settings + ...settings, + columns: newColumns })) } } @@ -155,12 +168,17 @@ class Grid extends React.Component { }) } - saveSettings(){ + saveSettings() { + let columns = this.state.columns.map(column => ({ + field: column.field, + visible: column.visible + })) + let settings = { pageSize: this.state.pageSize, sort: this.state.sort, sortDirection: this.state.sortDirection, - visibleColumns: this.state.visibleColumns + columns } let gridSettings = new dto.WebAppSettingCreation() @@ -174,114 +192,174 @@ class Grid extends React.Component { facade.updatePersons([update]) } - handleFilterChange(filter){ - this.setState(() => ({ - page: 0, - filter - }), () => { - this.loadData() - }) + handleFilterChange(column, filter) { + let filters = { + ...this.state.filters, + [column]: filter + } + + this.setState( + () => ({ + page: 0, + filters + }), + () => { + this.loadData() + } + ) } - handleColumnsChange(visibleColumns){ - this.setState(() => ({ - visibleColumns - }), () => { - this.saveSettings() + handleColumnVisibleChange(field) { + let columns = this.state.columns.map(column => { + if (column.field === field) { + return { + ...column, + visible: !column.visible + } + } else { + return column + } }) + + this.setState( + () => ({ + columns + }), + () => { + this.saveSettings() + } + ) } - handleSortChange(column){ - if(!column.sort){ + handleColumnOrderChange(sourceField, targetField) { + let columns = [...this.state.columns] + let sourceIndex = _.findIndex(columns, ['field', sourceField]) + let targetIndex = _.findIndex(columns, ['field', targetField]) + + if (sourceIndex !== -1 && targetIndex !== -1) { + let temp = columns[sourceIndex] + columns[sourceIndex] = columns[targetIndex] + columns[targetIndex] = temp + } + + this.setState( + () => ({ + columns + }), + () => { + this.saveSettings() + } + ) + } + + handleSortChange(column) { + if (!column.sort) { return } return () => { - this.setState((prevState) => ({ - sort: column.field, - sortDirection: prevState.sortDirection === 'asc' ? 'desc' : 'asc' - }), () => { - this.saveSettings() - this.loadData() - }) + this.setState( + prevState => ({ + sort: column.field, + sortDirection: prevState.sortDirection === 'asc' ? 'desc' : 'asc' + }), + () => { + this.saveSettings() + this.loadData() + } + ) } } - handlePageChange(page){ - this.setState(() => ({ - page - }), () => { - this.loadData() - }) + handlePageChange(page) { + this.setState( + () => ({ + page + }), + () => { + this.loadData() + } + ) } - handlePageSizeChange(pageSize){ - this.setState(() => ({ - page: 0, - pageSize - }), () => { - this.saveSettings() - this.loadData() - }) + handlePageSizeChange(pageSize) { + this.setState( + () => ({ + page: 0, + pageSize + }), + () => { + this.saveSettings() + this.loadData() + } + ) } - filter(objects){ - const filter = this.state.filter ? this.state.filter.trim().toUpperCase() : null - - function matches(value){ - if(filter){ - return value ? value.trim().toUpperCase().includes(filter) : false - }else{ + filter(objects) { + function matches(value, filter) { + if (filter) { + return value + ? value + .trim() + .toUpperCase() + .includes(filter.trim().toUpperCase()) + : false + } else { return true } } return _.filter(objects, row => { - return this.state.visibleColumns.some(visibleColumn => { - let column = this.columnsMap[visibleColumn] + let matchesAll = true + this.state.columns.forEach(column => { let value = _.get(row, column.field) - return matches(value) + let filter = this.state.filters[column.field] + matchesAll = matchesAll && matches(value, filter) }) + return matchesAll }) } - sort(objects){ + sort(objects) { const { sort, sortDirection } = this.state - if(sort){ - return objects.sort((t1, t2)=>{ + if (sort) { + let column = _.find(this.state.columns, ['field', sort]) + return objects.sort((t1, t2) => { let sign = sortDirection === 'asc' ? 1 : -1 - let column = this.columnsMap[sort] let v1 = _.get(t1, column.field) || '' let v2 = _.get(t2, column.field) || '' return sign * v1.localeCompare(v2) }) - }else{ + } else { return objects } } - page(objects){ + page(objects) { const { page, pageSize } = this.state - return objects.slice(page*pageSize, Math.min(objects.length, (page+1)*pageSize)) + return objects.slice( + page * pageSize, + Math.min(objects.length, (page + 1) * pageSize) + ) } render() { logger.log(logger.DEBUG, 'Grid.render') - if(!this.state.loaded){ + if (!this.state.loaded) { return <React.Fragment /> } const { classes } = this.props - const { page, pageSize, filter, visibleColumns } = this.state + const { page, pageSize, columns } = this.state let pagedObjects = null let totalCount = null - if(_.isFunction(this.props.data)){ + if (_.isFunction(this.props.data)) { pagedObjects = this.state.objects totalCount = this.state.totalCount - }else{ + } else { const filteredObjects = this.filter([...this.state.objects]) const sortedObjects = this.sort(filteredObjects) pagedObjects = this.page(sortedObjects) @@ -290,28 +368,21 @@ class Grid extends React.Component { return ( <div className={classes.container}> - <div className={classes.headerContainer}> - <FilterField - filter={filter} - filterChange={this.handleFilterChange} - /> - </div> <div className={classes.tableContainer}> <Table classes={{ root: classes.table }}> <TableHead classes={{ root: classes.tableHeader }}> <TableRow> - {this.columnsArray.map(column => - this.renderHeaderCell(column) - )} + {columns.map(column => this.renderFilterCell(column))} + </TableRow> + <TableRow> + {columns.map(column => this.renderHeaderCell(column))} </TableRow> </TableHead> <TableBody> {pagedObjects.map(row => { return ( <TableRow key={row.id} hover> - {this.columnsArray.map(column => - this.renderRowCell(column, row) - )} + {columns.map(column => this.renderRowCell(column, row))} </TableRow> ) })} @@ -330,20 +401,20 @@ class Grid extends React.Component { onPageSizeChange={this.handlePageSizeChange} /> <ColumnConfig - allColumns={this.columnsArray} - visibleColumns={visibleColumns} - onColumnsChange={this.handleColumnsChange} + columns={columns} + onVisibleChange={this.handleColumnVisibleChange} + onOrderChange={this.handleColumnOrderChange} /> </div> </div> ) } - renderHeaderCell(column){ - const { visibleColumns, sort, sortDirection } = this.state + renderHeaderCell(column) { + const { sort, sortDirection } = this.state - if(visibleColumns.includes(column.field)){ - if(column.sort){ + if (column.visible) { + if (column.sort) { return ( <TableCell key={column.field}> <TableSortLabel @@ -355,36 +426,50 @@ class Grid extends React.Component { </TableSortLabel> </TableCell> ) - }else{ - return ( - <TableCell key={column.field}> - {column.label} - </TableCell> - ) + } else { + return <TableCell key={column.field}>{column.label}</TableCell> } - }else{ + } else { return null } } - renderRowCell(column, row){ - const { visibleColumns } = this.state + renderFilterCell(column) { + const { filters } = this.state - if(visibleColumns.includes(column.field)){ - let rendered = column.render(row) + if (column.visible) { + let filter = filters[column.field] || '' + let filterChange = filter => { + this.handleFilterChange(column.field, filter) + } return ( <TableCell key={column.field}> - {rendered ? rendered : <span> </span> } + <FilterField filter={filter} filterChange={filterChange} /> </TableCell> ) - }else{ + } else { return null } } + renderRowCell(column, row) { + if (column.visible) { + let rendered = column.render(row) + return ( + <TableCell key={column.field}> + {rendered ? rendered : <span> </span>} + </TableCell> + ) + } else { + return null + } + } } export default _.flow( - connect(mapStateToProps, null), + connect( + mapStateToProps, + null + ), withStyles(styles) )(Grid) diff --git a/openbis_ng_ui/src/components/common/grid/PageConfig.jsx b/openbis_ng_ui/src/components/common/grid/PageConfig.jsx index bc1ff16bceac85e4465a96f4db3ff623cabdd6a2..9dedc1e8f18abdfbf47ce82c271b89c6fb2bab96 100644 --- a/openbis_ng_ui/src/components/common/grid/PageConfig.jsx +++ b/openbis_ng_ui/src/components/common/grid/PageConfig.jsx @@ -1,6 +1,6 @@ import _ from 'lodash' import React from 'react' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import Typography from '@material-ui/core/Typography' import TextField from '@material-ui/core/TextField' import FormControlLabel from '@material-ui/core/FormControlLabel' @@ -33,8 +33,7 @@ const styles = () => ({ }) class PageConfig extends React.Component { - - constructor(props){ + constructor(props) { super(props) this.handlePageSizeChange = this.handlePageSizeChange.bind(this) this.handleFirstPageButtonClick = this.handleFirstPageButtonClick.bind(this) @@ -43,7 +42,7 @@ class PageConfig extends React.Component { this.handleLastPageButtonClick = this.handleLastPageButtonClick.bind(this) } - handlePageSizeChange(event){ + handlePageSizeChange(event) { this.props.onPageSizeChange(event.target.value) } @@ -60,13 +59,15 @@ class PageConfig extends React.Component { } handleLastPageButtonClick() { - this.props.onPageChange(Math.max(0, Math.ceil(this.props.count / this.props.pageSize) - 1)) + this.props.onPageChange( + Math.max(0, Math.ceil(this.props.count / this.props.pageSize) - 1) + ) } render() { logger.log(logger.DEBUG, 'PageConfig.render') - const { classes, count, page, pageSize} = this.props + const { classes, count, page, pageSize } = this.props return ( <div className={classes.container}> @@ -82,7 +83,9 @@ class PageConfig extends React.Component { onChange={this.handlePageSizeChange} > {[5, 10, 20, 50, 100].map(pageSize => ( - <option key={pageSize} value={pageSize}>{pageSize}</option> + <option key={pageSize} value={pageSize}> + {pageSize} + </option> ))} </TextField> } @@ -90,37 +93,42 @@ class PageConfig extends React.Component { label: classes.pageSizeLabel, labelPlacementStart: classes.pageSizeLabelPlacement }} - label="Rows per page: " - labelPlacement="start" + label='Rows per page: ' + labelPlacement='start' /> </div> <div className={classes.pageRange}> <Typography> - {Math.min(count, page * pageSize + 1)}-{Math.min(count, (page + 1) * pageSize)} of {count} + {Math.min(count, page * pageSize + 1)}- + {Math.min(count, (page + 1) * pageSize)} of {count} </Typography> </div> <div className={classes.pageButtons}> <IconButton onClick={this.handleFirstPageButtonClick} disabled={page === 0} - aria-label="First Page" + aria-label='First Page' > <FirstPageIcon /> </IconButton> - <IconButton onClick={this.handleBackButtonClick} disabled={page === 0} aria-label="Previous Page"> + <IconButton + onClick={this.handleBackButtonClick} + disabled={page === 0} + aria-label='Previous Page' + > <KeyboardArrowLeft /> </IconButton> <IconButton onClick={this.handleNextButtonClick} disabled={page >= Math.ceil(count / pageSize) - 1} - aria-label="Next Page" + aria-label='Next Page' > <KeyboardArrowRight /> </IconButton> <IconButton onClick={this.handleLastPageButtonClick} disabled={page >= Math.ceil(count / pageSize) - 1} - aria-label="Last Page" + aria-label='Last Page' > <LastPageIcon /> </IconButton> @@ -128,9 +136,6 @@ class PageConfig extends React.Component { </div> ) } - } -export default _.flow( - withStyles(styles) -)(PageConfig) +export default _.flow(withStyles(styles))(PageConfig) diff --git a/openbis_ng_ui/src/components/common/loading/Loading.jsx b/openbis_ng_ui/src/components/common/loading/Loading.jsx index 17951d7dbabfd8857a0bb3b42f9fb2a0ddee44d7..fb9c8214f024a020826f5236f226de160cd0e2cf 100644 --- a/openbis_ng_ui/src/components/common/loading/Loading.jsx +++ b/openbis_ng_ui/src/components/common/loading/Loading.jsx @@ -1,9 +1,9 @@ import React from 'react' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import CircularProgress from '@material-ui/core/CircularProgress' import logger from '../../../common/logger.js' -const styles = (theme) => ({ +const styles = theme => ({ loader: { position: 'absolute', width: '100%', @@ -11,7 +11,7 @@ const styles = (theme) => ({ zIndex: 1000, backgroundColor: theme.palette.background.paper, opacity: 0.8, - textAlign: 'center', + textAlign: 'center' }, progress: { position: 'absolute', @@ -21,7 +21,6 @@ const styles = (theme) => ({ }) class Loading extends React.Component { - render() { logger.log(logger.DEBUG, 'Loading.render') @@ -29,12 +28,11 @@ class Loading extends React.Component { return ( <div> - { - this.props.loading && + {this.props.loading && ( <div className={classes.loader}> - <CircularProgress className={classes.progress}/> + <CircularProgress className={classes.progress} /> </div> - } + )} {this.props.children} </div> ) diff --git a/openbis_ng_ui/src/components/common/menu/Menu.jsx b/openbis_ng_ui/src/components/common/menu/Menu.jsx index ff4b7684e227720e25a0a73bd16920bb12c50d59..acbf147e7d82f001100394afd1809cc2a6c3eaf0 100644 --- a/openbis_ng_ui/src/components/common/menu/Menu.jsx +++ b/openbis_ng_ui/src/components/common/menu/Menu.jsx @@ -10,22 +10,22 @@ import SearchIcon from '@material-ui/icons/Search' import CloseIcon from '@material-ui/icons/Close' import LogoutIcon from '@material-ui/icons/PowerSettingsNew' import { fade } from '@material-ui/core/styles/colorManipulator' -import {connect} from 'react-redux' -import {withStyles} from '@material-ui/core/styles' +import { connect } from 'react-redux' +import { withStyles } from '@material-ui/core/styles' import logger from '../../../common/logger.js' import * as actions from '../../../store/actions/actions.js' import * as selectors from '../../../store/selectors/selectors.js' import * as pages from '../../../common/consts/pages.js' -const styles = (theme) => ({ +const styles = theme => ({ tabs: { - flexGrow: 1, + flexGrow: 1 }, search: { color: theme.palette.background.paper, backgroundColor: fade(theme.palette.background.paper, 0.15), '&:hover': { - backgroundColor: fade(theme.palette.background.paper, 0.25), + backgroundColor: fade(theme.palette.background.paper, 0.25) }, borderRadius: theme.shape.borderRadius, paddingLeft: theme.spacing(1), @@ -34,8 +34,8 @@ const styles = (theme) => ({ transition: theme.transitions.create('width'), width: '200px', '&:focus-within': { - width: '300px', - }, + width: '300px' + } }, searchIcon: { paddingLeft: theme.spacing(1) / 2, @@ -45,28 +45,27 @@ const styles = (theme) => ({ searchClear: { cursor: 'pointer' } - }) -function mapStateToProps(state){ +function mapStateToProps(state) { return { currentPage: selectors.getCurrentPage(state), searchText: selectors.getSearch(state) } } -function mapDispatchToProps(dispatch, ownProps){ +function mapDispatchToProps(dispatch, ownProps) { return { - currentPageChange: (event, value) => dispatch(actions.currentPageChange(value)), - searchChange: (value) => dispatch(actions.searchChange(value)), - search: (value) => dispatch(actions.search(ownProps.page, value)), + currentPageChange: (event, value) => + dispatch(actions.currentPageChange(value)), + searchChange: value => dispatch(actions.searchChange(value)), + search: value => dispatch(actions.search(ownProps.page, value)), logout: () => dispatch(actions.logout()) } } class Menu extends React.Component { - - constructor(props){ + constructor(props) { super(props) this.searchRef = React.createRef() this.handleSearchChange = this.handleSearchChange.bind(this) @@ -74,17 +73,17 @@ class Menu extends React.Component { this.handleSearchClear = this.handleSearchClear.bind(this) } - handleSearchChange(event){ + handleSearchChange(event) { this.props.searchChange(event.target.value) } - handleSearchKeyPress(event){ - if(event.key === 'Enter'){ + handleSearchKeyPress(event) { + if (event.key === 'Enter') { this.props.search(this.props.searchText) } } - handleSearchClear(event){ + handleSearchClear(event) { event.preventDefault() this.props.searchChange('') this.searchRef.current.focus() @@ -93,19 +92,21 @@ class Menu extends React.Component { render() { logger.log(logger.DEBUG, 'Menu.render') - const {classes, searchText} = this.props + const { classes, searchText } = this.props return ( - <AppBar position="static"> + <AppBar position='static'> <Toolbar> - <Tabs value={this.props.currentPage} + <Tabs + value={this.props.currentPage} onChange={this.props.currentPageChange} - classes={{root: classes.tabs}}> - <Tab value={pages.TYPES} label="Types"/> - <Tab value={pages.USERS} label="Users"/> + classes={{ root: classes.tabs }} + > + <Tab value={pages.TYPES} label='Types' /> + <Tab value={pages.USERS} label='Users' /> </Tabs> <TextField - placeholder="Search..." + placeholder='Search...' value={searchText} onChange={this.handleSearchChange} onKeyPress={this.handleSearchKeyPress} @@ -115,22 +116,24 @@ class Menu extends React.Component { startAdornment: this.renderSearchIcon(), endAdornment: this.renderSearchClearIcon(), classes: { - root: classes.search, + root: classes.search } - }}/> + }} + /> <Button - variant="contained" - color="primary" - onClick={this.props.logout}> - <LogoutIcon/> + variant='contained' + color='primary' + onClick={this.props.logout} + > + <LogoutIcon /> </Button> </Toolbar> </AppBar> ) } - renderSearchIcon(){ - const {classes} = this.props + renderSearchIcon() { + const { classes } = this.props return ( <InputAdornment> <SearchIcon classes={{ root: classes.searchIcon }} /> @@ -138,9 +141,9 @@ class Menu extends React.Component { ) } - renderSearchClearIcon(){ - const {classes, searchText} = this.props - if(searchText){ + renderSearchClearIcon() { + const { classes, searchText } = this.props + if (searchText) { return ( <InputAdornment> <CloseIcon @@ -149,11 +152,13 @@ class Menu extends React.Component { /> </InputAdornment> ) - }else{ - return (<React.Fragment></React.Fragment>) + } else { + return <React.Fragment></React.Fragment> } } - } -export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(Menu)) +export default connect( + mapStateToProps, + mapDispatchToProps +)(withStyles(styles)(Menu)) diff --git a/openbis_ng_ui/src/components/login/Login.jsx b/openbis_ng_ui/src/components/login/Login.jsx index b9211c38390b432fd83cc3248721244b6bb54898..87ca2fa6b0aaeb9db18f7650941dcf59c47271e4 100644 --- a/openbis_ng_ui/src/components/login/Login.jsx +++ b/openbis_ng_ui/src/components/login/Login.jsx @@ -1,6 +1,6 @@ import React from 'react' -import {withStyles} from '@material-ui/core/styles' -import {connect} from 'react-redux' +import { withStyles } from '@material-ui/core/styles' +import { connect } from 'react-redux' import flow from 'lodash/flow' import Button from '@material-ui/core/Button' @@ -17,39 +17,37 @@ const styles = { marginTop: '10%', marginBottom: '10em', width: '30em', - margin: '0 auto', + margin: '0 auto' }, textField: { - width: '100%', + width: '100%' }, button: { - marginTop: '1em', + marginTop: '1em' }, container: { width: '100%', height: '100%', - overflow: 'auto', - }, + overflow: 'auto' + } } function mapStateToProps() { - return { - } + return {} } function mapDispatchToProps(dispatch) { return { - login: (user, password) => dispatch(actions.login(user, password)), + login: (user, password) => dispatch(actions.login(user, password)) } } class WithLogin extends React.Component { - state = {} handleChange = name => event => { this.setState({ - [name]: event.target.value, + [name]: event.target.value }) } @@ -74,41 +72,39 @@ class WithLogin extends React.Component { <form> <Card className={classes.card}> <CardContent> - <Typography variant="h6"> - Login - </Typography> + <Typography variant='h6'>Login</Typography> <TextField - id="standard-name" - label="User" + id='standard-name' + label='User' className={classes.textField} - margin="normal" - autoComplete="username" + margin='normal' + autoComplete='username' autoFocus={true} - onKeyPress={(e) => { + onKeyPress={e => { this.keyPress(e) }} onChange={this.handleChange('user')} /> <TextField - id="standard-password-input" - label="Password" + id='standard-password-input' + label='Password' className={classes.textField} - type="password" - autoComplete="current-password" - margin="normal" - onKeyPress={(e) => { + type='password' + autoComplete='current-password' + margin='normal' + onKeyPress={e => { this.keyPress(e) }} onChange={this.handleChange('password')} /> <Button onClick={this.login} - color="primary" + color='primary' className={classes.button} - variant="contained"> + variant='contained' + > Login </Button> - </CardContent> </Card> </form> @@ -119,6 +115,9 @@ class WithLogin extends React.Component { } export default flow( - connect(mapStateToProps, mapDispatchToProps), + connect( + mapStateToProps, + mapDispatchToProps + ), withStyles(styles) )(WithLogin) diff --git a/openbis_ng_ui/src/components/types/Types.jsx b/openbis_ng_ui/src/components/types/Types.jsx index 3c694179b2a9f5e4b59b19157208fa8c8248a9c8..ebd8f3d308988810cf2bea7ca5b94b555e2b8f08 100644 --- a/openbis_ng_ui/src/components/types/Types.jsx +++ b/openbis_ng_ui/src/components/types/Types.jsx @@ -1,5 +1,5 @@ import React from 'react' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import logger from '../../common/logger.js' import * as pages from '../../common/consts/pages.js' import * as objectType from '../../common/consts/objectType.js' @@ -17,7 +17,7 @@ const styles = () => ({ container: { display: 'flex', width: '100%' - }, + } }) const objectTypeToComponent = { @@ -29,7 +29,6 @@ const objectTypeToComponent = { } class Types extends React.Component { - render() { logger.log(logger.DEBUG, 'Types.render') @@ -37,12 +36,14 @@ class Types extends React.Component { return ( <div className={classes.container}> - <Browser page={pages.TYPES}/> - <Content page={pages.TYPES} objectTypeToComponent={objectTypeToComponent}/> + <Browser page={pages.TYPES} /> + <Content + page={pages.TYPES} + objectTypeToComponent={objectTypeToComponent} + /> </div> ) } - } export default withStyles(styles)(Types) diff --git a/openbis_ng_ui/src/components/types/collectionType/CollectionType.jsx b/openbis_ng_ui/src/components/types/collectionType/CollectionType.jsx index c15e8e68333d6f5fdf3453475bc273eccbad4025..cd7c770d82f7e0459f7ef88baf66b24f95202680 100644 --- a/openbis_ng_ui/src/components/types/collectionType/CollectionType.jsx +++ b/openbis_ng_ui/src/components/types/collectionType/CollectionType.jsx @@ -2,12 +2,10 @@ import React from 'react' import logger from '../../../common/logger.js' class CollectionType extends React.Component { - render() { logger.log(logger.DEBUG, 'CollectionType.render') return <div>CollectionType</div> } - } export default CollectionType diff --git a/openbis_ng_ui/src/components/types/dataSetType/DataSetType.jsx b/openbis_ng_ui/src/components/types/dataSetType/DataSetType.jsx index ee4f5aa81ff8b56c688f64544956b0cc112de281..ca7fb3f06693bbcb3a1dcdce618fc2f34586fd68 100644 --- a/openbis_ng_ui/src/components/types/dataSetType/DataSetType.jsx +++ b/openbis_ng_ui/src/components/types/dataSetType/DataSetType.jsx @@ -2,12 +2,10 @@ import React from 'react' import logger from '../../../common/logger.js' class DataSetType extends React.Component { - render() { logger.log(logger.DEBUG, 'DataSetType.render') return <div>DataSetType</div> } - } export default DataSetType diff --git a/openbis_ng_ui/src/components/types/materialType/MaterialType.jsx b/openbis_ng_ui/src/components/types/materialType/MaterialType.jsx index 8493629f2cf2ebb658724681d7fd96344c9added..34a6831ea5bd70ec2072d913693f837e0279c6b3 100644 --- a/openbis_ng_ui/src/components/types/materialType/MaterialType.jsx +++ b/openbis_ng_ui/src/components/types/materialType/MaterialType.jsx @@ -2,12 +2,10 @@ import React from 'react' import logger from '../../../common/logger.js' class MaterialType extends React.Component { - render() { logger.log(logger.DEBUG, 'MaterialType.render') return <div>MaterialType</div> } - } export default MaterialType diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectType.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectType.jsx index 96d78d7f7eb672da9d1dedf8faeb2eb5106ac117..934eef36cccf271a187a64762cd9475433db1730 100644 --- a/openbis_ng_ui/src/components/types/objectType/ObjectType.jsx +++ b/openbis_ng_ui/src/components/types/objectType/ObjectType.jsx @@ -1,16 +1,16 @@ import _ from 'lodash' import React from 'react' -import {connect} from 'react-redux' -import {withStyles} from '@material-ui/core/styles' +import { connect } from 'react-redux' +import { withStyles } from '@material-ui/core/styles' import ObjectTypeForm from './ObjectTypeForm.jsx' import ObjectTypeFooter from './ObjectTypeFooter.jsx' import * as pages from '../../../common/consts/pages.js' import * as objectTypes from '../../../common/consts/objectType.js' import * as actions from '../../../store/actions/actions.js' import logger from '../../../common/logger.js' -import {facade, dto} from '../../../services/openbis.js' +import { facade, dto } from '../../../services/openbis.js' -const styles = (theme) => ({ +const styles = theme => ({ container: { height: '100%', display: 'flex', @@ -27,19 +27,29 @@ const styles = (theme) => ({ } }) -function mapDispatchToProps(dispatch, ownProps){ +function mapDispatchToProps(dispatch, ownProps) { return { - error: (error) => { dispatch(actions.setError(error)) }, - objectChange: (changed) => { dispatch(actions.objectChange(pages.TYPES, objectTypes.OBJECT_TYPE, ownProps.objectId, changed)) } + error: error => { + dispatch(actions.setError(error)) + }, + objectChange: changed => { + dispatch( + actions.objectChange( + pages.TYPES, + objectTypes.OBJECT_TYPE, + ownProps.objectId, + changed + ) + ) + } } } class ObjectType extends React.Component { - - constructor(props){ + constructor(props) { super(props) this.state = { - loaded: false, + loaded: false } this.handleChange = this.handleChange.bind(this) this.handleAdd = this.handleAdd.bind(this) @@ -49,31 +59,38 @@ class ObjectType extends React.Component { this.handleSave = this.handleSave.bind(this) } - componentDidMount(){ + componentDidMount() { this.load() } - componentDidUpdate(){ - if(!this.state.objectType){ + componentDidUpdate() { + if (!this.state.objectType) { return } - const changed = this.state.objectType.properties.some((property) => { - return !property.original || + const changed = this.state.objectType.properties.some(property => { + return ( + !property.original || !_.isEqual(property.mandatory, property.original.mandatory) || - !_.isEqual(property.propertyType ? property.propertyType.code : null, property.original.propertyType ? property.original.propertyType.code : null) || + !_.isEqual( + property.propertyType ? property.propertyType.code : null, + property.original.propertyType + ? property.original.propertyType.code + : null + ) || !_.isEqual(property.ordinal, property.original.ordinal) + ) }) - if(changed !== this.state.changed){ - this.setState(()=>({ + if (changed !== this.state.changed) { + this.setState(() => ({ changed })) this.props.objectChange(changed) } } - load(){ + load() { this.setState({ loaded: false }) @@ -90,38 +107,46 @@ class ObjectType extends React.Component { }) } - loadObjectType(objectTypeId){ + loadObjectType(objectTypeId) { let id = new dto.EntityTypePermId(objectTypeId) let fo = new dto.SampleTypeFetchOptions() - fo.withPropertyAssignments().withPropertyType().withMaterialType() - fo.withPropertyAssignments().withPropertyType().withVocabulary() - fo.withPropertyAssignments().sortBy().ordinal() + fo.withPropertyAssignments() + .withPropertyType() + .withMaterialType() + fo.withPropertyAssignments() + .withPropertyType() + .withVocabulary() + fo.withPropertyAssignments() + .sortBy() + .ordinal() return facade.getSampleTypes([id], fo).then(map => { let objectType = map[objectTypeId] - if(objectType){ + if (objectType) { return { code: objectType.code, - properties: objectType.propertyAssignments.map((assignment, index) => ({ - id: index + 1, - ordinal: index + 1, - propertyType: assignment.propertyType, - mandatory: assignment.mandatory, - original: { - ...assignment, - ordinal: index + 1 - }, - selected: false, - errors: {} - })) + properties: objectType.propertyAssignments.map( + (assignment, index) => ({ + id: index + 1, + ordinal: index + 1, + propertyType: assignment.propertyType, + mandatory: assignment.mandatory, + original: { + ...assignment, + ordinal: index + 1 + }, + selected: false, + errors: {} + }) + ) } - }else{ + } else { return null } }) } - loadPropertyTypes(){ + loadPropertyTypes() { let criteria = new dto.PropertyTypeSearchCriteria() let fo = new dto.PropertyTypeFetchOptions() fo.withVocabulary().withTerms() @@ -132,40 +157,46 @@ class ObjectType extends React.Component { }) } - handleChange(id, key, value){ - this.setState((prevState) => { - let newProperties = prevState.objectType.properties.map((property) => { - if(property.id === id){ - return { - ...property, - [key]: value + handleChange(id, key, value) { + this.setState( + prevState => { + let newProperties = prevState.objectType.properties.map(property => { + if (property.id === id) { + return { + ...property, + [key]: value + } + } else { + return property + } + }) + return { + ...prevState, + objectType: { + ...prevState.objectType, + properties: newProperties } - }else{ - return property } - }) - return { - ...prevState, - objectType: { - ...prevState.objectType, - properties: newProperties + }, + () => { + if (this.isObjectTypeValidated()) { + this.validate() } } - }, () => { - if(this.isObjectTypeValidated()){ - this.validate() - } - }) + ) } - handleRemove(){ - this.setState((prevState) => { - let newProperties = prevState.objectType.properties.reduce((array, property) => { - if(!property.selected){ - array.push(property) - } - return array - }, []) + handleRemove() { + this.setState(prevState => { + let newProperties = prevState.objectType.properties.reduce( + (array, property) => { + if (!property.selected) { + array.push(property) + } + return array + }, + [] + ) return { ...prevState, @@ -177,8 +208,8 @@ class ObjectType extends React.Component { }) } - handleAdd(){ - this.setState((prevState) => { + handleAdd() { + this.setState(prevState => { let newProperties = prevState.objectType.properties.map(property => { return { ...property, @@ -204,8 +235,8 @@ class ObjectType extends React.Component { }) } - handleSelect(id){ - this.setState((prevState) => ({ + handleSelect(id) { + this.setState(prevState => ({ ...prevState, objectType: { ...prevState.objectType, @@ -219,11 +250,11 @@ class ObjectType extends React.Component { })) } - handleReorder(oldPropertyIndex, newPropertyIndex){ + handleReorder(oldPropertyIndex, newPropertyIndex) { let oldProperties = this.state.objectType.properties - let newProperties = [ ...oldProperties ] + let newProperties = [...oldProperties] - let [ property ] = newProperties.splice(oldPropertyIndex, 1) + let [property] = newProperties.splice(oldPropertyIndex, 1) newProperties.splice(newPropertyIndex, 0, property) newProperties = newProperties.map((property, index) => { return { @@ -233,7 +264,7 @@ class ObjectType extends React.Component { } }) - this.setState((prevState) => ({ + this.setState(prevState => ({ ...prevState, objectType: { ...prevState.objectType, @@ -242,17 +273,17 @@ class ObjectType extends React.Component { })) } - validate(){ + validate() { let valid = true let newProperties = this.state.objectType.properties.map(property => { let errors = {} - if(!property.propertyType){ + if (!property.propertyType) { errors['propertyType'] = 'Cannot be empty' } - if(_.size(errors) > 0){ + if (_.size(errors) > 0) { valid = false } @@ -262,7 +293,7 @@ class ObjectType extends React.Component { } }) - this.setState((prevState) => ({ + this.setState(prevState => ({ ...prevState, validated: true, objectType: { @@ -274,8 +305,8 @@ class ObjectType extends React.Component { return valid } - handleSave(){ - if(this.validate()){ + handleSave() { + if (this.validate()) { let update = new dto.SampleTypeUpdate() update.setTypeId(new dto.EntityTypePermId(this.props.objectId)) @@ -284,18 +315,29 @@ class ObjectType extends React.Component { this.state.objectType.properties.forEach(property => { let newProperty = new dto.PropertyAssignmentCreation() - if(property.original && property.propertyType.code === property.original.propertyType.code){ + if ( + property.original && + property.propertyType.code === property.original.propertyType.code + ) { newProperty.ordinal = property.ordinal - newProperty.propertyTypeId = new dto.PropertyTypePermId(property.original.propertyType.code) + newProperty.propertyTypeId = new dto.PropertyTypePermId( + property.original.propertyType.code + ) newProperty.section = property.original.section - newProperty.pluginId = property.original.plugin ? new dto.PluginPermId(property.original.plugin.name) : null - newProperty.initialValueForExistingEntities = property.original.initialValueForExistingEntities + newProperty.pluginId = property.original.plugin + ? new dto.PluginPermId(property.original.plugin.name) + : null + newProperty.initialValueForExistingEntities = + property.original.initialValueForExistingEntities newProperty.showInEditView = property.original.showInEditView - newProperty.showRawValueInForms = property.original.showRawValueInForms + newProperty.showRawValueInForms = + property.original.showRawValueInForms newProperty.mandatory = property.mandatory - }else{ + } else { newProperty.ordinal = property.ordinal - newProperty.propertyTypeId = new dto.PropertyTypePermId(property.propertyType.code) + newProperty.propertyTypeId = new dto.PropertyTypePermId( + property.propertyType.code + ) newProperty.mandatory = property.mandatory } @@ -304,18 +346,21 @@ class ObjectType extends React.Component { update.getPropertyAssignments().set(newProperties) - facade.updateSampleTypes([update]).then(()=>{ - this.load() - }, (error) => { - this.props.error(error) - }) + facade.updateSampleTypes([update]).then( + () => { + this.load() + }, + error => { + this.props.error(error) + } + ) } } render() { logger.log(logger.DEBUG, 'ObjectType.render') - if(!this.state.loaded){ + if (!this.state.loaded) { return <div></div> } @@ -345,23 +390,25 @@ class ObjectType extends React.Component { ) } - isPropertySelected(){ - return this.state.objectType.properties.some((property) => { + isPropertySelected() { + return this.state.objectType.properties.some(property => { return property.selected }) } - isObjectTypeValidated(){ + isObjectTypeValidated() { return this.state.validated } - isObjectTypeChanged(){ + isObjectTypeChanged() { return this.state.changed } - } export default _.flow( - connect(null, mapDispatchToProps), + connect( + null, + mapDispatchToProps + ), withStyles(styles) )(ObjectType) diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectTypeFooter.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectTypeFooter.jsx index 04282adde58f352648b291141913cb26ae8b08de..3978a73d8ecb4f48d3da83463905efe2bd8558a9 100644 --- a/openbis_ng_ui/src/components/types/objectType/ObjectTypeFooter.jsx +++ b/openbis_ng_ui/src/components/types/objectType/ObjectTypeFooter.jsx @@ -1,16 +1,15 @@ import React from 'react' import Button from '@material-ui/core/Button' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import logger from '../../../common/logger.js' -const styles = (theme) => ({ +const styles = theme => ({ button: { marginRight: theme.spacing(2) } }) class ObjectTypeFooter extends React.Component { - render() { logger.log(logger.DEBUG, 'ObjectTypeFooter.render') @@ -47,7 +46,6 @@ class ObjectTypeFooter extends React.Component { </div> ) } - } export default withStyles(styles)(ObjectTypeFooter) diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectTypeForm.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectTypeForm.jsx index b30ccca12bab1996f7323399816d2ff950803345..449c8ea9feaaddcce39bb1e998662df3a91ca16e 100644 --- a/openbis_ng_ui/src/components/types/objectType/ObjectTypeForm.jsx +++ b/openbis_ng_ui/src/components/types/objectType/ObjectTypeForm.jsx @@ -2,14 +2,12 @@ import _ from 'lodash' import React from 'react' import ObjectTypeTitle from './ObjectTypeTitle.jsx' import ObjectTypeTable from './ObjectTypeTable.jsx' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import logger from '../../../common/logger.js' -const styles = () => ({ -}) +const styles = () => ({}) class ObjectTypeForm extends React.Component { - render() { logger.log(logger.DEBUG, 'ObjectTypeForm.render') @@ -18,11 +16,9 @@ class ObjectTypeForm extends React.Component { return ( <div> - <ObjectTypeTitle - objectType={objectType} - /> + <ObjectTypeTitle objectType={objectType} /> <form> - {properties && properties.length > 0 && + {properties && properties.length > 0 && ( <ObjectTypeTable objectType={objectType} propertyTypes={propertyTypes} @@ -30,14 +26,11 @@ class ObjectTypeForm extends React.Component { onReorder={this.props.onReorder} onChange={this.props.onChange} /> - } + )} </form> </div> ) } - } -export default _.flow( - withStyles(styles), -)(ObjectTypeForm) +export default _.flow(withStyles(styles))(ObjectTypeForm) diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyCell.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyCell.jsx index efc7b4b50987cc95fa6cfdb736509667374dd950..d80689480c5b4a814156403408503cd2467d0a38 100644 --- a/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyCell.jsx +++ b/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyCell.jsx @@ -1,7 +1,7 @@ import _ from 'lodash' import React from 'react' import TableCell from '@material-ui/core/TableCell' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import logger from '../../../common/logger.js' const styles = () => ({ @@ -11,7 +11,6 @@ const styles = () => ({ }) class ObjectTypePropertyCell extends React.Component { - render() { logger.log(logger.DEBUG, 'ObjectTypePropertyCell.render') @@ -23,9 +22,6 @@ class ObjectTypePropertyCell extends React.Component { </TableCell> ) } - } -export default _.flow( - withStyles(styles) -)(ObjectTypePropertyCell) +export default _.flow(withStyles(styles))(ObjectTypePropertyCell) diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyMandatory.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyMandatory.jsx index 6efb8ab477e59207f17d5b841359013591beee7c..01a61706bdba0d23bf3a5ea623b22e2d14631fd3 100644 --- a/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyMandatory.jsx +++ b/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyMandatory.jsx @@ -2,38 +2,41 @@ import _ from 'lodash' import React from 'react' import Checkbox from '@material-ui/core/Checkbox' import EditableField from '../../common/form/EditableField.jsx' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import logger from '../../../common/logger.js' const styles = () => ({ checkbox: { padding: '0px' - }, + } }) class ObjectTypePropertyMandatory extends React.Component { - - constructor(props){ + constructor(props) { super(props) this.renderField = this.renderField.bind(this) this.handleChange = this.handleChange.bind(this) } - handleChange(event){ + handleChange(event) { event.stopPropagation() - this.props.onChange(this.props.property.id, 'mandatory', event.target.checked) + this.props.onChange( + this.props.property.id, + 'mandatory', + event.target.checked + ) } - render(){ + render() { logger.log(logger.DEBUG, 'ObjectTypePropertyMandatory.render') return <EditableField renderField={this.renderField} /> } - renderField({ref, edited, handleBlur}){ - const {classes, property} = this.props + renderField({ ref, edited, handleBlur }) { + const { classes, property } = this.props - if(edited){ + if (edited) { return ( <Checkbox checked={property.mandatory} @@ -44,15 +47,10 @@ class ObjectTypePropertyMandatory extends React.Component { classes={{ root: classes.checkbox }} /> ) - }else{ - return ( - <span>{property.mandatory ? 'true' : 'false'}</span> - ) + } else { + return <span>{property.mandatory ? 'true' : 'false'}</span> } } - } -export default _.flow( - withStyles(styles), -)(ObjectTypePropertyMandatory) +export default _.flow(withStyles(styles))(ObjectTypePropertyMandatory) diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyPreview.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyPreview.jsx index bf7cc5d1ad825b08c664f8f3d2779550bdae0c97..c28d9cfa36827cae729fa7683383b02bf5bc3519 100644 --- a/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyPreview.jsx +++ b/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyPreview.jsx @@ -5,8 +5,8 @@ import TextField from '@material-ui/core/TextField' import Checkbox from '@material-ui/core/Checkbox' import InfoIcon from '@material-ui/icons/InfoOutlined' import Tooltip from '@material-ui/core/Tooltip' -import {withStyles} from '@material-ui/core/styles' -import {facade, dto} from '../../../services/openbis.js' +import { withStyles } from '@material-ui/core/styles' +import { facade, dto } from '../../../services/openbis.js' import logger from '../../../common/logger.js' const styles = () => ({ @@ -21,48 +21,49 @@ const styles = () => ({ }) class ObjectTypePropertyPreview extends React.Component { - - constructor(props){ + constructor(props) { super(props) this.state = { vocabularyValue: '', - materialValue: '', + materialValue: '' } this.setMaterial = this.setMaterial.bind(this) this.setVocabulary = this.setVocabulary.bind(this) } static getDerivedStateFromProps(props, state) { - if(!state.property - || state.property.propertyType !== props.property.propertyType - || state.property.mandatory !== props.property.mandatory){ + if ( + !state.property || + state.property.propertyType !== props.property.propertyType || + state.property.mandatory !== props.property.mandatory + ) { return { loaded: false, property: props.property, vocabularyTerms: [], materials: [] } - }else{ + } else { return null } } - componentDidMount(){ + componentDidMount() { this.load() } - componentDidUpdate(){ + componentDidUpdate() { this.load() } - load(){ - if(this.state.loaded){ + load() { + if (this.state.loaded) { return } - const {propertyType} = this.state.property + const { propertyType } = this.state.property - switch(propertyType.dataType){ + switch (propertyType.dataType) { case 'CONTROLLEDVOCABULARY': this.loadVocabulary() return @@ -77,13 +78,18 @@ class ObjectTypePropertyPreview extends React.Component { } } - render(){ + render() { logger.log(logger.DEBUG, 'ObjectTypePropertyPreview.render') - const {classes} = this.props + const { classes } = this.props return ( - <div className={classes.container} onClick={(event) => {event.stopPropagation()}}> + <div + className={classes.container} + onClick={event => { + event.stopPropagation() + }} + > {this.renderField()} <Tooltip title={this.getDescription()}> <InfoIcon /> @@ -92,10 +98,10 @@ class ObjectTypePropertyPreview extends React.Component { ) } - renderField(){ - const {propertyType} = this.state.property + renderField() { + const { propertyType } = this.state.property - switch(propertyType.dataType){ + switch (propertyType.dataType) { case 'BOOLEAN': return this.renderBoolean() case 'VARCHAR': @@ -114,53 +120,53 @@ class ObjectTypePropertyPreview extends React.Component { } } - renderBoolean(){ - const {classes} = this.props + renderBoolean() { + const { classes } = this.props return ( - <FormControlLabel classes={{ root: classes.boolean }} + <FormControlLabel + classes={{ root: classes.boolean }} control={<Checkbox />} label={this.getLabel()} /> ) } - renderVarchar(){ + renderVarchar() { return ( - <TextField - label={this.getLabel()} - fullWidth={true} - variant="filled" - /> + <TextField label={this.getLabel()} fullWidth={true} variant='filled' /> ) } - renderMultilineVarchar(){ + renderMultilineVarchar() { return ( <TextField label={this.getLabel()} multiline={true} fullWidth={true} - variant="filled" + variant='filled' /> ) } - renderNumber(){ + renderNumber() { return ( <TextField label={this.getLabel()} - type="number" + type='number' fullWidth={true} - variant="filled" + variant='filled' /> ) } - loadVocabulary(){ + loadVocabulary() { let criteria = new dto.VocabularyTermSearchCriteria() let fo = new dto.VocabularyTermFetchOptions() - criteria.withVocabulary().withCode().thatEquals(this.state.property.propertyType.vocabulary.code) + criteria + .withVocabulary() + .withCode() + .thatEquals(this.state.property.propertyType.vocabulary.code) return facade.searchVocabularyTerms(criteria, fo).then(result => { this.setState(() => ({ @@ -170,45 +176,50 @@ class ObjectTypePropertyPreview extends React.Component { }) } - getVocabulary(){ + getVocabulary() { return this.state.vocabularyValue } - setVocabulary(event){ + setVocabulary(event) { const value = event.target.value this.setState(() => ({ vocabularyValue: value })) } - renderVocabulary(){ + renderVocabulary() { return ( <TextField select SelectProps={{ - native: true, + native: true }} label={this.getLabel()} value={this.getVocabulary()} onChange={this.setVocabulary} fullWidth={true} - variant="filled" + variant='filled' > - <option value=""></option> + <option value=''></option> {this.state.vocabularyTerms.map(term => ( - <option key={term.code} value={term.code}>{term.label || term.code}</option> + <option key={term.code} value={term.code}> + {term.label || term.code} + </option> ))} </TextField> ) } - loadMaterial(){ + loadMaterial() { let criteria = new dto.MaterialSearchCriteria() let fo = new dto.MaterialFetchOptions() let materialType = this.state.property.propertyType.materialType - if(materialType){ - criteria.withType().withId().thatEquals(materialType.permId) + if (materialType) { + criteria + .withType() + .withId() + .thatEquals(materialType.permId) } return facade.searchMaterials(criteria, fo).then(result => { @@ -219,54 +230,53 @@ class ObjectTypePropertyPreview extends React.Component { }) } - getMaterial(){ + getMaterial() { return this.state.materialValue } - setMaterial(event){ + setMaterial(event) { const value = event.target.value this.setState(() => ({ materialValue: value })) } - renderMaterial(){ + renderMaterial() { return ( <TextField select SelectProps={{ - native: true, + native: true }} label={this.getLabel()} value={this.getMaterial()} onChange={this.setMaterial} fullWidth={true} - variant="filled" + variant='filled' > - <option value=""></option> + <option value=''></option> {this.state.materials.map(material => ( - <option key={material.code} value={material.code}>{material.code}</option> + <option key={material.code} value={material.code}> + {material.code} + </option> ))} </TextField> ) } - renderUnsupported(){ - return (<div>unsupported</div>) + renderUnsupported() { + return <div>unsupported</div> } - getLabel(){ + getLabel() { let mandatory = this.state.property.mandatory let label = this.state.property.propertyType.label return mandatory ? label + '*' : label } - getDescription(){ + getDescription() { return this.state.property.propertyType.description } - } -export default _.flow( - withStyles(styles) -)(ObjectTypePropertyPreview) +export default _.flow(withStyles(styles))(ObjectTypePropertyPreview) diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyRow.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyRow.jsx index f626780e6c1a5901e916d9db469e1ee96c5e5699..ca7b85739096c268e50dd493460e1433e3951db8 100644 --- a/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyRow.jsx +++ b/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyRow.jsx @@ -9,10 +9,10 @@ import ObjectTypePropertyCell from './ObjectTypePropertyCell.jsx' import ObjectTypePropertyPreview from './ObjectTypePropertyPreview.jsx' import ObjectTypePropertyType from './ObjectTypePropertyType.jsx' import ObjectTypePropertyMandatory from './ObjectTypePropertyMandatory.jsx' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import logger from '../../../common/logger.js' -const styles = (theme) => ({ +const styles = theme => ({ row: { backgroundColor: theme.palette.background.paper, '&:hover': { @@ -34,7 +34,7 @@ const source = { return { sourceIndex: props.index } }, endDrag(props, monitor) { - if(monitor.getItem() && monitor.getDropResult()){ + if (monitor.getItem() && monitor.getDropResult()) { const { sourceIndex } = monitor.getItem() const { targetIndex } = monitor.getDropResult() props.onReorder(sourceIndex, targetIndex) @@ -64,28 +64,27 @@ function targetCollect(connect, monitor) { } class ObjectTypePropertyRow extends React.Component { - - constructor(props){ + constructor(props) { super(props) this.handleRef = React.createRef() this.rowRef = React.createRef() this.handleSelect = this.handleSelect.bind(this) } - componentDidMount(){ + componentDidMount() { this.props.connectDragSource(this.handleRef.current) this.props.connectDragPreview(this.rowRef.current) this.props.connectDropTarget(this.rowRef.current) } - handleSelect(){ + handleSelect() { this.props.onSelect(this.props.property.id) } - render(){ + render() { logger.log(logger.DEBUG, 'ObjectTypePropertyRow.render') - const {classes, property} = this.props + const { classes, property } = this.props return ( <RootRef rootRef={this.rowRef}> @@ -115,22 +114,18 @@ class ObjectTypePropertyRow extends React.Component { ) } - renderPreview(){ - const {property} = this.props - - if(property.propertyType){ - return ( - <ObjectTypePropertyPreview - property={property} - /> - ) - }else{ - return (<div></div>) + renderPreview() { + const { property } = this.props + + if (property.propertyType) { + return <ObjectTypePropertyPreview property={property} /> + } else { + return <div></div> } } - renderPropertyType(){ - const {property, propertyTypes, onChange} = this.props + renderPropertyType() { + const { property, propertyTypes, onChange } = this.props return ( <ObjectTypePropertyType @@ -141,16 +136,12 @@ class ObjectTypePropertyRow extends React.Component { ) } - renderMandatory(){ - const {property, onChange} = this.props + renderMandatory() { + const { property, onChange } = this.props return ( - <ObjectTypePropertyMandatory - property={property} - onChange={onChange} - /> + <ObjectTypePropertyMandatory property={property} onChange={onChange} /> ) } - } export default _.flow( diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyType.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyType.jsx index 44ab9aa7db79c38cf61a4ba031ce6d07cee662d2..7f7b121edd19660d91441671a7ea61083d1723c3 100644 --- a/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyType.jsx +++ b/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyType.jsx @@ -6,14 +6,13 @@ import EditableField from '../../common/form/EditableField.jsx' import logger from '../../../common/logger.js' class ObjectTypePropertyType extends React.Component { - - constructor(props){ + constructor(props) { super(props) this.renderField = this.renderField.bind(this) this.handleChange = this.handleChange.bind(this) } - handleChange(event){ + handleChange(event) { event.stopPropagation() let propertyType = _.find(this.props.propertyTypes, propertyType => { return propertyType.code === event.target.value @@ -21,23 +20,23 @@ class ObjectTypePropertyType extends React.Component { this.props.onChange(this.props.property.id, 'propertyType', propertyType) } - render(){ + render() { logger.log(logger.DEBUG, 'ObjectTypePropertyType.render') return <EditableField renderField={this.renderField} /> } - renderField({ref, edited, handleBlur}){ - const {property, propertyTypes} = this.props - const {propertyType} = property + renderField({ ref, edited, handleBlur }) { + const { property, propertyTypes } = this.props + const { propertyType } = property - if(edited){ + if (edited) { return ( <TextField inputRef={ref} select SelectProps={{ - native: true, + native: true }} value={propertyType ? propertyType.code : ''} onChange={this.handleChange} @@ -46,32 +45,34 @@ class ObjectTypePropertyType extends React.Component { error={this.hasError()} helperText={this.getError()} > - <option value=""></option> - {propertyTypes && propertyTypes.map(propertyType => ( - <option key={propertyType.code} value={propertyType.code}>{propertyType.code}</option> - ))} + <option value=''></option> + {propertyTypes && + propertyTypes.map(propertyType => ( + <option key={propertyType.code} value={propertyType.code}> + {propertyType.code} + </option> + ))} </TextField> ) - }else{ + } else { return ( <div> <div>{propertyType ? propertyType.code : ''}</div> - {this.hasError() && + {this.hasError() && ( <FormHelperText error={true}>{this.getError()}</FormHelperText> - } + )} </div> ) } } - hasError(){ + hasError() { return this.getError() ? true : false } - getError(){ + getError() { return this.props.property.errors['propertyType'] } - } export default ObjectTypePropertyType diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectTypeTable.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectTypeTable.jsx index 36ff07cc35cd338d5c311eaeb56add8396879d6d..350901330d665b32528507b0a725d34b7dbdc5e8 100644 --- a/openbis_ng_ui/src/components/types/objectType/ObjectTypeTable.jsx +++ b/openbis_ng_ui/src/components/types/objectType/ObjectTypeTable.jsx @@ -6,7 +6,7 @@ import TableHead from '@material-ui/core/TableHead' import TableRow from '@material-ui/core/TableRow' import ObjectTypePropertyCell from './ObjectTypePropertyCell.jsx' import ObjectTypePropertyRow from './ObjectTypePropertyRow.jsx' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import logger from '../../../common/logger.js' const styles = () => ({ @@ -19,8 +19,7 @@ const styles = () => ({ }) class ObjectTypeTable extends React.Component { - - constructor(props){ + constructor(props) { super(props) this.state = {} } @@ -57,11 +56,9 @@ class ObjectTypeTable extends React.Component { </React.Fragment> ))} </TableBody> - </Table>) + </Table> + ) } - } -export default _.flow( - withStyles(styles) -)(ObjectTypeTable) +export default _.flow(withStyles(styles))(ObjectTypeTable) diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectTypeTitle.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectTypeTitle.jsx index 8b1f632ad9fef63a13cf103794d0fb0880295cb5..c57adcebe5c9923d9023f9d8e9d2194ba507cf12 100644 --- a/openbis_ng_ui/src/components/types/objectType/ObjectTypeTitle.jsx +++ b/openbis_ng_ui/src/components/types/objectType/ObjectTypeTitle.jsx @@ -3,19 +3,13 @@ import Typography from '@material-ui/core/Typography' import logger from '../../../common/logger.js' class ObjectTypeTitle extends React.Component { - render() { logger.log(logger.DEBUG, 'ObjectTypeHeader.render') - const {objectType} = this.props + const { objectType } = this.props - return ( - <Typography variant="h6"> - {objectType.code} - </Typography> - ) + return <Typography variant='h6'>{objectType.code}</Typography> } - } export default ObjectTypeTitle diff --git a/openbis_ng_ui/src/components/types/search/Search.jsx b/openbis_ng_ui/src/components/types/search/Search.jsx index 4c9591e0cf599a527275733b36c400333a916c33..c524ad3954f3ce7af35587d1a5e213491d390a2e 100644 --- a/openbis_ng_ui/src/components/types/search/Search.jsx +++ b/openbis_ng_ui/src/components/types/search/Search.jsx @@ -1,14 +1,14 @@ import _ from 'lodash' import React from 'react' -import {connect} from 'react-redux' -import {withStyles} from '@material-ui/core/styles' +import { connect } from 'react-redux' +import { withStyles } from '@material-ui/core/styles' import Link from '@material-ui/core/Link' import Grid from '../../common/grid/Grid.jsx' import * as ids from '../../../common/consts/ids.js' import * as pages from '../../../common/consts/pages.js' import * as objectTypes from '../../../common/consts/objectType.js' import * as actions from '../../../store/actions/actions.js' -import {facade, dto} from '../../../services/openbis.js' +import { facade, dto } from '../../../services/openbis.js' import logger from '../../../common/logger.js' const styles = () => ({ @@ -17,15 +17,16 @@ const styles = () => ({ } }) -function mapDispatchToProps(dispatch){ +function mapDispatchToProps(dispatch) { return { - objectOpen: (objectType, objectId) => { dispatch(actions.objectOpen(pages.TYPES, objectType, objectId)) } + objectOpen: (objectType, objectId) => { + dispatch(actions.objectOpen(pages.TYPES, objectType, objectId)) + } } } class Search extends React.Component { - - constructor(props){ + constructor(props) { super(props) this.state = { @@ -35,31 +36,45 @@ class Search extends React.Component { this.handleLinkClick = this.handleLinkClick.bind(this) } - componentDidMount(){ + componentDidMount() { this.load().then(types => { - this.setState(()=>({ + this.setState(() => ({ types, loaded: true })) }) } - load(){ + load() { return Promise.all([ this.searchObjectTypes(), this.searchCollectionTypes(), this.searchDataSetTypes(), this.searchMaterialTypes() ]).then(([objectTypes, collectionTypes, dataSetTypes, materialTypes]) => { - let allTypes = [].concat(objectTypes, collectionTypes, dataSetTypes, materialTypes) - return allTypes.map(type => ({ - ...type, - id: type.permId.entityKind + '-' + type.permId.permId - })) + let allTypes = [].concat( + objectTypes, + collectionTypes, + dataSetTypes, + materialTypes + ) + + let query = this.props.objectId.toUpperCase() + + return allTypes + .filter( + type => + (type.code && type.code.toUpperCase().includes(query)) || + (type.description && type.description.toUpperCase().includes(query)) + ) + .map(type => ({ + ...type, + id: type.permId.entityKind + '-' + type.permId.permId + })) }) } - searchObjectTypes(){ + searchObjectTypes() { let criteria = new dto.SampleTypeSearchCriteria() let fo = new dto.SampleTypeFetchOptions() return facade.searchSampleTypes(criteria, fo).then(result => { @@ -67,7 +82,7 @@ class Search extends React.Component { }) } - searchCollectionTypes(){ + searchCollectionTypes() { let criteria = new dto.ExperimentTypeSearchCriteria() let fo = new dto.ExperimentTypeFetchOptions() return facade.searchExperimentTypes(criteria, fo).then(result => { @@ -75,7 +90,7 @@ class Search extends React.Component { }) } - searchDataSetTypes(){ + searchDataSetTypes() { let criteria = new dto.DataSetTypeSearchCriteria() let fo = new dto.DataSetTypeFetchOptions() return facade.searchDataSetTypes(criteria, fo).then(result => { @@ -83,7 +98,7 @@ class Search extends React.Component { }) } - searchMaterialTypes(){ + searchMaterialTypes() { let criteria = new dto.MaterialTypeSearchCriteria() let fo = new dto.MaterialTypeFetchOptions() return facade.searchMaterialTypes(criteria, fo).then(result => { @@ -91,22 +106,25 @@ class Search extends React.Component { }) } - handleLinkClick(permId){ + handleLinkClick(permId) { const entityKindToObjecType = { - 'SAMPLE': objectTypes.OBJECT_TYPE, - 'EXPERIMENT': objectTypes.COLLECTION_TYPE, - 'DATA_SET': objectTypes.DATA_SET_TYPE, - 'MATERIAL': objectTypes.MATERIAL_TYPE + SAMPLE: objectTypes.OBJECT_TYPE, + EXPERIMENT: objectTypes.COLLECTION_TYPE, + DATA_SET: objectTypes.DATA_SET_TYPE, + MATERIAL: objectTypes.MATERIAL_TYPE } return () => { - this.props.objectOpen(entityKindToObjecType[permId.entityKind], permId.permId) + this.props.objectOpen( + entityKindToObjecType[permId.entityKind], + permId.permId + ) } } render() { logger.log(logger.DEBUG, 'Search.render') - if(!this.state.loaded){ + if (!this.state.loaded) { return null } @@ -116,34 +134,37 @@ class Search extends React.Component { return ( <Grid id={ids.TYPES_GRID_ID} - filter={this.props.objectId} columns={[ { field: 'permId.entityKind', - label: 'Kind', + label: 'Kind' }, { field: 'code', render: row => ( <Link - component="button" + component='button' classes={{ root: classes.tableLink }} - onClick={this.handleLinkClick(row.permId)}>{row.code} + onClick={this.handleLinkClick(row.permId)} + > + {row.code} </Link> ) }, { - field: 'description', + field: 'description' } ]} data={types} /> ) } - } export default _.flow( - connect(null, mapDispatchToProps), + connect( + null, + mapDispatchToProps + ), withStyles(styles) )(Search) diff --git a/openbis_ng_ui/src/components/users/Users.jsx b/openbis_ng_ui/src/components/users/Users.jsx index 816abd7bfa20f6a3c9f0aaea6d3b62b2afad8158..12a5cf3698bca562785a74fc6a8d7f958cc2e74c 100644 --- a/openbis_ng_ui/src/components/users/Users.jsx +++ b/openbis_ng_ui/src/components/users/Users.jsx @@ -1,5 +1,5 @@ import React from 'react' -import {withStyles} from '@material-ui/core/styles' +import { withStyles } from '@material-ui/core/styles' import logger from '../../common/logger.js' import * as pages from '../../common/consts/pages.js' import * as objectType from '../../common/consts/objectType.js' @@ -15,7 +15,7 @@ const styles = () => ({ container: { display: 'flex', width: '100%' - }, + } }) const objectTypeToComponent = { @@ -25,7 +25,6 @@ const objectTypeToComponent = { } class Users extends React.Component { - render() { logger.log(logger.DEBUG, 'Users.render') @@ -33,12 +32,14 @@ class Users extends React.Component { return ( <div className={classes.container}> - <Browser page={pages.USERS}/> - <Content page={pages.USERS} objectTypeToComponent={objectTypeToComponent}/> + <Browser page={pages.USERS} /> + <Content + page={pages.USERS} + objectTypeToComponent={objectTypeToComponent} + /> </div> ) } - } export default withStyles(styles)(Users) diff --git a/openbis_ng_ui/src/components/users/group/Group.jsx b/openbis_ng_ui/src/components/users/group/Group.jsx index a1fa93d6cd098f824148d50ccd5238bb53fe777b..ba07050d541d7253df13582a88e694ffc1a2bbed 100644 --- a/openbis_ng_ui/src/components/users/group/Group.jsx +++ b/openbis_ng_ui/src/components/users/group/Group.jsx @@ -2,12 +2,10 @@ import React from 'react' import logger from '../../../common/logger.js' class Group extends React.Component { - render() { logger.log(logger.DEBUG, 'Group.render') return <div>Group</div> } - } export default Group diff --git a/openbis_ng_ui/src/components/users/search/Search.jsx b/openbis_ng_ui/src/components/users/search/Search.jsx index 03a26ce48bbd1f52b94eae2830c7dc138cb1cb90..337e8bbd177033426570cfa08e5cbd73d50fb8f2 100644 --- a/openbis_ng_ui/src/components/users/search/Search.jsx +++ b/openbis_ng_ui/src/components/users/search/Search.jsx @@ -1,48 +1,58 @@ import React from 'react' import Grid from '../../common/grid/Grid.jsx' import * as ids from '../../../common/consts/ids.js' -import {facade, dto} from '../../../services/openbis.js' +import { facade, dto } from '../../../services/openbis.js' import logger from '../../../common/logger.js' class Search extends React.Component { - - constructor(props){ + constructor(props) { super(props) + this.state = { + loaded: false + } + this.load = this.load.bind(this) } - load({ filter, page, pageSize }){ + componentDidMount() { + this.load().then(users => { + this.setState(() => ({ + users, + loaded: true + })) + }) + } + + load() { + let query = this.props.objectId + let criteria = new dto.PersonSearchCriteria() let fo = new dto.PersonFetchOptions() criteria.withOrOperator() - criteria.withUserId().thatContains(filter) - criteria.withFirstName().thatContains(filter) - criteria.withLastName().thatContains(filter) - - fo.count(pageSize) - fo.from(page*pageSize) + criteria.withUserId().thatContains(query) + criteria.withFirstName().thatContains(query) + criteria.withLastName().thatContains(query) return facade.searchPersons(criteria, fo).then(result => { - let objects = result.objects.map(user => ({ + return result.objects.map(user => ({ ...user, id: user.userId })) - return { - objects, - totalCount: result.totalCount - } }) } render() { logger.log(logger.DEBUG, 'Search.render') + if (!this.state.loaded) { + return null + } + return ( <Grid id={ids.USERS_GRID_ID} - filter={this.props.objectId} columns={[ { field: 'userId' @@ -54,11 +64,10 @@ class Search extends React.Component { field: 'lastName' } ]} - data={this.load} + data={this.state.users} /> ) } - } export default Search diff --git a/openbis_ng_ui/src/components/users/user/User.jsx b/openbis_ng_ui/src/components/users/user/User.jsx index 8642343df0b760bf07e0a7b83ca21466f1d4fe2a..c81f4aafa3be357ad2730f24a440bee8f33c85c4 100644 --- a/openbis_ng_ui/src/components/users/user/User.jsx +++ b/openbis_ng_ui/src/components/users/user/User.jsx @@ -2,12 +2,10 @@ import React from 'react' import logger from '../../../common/logger.js' class User extends React.Component { - render() { logger.log(logger.DEBUG, 'User.render') return <div>User</div> } - } export default User diff --git a/openbis_ng_ui/src/index.js b/openbis_ng_ui/src/index.js index 5cb15aae416b8b02f1c24c0339201364306dab2d..a8a9cf0cefd21b48efcf057edff4c3c2f352c5b1 100644 --- a/openbis_ng_ui/src/index.js +++ b/openbis_ng_ui/src/index.js @@ -26,10 +26,10 @@ const render = () => { const store = require('./store/store.js').default ReactDOM.render( - <Provider store = { store }> - <MuiThemeProvider theme={ theme }> + <Provider store={store}> + <MuiThemeProvider theme={theme}> <DragAndDropProvider> - <App /> + <App /> </DragAndDropProvider> </MuiThemeProvider> </Provider>, diff --git a/openbis_ng_ui/src/profile.js b/openbis_ng_ui/src/profile.js index cc4eef0850e2434e60ae6b0192f7e2bae6842831..115711c3990626d01d0f3212c59fcc8ce069390d 100644 --- a/openbis_ng_ui/src/profile.js +++ b/openbis_ng_ui/src/profile.js @@ -2,4 +2,4 @@ let profile = { devEmail: 'dummy.address@test.ch' } -export default profile \ No newline at end of file +export default profile diff --git a/openbis_ng_ui/src/services/openbis/dto.js b/openbis_ng_ui/src/services/openbis/dto.js index 84c4f0f8e03dae0a1438a8340f6ffbf2feded06d..64881181464bd80e4a39570fc90d0f0825641938 100644 --- a/openbis_ng_ui/src/services/openbis/dto.js +++ b/openbis_ng_ui/src/services/openbis/dto.js @@ -26,27 +26,32 @@ const CLASS_FULL_NAMES = [ 'as/dto/vocabulary/search/VocabularyTermSearchCriteria', 'as/dto/vocabulary/fetchoptions/VocabularyTermFetchOptions', 'as/dto/plugin/id/PluginPermId', - 'as/dto/webapp/create/WebAppSettingCreation', + 'as/dto/webapp/create/WebAppSettingCreation' ] class Dto { - - init(){ + init() { let _this = this - let load = function(index){ + let load = function(index) { return new Promise((resolve, reject) => { - if(index < CLASS_FULL_NAMES.length){ + if (index < CLASS_FULL_NAMES.length) { let classFullName = CLASS_FULL_NAMES[index] - let className = classFullName.substring(classFullName.lastIndexOf('/') + 1) + let className = classFullName.substring( + classFullName.lastIndexOf('/') + 1 + ) /* eslint-disable-next-line no-undef */ - requirejs([classFullName], clazz => { - _this[className] = clazz - return load(index + 1).then(resolve, reject) - }, error => { - reject(error) - }) - }else{ + requirejs( + [classFullName], + clazz => { + _this[className] = clazz + return load(index + 1).then(resolve, reject) + }, + error => { + reject(error) + } + ) + } else { resolve() } }) @@ -54,14 +59,13 @@ class Dto { return load(0) } - } const dto = new Dto() CLASS_FULL_NAMES.forEach(classFullName => { let className = classFullName.substring(classFullName.lastIndexOf('/') + 1) - dto[className] = function(){} + dto[className] = function() {} }) export { dto } diff --git a/openbis_ng_ui/src/services/openbis/facade.js b/openbis_ng_ui/src/services/openbis/facade.js index 4fa673a17f3eeefcbfbad7e7283c0097cbadc6dd..a1626904dddc93a750642e6ad4467561669b9163 100644 --- a/openbis_ng_ui/src/services/openbis/facade.js +++ b/openbis_ng_ui/src/services/openbis/facade.js @@ -1,30 +1,35 @@ const autoBind = require('auto-bind') export class Facade { - constructor() { autoBind(this) } - init(){ + init() { let _this = this return new Promise((resolve, reject) => { /* eslint-disable-next-line no-undef */ - requirejs(['openbis'], openbis => { - _this.v3 = new openbis() - resolve() - }, error => { - reject(error) - }) + requirejs( + ['openbis'], + openbis => { + _this.v3 = new openbis() + resolve() + }, + error => { + reject(error) + } + ) }) } login(user, password) { let v3 = this.v3 return new Promise((resolve, reject) => { - v3.login(user, password).done(resolve).fail(() => { - reject({message: 'Login failed'}) - }) + v3.login(user, password) + .done(resolve) + .fail(() => { + reject({ message: 'Login failed' }) + }) }) } @@ -32,11 +37,11 @@ export class Facade { return this.v3.logout() } - getPersons(ids, fo){ + getPersons(ids, fo) { return this.v3.getPersons(ids, fo) } - updatePersons(updates){ + updatePersons(updates) { return this.v3.updatePersons(updates) } @@ -60,7 +65,7 @@ export class Facade { return this.v3.searchAuthorizationGroups(criteria, fo) } - getSampleTypes(ids, fo){ + getSampleTypes(ids, fo) { return this.v3.getSampleTypes(ids, fo) } @@ -68,7 +73,7 @@ export class Facade { return this.v3.searchSampleTypes(criteria, fo) } - updateSampleTypes(updates){ + updateSampleTypes(updates) { return this.v3.updateSampleTypes(updates) } @@ -83,7 +88,6 @@ export class Facade { searchMaterialTypes(criteria, fo) { return this.v3.searchMaterialTypes(criteria, fo) } - } const facade = new Facade() diff --git a/openbis_ng_ui/src/store/actions/api.js b/openbis_ng_ui/src/store/actions/api.js index 2a785b43c2f951f91d3c3ece5f42042ffbe59867..e5a3338db543b25df8727a0182e57088c6f4baab 100644 --- a/openbis_ng_ui/src/store/actions/api.js +++ b/openbis_ng_ui/src/store/actions/api.js @@ -2,7 +2,7 @@ export const API_REQUEST = 'API_REQUEST' export const API_SUCCESS = 'API_SUCCESS' export const API_ERROR = 'API_ERROR' -export const apiRequest = ({method, params, meta}) => ({ +export const apiRequest = ({ method, params, meta }) => ({ type: API_REQUEST, payload: { method, @@ -11,7 +11,7 @@ export const apiRequest = ({method, params, meta}) => ({ meta }) -export const apiSuccess = ({result, meta}) => ({ +export const apiSuccess = ({ result, meta }) => ({ type: API_SUCCESS, payload: { result @@ -19,7 +19,7 @@ export const apiSuccess = ({result, meta}) => ({ meta }) -export const apiError = ({error, meta}) => ({ +export const apiError = ({ error, meta }) => ({ type: API_ERROR, payload: { error diff --git a/openbis_ng_ui/src/store/actions/app.js b/openbis_ng_ui/src/store/actions/app.js index e2bccce38ed366468bafe6003d2daf409fe489ba..997e5d1fc003d8281c1cf7cfce9e844d2dcac535 100644 --- a/openbis_ng_ui/src/store/actions/app.js +++ b/openbis_ng_ui/src/store/actions/app.js @@ -37,63 +37,63 @@ export const search = (page, text) => ({ } }) -export const currentPageChange = (currentPage) => ({ +export const currentPageChange = currentPage => ({ type: CURRENT_PAGE_CHANGE, payload: { currentPage } }) -export const searchChange = (search) => ({ +export const searchChange = search => ({ type: SEARCH_CHANGE, payload: { search } }) -export const errorChange = (error) => ({ +export const errorChange = error => ({ type: ERROR_CHANGE, payload: { error } }) -export const routeChange = (route) => ({ +export const routeChange = route => ({ type: ROUTE_CHANGE, payload: { route } }) -export const setLoading = (loading) => ({ +export const setLoading = loading => ({ type: SET_LOADING, payload: { loading } }) -export const setSearch = (search) => ({ +export const setSearch = search => ({ type: SET_SEARCH, payload: { search } }) -export const setSession = (session) => ({ +export const setSession = session => ({ type: SET_SESSION, payload: { session } }) -export const setError = (error) => ({ +export const setError = error => ({ type: SET_ERROR, payload: { error } }) -export const setRoute = (route) => ({ +export const setRoute = route => ({ type: SET_ROUTE, payload: { route diff --git a/openbis_ng_ui/src/store/actions/browser.js b/openbis_ng_ui/src/store/actions/browser.js index 9bc2e1a2d7de3ca65d138eafa383458dcda3aba9..70d4a875b21436ac97d2d4b72c7d0dc54631e436 100644 --- a/openbis_ng_ui/src/store/actions/browser.js +++ b/openbis_ng_ui/src/store/actions/browser.js @@ -12,7 +12,7 @@ export const BROWSER_SET_EXPANDED_NODES = 'BROWSER_SET_EXPANDED_NODES' export const BROWSER_ADD_EXPANDED_NODES = 'BROWSER_ADD_EXPANDED_NODES' export const BROWSER_REMOVE_EXPANDED_NODES = 'BROWSER_REMOVE_EXPANDED_NODES' -export const browserInit = (page) => ({ +export const browserInit = page => ({ type: BROWSER_INIT, payload: { page diff --git a/openbis_ng_ui/src/store/common/browser.js b/openbis_ng_ui/src/store/common/browser.js index 31b69b5b9ec235b98460543e5358e31f354ac27b..69d60d6b5bf5a9568e3e51c7c85acb0127941872 100644 --- a/openbis_ng_ui/src/store/common/browser.js +++ b/openbis_ng_ui/src/store/common/browser.js @@ -1,24 +1,27 @@ import _ from 'lodash' -export function mapNodes(parent, nodes, fn){ - return nodes.map(node => { - return fn(parent, node) - }).filter(node => { - return node !== null - }).map(node => { - if(node.children){ - node.children = mapNodes(node, node.children, fn) - } - return node - }) +export function mapNodes(parent, nodes, fn) { + return nodes + .map(node => { + return fn(parent, node) + }) + .filter(node => { + return node !== null + }) + .map(node => { + if (node.children) { + node.children = mapNodes(node, node.children, fn) + } + return node + }) } -export function getAllNodes(nodes){ +export function getAllNodes(nodes) { let levels = getAllNodesByLevel(nodes) return _.concat(...levels) } -function getAllNodesByLevel(nodes){ +function getAllNodesByLevel(nodes) { let levels = [] let toVisit = [] @@ -46,29 +49,32 @@ function getAllNodesByLevel(nodes){ return levels } -export function sortNodes(nodes){ +export function sortNodes(nodes) { nodes.sort((n1, n2) => { return n1.text.localeCompare(n2.text) }) nodes.forEach(node => { - if(node.children){ + if (node.children) { sortNodes(node.children) } }) } -export function getMatchingNodes(nodes, matchesFn){ +export function getMatchingNodes(nodes, matchesFn) { let allNodes = getAllNodes(nodes) let matchingNodes = {} - let hasMatchingChildren = function(node){ - return node.children && _.some(node.children, child => { - return matchingNodes[child.id] - }) + let hasMatchingChildren = function(node) { + return ( + node.children && + _.some(node.children, child => { + return matchingNodes[child.id] + }) + ) } allNodes.reverse().forEach(node => { - if(hasMatchingChildren(node) || matchesFn(node)){ + if (hasMatchingChildren(node) || matchesFn(node)) { matchingNodes[node.id] = node.id } }) diff --git a/openbis_ng_ui/src/store/history.js b/openbis_ng_ui/src/store/history.js index 211bc90d70713b2462e756b406c2e75bcdd73b13..290663fb7799971518ae9246378845ed80c4497f 100644 --- a/openbis_ng_ui/src/store/history.js +++ b/openbis_ng_ui/src/store/history.js @@ -3,28 +3,27 @@ import * as actions from './actions/actions.js' import routes from '../common/consts/routes.js' let history = createHashHistory({ - hashType: 'noslash' -}) + hashType: 'noslash' +}) -history.configure = (store) => { +history.configure = store => { + history.listen(location => { + let route = routes.parse(location.pathname) + store.dispatch(actions.routeChange(route.path)) + }) - history.listen((location) => { - let route = routes.parse(location.pathname) - store.dispatch(actions.routeChange(route.path)) - }) + let currentRoute = store.getState().route - let currentRoute = store.getState().route + store.subscribe(() => { + let newRoute = store.getState().route - store.subscribe(() => { - let newRoute = store.getState().route - - if(newRoute && newRoute !== currentRoute){ - currentRoute = newRoute - if(currentRoute && currentRoute !== history.location.pathname){ - history.push(currentRoute) - } - } - }) + if (newRoute && newRoute !== currentRoute) { + currentRoute = newRoute + if (currentRoute && currentRoute !== history.location.pathname) { + history.push(currentRoute) + } + } + }) } -export default history \ No newline at end of file +export default history diff --git a/openbis_ng_ui/src/store/middleware/loadingThrottle.js b/openbis_ng_ui/src/store/middleware/loadingThrottle.js index ca95f9817b92bff1e470be9c786adee2883f152b..08f42053561321d0cac31e8255e891376e551f1c 100644 --- a/openbis_ng_ui/src/store/middleware/loadingThrottle.js +++ b/openbis_ng_ui/src/store/middleware/loadingThrottle.js @@ -3,26 +3,27 @@ import * as actions from '../actions/actions.js' const MIN_LOADING_TIME = 500 let startLoadingTimestamp = null -export default () => (next) => (action) => { - if(action.type === actions.SET_LOADING){ - if(action.payload.loading){ - if(startLoadingTimestamp === null){ +export default () => next => action => { + if (action.type === actions.SET_LOADING) { + if (action.payload.loading) { + if (startLoadingTimestamp === null) { startLoadingTimestamp = new Date().getTime() } next(action) - }else{ - let timeToWait = startLoadingTimestamp + MIN_LOADING_TIME - new Date().getTime() - if(timeToWait > 0){ + } else { + let timeToWait = + startLoadingTimestamp + MIN_LOADING_TIME - new Date().getTime() + if (timeToWait > 0) { setTimeout(() => { startLoadingTimestamp = null next(action) }, timeToWait) - }else{ + } else { startLoadingTimestamp = null next(action) } } - }else{ + } else { next(action) } } diff --git a/openbis_ng_ui/src/store/middleware/stateChangeCheck.js b/openbis_ng_ui/src/store/middleware/stateChangeCheck.js index f6b8bf48e5800d503375bdfc34142f0d4295e0f1..49a0cc07788d6613b9948879b4985e7dbc2bfaa4 100644 --- a/openbis_ng_ui/src/store/middleware/stateChangeCheck.js +++ b/openbis_ng_ui/src/store/middleware/stateChangeCheck.js @@ -1,8 +1,8 @@ import _ from 'lodash' import logger from '../../common/logger.js' -export default (store) => (next) => (action) => { - if(logger.isLevelEnabled(logger.DEBUG)){ +export default store => next => action => { + if (logger.isLevelEnabled(logger.DEBUG)) { logger.group(logger.DEBUG, 'Action ' + action.type, action) let beforeState = store.getState() @@ -15,53 +15,84 @@ export default (store) => (next) => (action) => { let modifiedPreviousState = !_.isEqual(beforeState, beforeStateClone) logger.groupEnd(logger.DEBUG) - if(unmodifiedNewObjects || modifiedPreviousState){ - if(unmodifiedNewObjects){ - logger.log(logger.ERROR, 'ERROR state changed incorrectly - returned new objects without changes', beforeState, afterState) + if (unmodifiedNewObjects || modifiedPreviousState) { + if (unmodifiedNewObjects) { + logger.log( + logger.ERROR, + 'ERROR state changed incorrectly - returned new objects without changes', + beforeState, + afterState + ) } - if(modifiedPreviousState){ - logger.log(logger.ERROR, 'ERROR state changed incorrectly - reducer modified previous state object', beforeStateClone, beforeState, afterState) + if (modifiedPreviousState) { + logger.log( + logger.ERROR, + 'ERROR state changed incorrectly - reducer modified previous state object', + beforeStateClone, + beforeState, + afterState + ) } - }else{ - if(_.isEqual(beforeState, afterState)){ + } else { + if (_.isEqual(beforeState, afterState)) { logger.log(logger.DEBUG, 'OK state not changed', afterState) - }else{ - logger.log(logger.DEBUG, 'OK state changed correctly', beforeState, afterState) + } else { + logger.log( + logger.DEBUG, + 'OK state changed correctly', + beforeState, + afterState + ) } } logger.groupEnd(logger.DEBUG) - }else{ + } else { next(action) } } -function diff(beforeState, afterState, path){ +function diff(beforeState, afterState, path) { let unmodifiedNewObjects = false - if(_.isObject(beforeState) && _.isObject(afterState)){ - if(beforeState === afterState){ + if (_.isObject(beforeState) && _.isObject(afterState)) { + if (beforeState === afterState) { logger.log(logger.TRACE, 'OK - ' + path + ' - same object') - }else{ - if(_.isEqual(beforeState, afterState)){ - logger.log(logger.DEBUG, 'ERROR - ' + path + ' - new object without changes', afterState) + } else { + if (_.isEqual(beforeState, afterState)) { + logger.log( + logger.DEBUG, + 'ERROR - ' + path + ' - new object without changes', + afterState + ) unmodifiedNewObjects = true - }else{ - logger.log(logger.DEBUG, 'OK* - ' + path + ' - new object with changes', beforeState, afterState) + } else { + logger.log( + logger.DEBUG, + 'OK* - ' + path + ' - new object with changes', + beforeState, + afterState + ) } let props = _.union(_.keys(beforeState), _.keys(afterState)) props.forEach(prop => { let beforeProp = beforeState ? beforeState[prop] : undefined let afterProp = afterState ? afterState[prop] : undefined - unmodifiedNewObjects = unmodifiedNewObjects | diff(beforeProp, afterProp, path + '/' + prop) + unmodifiedNewObjects = + unmodifiedNewObjects | diff(beforeProp, afterProp, path + '/' + prop) }) } - }else{ - if(_.isEqual(beforeState, afterState)){ + } else { + if (_.isEqual(beforeState, afterState)) { logger.log(logger.TRACE, 'OK - ' + path + ' - same value', afterState) - }else{ - logger.log(logger.DEBUG, 'OK* - ' + path + ' - changed value', beforeState, afterState) + } else { + logger.log( + logger.DEBUG, + 'OK* - ' + path + ' - changed value', + beforeState, + afterState + ) } } diff --git a/openbis_ng_ui/src/store/reducers/reducers.js b/openbis_ng_ui/src/store/reducers/reducers.js index 6ce7ab29fd0601bd1eb2442f1d9027d24e205080..a13a279f0c6384177bfe6730665ab5324b2597ac 100644 --- a/openbis_ng_ui/src/store/reducers/reducers.js +++ b/openbis_ng_ui/src/store/reducers/reducers.js @@ -5,7 +5,7 @@ import session from './session/session.js' import route from './route/route.js' export default function root(state = {}, action) { - if(action.type === actions.INIT){ + if (action.type === actions.INIT) { state = {} } return combineReducers({ diff --git a/openbis_ng_ui/src/store/reducers/route/route.js b/openbis_ng_ui/src/store/reducers/route/route.js index aa02078d9663909db49368c68aabc13fe4e1d606..4218410aeaa9d65f8f4e11a8b1ec7f6df6a22920 100644 --- a/openbis_ng_ui/src/store/reducers/route/route.js +++ b/openbis_ng_ui/src/store/reducers/route/route.js @@ -1,6 +1,6 @@ import * as actions from '../../actions/actions.js' -export default function route(state = null, action){ +export default function route(state = null, action) { switch (action.type) { case actions.SET_ROUTE: { return action.payload.route diff --git a/openbis_ng_ui/src/store/reducers/session/session.js b/openbis_ng_ui/src/store/reducers/session/session.js index 9c28ef12190046c1496d7bddf6850513f0c7346a..5a9a3ea08b771d0880969643b1490edc26e08b4d 100644 --- a/openbis_ng_ui/src/store/reducers/session/session.js +++ b/openbis_ng_ui/src/store/reducers/session/session.js @@ -1,6 +1,6 @@ import * as actions from '../../actions/actions.js' -export default function session(state = null, action){ +export default function session(state = null, action) { switch (action.type) { case actions.SET_SESSION: { return action.payload.session diff --git a/openbis_ng_ui/src/store/reducers/ui/pages/common/browser.js b/openbis_ng_ui/src/store/reducers/ui/pages/common/browser.js index 1fa5e8ba9545d2be4cf44c837320f851398b70da..f7be907e6f0413b980fe8124a4199bb59863819c 100644 --- a/openbis_ng_ui/src/store/reducers/ui/pages/common/browser.js +++ b/openbis_ng_ui/src/store/reducers/ui/pages/common/browser.js @@ -10,12 +10,12 @@ export const browser = combineReducers({ expandedNodes }) -function filter(state = '', action){ - switch(action.type){ +function filter(state = '', action) { + switch (action.type) { case actions.BROWSER_SET_FILTER: - if(_.isEqual(state, action.payload.filter)){ + if (_.isEqual(state, action.payload.filter)) { return state - }else{ + } else { return action.payload.filter } default: @@ -23,8 +23,8 @@ function filter(state = '', action){ } } -function nodes(state = [], action){ - switch(action.type){ +function nodes(state = [], action) { + switch (action.type) { case actions.BROWSER_SET_NODES: return action.payload.nodes default: @@ -32,12 +32,12 @@ function nodes(state = [], action){ } } -function selectedNodes(state = [], action){ - switch(action.type){ +function selectedNodes(state = [], action) { + switch (action.type) { case actions.BROWSER_SET_SELECTED_NODES: - if(_.isEqual(state, action.payload.ids)){ + if (_.isEqual(state, action.payload.ids)) { return state - }else{ + } else { return action.payload.ids } default: @@ -45,43 +45,43 @@ function selectedNodes(state = [], action){ } } -function visibleNodes(state = [], action){ - switch(action.type){ - case actions.BROWSER_SET_VISIBLE_NODES:{ - if(_.isEqual(state, action.payload.ids)){ +function visibleNodes(state = [], action) { + switch (action.type) { + case actions.BROWSER_SET_VISIBLE_NODES: { + if (_.isEqual(state, action.payload.ids)) { return state } else { return action.payload.ids } } - default:{ + default: { return state } } } -function expandedNodes(state = [], action){ +function expandedNodes(state = [], action) { let newState = null - switch(action.type){ - case actions.BROWSER_SET_EXPANDED_NODES:{ + switch (action.type) { + case actions.BROWSER_SET_EXPANDED_NODES: { newState = action.payload.ids break } - case actions.BROWSER_ADD_EXPANDED_NODES:{ + case actions.BROWSER_ADD_EXPANDED_NODES: { newState = _.union(state, action.payload.ids) break } - case actions.BROWSER_REMOVE_EXPANDED_NODES:{ + case actions.BROWSER_REMOVE_EXPANDED_NODES: { newState = _.without(state, ...action.payload.ids) break } - default:{ + default: { return state } } - if(_.isEqual(state, newState)){ + if (_.isEqual(state, newState)) { return state } else { return newState diff --git a/openbis_ng_ui/src/store/reducers/ui/pages/common/page.js b/openbis_ng_ui/src/store/reducers/ui/pages/common/page.js index 0a6bc47eacc27566f9749f0833a971b4cda83c65..3b07945d25f4938d93f8523fb743388fac3be5c6 100644 --- a/openbis_ng_ui/src/store/reducers/ui/pages/common/page.js +++ b/openbis_ng_ui/src/store/reducers/ui/pages/common/page.js @@ -3,16 +3,19 @@ import * as actions from '../../../../actions/actions.js' export * from './browser.js' -export function isPageAction(page, action){ - return action.type === actions.INIT || page === (action.payload && action.payload.page) +export function isPageAction(page, action) { + return ( + action.type === actions.INIT || + page === (action.payload && action.payload.page) + ) } export const currentRoute = (state = null, action) => { - switch(action.type){ - case actions.SET_CURRENT_ROUTE:{ + switch (action.type) { + case actions.SET_CURRENT_ROUTE: { return action.payload.currentRoute } - default:{ + default: { return state } } @@ -21,21 +24,29 @@ export const currentRoute = (state = null, action) => { export const openObjects = (state = [], action) => { let newState = null - switch(action.type){ - case actions.ADD_OPEN_OBJECT:{ - newState = _.unionWith(state, [{type: action.payload.type, id: action.payload.id}], _.isEqual) + switch (action.type) { + case actions.ADD_OPEN_OBJECT: { + newState = _.unionWith( + state, + [{ type: action.payload.type, id: action.payload.id }], + _.isEqual + ) break } - case actions.REMOVE_OPEN_OBJECT:{ - newState = _.differenceWith(state, [{type: action.payload.type, id: action.payload.id}], _.isEqual) + case actions.REMOVE_OPEN_OBJECT: { + newState = _.differenceWith( + state, + [{ type: action.payload.type, id: action.payload.id }], + _.isEqual + ) break } - default:{ + default: { return state } } - if(_.isEqual(state, newState)){ + if (_.isEqual(state, newState)) { return state } else { return newState @@ -45,21 +56,29 @@ export const openObjects = (state = [], action) => { export const changedObjects = (state = [], action) => { let newState = null - switch(action.type){ - case actions.ADD_CHANGED_OBJECT:{ - newState = _.unionWith(state, [{type: action.payload.type, id: action.payload.id}], _.isEqual) + switch (action.type) { + case actions.ADD_CHANGED_OBJECT: { + newState = _.unionWith( + state, + [{ type: action.payload.type, id: action.payload.id }], + _.isEqual + ) break } - case actions.REMOVE_CHANGED_OBJECT:{ - newState = _.differenceWith(state, [{type: action.payload.type, id: action.payload.id}], _.isEqual) + case actions.REMOVE_CHANGED_OBJECT: { + newState = _.differenceWith( + state, + [{ type: action.payload.type, id: action.payload.id }], + _.isEqual + ) break } - default:{ + default: { return state } } - if(_.isEqual(state, newState)){ + if (_.isEqual(state, newState)) { return state } else { return newState diff --git a/openbis_ng_ui/src/store/reducers/ui/pages/login/login.js b/openbis_ng_ui/src/store/reducers/ui/pages/login/login.js index fcc72a8d641c3f67d1f01482812784c5655021a6..9780c6f45516aeb29ba87c530072a5a22e0e2881 100644 --- a/openbis_ng_ui/src/store/reducers/ui/pages/login/login.js +++ b/openbis_ng_ui/src/store/reducers/ui/pages/login/login.js @@ -1,3 +1,3 @@ -export default function login(state = {}){ +export default function login(state = {}) { return state } diff --git a/openbis_ng_ui/src/store/reducers/ui/pages/types/types.js b/openbis_ng_ui/src/store/reducers/ui/pages/types/types.js index dac9df43a704b6315bd76b21b498e138a0ea4a1f..86af2ada258dc284aadd1e35ad9b75eaa70e54e0 100644 --- a/openbis_ng_ui/src/store/reducers/ui/pages/types/types.js +++ b/openbis_ng_ui/src/store/reducers/ui/pages/types/types.js @@ -2,15 +2,15 @@ import { combineReducers } from 'redux' import * as pages from '../../../../../common/consts/pages.js' import * as page from '../common/page.js' -export default function types(state = {}, action){ - if(page.isPageAction(pages.TYPES, action)){ +export default function types(state = {}, action) { + if (page.isPageAction(pages.TYPES, action)) { return combineReducers({ currentRoute: page.currentRoute, browser: page.browser, openObjects: page.openObjects, changedObjects: page.changedObjects })(state, action) - }else{ + } else { return state } } diff --git a/openbis_ng_ui/src/store/reducers/ui/pages/users/users.js b/openbis_ng_ui/src/store/reducers/ui/pages/users/users.js index cc1ab4ef496bc0f211f4d99dcda4e19e1291c37b..3213985686178ea5c58335c7b1ecad0f1a3b7acd 100644 --- a/openbis_ng_ui/src/store/reducers/ui/pages/users/users.js +++ b/openbis_ng_ui/src/store/reducers/ui/pages/users/users.js @@ -2,15 +2,15 @@ import { combineReducers } from 'redux' import * as pages from '../../../../../common/consts/pages.js' import * as page from '../common/page.js' -export default function types(state = {}, action){ - if(page.isPageAction(pages.USERS, action)){ +export default function types(state = {}, action) { + if (page.isPageAction(pages.USERS, action)) { return combineReducers({ currentRoute: page.currentRoute, browser: page.browser, openObjects: page.openObjects, changedObjects: page.changedObjects })(state, action) - }else{ + } else { return state } } diff --git a/openbis_ng_ui/src/store/reducers/ui/ui.js b/openbis_ng_ui/src/store/reducers/ui/ui.js index 1ea3cc7803e6ea3e4a35eb36ef79dc9114fbd655..e86b2eb900a337193121d80f51c0dac53928a3a8 100644 --- a/openbis_ng_ui/src/store/reducers/ui/ui.js +++ b/openbis_ng_ui/src/store/reducers/ui/ui.js @@ -9,7 +9,7 @@ export default combineReducers({ error }) -function loading(state = false, action){ +function loading(state = false, action) { switch (action.type) { case actions.SET_LOADING: { return action.payload.loading @@ -20,7 +20,7 @@ function loading(state = false, action){ } } -function search(state = '', action){ +function search(state = '', action) { switch (action.type) { case actions.SET_SEARCH: { return action.payload.search @@ -31,7 +31,7 @@ function search(state = '', action){ } } -function error(state = null, action){ +function error(state = null, action) { switch (action.type) { case actions.SET_ERROR: { return action.payload.error diff --git a/openbis_ng_ui/src/store/sagas/api.js b/openbis_ng_ui/src/store/sagas/api.js index e1cf527237829b6ddf414723d084599bc5e9530f..62c4b97b18a60a95c2c63ac2496bf1ed7d9d95ec 100644 --- a/openbis_ng_ui/src/store/sagas/api.js +++ b/openbis_ng_ui/src/store/sagas/api.js @@ -1,18 +1,28 @@ -import {put, takeEvery, apply} from 'redux-saga/effects' -import {facade} from '../../services/openbis.js' +import { put, takeEvery, apply } from 'redux-saga/effects' +import { facade } from '../../services/openbis.js' import * as actions from '../actions/actions.js' export default function* apiSaga() { yield takeEvery(actions.API_REQUEST, apiRequest) } -function* apiRequest(action){ +function* apiRequest(action) { const { method, params } = action.payload - try{ + try { let result = yield apply(facade, facade[method], params || []) - yield put(actions.apiSuccess({result, meta: Object.assign({}, action.meta, {method, params})})) - }catch(error){ - yield put(actions.apiError({error, meta: Object.assign({}, action.meta, {method, params})})) + yield put( + actions.apiSuccess({ + result, + meta: Object.assign({}, action.meta, { method, params }) + }) + ) + } catch (error) { + yield put( + actions.apiError({ + error, + meta: Object.assign({}, action.meta, { method, params }) + }) + ) } } diff --git a/openbis_ng_ui/src/store/sagas/app.js b/openbis_ng_ui/src/store/sagas/app.js index 60d368fbd63798d0b566b5a4a2e92a8a7fae6b2f..328d1306929499241049519a77c0ce5bcdab6dcf 100644 --- a/openbis_ng_ui/src/store/sagas/app.js +++ b/openbis_ng_ui/src/store/sagas/app.js @@ -1,5 +1,5 @@ -import {call, put, putAndWait, takeEvery, select} from './effects.js' -import {facade, dto} from '../../services/openbis.js' +import { call, put, putAndWait, takeEvery, select } from './effects.js' +import { facade, dto } from '../../services/openbis.js' import * as selectors from '../selectors/selectors.js' import * as actions from '../actions/actions.js' import * as objectTypes from '../../common/consts/objectType.js' @@ -17,83 +17,89 @@ export default function* appSaga() { } function* init() { - try{ + try { yield put(actions.setLoading(true)) yield call([dto, dto.init]) yield call([facade, facade.init]) - }catch(e){ + } catch (e) { yield put(actions.setError(e)) - }finally{ + } finally { yield put(actions.setLoading(false)) } } function* login(action) { - try{ + try { yield put(actions.setLoading(true)) let { username, password } = action.payload - let loginResponse = yield putAndWait(actions.apiRequest({method: 'login', params: [ username, password ]})) + let loginResponse = yield putAndWait( + actions.apiRequest({ method: 'login', params: [username, password] }) + ) - if(loginResponse.payload.result){ - yield put(actions.setSession({ - sessionToken: loginResponse.payload.result, - userName: username - })) + if (loginResponse.payload.result) { + yield put( + actions.setSession({ + sessionToken: loginResponse.payload.result, + userName: username + }) + ) let path = yield select(selectors.getRoute) let route = routes.parse(path) yield put(actions.routeChange(route.path)) - }else{ + } else { throw { message: 'Incorrect used or password' } } - }catch(e){ + } catch (e) { yield put(actions.setError(e)) - }finally{ + } finally { yield put(actions.setLoading(false)) } } function* logout() { - try{ + try { yield put(actions.setLoading(true)) - yield putAndWait(actions.apiRequest({method: 'logout'})) + yield putAndWait(actions.apiRequest({ method: 'logout' })) yield put(actions.init()) yield put(actions.routeChange('/')) - }catch(e){ + } catch (e) { yield put(actions.setError(e)) - }finally{ + } finally { yield put(actions.setLoading(false)) } } function* search(action) { - const {page, text} = action.payload - yield put(actions.objectOpen(page, objectTypes.SEARCH, text.trim())) + const { page, text } = action.payload + if (text.trim().length > 0) { + yield put(actions.objectOpen(page, objectTypes.SEARCH, text.trim())) + } yield put(actions.setSearch('')) } -function* currentPageChange(action){ +function* currentPageChange(action) { let page = action.payload.currentPage let route = yield select(selectors.getCurrentRoute, page) - if(route){ + if (route) { yield put(actions.routeChange(route)) - }else{ + } else { route = routes.format({ page }) yield put(actions.routeChange(route)) } } -function* searchChange(action){ +function* searchChange(action) { yield put(actions.setSearch(action.payload.search)) } -function* errorChange(action){ +function* errorChange(action) { yield put(actions.setError(action.payload.error)) } -function* routeChange(action){ +function* routeChange(action) { const route = action.payload.route yield put(actions.setRoute(route)) } diff --git a/openbis_ng_ui/src/store/sagas/browser/browser.js b/openbis_ng_ui/src/store/sagas/browser/browser.js index fb209d7df413ff0f239ec14c178da987ba6414dc..15794e867d5a562911c56b5a6e291bd95f9ad494 100644 --- a/openbis_ng_ui/src/store/sagas/browser/browser.js +++ b/openbis_ng_ui/src/store/sagas/browser/browser.js @@ -1,5 +1,5 @@ import _ from 'lodash' -import {put, takeEvery, select} from './../effects.js' +import { put, takeEvery, select } from './../effects.js' import * as selectors from '../../selectors/selectors.js' import * as actions from '../../actions/actions.js' @@ -31,7 +31,7 @@ function* browserInit(action) { yield put(actions.browserSetVisibleNodes(page, filteredNodes)) } -function* browserFilterChange(action){ +function* browserFilterChange(action) { let page = action.payload.page let filter = action.payload.filter @@ -39,26 +39,29 @@ function* browserFilterChange(action){ let filteredNodes = new Set(browserFilter(allNodes, filter)) let expandedNodes = [] - if(filter && filter.trim()){ + if (filter && filter.trim()) { common.mapNodes(null, allNodes, (parent, node) => { - if(_.size(node.children) > 0 && _.some(node.children, child => { - return filteredNodes.has(child.id) - })){ + if ( + _.size(node.children) > 0 && + _.some(node.children, child => { + return filteredNodes.has(child.id) + }) + ) { expandedNodes.push(node.id) } return node }) } - if(_.size(expandedNodes) > 0){ + if (_.size(expandedNodes) > 0) { yield put(actions.browserAddExpandedNodes(page, expandedNodes)) } yield put(actions.browserSetVisibleNodes(page, filteredNodes)) yield put(actions.browserSetFilter(page, action.payload.filter)) } -function* browserNodeSelect(action){ - let {page, id} = action.payload +function* browserNodeSelect(action) { + let { page, id } = action.payload let allNodes = yield select(selectors.getAllBrowserNodes, page) let allNodesAllLevels = common.getAllNodes(allNodes) @@ -66,59 +69,73 @@ function* browserNodeSelect(action){ return node.id === id }) - if(nodeWithId && nodeWithId.object){ - let idsToSelect = _.reduce(allNodesAllLevels, (array, node) => { - if(_.isEqual(nodeWithId.object, node.object)){ - array.push(node.id) - } - return array - }, []) + if (nodeWithId && nodeWithId.object) { + let idsToSelect = _.reduce( + allNodesAllLevels, + (array, node) => { + if (_.isEqual(nodeWithId.object, node.object)) { + array.push(node.id) + } + return array + }, + [] + ) yield put(actions.browserSetSelectedNodes(page, idsToSelect)) - yield put(actions.objectOpen(page, nodeWithId.object.type, nodeWithId.object.id)) - }else{ + yield put( + actions.objectOpen(page, nodeWithId.object.type, nodeWithId.object.id) + ) + } else { let idsToSelect = nodeWithId ? [nodeWithId.id] : [] yield put(actions.browserSetSelectedNodes(page, idsToSelect)) } } -function* browserNodeExpand(action){ - yield put(actions.browserAddExpandedNodes(action.payload.page, [action.payload.id])) +function* browserNodeExpand(action) { + yield put( + actions.browserAddExpandedNodes(action.payload.page, [action.payload.id]) + ) } -function* browserNodeCollapse(action){ - yield put(actions.browserRemoveExpandedNodes(action.payload.page, [action.payload.id])) +function* browserNodeCollapse(action) { + yield put( + actions.browserRemoveExpandedNodes(action.payload.page, [action.payload.id]) + ) } -function browserFilter(nodes, filter){ - filter = filter && filter.trim().toLowerCase() || '' +function browserFilter(nodes, filter) { + filter = (filter && filter.trim().toLowerCase()) || '' return common.getMatchingNodes(nodes, node => { return node.text && node.text.toLowerCase().indexOf(filter) !== -1 }) } -function* routeChange(action){ +function* routeChange(action) { let route = routes.parse(action.payload.route) let browser = yield select(selectors.getBrowser, route.page) - if(browser){ + if (browser) { let selectedObject = { type: route.type, id: route.id } let allNodes = yield select(selectors.getAllBrowserNodes, route.page) let allNodesAllLevels = common.getAllNodes(allNodes) - let idsToSelect = _.reduce(allNodesAllLevels, (array, node) => { - if(_.isEqual(selectedObject, node.object)){ - array.push(node.id) - } - return array - }, []) + let idsToSelect = _.reduce( + allNodesAllLevels, + (array, node) => { + if (_.isEqual(selectedObject, node.object)) { + array.push(node.id) + } + return array + }, + [] + ) yield put(actions.browserSetSelectedNodes(route.page, idsToSelect)) } } -function getBrowserImpl(page){ - switch(page){ - case pages.TYPES : +function getBrowserImpl(page) { + switch (page) { + case pages.TYPES: return typesBrowser case pages.USERS: return usersBrowser diff --git a/openbis_ng_ui/src/store/sagas/browser/types.js b/openbis_ng_ui/src/store/sagas/browser/types.js index dc23f3afdb989e8dbb2884eee6755e2dd717c1ff..64618235b68d071b8d8c1246af384a63b19f15a4 100644 --- a/openbis_ng_ui/src/store/sagas/browser/types.js +++ b/openbis_ng_ui/src/store/sagas/browser/types.js @@ -1,19 +1,24 @@ import _ from 'lodash' -import {putAndWait} from './../effects.js' -import {dto} from '../../../services/openbis.js' +import { putAndWait } from './../effects.js' +import { dto } from '../../../services/openbis.js' import * as objectType from '../../../common/consts/objectType.js' import * as actions from '../../actions/actions.js' import * as common from '../../common/browser.js' export function* createNodes() { - let {objectTypes, collectionTypes, dataSetTypes, materialTypes} = yield getTypes() + let { + objectTypes, + collectionTypes, + dataSetTypes, + materialTypes + } = yield getTypes() - let convert = function(types, typeName){ + let convert = function(types, typeName) { return _.map(types, type => { return { id: `${typeName}s/${type.code}`, text: type.code, - object: {type: typeName, id: type.code} + object: { type: typeName, id: type.code } } }) } @@ -28,48 +33,65 @@ export function* createNodes() { common.sortNodes(dataSetTypeNodes) common.sortNodes(materialTypeNodes) - let nodes = [{ - id: 'objectTypes', - text: 'Object Types', - children: objectTypeNodes - }, { - id: 'collectionTypes', - text: 'Collection Types', - children: collectionTypeNodes - },{ - id: 'dataSetTypes', - text: 'Data Set Types', - children: dataSetTypeNodes - },{ - id: 'materialTypes', - text: 'Material Types', - children: materialTypeNodes - }] + let nodes = [ + { + id: 'objectTypes', + text: 'Object Types', + children: objectTypeNodes + }, + { + id: 'collectionTypes', + text: 'Collection Types', + children: collectionTypeNodes + }, + { + id: 'dataSetTypes', + text: 'Data Set Types', + children: dataSetTypeNodes + }, + { + id: 'materialTypes', + text: 'Material Types', + children: materialTypeNodes + } + ] return nodes } -function* getTypes(){ +function* getTypes() { let responses = yield putAndWait({ objectTypes: actions.apiRequest({ method: 'searchSampleTypes', - params: [new dto.SampleTypeSearchCriteria(), new dto.SampleTypeFetchOptions()] + params: [ + new dto.SampleTypeSearchCriteria(), + new dto.SampleTypeFetchOptions() + ] }), collectionTypes: actions.apiRequest({ method: 'searchExperimentTypes', - params: [new dto.ExperimentTypeSearchCriteria(), new dto.ExperimentTypeFetchOptions()] + params: [ + new dto.ExperimentTypeSearchCriteria(), + new dto.ExperimentTypeFetchOptions() + ] }), dataSetTypes: actions.apiRequest({ method: 'searchDataSetTypes', - params: [new dto.DataSetTypeSearchCriteria(), new dto.DataSetTypeFetchOptions()] + params: [ + new dto.DataSetTypeSearchCriteria(), + new dto.DataSetTypeFetchOptions() + ] }), materialTypes: actions.apiRequest({ method: 'searchMaterialTypes', - params: [new dto.MaterialTypeSearchCriteria(), new dto.MaterialTypeFetchOptions()] + params: [ + new dto.MaterialTypeSearchCriteria(), + new dto.MaterialTypeFetchOptions() + ] }) }) - let convert = function(response){ + let convert = function(response) { return response.payload.result.getObjects().map(type => { return { code: type.getCode(), diff --git a/openbis_ng_ui/src/store/sagas/browser/users.js b/openbis_ng_ui/src/store/sagas/browser/users.js index f81deccee23883bab860505fb8d18e9d9f2a884d..265b6fc3c57c8f94ef0036d418dbb07880ff226b 100644 --- a/openbis_ng_ui/src/store/sagas/browser/users.js +++ b/openbis_ng_ui/src/store/sagas/browser/users.js @@ -1,12 +1,12 @@ import _ from 'lodash' -import {putAndWait} from './../effects.js' -import {dto} from '../../../services/openbis.js' +import { putAndWait } from './../effects.js' +import { dto } from '../../../services/openbis.js' import * as objectType from '../../../common/consts/objectType.js' import * as actions from '../../actions/actions.js' import * as common from '../../common/browser.js' export function* createNodes() { - let {users, groups} = yield getUsersAndGroups() + let { users, groups } = yield getUsersAndGroups() let userNodes = _.map(users, user => { return { @@ -44,32 +44,39 @@ export function* createNodes() { common.sortNodes(groupNodes) - let nodes = [{ - id: 'users', - text: 'Users', - children: userNodes - }, { - id: 'groups', - text: 'Groups', - children: groupNodes - }] + let nodes = [ + { + id: 'users', + text: 'Users', + children: userNodes + }, + { + id: 'groups', + text: 'Groups', + children: groupNodes + } + ] return nodes } -function* getUsersAndGroups(){ - let getUsersReponse = yield putAndWait(actions.apiRequest({ - method: 'searchPersons', - params: [new dto.PersonSearchCriteria(), new dto.PersonFetchOptions()] - })) +function* getUsersAndGroups() { + let getUsersReponse = yield putAndWait( + actions.apiRequest({ + method: 'searchPersons', + params: [new dto.PersonSearchCriteria(), new dto.PersonFetchOptions()] + }) + ) let groupFetchOptions = new dto.AuthorizationGroupFetchOptions() groupFetchOptions.withUsers() - let getGroupsReponse = yield putAndWait(actions.apiRequest({ - method: 'searchAuthorizationGroups', - params: [new dto.AuthorizationGroupSearchCriteria(), groupFetchOptions] - })) + let getGroupsReponse = yield putAndWait( + actions.apiRequest({ + method: 'searchAuthorizationGroups', + params: [new dto.AuthorizationGroupSearchCriteria(), groupFetchOptions] + }) + ) let users = {} let groups = {} @@ -88,7 +95,7 @@ function* getUsersAndGroups(){ code: group.code, userIds: group.users.reduce((groupUserIds, groupUser) => { let user = users[groupUser.userId] - if(user){ + if (user) { user.groupIds.push(group.code) groupUserIds.push(user.userId) } @@ -97,5 +104,5 @@ function* getUsersAndGroups(){ } }) - return {users, groups} + return { users, groups } } diff --git a/openbis_ng_ui/src/store/sagas/effects.js b/openbis_ng_ui/src/store/sagas/effects.js index b61930b7ef88929d50ff18f3e57e6372a6a8a151..783b763f1094e76fd57abecff831e895a92e0f7b 100644 --- a/openbis_ng_ui/src/store/sagas/effects.js +++ b/openbis_ng_ui/src/store/sagas/effects.js @@ -1,15 +1,18 @@ import _ from 'lodash' -import {put, take} from 'redux-saga/effects' +import { put, take } from 'redux-saga/effects' -function generateCorrelationId(){ +function generateCorrelationId() { let date = new Date() - let timestamp = date.getUTCHours() + ':' + date.getUTCMinutes() + ':' + date.getUTCSeconds() + let timestamp = + date.getUTCHours() + ':' + date.getUTCMinutes() + ':' + date.getUTCSeconds() let random = Math.floor(Math.random() * 1000000) return timestamp + '-' + random } -export function* putAndWait(actionOrActionsMap){ - let actionsMap = _.isString(actionOrActionsMap.type) ? {action: actionOrActionsMap} : actionOrActionsMap +export function* putAndWait(actionOrActionsMap) { + let actionsMap = _.isString(actionOrActionsMap.type) + ? { action: actionOrActionsMap } + : actionOrActionsMap let correlationId = generateCorrelationId() let correlationKeys = [] @@ -20,19 +23,23 @@ export function* putAndWait(actionOrActionsMap){ actions.push(action) }) - for(let i = 0; i < actions.length; i++){ + for (let i = 0; i < actions.length; i++) { let actionWithCorrelation = Object.assign(actions[i], { - meta: { correlation: { id: correlationId, key: correlationKeys[i] }} + meta: { correlation: { id: correlationId, key: correlationKeys[i] } } }) yield put(actionWithCorrelation) } let responsesMap = {} - while(_.size(responsesMap) < correlationKeys.length){ + while (_.size(responsesMap) < correlationKeys.length) { let potentialResponse = yield take('*') - if(potentialResponse.meta && potentialResponse.meta.correlation && potentialResponse.meta.correlation.id === correlationId){ - if(potentialResponse.payload.error){ + if ( + potentialResponse.meta && + potentialResponse.meta.correlation && + potentialResponse.meta.correlation.id === correlationId + ) { + if (potentialResponse.payload.error) { throw potentialResponse.payload.error } let correlationKey = potentialResponse.meta.correlation.key @@ -40,7 +47,9 @@ export function* putAndWait(actionOrActionsMap){ } } - return _.isString(actionOrActionsMap.type) ? responsesMap.action : responsesMap + return _.isString(actionOrActionsMap.type) + ? responsesMap.action + : responsesMap } export * from 'redux-saga/effects' diff --git a/openbis_ng_ui/src/store/sagas/page.js b/openbis_ng_ui/src/store/sagas/page.js index 61a9b07ce61d86d3fa27519a36319f9897d90f9c..72ff8997676bc7b259be6ef073373977e466b2a4 100644 --- a/openbis_ng_ui/src/store/sagas/page.js +++ b/openbis_ng_ui/src/store/sagas/page.js @@ -1,5 +1,5 @@ import _ from 'lodash' -import {put, takeEvery, select} from './effects.js' +import { put, takeEvery, select } from './effects.js' import * as selectors from '../selectors/selectors.js' import * as actions from '../actions/actions.js' import routes from '../../common/consts/routes.js' @@ -22,9 +22,9 @@ function* objectOpen(action) { function* objectChange(action) { let { page, type, id, changed } = action.payload - if(changed){ + if (changed) { yield put(actions.addChangedObject(page, type, id)) - }else{ + } else { yield put(actions.removeChangedObject(page, type, id)) } } @@ -35,14 +35,18 @@ function* objectClose(action) { let selectedObject = yield select(getSelectedObject, page) let openObjects = yield select(selectors.getOpenObjects, page) - if(selectedObject && selectedObject.type === type && selectedObject.id === id){ - if(_.size(openObjects) === 1){ + if ( + selectedObject && + selectedObject.type === type && + selectedObject.id === id + ) { + if (_.size(openObjects) === 1) { selectedObject = null - }else{ + } else { let selectedIndex = _.findIndex(openObjects, selectedObject) - if(selectedIndex === 0){ + if (selectedIndex === 0) { selectedObject = openObjects[selectedIndex + 1] - }else{ + } else { selectedObject = openObjects[selectedIndex - 1] } } @@ -51,23 +55,27 @@ function* objectClose(action) { yield put(actions.removeOpenObject(page, type, id)) yield put(actions.removeChangedObject(page, type, id)) - if(selectedObject){ - let route = routes.format({ page, type: selectedObject.type, id: selectedObject.id }) + if (selectedObject) { + let route = routes.format({ + page, + type: selectedObject.type, + id: selectedObject.id + }) yield put(actions.routeChange(route)) - }else{ + } else { let route = routes.format({ page }) yield put(actions.routeChange(route)) } } -function* routeChange(action){ +function* routeChange(action) { let route = routes.parse(action.payload.route) - if(route.type && route.id){ + if (route.type && route.id) { let openObjects = yield select(selectors.getOpenObjects, route.page) let selectedObject = { type: route.type, id: route.id } - if(_.findIndex(openObjects, selectedObject) === -1){ + if (_.findIndex(openObjects, selectedObject) === -1) { yield put(actions.addOpenObject(route.page, route.type, route.id)) } } diff --git a/openbis_ng_ui/src/store/sagas/sagas.js b/openbis_ng_ui/src/store/sagas/sagas.js index 7bc02f20265a2f4b9ded3f5c668d8f2af66557b2..e7e6363f978643533d1da74cef346542450897e2 100644 --- a/openbis_ng_ui/src/store/sagas/sagas.js +++ b/openbis_ng_ui/src/store/sagas/sagas.js @@ -1,14 +1,9 @@ -import {all} from 'redux-saga/effects' +import { all } from 'redux-saga/effects' import app from './app.js' import api from './api.js' import page from './page.js' import browser from './browser/browser.js' export default function* root() { - yield all([ - api(), - app(), - page(), - browser() - ]) + yield all([api(), app(), page(), browser()]) } diff --git a/openbis_ng_ui/src/store/selectors/app.js b/openbis_ng_ui/src/store/selectors/app.js index 7814cdb1457b768314ac9521bfdf6d40fe033068..23cdfafef467958a274b45d2ea9f75aa195d5767 100644 --- a/openbis_ng_ui/src/store/selectors/app.js +++ b/openbis_ng_ui/src/store/selectors/app.js @@ -1,26 +1,26 @@ import routes from '../../common/consts/routes.js' -export const getLoading = (state) => { +export const getLoading = state => { return state.ui.loading } -export const getSearch = (state) => { +export const getSearch = state => { return state.ui.search } -export const getRoute = (state) => { +export const getRoute = state => { return state.route } -export const getCurrentPage = (state) => { +export const getCurrentPage = state => { let route = routes.parse(state.route) return route.page } -export const getError = (state) => { +export const getError = state => { return state.ui.error } -export const getSession = (state) => { +export const getSession = state => { return state.session } diff --git a/openbis_ng_ui/src/store/selectors/browser.js b/openbis_ng_ui/src/store/selectors/browser.js index 71e1b19b0b80726e3bd138e1dcaf5af63ec35c8e..702e51331fa081cd44fd8e2fe3c816327cd287c3 100644 --- a/openbis_ng_ui/src/store/selectors/browser.js +++ b/openbis_ng_ui/src/store/selectors/browser.js @@ -35,17 +35,17 @@ export const createGetBrowserNodes = () => { let nodes = browser.nodes nodes = common.mapNodes(null, nodes, (parent, node) => { - if(visibleNodes.has(node.id)){ + if (visibleNodes.has(node.id)) { let selected = selectedNodes.has(node.id) let expanded = expandedNodes.has(node.id) return Object.assign({ selected, expanded }, node) - }else{ + } else { return null } }) nodes = common.mapNodes(null, nodes, (parent, node) => { - if(_.size(node.children) === 0){ + if (_.size(node.children) === 0) { delete node['children'] } return node diff --git a/openbis_ng_ui/src/store/selectors/page.js b/openbis_ng_ui/src/store/selectors/page.js index e8a31f0a8f0b6e621a4d5a73307340a0d217da9f..6318cbba7549c16e3494449ba7a2a8f9ebda4cfa 100644 --- a/openbis_ng_ui/src/store/selectors/page.js +++ b/openbis_ng_ui/src/store/selectors/page.js @@ -22,9 +22,9 @@ export const createGetSelectedObject = () => { [getCurrentRoute], path => { logger.log(logger.DEBUG, 'pageSelector.getSelectedObject') - if(path){ + if (path) { let route = routes.parse(path) - if(route && route.type && route.id){ + if (route && route.type && route.id) { return { type: route.type, id: route.id } } } diff --git a/openbis_ng_ui/src/store/store.js b/openbis_ng_ui/src/store/store.js index bdf32e07bbb2ae9be8220b6ea4d6cd2313b36614..91f70997aec01d8245fb794b858bd1033d361d2e 100644 --- a/openbis_ng_ui/src/store/store.js +++ b/openbis_ng_ui/src/store/store.js @@ -5,11 +5,15 @@ import rootReducer from './reducers/reducers.js' import rootSaga from './sagas/sagas.js' import history from './history.js' -function createStoreWithMiddleware(){ +function createStoreWithMiddleware() { const sagaMiddleware = createSagaMiddleware() - const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose + const composeEnhancers = + window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose - let store = createStore(rootReducer, composeEnhancers(applyMiddleware(...middlewares, sagaMiddleware))) + let store = createStore( + rootReducer, + composeEnhancers(applyMiddleware(...middlewares, sagaMiddleware)) + ) sagaMiddleware.run(rootSaga) history.configure(store) diff --git a/openbis_ng_ui/srcTest/common/fixture.js b/openbis_ng_ui/srcTest/common/fixture.js index 31054125e47f115f8e56d9f33dcbaa4d97837dd5..1f7035e15c2f4ccb751f88149151936259e6936a 100644 --- a/openbis_ng_ui/srcTest/common/fixture.js +++ b/openbis_ng_ui/srcTest/common/fixture.js @@ -16,19 +16,19 @@ export const ANOTHER_USER_DTO = { export const ALL_USERS_GROUP_DTO = { code: 'all-users-group', - users: [ TEST_USER_DTO, ANOTHER_USER_DTO ] + users: [TEST_USER_DTO, ANOTHER_USER_DTO] } export const TEST_GROUP_DTO = { code: 'test-group', - users: [ TEST_USER_DTO ] + users: [TEST_USER_DTO] } export const ANOTHER_GROUP_DTO = { code: 'another-group', - users: [ ANOTHER_USER_DTO ] + users: [ANOTHER_USER_DTO] } -export function object(type, id){ +export function object(type, id) { return { type, id } } diff --git a/openbis_ng_ui/srcTest/components/common/browser/browser.test.js b/openbis_ng_ui/srcTest/components/common/browser/browser.test.js index 49aa1fef982ebc7ad35daca9d3b5745efe0b6c21..505a6f71be441547f5710d7b556f5f93979a5a39 100644 --- a/openbis_ng_ui/srcTest/components/common/browser/browser.test.js +++ b/openbis_ng_ui/srcTest/components/common/browser/browser.test.js @@ -17,30 +17,33 @@ beforeEach(() => { }) describe('browser', () => { - test('test', () => { facade.searchPersons.mockReturnValue({ - objects: [ fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO ] + objects: [fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO] }) facade.searchAuthorizationGroups.mockReturnValue({ - objects: [ fixture.TEST_GROUP_DTO, fixture.ANOTHER_GROUP_DTO, fixture.ALL_USERS_GROUP_DTO ] + objects: [ + fixture.TEST_GROUP_DTO, + fixture.ANOTHER_GROUP_DTO, + fixture.ALL_USERS_GROUP_DTO + ] }) dto.AuthorizationGroupFetchOptions.mockImplementation(() => { return { - withUsers: function(){} + withUsers: function() {} } }) store.dispatch(actions.init()) - let wrapper = mount(<Browser store={store} page={pages.USERS}/>) + let wrapper = mount(<Browser store={store} page={pages.USERS} />) expectFilter(wrapper, '') expectNodes(wrapper, [ - { level: 0, text: 'Users'}, - { level: 0, text: 'Groups'} + { level: 0, text: 'Users' }, + { level: 0, text: 'Groups' } ]) simulateNodeIconClick(wrapper, 'users') @@ -48,10 +51,10 @@ describe('browser', () => { expectFilter(wrapper, '') expectNodes(wrapper, [ - { level: 0, text: 'Users'}, - { level: 1, text: fixture.ANOTHER_USER_DTO.userId}, - { level: 1, text: fixture.TEST_USER_DTO.userId}, - { level: 0, text: 'Groups'} + { level: 0, text: 'Users' }, + { level: 1, text: fixture.ANOTHER_USER_DTO.userId }, + { level: 1, text: fixture.TEST_USER_DTO.userId }, + { level: 0, text: 'Groups' } ]) simulateFilterChange(wrapper, fixture.ANOTHER_GROUP_DTO.code.toUpperCase()) @@ -59,36 +62,39 @@ describe('browser', () => { expectFilter(wrapper, fixture.ANOTHER_GROUP_DTO.code.toUpperCase()) expectNodes(wrapper, [ - { level: 0, text: 'Users'}, - { level: 1, text: fixture.ANOTHER_USER_DTO.userId}, - { level: 2, text: fixture.ANOTHER_GROUP_DTO.code}, - { level: 0, text: 'Groups'}, - { level: 1, text: fixture.ANOTHER_GROUP_DTO.code} + { level: 0, text: 'Users' }, + { level: 1, text: fixture.ANOTHER_USER_DTO.userId }, + { level: 2, text: fixture.ANOTHER_GROUP_DTO.code }, + { level: 0, text: 'Groups' }, + { level: 1, text: fixture.ANOTHER_GROUP_DTO.code } ]) }) - }) -function simulateNodeIconClick(wrapper, id){ - wrapper.findWhere(node => { - return node.name() === 'BrowserNode' && node.prop('node').id === id - }).find('svg').first().simulate('click') +function simulateNodeIconClick(wrapper, id) { + wrapper + .findWhere(node => { + return node.name() === 'BrowserNode' && node.prop('node').id === id + }) + .find('svg') + .first() + .simulate('click') } -function simulateFilterChange(wrapper, filter){ +function simulateFilterChange(wrapper, filter) { let input = wrapper.find('FilterField').find('input') input.instance().value = filter input.simulate('change') } -function expectFilter(wrapper, expectedFilter){ +function expectFilter(wrapper, expectedFilter) { const actualFilter = wrapper.find('FilterField').map(node => { return node.prop('filter') })[0] expect(actualFilter).toEqual(expectedFilter) } -function expectNodes(wrapper, expectedNodes){ +function expectNodes(wrapper, expectedNodes) { const actualNodes = wrapper.find('BrowserNode').map(node => { const text = node.prop('node').text const selected = node.prop('node').selected diff --git a/openbis_ng_ui/srcTest/components/types/objectType/objectType.test.js b/openbis_ng_ui/srcTest/components/types/objectType/objectType.test.js index ed87ef51021265d3d00b1a7bdbcedeb75bac607a..3067e88d92cf32c9e7ba560960038b8e90c2480f 100644 --- a/openbis_ng_ui/srcTest/components/types/objectType/objectType.test.js +++ b/openbis_ng_ui/srcTest/components/types/objectType/objectType.test.js @@ -16,9 +16,7 @@ beforeEach(() => { }) describe('browser', () => { - - test('test', (done) => { - + test('test', done => { dto.SampleTypeFetchOptions.mockImplementation(() => ({ withPropertyAssignments: () => ({ withPropertyType: () => ({ @@ -46,79 +44,103 @@ describe('browser', () => { }) })) - facade.getSampleTypes.mockReturnValue(Promise.resolve({ - 'TEST_OBJECT_TYPE': { - code: 'TEST_OBJECT_TYPE', - propertyAssignments: [{ - propertyType: { - code: 'VARCHAR_PROPERTY_TYPE', - label: 'varchar label', - description: 'varchar description', - dataType: 'VARCHAR' - }, - mandatory: false - },{ - propertyType: { - code: 'MATERIAL_PROPERTY_TYPE', - label: 'material label', - description: 'material description', - dataType: 'MATERIAL' - }, - mandatory: true - },{ - propertyType: { - code: 'DICTIONARY_PROPERTY_TYPE', - label: 'dictionary label', - description: 'dictionary description', - dataType: 'CONTROLLEDVOCABULARY', - vocabulary: { - code: 'VOCABULARY_1' + facade.getSampleTypes.mockReturnValue( + Promise.resolve({ + TEST_OBJECT_TYPE: { + code: 'TEST_OBJECT_TYPE', + propertyAssignments: [ + { + propertyType: { + code: 'VARCHAR_PROPERTY_TYPE', + label: 'varchar label', + description: 'varchar description', + dataType: 'VARCHAR' + }, + mandatory: false + }, + { + propertyType: { + code: 'MATERIAL_PROPERTY_TYPE', + label: 'material label', + description: 'material description', + dataType: 'MATERIAL' + }, + mandatory: true + }, + { + propertyType: { + code: 'DICTIONARY_PROPERTY_TYPE', + label: 'dictionary label', + description: 'dictionary description', + dataType: 'CONTROLLEDVOCABULARY', + vocabulary: { + code: 'VOCABULARY_1' + } + }, + mandatory: true } - }, - mandatory: true - }] - } - })) + ] + } + }) + ) - facade.searchPropertyTypes.mockReturnValue(Promise.resolve({ - objects: [{ - code: 'VARCHAR_PROPERTY_TYPE' - },{ - code: 'MATERIAL_PROPERTY_TYPE' - },{ - code: 'DICTIONARY_PROPERTY_TYPE' - }] - })) + facade.searchPropertyTypes.mockReturnValue( + Promise.resolve({ + objects: [ + { + code: 'VARCHAR_PROPERTY_TYPE' + }, + { + code: 'MATERIAL_PROPERTY_TYPE' + }, + { + code: 'DICTIONARY_PROPERTY_TYPE' + } + ] + }) + ) - facade.searchMaterials.mockReturnValue(Promise.resolve({ - objects: [{ - code: 'MATERIAL_1' - },{ - code: 'MATERIAL_2' - }, { - code: 'MATERIAL_3' - }] - })) + facade.searchMaterials.mockReturnValue( + Promise.resolve({ + objects: [ + { + code: 'MATERIAL_1' + }, + { + code: 'MATERIAL_2' + }, + { + code: 'MATERIAL_3' + } + ] + }) + ) - facade.searchVocabularyTerms.mockReturnValue(Promise.resolve({ - objects: [{ - code: 'TERM_1' - },{ - code: 'TERM_2' - }, { - code: 'TERM_3' - }] - })) + facade.searchVocabularyTerms.mockReturnValue( + Promise.resolve({ + objects: [ + { + code: 'TERM_1' + }, + { + code: 'TERM_2' + }, + { + code: 'TERM_3' + } + ] + }) + ) store.dispatch(actions.init()) let wrapper = mount( <DragAndDropProvider> - <ObjectType store={store} objectId="TEST_OBJECT_TYPE"/> + <ObjectType store={store} objectId='TEST_OBJECT_TYPE' /> </DragAndDropProvider> ) - setTimeout(()=>{ + setTimeout(() => { wrapper.update() expectTitle(wrapper, 'TEST_OBJECT_TYPE') @@ -128,47 +150,62 @@ describe('browser', () => { expectPropertyMandatory(wrapper, 1, 'true') expectPropertyType(wrapper, 1, 'MATERIAL_PROPERTY_TYPE') - expectPropertyPreviewMaterial(wrapper, 1, ['', 'MATERIAL_1', 'MATERIAL_2', 'MATERIAL_3']) + expectPropertyPreviewMaterial(wrapper, 1, [ + '', + 'MATERIAL_1', + 'MATERIAL_2', + 'MATERIAL_3' + ]) expectPropertyMandatory(wrapper, 2, 'true') expectPropertyType(wrapper, 2, 'DICTIONARY_PROPERTY_TYPE') - expectPropertyPreviewDictionary(wrapper, 2, ['', 'TERM_1', 'TERM_2', 'TERM_3']) + expectPropertyPreviewDictionary(wrapper, 2, [ + '', + 'TERM_1', + 'TERM_2', + 'TERM_3' + ]) done() }, 0) }) - }) -function expectTitle(wrapper, expectedTitle){ +function expectTitle(wrapper, expectedTitle) { const actualTitle = wrapper.find('ObjectTypeTitle').text() expect(actualTitle).toEqual(expectedTitle) } -function expectPropertyMandatory(wrapper, index, expected){ +function expectPropertyMandatory(wrapper, index, expected) { let row = wrapper.find('ObjectTypePropertyRow').at(index) let actual = row.find('ObjectTypePropertyMandatory').text() expect(actual).toEqual(expected) } -function expectPropertyType(wrapper, index, expected){ +function expectPropertyType(wrapper, index, expected) { let row = wrapper.find('ObjectTypePropertyRow').at(index) let actual = row.find('ObjectTypePropertyType').text() expect(actual).toEqual(expected) } -function expectPropertyPreviewMaterial(wrapper, index, expected){ +function expectPropertyPreviewMaterial(wrapper, index, expected) { let row = wrapper.find('ObjectTypePropertyRow').at(index) - let actual = row.find('ObjectTypePropertyPreview').find('option').map(node => { - return node.prop('value') - }) + let actual = row + .find('ObjectTypePropertyPreview') + .find('option') + .map(node => { + return node.prop('value') + }) expect(actual).toEqual(expected) } -function expectPropertyPreviewDictionary(wrapper, index, expected){ +function expectPropertyPreviewDictionary(wrapper, index, expected) { let row = wrapper.find('ObjectTypePropertyRow').at(index) - let actual = row.find('ObjectTypePropertyPreview').find('option').map(node => { - return node.prop('value') - }) + let actual = row + .find('ObjectTypePropertyPreview') + .find('option') + .map(node => { + return node.prop('value') + }) expect(actual).toEqual(expected) } diff --git a/openbis_ng_ui/srcTest/store/sagas/app.test.js b/openbis_ng_ui/srcTest/store/sagas/app.test.js index a1bd8132322adcb742c3c00048df050c00ef1dcf..65f601caa7c8e58076160d2ce73e75a1efd7422b 100644 --- a/openbis_ng_ui/srcTest/store/sagas/app.test.js +++ b/openbis_ng_ui/srcTest/store/sagas/app.test.js @@ -35,7 +35,9 @@ describe('app', () => { const state = store.getState() expect(selectors.getSession(state)).toBeNull() - expect(selectors.getError(state)).toEqual({ message: 'Incorrect used or password' }) + expect(selectors.getError(state)).toEqual({ + message: 'Incorrect used or password' + }) }) test('logout', () => { diff --git a/openbis_ng_ui/srcTest/store/sagas/browser.test.js b/openbis_ng_ui/srcTest/store/sagas/browser.test.js index 50bdffa3e85442225f790c6511ead1a7b4a7912d..ccb87d516d9708a8493bab4a1ecf22fb42d1eb78 100644 --- a/openbis_ng_ui/srcTest/store/sagas/browser.test.js +++ b/openbis_ng_ui/srcTest/store/sagas/browser.test.js @@ -22,19 +22,22 @@ beforeEach(() => { }) describe('browser', () => { - test('init', () => { facade.searchPersons.mockReturnValue({ - objects: [ fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO ] + objects: [fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO] }) facade.searchAuthorizationGroups.mockReturnValue({ - objects: [ fixture.TEST_GROUP_DTO, fixture.ANOTHER_GROUP_DTO, fixture.ALL_USERS_GROUP_DTO ] + objects: [ + fixture.TEST_GROUP_DTO, + fixture.ANOTHER_GROUP_DTO, + fixture.ALL_USERS_GROUP_DTO + ] }) dto.AuthorizationGroupFetchOptions.mockImplementation(() => { return { - withUsers: function(){} + withUsers: function() {} } }) @@ -44,24 +47,56 @@ describe('browser', () => { expectNodes(selectors.createGetBrowserNodes()(state, pages.USERS), [ node(['users'], false, false, [ node(['users', fixture.ANOTHER_USER_DTO.userId], false, false, [ - node(['users', fixture.ANOTHER_USER_DTO.userId, fixture.ALL_USERS_GROUP_DTO.code]), - node(['users', fixture.ANOTHER_USER_DTO.userId, fixture.ANOTHER_GROUP_DTO.code]) + node([ + 'users', + fixture.ANOTHER_USER_DTO.userId, + fixture.ALL_USERS_GROUP_DTO.code + ]), + node([ + 'users', + fixture.ANOTHER_USER_DTO.userId, + fixture.ANOTHER_GROUP_DTO.code + ]) ]), node(['users', fixture.TEST_USER_DTO.userId], false, false, [ - node(['users', fixture.TEST_USER_DTO.userId, fixture.ALL_USERS_GROUP_DTO.code]), - node(['users', fixture.TEST_USER_DTO.userId, fixture.TEST_GROUP_DTO.code]) + node([ + 'users', + fixture.TEST_USER_DTO.userId, + fixture.ALL_USERS_GROUP_DTO.code + ]), + node([ + 'users', + fixture.TEST_USER_DTO.userId, + fixture.TEST_GROUP_DTO.code + ]) ]) ]), node(['groups'], false, false, [ node(['groups', fixture.ALL_USERS_GROUP_DTO.code], false, false, [ - node(['groups', fixture.ALL_USERS_GROUP_DTO.code, fixture.ANOTHER_USER_DTO.userId]), - node(['groups', fixture.ALL_USERS_GROUP_DTO.code, fixture.TEST_USER_DTO.userId]) + node([ + 'groups', + fixture.ALL_USERS_GROUP_DTO.code, + fixture.ANOTHER_USER_DTO.userId + ]), + node([ + 'groups', + fixture.ALL_USERS_GROUP_DTO.code, + fixture.TEST_USER_DTO.userId + ]) ]), node(['groups', fixture.ANOTHER_GROUP_DTO.code], false, false, [ - node(['groups', fixture.ANOTHER_GROUP_DTO.code, fixture.ANOTHER_USER_DTO.userId]) + node([ + 'groups', + fixture.ANOTHER_GROUP_DTO.code, + fixture.ANOTHER_USER_DTO.userId + ]) ]), node(['groups', fixture.TEST_GROUP_DTO.code], false, false, [ - node(['groups', fixture.TEST_GROUP_DTO.code, fixture.TEST_USER_DTO.userId]) + node([ + 'groups', + fixture.TEST_GROUP_DTO.code, + fixture.TEST_USER_DTO.userId + ]) ]) ]) ]) @@ -72,27 +107,40 @@ describe('browser', () => { test('filter', () => { facade.searchPersons.mockReturnValue({ - objects: [ fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO ] + objects: [fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO] }) facade.searchAuthorizationGroups.mockReturnValue({ - objects: [ fixture.TEST_GROUP_DTO, fixture.ANOTHER_GROUP_DTO, fixture.ALL_USERS_GROUP_DTO ] + objects: [ + fixture.TEST_GROUP_DTO, + fixture.ANOTHER_GROUP_DTO, + fixture.ALL_USERS_GROUP_DTO + ] }) dto.AuthorizationGroupFetchOptions.mockImplementation(() => { return { - withUsers: function(){} + withUsers: function() {} } }) store.dispatch(actions.browserInit(pages.USERS)) - store.dispatch(actions.browserFilterChange(pages.USERS, fixture.ANOTHER_GROUP_DTO.code.toUpperCase())) + store.dispatch( + actions.browserFilterChange( + pages.USERS, + fixture.ANOTHER_GROUP_DTO.code.toUpperCase() + ) + ) const state = store.getState() expectNodes(selectors.createGetBrowserNodes()(state, pages.USERS), [ node(['users'], true, false, [ node(['users', fixture.ANOTHER_USER_DTO.userId], true, false, [ - node(['users', fixture.ANOTHER_USER_DTO.userId, fixture.ANOTHER_GROUP_DTO.code]) + node([ + 'users', + fixture.ANOTHER_USER_DTO.userId, + fixture.ANOTHER_GROUP_DTO.code + ]) ]) ]), node(['groups'], true, false, [ @@ -106,7 +154,7 @@ describe('browser', () => { test('select node', () => { facade.searchPersons.mockReturnValue({ - objects: [ fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO ] + objects: [fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO] }) facade.searchAuthorizationGroups.mockReturnValue({ @@ -115,14 +163,22 @@ describe('browser', () => { dto.AuthorizationGroupFetchOptions.mockImplementation(() => { return { - withUsers: function(){} + withUsers: function() {} } }) - let testUserObject = fixture.object(objectType.USER, fixture.TEST_USER_DTO.userId) + let testUserObject = fixture.object( + objectType.USER, + fixture.TEST_USER_DTO.userId + ) store.dispatch(actions.browserInit(pages.USERS)) - store.dispatch(actions.browserNodeSelect(pages.USERS, nodeId(['users', fixture.TEST_USER_DTO.userId]))) + store.dispatch( + actions.browserNodeSelect( + pages.USERS, + nodeId(['users', fixture.TEST_USER_DTO.userId]) + ) + ) let state = store.getState() expectNodes(selectors.createGetBrowserNodes()(state, pages.USERS), [ @@ -139,7 +195,7 @@ describe('browser', () => { test('select another node', () => { facade.searchPersons.mockReturnValue({ - objects: [ fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO ] + objects: [fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO] }) facade.searchAuthorizationGroups.mockReturnValue({ @@ -148,16 +204,32 @@ describe('browser', () => { dto.AuthorizationGroupFetchOptions.mockImplementation(() => { return { - withUsers: function(){} + withUsers: function() {} } }) - let testUserObject = fixture.object(objectType.USER, fixture.TEST_USER_DTO.userId) - let anotherUserObject = fixture.object(objectType.USER, fixture.ANOTHER_USER_DTO.userId) + let testUserObject = fixture.object( + objectType.USER, + fixture.TEST_USER_DTO.userId + ) + let anotherUserObject = fixture.object( + objectType.USER, + fixture.ANOTHER_USER_DTO.userId + ) store.dispatch(actions.browserInit(pages.USERS)) - store.dispatch(actions.browserNodeSelect(pages.USERS, nodeId(['users', fixture.TEST_USER_DTO.userId]))) - store.dispatch(actions.browserNodeSelect(pages.USERS, nodeId(['users', fixture.ANOTHER_USER_DTO.userId]))) + store.dispatch( + actions.browserNodeSelect( + pages.USERS, + nodeId(['users', fixture.TEST_USER_DTO.userId]) + ) + ) + store.dispatch( + actions.browserNodeSelect( + pages.USERS, + nodeId(['users', fixture.ANOTHER_USER_DTO.userId]) + ) + ) let state = store.getState() expectNodes(selectors.createGetBrowserNodes()(state, pages.USERS), [ @@ -174,7 +246,7 @@ describe('browser', () => { test('select virtual node', () => { facade.searchPersons.mockReturnValue({ - objects: [ fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO ] + objects: [fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO] }) facade.searchAuthorizationGroups.mockReturnValue({ @@ -183,7 +255,7 @@ describe('browser', () => { dto.AuthorizationGroupFetchOptions.mockImplementation(() => { return { - withUsers: function(){} + withUsers: function() {} } }) @@ -205,34 +277,58 @@ describe('browser', () => { test('select two nodes that represent the same object', () => { facade.searchPersons.mockReturnValue({ - objects: [ fixture.TEST_USER_DTO ] + objects: [fixture.TEST_USER_DTO] }) facade.searchAuthorizationGroups.mockReturnValue({ - objects: [ fixture.TEST_GROUP_DTO ] + objects: [fixture.TEST_GROUP_DTO] }) dto.AuthorizationGroupFetchOptions.mockImplementation(() => { return { - withUsers: function(){} + withUsers: function() {} } }) - let testUserObject = fixture.object(objectType.USER, fixture.TEST_USER_DTO.userId) + let testUserObject = fixture.object( + objectType.USER, + fixture.TEST_USER_DTO.userId + ) store.dispatch(actions.browserInit(pages.USERS)) - store.dispatch(actions.browserNodeSelect(pages.USERS, nodeId(['users', fixture.TEST_USER_DTO.userId]))) + store.dispatch( + actions.browserNodeSelect( + pages.USERS, + nodeId(['users', fixture.TEST_USER_DTO.userId]) + ) + ) let state = store.getState() expectNodes(selectors.createGetBrowserNodes()(state, pages.USERS), [ node(['users'], false, false, [ node(['users', fixture.TEST_USER_DTO.userId], false, true, [ - node(['users', fixture.TEST_USER_DTO.userId, fixture.TEST_GROUP_DTO.code], false, false) + node( + [ + 'users', + fixture.TEST_USER_DTO.userId, + fixture.TEST_GROUP_DTO.code + ], + false, + false + ) ]) ]), node(['groups'], false, false, [ node(['groups', fixture.TEST_GROUP_DTO.code], false, false, [ - node(['groups', fixture.TEST_GROUP_DTO.code, fixture.TEST_USER_DTO.userId], false, true) + node( + [ + 'groups', + fixture.TEST_GROUP_DTO.code, + fixture.TEST_USER_DTO.userId + ], + false, + true + ) ]) ]) ]) @@ -240,18 +336,43 @@ describe('browser', () => { expectSelectedObject(pages.USERS, testUserObject) expectOpenObjects(pages.USERS, [testUserObject]) - store.dispatch(actions.browserNodeSelect(pages.USERS, nodeId(['groups', fixture.TEST_GROUP_DTO.code, fixture.TEST_USER_DTO.userId]))) + store.dispatch( + actions.browserNodeSelect( + pages.USERS, + nodeId([ + 'groups', + fixture.TEST_GROUP_DTO.code, + fixture.TEST_USER_DTO.userId + ]) + ) + ) state = store.getState() expectNodes(selectors.createGetBrowserNodes()(state, pages.USERS), [ node(['users'], false, false, [ node(['users', fixture.TEST_USER_DTO.userId], false, true, [ - node(['users', fixture.TEST_USER_DTO.userId, fixture.TEST_GROUP_DTO.code], false, false) + node( + [ + 'users', + fixture.TEST_USER_DTO.userId, + fixture.TEST_GROUP_DTO.code + ], + false, + false + ) ]) ]), node(['groups'], false, false, [ node(['groups', fixture.TEST_GROUP_DTO.code], false, false, [ - node(['groups', fixture.TEST_GROUP_DTO.code, fixture.TEST_USER_DTO.userId], false, true) + node( + [ + 'groups', + fixture.TEST_GROUP_DTO.code, + fixture.TEST_USER_DTO.userId + ], + false, + true + ) ]) ]) ]) @@ -266,12 +387,12 @@ describe('browser', () => { }) facade.searchAuthorizationGroups.mockReturnValue({ - objects: [ fixture.TEST_GROUP_DTO ] + objects: [fixture.TEST_GROUP_DTO] }) dto.AuthorizationGroupFetchOptions.mockImplementation(() => { return { - withUsers: function(){} + withUsers: function() {} } }) @@ -302,34 +423,33 @@ describe('browser', () => { expectSelectedObject(pages.USERS, null) expectOpenObjects(pages.USERS, []) }) - }) -function nodeId(idParts){ +function nodeId(idParts) { return idParts.join('/') } -function node(idParts, expanded = false, selected = false, children){ +function node(idParts, expanded = false, selected = false, children) { let node = { id: nodeId(idParts), expanded, selected } - if(children !== undefined){ + if (children !== undefined) { node['children'] = children } return node } -function expectNodes(actualNodes, expectedNodes){ +function expectNodes(actualNodes, expectedNodes) { const keys = new Set(['id', 'expanded', 'selected', 'children']) var actualNodesClone = _.cloneDeep(actualNodes) common.mapNodes(null, actualNodesClone, (parent, node) => { _.keys(node).forEach(key => { - if(!keys.has(key)){ + if (!keys.has(key)) { delete node[key] } }) @@ -339,10 +459,12 @@ function expectNodes(actualNodes, expectedNodes){ expect(actualNodesClone).toEqual(expectedNodes) } -function expectSelectedObject(page, object){ - expect(selectors.createGetSelectedObject()(store.getState(), page)).toEqual(object) +function expectSelectedObject(page, object) { + expect(selectors.createGetSelectedObject()(store.getState(), page)).toEqual( + object + ) } -function expectOpenObjects(page, objects){ +function expectOpenObjects(page, objects) { expect(selectors.getOpenObjects(store.getState(), page)).toEqual(objects) } diff --git a/openbis_ng_ui/srcTest/store/sagas/page.test.js b/openbis_ng_ui/srcTest/store/sagas/page.test.js index 7c779c1225f1a3b057d603e75bb90922b72c42e1..4671f9e11f066270d8a199519a3fc63254c0364c 100644 --- a/openbis_ng_ui/srcTest/store/sagas/page.test.js +++ b/openbis_ng_ui/srcTest/store/sagas/page.test.js @@ -15,10 +15,12 @@ beforeEach(() => { }) describe('page', () => { - test('objectOpen objectClose', () => { let object1 = fixture.object(objectType.USER, fixture.TEST_USER_DTO.userId) - let object2 = fixture.object(objectType.USER, fixture.ANOTHER_USER_DTO.userId) + let object2 = fixture.object( + objectType.USER, + fixture.ANOTHER_USER_DTO.userId + ) let object3 = fixture.object(objectType.GROUP, fixture.TEST_GROUP_DTO.code) store.dispatch(actions.objectOpen(pages.USERS, object1.type, object1.id)) @@ -51,14 +53,13 @@ describe('page', () => { expectSelectedObject(pages.USERS, null) expectOpenObjects(pages.USERS, []) }) - }) -function expectSelectedObject(page, object){ +function expectSelectedObject(page, object) { let getSelectedObject = selectors.createGetSelectedObject() expect(getSelectedObject(store.getState(), page)).toEqual(object) } -function expectOpenObjects(page, objects){ +function expectOpenObjects(page, objects) { expect(selectors.getOpenObjects(store.getState(), page)).toEqual(objects) } diff --git a/openbis_ng_ui/webpack.config.dev.js b/openbis_ng_ui/webpack.config.dev.js index c429af25ffc681e744f176a39cc3b158739ead8a..5afd22033e6228d69cb8734723f092b80587f923 100644 --- a/openbis_ng_ui/webpack.config.dev.js +++ b/openbis_ng_ui/webpack.config.dev.js @@ -10,26 +10,26 @@ module.exports = { }, devServer: { - host: "0.0.0.0", + host: '0.0.0.0', port: 8124, inline: true, - contentBase: "./src", + contentBase: './src', https: false, watchOptions: { aggregateTimeout: 300, poll: 1000 }, proxy: { - "/openbis": { - "target": 'http://localhost:8888', - "pathRewrite": {'^/openbis/resources' : '/openbis-test/resources'}, - "changeOrigin": true, - "secure": false, + '/openbis': { + target: 'http://localhost:8888', + pathRewrite: { '^/openbis/resources': '/openbis-test/resources' }, + changeOrigin: true, + secure: false } } }, - devtool: "source-map", + devtool: 'source-map', mode: 'development', @@ -39,15 +39,12 @@ module.exports = { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { - loader: "babel-loader" + loader: 'babel-loader' } }, { test: /\.(css)$/, - use: [ - 'style-loader', - 'css-loader' - ] + use: ['style-loader', 'css-loader'] }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, @@ -65,7 +62,10 @@ module.exports = { filename: './index.html', template: './index.html' }), -// new Webpack.WatchIgnorePlugin(['/home/vagrant/openbis/openbis_ng_ui/react/node_modules/', '/home/vagrant/openbis/openbis_ng_ui/react/node/']) - new Webpack.WatchIgnorePlugin([new RegExp("/node_modules/"), new RegExp("/node/")]) + // new Webpack.WatchIgnorePlugin(['/home/vagrant/openbis/openbis_ng_ui/react/node_modules/', '/home/vagrant/openbis/openbis_ng_ui/react/node/']) + new Webpack.WatchIgnorePlugin([ + new RegExp('/node_modules/'), + new RegExp('/node/') + ]) ] } diff --git a/openbis_ng_ui/webpack.config.js b/openbis_ng_ui/webpack.config.js index d6d7ba256aba882a80fab8a120d34cdd5818ac14..17d891f631f5fb485b2753770eef02b907f44a24 100644 --- a/openbis_ng_ui/webpack.config.js +++ b/openbis_ng_ui/webpack.config.js @@ -1,5 +1,5 @@ /* eslint-disable */ -const HtmlWebpackPlugin = require('html-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: './src/index.js', @@ -16,15 +16,12 @@ module.exports = { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { - loader: "babel-loader" + loader: 'babel-loader' } }, { test: /\.(css)$/, - use: [ - 'style-loader', - 'css-loader' - ] + use: ['style-loader', 'css-loader'] }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, @@ -43,4 +40,4 @@ module.exports = { template: './index.html' }) ] -}; +} diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls index 8b219a0b5747b886494c221c82bd3f3f1f470262..dc41839238990f7688c5a4b4f956986f3414f318 100644 Binary files a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls and b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls differ diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls index 16332eb71f3a9ddbf34d818a04319e5fb81200c1..21bc5da6b913898b698b337c5eaa1dd604c75b92 100644 Binary files a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls and b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls differ diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js index b713b303923d04fa508d679c6db1851f4d2ce138..6027757010cb5d97163a8d04c21d03cdcbc93eae 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js @@ -61,7 +61,8 @@ $.extend(DefaultProfile.prototype, { showVocabularyViewer : true, showUserManager : true, showUserProfile : true, - showResearchCollectionExportBuilder : false, + // Not fully implemented yet. + // showResearchCollectionExportBuilder : false, showZenodoExportBuilder : false, } diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js index a4c8633e158bed6a2a2c053e454cc75e19aeff45..4a42b1c0c13a61c3ff253c9f5535a822f8707862 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js @@ -83,13 +83,35 @@ function MainController(profile) { //Functionality to keep state this.backStack = []; + + this.zenodoApiTokenKey = "personal-zenodo-api-token"; // // Validates and enters the app // - + this.enterApp = function(data, username, password) { + var _this = this; + if(data && !username && !password) { + this.openbisV1.listDataStores(function(result) { + if(result && result.error && result.error.message) { + var callback = function() {Util.unblockUI();}; + Util.showUserError(result.error.message, callback); + } else { + _this.initApp(data, username, password); + } + }); + } else { + this.initApp(data, username, password); + } + } + + this.initApp = function(data, username, password) { var localReference = this; + // + // Check + // + // // Check Credentials // @@ -197,6 +219,9 @@ function MainController(profile) { Util.unblockUI(); LayoutManager.resize(mainController.views, true); // Maybe fixes white screen on startup? + + // Keep Alive + localReference.serverFacade.scheduleKeepAlive(); }); }; diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/plugins/MicroscopyTechnology.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/plugins/MicroscopyTechnology.js index 4cfa7f5bc6f39d2437a318ff913f9341cd1c48f1..6c5dfe97af5360ab100cd3f55b4ac2af1f306459 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/plugins/MicroscopyTechnology.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/plugins/MicroscopyTechnology.js @@ -80,15 +80,19 @@ $.extend(MicroscopyTechnology.prototype, ELNLIMSPlugin.prototype, { // Show the series name instead of the dataset code view.getDataSetText = function (dataSetCode) { + var displayName = dataSetCode; + // Return the series name for (var i = 0; i < model.datasets.length; i++) { - if (model.datasets[i].code === dataSetCode) { - return model.datasets[0].properties["$NAME"]; + if (model.datasets[i].code === dataSetCode && + model.datasets[i].properties[profile.propertyReplacingCode]) { + displayName = model.datasets[i].properties[profile.propertyReplacingCode]; + break; } } // If not found, return the dataset code - return dataSetCode; + return displayName; }; }); @@ -125,13 +129,13 @@ $.extend(MicroscopyTechnology.prototype, ELNLIMSPlugin.prototype, { // Show the series name instead of the dataset code view.getDataSetText = function (dataSetCode) { + var displayName = dataSetCode; // Return the series name - if (model.dataSet.code === dataSetCode) { - return model.dataSet.properties["$NAME"]; - } else { - // Fall-back (that should not happen) - return dataSetCode; + if (model.dataSet.code === dataSetCode && + model.dataSet.properties[profile.propertyReplacingCode]) { + displayName = model.dataSet.properties[profile.propertyReplacingCode]; } + return displayName; }; }); @@ -189,11 +193,9 @@ $.extend(MicroscopyTechnology.prototype, ELNLIMSPlugin.prototype, { } // Prepare the name to be shown - var name; - if (sample.properties["$NAME"]) { - name = sample.properties["$NAME"]; - } else { - name = sample.code; + var name = sample.code; + if (sample.properties[profile.propertyReplacingCode]) { + name = sample.properties[profile.propertyReplacingCode]; } // Make sure it is not too long diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js index 96e277604e4a4ba2387881404142543ed6e52463..cf273afe3bfe7883c1fbe5f6e7775c43a7243742 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js @@ -51,7 +51,8 @@ function ServerFacade(openbisServer) { var responseInterceptor = function(response, action){ var isError = false; if(response && response.error) { - if(response.error.message === "Session no longer available. Please login again.") { + if(response.error.message === "Session no longer available. Please login again." + || response.error.message.endsWith("is invalid: user is not logged in.")) { isError = true; Util.showError(response.error.message, function() { location.reload(true); @@ -184,7 +185,27 @@ function ServerFacade(openbisServer) { }); } */ - + + this.scheduleKeepAlive = function() { + var _this = this; + var TIMEOUT = 60000; //60 Seconds + + setTimeout(function(){ + _this.keepAlive(); + }, TIMEOUT); + } + + this.keepAlive = function() { + var _this = this; + mainController.openbisV3.isSessionActive().done(function(isSessionActive) { + var timeStamp = Math.floor(Date.now() / 1000); + _this.scheduleKeepAlive(); + }).fail(function(error) { + var timeStamp = Math.floor(Date.now() / 1000); + _this.scheduleKeepAlive(); + }); + } + this.getPersons = function(personIds, callbackFunction) { if(!mainController.openbisV3.getPersons) { return null; // In case the method doesn't exist, do nothing @@ -396,7 +417,7 @@ function ServerFacade(openbisServer) { }); }; - this.exportZenodo = function(entities, includeRoot, metadataOnly, userInformation, callbackFunction) { + this.exportZenodo = function(entities, includeRoot, metadataOnly, userInformation, title, accessToken, callbackFunction) { this.asyncExportZenodo({ "method": "exportAll", "includeRoot": includeRoot, @@ -405,6 +426,8 @@ function ServerFacade(openbisServer) { "userInformation": userInformation, "originUrl": window.location.origin, "sessionToken": this.openbisServer.getSession(), + "submissionTitle": title, + "accessToken": accessToken }, callbackFunction, "zenodo-exports-api"); }; @@ -420,7 +443,6 @@ function ServerFacade(openbisServer) { var options = new AggregationServiceExecutionOptions(); options.withParameter("sessionToken", parameters["sessionToken"]); - options.withParameter("entities", parameters["entities"]); options.withParameter("includeRoot", parameters["includeRoot"]); options.withParameter("metadataOnly", parameters["metadataOnly"]); @@ -429,6 +451,8 @@ function ServerFacade(openbisServer) { options.withParameter("submissionType", parameters["submissionType"]); options.withParameter("submissionUrl", parameters["submissionUrl"]); options.withParameter("entities", parameters["entities"]); + options.withParameter("submissionTitle", parameters["submissionTitle"]); + options.withParameter("accessToken", parameters["accessToken"]); options.withParameter("userId", parameters["userInformation"]["id"]); options.withParameter("userEmail", parameters["userInformation"]["email"]); options.withParameter("userFirstName", parameters["userInformation"]["firstName"]); diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/JExcelEditorManager.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/JExcelEditorManager.js index 4c1ab4c4bf814f16365bc0b97ff969d0d85277be..8243e23ce83bab15d142dc9e1940e36d27755669 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/JExcelEditorManager.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/JExcelEditorManager.js @@ -7,15 +7,16 @@ var JExcelEditorManager = new function() { return function(el, record, x, y, value) { var jExcelEditor = _this.jExcelEditors[guid]; if(jExcelEditor) { - // Change column width - var columnWidth = parseInt(jExcelEditor.getWidth(x)); - var td = el.children[1].children[0].children[2].children[y].children[parseInt(x)+1]; - var columnScrollWidth = td.scrollWidth; - - if(columnScrollWidth > columnWidth) { - jExcelEditor.setWidth(x, columnScrollWidth + 10); + if(value) { + // Change column width + var columnWidth = parseInt(jExcelEditor.getWidth(x)); + var td = el.children[1].children[0].children[2].children[y].children[parseInt(x)+1]; + var columnScrollWidth = td.scrollWidth; + + if(columnScrollWidth > columnWidth) { + jExcelEditor.setWidth(x, columnScrollWidth + 10); + } } - // Save Editor var data = jExcelEditor.getData(); var style = jExcelEditor.getStyle(); diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ProjectForm/ProjectFormView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ProjectForm/ProjectFormView.js index 67397978ff96378c0be5e27ba058d66d09281d25..4ce098a9fb4f4424a97ece930ee640d19c97b63e 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ProjectForm/ProjectFormView.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ProjectForm/ProjectFormView.js @@ -78,7 +78,7 @@ function ProjectFormView(projectFormController, projectFormModel) { if (experimentType) { defaultValue = _this._projectFormModel.project.spaceCode; } else { - defaultValue = ""; + defaultValue = "COLLECTION"; } } else { defaultValue = "DEFAULT_EXPERIMENT"; diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SideMenu/SideMenuWidgetView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SideMenu/SideMenuWidgetView.js index ecd57517a2c01f717c852ea89b205c396594b486..8cbb61294a164f081712b1385f5ad793adc4eb3d 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SideMenu/SideMenuWidgetView.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SideMenu/SideMenuWidgetView.js @@ -240,7 +240,8 @@ function SideMenuWidgetView(sideMenuWidgetController, sideMenuWidgetModel) {     treeModelUtils.push({ title : jupyterNotebook, entityType: "NEW_JUPYTER_NOTEBOOK", key : "NEW_JUPYTER_NOTEBOOK", folder : false, lazy : false, view : "showNewJupyterNotebookCreator" }); } - if(profile.mainMenu.showUserProfile && profile.isFileAuthenticationService && profile.isFileAuthenticationUser) { + if(profile.mainMenu.showUserProfile) { + // && profile.isFileAuthenticationService && profile.isFileAuthenticationUser     var userProfileLink = _this.getLinkForNode("User Profile", "USER_PROFILE", "showUserProfilePage", null);     treeModelUtils.push({ title : userProfileLink, entityType: "USER_PROFILE", key : "USER_PROFILE", folder : false, lazy : false, view : "showUserProfilePage", icon : "glyphicon glyphicon-user" }); } @@ -276,15 +277,16 @@ function SideMenuWidgetView(sideMenuWidgetController, sideMenuWidgetModel) { }); } - if (profile.mainMenu.showResearchCollectionExportBuilder) { - var researchCollectionExportBuilderLink = _this.getLinkForNode("Export to Research Collection", - "EXPORT_TO_RESEARCH_COLLECTION", "showResearchCollectionExportPage", null); - treeModelExports.push({ - displayName: "Export to Research Collection", title: researchCollectionExportBuilderLink, - entityType: "EXPORT_TO_RESEARCH_COLLECTION", key: "EXPORT_TO_RESEARCH_COLLECTION", folder: false, lazy: false, - view: "showResearchCollectionExportPage", icon: "./img/research-collection-icon.png" - }); - } + // Not fully implemented yet. + // if (profile.mainMenu.showResearchCollectionExportBuilder) { + // var researchCollectionExportBuilderLink = _this.getLinkForNode("Export to Research Collection", + // "EXPORT_TO_RESEARCH_COLLECTION", "showResearchCollectionExportPage", null); + // treeModelExports.push({ + // displayName: "Export to Research Collection", title: researchCollectionExportBuilderLink, + // entityType: "EXPORT_TO_RESEARCH_COLLECTION", key: "EXPORT_TO_RESEARCH_COLLECTION", folder: false, lazy: false, + // view: "showResearchCollectionExportPage", icon: "./img/research-collection-icon.png" + // }); + // } if (profile.mainMenu.showZenodoExportBuilder) { var zenodoExportBuilderLink = _this.getLinkForNode("Export to Zenodo", "EXPORT_TO_ZENODO", "showZenodoExportPage", null); diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/UserProfile/UserProfileController.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/UserProfile/UserProfileController.js index 74c870c9621ff3375bbb3817d4d63ead3cee369c..93d3c3fc9e2ebfd73e007cdd00da66bee76928ac 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/UserProfile/UserProfileController.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/UserProfile/UserProfileController.js @@ -18,6 +18,7 @@ function UserProfileController(mainController, mode) { this._mainController = mainController; this._userProfileModel = new UserProfileModel(mode); this._userProfileView = new UserProfileView(this, this._userProfileModel); + this._zenodoApiTokenKey = this._mainController.zenodoApiTokenKey; this.init = function(views) { this._userProfileView.repaint(views); @@ -49,6 +50,8 @@ function UserProfileController(mainController, mode) { return; } var userId = this._mainController.serverFacade.getUserId(); + + this.setSettingValue(this._zenodoApiTokenKey, userInformation.zenodoToken); this._mainController.serverFacade.updateUserInformation(userId, userInformation, (function(ok) { if (ok) { Util.showInfo("Profile saved. You will be logged out automatically in order to reload the profile data upon login.", (function() { @@ -69,4 +72,17 @@ function UserProfileController(mainController, mode) { return errors; } + this.isFileAuthentication = function() { + return this._mainController.profile.isFileAuthenticationService && + this._mainController.profile.isFileAuthenticationUser; + } + + this.getSettingValue = function (key, callback) { + this._mainController.serverFacade.getSetting(key, callback); + }; + + this.setSettingValue = function (key, value) { + this._mainController.serverFacade.setSetting(key, value); + }; + } \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/UserProfile/UserProfileView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/UserProfile/UserProfileView.js index 22e972b17ff4a1c0ed7cd41180acf7d7211b6d91..65d80e7574d388b5922b96ea0459bc8b35175128 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/UserProfile/UserProfileView.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/UserProfile/UserProfileView.js @@ -21,6 +21,7 @@ function UserProfileView(userProfileController, userProfileModel) { this._$firstNameInput = null; this._$lastNameInput = null; this._$emailInput = null; + this._$zenodoToken = null; this.repaint = function(views) { @@ -71,14 +72,28 @@ function UserProfileView(userProfileController, userProfileModel) { this._$emailInput = $("<input>", { type : "text", class : "form-control" }); this._$emailInput.val(getUserInformation.email); $formColumn.append(this._getFormGroup(this._$emailInput, "Email:")); + // personal Zenodo API token + this._$zenodoToken = $("<input>", { type : "text", class : "form-control" }); + + this._userProfileController.getSettingValue(this._userProfileController._zenodoApiTokenKey, (function (settingsValue) { + if (settingsValue) { + this._$zenodoToken.val(settingsValue.trim()); + } + }).bind(this)); + $formColumn.append(this._getFormGroup(this._$zenodoToken, "Zenodo API Token:")); + // disable in view mode - if (this._userProfileModel.mode === FormMode.VIEW) { + if (this._userProfileModel.mode === FormMode.VIEW || + !this._userProfileController.isFileAuthentication()) { this._$firstNameInput.prop("disabled", true); this._$lastNameInput.prop("disabled", true); this._$emailInput.prop("disabled", true); } - }).bind(this)); + if (this._userProfileModel.mode === FormMode.VIEW) { + this._$zenodoToken.prop("disabled", true); + } + }).bind(this)); } this._getOptionsMenu = function() { @@ -96,6 +111,7 @@ function UserProfileView(userProfileController, userProfileModel) { firstName : this._$firstNameInput.val(), lastName : this._$lastNameInput.val(), email : this._$emailInput.val(), + zenodoToken : this._$zenodoToken.val() }; } diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportController.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportController.js index 34f489234de0f0119b948bb8139ea5a7e76855ff..fb99fc614e7ea5cb2e89a2a84a54680df5e6d190 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportController.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportController.js @@ -15,16 +15,26 @@ */ function ZenodoExportController(parentController) { - var exportModel = new ZenodoExportModel(); - var exportView = new ZenodoExportView(this, exportModel); + this.exportModel = null; + this.exportView = null; + this.zenodoApiTokenKey = parentController.zenodoApiTokenKey; this.init = function(views) { - exportView.repaint(views); + this.getSettingValue(this.zenodoApiTokenKey, (function (accessToken) { + if (accessToken && accessToken !== '') { + this.exportModel = new ZenodoExportModel(accessToken); + this.exportView = new ZenodoExportView(this, this.exportModel); + this.exportView.repaint(views); + } else { + Util.showError('Personal Zenodo API Token missing, please set it on your user profile.'); + } + }).bind(this)); }; this.exportSelected = function() { var _this = this; - var selectedNodes = $(exportModel.tree).fancytree('getTree').getSelectedNodes(); + var selectedNodes = $(this.exportModel.tree).fancytree('getTree').getSelectedNodes(); + var title = this.exportView.$titleTextBox.val().trim(); var toExport = []; for (var eIdx = 0; eIdx < selectedNodes.length; eIdx++) { @@ -34,12 +44,14 @@ function ZenodoExportController(parentController) { if (toExport.length === 0) { Util.showInfo('First select something to export.'); + } else if (title === "") { + Util.showInfo('Please enter a title.'); } else if (!this.isValid(toExport)) { Util.showInfo('Not only spaces and the root should be selected. It will result in an empty export file.'); } else { Util.blockUI(); - this.getUserInformation(function(userInformation) { - mainController.serverFacade.exportZenodo(toExport, true, false, userInformation, + this.getUserInformation((function(userInformation) { + mainController.serverFacade.exportZenodo(toExport, true, false, userInformation, title, this.exportModel.accessToken, function(operationExecutionPermId) { _this.waitForOpExecutionResponse(operationExecutionPermId, function(error, result) { Util.unblockUI(); @@ -56,7 +68,7 @@ function ZenodoExportController(parentController) { } }); }); - }); + }).bind(this)); } }; @@ -109,4 +121,8 @@ function ZenodoExportController(parentController) { callback(userInformation); }); }; + + this.getSettingValue = function (key, callback) { + parentController.serverFacade.getSetting(key, callback); + }; } \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportModel.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportModel.js index f555844b57e82e8c640965767f2077b1b66f213e..dc4f73489ca42ef5f9dc9cd1abe15ef67a0a8c35 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportModel.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportModel.js @@ -14,5 +14,6 @@ * limitations under the License. */ -function ZenodoExportModel() { +function ZenodoExportModel(accessToken) { + this.accessToken = accessToken; } \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportView.js index 775a8893be6bbaed639dd4409ab6e73e14e56b43..a2ae6996d23f4730a24de4e2ff05f4b6df87fae4 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportView.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportView.js @@ -21,7 +21,7 @@ function ZenodoExportView(exportController, exportModel) { var $form = $("<div>"); var $formColumn = $("<form>", { - 'name': 'rcExportForm', + 'name': 'zenodoExportForm', 'role': 'form', 'action': 'javascript:void(0);', 'onsubmit': 'mainController.currentView.exportSelected();' @@ -53,8 +53,17 @@ function ZenodoExportView(exportController, exportModel) { var $formTitle = $('<h2>').append('Zenodo Export Builder'); $header.append($formTitle); + this.paintTitleTextBox($container); + var $exportButton = $('<input>', { 'type': 'submit', 'class': 'btn btn-primary', 'value': 'Export Selected', - 'onClick': '$("form[name=\'rcExportForm\']").submit()'}); + 'onClick': '$("form[name=\'zenodoExportForm\']").submit()'}); $header.append($exportButton); }; + + this.paintTitleTextBox = function ($container) { + this.$titleTextBox = FormUtil.getTextInputField('zenodo-submission-title', 'Submission title', true); + var titleTextBoxFormGroup = FormUtil.getFieldForComponentWithLabel(this.$titleTextBox, 'Submission Title', null, true); + titleTextBoxFormGroup.css('width', '50%'); + $container.append(titleTextBoxFormGroup); + }; } \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py index 66e91990f86074101ad93186f4f6c7aac001b285..f44630466dd4df1741ea821304dfa691d8dc1b08 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py @@ -271,7 +271,7 @@ def generateXML(zipOutputStream, fileMetadata, exportDirPath, submissionType, us titleField = ET.SubElement(dim, ET.QName(dimNS, 'field')) titleField.set('mdschema', 'dc') titleField.set('element', 'title') - titleField.text = str(time.time()) + titleField.text = '' typeField = ET.SubElement(dim, ET.QName(dimNS, 'field')) typeField.set('mdschema', 'dc') diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties index cc77ea6f8cfc5d2c91b4292696d89990276b2b7b..1e4e8fa3aa70869ce9c174f3bdcf7d2c5941224b 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties @@ -2,5 +2,4 @@ label = Zenodo Exports API class = ch.systemsx.cisd.openbis.dss.generic.server.plugins.jython.JythonIngestionService script-path = zenodoExports.py limit-data-size-megabytes=${zenodo-exports-api-limit-data-size-megabytes:4000} -zenodoUrl=${zenodo-exports-api-zenodoUrl:https://localhost} -accessToken=${zenodo-exports-api-accessToken} \ No newline at end of file +zenodoUrl=${zenodo-exports-api-zenodoUrl:https://localhost} \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/zenodoExports.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/zenodoExports.py index 4934cac3586e661ffe31a3142a24b52af2876ba7..5922f0f8827de532880931ddc8150ef0501f88fc 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/zenodoExports.py +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/zenodoExports.py @@ -93,7 +93,7 @@ def export(entities, tr, params): def sendToZenodo(tr, params, tempZipFilePath, entities): depositRootUrl = str(getConfigurationProperty(tr, 'zenodoUrl')) + '/api/deposit/depositions' - accessToken = str(getConfigurationProperty(tr, 'accessToken')) + accessToken = params.get('accessToken') operationLog.info('accessToken: %s' % accessToken) httpClient = None @@ -110,7 +110,7 @@ def sendToZenodo(tr, params, tempZipFilePath, entities): selfUrl = depositionLinks.get('self') submitFile(httpClient.newRequest(depositUrl), accessToken, tempZipFilePath) - addMetadata(httpClient.newRequest(selfUrl), accessToken) + addMetadata(params, httpClient.newRequest(selfUrl), accessToken) entityPermIds = map(lambda entity: entity['permId'], entities) zenodoCallable = ZenodoCallable(params, accessToken, selfUrl, @@ -140,10 +140,10 @@ def submitFile(request, accessToken, tempZipFilePath): return JSONObject(contentStr) -def addMetadata(request, accessToken): +def addMetadata(params, request, accessToken): data = { 'metadata': { - 'title': str(time.time()), + 'title': params.get('submissionTitle'), 'license': 'cc-zero', 'upload_type': 'dataset', 'description': 'Add some description.', @@ -225,7 +225,7 @@ class ZenodoCallable(object): actionCompleted = True elif publicationJson.get('submitted'): operationLog.info('Publication #%d submitted. Registering metadata.' % publicationJson.get('id')) - self.registerPublicationInOpenbis(publicationJson.get('metadata')) + self.registerPublicationInOpenbis(publicationJson.get('metadata'), publicationJson.get('links')) actionCompleted = True else: operationLog.info('Publication #%d not submitted yet.' % publicationJson.get('id')) @@ -244,13 +244,13 @@ class ZenodoCallable(object): return actionCompleted - def registerPublicationInOpenbis(self, publicationMetadataJson): + def registerPublicationInOpenbis(self, publicationMetadataJson, publicationLinksJson): sessionToken = self.params.get('sessionToken') v3 = ServiceProvider.getV3ApplicationService() id = CustomASServiceCode('publication-api') options = CustomASServiceExecutionOptions() \ .withParameter('method', 'insertPublication') \ - .withParameter('publicationURL', self.selfUrl) \ + .withParameter('publicationURL', publicationLinksJson.get('doi')) \ .withParameter('openBISRelatedIdentifiers', self.permIdsStr) \ .withParameter('name', publicationMetadataJson.get('title')) \ .withParameter('publicationDescription', publicationMetadataJson.get('description')) \ diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/entrypoint.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/entrypoint.py index 9806b27718bb3855c9f0eff450b44c2edb637b3b..1f324a67b25e1605aa2cc5c1ec4fde6c329c2088 100644 --- a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/entrypoint.py +++ b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/entrypoint.py @@ -3,10 +3,10 @@ import os from ch.ethz.sis.openbis.generic.asapi.v3.dto.operation import SynchronousOperationExecutionOptions from ch.systemsx.cisd.common.exceptions import UserFailureException from ch.systemsx.cisd.openbis.generic.server import CommonServiceProvider -from parsers import get_creations_from, get_definitions_from, get_creation_metadata_from, \ +from parsers import get_creations_from, get_definitions_from_xls, get_definitions_from_csv, get_creation_metadata_from, \ CreationOrUpdateToOperationParser, versionable_types from processors import OpenbisDuplicatesHandler, PropertiesLabelHandler, DuplicatesHandler, \ - unify_properties_representation_of + unify_properties_representation_of, validate_creations from search_engines import SearchEngine from utils import FileHandler from utils.openbis_utils import get_version_name_for, get_metadata_name_for @@ -14,9 +14,9 @@ from utils.openbis_utils import get_version_name_for, get_metadata_name_for REMOVE_VERSIONS = False -def validate_data(xls_byte_arrays, update_mode, xls_name): - if xls_byte_arrays is None: - raise UserFailureException('Excel sheet has not been provided. "xls" parameter is None') +def validate_data(xls_byte_arrays, csv_strings, update_mode, xls_name): + if xls_byte_arrays is None and csv_strings is None: + raise UserFailureException('Nor Excel sheet nor csv has not been provided. "xls" and "csv" parameters are None') if update_mode not in ['IGNORE_EXISTING', 'FAIL_IF_EXISTS', 'UPDATE_IF_EXISTS']: raise UserFailureException( 'Update mode has to be one of following: IGNORE_EXISTING FAIL_IF_EXISTS UPDATE_IF_EXISTS but was ' + ( @@ -69,12 +69,15 @@ def process(context, parameters): search_engine = SearchEngine(api, session_token) xls_byte_arrays = parameters.get('xls', None) + csv_strings = parameters.get('csv', None) xls_name = parameters.get('xls_name', None) scripts = parameters.get('scripts', {}) update_mode = parameters.get('update_mode', None) - validate_data(xls_byte_arrays, update_mode, xls_name) - definitions = get_definitions_from(xls_byte_arrays) + validate_data(xls_byte_arrays, csv_strings, update_mode, xls_name) + definitions = get_definitions_from_xls(xls_byte_arrays) + definitions.extend(get_definitions_from_csv(csv_strings)) creations = get_creations_from(definitions, FileHandler(scripts)) + validate_creations(creations) creations_metadata = get_creation_metadata_from(definitions) creations = DuplicatesHandler.get_distinct_creations(creations) xls_version_filepath = get_property("xls-import.version-data-file", "../../../xls-import-version-info.json") diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/__init__.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/__init__.py index 28a31b2287a5d71b2edf9d0be2aea7bde976f2c5..4bd91747064726fa6a71e79d8327ae2396c265d0 100644 --- a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/__init__.py +++ b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/__init__.py @@ -7,8 +7,9 @@ from .definition_to_creation import PropertyTypeDefinitionToCreationType, Vocabu SampleDefinitionToCreationType, ScriptDefinitionToCreationType from .definition_to_creation_metadata import DefinitionToCreationMetadataParser from .excel_to_poi import ExcelToPoiParser -from .parsers_facade import get_creations_from, get_definitions_from, get_creation_metadata_from -from .poi_to_definition import PoiToDefinitionParser, Definition +from .parsers_facade import get_creations_from, get_definitions_from_xls, get_definitions_from_csv, \ + get_creation_metadata_from +from .to_definition import PoiToDefinitionParser, CsvReaderToDefinitionParser, Definition from .creation_to_update import CreationToUpdateParser, UpdateTypes, PropertyTypeCreationToUpdateType, \ VocabularyCreationToUpdateType, VocabularyTermCreationToUpdateType, PropertyAssignmentCreationToUpdateType, \ SampleTypeCreationToUpdateType, ExperimentTypeCreationToUpdateType, DatasetTypeCreationToUpdateType, \ diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/parsers_facade.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/parsers_facade.py index 51624c520c9fe2e88f51958a7843a7ed19e2973c..a2884641bee4bb0542245d2dec6b2ff9adf7d82e 100644 --- a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/parsers_facade.py +++ b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/parsers_facade.py @@ -1,21 +1,29 @@ from .definition_to_creation import DefinitionToCreationParser from .excel_to_poi import ExcelToPoiParser -from .poi_to_definition import PoiToDefinitionParser - +from .to_definition import PoiToDefinitionParser, CsvReaderToDefinitionParser from .definition_to_creation_metadata import DefinitionToCreationMetadataParser -def get_definitions_from(xls_byte_arrays): +def get_definitions_from_xls(xls_byte_arrays): definitions = [] - for excel_byte_array in xls_byte_arrays: + for excel_byte_array in xls_byte_arrays or []: poi_definitions = ExcelToPoiParser.parse(excel_byte_array) partial_definitions = PoiToDefinitionParser.parse(poi_definitions) definitions.extend(partial_definitions) return definitions +def get_definitions_from_csv(csv_strings): + definitions = [] + for csv_string in csv_strings or []: + csv_definitions = CsvReaderToDefinitionParser.parse(csv_string) + partial_definitions = PoiToDefinitionParser.parse(csv_definitions) + definitions.extend(partial_definitions) + + return definitions + + def get_creations_from(definitions, context): - pass return DefinitionToCreationParser.parse(definitions, context) diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/poi_to_definition/__init__.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/poi_to_definition/__init__.py deleted file mode 100644 index 3d310af376a0dde1f2137448d01b8829d2dba457..0000000000000000000000000000000000000000 --- a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/poi_to_definition/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .poi_to_definition import PoiToDefinitionParser -from .definition import Definition diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/__init__.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0c359051546a35c60c8bf3d4aa30c97480627876 --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/__init__.py @@ -0,0 +1,3 @@ +from .poi_to_definition.poi_to_definition import PoiToDefinitionParser +from .csv_to_definition.csv_to_definition import CsvReaderToDefinitionParser +from .definition import Definition diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/csv_to_definition/__init__.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/csv_to_definition/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/csv_to_definition/csv_to_definition.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/csv_to_definition/csv_to_definition.py new file mode 100644 index 0000000000000000000000000000000000000000..bb3fcffad0777bab6eb4c1c273d78f24358a992e --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/csv_to_definition/csv_to_definition.py @@ -0,0 +1,28 @@ +from StringIO import StringIO +import csv + + +class CsvReaderToDefinitionParser(): + + @staticmethod + def parse(csv_string): + def is_row_empty(row): + return not ''.join(row).strip() + + f = StringIO("".join(map(chr, csv_string))) + reader = csv.reader(f, delimiter=',') + definitions = [] + definition_rows = [] + previous_row_empty = False + for row in reader: + if is_row_empty(row) and previous_row_empty: + break + if is_row_empty(row): + definitions.append(definition_rows) + definition_rows = [] + previous_row_empty = True + continue + previous_row_empty = False + definition_rows.append({k: v for k, v in enumerate(row) if v != ''}) + + return definitions diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/poi_to_definition/definition.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/definition.py similarity index 100% rename from openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/poi_to_definition/definition.py rename to openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/definition.py diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/poi_to_definition/__init__.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/poi_to_definition/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/poi_to_definition/definition_parsers.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/poi_to_definition/definition_parsers.py similarity index 66% rename from openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/poi_to_definition/definition_parsers.py rename to openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/poi_to_definition/definition_parsers.py index d17255c0f85deeb23f734f17ccd72eaf8ef9e499..5235edd7758f33af526098cbf1c98257b6d8542e 100644 --- a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/poi_to_definition/definition_parsers.py +++ b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/poi_to_definition/definition_parsers.py @@ -1,18 +1,20 @@ from java.lang import UnsupportedOperationException -from .definition import Definition +from ..definition import Definition from .poi_cleaner import PoiCleaner class DefinitionParserFactory(object): - @staticmethod - def get_parser(definition_type): - if definition_type in ['VOCABULARY_TYPE', 'SAMPLE_TYPE', 'EXPERIMENT_TYPE', 'DATASET_TYPE', 'EXPERIMENT', 'SAMPLE']: - return GeneralDefinitionParser - elif definition_type in ['PROPERTY_TYPE', 'SPACE', 'PROJECT']: - return PropertiesOnlyDefinitionParser - else: - raise UnsupportedOperationException("Definition of " + str(definition_type) + " is not supported (probably yet).") + @staticmethod + def get_parser(definition_type): + if definition_type in ['VOCABULARY_TYPE', 'SAMPLE_TYPE', 'EXPERIMENT_TYPE', 'DATASET_TYPE', 'EXPERIMENT', + 'SAMPLE']: + return GeneralDefinitionParser + elif definition_type in ['PROPERTY_TYPE', 'SPACE', 'PROJECT']: + return PropertiesOnlyDefinitionParser + else: + raise UnsupportedOperationException( + "Definition of " + str(definition_type) + " is not supported (probably yet).") class PropertiesOnlyDefinitionParser(object): @@ -25,12 +27,12 @@ class PropertiesOnlyDefinitionParser(object): PROPERTIES_VALUES_ROW_START = 2 row_numbers = { - 'DEFINITION_TYPE_ROW' : DEFINITION_TYPE_ROW, - 'DEFINITION_TYPE_CELL' : DEFINITION_TYPE_CELL, - 'ATTRIBUTES_HEADER_ROW' : None, - 'ATTRIBUTES_VALUES_ROW' : None, - 'PROPERTIES_HEADER_ROW' : PROPERTIES_HEADER_ROW, - 'PROPERTIES_VALUES_ROW_START' : PROPERTIES_VALUES_ROW_START + 'DEFINITION_TYPE_ROW': DEFINITION_TYPE_ROW, + 'DEFINITION_TYPE_CELL': DEFINITION_TYPE_CELL, + 'ATTRIBUTES_HEADER_ROW': None, + 'ATTRIBUTES_VALUES_ROW': None, + 'PROPERTIES_HEADER_ROW': PROPERTIES_HEADER_ROW, + 'PROPERTIES_VALUES_ROW_START': PROPERTIES_VALUES_ROW_START } poi_definition = PoiCleaner.clean_data(poi_definition, row_numbers) @@ -65,12 +67,12 @@ class GeneralDefinitionParser(object): PROPERTIES_VALUES_ROW_START = 4 row_numbers = { - 'DEFINITION_TYPE_ROW' : DEFINITION_TYPE_ROW, - 'DEFINITION_TYPE_CELL' : DEFINITION_TYPE_CELL, - 'ATTRIBUTES_HEADER_ROW' : ATTRIBUTES_HEADER_ROW, - 'ATTRIBUTES_VALUES_ROW' : ATTRIBUTES_VALUES_ROW, - 'PROPERTIES_HEADER_ROW' : PROPERTIES_HEADER_ROW, - 'PROPERTIES_VALUES_ROW_START' : PROPERTIES_VALUES_ROW_START + 'DEFINITION_TYPE_ROW': DEFINITION_TYPE_ROW, + 'DEFINITION_TYPE_CELL': DEFINITION_TYPE_CELL, + 'ATTRIBUTES_HEADER_ROW': ATTRIBUTES_HEADER_ROW, + 'ATTRIBUTES_VALUES_ROW': ATTRIBUTES_VALUES_ROW, + 'PROPERTIES_HEADER_ROW': PROPERTIES_HEADER_ROW, + 'PROPERTIES_VALUES_ROW_START': PROPERTIES_VALUES_ROW_START } poi_definition = PoiCleaner.clean_data(poi_definition, row_numbers) diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/poi_to_definition/poi_cleaner.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/poi_to_definition/poi_cleaner.py similarity index 90% rename from openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/poi_to_definition/poi_cleaner.py rename to openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/poi_to_definition/poi_cleaner.py index df03358a84096673c70b8c78d9352625a4f1745f..e4698a6c51da7c8e5b3bc29934cb63f7024fc1c4 100644 --- a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/poi_to_definition/poi_cleaner.py +++ b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/poi_to_definition/poi_cleaner.py @@ -25,13 +25,14 @@ class PoiCleaner(object): If there's no corresponding value, None is inserted. 4. Headers to lowercase ''' - definition[DEFINITION_TYPE_ROW] = {0:definition[DEFINITION_TYPE_ROW][DEFINITION_TYPE_CELL]} + definition[DEFINITION_TYPE_ROW] = {0: definition[DEFINITION_TYPE_ROW][DEFINITION_TYPE_CELL]} if ATTRIBUTES_HEADER_ROW is not None: PoiCleaner.delete_empty_cells_from(definition, ATTRIBUTES_HEADER_ROW) if ATTRIBUTES_VALUES_ROW is not None: PoiCleaner.delete_cells_if_no_header(definition, ATTRIBUTES_HEADER_ROW, ATTRIBUTES_VALUES_ROW) - PoiCleaner.create_cells_if_no_value_but_header_exists(definition, ATTRIBUTES_HEADER_ROW, ATTRIBUTES_VALUES_ROW) + PoiCleaner.create_cells_if_no_value_but_header_exists(definition, ATTRIBUTES_HEADER_ROW, + ATTRIBUTES_VALUES_ROW) definition[ATTRIBUTES_HEADER_ROW] = PoiCleaner.dict_values_to_lowercase(definition[ATTRIBUTES_HEADER_ROW]) if PROPERTIES_HEADER_ROW is not None and PoiCleaner.hasProperties(definition, PROPERTIES_HEADER_ROW): @@ -39,7 +40,8 @@ class PoiCleaner(object): if PROPERTIES_VALUES_ROW_START is not None: for property_value_row_num in range(PROPERTIES_VALUES_ROW_START, len(definition)): PoiCleaner.delete_cells_if_no_header(definition, PROPERTIES_HEADER_ROW, property_value_row_num) - PoiCleaner.create_cells_if_no_value_but_header_exists(definition, PROPERTIES_HEADER_ROW, property_value_row_num) + PoiCleaner.create_cells_if_no_value_but_header_exists(definition, PROPERTIES_HEADER_ROW, + property_value_row_num) definition[PROPERTIES_HEADER_ROW] = PoiCleaner.dict_values_to_lowercase(definition[PROPERTIES_HEADER_ROW]) return definition diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/poi_to_definition/poi_to_definition.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/poi_to_definition/poi_to_definition.py similarity index 100% rename from openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/poi_to_definition/poi_to_definition.py rename to openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/to_definition/poi_to_definition/poi_to_definition.py diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/processors/__init__.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/processors/__init__.py index 4bf02ed2f60853c45dde046f96961ab7b0a0f090..7c32a00104eb924efb381e5a786c4149a3bf0c7a 100644 --- a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/processors/__init__.py +++ b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/processors/__init__.py @@ -3,3 +3,4 @@ from .duplicates_handler import DuplicatesHandler from .properties_label_handler import PropertiesLabelHandler from .representation_unifier import unify_properties_representation_of from .version_handler import VersionHandler +from .creation_validator import validate_creations diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/processors/creation_validator.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/processors/creation_validator.py new file mode 100644 index 0000000000000000000000000000000000000000..ca81778bffb892efc715553c606fea9de6290ff6 --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/processors/creation_validator.py @@ -0,0 +1,49 @@ +from parsers import PropertyTypeDefinitionToCreationType +from ch.systemsx.cisd.common.exceptions import UserFailureException + + +def validate_creations(creations): + ''' + This validator checks for: + - whether property types with same code are all the same across xls + - If allowed entries align with creations about to be made + + It throws an exception if this is false. + ''' + if PropertyTypeDefinitionToCreationType not in creations: + return + property_type_creations = creations[PropertyTypeDefinitionToCreationType] + different_duplicates = {} + for property_type in property_type_creations: + for second_property_type in property_type_creations: + if property_type.code == second_property_type.code: + difference_info = {} + attributes_to_check = ['label', 'description', 'dataType', 'internalNameSpace', 'vocabularyId', + 'metaData'] + for attribute in attributes_to_check: + attribute_of_property_type = getattr(property_type, attribute) + attribute_of_second_property_type = getattr(second_property_type, attribute) + if attribute_of_property_type != attribute_of_second_property_type: + if attribute not in difference_info: + difference_info[attribute] = [] + attribute_pair = [str(attribute_of_property_type), str(attribute_of_second_property_type)] + difference_info[attribute].extend(attribute_pair) + + if difference_info: + if property_type.code not in different_duplicates: + different_duplicates[property_type.code] = {} + for key in difference_info: + if key not in different_duplicates[property_type.code]: + different_duplicates[property_type.code][key] = set(difference_info[key]) + else: + different_duplicates[property_type.code][key].update(difference_info[key]) + + if different_duplicates: + error_msg = "Following property types have ambiguous definition: \n" + \ + '\n'.join(['Property Code: ' + str(code) + '\n' + '\n'.join( + ["Attribute with difference: " + attr_name + ", has following values: " + '\n' + ', '.join( + ['"' + str(attribute_value) + '"' for attribute_value in duplicates_attributes]) + for attr_name, duplicates_attributes in diff_info.items()]) for code, diff_info in + different_duplicates.items()]) + '\n' + + raise UserFailureException(error_msg) diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/AbstractImportTest.java b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/AbstractImportTest.java index 19f8b06059efe9c0d1d97a20d76b14fe96513572..193174e4d5c77a34c4cc2f2c2f21abe1f62f9ecd 100644 --- a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/AbstractImportTest.java +++ b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/AbstractImportTest.java @@ -2,35 +2,52 @@ package ch.ethz.sis.openbis.systemtest.plugin.excelimport; import java.io.File; +import ch.ethz.sis.openbis.generic.server.asapi.v3.IApplicationServerInternalApi; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests; import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeSuite; import ch.systemsx.cisd.openbis.generic.server.util.TestInitializer; import ch.systemsx.cisd.openbis.generic.shared.Constants; import ch.systemsx.cisd.openbis.generic.shared.coreplugin.CorePluginsUtils; -public class AbstractImportTest extends AbstractTransactionalTestNGSpringContextTests -{ - - protected String FILES_DIR; +public class AbstractImportTest extends AbstractTransactionalTestNGSpringContextTests { private String XLS_VERSIONING_DIR = "xls-import.version-data-file"; + private static final String TEST_USER = "test"; + + private static final String PASSWORD = "password"; + + @Autowired + protected IApplicationServerInternalApi v3api; + + protected String sessionToken; + + protected String FILES_DIR; + @BeforeSuite - public void setupSuite() - { + public void setupSuite() { System.setProperty(XLS_VERSIONING_DIR, "./versioning.bin"); System.setProperty(CorePluginsUtils.CORE_PLUGINS_FOLDER_KEY, "dist/core-plugins"); System.setProperty(Constants.ENABLED_MODULES_KEY, "xls-import"); TestInitializer.initEmptyDbNoIndex(); } + @BeforeMethod + public void beforeTest() { + sessionToken = v3api.login(TEST_USER, PASSWORD); + System.out.println("AHAHHAHAHAHHA"); + System.out.println(sessionToken); + } + @AfterMethod - public void afterTest() - { + public void afterTest() { File f = new File("./versioning.bin"); f.delete(); + v3api.logout(sessionToken); } } diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportDatasetTypesTest.java b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportDatasetTypesTest.java index b65c8206c03512db46bc793195edd20975cf9ed0..d042fb24fe8c7906fc9b9c42dbadef71e81cb402 100644 --- a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportDatasetTypesTest.java +++ b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportDatasetTypesTest.java @@ -15,6 +15,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.transaction.annotation.Transactional; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -28,15 +29,7 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException; @ContextConfiguration(locations = "classpath:applicationContext.xml") @Transactional(transactionManager = "transaction-manager") @Rollback -public class ImportDatasetTypesTest extends AbstractImportTest -{ - - @Autowired - private IApplicationServerInternalApi v3api; - - private static final String TEST_USER = "test"; - - private static final String PASSWORD = "password"; +public class ImportDatasetTypesTest extends AbstractImportTest { private static final String DATASET_TYPES_XLS = "dataset_types/normal_dataset.xls"; @@ -50,25 +43,15 @@ public class ImportDatasetTypesTest extends AbstractImportTest private static String FILES_DIR; - private String sessionToken; - @BeforeClass - public void setupClass() throws IOException - { + public void setupClass() throws IOException { String f = ImportDatasetTypesTest.class.getName().replace(".", "/"); FILES_DIR = f.substring(0, f.length() - ImportDatasetTypesTest.class.getSimpleName().length()) + "/test_files/"; } - @BeforeMethod - public void beforeTest() - { - sessionToken = v3api.login(TEST_USER, PASSWORD); - } - @Test @DirtiesContext - public void testNormalDatasetTypesAreCreated() throws Exception - { + public void testNormalDatasetTypesAreCreated() throws Exception { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, DATASET_TYPES_XLS))); // WHEN @@ -98,8 +81,7 @@ public class ImportDatasetTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testDatasetTypesWithoutPropertiesTypesAreCreated() throws IOException - { + public void testDatasetTypesWithoutPropertiesTypesAreCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, DATASET_WITHOUT_PROPERTIES))); // WHEN @@ -111,8 +93,7 @@ public class ImportDatasetTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testDatasetTypesWithValidationScript() throws Exception - { + public void testDatasetTypesWithValidationScript() throws Exception { // GIVEN TestUtils.createFrom(v3api, sessionToken, TestUtils.getValidationPluginMap(), Paths.get(FilenameUtils.concat(FILES_DIR, DATASET_WITH_VALIDATION_SCRIPT))); @@ -124,8 +105,7 @@ public class ImportDatasetTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testDatasetTypesUpdate() throws Exception - { + public void testDatasetTypesUpdate() throws Exception { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, DATASET_TYPES_XLS))); // WHEN @@ -155,8 +135,7 @@ public class ImportDatasetTypesTest extends AbstractImportTest } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfNoSampleCode() throws IOException - { + public void shouldThrowExceptionIfNoSampleCode() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, DATASET_NO_CODE))); } diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportExperimentTypesTest.java b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportExperimentTypesTest.java index d0007539af55c929e69b82dcb3d1cb82b2b27b6f..4d96764ed4c66d3282e3ea1fe6ab721c2d0e5549 100644 --- a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportExperimentTypesTest.java +++ b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportExperimentTypesTest.java @@ -15,6 +15,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.transaction.annotation.Transactional; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -28,8 +29,8 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException; @ContextConfiguration(locations = "classpath:applicationContext.xml") @Transactional(transactionManager = "transaction-manager") @Rollback -public class ImportExperimentTypesTest extends AbstractImportTest -{ +public class ImportExperimentTypesTest extends AbstractImportTest { + @Autowired private IApplicationServerInternalApi v3api; @@ -47,25 +48,15 @@ public class ImportExperimentTypesTest extends AbstractImportTest private static String FILES_DIR; - private String sessionToken; - @BeforeClass - public void setupClass() throws IOException - { + public void setupClass() throws IOException { String f = ImportExperimentTypesTest.class.getName().replace(".", "/"); FILES_DIR = f.substring(0, f.length() - ImportExperimentTypesTest.class.getSimpleName().length()) + "/test_files/"; } - @BeforeMethod - public void beforeTest() - { - sessionToken = v3api.login(TEST_USER, PASSWORD); - } - @Test @DirtiesContext - public void testNormalExperimentTypesAreCreated() throws Exception - { + public void testNormalExperimentTypesAreCreated() throws Exception { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENT_TYPES_XLS))); // WHEN @@ -96,8 +87,7 @@ public class ImportExperimentTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testExperimentTypesUpdate() throws Exception - { + public void testExperimentTypesUpdate() throws Exception { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENT_TYPES_XLS))); // WHEN @@ -113,9 +103,9 @@ public class ImportExperimentTypesTest extends AbstractImportTest assertFalse(nameProperty.isMandatory()); assertTrue(nameProperty.isShowInEditView()); assertEquals(nameProperty.getSection(), "General information"); + assertEquals(nameProperty.getPropertyType().getDescription(), "NameUpdateDescription"); assertEquals(nameProperty.getPropertyType().getLabel(), "NameUpdate"); assertEquals(nameProperty.getPropertyType().getDataType(), DataType.VARCHAR); - assertEquals(nameProperty.getPropertyType().getDescription(), "NameUpdateDescription"); assertEquals(nameProperty.getPlugin(), null); assertFalse(defaultObjectTypeProperty.isMandatory()); assertTrue(defaultObjectTypeProperty.isShowInEditView()); @@ -129,8 +119,7 @@ public class ImportExperimentTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testExperimentTypesWithValidationScript() throws IOException - { + public void testExperimentTypesWithValidationScript() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, TestUtils.getValidationPluginMap(), Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENT_WITH_VALIDATION_SCRIPT))); @@ -141,8 +130,7 @@ public class ImportExperimentTypesTest extends AbstractImportTest } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfNoSampleCode() throws IOException - { + public void shouldThrowExceptionIfNoSampleCode() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENT_NO_CODE))); } diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportExperimentsTest.java b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportExperimentsTest.java index 55f21af7fac615b0d077d23562e13af4559ab896..dea9d2631bf587ace4e20e7a5e0f1c1705b65ba2 100644 --- a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportExperimentsTest.java +++ b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportExperimentsTest.java @@ -11,6 +11,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.transaction.annotation.Transactional; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -22,8 +23,7 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException; @ContextConfiguration(locations = "classpath:applicationContext.xml") @Transactional(transactionManager = "transaction-manager") @Rollback -public class ImportExperimentsTest extends AbstractImportTest -{ +public class ImportExperimentsTest extends AbstractImportTest { @Autowired private IApplicationServerInternalApi v3api; @@ -67,25 +67,15 @@ public class ImportExperimentsTest extends AbstractImportTest private static String FILES_DIR; - private String sessionToken; - @BeforeClass - public void setupClass() throws IOException - { + public void setupClass() throws IOException { String f = ImportExperimentsTest.class.getName().replace(".", "/"); FILES_DIR = f.substring(0, f.length() - ImportExperimentsTest.class.getSimpleName().length()) + "/test_files/"; } - @BeforeMethod - public void beforeTest() - { - sessionToken = v3api.login(TEST_USER, PASSWORD); - } - @Test @DirtiesContext - public void testExperimentsAreCreated() throws IOException - { + public void testExperimentsAreCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENT_XLS))); // WHEN @@ -99,8 +89,7 @@ public class ImportExperimentsTest extends AbstractImportTest @Test @DirtiesContext - public void testExperimentsAreCreatedSecondExperiment() throws IOException - { + public void testExperimentsAreCreatedSecondExperiment() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENT_XLS))); // WHEN @@ -114,8 +103,7 @@ public class ImportExperimentsTest extends AbstractImportTest @Test @DirtiesContext - public void testExperimentsAreCreatedWithEverythingOnServer() throws IOException - { + public void testExperimentsAreCreatedWithEverythingOnServer() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENT_TYPE))); TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SPACE))); @@ -132,8 +120,7 @@ public class ImportExperimentsTest extends AbstractImportTest @Test @DirtiesContext - public void testExperimentsAreCreatedWithEverythingOnServerAndInXls() throws IOException - { + public void testExperimentsAreCreatedWithEverythingOnServerAndInXls() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENT_TYPE))); TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SPACE))); @@ -149,16 +136,14 @@ public class ImportExperimentsTest extends AbstractImportTest } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfExperimentTypeDoesntExist() throws IOException - { + public void shouldThrowExceptionIfExperimentTypeDoesntExist() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SPACE))); TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROJECT))); TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENTS_ALL_ELSEWHERE))); } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfProjectDoesntExist() throws IOException - { + public void shouldThrowExceptionIfProjectDoesntExist() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENT_TYPE))); TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SPACE))); TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENTS_ALL_ELSEWHERE))); @@ -166,8 +151,7 @@ public class ImportExperimentsTest extends AbstractImportTest @Test @DirtiesContext - public void testExperimentsAreCreatedWithTypeOnServer() throws IOException - { + public void testExperimentsAreCreatedWithTypeOnServer() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENT_TYPE))); TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENTS_WITH_TYPE_ELSEWHERE))); @@ -182,8 +166,7 @@ public class ImportExperimentsTest extends AbstractImportTest @Test @DirtiesContext - public void testExperimentsAreCreatedWithTypeOnServerAndInXls() throws IOException - { + public void testExperimentsAreCreatedWithTypeOnServerAndInXls() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENT_TYPE))); TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SPACE))); @@ -199,15 +182,13 @@ public class ImportExperimentsTest extends AbstractImportTest } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfExperimentNoCode() throws IOException - { + public void shouldThrowExceptionIfExperimentNoCode() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENTS_NO_CODE))); } @Test @DirtiesContext - public void testExperimentsAreCreatedWhenNonMandatoryPropertiesAreNotProvided() throws IOException - { + public void testExperimentsAreCreatedWhenNonMandatoryPropertiesAreNotProvided() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENTS_WITH_NON_MANDATORY_PROPERTY_MISSING))); // WHEN @@ -220,15 +201,13 @@ public class ImportExperimentsTest extends AbstractImportTest } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfExperimentNoProject() throws IOException - { + public void shouldThrowExceptionIfExperimentNoProject() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENTS_NO_PROJECT_ATTRIBUTE))); } @Test @DirtiesContext - public void testExperimentsAreCreatedWithSpaceAndProjectOnServer() throws IOException - { + public void testExperimentsAreCreatedWithSpaceAndProjectOnServer() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SPACE))); TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROJECT))); @@ -244,8 +223,7 @@ public class ImportExperimentsTest extends AbstractImportTest @Test @DirtiesContext - public void testExperimentsAreCreatedWithSpaceOnServer() throws IOException - { + public void testExperimentsAreCreatedWithSpaceOnServer() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SPACE))); TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENTS_SPACE_ELSEWHERE))); @@ -260,8 +238,7 @@ public class ImportExperimentsTest extends AbstractImportTest @Test @DirtiesContext - public void testExperimentsAreCreatedWithTypeAndSpaceOnServer() throws IOException - { + public void testExperimentsAreCreatedWithTypeAndSpaceOnServer() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENT_TYPE))); TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SPACE))); @@ -276,15 +253,13 @@ public class ImportExperimentsTest extends AbstractImportTest } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfMandatoryPropertyMissing() throws IOException - { + public void shouldThrowExceptionIfMandatoryPropertyMissing() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENTS_WITH_MANDATORY_PROPERTY_MISSING))); } @Test @DirtiesContext - public void testExperimentsAreCreatedIfMandatoryPropertyArePresent() throws IOException - { + public void testExperimentsAreCreatedIfMandatoryPropertyArePresent() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENTS_WITH_MANDATORY_PROPERTY_PRESENT))); // WHEN @@ -298,8 +273,7 @@ public class ImportExperimentsTest extends AbstractImportTest @Test @DirtiesContext - public void testExperimentsAreCreatedWhenPropertiesAreAddressedByLabelsWithTypeInXls() throws IOException - { + public void testExperimentsAreCreatedWhenPropertiesAreAddressedByLabelsWithTypeInXls() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENTS_PROPERTIES_COLUMNS_AS_LABELS))); // WHEN @@ -313,8 +287,7 @@ public class ImportExperimentsTest extends AbstractImportTest @Test @DirtiesContext - public void testExperimentsAreCreatedWhenPropertiesAreAddressedByLabelsWithTypeOnServer() throws IOException - { + public void testExperimentsAreCreatedWhenPropertiesAreAddressedByLabelsWithTypeOnServer() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, EXPERIMENT_TYPE))); TestUtils.createFrom(v3api, sessionToken, diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportFromCsvTest.java b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportFromCsvTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ef83a7a2058334f7bf6bdf0213e070bc4d0cf9a2 --- /dev/null +++ b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportFromCsvTest.java @@ -0,0 +1,53 @@ +package ch.ethz.sis.openbis.systemtest.plugin.excelimport; + +import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.Vocabulary; +import ch.ethz.sis.openbis.generic.server.asapi.v3.IApplicationServerInternalApi; +import org.apache.commons.io.FilenameUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.Rollback; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.transaction.annotation.Transactional; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.nio.file.Paths; + +import static org.testng.Assert.assertNotNull; + +@ContextConfiguration(locations = "classpath:applicationContext.xml") +@Transactional(transactionManager = "transaction-manager") +@Rollback +public class ImportFromCsvTest extends AbstractImportTest { + + @Autowired + private IApplicationServerInternalApi v3api; + + private static final String TEST_USER = "test"; + + private static final String PASSWORD = "password"; + + private static final String FULL_TYPES_CSV = "csv/types.csv"; + + @BeforeClass + public void setupClass() throws IOException { + String f = ImportExperimentTypesTest.class.getName().replace(".", "/"); + FILES_DIR = f.substring(0, f.length() - ImportExperimentTypesTest.class.getSimpleName().length()) + "/test_files/"; + } + + @Test + @DirtiesContext + public void testNormalExperimentTypesAreCreated() throws Exception { + // GIVEN + TestUtils.createFromCsv(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, FULL_TYPES_CSV))); + // WHEN + Vocabulary detection = TestUtils.getVocabulary(v3api, sessionToken, "DETECTION"); + // THEN + assertNotNull(detection); + } + + +} diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportProjectsTest.java b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportProjectsTest.java index 7b06530d87c6fa5575741a913414f78c8937692d..72f44c1a0ef59fc056560028be21af5c51e89734 100644 --- a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportProjectsTest.java +++ b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportProjectsTest.java @@ -11,6 +11,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.transaction.annotation.Transactional; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -22,8 +23,7 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException; @ContextConfiguration(locations = "classpath:applicationContext.xml") @Transactional(transactionManager = "transaction-manager") @Rollback -public class ImportProjectsTest extends AbstractImportTest -{ +public class ImportProjectsTest extends AbstractImportTest { @Autowired private IApplicationServerInternalApi v3api; @@ -45,25 +45,15 @@ public class ImportProjectsTest extends AbstractImportTest private static String FILES_DIR; - private String sessionToken; - @BeforeClass - public void setupClass() throws IOException - { + public void setupClass() throws IOException { String f = ImportProjectsTest.class.getName().replace(".", "/"); FILES_DIR = f.substring(0, f.length() - ImportProjectsTest.class.getSimpleName().length()) + "/test_files/"; } - @BeforeMethod - public void beforeTest() - { - sessionToken = v3api.login(TEST_USER, PASSWORD); - } - @Test @DirtiesContext - public void testProjectsAreCreated() throws IOException - { + public void testProjectsAreCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROJECTS_XLS))); // WHEN @@ -76,8 +66,7 @@ public class ImportProjectsTest extends AbstractImportTest @Test @DirtiesContext - public void testProjectsAreCreatedSecondProject() throws IOException - { + public void testProjectsAreCreatedSecondProject() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROJECTS_XLS))); // WHEN @@ -89,15 +78,13 @@ public class ImportProjectsTest extends AbstractImportTest } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfNoProjectCode() throws IOException - { + public void shouldThrowExceptionIfNoProjectCode() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROJECTS_NO_CODE))); } @Test @DirtiesContext - public void testProjectsAreCreatedNoDescription() throws IOException - { + public void testProjectsAreCreatedNoDescription() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROJECTS_NO_DESCRIPTION))); // WHEN @@ -109,15 +96,13 @@ public class ImportProjectsTest extends AbstractImportTest } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfNoProjectSpace() throws IOException - { + public void shouldThrowExceptionIfNoProjectSpace() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROJECTS_NO_SPACE))); } @Test @DirtiesContext - public void testProjectsAreCreatedSpaceOnServer() throws IOException - { + public void testProjectsAreCreatedSpaceOnServer() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SPACES))); TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROJECTS_WITH_SPACES_ON_SERVER))); diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportPropertyTypesTest.java b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportPropertyTypesTest.java index 8b0def35d27e07b085af53fe2dce981ace80eb7d..7db73a88f35d6717cc265e4b5ac14f73c75a1f3f 100644 --- a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportPropertyTypesTest.java +++ b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportPropertyTypesTest.java @@ -14,6 +14,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.transaction.annotation.Transactional; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -26,8 +27,7 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException; @ContextConfiguration(locations = "classpath:applicationContext.xml") @Transactional(transactionManager = "transaction-manager") @Rollback -public class ImportPropertyTypesTest extends AbstractImportTest -{ +public class ImportPropertyTypesTest extends AbstractImportTest { @Autowired private IApplicationServerInternalApi v3api; @@ -50,6 +50,10 @@ public class ImportPropertyTypesTest extends AbstractImportTest private static final String PROPERTY_NON_VOCAB_TYPE_VOCABULARY_CODE = "property_types/vocabcode_when_not_vocabtype.xls"; + private static final String PROPERTY_DUPLICATES_DIFFERENT = "property_types/duplicates_different.xls"; + + private static final String PROPERTY_TYPES_DUPLICATES_SAME = "property_types/duplicates_same.xls"; + private static final String PROPERTY_VOCABULARY_ON_SERVER = "property_types/with_vocab_on_server.xls"; private static final String PROPERTY_VOCAB_TYPE = "property_types/with_vocab.xls"; @@ -58,25 +62,15 @@ public class ImportPropertyTypesTest extends AbstractImportTest private static String FILES_DIR; - private String sessionToken; - @BeforeClass - public void setupClass() throws IOException - { + public void setupClass() throws IOException { String f = ImportPropertyTypesTest.class.getName().replace(".", "/"); FILES_DIR = f.substring(0, f.length() - ImportPropertyTypesTest.class.getSimpleName().length()) + "/test_files/"; } - @BeforeMethod - public void beforeTest() - { - sessionToken = v3api.login(TEST_USER, PASSWORD); - } - @Test @DirtiesContext - public void testNormalPropertyTypesAreCreated() throws IOException - { + public void testNormalPropertyTypesAreCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROPERTY_TYPES_XLS))); // WHEN @@ -93,8 +87,7 @@ public class ImportPropertyTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testInternalPropertyTypesAreCreated() throws IOException - { + public void testInternalPropertyTypesAreCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROPERTY_TYPES_XLS))); // WHEN @@ -109,39 +102,55 @@ public class ImportPropertyTypesTest extends AbstractImportTest assertNull(notes.getVocabulary()); } + @Test + @DirtiesContext + public void testDuplicatesPropertiesAreAllowedIfTheyAreTheSame() throws IOException { + // GIVEN + TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROPERTY_TYPES_DUPLICATES_SAME))); + // WHEN + PropertyType notes = TestUtils.getPropertyType(v3api, sessionToken, "NOTES"); + // THEN + assertEquals(notes.getCode(), "NOTES"); + assertEquals(notes.getLabel(), "Notes"); + assertEquals(notes.getDataType(), DataType.MULTILINE_VARCHAR); + assertEquals(notes.getDescription(), "Notes Descripton"); + assertFalse(notes.isInternalNameSpace()); + assertFalse(notes.isManagedInternally()); + assertNull(notes.getVocabulary()); + } + @Test(expectedExceptions = UserFailureException.class) - public void testPropertyTypeNoCode() throws IOException - { + public void testPropertyTypeNoCode() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROPERTY_NO_CODE))); } @Test(expectedExceptions = UserFailureException.class) - public void testPropertyTypeNoLabel() throws IOException - { + public void testPropertyTypeNoLabel() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROPERTY_NO_LABEL))); } @Test(expectedExceptions = UserFailureException.class) - public void testPropertyTypeNoVocabularyCodeWhenVocabularyType() throws IOException - { + public void testPropertyTypesDuplicatesAreDifferent() throws IOException { + TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROPERTY_DUPLICATES_DIFFERENT))); + } + + @Test(expectedExceptions = UserFailureException.class) + public void testPropertyTypeNoVocabularyCodeWhenVocabularyType() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROPERTY_VOCAB_TYPE_NO_VOCABULARY_CODE))); } @Test(expectedExceptions = UserFailureException.class) - public void testPropertyTypeNoDataType() throws IOException - { + public void testPropertyTypeNoDataType() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROPERTY_NO_DATA_TYPE))); } @Test(expectedExceptions = UserFailureException.class) - public void testPropertyTypeNoDescription() throws IOException - { + public void testPropertyTypeNoDescription() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROPERTY_NO_DESCRIPTION))); } @Test(expectedExceptions = UserFailureException.class) - public void testPropertyTypeVocabularyCodeToNonVocabularyType() throws IOException - { + public void testPropertyTypeVocabularyCodeToNonVocabularyType() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PROPERTY_NON_VOCAB_TYPE_VOCABULARY_CODE))); } diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportSampleTypesTest.java b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportSampleTypesTest.java index 78c3538bf5c6fcc708c07b90dfd5d8c569b93112..9dc77235e4fbe810f07c1c4ea99d6bef16fa1a88 100644 --- a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportSampleTypesTest.java +++ b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportSampleTypesTest.java @@ -14,6 +14,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.transaction.annotation.Transactional; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -28,8 +29,7 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException; @ContextConfiguration(locations = "classpath:applicationContext.xml") @Transactional(transactionManager = "transaction-manager") @Rollback -public class ImportSampleTypesTest extends AbstractImportTest -{ +public class ImportSampleTypesTest extends AbstractImportTest { private static final String SAMPLE_TYPES_XLS = "sample_types/normal_samples.xls"; @@ -58,25 +58,15 @@ public class ImportSampleTypesTest extends AbstractImportTest private static String FILES_DIR; - private String sessionToken; - @BeforeClass - public void setupClass() throws IOException - { + public void setupClass() throws IOException { String f = ImportSampleTypesTest.class.getName().replace(".", "/"); FILES_DIR = f.substring(0, f.length() - ImportSampleTypesTest.class.getSimpleName().length()) + "/test_files/"; } - @BeforeMethod - public void beforeTest() - { - sessionToken = v3api.login(TEST_USER, PASSWORD); - } - @Test @DirtiesContext - public void testNormalSampleTypesAreCreated() throws IOException - { + public void testNormalSampleTypesAreCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLE_TYPES_XLS))); // WHEN @@ -87,8 +77,7 @@ public class ImportSampleTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testPropertyTypeAssignmentsFromNormalSampleTypesAreCreated() throws IOException - { + public void testPropertyTypeAssignmentsFromNormalSampleTypesAreCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLE_TYPES_XLS))); // WHEN @@ -111,8 +100,7 @@ public class ImportSampleTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testPropertyTypeAssignmentsFromNormalv2SampleTypesAreCreated() throws IOException - { + public void testPropertyTypeAssignmentsFromNormalv2SampleTypesAreCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLE_TYPES_XLS_DIFFERENT_PROPERTY_ASSIGN))); // WHEN @@ -129,8 +117,7 @@ public class ImportSampleTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testPropertyTypesFromNormalSampleTypesAreCreated() throws IOException - { + public void testPropertyTypesFromNormalSampleTypesAreCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLE_TYPES_XLS))); // WHEN @@ -151,8 +138,7 @@ public class ImportSampleTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testSampleTypesWithPropertyHavingDynamicScript() throws IOException - { + public void testSampleTypesWithPropertyHavingDynamicScript() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, TestUtils.getDynamicPluginMap(), Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLE_TYPES_WITH_DYNAMIC_SCRIPT))); @@ -168,8 +154,7 @@ public class ImportSampleTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testSampleTypesWithPropertyHavingValidationScript() throws IOException - { + public void testSampleTypesWithPropertyHavingValidationScript() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, TestUtils.getValidationPluginMap(), Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLE_TYPES_WITH_VALIDATION_SCRIPT))); @@ -185,8 +170,7 @@ public class ImportSampleTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testSampleTypesWithVocabularyInXls() throws IOException - { + public void testSampleTypesWithVocabularyInXls() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLE_TYPES_WITH_VOCABULARY))); // WHEN @@ -199,8 +183,7 @@ public class ImportSampleTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testSampleTypesWithVocabularyOnServer() throws IOException - { + public void testSampleTypesWithVocabularyOnServer() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, VOCABULARY_DETECTION))); TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLE_TYPES_WITH_VOCABULARY_ON_SERVER))); @@ -214,8 +197,7 @@ public class ImportSampleTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testSampleTypesWithAutoGeneratedCodeAttribute() throws IOException - { + public void testSampleTypesWithAutoGeneratedCodeAttribute() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLE_TYPES_WITH_AUTO_GENERATED_CODES))); // WHEN @@ -225,8 +207,7 @@ public class ImportSampleTypesTest extends AbstractImportTest } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfNoSampleCode() throws IOException - { + public void shouldThrowExceptionIfNoSampleCode() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLE_TYPE_NO_CODE))); } diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportSamplesTest.java b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportSamplesTest.java index f83fb7839c5e85bc3e832ed6d54b4e3ee3802752..da630f96e445df3ee1cc0bfc36e84f682d22cb45 100644 --- a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportSamplesTest.java +++ b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportSamplesTest.java @@ -12,6 +12,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.transaction.annotation.Transactional; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -23,8 +24,7 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException; @ContextConfiguration(locations = "classpath:applicationContext.xml") @Transactional(transactionManager = "transaction-manager") @Rollback -public class ImportSamplesTest extends AbstractImportTest -{ +public class ImportSamplesTest extends AbstractImportTest { @Autowired private IApplicationServerInternalApi v3api; @@ -64,25 +64,15 @@ public class ImportSamplesTest extends AbstractImportTest private static String FILES_DIR; - private String sessionToken; - @BeforeClass - public void setupClass() throws IOException - { + public void setupClass() throws IOException { String f = ImportSamplesTest.class.getName().replace(".", "/"); FILES_DIR = f.substring(0, f.length() - ImportSamplesTest.class.getSimpleName().length()) + "/test_files/"; } - @BeforeMethod - public void beforeTest() - { - sessionToken = v3api.login(TEST_USER, PASSWORD); - } - @Test @DirtiesContext - public void testSamplesAreCreated() throws IOException - { + public void testSamplesAreCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLES_XLS))); // WHEN @@ -96,8 +86,7 @@ public class ImportSamplesTest extends AbstractImportTest @Test @DirtiesContext - public void testSamplesAreCreatedSecondSample() throws IOException - { + public void testSamplesAreCreatedSecondSample() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLES_XLS))); // WHEN @@ -111,8 +100,7 @@ public class ImportSamplesTest extends AbstractImportTest @Test @DirtiesContext - public void testSamplesAreCreatedThirdSample() throws IOException - { + public void testSamplesAreCreatedThirdSample() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLES_XLS))); // WHEN @@ -126,8 +114,7 @@ public class ImportSamplesTest extends AbstractImportTest @Test @DirtiesContext - public void testSamplesAreCreatedWhenSpaceOnServer() throws IOException - { + public void testSamplesAreCreatedWhenSpaceOnServer() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SPACE))); TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLES_SPACE_ELSEWHERE))); @@ -139,8 +126,7 @@ public class ImportSamplesTest extends AbstractImportTest @Test @DirtiesContext - public void testSamplesAreCreatedWhenSpaceInSeparateXls() throws IOException - { + public void testSamplesAreCreatedWhenSpaceInSeparateXls() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLES_SPACE_ELSEWHERE)), @@ -152,15 +138,13 @@ public class ImportSamplesTest extends AbstractImportTest } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfSpaceDoesntExist() throws IOException - { + public void shouldThrowExceptionIfSpaceDoesntExist() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLES_SPACE_ELSEWHERE))); } @Test @DirtiesContext - public void testSamplesAreCreatedWhenSampleTypeOnServer() throws IOException - { + public void testSamplesAreCreatedWhenSampleTypeOnServer() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, VOCABULARY_TYPE)), @@ -174,8 +158,7 @@ public class ImportSamplesTest extends AbstractImportTest @Test @DirtiesContext - public void testSamplesAreCreatedWhenSampleTypeInSeparateXls() throws IOException - { + public void testSamplesAreCreatedWhenSampleTypeInSeparateXls() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLES_SAMPLE_TYPE_ELSWHERE)), @@ -188,8 +171,7 @@ public class ImportSamplesTest extends AbstractImportTest @Test @DirtiesContext - public void testSamplesChildrenAreAssignedWhenAddressedByCodeInXls() throws IOException - { + public void testSamplesChildrenAreAssignedWhenAddressedByCodeInXls() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, CHILD_AS_CODE))); @@ -205,8 +187,7 @@ public class ImportSamplesTest extends AbstractImportTest @Test @DirtiesContext - public void testSamplesParentsAreAssignedWhenAddressedByCodeInXls() throws IOException - { + public void testSamplesParentsAreAssignedWhenAddressedByCodeInXls() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PARENT_AS_CODE))); @@ -222,8 +203,7 @@ public class ImportSamplesTest extends AbstractImportTest @Test @DirtiesContext - public void testSamplesChildrenAreAssignedWhenAddressedByDollartagInXls() throws IOException - { + public void testSamplesChildrenAreAssignedWhenAddressedByDollartagInXls() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, CHILD_AS_DOLLARTAG))); @@ -239,8 +219,7 @@ public class ImportSamplesTest extends AbstractImportTest @Test @DirtiesContext - public void testSamplesParentsAreAssignedWhenAddressedByDollartagInXls() throws IOException - { + public void testSamplesParentsAreAssignedWhenAddressedByDollartagInXls() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, PARENT_AS_DOLLARTAG))); @@ -256,8 +235,7 @@ public class ImportSamplesTest extends AbstractImportTest @Test @DirtiesContext - public void testCreatesSampleWithNonMandatoryFieldsMissing() throws IOException - { + public void testCreatesSampleWithNonMandatoryFieldsMissing() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, NON_MANDATORY_FIELD_MISSING))); @@ -270,8 +248,7 @@ public class ImportSamplesTest extends AbstractImportTest @Test @DirtiesContext - public void testCreatesSampleWithAutogeneratedCodeWhenOnPerSampleLevel() throws IOException - { + public void testCreatesSampleWithAutogeneratedCodeWhenOnPerSampleLevel() throws IOException { // GIVEN String result = TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, AUTO_GENERATED_SAMPLE_LEVEL))); @@ -285,8 +262,7 @@ public class ImportSamplesTest extends AbstractImportTest @Test @DirtiesContext - public void testCreatesSampleWithAutogeneratedCodeWhenOnSampleTypeLevel() throws IOException - { + public void testCreatesSampleWithAutogeneratedCodeWhenOnSampleTypeLevel() throws IOException { // GIVEN String result = TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, AUTO_GENERATED_SAMPLE_TYPE_LEVEL))); @@ -299,14 +275,12 @@ public class ImportSamplesTest extends AbstractImportTest } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfSamplesSpaceProjectDoesntExist() throws IOException - { + public void shouldThrowExceptionIfSamplesSpaceProjectDoesntExist() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SAMPLES_SPACE_PROJECT_EXPERIMENT_ELSEWHERE))); } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfMandatoryPropertyIsMissing() throws IOException - { + public void shouldThrowExceptionIfMandatoryPropertyIsMissing() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, MANDATORY_FIELD_MISSING))); } diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportSpacesTest.java b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportSpacesTest.java index c76bf95b2505429d0711af6453ffebf7dbdd9fc8..fbf983dcbe02e05d2cf054323c3d9622fc0415a0 100644 --- a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportSpacesTest.java +++ b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportSpacesTest.java @@ -11,6 +11,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.transaction.annotation.Transactional; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -22,8 +23,7 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException; @ContextConfiguration(locations = "classpath:applicationContext.xml") @Transactional(transactionManager = "transaction-manager") @Rollback -public class ImportSpacesTest extends AbstractImportTest -{ +public class ImportSpacesTest extends AbstractImportTest { @Autowired private IApplicationServerInternalApi v3api; @@ -39,25 +39,15 @@ public class ImportSpacesTest extends AbstractImportTest private static String FILES_DIR; - private String sessionToken; - @BeforeClass - public void setupClass() throws IOException - { + public void setupClass() throws IOException { String f = ImportSpacesTest.class.getName().replace(".", "/"); FILES_DIR = f.substring(0, f.length() - ImportSpacesTest.class.getSimpleName().length()) + "/test_files/"; } - @BeforeMethod - public void beforeTest() - { - sessionToken = v3api.login(TEST_USER, PASSWORD); - } - @Test @DirtiesContext - public void testNormalSpacesAreCreated() throws IOException - { + public void testNormalSpacesAreCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SPACES_XLS))); // WHEN @@ -69,8 +59,7 @@ public class ImportSpacesTest extends AbstractImportTest @Test @DirtiesContext - public void testNormalSpacesAreCreatedSecondSpace() throws IOException - { + public void testNormalSpacesAreCreatedSecondSpace() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SPACES_XLS))); // WHEN @@ -81,15 +70,13 @@ public class ImportSpacesTest extends AbstractImportTest } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfNoSpaceCode() throws IOException - { + public void shouldThrowExceptionIfNoSpaceCode() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SPACES_NO_CODE))); } @Test @DirtiesContext - public void shouldCreateSpaceWhenNoDescription() throws IOException - { + public void shouldCreateSpaceWhenNoDescription() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, SPACES_NO_DESCRIPTION))); // WHEN diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportVocabularyTypesTest.java b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportVocabularyTypesTest.java index 4a8f9088f44b3de91fbf0614bb0d1f088b6556d3..8b7778c786933c00dc0b483e14ac7ad0ffeefdd9 100644 --- a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportVocabularyTypesTest.java +++ b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/ImportVocabularyTypesTest.java @@ -30,6 +30,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.transaction.annotation.Transactional; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -42,8 +43,7 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException; @ContextConfiguration(locations = "classpath:applicationContext.xml") @Transactional(transactionManager = "transaction-manager") @Rollback -public class ImportVocabularyTypesTest extends AbstractImportTest -{ +public class ImportVocabularyTypesTest extends AbstractImportTest { private static final String VOCABULARIES_TYPES_XLS = "vocabularies/normal_vocab.xls"; @@ -68,26 +68,15 @@ public class ImportVocabularyTypesTest extends AbstractImportTest private static String FILES_DIR; - private String sessionToken; - @BeforeClass - public void setupClass() throws IOException - { + public void setupClass() throws IOException { String f = ImportVocabularyTypesTest.class.getName().replace(".", "/"); FILES_DIR = f.substring(0, f.length() - ImportVocabularyTypesTest.class.getSimpleName().length()) + "/test_files/"; - - } - - @BeforeMethod - public void beforeTest() - { - sessionToken = v3api.login(TEST_USER, PASSWORD); } @Test @DirtiesContext - public void testNormalVocabularyCreationIsCreated() throws IOException - { + public void testNormalVocabularyCreationIsCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, VOCABULARIES_TYPES_XLS))); // WHEN @@ -99,8 +88,7 @@ public class ImportVocabularyTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testNormalVocabularyHasFirstTermCreated() throws IOException - { + public void testNormalVocabularyHasFirstTermCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, VOCABULARIES_TYPES_XLS))); // WHEN @@ -114,8 +102,7 @@ public class ImportVocabularyTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testNormalVocabularyCreatedNoExtraVocabulary() throws IOException - { + public void testNormalVocabularyCreatedNoExtraVocabulary() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, VOCABULARIES_TYPES_XLS))); // WHEN @@ -126,8 +113,7 @@ public class ImportVocabularyTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testNormalVocabularyHasSecondTermCreated() throws IOException - { + public void testNormalVocabularyHasSecondTermCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, VOCABULARIES_TYPES_XLS))); // WHEN @@ -141,8 +127,7 @@ public class ImportVocabularyTypesTest extends AbstractImportTest @Test @DirtiesContext - public void testVocabularyWithNoTermDescriptionShouldBeCreated() throws IOException - { + public void testVocabularyWithNoTermDescriptionShouldBeCreated() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, VOCABULARIES_NO_TERM_DESCRIPTION))); // WHEN @@ -153,21 +138,18 @@ public class ImportVocabularyTypesTest extends AbstractImportTest } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfNoVocabularyCode() throws IOException - { + public void shouldThrowExceptionIfNoVocabularyCode() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, VOCABULARIES_NO_CODE))); } @Test(expectedExceptions = UserFailureException.class) - public void shouldThrowExceptionIfNoTermCode() throws IOException - { + public void shouldThrowExceptionIfNoTermCode() throws IOException { TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, VOCABULARIES_NO_TERM_CODE))); } @Test @DirtiesContext - public void shouldNotThrowExceptionIfNoVocabularyDescription() throws IOException - { + public void shouldNotThrowExceptionIfNoVocabularyDescription() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, VOCABULARIES_NO_DESCRIPTION))); // WHEN @@ -179,8 +161,7 @@ public class ImportVocabularyTypesTest extends AbstractImportTest @Test @DirtiesContext - public void shouldNotThrowExceptionIfNoTermLabel() throws IOException - { + public void shouldNotThrowExceptionIfNoTermLabel() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, VOCABULARIES_NO_TERM_LABEL))); // WHEN @@ -192,8 +173,7 @@ public class ImportVocabularyTypesTest extends AbstractImportTest @Test @DirtiesContext - public void shouldNotThrowExceptionIfNoTerms() throws IOException - { + public void shouldNotThrowExceptionIfNoTerms() throws IOException { // GIVEN TestUtils.createFrom(v3api, sessionToken, Paths.get(FilenameUtils.concat(FILES_DIR, VOCABULARIES_NO_TERMS))); // WHEN diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/TestUtils.java b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/TestUtils.java index 8991380e7946060be63d06291bb7f7c213121bc6..f81328e301bb1d1e93f76c71cf8592eb7f73e84a 100644 --- a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/TestUtils.java +++ b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/TestUtils.java @@ -9,6 +9,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import org.apache.commons.io.IOUtils; @@ -53,58 +54,51 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.id.VocabularyPermId; import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.search.VocabularySearchCriteria; import ch.ethz.sis.openbis.generic.server.asapi.v3.IApplicationServerInternalApi; -public class TestUtils -{ +public class TestUtils { private static final String TEST_XLS = "TEST-XLS"; private static final String XLS_NAME = "xls_name"; private static final String UPDATE_MODE = "update_mode"; - public static final String XLS_PARAM = "xls"; + private static final String XLS_PARAM = "xls"; - public static final String SCRIPTS_PARAM = "scripts"; + public static final String CSV_PARAM = "csv"; - public static final String XLS_IMPORT_API = "xls-import-api"; + private static final String SCRIPTS_PARAM = "scripts"; - static Vocabulary getVocabulary(IApplicationServerInternalApi v3api, String sessionToken, String code) - { + private static final String XLS_IMPORT_API = "xls-import-api"; + + static Vocabulary getVocabulary(IApplicationServerInternalApi v3api, String sessionToken, String code) { VocabularySearchCriteria criteria = new VocabularySearchCriteria(); criteria.withId().thatEquals(new VocabularyPermId(code)); - VocabularyFetchOptions fo = new VocabularyFetchOptions(); fo.withTerms(); SearchResult<Vocabulary> result = v3api.searchVocabularies(sessionToken, criteria, fo); - if (result.getObjects().size() > 0) - { + if (result.getObjects().size() > 0) { return result.getObjects().get(0); - } else - { + } else { return null; } } - static List<Vocabulary> getAllVocabularies(IApplicationServerInternalApi v3api, String sessionToken) - { + static List<Vocabulary> getAllVocabularies(IApplicationServerInternalApi v3api, String sessionToken) { VocabularySearchCriteria criteria = new VocabularySearchCriteria(); VocabularyFetchOptions fo = new VocabularyFetchOptions(); fo.withTerms(); SearchResult<Vocabulary> result = v3api.searchVocabularies(sessionToken, criteria, fo); - if (result.getObjects().size() > 0) - { + if (result.getObjects().size() > 0) { return result.getObjects(); - } else - { + } else { return null; } } - static SampleType getSampleType(IApplicationServerInternalApi v3api, String sessionToken, String code) - { + static SampleType getSampleType(IApplicationServerInternalApi v3api, String sessionToken, String code) { SampleTypeSearchCriteria criteria = new SampleTypeSearchCriteria(); criteria.withCode().thatEquals(code); @@ -116,17 +110,14 @@ public class TestUtils SearchResult<SampleType> result = v3api.searchSampleTypes(sessionToken, criteria, fo); - if (result.getObjects().size() > 0) - { + if (result.getObjects().size() > 0) { return result.getObjects().get(0); - } else - { + } else { return null; } } - static ExperimentType getExperimentType(IApplicationServerInternalApi v3api, String sessionToken, String code) - { + static ExperimentType getExperimentType(IApplicationServerInternalApi v3api, String sessionToken, String code) { ExperimentTypeSearchCriteria criteria = new ExperimentTypeSearchCriteria(); criteria.withCode().thatEquals(code); @@ -138,17 +129,14 @@ public class TestUtils SearchResult<ExperimentType> result = v3api.searchExperimentTypes(sessionToken, criteria, fo); - if (result.getObjects().size() > 0) - { + if (result.getObjects().size() > 0) { return result.getObjects().get(0); - } else - { + } else { return null; } } - static DataSetType getDatasetType(IApplicationServerInternalApi v3api, String sessionToken, String code) - { + static DataSetType getDatasetType(IApplicationServerInternalApi v3api, String sessionToken, String code) { DataSetTypeSearchCriteria criteria = new DataSetTypeSearchCriteria(); criteria.withCode().thatEquals(code); @@ -160,17 +148,14 @@ public class TestUtils SearchResult<DataSetType> result = v3api.searchDataSetTypes(sessionToken, criteria, fo); - if (result.getObjects().size() > 0) - { + if (result.getObjects().size() > 0) { return result.getObjects().get(0); - } else - { + } else { return null; } } - static PropertyType getPropertyType(IApplicationServerInternalApi v3api, String sessionToken, String code) - { + static PropertyType getPropertyType(IApplicationServerInternalApi v3api, String sessionToken, String code) { PropertyTypeSearchCriteria criteria = new PropertyTypeSearchCriteria(); criteria.withCode().thatEquals(code); @@ -179,17 +164,14 @@ public class TestUtils SearchResult<PropertyType> result = v3api.searchPropertyTypes(sessionToken, criteria, fo); - if (result.getObjects().size() > 0) - { + if (result.getObjects().size() > 0) { return result.getObjects().get(0); - } else - { + } else { return null; } } - static Space getSpace(IApplicationServerInternalApi v3api, String sessionToken, String code) - { + static Space getSpace(IApplicationServerInternalApi v3api, String sessionToken, String code) { SpaceSearchCriteria criteria = new SpaceSearchCriteria(); criteria.withCode().thatEquals(code); @@ -197,17 +179,14 @@ public class TestUtils SearchResult<Space> result = v3api.searchSpaces(sessionToken, criteria, fo); - if (result.getObjects().size() > 0) - { + if (result.getObjects().size() > 0) { return result.getObjects().get(0); - } else - { + } else { return null; } } - static Project getProject(IApplicationServerInternalApi v3api, String sessionToken, String code) - { + static Project getProject(IApplicationServerInternalApi v3api, String sessionToken, String code) { ProjectSearchCriteria criteria = new ProjectSearchCriteria(); criteria.withCode().thatEquals(code); @@ -216,18 +195,15 @@ public class TestUtils SearchResult<Project> result = v3api.searchProjects(sessionToken, criteria, fo); - if (result.getObjects().size() > 0) - { + if (result.getObjects().size() > 0) { return result.getObjects().get(0); - } else - { + } else { return null; } } static Experiment getExperiment(IApplicationServerInternalApi v3api, String sessionToken, String experimentCode, String projectCode, - String spaceCode) - { + String spaceCode) { List<IExperimentId> ids = new ArrayList<>(); ids.add(new ExperimentIdentifier(spaceCode, projectCode, experimentCode)); @@ -238,33 +214,28 @@ public class TestUtils List<Experiment> result = v3api.getExperiments(sessionToken, ids, fo).values().stream().collect(Collectors.toList()); - if (result.size() > 0) - { + if (result.size() > 0) { return result.get(0); - } else - { + } else { return null; } } - static Sample getSample(IApplicationServerInternalApi v3api, String sessionToken, String sampleCode, String spaceCode) - { + static Sample getSample(IApplicationServerInternalApi v3api, String sessionToken, String sampleCode, String spaceCode) { List<ISampleId> ids = new ArrayList<>(); ids.add(new SampleIdentifier(spaceCode, null, null, sampleCode)); return getSamples(v3api, sessionToken, ids); } - static Sample getSampleByPermId(IApplicationServerInternalApi v3api, String sessionToken, String permId) - { + static Sample getSampleByPermId(IApplicationServerInternalApi v3api, String sessionToken, String permId) { List<ISampleId> ids = new ArrayList<>(); ids.add(new SamplePermId(permId)); return getSamples(v3api, sessionToken, ids); } - private static Sample getSamples(IApplicationServerInternalApi v3api, String sessionToken, List<ISampleId> ids) - { + private static Sample getSamples(IApplicationServerInternalApi v3api, String sessionToken, List<ISampleId> ids) { SampleFetchOptions fo = new SampleFetchOptions(); SampleFetchOptions childrenFo = fo.withChildren(); childrenFo.withSpace(); @@ -280,20 +251,16 @@ public class TestUtils List<Sample> result = v3api.getSamples(sessionToken, ids, fo).values().stream().collect(Collectors.toList()); - if (result.size() > 0) - { + if (result.size() > 0) { return result.get(0); - } else - { + } else { return null; } } - static String createFrom(IApplicationServerInternalApi v3api, String sessionToken, Path... xls_paths) throws IOException - { + static String createFrom(IApplicationServerInternalApi v3api, String sessionToken, Path... xls_paths) throws IOException { List<byte[]> excels = new ArrayList<>(); - for (Path xls_path : xls_paths) - { + for (Path xls_path : xls_paths) { byte[] xls = readData(xls_path); excels.add(xls); } @@ -304,18 +271,28 @@ public class TestUtils return (String) v3api.executeCustomASService(sessionToken, new CustomASServiceCode(XLS_IMPORT_API), options); } + static String createFromCsv(IApplicationServerInternalApi v3api, String sessionToken, Path... csv_paths) throws IOException { + List<byte[]> csvs = new ArrayList<>(); + for (Path csv_path : csv_paths) { + byte[] csv = readData(csv_path); + csvs.add(csv); + } + CustomASServiceExecutionOptions options = new CustomASServiceExecutionOptions(); + options.withParameter(CSV_PARAM, csvs); + options.withParameter(UPDATE_MODE, UpdateMode.IGNORE_EXISTING.name()); + options.withParameter(XLS_NAME, TEST_XLS); + return (String) v3api.executeCustomASService(sessionToken, new CustomASServiceCode(XLS_IMPORT_API), options); + } + static String createFrom(IApplicationServerInternalApi v3api, String sessionToken, Map<String, String> scripts, Path... xls_paths) - throws IOException - { + throws IOException { return TestUtils.createFrom(v3api, sessionToken, scripts, UpdateMode.IGNORE_EXISTING, xls_paths); } static String createFrom(IApplicationServerInternalApi v3api, String sessionToken, Map<String, String> scripts, UpdateMode updateMode, - Path... xls_paths) throws IOException - { + Path... xls_paths) throws IOException { List<byte[]> excels = new ArrayList<>(); - for (Path xls_path : xls_paths) - { + for (Path xls_path : xls_paths) { byte[] xls = readData(xls_path); excels.add(xls); } @@ -327,18 +304,15 @@ public class TestUtils return (String) v3api.executeCustomASService(sessionToken, new CustomASServiceCode(XLS_IMPORT_API), options); } - static String getValidationScript() - { + static String getValidationScript() { return "def validate(entity, isNew):\n if isNew:\n return"; } - static String getDynamicScript() - { + static String getDynamicScript() { return "def calculate():\n return 1"; } - static Map<String, String> getValidationPluginMap() - { + static Map<String, String> getValidationPluginMap() { String dynamicScriptString = getValidationScript(); Map<String, String> scriptsMap = new HashMap<>(); scriptsMap.put("valid.py", dynamicScriptString); @@ -346,8 +320,7 @@ public class TestUtils return scriptsMap; } - static Map<String, String> getDynamicPluginMap() - { + static Map<String, String> getDynamicPluginMap() { String dynamicScriptString = getDynamicScript(); Map<String, String> scriptsMap = new HashMap<>(); scriptsMap.put("dynamic/dynamic.py", dynamicScriptString); @@ -355,8 +328,7 @@ public class TestUtils return scriptsMap; } - static String extractSamplePermIdFromResults(String result) - { + static String extractSamplePermIdFromResults(String result) { // Note this will work only if we created single sample!! String permId = result.substring(result.indexOf("CreateSamplesOperationResult") + "CreateSamplesOperationResult".length()); permId = StringUtils.strip(permId, "[]"); @@ -364,15 +336,13 @@ public class TestUtils } static List<PropertyAssignment> extractAndSortPropertyAssignmentsPerGivenPropertyName(IEntityType rawData, List<String> propertyNames) - throws Exception - { + throws Exception { List<PropertyAssignment> propertyAssignments = rawData.getPropertyAssignments(); List<PropertyAssignment> sortedPropertyAssignments = propertyNames.stream().map(propertyName -> { return propertyAssignments.stream().filter(prop -> prop.getPropertyType().getPermId().toString().equals(propertyName)).findFirst().get(); }).collect(Collectors.toList()); - if (sortedPropertyAssignments.stream().anyMatch(property -> property == null)) - { + if (sortedPropertyAssignments.stream().anyMatch(Objects::isNull)) { throw new Exception("Some properties are missing" + "\nFollowing properties are expected " + Arrays.toString(propertyNames.toArray()) + "\n Available properties are: " + Arrays.toString(propertyAssignments.toArray())); @@ -381,21 +351,12 @@ public class TestUtils return sortedPropertyAssignments; } - private static byte[] readData(Path xls_path) throws IOException - { + private static byte[] readData(Path xls_path) throws IOException { String path = xls_path.toString(); - InputStream resourceAsStream = TestUtils.class.getClassLoader().getResourceAsStream(path); - try - { + try (InputStream resourceAsStream = TestUtils.class.getClassLoader().getResourceAsStream(path)) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); IOUtils.copy(resourceAsStream, byteArrayOutputStream); return byteArrayOutputStream.toByteArray(); - } finally - { - if (resourceAsStream != null) - { - resourceAsStream.close(); - } } } diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/csv/types.csv b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/csv/types.csv new file mode 100644 index 0000000000000000000000000000000000000000..f316aaed6c9fbd4852819bfbdecfb0892bca93be --- /dev/null +++ b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/csv/types.csv @@ -0,0 +1,7 @@ +VOCABULARY_TYPE,,,,,,,,,,,,,,,,, +Version,Code,Description,,,,,,,,,,,,,,, +225,DETECTION,Protein detection system,,,,,,,,,,,,,,, +Version,Code,Label,Description,,,,,,,,,,,,,, +225,HRP,horseradish peroxydase,The antibody is conjugated with the horseradish peroxydase,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,, diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/experiment_types/normal_experiment.xls b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/experiment_types/normal_experiment.xls index 166c329684354e9de6cd843ebb64be76e7d42dd3..4d4d7fab58aaca4cfd9b45f3c386ddbd8e1e37b2 100644 Binary files a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/experiment_types/normal_experiment.xls and b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/experiment_types/normal_experiment.xls differ diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/experiment_types/normal_experiment_update.xls b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/experiment_types/normal_experiment_update.xls index 6dadc7a18f04e1960f2ccac1f9ef22294637c5eb..fbf1ec66ae61acad2e326f21bb3a71cb5addc473 100644 Binary files a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/experiment_types/normal_experiment_update.xls and b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/experiment_types/normal_experiment_update.xls differ diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/property_types/duplicates_different.xls b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/property_types/duplicates_different.xls new file mode 100644 index 0000000000000000000000000000000000000000..81f20402fd34580406f2b52dc078df22f1e712bf Binary files /dev/null and b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/property_types/duplicates_different.xls differ diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/property_types/duplicates_same.xls b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/property_types/duplicates_same.xls new file mode 100644 index 0000000000000000000000000000000000000000..35b3eb68daf6ab65fbe014d77afe51c26311a8db Binary files /dev/null and b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/property_types/duplicates_same.xls differ diff --git a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/property_types/normal_property_type.xls b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/property_types/normal_property_type.xls index b60b0d1af6c8bcc493e04f7ab025c2f30bcba1b4..eb22dc6e62a54f32184abf1b8c37691847f17000 100644 Binary files a/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/property_types/normal_property_type.xls and b/openbis_standard_technologies/sourceTest/java/ch/ethz/sis/openbis/systemtest/plugin/excelimport/test_files/property_types/normal_property_type.xls differ