From a26eb4819dcc5f8a6adf43ebe94407d4cfced425 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 25 Feb 2022 14:06:17 +0100 Subject: [PATCH 001/134] Migrated interdomain REST server to frontend REST server --- shongo-controller/pom.xml | 28 ++++ .../cesnet/shongo/controller/Controller.java | 105 +++------------ .../controller/ControllerConfiguration.java | 108 ++++++++------- .../authorization/ServerAuthorization.java | 2 +- .../domains/DomainAuthentication.java | 12 +- .../shongo/controller/rest/RESTApiServer.java | 124 ++++++++++++++++++ .../controller/rest/auth/AuthFilter.java | 50 +++++++ .../controller/rest/auth/SecurityConfig.java | 31 +++++ ...omain-servlet.xml => rest-api-servlet.xml} | 15 ++- .../controller/AbstractControllerTest.java | 2 +- .../controller/domains/InterDomainTest.java | 20 +-- 11 files changed, 339 insertions(+), 158 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/AuthFilter.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java rename shongo-controller/src/main/resources/WEB-INF/{interDomain-servlet.xml => rest-api-servlet.xml} (68%) diff --git a/shongo-controller/pom.xml b/shongo-controller/pom.xml index a3266ef6b..63e73e58f 100644 --- a/shongo-controller/pom.xml +++ b/shongo-controller/pom.xml @@ -74,6 +74,34 @@ 1.8.4 + + + + org.springframework.security + spring-security-web + ${spring.version} + + + org.springframework.security + spring-security-config + ${spring.version} + + + org.springframework.security + spring-security-oauth2-core + ${spring.version} + + + org.springframework.security + spring-security-oauth2-jose + ${spring.version} + + + org.springframework.security + spring-security-oauth2-resource-server + ${spring.version} + + org.codehaus.jackson diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/Controller.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/Controller.java index 7a27f8b8b..2bf4dc3e2 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/Controller.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/Controller.java @@ -1,6 +1,5 @@ package cz.cesnet.shongo.controller; -import com.google.common.base.Strings; import cz.cesnet.shongo.api.rpc.Service; import cz.cesnet.shongo.controller.api.UserSettings; import cz.cesnet.shongo.controller.api.jade.ServiceImpl; @@ -11,20 +10,18 @@ import cz.cesnet.shongo.controller.calendar.CalendarManager; import cz.cesnet.shongo.controller.calendar.connector.CalDAVConnector; import cz.cesnet.shongo.controller.calendar.connector.CalendarConnector; -import cz.cesnet.shongo.controller.domains.BasicAuthFilter; import cz.cesnet.shongo.controller.domains.InterDomainAgent; -import cz.cesnet.shongo.controller.domains.SSLClientCertFilter; import cz.cesnet.shongo.controller.executor.Executor; import cz.cesnet.shongo.controller.notification.executor.EmailNotificationExecutor; import cz.cesnet.shongo.controller.notification.executor.NotificationExecutor; import cz.cesnet.shongo.controller.notification.NotificationManager; +import cz.cesnet.shongo.controller.rest.RESTApiServer; import cz.cesnet.shongo.controller.scheduler.Preprocessor; import cz.cesnet.shongo.controller.scheduler.Scheduler; import cz.cesnet.shongo.controller.util.NativeQuery; import cz.cesnet.shongo.jade.Agent; import cz.cesnet.shongo.jade.Container; import cz.cesnet.shongo.ssl.ConfiguredSSLContext; -import cz.cesnet.shongo.ssl.SSLCommunication; import cz.cesnet.shongo.util.Logging; import cz.cesnet.shongo.util.Timer; import org.apache.commons.cli.*; @@ -35,21 +32,15 @@ import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletMapping; import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.eclipse.jetty.webapp.WebAppContext; import org.joda.time.DateTimeZone; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.web.servlet.DispatcherServlet; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; -import javax.servlet.DispatcherType; import java.io.IOException; import java.io.InputStream; -import java.net.URL; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; @@ -537,7 +528,7 @@ public void startAll() throws Exception { start(); startRpc(); startJade(); - startInterDomainRESTApi(); + startRESTApi(); startWorkerThread(); startComponents(); } @@ -629,87 +620,21 @@ public Container startJade() return jadeContainer; } - public Server startInterDomainRESTApi() throws NoSuchAlgorithmException, CertificateException, InvalidAlgorithmParameterException, IOException, KeyStoreException { - if (configuration.isInterDomainConfigured()) { - logger.info("Starting Inter Domain REST server on {}:{}...", - configuration.getInterDomainHost(), configuration.getInterDomainPort()); - - restServer = new Server(); - // Configure SSL - ConfiguredSSLContext.getInstance().loadConfiguration(configuration); - - // Create web app - WebAppContext webAppContext = new WebAppContext(); - String servletPath = "/*"; - webAppContext.addServlet(new ServletHolder("interDomain", DispatcherServlet.class), servletPath); - webAppContext.setParentLoaderPriority(true); - - URL resourceBaseUrl = Controller.class.getClassLoader().getResource("WEB-INF"); - if (resourceBaseUrl == null) { - throw new RuntimeException("WEB-INF is not in classpath."); - } - String resourceBase = resourceBaseUrl.toExternalForm().replace("/WEB-INF", "/"); - webAppContext.setResourceBase(resourceBase); - - final HttpConfiguration http_config = new HttpConfiguration(); - - // Configure HTTPS connector - http_config.setSecureScheme(HttpScheme.HTTPS.asString()); - http_config.setSecurePort(configuration.getInterDomainPort()); - final HttpConfiguration https_config = new HttpConfiguration(http_config); - https_config.addCustomizer(new SecureRequestCustomizer()); - - - final SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); - trustStore.load(null); - // Load certificates of foreign domain's CAs - for (String certificatePath : configuration.getForeignDomainsCaCertFiles()) { - trustStore.setCertificateEntry(certificatePath.substring(0, certificatePath.lastIndexOf('.')), - SSLCommunication.readPEMCert(certificatePath)); - } - sslContextFactory.setKeyStorePath(configuration.getInterDomainSslKeyStore()); - sslContextFactory.setKeyStoreType(configuration.getInterDomainSslKeyStoreType()); - sslContextFactory.setKeyStorePassword(configuration.getInterDomainSslKeyStorePassword()); - sslContextFactory.setTrustStore(trustStore); - if (configuration.requiresClientPKIAuth()) { - // Enable forced client auth - sslContextFactory.setNeedClientAuth(true); - // Enable SSL client filter by certificates - EnumSet filterTypes = EnumSet.of(DispatcherType.REQUEST); - webAppContext.addFilter(SSLClientCertFilter.class, servletPath, filterTypes); - } - else { - EnumSet filterTypes = EnumSet.of(DispatcherType.REQUEST); - webAppContext.addFilter(BasicAuthFilter.class, servletPath, filterTypes); - } - - final ServerConnector httpsConnector = new ServerConnector(restServer, - new SslConnectionFactory(sslContextFactory, "http/1.1"), - new HttpConnectionFactory(https_config)); - String host = configuration.getInterDomainHost(); - if (!Strings.isNullOrEmpty(host)) { - httpsConnector.setHost(host); - } - httpsConnector.setPort(configuration.getInterDomainPort()); - httpsConnector.setIdleTimeout(configuration.getInterDomainCommandTimeout()); - - + /** + * Creates a Jetty REST api server for frontend and inter-domain extension. + */ + public Server startRESTApi() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { + logger.info("Starting REST api server on {}:{}...", + configuration.getRESTApiHost(), configuration.getRESTApiPort()); - restServer.setConnectors(new Connector[]{httpsConnector}); + restServer = RESTApiServer.start(configuration); - restServer.setHandler(webAppContext); - try { - restServer.start(); - logger.info("Inter Domain REST server successfully started."); - } catch (Exception exception) { - throw new RuntimeException(exception); - } - - this.interDomainInitialized = true; - return restServer; + if (configuration.isInterDomainConfigured()) { + interDomainInitialized = true; } - return null; + logger.info("REST api server successfully started."); + + return restServer; } /** @@ -803,7 +728,7 @@ public void stop() } if (restServer != null) { - logger.info("Stopping Controller Inter Domain REST server..."); + logger.info("Stopping Controller REST server..."); try { restServer.stop(); } catch (Exception exception) { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java index d9e1cffbf..7315dec1c 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java @@ -13,6 +13,8 @@ import org.joda.time.Period; import org.postgresql.util.Base64; +import java.net.MalformedURLException; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.regex.MatchResult; @@ -59,16 +61,21 @@ public class ControllerConfiguration extends CombinedConfiguration public static final String JADE_AGENT_NAME = "jade.agent-name"; public static final String JADE_PLATFORM_ID = "jade.platform-id"; + /** + * REST api configuration + */ + public static final String REST_API = "rest-api"; + public static final String REST_API_HOST = "rest-api.host"; + public static final String REST_API_PORT = "rest-api.port"; + public static final String REST_API_SSL_KEY_STORE = REST_API + ".ssl-key-store"; + public static final String REST_API_SSL_KEY_STORE_TYPE = REST_API + ".ssl-key-store-type"; + public static final String REST_API_SSL_KEY_STORE_PASSWORD = REST_API + ".ssl-key-store-password"; + /** * Interdomains configuration */ public static final String INTERDOMAIN = "domain.inter-domain-connection"; - public static final String INTERDOMAIN_HOST = INTERDOMAIN + ".host"; - public static final String INTERDOMAIN_PORT = INTERDOMAIN + ".port"; public static final String INTERDOMAIN_PKI_CLIENT_AUTH = INTERDOMAIN + ".pki-client-auth"; - public static final String INTERDOMAIN_SSL_KEY_STORE = INTERDOMAIN + ".ssl-key-store"; - public static final String INTERDOMAIN_SSL_KEY_STORE_TYPE = INTERDOMAIN + ".ssl-key-store-type"; - public static final String INTERDOMAIN_SSL_KEY_STORE_PASSWORD = INTERDOMAIN + ".ssl-key-store-password"; public static final String INTERDOMAIN_TRUSTED_CA_CERT_FILES = INTERDOMAIN + ".ssl-trust-store.ca-certificate"; public static final String INTERDOMAIN_COMMAND_TIMEOUT = INTERDOMAIN + ".command-timeout"; public static final String INTERDOMAIN_CACHE_REFRESH_RATE = INTERDOMAIN + ".cache-refresh-rate"; @@ -294,6 +301,21 @@ public int getRpcPort() return getInt(RPC_PORT); } + /** + * @return XML-RPC url + */ + public URL getRpcUrl() throws MalformedURLException { + int rpcPort; + + try { + rpcPort = getRpcPort(); + } catch (NoSuchElementException e) { + rpcPort = 8181; + } + String urlString = String.format("http://%s:%d", getRpcHost(false), rpcPort); + return new URL(urlString); + } + /** * @return XML-RPC ssl key store */ @@ -481,43 +503,54 @@ else if (name.equals("domain.shortName")) { public boolean isInterDomainConfigured() { - if (getInterDomainPort() != null) { - if (requiresClientPKIAuth() && hasInterDomainPKI()) { - return true; - } - if (hasInterDomainBasicAuth()) { - return true; - } + if (requiresClientPKIAuth() && hasRESTApiPKI()) { + return true; } - return false; + return hasInterDomainBasicAuth(); } - public boolean hasInterDomainPKI() + public boolean hasInterDomainBasicAuth() { - if (Strings.isNullOrEmpty(getInterDomainSslKeyStore()) - || Strings.isNullOrEmpty(getInterDomainSslKeyStoreType()) - || Strings.isNullOrEmpty(getInterDomainSslKeyStorePassword())) { - return false; - } - return true; + return !Strings.isNullOrEmpty(getInterDomainBasicAuthPasswordHash()); } - public boolean hasInterDomainBasicAuth() + public String getRESTApiHost() { - if (Strings.isNullOrEmpty(getInterDomainBasicAuthPasswordHash())) { - return false; - } - return true; + String host = getString(ControllerConfiguration.REST_API_HOST); + return host != null ? host : "localhost"; + } + + public Integer getRESTApiPort() + { + Integer port = getInteger(ControllerConfiguration.REST_API_PORT, null); + return port != null ? port : 9999; } - public String getInterDomainHost() + public String getRESTApiSslKeyStore() { - return getString(ControllerConfiguration.INTERDOMAIN_HOST); + String sslKeyStore = getString(ControllerConfiguration.REST_API_SSL_KEY_STORE); + if (sslKeyStore == null || sslKeyStore.trim().isEmpty()) { + return null; + } + return sslKeyStore; + } + + public String getRESTApiSslKeyStoreType() { + return getString(ControllerConfiguration.REST_API_SSL_KEY_STORE_TYPE); } - public Integer getInterDomainPort() + public String getRESTApiSslKeyStorePassword() { + return getString(ControllerConfiguration.REST_API_SSL_KEY_STORE_PASSWORD); + } + + public boolean hasRESTApiPKI() { - return getInteger(ControllerConfiguration.INTERDOMAIN_PORT, null); + if (Strings.isNullOrEmpty(getRESTApiSslKeyStore()) + || Strings.isNullOrEmpty(getRESTApiSslKeyStoreType()) + || Strings.isNullOrEmpty(getRESTApiSslKeyStorePassword())) { + return false; + } + return true; } /** @@ -529,15 +562,6 @@ public boolean requiresClientPKIAuth() return getBoolean(ControllerConfiguration.INTERDOMAIN_PKI_CLIENT_AUTH, false); } - public String getInterDomainSslKeyStore() - { - String sslKeyStore = getString(ControllerConfiguration.INTERDOMAIN_SSL_KEY_STORE); - if (sslKeyStore == null || sslKeyStore.trim().isEmpty()) { - return null; - } - return sslKeyStore; - } - public String getInterDomainBasicAuthPasswordHash() { String password = getString(ControllerConfiguration.INTERDOMAIN_BASIC_AUTH_PASSWORD); @@ -547,14 +571,6 @@ public String getInterDomainBasicAuthPasswordHash() return SSLCommunication.hashPassword(password.getBytes()); } - public String getInterDomainSslKeyStoreType() { - return getString(ControllerConfiguration.INTERDOMAIN_SSL_KEY_STORE_TYPE); - } - - public String getInterDomainSslKeyStorePassword() { - return getString(ControllerConfiguration.INTERDOMAIN_SSL_KEY_STORE_PASSWORD); - } - public List getForeignDomainsCaCertFiles() { List caCertFiles = new ArrayList(); for (Object o : getList(ControllerConfiguration.INTERDOMAIN_TRUSTED_CA_CERT_FILES)) { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java index af7846db2..5992f9169 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java @@ -47,7 +47,7 @@ public class ServerAuthorization extends Authorization /** * Authentication service path in auth-server. */ - private static final String AUTHENTICATION_SERVICE_PATH = "/oidc-authn/oic"; + private static final String AUTHENTICATION_SERVICE_PATH = "/oidc"; /** * User web service path in auth-server. diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/domains/DomainAuthentication.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/domains/DomainAuthentication.java index c422e6d45..7941db6fb 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/domains/DomainAuthentication.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/domains/DomainAuthentication.java @@ -47,17 +47,17 @@ public DomainAuthentication(ControllerConfiguration configuration, this.domainService = domainService; this.notifier = notifier; try { - KeyStore keyStore = KeyStore.getInstance(configuration.getInterDomainSslKeyStoreType()); - FileInputStream keyStoreFile = new FileInputStream(configuration.getInterDomainSslKeyStore()); - keyStore.load(keyStoreFile, configuration.getInterDomainSslKeyStorePassword().toCharArray()); + KeyStore keyStore = KeyStore.getInstance(configuration.getRESTApiSslKeyStoreType()); + FileInputStream keyStoreFile = new FileInputStream(configuration.getRESTApiSslKeyStore()); + keyStore.load(keyStoreFile, configuration.getRESTApiSslKeyStorePassword().toCharArray()); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); - keyManagerFactory.init(keyStore, configuration.getInterDomainSslKeyStorePassword().toCharArray()); + keyManagerFactory.init(keyStore, configuration.getRESTApiSslKeyStorePassword().toCharArray()); this.keyManagerFactory = keyManagerFactory; } catch (GeneralSecurityException e) { - throw new RuntimeException("Failed to load keystore " + configuration.getInterDomainSslKeyStore(), e); + throw new RuntimeException("Failed to load keystore " + configuration.getRESTApiSslKeyStore(), e); } catch (IOException e) { e.printStackTrace(); - throw new RuntimeException("Failed to read keystore " + configuration.getInterDomainSslKeyStore(), e); + throw new RuntimeException("Failed to read keystore " + configuration.getRESTApiSslKeyStore(), e); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java new file mode 100644 index 000000000..ed72d7129 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java @@ -0,0 +1,124 @@ +package cz.cesnet.shongo.controller.rest; + +import com.google.common.base.Strings; +import cz.cesnet.shongo.controller.Controller; +import cz.cesnet.shongo.controller.ControllerConfiguration; +import cz.cesnet.shongo.controller.domains.BasicAuthFilter; +import cz.cesnet.shongo.controller.domains.SSLClientCertFilter; +import cz.cesnet.shongo.ssl.ConfiguredSSLContext; +import cz.cesnet.shongo.ssl.SSLCommunication; +import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.server.*; +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.webapp.WebAppContext; +import org.springframework.web.filter.DelegatingFilterProxy; +import org.springframework.web.servlet.DispatcherServlet; + +import javax.servlet.DispatcherType; +import java.io.IOException; +import java.net.URL; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.EnumSet; + +public class RESTApiServer { + private static final String SERVLET_PATH = "/"; + private static final String SERVLET_NAME = "rest-api"; + private static final String INTER_DOMAIN_API_PATH = "/domain/**"; + + public static Server start(ControllerConfiguration configuration) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { + Server restServer = new Server(); + ConfiguredSSLContext.getInstance().loadConfiguration(configuration); + String resourceBase = RESTApiServer.getResourceBase(); + + WebAppContext webAppContext = new WebAppContext(); + webAppContext.addServlet(new ServletHolder(SERVLET_NAME, DispatcherServlet.class), SERVLET_PATH); + webAppContext.setResourceBase(resourceBase); + webAppContext.setParentLoaderPriority(true); + webAppContext.addFilter( + new FilterHolder(new DelegatingFilterProxy("springSecurityFilterChain")), + "/*", EnumSet.allOf(DispatcherType.class) + ); + + ServerConnector httpsConnector = createHTTPConnector(configuration, webAppContext, restServer); + + restServer.setConnectors(new Connector[]{httpsConnector}); + restServer.setHandler(webAppContext); + + try { + restServer.start(); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + + return restServer; + } + + private static String getResourceBase() { + URL resourceBaseUrl = Controller.class.getClassLoader().getResource("WEB-INF"); + if (resourceBaseUrl == null) { + throw new RuntimeException("WEB-INF is not in classpath."); + } + return resourceBaseUrl.toExternalForm().replace("/WEB-INF", "/"); + } + + private static ServerConnector createHTTPConnector(ControllerConfiguration configuration, WebAppContext webAppContext, Server server) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { + final HttpConfiguration http_config = new HttpConfiguration(); + http_config.setSecurePort(configuration.getRESTApiPort()); + ServerConnector serverConnector; + + if (configuration.hasRESTApiPKI()) { + http_config.setSecureScheme(HttpScheme.HTTPS.asString()); + + final HttpConfiguration https_config = new HttpConfiguration(http_config); + https_config.addCustomizer(new SecureRequestCustomizer()); + + final SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); + + sslContextFactory.setKeyStorePath(configuration.getRESTApiSslKeyStore()); + sslContextFactory.setKeyStoreType(configuration.getRESTApiSslKeyStoreType()); + sslContextFactory.setKeyStorePassword(configuration.getRESTApiSslKeyStorePassword()); + + if (configuration.isInterDomainConfigured()) { + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(null); + // Load certificates of foreign domain's CAs + for (String certificatePath : configuration.getForeignDomainsCaCertFiles()) { + trustStore.setCertificateEntry(certificatePath.substring(0, certificatePath.lastIndexOf('.')), + SSLCommunication.readPEMCert(certificatePath)); + } + sslContextFactory.setTrustStore(trustStore); + + if (configuration.requiresClientPKIAuth()) { + // Enable forced client auth + sslContextFactory.setNeedClientAuth(true); + // Enable SSL client filter by certificates + EnumSet filterTypes = EnumSet.of(DispatcherType.REQUEST); + webAppContext.addFilter(SSLClientCertFilter.class, INTER_DOMAIN_API_PATH, filterTypes); + } else { + EnumSet filterTypes = EnumSet.of(DispatcherType.REQUEST); + webAppContext.addFilter(BasicAuthFilter.class, INTER_DOMAIN_API_PATH, filterTypes); + } + } + serverConnector = new ServerConnector(server, + new SslConnectionFactory(sslContextFactory, "http/1.1"), + new HttpConnectionFactory(https_config)); + } else { + http_config.setSecureScheme(HttpScheme.HTTP.asString()); + serverConnector = new ServerConnector(server, new HttpConnectionFactory(http_config)); + } + + String host = configuration.getRESTApiHost(); + if (!Strings.isNullOrEmpty(host)) { + serverConnector.setHost(host); + } + serverConnector.setPort(configuration.getRESTApiPort()); +// serverConnector.setIdleTimeout(configuration.getInterDomainCommandTimeout()); + + return serverConnector; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/AuthFilter.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/AuthFilter.java new file mode 100644 index 000000000..7c530ef83 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/AuthFilter.java @@ -0,0 +1,50 @@ +package cz.cesnet.shongo.controller.rest.auth; + +import cz.cesnet.shongo.controller.ControllerReportSet; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.authorization.Authorization; +import org.springframework.web.filter.GenericFilterBean; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Authorizes request based on authentication token. + * + * @author Filip Karnis + */ +public class AuthFilter extends GenericFilterBean { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ControllerReportSet.SecurityInvalidTokenException, ServletException, IOException { + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + Authorization authorization = Authorization.getInstance(); + + String accessToken = httpRequest.getHeader("Authorization"); + if (accessToken == null) { + httpResponse.sendError(401, "No Authorization header found."); + return; + } + + String[] tokenParts = accessToken.split("Bearer"); + if (tokenParts.length != 2) { + httpResponse.sendError(401, "Invalid access token."); + return; + } + + String sanitizedToken = accessToken.split("Bearer")[1].strip(); + SecurityToken securityToken = new SecurityToken(sanitizedToken); + securityToken.setUserInformation(authorization.getUserInformation(securityToken)); + + try { + authorization.validate(securityToken); + } catch (ControllerReportSet.SecurityInvalidTokenException e) { + httpResponse.sendError(401, "Request unauthorized."); + return; + } + chain.doFilter(httpRequest, httpResponse); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java new file mode 100644 index 000000000..5008efb38 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java @@ -0,0 +1,31 @@ +package cz.cesnet.shongo.controller.rest.auth; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; + +/** + * Configures spring security for REST api server. + * + * @author Filip Karnis + */ +@Configuration +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + public void configure(WebSecurity web) { + web.ignoring().antMatchers("/domain/**"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + AuthFilter authFilter = new AuthFilter(); + http.addFilterAt(authFilter, BasicAuthenticationFilter.class) + .authorizeRequests() + .antMatchers("/domain/**").permitAll(); + } +} diff --git a/shongo-controller/src/main/resources/WEB-INF/interDomain-servlet.xml b/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml similarity index 68% rename from shongo-controller/src/main/resources/WEB-INF/interDomain-servlet.xml rename to shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml index 0bc324e63..013bf7787 100644 --- a/shongo-controller/src/main/resources/WEB-INF/interDomain-servlet.xml +++ b/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml @@ -14,7 +14,14 @@ - - - - \ No newline at end of file + + + + + + + + + + + diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java index 341a0a091..f97fdd074 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java @@ -340,7 +340,7 @@ public void stop() logger.debug("Starting controller for " + getClass().getName() + "..."); controller.start(); controller.startRpc(); - controller.startInterDomainRESTApi(); + controller.startRESTApi(); // Start client controllerClient = new ControllerClient(controller.getRpcHost(), controller.getRpcPort()); diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/domains/InterDomainTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/domains/InterDomainTest.java index c02d2608e..7ee2b8164 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/domains/InterDomainTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/domains/InterDomainTest.java @@ -35,11 +35,11 @@ public class InterDomainTest extends AbstractControllerTest @Override public void before() throws Exception { - System.setProperty(ControllerConfiguration.INTERDOMAIN_HOST, INTERDOMAIN_LOCAL_HOST); - System.setProperty(ControllerConfiguration.INTERDOMAIN_PORT, INTERDOMAIN_LOCAL_PORT.toString()); - System.setProperty(ControllerConfiguration.INTERDOMAIN_SSL_KEY_STORE, "./shongo-controller/src/test/resources/keystore/server.p12"); - System.setProperty(ControllerConfiguration.INTERDOMAIN_SSL_KEY_STORE_PASSWORD, "shongo"); - System.setProperty(ControllerConfiguration.INTERDOMAIN_SSL_KEY_STORE_TYPE, "PKCS12"); + System.setProperty(ControllerConfiguration.REST_API_HOST, INTERDOMAIN_LOCAL_HOST); + System.setProperty(ControllerConfiguration.REST_API_PORT, INTERDOMAIN_LOCAL_PORT.toString()); + System.setProperty(ControllerConfiguration.REST_API_SSL_KEY_STORE, "./shongo-controller/src/test/resources/keystore/server.p12"); + System.setProperty(ControllerConfiguration.REST_API_SSL_KEY_STORE_PASSWORD, "shongo"); + System.setProperty(ControllerConfiguration.REST_API_SSL_KEY_STORE_TYPE, "PKCS12"); System.setProperty(ControllerConfiguration.INTERDOMAIN_PKI_CLIENT_AUTH, "false"); System.setProperty(ControllerConfiguration.INTERDOMAIN_COMMAND_TIMEOUT, "PT10S"); System.setProperty(ControllerConfiguration.INTERDOMAIN_BASIC_AUTH_PASSWORD, INTERDOMAIN_LOCAL_PASSWORD); @@ -63,11 +63,11 @@ public void before() throws Exception @After public void tearDown() throws Exception { - System.clearProperty(ControllerConfiguration.INTERDOMAIN_HOST); - System.clearProperty(ControllerConfiguration.INTERDOMAIN_PORT); - System.clearProperty(ControllerConfiguration.INTERDOMAIN_SSL_KEY_STORE); - System.clearProperty(ControllerConfiguration.INTERDOMAIN_SSL_KEY_STORE_PASSWORD); - System.clearProperty(ControllerConfiguration.INTERDOMAIN_SSL_KEY_STORE_TYPE); + System.clearProperty(ControllerConfiguration.REST_API_HOST); + System.clearProperty(ControllerConfiguration.REST_API_PORT); + System.clearProperty(ControllerConfiguration.REST_API_SSL_KEY_STORE); + System.clearProperty(ControllerConfiguration.REST_API_SSL_KEY_STORE_PASSWORD); + System.clearProperty(ControllerConfiguration.REST_API_SSL_KEY_STORE_TYPE); System.clearProperty(ControllerConfiguration.INTERDOMAIN_PKI_CLIENT_AUTH); System.clearProperty(ControllerConfiguration.INTERDOMAIN_COMMAND_TIMEOUT); System.clearProperty(ControllerConfiguration.INTERDOMAIN_BASIC_AUTH_PASSWORD); From 08dd00d18d6492f6155599e596d548adbee55f81 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 25 Feb 2022 14:57:57 +0100 Subject: [PATCH 002/134] Added reservation request endpoint --- .../controller/ControllerConfiguration.java | 29 +++- .../api/ReservationRequestController.java | 97 ++++++++++++ .../controller/rest/auth/AuthFilter.java | 25 +++- .../rest/models/TechnologyModel.java | 101 +++++++++++++ .../reservationrequest/SpecificationType.java | 139 ++++++++++++++++++ .../resources/WEB-INF/rest-api-servlet.xml | 4 + 6 files changed, 386 insertions(+), 9 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TechnologyModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java index 7315dec1c..0709cb166 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java @@ -108,7 +108,12 @@ public class ControllerConfiguration extends CombinedConfiguration public static final String CALDAV_BASIC_AUTH_USERNAME = "caldav-connector.basic-auth.username"; public static final String CALDAV_BASIC_AUTH_PASSWORD = "caldav-connector.basic-auth.password"; - + /** + * Tags configuration. + */ + public static final String VEHICLE_TAG = "tags.vehicle"; + public static final String PARKING_PLACE_TAG = "tags.parking-place"; + public static final String MEETING_ROOM_TAG = "tags.meeting-room"; /** * Period in which the executor works. @@ -606,4 +611,26 @@ public String getCalDAVEncodedBasicAuth() String authStringEnc = Base64.encodeBytes(authString.getBytes(StandardCharsets.UTF_8)); return authStringEnc; } + + + /** + * @return name of tag for meeting rooms + */ + public String getMeetingRoomTagName() { + return getString(MEETING_ROOM_TAG); + } + + /** + * @return name of tag for cars + */ + public String getVehicleTagName() { + return getString(VEHICLE_TAG); + } + + /** + * @return name of tag for parking places + */ + public String getParkingPlaceTagName() { + return getString(PARKING_PLACE_TAG); + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java new file mode 100644 index 000000000..ff448df9a --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java @@ -0,0 +1,97 @@ +package cz.cesnet.shongo.controller.rest.api; + +import cz.cesnet.shongo.Temporal; +import cz.cesnet.shongo.api.UserInformation; +import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.request.ListResponse; +import cz.cesnet.shongo.controller.api.request.ReservationRequestListRequest; +import cz.cesnet.shongo.controller.api.rpc.ReservationService; +import cz.cesnet.shongo.controller.rest.models.reservationrequest.SpecificationType; +import cz.cesnet.shongo.controller.rest.models.TechnologyModel; +import org.joda.time.Interval; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.joda.time.DateTime; +import java.util.Set; + +import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; + +/** + * @author Filip Karnis + */ +@RestController +@RequestMapping("/api/v1/reservation_requests") +public class ReservationRequestController { + + private final ReservationService reservationService; + + public ReservationRequestController(@Autowired ReservationService reservationService) { + this.reservationService = reservationService; + } + + @GetMapping("") + ListResponse listRequests( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @RequestParam(value = "start", required = false) Integer start, + @RequestParam(value = "count", required = false) Integer count, + @RequestParam(value = "sort", required = false, + defaultValue = "DATETIME") ReservationRequestListRequest.Sort sort, + @RequestParam(value = "sort_desc", required = false, defaultValue = "true") boolean sortDescending, + @RequestParam(value = "specification_type", required = false) Set specificationTypes, + @RequestParam(value = "specification_technology", required = false) TechnologyModel specificationTechnology, + @RequestParam(value = "interval_from", required = false) DateTime intervalFrom, + @RequestParam(value = "interval_to", required = false) DateTime intervalTo, + @RequestParam(value = "user_id", required = false) String userId, + @RequestParam(value = "participant_user_id", required = false) String participantUserId, + @RequestParam(value = "search", required = false) String search) + { + ReservationRequestListRequest request = new ReservationRequestListRequest(); + + request.setSecurityToken(securityToken); + request.setStart(start); + request.setCount(count); + request.setSort(sort); + request.setSortDescending(sortDescending); + + if (specificationTypes != null && !specificationTypes.isEmpty()) { + if (specificationTypes.contains(SpecificationType.ADHOC_ROOM)) { + request.addSpecificationType(ReservationRequestSummary.SpecificationType.ROOM); + } + if (specificationTypes.contains(SpecificationType.PERMANENT_ROOM)) { + request.addSpecificationType(ReservationRequestSummary.SpecificationType.PERMANENT_ROOM); + } + if (specificationTypes.contains(SpecificationType.PERMANENT_ROOM_CAPACITY)) { + request.addSpecificationType(ReservationRequestSummary.SpecificationType.USED_ROOM); + } + if (specificationTypes.contains(SpecificationType.MEETING_ROOM)) { + request.addSpecificationType(ReservationRequestSummary.SpecificationType.RESOURCE); + } + } + if (specificationTechnology != null) { + request.setSpecificationTechnologies(specificationTechnology.getTechnologies()); + } + if (intervalFrom != null || intervalTo != null) { + if (intervalFrom == null) { + intervalFrom = Temporal.DATETIME_INFINITY_START; + } + if (intervalTo == null) { + intervalTo = Temporal.DATETIME_INFINITY_END; + } + if (intervalTo.isAfter(intervalFrom)) { + request.setInterval(new Interval(intervalFrom, intervalTo)); + } + } + if (userId != null && UserInformation.isLocal(userId)) { + request.setUserId(userId); + } + if (participantUserId != null) { + request.setParticipantUserId(participantUserId); + } + if (search != null) { + request.setSearch(search); + } + + return reservationService.listReservationRequests(request); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/AuthFilter.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/AuthFilter.java index 7c530ef83..af51ab5a6 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/AuthFilter.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/AuthFilter.java @@ -5,7 +5,10 @@ import cz.cesnet.shongo.controller.authorization.Authorization; import org.springframework.web.filter.GenericFilterBean; -import javax.servlet.*; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -17,32 +20,38 @@ */ public class AuthFilter extends GenericFilterBean { + private static final String AUTHORIZATION_HEADER = "Authorization"; + private static final String BEARER = "Bearer"; + public static final String TOKEN = "TOKEN"; + @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ControllerReportSet.SecurityInvalidTokenException, ServletException, IOException { + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws ControllerReportSet.SecurityInvalidTokenException, ServletException, IOException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; Authorization authorization = Authorization.getInstance(); - String accessToken = httpRequest.getHeader("Authorization"); + String accessToken = httpRequest.getHeader(AUTHORIZATION_HEADER); if (accessToken == null) { - httpResponse.sendError(401, "No Authorization header found."); + httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "No Authorization header found."); return; } - String[] tokenParts = accessToken.split("Bearer"); + String[] tokenParts = accessToken.split(BEARER); if (tokenParts.length != 2) { - httpResponse.sendError(401, "Invalid access token."); + httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid access token."); return; } - String sanitizedToken = accessToken.split("Bearer")[1].strip(); + String sanitizedToken = tokenParts[1].strip(); SecurityToken securityToken = new SecurityToken(sanitizedToken); securityToken.setUserInformation(authorization.getUserInformation(securityToken)); try { authorization.validate(securityToken); + httpRequest.setAttribute(TOKEN, securityToken); } catch (ControllerReportSet.SecurityInvalidTokenException e) { - httpResponse.sendError(401, "Request unauthorized."); + httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Request unauthorized."); return; } chain.doFilter(httpRequest, httpResponse); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TechnologyModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TechnologyModel.java new file mode 100644 index 000000000..c9e110241 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TechnologyModel.java @@ -0,0 +1,101 @@ +package cz.cesnet.shongo.controller.rest.models; + +import cz.cesnet.shongo.Technology; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Technology of the alias/room reservation request or executable. + * + * @author Martin Srom + */ +public enum TechnologyModel +{ + + PEXIP("views.technologyModel.PEXIP", Technology.H323, Technology.SIP, + Technology.SKYPE_FOR_BUSINESS, Technology.RTMP, + Technology.WEBRTC), + + /** + * {@link Technology#ADOBE_CONNECT} + */ + ADOBE_CONNECT("views.technologyModel.ADOBE_CONNECT", Technology.ADOBE_CONNECT), + + /** + * {@link Technology#FREEPBX} + */ + FREEPBX("views.technologyModel.FREEPBX", Technology.FREEPBX), + /** + * {@link Technology#H323} and/or {@link Technology#SIP} + */ + H323_SIP("views.technologyModel.H323_SIP", Technology.H323, Technology.SIP); + + + /** + * Code of the title which can be displayed to user. + */ + private final String titleCode; + + /** + * Set of {@link Technology}s which it represents. + */ + private final Set technologies; + + /** + * Constructor. + * + * @param titleCode sets the {@link #titleCode} + * @param technologies sets the {@link #technologies} + */ + TechnologyModel(String titleCode, Technology... technologies) + { + this.titleCode = titleCode; + Set technologySet = new HashSet(); + for (Technology technology : technologies) { + technologySet.add(technology); + } + this.technologies = Collections.unmodifiableSet(technologySet); + } + + /** + * @return {@link #titleCode} + */ + public String getTitleCode() + { + return titleCode; + } + + /** + * @return {@link #technologies} + */ + public Set getTechnologies() + { + return technologies; + } + + /** + * @param technologies which must the returned {@link TechnologyModel} contain + * @return {@link TechnologyModel} which contains all given {@code technologies} + */ + public static TechnologyModel find(Set technologies) + { + if (technologies.size() == 0) { + return null; + } + if (H323_SIP.technologies.containsAll(technologies)) { + return H323_SIP; + } + else if (ADOBE_CONNECT.technologies.containsAll(technologies)) { + return ADOBE_CONNECT; + } + else if (FREEPBX.technologies.containsAll(technologies)) { + return FREEPBX; + } + else if (PEXIP.technologies.containsAll(technologies)) { + return PEXIP; + } + return null; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java new file mode 100644 index 000000000..0dd231bd9 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java @@ -0,0 +1,139 @@ +package cz.cesnet.shongo.controller.rest.models.reservationrequest; + +import cz.cesnet.shongo.TodoImplementException; +import cz.cesnet.shongo.controller.Controller; +import cz.cesnet.shongo.controller.ControllerConfiguration; +import cz.cesnet.shongo.controller.api.ReservationRequestSummary; + +/** + * Type of specification for a reservation request. + * + * @author Martin Srom + */ +public enum SpecificationType +{ + /** + * For ad-hoc room. + */ + ADHOC_ROOM(true, false), + + /** + * For permanent room. + */ + PERMANENT_ROOM(true, false), + + /** + * For permanent room capacity. + */ + PERMANENT_ROOM_CAPACITY(false, false), + + /** + * For meeting room. + */ + MEETING_ROOM(false, true), + + /** + * For parking place. + */ + PARKING_PLACE(false, true), + + /** + * For vehicles. + */ + VEHICLE(false, true); + + /** + * Specifies whether it is a room. + */ + private final boolean isRoom; + + /** + * Specifies whether it is physical resource. + */ + private final boolean isPhysical; + + /** + * Constructor. + * + * @param isRoom sets the {@link #isRoom} + * @param isPhysical + */ + SpecificationType(boolean isRoom, boolean isPhysical) + { + this.isRoom = isRoom; + this.isPhysical = isPhysical; + } + + /** + * @return {@link #isRoom} + */ + public boolean isRoom() + { + return isRoom; + } + + /** + * @return {@link #isPhysical} + */ + public boolean isPhysical() { + return isPhysical; + } + + /** + * @param reservationRequestSummary + * @return {@link SpecificationType} from given {@code reservationRequestSummary} + */ + public static SpecificationType fromReservationRequestSummary(ReservationRequestSummary reservationRequestSummary) + { + ControllerConfiguration configuration = getControllerConfiguration(); + + switch (reservationRequestSummary.getSpecificationType()) { + case ROOM: + return ADHOC_ROOM; + case PERMANENT_ROOM: + return PERMANENT_ROOM; + case USED_ROOM: + return PERMANENT_ROOM_CAPACITY; + case RESOURCE: + String resourceTags = reservationRequestSummary.getResourceTags(); + String parkTagName = configuration.getParkingPlaceTagName(); + String vehicleTagName = configuration.getVehicleTagName(); + if (resourceTags != null) { + if (parkTagName != null && resourceTags.contains(parkTagName)) { + return PARKING_PLACE; + } else if (vehicleTagName != null && resourceTags.contains(vehicleTagName)) { + return VEHICLE; + } + } + return MEETING_ROOM; + default: + throw new TodoImplementException(reservationRequestSummary.getSpecificationType()); + } + } + + /** + * @param string + * @return {@link SpecificationType} from given {@code string} + */ + public static SpecificationType fromString(String string) + { + ControllerConfiguration configuration = getControllerConfiguration(); + + if (string == null) { + return null; + } else if (string.equals(configuration.getMeetingRoomTagName())) { + return MEETING_ROOM; + } else if (string.equals(configuration.getVehicleTagName())) { + return VEHICLE; + } else if (string.equals(configuration.getParkingPlaceTagName())) { + return PARKING_PLACE; + } + throw new TodoImplementException("SpecificationType.fromString for " + string); + + } + + private static ControllerConfiguration getControllerConfiguration() { + Controller controller = Controller.getInstance(); + return controller.getConfiguration(); + } +} diff --git a/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml b/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml index 013bf7787..a12890341 100644 --- a/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml +++ b/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml @@ -24,4 +24,8 @@ + + + + From 840de899b454ad56915b63510e953ce362d1c69b Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 25 Feb 2022 17:52:16 +0100 Subject: [PATCH 003/134] Added OpenApi generation and Swagger ui --- shongo-controller/pom.xml | 7 ++ .../shongo/controller/rest/OpenApiConfig.java | 93 +++++++++++++++++++ .../api/ReservationRequestController.java | 2 + .../controller/rest/auth/SecurityConfig.java | 25 ++++- 4 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/OpenApiConfig.java diff --git a/shongo-controller/pom.xml b/shongo-controller/pom.xml index 63e73e58f..c621b0252 100644 --- a/shongo-controller/pom.xml +++ b/shongo-controller/pom.xml @@ -102,6 +102,13 @@ ${spring.version} + + + org.springdoc + springdoc-openapi-ui + 1.4.0 + + org.codehaus.jackson diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/OpenApiConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/OpenApiConfig.java new file mode 100644 index 000000000..866abaffa --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/OpenApiConfig.java @@ -0,0 +1,93 @@ +package cz.cesnet.shongo.controller.rest; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.security.SecurityScheme; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.core.io.Resource; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.resource.*; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.io.InputStream; + +/** + * Configures OpenApi and SwaggerUI using springdoc. + * + * @author Filip Karnis + */ +@Configuration +@EnableWebMvc +@OpenAPIDefinition( + info = @Info(title = "Shongo API", version = "v1"), + security = @SecurityRequirement(name = "bearerAuth") +) +@SecurityScheme( + name = "bearerAuth", + type = SecuritySchemeType.HTTP, + scheme = "bearer" +) +@ComponentScan(basePackages = {"org.springdoc"}) +@Import({ + org.springdoc.core.SpringDocConfiguration.class, + org.springdoc.webmvc.core.SpringDocWebMvcConfiguration.class, + org.springdoc.webmvc.ui.SwaggerConfig.class, + org.springdoc.core.SwaggerUiConfigProperties.class, + org.springdoc.core.SwaggerUiOAuthProperties.class, + org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.class +}) +class OpenApiConfig implements WebMvcConfigurer { + + private static final String PET_STORE_URL = "https://petstore.swagger.io/v2/swagger.json"; + private static final String SPRINGDOC_OPENAPI_URL = "/v3/api-docs"; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/**/*.html") + .addResourceLocations("classpath:/META-INF/resources/webjars/") + .resourceChain(false) + .addResolver(new WebJarsResourceResolver()) + .addResolver(new PathResourceResolver()) + .addTransformer(new IndexPageTransformer()); + } + + /** + * Replaces the default openapi config URL in swagger with the one generated by springdoc. + */ + public static class IndexPageTransformer implements ResourceTransformer { + @Override + public Resource transform(HttpServletRequest request, Resource resource, + ResourceTransformerChain transformerChain) throws IOException { + if (resource.getURL().toString().endsWith("/index.html")) { + String html = getHtmlContent(resource); + html = overwritePetStore(html); + return new TransformedResource(resource, html.getBytes()); + } else { + return resource; + } + } + + private String overwritePetStore(String html) { + return html.replace(PET_STORE_URL, SPRINGDOC_OPENAPI_URL); + } + + private String getHtmlContent(Resource resource) { + try { + InputStream inputStream = resource.getInputStream(); + java.util.Scanner s = new java.util.Scanner(inputStream).useDelimiter("\\A"); + String content = s.next(); + inputStream.close(); + return content; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java index ff448df9a..8f4647b17 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java @@ -9,6 +9,7 @@ import cz.cesnet.shongo.controller.api.rpc.ReservationService; import cz.cesnet.shongo.controller.rest.models.reservationrequest.SpecificationType; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; +import io.swagger.v3.oas.annotations.Operation; import org.joda.time.Interval; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -30,6 +31,7 @@ public ReservationRequestController(@Autowired ReservationService reservationSer this.reservationService = reservationService; } + @Operation(summary = "Lists reservation requests") @GetMapping("") ListResponse listRequests( @RequestAttribute(TOKEN) SecurityToken securityToken, diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java index 5008efb38..839370287 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java @@ -1,11 +1,17 @@ package cz.cesnet.shongo.controller.rest.auth; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.List; /** * Configures spring security for REST api server. @@ -18,14 +24,29 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) { - web.ignoring().antMatchers("/domain/**"); + web.ignoring() + .antMatchers("/domain/**") + .antMatchers("/v3/api-docs") + .antMatchers("/swagger-ui/**"); } @Override protected void configure(HttpSecurity http) throws Exception { + http.cors().and().csrf().disable(); AuthFilter authFilter = new AuthFilter(); http.addFilterAt(authFilter, BasicAuthenticationFilter.class) .authorizeRequests() - .antMatchers("/domain/**").permitAll(); + .antMatchers("/domain/**").permitAll() + .antMatchers("/v3/api-docs").permitAll() + .antMatchers("/swagger-ui/**").permitAll(); + } + + @Bean + CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration().applyPermitDefaultValues(); + configuration.setAllowedMethods(List.of(CorsConfiguration.ALL)); + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; } } From d06ff69376d7d8160e1bdf40ff9a400533f12ee7 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 25 Feb 2022 18:18:46 +0100 Subject: [PATCH 004/134] Added permissions endpoint --- .../controller/rest/api/AuthController.java | 40 +++++++++++++++++++ .../resources/WEB-INF/rest-api-servlet.xml | 4 ++ 2 files changed, 44 insertions(+) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/AuthController.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/AuthController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/AuthController.java new file mode 100644 index 000000000..246358c4e --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/AuthController.java @@ -0,0 +1,40 @@ +package cz.cesnet.shongo.controller.rest.api; + +import cz.cesnet.shongo.controller.SystemPermission; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; + +/** + * Rest controller for user authorization endpoints. + * + * @author Filip Karnis + */ +@RestController +@RequestMapping("/api/v1") +public class AuthController { + + private final AuthorizationService authorizationService; + + public AuthController(@Autowired AuthorizationService reservationService) { + this.authorizationService = reservationService; + } + + @Operation(summary = "Returns user's system permissions.") + @GetMapping("/permissions") + public List getPermissions( + @RequestAttribute(TOKEN) SecurityToken securityToken) + { + return Stream.of(SystemPermission.values()).filter(permission -> + authorizationService.hasSystemPermission(securityToken, permission) + ).collect(Collectors.toList()); + } +} diff --git a/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml b/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml index a12890341..e9b62eca1 100644 --- a/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml +++ b/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml @@ -28,4 +28,8 @@ + + + + From e097a2e789013c685d2db872cbe72af2205eb488 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sat, 26 Feb 2022 19:23:26 +0100 Subject: [PATCH 005/134] Added lombok dependency --- shongo-controller/pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/shongo-controller/pom.xml b/shongo-controller/pom.xml index c621b0252..ef0a2e6ca 100644 --- a/shongo-controller/pom.xml +++ b/shongo-controller/pom.xml @@ -236,6 +236,13 @@ ical4j-zoneinfo-outlook 1.0.3 + + + org.projectlombok + lombok + 1.18.22 + provided + From 6019a41c38e8853e7634268c764489db2f516d0b Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sun, 6 Mar 2022 19:06:03 +0100 Subject: [PATCH 006/134] Added rest api cache --- .../cesnet/shongo/controller/rest/Cache.java | 702 ++++++++++++++++++ .../rest/ObjectInaccessibleException.java | 21 + .../controller/rest/api/AuthController.java | 40 - .../rest/models/UnsupportedApiException.java | 30 + .../resources/WEB-INF/rest-api-servlet.xml | 18 + 5 files changed, 771 insertions(+), 40 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ObjectInaccessibleException.java delete mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/AuthController.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/UnsupportedApiException.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java new file mode 100644 index 000000000..90e90c5b5 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java @@ -0,0 +1,702 @@ +package cz.cesnet.shongo.controller.rest; + +import cz.cesnet.shongo.CommonReportSet; +import cz.cesnet.shongo.ExpirationMap; +import cz.cesnet.shongo.TodoImplementException; +import cz.cesnet.shongo.api.UserInformation; +import cz.cesnet.shongo.controller.ControllerReportSet; +import cz.cesnet.shongo.controller.ObjectPermission; +import cz.cesnet.shongo.controller.SystemPermission; +import cz.cesnet.shongo.controller.api.*; +import cz.cesnet.shongo.controller.api.request.*; +import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; +import cz.cesnet.shongo.controller.api.rpc.ExecutableService; +import cz.cesnet.shongo.controller.api.rpc.ReservationService; +import cz.cesnet.shongo.controller.api.rpc.ResourceService; +import cz.cesnet.shongo.controller.rest.models.UnsupportedApiException; +import cz.cesnet.shongo.controller.rest.models.resource.ResourcesUtilization; +import org.joda.time.DateTime; +import org.joda.time.Duration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Cache of {@link UserInformation}s, {@link ObjectPermission}s, {@link ReservationRequestSummary}s. + * + * @author Filip Karnis + * @author Martin Srom + */ +public class Cache +{ + private static Logger logger = LoggerFactory.getLogger(Cache.class); + + /** + * Expiration of user information/permissions in minutes. + */ + private static final long USER_EXPIRATION_MINUTES = 5; + + private final AuthorizationService authorizationService; + + private final ResourceService resourceService; + + private final ReservationService reservationService; + + private final ExecutableService executableService; + + /** + * {@link UserInformation}s by {@link SecurityToken}. + */ + private ExpirationMap> userPermissionsByToken = new ExpirationMap<>(); + + /** + * {@link UserInformation}s by user-ids. + */ + private ExpirationMap userInformationByUserId = + new ExpirationMap(); + + /** + * {@link Group}s by group-ids. + */ + private ExpirationMap groupByGroupId = + new ExpirationMap(); + + /** + * {@link UserState}s by {@link SecurityToken}. + */ + private ExpirationMap userStateByToken = new ExpirationMap(); + + /** + * {@link ResourceSummary} by identifier. + */ + private ExpirationMap resourceById = + new ExpirationMap(); + + /** + * {@link ReservationRequestSummary} by identifier. + */ + private ExpirationMap reservationRequestById = + new ExpirationMap(); + + /** + * {@link Reservation} by identifier. + */ + private ExpirationMap reservationById = + new ExpirationMap(); + + /** + * Ids of resources with public calendar by their calendarUriKey + */ + private ExpirationMap resourceIdsWithPublicCalendarByUriKey = + new ExpirationMap(); + + /** + * {@link Reservation} by identifier. + */ + private ExpirationMap executableById = + new ExpirationMap(); + + /** + * @see ResourcesUtilization + */ + private final ExpirationMap resourcesUtilizationByToken = + new ExpirationMap(); + + /** + * Cached information for single user. + */ + private static class UserState + { + /** + * Set of permissions which the user has for object. + */ + private ExpirationMap> objectPermissionsByObject = + new ExpirationMap>(); + + /** + * Constructor. + */ + public UserState() + { + objectPermissionsByObject.setExpiration(Duration.standardMinutes(USER_EXPIRATION_MINUTES)); + } + } + + /** + * Constructor. + */ + public Cache( + @Autowired AuthorizationService authorizationService, + @Autowired ResourceService resourceService, + @Autowired ReservationService reservationService, + @Autowired ExecutableService executableService) + { + this.authorizationService = authorizationService; + this.resourceService = resourceService; + this.reservationService = reservationService; + this.executableService = executableService; + + // Set expiration durations + userPermissionsByToken.setExpiration(Duration.standardMinutes(5)); + userInformationByUserId.setExpiration(Duration.standardMinutes(USER_EXPIRATION_MINUTES)); + groupByGroupId.setExpiration(Duration.standardMinutes(USER_EXPIRATION_MINUTES)); + userStateByToken.setExpiration(Duration.standardHours(1)); + resourceById.setExpiration(Duration.standardHours(1)); + reservationRequestById.setExpiration(Duration.standardMinutes(5)); + reservationById.setExpiration(Duration.standardMinutes(5)); + executableById.setExpiration(Duration.standardSeconds(10)); + resourcesUtilizationByToken.setExpiration(Duration.standardMinutes(10)); + resourceIdsWithPublicCalendarByUriKey.setExpiration(Duration.standardMinutes(10)); + } + + /** + * Method called each 5 minutes to clear expired items. + */ + @Scheduled(fixedDelay = (USER_EXPIRATION_MINUTES * 60 * 1000)) + public synchronized void clearExpired() + { + logger.debug("Clearing expired user cache..."); + DateTime dateTimeNow = DateTime.now(); + userPermissionsByToken.clearExpired(dateTimeNow); + userInformationByUserId.clearExpired(dateTimeNow); + userStateByToken.clearExpired(dateTimeNow); + for (UserState userState : userStateByToken) { + userState.objectPermissionsByObject.clearExpired(dateTimeNow); + } + resourceById.clearExpired(dateTimeNow); + reservationRequestById.clearExpired(dateTimeNow); + reservationById.clearExpired(dateTimeNow); + executableById.clearExpired(dateTimeNow); + resourcesUtilizationByToken.clearExpired(dateTimeNow); + resourceIdsWithPublicCalendarByUriKey.clearExpired(dateTimeNow); + } + + /** + * @param executableId to be removed from the {@link #executableById} + */ + public synchronized void clearExecutable(String executableId) + { + executableById.remove(executableId); + } + + /** + * @param securityToken to be removed from the {@link #userPermissionsByToken} + */ + public synchronized void clearUserPermissions(SecurityToken securityToken) + { + userPermissionsByToken.remove(securityToken); + } + + /** + * @param securityToken + * @return list of {@link SystemPermission} that requesting user has + */ + public synchronized List getSystemPermissions(SecurityToken securityToken) + { + List userPermissions = userPermissionsByToken.get(securityToken); + if (userPermissions != null) { + return userPermissions; + } + userPermissions = Stream.of(SystemPermission.values()).filter(permission -> + authorizationService.hasSystemPermission(securityToken, permission) + ).collect(Collectors.toList()); + userPermissionsByToken.put(securityToken, userPermissions); + return userPermissions; + } + + /** + * @param securityToken to be used for fetching the {@link UserInformation} + * @param userId user-id of the requested user + * @return {@link UserInformation} for given {@code userId} + */ + public synchronized UserInformation getUserInformation(SecurityToken securityToken, String userId) + { + if (userId == null) { + return null; + } + UserInformation userInformation = userInformationByUserId.get(userId); + if (userInformation != null) { + return userInformation; + } + try { + ListResponse response = authorizationService.listUsers( + new UserListRequest(securityToken, userId)); + if (response.getCount() == 0) { + throw new ControllerReportSet.UserNotExistsException(userId); + } + userInformation = response.getItem(0); + } + catch (ControllerReportSet.UserNotExistsException exception) { + logger.warn("User with id '" + userId + "' doesn't exist.", exception); + userInformation = createNotExistingUserInformation(userId); + } + userInformationByUserId.put(userId, userInformation); + return userInformation; + } + + /** + * @param securityToken to be used for fetching the {@link UserInformation}s + * @param userIds user-ids of the requested users + */ + public synchronized void fetchUserInformation(SecurityToken securityToken, Collection userIds) + { + Set missingUserIds = null; + for (String userId : userIds) { + if (!userInformationByUserId.contains(userId)) { + if (missingUserIds == null) { + missingUserIds = new HashSet(); + } + missingUserIds.add(userId); + } + } + if (missingUserIds != null) { + while (missingUserIds.size() > 0) { + try { + ListResponse response = authorizationService.listUsers( + new UserListRequest(securityToken, missingUserIds)); + for (UserInformation userInformation : response.getItems()) { + String userId = userInformation.getUserId(); + userInformationByUserId.put(userId, userInformation); + missingUserIds.remove(userId); + } + if (missingUserIds.size() > 0) { + throw new ControllerReportSet.UserNotExistsException(missingUserIds.iterator().next()); + } + } + catch (ControllerReportSet.UserNotExistsException exception) { + String userId = exception.getUser(); + logger.warn("User with id '" + userId + "' doesn't exist.", exception); + UserInformation userInformation = createNotExistingUserInformation(userId); + userInformationByUserId.put(userId, userInformation); + missingUserIds.remove(userId); + continue; + } + } + } + } + + /** + * @param securityToken to be used for fetching the {@link Group} + * @param groupId group-id of the requested group + * @return {@link Group} for given {@code groupId} + */ + public synchronized Group getGroup(SecurityToken securityToken, String groupId) + { + Group group = groupByGroupId.get(groupId); + if (group == null) { + ListResponse response = authorizationService.listGroups( + new GroupListRequest(securityToken, groupId)); + if (response.getCount() == 0) { + throw new ControllerReportSet.GroupNotExistsException(groupId); + } + group = response.getItem(0); + groupByGroupId.put(groupId, group); + } + return group; + } + + /** + * @param securityToken + * @return {@link UserState} for user with given {@code securityToken} + */ + private synchronized UserState getUserState(SecurityToken securityToken) + { + UserState userState = userStateByToken.get(securityToken); + if (userState == null) { + userState = new UserState(); + userStateByToken.put(securityToken, userState); + } + return userState; + } + + /** + * @param securityToken of the requesting user + * @param objectId of the object + * @return set of {@link ObjectPermission} for requesting user and given {@code objectId} + */ + public synchronized Set getObjectPermissions(SecurityToken securityToken, String objectId) + { + UserState userState = getUserState(securityToken); + Set objectPermissions = userState.objectPermissionsByObject.get(objectId); + if (objectPermissions == null) { + Map permissionsByObject = authorizationService.listObjectPermissions( + new ObjectPermissionListRequest(securityToken, objectId)); + objectPermissions = new HashSet(); + objectPermissions.addAll(permissionsByObject.get(objectId).getObjectPermissions()); + userState.objectPermissionsByObject.put(objectId, objectPermissions); + } + return objectPermissions; + } + + /** + * @param securityToken + * @param reservationRequests + * @return map of {@link ObjectPermission}s by reservation request identifier + */ + public Map> getReservationRequestsPermissions(SecurityToken securityToken, + Collection reservationRequests) + { + Map> permissionsByReservationRequestId = new HashMap>(); + Set reservationRequestIds = new HashSet(); + for (ReservationRequestSummary reservationRequest : reservationRequests) { + String reservationRequestId = reservationRequest.getId(); + Set objectPermissions = + getObjectPermissionsWithoutFetching(securityToken, reservationRequestId); + if (objectPermissions != null) { + permissionsByReservationRequestId.put(reservationRequestId, objectPermissions); + } + else { + reservationRequestIds.add(reservationRequestId); + } + } + if (reservationRequestIds.size() > 0) { + permissionsByReservationRequestId.putAll(fetchObjectPermissions(securityToken, reservationRequestIds)); + } + return permissionsByReservationRequestId; + } + + /** + * @param securityToken of the requesting user + * @param objectId of the object + * @return set of {@link ObjectPermission} for requesting user and given {@code objectId} + * or null if the {@link ObjectPermission}s aren't cached + */ + public synchronized Set getObjectPermissionsWithoutFetching( + SecurityToken securityToken, String objectId) + { + UserState userState = getUserState(securityToken); + return userState.objectPermissionsByObject.get(objectId); + } + + /** + * Fetch {@link ObjectPermission}s for given {@code objectIds}. + * + * @param securityToken + * @param objectIds + * @return fetched {@link ObjectPermission}s by {@code objectIds} + */ + public synchronized Map> fetchObjectPermissions( + SecurityToken securityToken, Set objectIds) + { + Map> result = new HashMap>(); + if (objectIds.isEmpty()) { + return result; + } + UserState userState = getUserState(securityToken); + Map permissionsByObject = + authorizationService.listObjectPermissions(new ObjectPermissionListRequest(securityToken, objectIds)); + for (Map.Entry entry : permissionsByObject.entrySet()) { + String objectId = entry.getKey(); + Set objectPermissions = userState.objectPermissionsByObject.get(objectId); + if (objectPermissions == null) { + objectPermissions = new HashSet(); + userState.objectPermissionsByObject.put(objectId, objectPermissions); + } + objectPermissions.clear(); + objectPermissions.addAll(entry.getValue().getObjectPermissions()); + result.put(objectId, objectPermissions); + } + return result; + } + + /** + * @param securityToken to be used for fetching the {@link ResourceSummary}s + * @param resourceIds resource-ids to be fetched + */ + public synchronized void fetchResourceSummaries(SecurityToken securityToken, Collection resourceIds) + { + Set missingResourceIds = null; + for (String resourceId : resourceIds) { + if (!resourceById.contains(resourceId)) { + if (missingResourceIds == null) { + missingResourceIds = new HashSet(); + } + missingResourceIds.add(resourceId); + } + } + if (missingResourceIds != null) { + ResourceListRequest request = new ResourceListRequest(); + request.setSecurityToken(securityToken); + for (String resourceId : request.getResourceIds()) { + request.addResourceId(resourceId); + } + ListResponse response = resourceService.listResources(request); + for (ResourceSummary resource : response.getItems()) { + String resourceId = resource.getId(); + resourceById.put(resourceId, resource); + missingResourceIds.remove(resourceId); + } + if (missingResourceIds.size() > 0) { + throw new CommonReportSet.ObjectNotExistsException(ResourceSummary.class.getSimpleName(), + missingResourceIds.iterator().next()); + } + } + } + + /** + * @param securityToken + * @param resourceId + * @return {@link ResourceSummary} for given {@code resourceId} + */ + public ResourceSummary getResourceSummary(SecurityToken securityToken, String resourceId) + { + ResourceSummary resourceSummary = resourceById.get(resourceId); + if (resourceSummary == null) { + ResourceListRequest request = new ResourceListRequest(); + request.setSecurityToken(securityToken); + request.addResourceId(resourceId); + ListResponse response = resourceService.listResources(request); + if (response.getItemCount() == 1) { + resourceSummary = response.getItem(0); + resourceById.put(resourceSummary.getId(), resourceSummary); + } + } + if (resourceSummary == null) { + throw new CommonReportSet.ObjectNotExistsException(ResourceSummary.class.getSimpleName(), resourceId); + } + return resourceSummary; + } + + /** + * Load {@link ReservationRequestSummary}s for given {@code reservationRequestIds} to the {@link Cache}. + * + * @param securityToken + * @param reservationRequestIds + */ + public synchronized void fetchReservationRequests(SecurityToken securityToken, Set reservationRequestIds) + { + Set missingReservationRequestIds = null; + for (String reservationRequestId : reservationRequestIds) { + if (!reservationRequestById.contains(reservationRequestId)) { + if (missingReservationRequestIds == null) { + missingReservationRequestIds = new HashSet(); + } + missingReservationRequestIds.add(reservationRequestId); + } + } + if (missingReservationRequestIds != null) { + ReservationRequestListRequest request = new ReservationRequestListRequest(); + request.setSecurityToken(securityToken); + request.setReservationRequestIds(missingReservationRequestIds); + ListResponse response = reservationService.listReservationRequests(request); + for (ReservationRequestSummary reservationRequest : response) { + if (reservationRequest.isAllowCache()) { + reservationRequestById.put(reservationRequest.getId(), reservationRequest); + } + } + } + } + + /** + * Retrieve {@link ReservationRequestSummary} from {@link Cache} or from {@link #reservationService} + * if it doesn't exist in the {@link Cache}. + * + * @param securityToken + * @param reservationRequestId + * @return {@link ReservationRequestSummary} for given {@code reservationRequestId} + */ + public synchronized ReservationRequestSummary getReservationRequestSummary(SecurityToken securityToken, + String reservationRequestId) + { + ReservationRequestSummary reservationRequest = reservationRequestById.get(reservationRequestId); + if (reservationRequest == null) { + reservationRequest = getReservationRequestSummaryNotCached(securityToken, reservationRequestId); + } + return reservationRequest; + } + + /** + * Similar as {@link #getReservationRequestSummary} but the {@link ReservationRequestSummary} is loaded from + * the {@link #reservationService} also when it is in {@link AllocationState#NOT_ALLOCATED} state (to update it). + * + * @param securityToken + * @param reservationRequestId + * @return {@link ReservationRequestSummary} for given {@code reservationRequestId} + */ + public synchronized ReservationRequestSummary getAllocatedReservationRequestSummary(SecurityToken securityToken, + String reservationRequestId) + { + ReservationRequestSummary reservationRequest = reservationRequestById.get(reservationRequestId); + if (reservationRequest == null || reservationRequest.getAllocatedReservationId() == null) { + reservationRequest = getReservationRequestSummaryNotCached(securityToken, reservationRequestId); + } + return reservationRequest; + } + + /** + * @param securityToken + * @param reservationRequestId + * @return {@link ReservationRequestSummary} for given {@code reservationRequestId} + */ + public synchronized ReservationRequestSummary getReservationRequestSummaryNotCached(SecurityToken securityToken, + String reservationRequestId) + { + ReservationRequestListRequest request = new ReservationRequestListRequest(); + request.setSecurityToken(securityToken); + request.addReservationRequestId(reservationRequestId); + ListResponse response = reservationService.listReservationRequests(request); + if (response.getItemCount() > 0) { + ReservationRequestSummary reservationRequest = response.getItem(0); + if (reservationRequest.isAllowCache()) { + reservationRequestById.put(reservationRequest.getId(), reservationRequest); + } + return reservationRequest; + } + throw new ObjectInaccessibleException(reservationRequestId); + } + + /** + * @param securityToken + * @param reservationId + * @return {@link Reservation} for given {@code reservationId} + */ + public synchronized Reservation getReservation(SecurityToken securityToken, String reservationId) + { + Reservation reservation = reservationById.get(reservationId); + if (reservation == null) { + reservation = reservationService.getReservation(securityToken, reservationId); + reservationById.put(reservationId, reservation); + } + return reservation; + } + + /** + * @param securityToken + * @param executable + * @return reservation request id for given {@code executable} + */ + public synchronized String getReservationRequestIdByExecutable(SecurityToken securityToken, Executable executable) + { + Reservation reservation = getReservation(securityToken, executable.getReservationId()); + return reservation.getReservationRequestId(); + } + + /** + * @param securityToken + * @param objectId + * @return reservation request id for given {@code objectId} + */ + public synchronized String getReservationRequestId(SecurityToken securityToken, String objectId) + { + if (objectId.contains(":req:")) { + return objectId; + } + else if (objectId.contains(":rsv:")) { + Reservation reservation = getReservation(securityToken, objectId); + return reservation.getReservationRequestId(); + } + else if (objectId.contains(":exe:")) { + Executable executable = getExecutable(securityToken, objectId); + return getReservationRequestIdByExecutable(securityToken, executable); + } + else { + throw new TodoImplementException(objectId); + } + } + + /** + * @param securityToken + * @param objectId + * @return executable id for given {@code objectId} + */ + public String getExecutableId(SecurityToken securityToken, String objectId) + { + if (objectId.contains(":exe:")) { + return objectId; + } + else { + if (objectId.contains(":req:")) { + ReservationRequestSummary request = getAllocatedReservationRequestSummary(securityToken, objectId); + String reservationId = request.getAllocatedReservationId(); + if (reservationId == null) { + throw new TodoImplementException("Reservation doesn't exist."); + } + objectId = reservationId; + } + if (objectId.contains(":rsv:")) { + Reservation reservation = getReservation(securityToken, objectId); + Executable executable = reservation.getExecutable(); + if (executable == null) { + throw new UnsupportedApiException("Reservation " + objectId + " doesn't have executable."); + } + return executable.getId(); + } + else { + throw new TodoImplementException(objectId); + } + } + } + + /** + * @param securityToken + * @param executableId + * @return {@link Executable} for given {@code executableId} + */ + public synchronized Executable getExecutable(SecurityToken securityToken, String executableId) + { + Executable executable = executableById.get(executableId); + if (executable == null) { + executable = executableService.getExecutable(securityToken, executableId); + executableById.put(executableId, executable); + } + return executable; + } + + /** + * @param securityToken for which the {@link ResourcesUtilization} shall be returned + * @param forceRefresh specifies whether a fresh version should be returned + * @return {@link ResourcesUtilization} for given {@code securityToken} + */ + public ResourcesUtilization getResourcesUtilization(SecurityToken securityToken, boolean forceRefresh) + { + synchronized (resourcesUtilizationByToken) { + ResourcesUtilization resourcesUtilization = resourcesUtilizationByToken.get(securityToken); + if (resourcesUtilization == null || forceRefresh) { + resourcesUtilization = new ResourcesUtilization(securityToken, resourceService, reservationService); + resourcesUtilizationByToken.put(securityToken, resourcesUtilization); + } + return resourcesUtilization; + } + } + + /** + * Returns resource's ID for given uriKey. + * Accessible resource IDs are cached. + * + * @param uriKey + * @return resource's reservations iCalendar text for export + */ + public String getResourceIdWithUriKey(String uriKey) + { + + resourceIdsWithPublicCalendarByUriKey.clearExpired(DateTime.now()); + //Check if resource really exists + if (resourceIdsWithPublicCalendarByUriKey.size() == 0) { + for (ResourceSummary resourceSummary: resourceService.getResourceIdsWithPublicCalendar()) { + resourceIdsWithPublicCalendarByUriKey.put(resourceSummary.getCalendarUriKey(),resourceSummary.getId()); + } + } + if (!resourceIdsWithPublicCalendarByUriKey.contains(uriKey)) { + return null; + } + return resourceIdsWithPublicCalendarByUriKey.get(uriKey); + } + + /** + * @param userId + * @return {@link UserInformation} for not existing user with given {@code userId} + */ + private static UserInformation createNotExistingUserInformation(String userId) + { + UserInformation userInformation = new UserInformation(); + userInformation.setUserId(userId); + userInformation.setFirstName("Non-Existent-User"); + userInformation.setLastName("(" + userId + ")"); + return userInformation; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ObjectInaccessibleException.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ObjectInaccessibleException.java new file mode 100644 index 000000000..384d9b9d7 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ObjectInaccessibleException.java @@ -0,0 +1,21 @@ +package cz.cesnet.shongo.controller.rest; + +/** + * TODO: + * + * @author Martin Srom + */ +public class ObjectInaccessibleException extends RuntimeException +{ + private final String objectId; + + public ObjectInaccessibleException(String objectId) + { + this.objectId = objectId; + } + + public String getObjectId() + { + return objectId; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/AuthController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/AuthController.java deleted file mode 100644 index 246358c4e..000000000 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/AuthController.java +++ /dev/null @@ -1,40 +0,0 @@ -package cz.cesnet.shongo.controller.rest.api; - -import cz.cesnet.shongo.controller.SystemPermission; -import cz.cesnet.shongo.controller.api.SecurityToken; -import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; -import io.swagger.v3.oas.annotations.Operation; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; - -/** - * Rest controller for user authorization endpoints. - * - * @author Filip Karnis - */ -@RestController -@RequestMapping("/api/v1") -public class AuthController { - - private final AuthorizationService authorizationService; - - public AuthController(@Autowired AuthorizationService reservationService) { - this.authorizationService = reservationService; - } - - @Operation(summary = "Returns user's system permissions.") - @GetMapping("/permissions") - public List getPermissions( - @RequestAttribute(TOKEN) SecurityToken securityToken) - { - return Stream.of(SystemPermission.values()).filter(permission -> - authorizationService.hasSystemPermission(securityToken, permission) - ).collect(Collectors.toList()); - } -} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/UnsupportedApiException.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/UnsupportedApiException.java new file mode 100644 index 000000000..fc4fa3198 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/UnsupportedApiException.java @@ -0,0 +1,30 @@ +package cz.cesnet.shongo.controller.rest.models; + +/** + * Unsupported API data. + * + * @author Martin Srom + */ +public class UnsupportedApiException extends RuntimeException +{ + public UnsupportedApiException(Object object) + { + super(object.getClass().getCanonicalName()); + } + + public UnsupportedApiException(String message) + { + super(message); + } + + public UnsupportedApiException(String message, Object... arguments) + { + super(String.format(message, arguments)); + } + + @Override + public String getMessage() + { + return String.format("Not supported: %s", super.getMessage()); + } +} diff --git a/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml b/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml index e9b62eca1..3648f4aab 100644 --- a/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml +++ b/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml @@ -32,4 +32,22 @@ + + + + + + + + + + + + + + + + + + From 4db70e14f1a0083a747e09bd52e16ad52f829353 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Tue, 8 Mar 2022 02:25:49 +0100 Subject: [PATCH 007/134] Added users and groups endpoints --- .../controller/rest/api/UserController.java | 153 ++++++++++++++++++ .../rest/models/users/SettingsModel.java | 31 ++++ 2 files changed, 184 insertions(+) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/UserController.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/users/SettingsModel.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/UserController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/UserController.java new file mode 100644 index 000000000..896c77bf6 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/UserController.java @@ -0,0 +1,153 @@ +package cz.cesnet.shongo.controller.rest.api; + +import cz.cesnet.shongo.api.UserInformation; +import cz.cesnet.shongo.controller.SystemPermission; +import cz.cesnet.shongo.controller.api.Group; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.UserSettings; +import cz.cesnet.shongo.controller.api.request.GroupListRequest; +import cz.cesnet.shongo.controller.api.request.ListResponse; +import cz.cesnet.shongo.controller.api.request.UserListRequest; +import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; +import cz.cesnet.shongo.controller.rest.Cache; +import cz.cesnet.shongo.controller.rest.models.users.SettingsModel; +import io.swagger.v3.oas.annotations.Operation; +import org.joda.time.DateTimeZone; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Locale; + +import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; + +/** + * Rest controller for user endpoints. + * + * @author Filip Karnis + */ +@RestController +@RequestMapping("/api/v1") +public class UserController { + + private final AuthorizationService authorizationService; + private final Cache cache; + + public UserController(@Autowired AuthorizationService reservationService, @Autowired Cache cache) { + this.authorizationService = reservationService; + this.cache = cache; + } + + /** + * Handle request for list of {@link UserInformation}s which contains given {@code filter} text in any field. + * + * @param filter + * @return list of {@link UserInformation}s + */ + @Operation(summary = "Lists users.") + @GetMapping("/users") + public ListResponse getUsers( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @RequestParam(value = "filter", required = false) String filter, + @RequestParam(value = "groupId", required = false) String groupId) + { + UserListRequest request = new UserListRequest(); + request.setSecurityToken(securityToken); + request.setSearch(filter); + if (groupId != null) { + request.addGroupId(groupId); + } + + return authorizationService.listUsers(request); + } + + /** + * Handle request for {@link UserInformation} by given {@code userId}. + * + * @param userId + * @return {@link UserInformation} + */ + @Operation(summary = "Returns information about user.") + @GetMapping("/users/{userId:.+}") + public UserInformation getUser( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String userId) + { + return cache.getUserInformation(securityToken, userId); + } + + /** + * Returns user settings with user's system permissions by given {@link SecurityToken}. + * + * @return {@link SettingsModel} + */ + @Operation(summary = "Returns user's settings.") + @GetMapping("/settings") + public SettingsModel getUserSettings(@RequestAttribute(TOKEN) SecurityToken securityToken) + { + UserSettings settings = authorizationService.getUserSettings(securityToken); + List permissions = cache.getSystemPermissions(securityToken); + return new SettingsModel(settings, permissions); + } + + /** + * Handle request for updating user's settings. + * + * @param newSettings new settings of user + */ + @Operation(summary = "Updates user's settings.") + @PutMapping("/settings") + public SettingsModel updateUserSettings( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @RequestBody UserSettings newSettings) + { + UserSettings userSettings = authorizationService.getUserSettings(securityToken); + + boolean useWebService = newSettings.isUseWebService(); + userSettings.setUseWebService(newSettings.isUseWebService()); + if (!useWebService) { + userSettings.setLocale(newSettings.getLocale()); + userSettings.setHomeTimeZone(newSettings.getHomeTimeZone()); + } + userSettings.setCurrentTimeZone(newSettings.getCurrentTimeZone()); + + authorizationService.updateUserSettings(securityToken, userSettings); + cache.clearUserPermissions(securityToken); + + return getUserSettings(securityToken); + } + + /** + * Handle request for list of {@link Group}s which contains given {@code filter} text in any field. + * + * @param filter + * @return list of {@link Group}s + */ + @Operation(summary = "Lists groups.") + @GetMapping("/groups") + public ListResponse getGroups( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @RequestParam(value = "filter", required = false) String filter) + { + GroupListRequest request = new GroupListRequest(); + request.setSecurityToken(securityToken); + request.setSearch(filter); + + return authorizationService.listGroups(request); + } + + /** + * Handle request for {@link Group} by given {@code groupId}. + * + * @param groupId + * @return {@link Group} + */ + @Operation(summary = "Returns information about group.") + @GetMapping("/groups/{groupId:.+}") + public Group getGroup( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String groupId) + { + return cache.getGroup(securityToken, groupId); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/users/SettingsModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/users/SettingsModel.java new file mode 100644 index 000000000..e5df519df --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/users/SettingsModel.java @@ -0,0 +1,31 @@ +package cz.cesnet.shongo.controller.rest.models.users; + +import cz.cesnet.shongo.controller.SystemPermission; +import cz.cesnet.shongo.controller.api.UserSettings; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.joda.time.DateTimeZone; + +import java.util.List; +import java.util.Locale; + +@Data +@NoArgsConstructor +public class SettingsModel { + + private Boolean useWebService; + private Locale locale; + private DateTimeZone homeTimeZone; + private DateTimeZone currentTimeZone; + private Boolean administrationMode; + private List permissions; + + public SettingsModel(UserSettings userSettings, List permissions) { + this.useWebService = userSettings.isUseWebService(); + this.locale = userSettings.getLocale(); + this.homeTimeZone = userSettings.getHomeTimeZone(); + this.currentTimeZone = userSettings.getCurrentTimeZone(); + this.administrationMode = userSettings.getAdministrationMode(); + this.permissions = permissions; + } +} From 7f2438c82cb43f129e4360e09a07de1866529ab5 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 10 Mar 2022 02:12:34 +0100 Subject: [PATCH 008/134] Added room endpoints --- .../controller/rest/api/RoomController.java | 87 +++++++++++ .../rest/models/room/RoomAuthorizedData.java | 30 ++++ .../rest/models/room/RoomModel.java | 65 +++++++++ .../rest/models/room/RoomState.java | 137 ++++++++++++++++++ .../controller/rest/models/room/RoomType.java | 74 ++++++++++ 5 files changed, 393 insertions(+) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RoomController.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomAuthorizedData.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomState.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomType.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RoomController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RoomController.java new file mode 100644 index 000000000..40b3855fa --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RoomController.java @@ -0,0 +1,87 @@ +package cz.cesnet.shongo.controller.rest.api; + +import cz.cesnet.shongo.controller.api.AbstractRoomExecutable; +import cz.cesnet.shongo.controller.api.ExecutableSummary; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.request.ExecutableListRequest; +import cz.cesnet.shongo.controller.api.request.ListResponse; +import cz.cesnet.shongo.controller.api.rpc.ExecutableService; +import cz.cesnet.shongo.controller.rest.Cache; +import cz.cesnet.shongo.controller.rest.models.room.RoomAuthorizedData; +import cz.cesnet.shongo.controller.rest.models.room.RoomModel; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.stream.Collectors; + +import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; + +/** + * Rest controller for room endpoints. + * + * @author Filip Karnis + */ +@RestController +@RequestMapping("/api/v1/rooms") +public class RoomController { + + private final Cache cache; + private final ExecutableService executableService; + + public RoomController(@Autowired Cache cache, @Autowired ExecutableService executableService) { + this.cache = cache; + this.executableService = executableService; + } + + @Operation(summary = "Lists rooms (executables).") + @GetMapping("") + public ListResponse listRooms( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @RequestParam(value = "start", required = false) Integer start, + @RequestParam(value = "count", required = false) Integer count, + @RequestParam(value = "sort", required = false, defaultValue = "SLOT") ExecutableListRequest.Sort sort, + @RequestParam(value = "sort-desc", required = false, defaultValue = "true") boolean sortDescending, + @RequestParam(value = "room-id", required = false) String roomId, + @RequestParam(value = "participant-user-id", required = false) String participantUserId) + { + ExecutableListRequest request = new ExecutableListRequest(); + request.setSecurityToken(securityToken); + request.setStart(start); + request.setCount(count); + request.setSort(sort); + request.setSortDescending(sortDescending); + if (roomId != null) { + request.addType(ExecutableSummary.Type.USED_ROOM); + request.setRoomId(roomId); + } + else { + if (participantUserId != null) { + request.setRoomLicenseCount(ExecutableListRequest.FILTER_NON_ZERO); + request.addType(ExecutableSummary.Type.USED_ROOM); + } + request.addType(ExecutableSummary.Type.ROOM); + } + request.setParticipantUserId(participantUserId); + + ListResponse response = executableService.listExecutables(request); + ListResponse listResponse = new ListResponse<>(); + listResponse.addAll(response.getItems().stream().map(RoomModel::new).collect(Collectors.toList())); + listResponse.setStart(response.getStart()); + listResponse.setCount(response.getCount()); + return listResponse; + } + + @Operation(summary = "Gets room's (executable's) authorized data.") + @GetMapping("/{objectId}") + public RoomAuthorizedData getRoom( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable("objectId") String objectId) + { + String roomId = cache.getExecutableId(securityToken, objectId); + AbstractRoomExecutable roomExecutable = + (AbstractRoomExecutable) executableService.getExecutable(securityToken, roomId); + + return new RoomAuthorizedData(roomExecutable); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomAuthorizedData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomAuthorizedData.java new file mode 100644 index 000000000..e08fe281b --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomAuthorizedData.java @@ -0,0 +1,30 @@ +package cz.cesnet.shongo.controller.rest.models.room; + +import cz.cesnet.shongo.api.Alias; +import cz.cesnet.shongo.controller.api.AbstractRoomExecutable; +import lombok.Data; + +import java.util.List; + +/** + * Represents authorized data for room (executable). + * + * @author Filip Karnis + */ +@Data +public class RoomAuthorizedData { + + private String pin; + private String adminPin; + private String guestPin; + private Boolean allowGuests; + private List aliases; + + public RoomAuthorizedData(AbstractRoomExecutable roomExecutable) { + pin = roomExecutable.getPin(); + adminPin = roomExecutable.getAdminPin(); + guestPin = roomExecutable.getGuestPin(); + allowGuests = roomExecutable.getAllowGuests(); + aliases = roomExecutable.getAliases(); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomModel.java new file mode 100644 index 000000000..32dd27a0c --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomModel.java @@ -0,0 +1,65 @@ +package cz.cesnet.shongo.controller.rest.models.room; + +import cz.cesnet.shongo.controller.api.ExecutableSummary; +import cz.cesnet.shongo.controller.rest.models.TechnologyModel; +import cz.cesnet.shongo.controller.rest.models.TimeInterval; +import lombok.Data; +import org.joda.time.Interval; + +/** + * Represents room (executable). + * + * @author Filip Karnis + */ +@Data +public class RoomModel { + + private String id; + private ExecutableSummary.Type type; + private TimeInterval slot; + private TimeInterval earliestSlot; + private String description; + private String name; + private TechnologyModel technology; + private RoomState state; + private boolean isDeprecated; + private int licenceCount; + private int usageCount; + + public RoomModel(ExecutableSummary summary) { + this.id = summary.getId(); + this.type = summary.getType(); + this.name = summary.getRoomName(); + this.description = summary.getRoomDescription(); + this.technology = TechnologyModel.find(summary.getRoomTechnologies()); + this.usageCount = summary.getRoomUsageCount(); + + this.state = RoomState.fromRoomState( + summary.getState(), summary.getRoomLicenseCount(), + summary.getRoomUsageState()); + + Interval slot = summary.getRoomUsageSlot(); + if (slot == null) { + slot = summary.getSlot(); + } + this.slot = new TimeInterval(slot); + + boolean isDeprecated; + switch (state) { + case STARTED: + case STARTED_AVAILABLE: + isDeprecated = false; + break; + default: + isDeprecated = slot.getEnd().isBeforeNow(); + break; + } + this.isDeprecated = isDeprecated; + + Integer licenseCount = summary.getRoomUsageLicenseCount(); + if (licenseCount == null) { + licenseCount = summary.getRoomLicenseCount(); + } + this.licenceCount = licenseCount; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomState.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomState.java new file mode 100644 index 000000000..41b157b2f --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomState.java @@ -0,0 +1,137 @@ +package cz.cesnet.shongo.controller.rest.models.room; + +import cz.cesnet.shongo.TodoImplementException; +import cz.cesnet.shongo.controller.api.ExecutableState; +import org.springframework.context.MessageSource; + +import java.util.Locale; + +/** + * Represents a room state. + * + * @author Martin Srom + */ +public enum RoomState +{ + /** + * Room is not started. + */ + NOT_STARTED(false, false), + + /** + * Room is started. + */ + STARTED(true, true), + + /** + * Room is not available for participants to join. + */ + STARTED_NOT_AVAILABLE(true, false), + + /** + * Room is available for participants to join. + */ + STARTED_AVAILABLE(true, true), + + /** + * Room has been stopped. + */ + STOPPED(false, false), + + /** + * Room is not available for participants to join due to error. + */ + FAILED(false, false); + + /** + * Specifies whether this state represents an started room. + */ + private final boolean isStarted; + + /** + * Specifies whether this state represents an available for participants to join room. + */ + private final boolean isAvailable; + + /** + * Constructor. + * + * @param isAvailable sets the {@link #isAvailable} + */ + RoomState(boolean isStarted, boolean isAvailable) + { + this.isStarted = isStarted; + this.isAvailable = isAvailable; + } + + /** + * @return {@link #isStarted} + */ + public boolean isStarted() + { + return isStarted; + } + + /** + * @return {@link #isAvailable} + */ + public boolean isAvailable() + { + return isAvailable; + } + + public String getMessage(MessageSource messageSource, Locale locale, RoomType roomType) + { + return messageSource.getMessage( + "views.executable.roomState." + roomType + "." + this, null, locale); + } + + public String getHelp(MessageSource messageSource, Locale locale, RoomType roomType) + { + return messageSource.getMessage( + "views.executable.roomStateHelp." + roomType + "." + this, null, locale); + } + + /** + * @param roomState + * @param roomLicenseCount + * @param roomUsageState + * @return {@link RoomState} + */ + public static RoomState fromRoomState(ExecutableState roomState, Integer roomLicenseCount, ExecutableState roomUsageState) + { + switch (roomState) { + case NOT_STARTED: + return NOT_STARTED; + case STARTED: + if (roomUsageState != null) { + // Permanent room with earliest usage + return roomUsageState.isAvailable() ? STARTED_AVAILABLE : STARTED_NOT_AVAILABLE; + } + else if (roomLicenseCount == null || roomLicenseCount == 0) { + // Permanent room without earliest usage + return STARTED_NOT_AVAILABLE; + } + else { + // Other room + return STARTED; + } + case STOPPED: + case STOPPING_FAILED: + return RoomState.STOPPED; + case STARTING_FAILED: + return RoomState.FAILED; + default: + throw new TodoImplementException(roomState); + } + } + + /** + * @param roomState + * @return {@link RoomState} + */ + public static RoomState fromRoomState(ExecutableState roomState) + { + return fromRoomState(roomState, 1, null); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomType.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomType.java new file mode 100644 index 000000000..62fe44036 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomType.java @@ -0,0 +1,74 @@ +package cz.cesnet.shongo.controller.rest.models.room; + +import cz.cesnet.shongo.TodoImplementException; +import cz.cesnet.shongo.controller.api.AbstractRoomExecutable; +import cz.cesnet.shongo.controller.api.ExecutableSummary; +import cz.cesnet.shongo.controller.api.RoomExecutable; +import cz.cesnet.shongo.controller.api.UsedRoomExecutable; + +/** + * Type of {@link RoomModel}. + * + * @author Martin Srom + */ +public enum RoomType +{ + /** + * Ad-hoc room. + */ + ADHOC_ROOM, + + /** + * Permanent room. + */ + PERMANENT_ROOM, + + /** + * Used room. + */ + USED_ROOM; + + /** + * @param executableSummary + * @return {@link RoomType} from given {@code executableSummary} + */ + public static RoomType fromExecutableSummary(ExecutableSummary executableSummary) + { + if (executableSummary.getType().equals(ExecutableSummary.Type.ROOM)) { + if (executableSummary.getRoomLicenseCount() == 0) { + return PERMANENT_ROOM; + } + else { + return ADHOC_ROOM; + } + } + else if (executableSummary.getType().equals(ExecutableSummary.Type.USED_ROOM)) { + return USED_ROOM; + } + else { + throw new TodoImplementException(executableSummary.getType()); + } + } + + /** + * @param roomExecutable + * @return {@link RoomType} from given {@code roomExecutable} + */ + public static RoomType fromRoomExecutable(AbstractRoomExecutable roomExecutable) + { + if (roomExecutable instanceof RoomExecutable) { + if (roomExecutable.getLicenseCount() == 0) { + return PERMANENT_ROOM; + } + else { + return ADHOC_ROOM; + } + } + else if (roomExecutable instanceof UsedRoomExecutable) { + return USED_ROOM; + } + else { + throw new TodoImplementException(roomExecutable.getClass()); + } + } +} From 7a2e1ced69ef720e6622ca73a3c49e09f8e3e1f1 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sat, 12 Mar 2022 17:07:50 +0100 Subject: [PATCH 009/134] Added additional reservation request endpoints --- .../cesnet/shongo/controller/rest/Cache.java | 37 ++-- .../api/ReservationRequestController.java | 134 ++++++++++-- .../controller/rest/models/TimeInterval.java | 23 ++ .../ReservationRequestDetailModel.java | 45 ++++ .../ReservationRequestHistoryModel.java | 32 +++ .../ReservationRequestModel.java | 74 +++++++ .../ReservationRequestState.java | 201 ++++++++++++++++++ .../reservationrequest/RoomCapacityModel.java | 22 ++ .../reservationrequest/VirtualRoomModel.java | 27 +++ 9 files changed, 561 insertions(+), 34 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RoomCapacityModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/VirtualRoomModel.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java index 90e90c5b5..bb55dcee8 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java @@ -609,27 +609,28 @@ public String getExecutableId(SecurityToken securityToken, String objectId) if (objectId.contains(":exe:")) { return objectId; } - else { - if (objectId.contains(":req:")) { - ReservationRequestSummary request = getAllocatedReservationRequestSummary(securityToken, objectId); - String reservationId = request.getAllocatedReservationId(); - if (reservationId == null) { - throw new TodoImplementException("Reservation doesn't exist."); - } - objectId = reservationId; - } - if (objectId.contains(":rsv:")) { - Reservation reservation = getReservation(securityToken, objectId); - Executable executable = reservation.getExecutable(); - if (executable == null) { - throw new UnsupportedApiException("Reservation " + objectId + " doesn't have executable."); - } - return executable.getId(); + + if (objectId.contains(":req:")) { + ReservationRequestSummary request = getAllocatedReservationRequestSummary(securityToken, objectId); + String reservationId = request.getAllocatedReservationId(); + if (reservationId == null) { + logger.info("Reservation doesn't exist."); + return null; } - else { - throw new TodoImplementException(objectId); + objectId = reservationId; + } + + if (objectId.contains(":rsv:")) { + Reservation reservation = getReservation(securityToken, objectId); + Executable executable = reservation.getExecutable(); + if (executable == null) { + logger.info("Reservation " + objectId + " doesn't have executable."); + return null; } + return executable.getId(); } + + throw new TodoImplementException(objectId); } /** diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java index 8f4647b17..1e8a2662b 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java @@ -2,46 +2,66 @@ import cz.cesnet.shongo.Temporal; import cz.cesnet.shongo.api.UserInformation; -import cz.cesnet.shongo.controller.api.ReservationRequestSummary; -import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.ObjectPermission; +import cz.cesnet.shongo.controller.api.*; import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.request.ReservationRequestListRequest; +import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.api.rpc.ReservationService; -import cz.cesnet.shongo.controller.rest.models.reservationrequest.SpecificationType; +import cz.cesnet.shongo.controller.rest.Cache; +import cz.cesnet.shongo.controller.rest.models.reservationrequest.*; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; +import cz.cesnet.shongo.controller.rest.models.room.RoomAuthorizedData; import io.swagger.v3.oas.annotations.Operation; import org.joda.time.Interval; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.joda.time.DateTime; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; /** + * Rest controller for reservation request endpoints. + * * @author Filip Karnis */ @RestController @RequestMapping("/api/v1/reservation_requests") public class ReservationRequestController { + private final Cache cache; private final ReservationService reservationService; + private final ExecutableService executableService; - public ReservationRequestController(@Autowired ReservationService reservationService) { + public ReservationRequestController( + @Autowired Cache cache, + @Autowired ReservationService reservationService, + @Autowired ExecutableService executableService) + { + this.cache = cache; this.reservationService = reservationService; + this.executableService = executableService; } - @Operation(summary = "Lists reservation requests") - @GetMapping("") - ListResponse listRequests( + @Operation(summary = "Lists reservation requests.") + @GetMapping() + ListResponse listRequests( @RequestAttribute(TOKEN) SecurityToken securityToken, @RequestParam(value = "start", required = false) Integer start, @RequestParam(value = "count", required = false) Integer count, @RequestParam(value = "sort", required = false, defaultValue = "DATETIME") ReservationRequestListRequest.Sort sort, @RequestParam(value = "sort_desc", required = false, defaultValue = "true") boolean sortDescending, + @RequestParam(value = "allocation_state", required = false) AllocationState allocationState, + @RequestParam(value = "parentRequestId", required = false) String permanentRoomId, @RequestParam(value = "specification_type", required = false) Set specificationTypes, - @RequestParam(value = "specification_technology", required = false) TechnologyModel specificationTechnology, + @RequestParam(value = "technology", required = false) TechnologyModel technology, @RequestParam(value = "interval_from", required = false) DateTime intervalFrom, @RequestParam(value = "interval_to", required = false) DateTime intervalTo, @RequestParam(value = "user_id", required = false) String userId, @@ -55,6 +75,14 @@ ListResponse listRequests( request.setCount(count); request.setSort(sort); request.setSortDescending(sortDescending); + request.setAllocationState(allocationState); + request.setParticipantUserId(participantUserId); + request.setSearch(search); + + if (permanentRoomId != null) { + request.setReusedReservationRequestId(permanentRoomId); + specificationTypes.add(SpecificationType.PERMANENT_ROOM_CAPACITY); + } if (specificationTypes != null && !specificationTypes.isEmpty()) { if (specificationTypes.contains(SpecificationType.ADHOC_ROOM)) { @@ -70,8 +98,8 @@ ListResponse listRequests( request.addSpecificationType(ReservationRequestSummary.SpecificationType.RESOURCE); } } - if (specificationTechnology != null) { - request.setSpecificationTechnologies(specificationTechnology.getTechnologies()); + if (technology != null) { + request.setSpecificationTechnologies(technology.getTechnologies()); } if (intervalFrom != null || intervalTo != null) { if (intervalFrom == null) { @@ -87,13 +115,87 @@ ListResponse listRequests( if (userId != null && UserInformation.isLocal(userId)) { request.setUserId(userId); } - if (participantUserId != null) { - request.setParticipantUserId(participantUserId); - } - if (search != null) { - request.setSearch(search); + + ListResponse response = reservationService.listReservationRequests(request); + Map> permissionsByReservationRequestId = + cache.getReservationRequestsPermissions(securityToken, response.getItems()); + + ListResponse listResponse = new ListResponse<>(); + listResponse.addAll(response.getItems().stream().map(item -> { + UserInformation user = cache.getUserInformation(securityToken, item.getUserId()); + return new ReservationRequestModel(item, permissionsByReservationRequestId, user); + }).collect(Collectors.toList())); + listResponse.setStart(response.getStart()); + listResponse.setCount(response.getCount()); + return listResponse; + } + + @Operation(summary = "Returns reservation request.") + @GetMapping("/{id:.+}") + ReservationRequestDetailModel getRequest( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id) + { + ReservationRequestSummary summary = cache.getReservationRequestSummary(securityToken, id); + + List history = + reservationService.getReservationRequestHistory(securityToken, id) + .stream().map(ReservationRequestHistoryModel::new).collect(Collectors.toList()); + + String roomId = cache.getExecutableId(securityToken, id); + RoomAuthorizedData authorizedData = null; + if (roomId != null) { + AbstractRoomExecutable roomExecutable = + (AbstractRoomExecutable) executableService.getExecutable(securityToken, roomId); + authorizedData = new RoomAuthorizedData(roomExecutable); } - return reservationService.listReservationRequests(request); + List requests = new ArrayList<>(); + requests.add(summary); + Map> permissionsByReservationRequestId = + cache.getReservationRequestsPermissions(securityToken, requests); + + UserInformation ownerInformation = cache.getUserInformation(securityToken, summary.getUserId()); + + return new ReservationRequestDetailModel( + summary, permissionsByReservationRequestId, ownerInformation, authorizedData, history + ); + } + + @Operation(summary = "Accepts reservation request.") + @PostMapping("/{id:.+}/accept") + void acceptRequest( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id) + { + reservationService.confirmReservationRequest(securityToken, id, true); + } + + @Operation(summary = "Rejects reservation request.") + @PostMapping("/{id:.+}/reject") + void rejectRequest( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id, + @RequestParam(required = false) String reason) + { + reservationService.denyReservationRequest(securityToken, id, reason); + } + + @Operation(summary = "Deletes reservation request.") + @DeleteMapping("/{id:.+}") + void deleteRequest( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id) + { + reservationService.deleteReservationRequest(securityToken, id); + } + + @Operation(summary = "Reverts reservation request modifications.") + @PostMapping("/{id:.+}/revert") + void revertRequest( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id) + { + reservationService.revertReservationRequest(securityToken, id); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java new file mode 100644 index 000000000..8e61a0481 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java @@ -0,0 +1,23 @@ +package cz.cesnet.shongo.controller.rest.models; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.joda.time.DateTime; +import org.joda.time.Interval; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; + +@Data +@NoArgsConstructor +public class TimeInterval { + + public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + + private DateTime start; + private DateTime end; + + public TimeInterval(Interval interval) { + this.start = interval.getStart(); + this.end = interval.getEnd(); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java new file mode 100644 index 000000000..927d66768 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java @@ -0,0 +1,45 @@ +package cz.cesnet.shongo.controller.rest.models.reservationrequest; + +import cz.cesnet.shongo.api.UserInformation; +import cz.cesnet.shongo.controller.ObjectPermission; +import cz.cesnet.shongo.controller.api.AllocationState; +import cz.cesnet.shongo.controller.api.ExecutableState; +import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.rest.models.room.RoomAuthorizedData; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Represents reservation request's detail info. + * + * @author Filip Karnis + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class ReservationRequestDetailModel extends ReservationRequestModel { + + private AllocationState allocationState; + private ExecutableState executableState; + private Boolean notifyParticipants; // TODO + private RoomAuthorizedData authorizedData; + private List history; + + public ReservationRequestDetailModel( + ReservationRequestSummary summary, + Map> permissionsByReservationRequestId, + UserInformation ownerInformation, + RoomAuthorizedData authorizedData, + List history) + { + super(summary, permissionsByReservationRequestId, ownerInformation); + + this.allocationState = summary.getAllocationState(); + this.executableState = summary.getExecutableState(); + this.authorizedData = authorizedData; + this.history = history; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java new file mode 100644 index 000000000..5ffd1bc4d --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java @@ -0,0 +1,32 @@ +package cz.cesnet.shongo.controller.rest.models.reservationrequest; + +import cz.cesnet.shongo.controller.api.AllocationState; +import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.api.ReservationRequestType; +import lombok.Data; +import org.joda.time.DateTime; + +/** + * Represents reservation request's history. + * + * @author Filip Karnis + */ +@Data +public class ReservationRequestHistoryModel { + + private String id; + private DateTime createdAt; + private String createdBy; + private ReservationRequestType type; + private AllocationState allocationState; + private ReservationRequestState state; + + public ReservationRequestHistoryModel(ReservationRequestSummary summary) { + this.id = summary.getId(); + this.createdAt = summary.getDateTime(); + this.createdBy = summary.getUserId(); + this.type = summary.getType(); + this.allocationState = summary.getAllocationState(); + this.state = ReservationRequestState.fromApi(summary); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java new file mode 100644 index 000000000..3e6947661 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java @@ -0,0 +1,74 @@ +package cz.cesnet.shongo.controller.rest.models.reservationrequest; + +import cz.cesnet.shongo.api.UserInformation; +import cz.cesnet.shongo.controller.ObjectPermission; +import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.api.ReservationRequestType; +import cz.cesnet.shongo.controller.rest.models.TimeInterval; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.joda.time.DateTime; + +import java.util.Map; +import java.util.Set; + +/** + * Represents reservation request. + * + * @author Filip Karnis + */ +@Data +@NoArgsConstructor +public class ReservationRequestModel { + + private String id; + private String description; + private DateTime createdAt; + private String parentRequestId; + private ReservationRequestState state; + private Boolean isWritable; + private Boolean isProvidable; + private String ownerName; + private String ownerEmail; + private TimeInterval slot; + private Boolean isDeprecated; + private ReservationRequestType type; + private VirtualRoomModel virtualRoomData; + private RoomCapacityModel roomCapacityData; + private String lastReservationId; + private Integer futureSlotCount; + + public ReservationRequestModel( + ReservationRequestSummary summary, + Map> permissionsByReservationRequestId, + UserInformation ownerInformation) + { + this.id = summary.getId(); + this.description = summary.getDescription(); + this.createdAt = summary.getDateTime(); + this.parentRequestId = summary.getParentReservationRequestId(); + this.state = ReservationRequestState.fromApi(summary); + this.ownerName = ownerInformation.getFullName(); + this.ownerEmail = ownerInformation.getEmail(); + this.slot = new TimeInterval(summary.getEarliestSlot()); + this.type = summary.getType(); + this.virtualRoomData = new VirtualRoomModel(summary); + this.roomCapacityData = new RoomCapacityModel(summary); + this.lastReservationId = summary.getLastReservationId(); + this.futureSlotCount = summary.getFutureSlotCount(); + + Set objectPermissions = permissionsByReservationRequestId.get(id); + this.isWritable = objectPermissions.contains(ObjectPermission.WRITE); + this.isProvidable = objectPermissions.contains(ObjectPermission.PROVIDE_RESERVATION_REQUEST); + + switch (state != null ? state : ReservationRequestState.ALLOCATED) { + case ALLOCATED_STARTED: + case ALLOCATED_STARTED_AVAILABLE: + this.isDeprecated = false; + break; + default: + this.isDeprecated = slot != null && slot.getEnd().isBeforeNow(); + break; + } + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java new file mode 100644 index 000000000..df11175bc --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java @@ -0,0 +1,201 @@ +package cz.cesnet.shongo.controller.rest.models.reservationrequest; + +import cz.cesnet.shongo.controller.api.AllocationState; +import cz.cesnet.shongo.controller.api.ExecutableState; +import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.api.ReservationRequestType; +import org.springframework.context.MessageSource; + +import java.util.Locale; + +/** + * Represents a reservation request state. + * + * @author Martin Srom + */ +public enum ReservationRequestState +{ + /** + * Reservation request has not been confirmed yet, nor allocated by the scheduler yet. + */ + CONFIRM_AWAITING(false), + + /** + * Reservation request has not been allocated by the scheduler yet. + */ + NOT_ALLOCATED(false), + + /** + * Reservation request is allocated by the scheduler but the allocated executable has not been started yet. + */ + ALLOCATED(true), + + /** + * Reservation request is allocated by the scheduler and the allocated executable is started. + */ + ALLOCATED_STARTED(true), + + /** + * Reservation request is allocated by the scheduler and the allocated room is started but not available for participants to join. + */ + ALLOCATED_STARTED_NOT_AVAILABLE(true), + + /** + * Reservation request is allocated by the scheduler and the allocated room is started and available for participants to join. + */ + ALLOCATED_STARTED_AVAILABLE(true), + + /** + * Reservation request is allocated by the scheduler and the allocated executable has been started and stopped. + */ + ALLOCATED_FINISHED(true), + + /** + * Reservation request cannot be allocated by the scheduler or the starting of executable failed. + */ + FAILED(false), + + /** + * The reservation request has been denied. It won't be allocated. + */ + DENIED(false), + + /** + * Modification of reservation request cannot be allocated by the scheduler + * but some previous version of reservation request has been allocated and started. + */ + MODIFICATION_FAILED(false); + + /** + * Specifies whether reservation request is allocated. + */ + private final boolean allocated; + + /** + * Constructor. + * + * @param allocated sets the {@link #allocated} + */ + private ReservationRequestState(boolean allocated) + { + this.allocated = allocated; + } + + /** + * @return {@link #allocated} + */ + public boolean isAllocated() + { + return allocated; + } + + public String getMessage(MessageSource messageSource, Locale locale, SpecificationType specificationType) + { + return messageSource.getMessage( + "views.reservationRequest.state." + specificationType + "." + this, null, locale); + } + + public String getHelp(MessageSource messageSource, Locale locale, SpecificationType specificationType, + String reservationId) + { + String helpMessage = "views.reservationRequest.stateHelp." + specificationType + "." + this; + if (this.equals(FAILED) && reservationId != null) { + return messageSource.getMessage(helpMessage + ".hasReservation", null, locale); + } + return messageSource.getMessage(helpMessage, null, locale); + } + + public String getHelp(MessageSource messageSource, Locale locale, SpecificationType specificationType) + { + String helpMessageCode = "views.reservationRequest.stateHelp." + specificationType + "." + this; + return messageSource.getMessage(helpMessageCode, null, locale); + } + + public static ReservationRequestState fromApi(ReservationRequestSummary reservationRequest) + { + return fromApi(reservationRequest.getAllocationState(), reservationRequest.getExecutableState(), + reservationRequest.getUsageExecutableState(), reservationRequest.getType(), + SpecificationType.fromReservationRequestSummary(reservationRequest), + reservationRequest.getAllocatedReservationId()); + } + + /** + * @param allocationState + * @param executableState + * @param usageExecutableState + * @param reservationRequestType + * @param specificationType + * @param lastReservationId + * @return {@link ReservationRequestState} + */ + public static ReservationRequestState fromApi(AllocationState allocationState, ExecutableState executableState, + ExecutableState usageExecutableState, ReservationRequestType reservationRequestType, + SpecificationType specificationType, String lastReservationId) + { + if (allocationState == null) { + return null; + } + switch (allocationState) { + case ALLOCATED: + if (executableState != null) { + switch (specificationType) { + case PERMANENT_ROOM: + switch (executableState) { + case STARTED: + if (usageExecutableState != null && usageExecutableState.isAvailable()) { + return ALLOCATED_STARTED_AVAILABLE; + } + else { + return ALLOCATED_STARTED_NOT_AVAILABLE; + } + case STOPPED: + case STOPPING_FAILED: + return ALLOCATED_FINISHED; + case STARTING_FAILED: + return FAILED; + default: + return ALLOCATED; + } + case PERMANENT_ROOM_CAPACITY: + switch (executableState) { + case STARTED: + return ALLOCATED_STARTED; + case STOPPED: + case STOPPING_FAILED: + return ALLOCATED_FINISHED; + case STARTING_FAILED: + return FAILED; + default: + return ALLOCATED; + } + case ADHOC_ROOM: + switch (executableState) { + case STARTED: + return ALLOCATED_STARTED; + case STOPPED: + case STOPPING_FAILED: + return ALLOCATED_FINISHED; + case STARTING_FAILED: + return FAILED; + default: + return ALLOCATED; + } + } + } + return ALLOCATED; + case ALLOCATION_FAILED: + if (reservationRequestType.equals(ReservationRequestType.MODIFIED) && lastReservationId != null) { + return MODIFICATION_FAILED; + } + else { + return FAILED; + } + case CONFIRM_AWAITING: + return CONFIRM_AWAITING; + case DENIED: + return DENIED; + default: + return NOT_ALLOCATED; + } + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RoomCapacityModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RoomCapacityModel.java new file mode 100644 index 000000000..8eaa4ecd6 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RoomCapacityModel.java @@ -0,0 +1,22 @@ +package cz.cesnet.shongo.controller.rest.models.reservationrequest; + +import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import lombok.Data; + +@Data +public class RoomCapacityModel { + + private String roomReservationRequestId; + private Integer capacityParticipantCount; + private Boolean hasRoomRecordingService; + private Boolean hasRoomRecordings; + private Boolean isRecordingActive; + + public RoomCapacityModel(ReservationRequestSummary summary) { + this.roomReservationRequestId = summary.getReusedReservationRequestId(); + this.capacityParticipantCount = summary.getRoomParticipantCount(); + this.hasRoomRecordingService = summary.hasRoomRecordingService(); + this.hasRoomRecordings = summary.hasRoomRecordings(); + // TODO this.isRecordingActive = summary.; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/VirtualRoomModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/VirtualRoomModel.java new file mode 100644 index 000000000..4e3b386da --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/VirtualRoomModel.java @@ -0,0 +1,27 @@ +package cz.cesnet.shongo.controller.rest.models.reservationrequest; + +import cz.cesnet.shongo.Technology; +import cz.cesnet.shongo.controller.api.ExecutableState; +import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.rest.models.TechnologyModel; +import lombok.Data; + +import java.util.Set; + +@Data +public class VirtualRoomModel { + + private String roomName; + private ExecutableState state; + private TechnologyModel technology; + private Boolean hasRoomRecordings; + + public VirtualRoomModel(ReservationRequestSummary summary) { + this.roomName = summary.getRoomName(); + this.hasRoomRecordings = summary.hasRoomRecordings(); + this.state = summary.getExecutableState(); + + Set technologies = summary.getSpecificationTechnologies(); + this.technology = TechnologyModel.find(technologies); + } +} From 2bcec11822e93c9f1e872ce6d2e8caaafa1a26a3 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sun, 20 Mar 2022 18:56:04 +0100 Subject: [PATCH 010/134] Added jackson dependency for joda datetime --- shongo-controller/pom.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/shongo-controller/pom.xml b/shongo-controller/pom.xml index ef0a2e6ca..f6f14667f 100644 --- a/shongo-controller/pom.xml +++ b/shongo-controller/pom.xml @@ -22,6 +22,7 @@ 5.2.2.RELEASE + 2.10.1 @@ -115,6 +116,17 @@ jackson-mapper-asl 1.9.13 + + + com.fasterxml.jackson.core + jackson-core + ${jackson.version} + + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson.version} + From 6806e0069074dfd431458b13568115985d34040f Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 23 Mar 2022 19:05:10 +0100 Subject: [PATCH 011/134] Added participant endpoints --- .../shongo/controller/rest/CacheProvider.java | 110 ++++++++ .../rest/api/ParticipantController.java | 190 ++++++++++++++ .../controller/rest/models/CommonModel.java | 96 +++++++ .../detail/ParticipantConfigurationModel.java | 87 +++++++ .../rest/models/detail/ParticipantModel.java | 243 ++++++++++++++++++ 5 files changed, 726 insertions(+) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/CacheProvider.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/CommonModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantConfigurationModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/CacheProvider.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/CacheProvider.java new file mode 100644 index 000000000..cf3308591 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/CacheProvider.java @@ -0,0 +1,110 @@ +package cz.cesnet.shongo.controller.rest; + +import cz.cesnet.shongo.api.UserInformation; +import cz.cesnet.shongo.controller.api.Executable; +import cz.cesnet.shongo.controller.api.Group; +import cz.cesnet.shongo.controller.api.Reservation; +import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.api.ResourceSummary; +import cz.cesnet.shongo.controller.api.SecurityToken; + +/** + * {@link Cache} provided for specified {@link #securityToken}. + * + * @author Martin Srom + */ +public class CacheProvider +{ + /** + * {@link Cache} to be used for retrieving {@link UserInformation}. + */ + private Cache cache; + + /** + * {@link SecurityToken} to be used for retrieving {@link UserInformation} by the {@link #cache}. + */ + private SecurityToken securityToken; + + /** + * Constructor. + * + * @param cache sets the {@link #cache} + * @param securityToken sets the {@link #securityToken} + */ + public CacheProvider(Cache cache, SecurityToken securityToken) + { + this.cache = cache; + this.securityToken = securityToken; + } + + /** + * @return {@link #securityToken} + */ + public SecurityToken getSecurityToken() + { + return securityToken; + } + + /** + * @param userId + * @return {@link UserInformation} for given {@code userId} + */ + public UserInformation getUserInformation(String userId) + { + return cache.getUserInformation(securityToken, userId); + } + + /** + * @param groupId + * @return {@link Group} for given {@code groupId} + */ + public Group getGroup(String groupId) + { + return cache.getGroup(securityToken, groupId); + } + + /** + * @param resourceId + * @return {@link ResourceSummary} for given {@code resourceId} + */ + public ResourceSummary getResourceSummary(String resourceId) + { + return cache.getResourceSummary(securityToken, resourceId); + } + + /** + * @param reservationRequestId + * @return {@link ReservationRequestSummary} for given {@code reservationRequestId} + */ + public ReservationRequestSummary getAllocatedReservationRequestSummary(String reservationRequestId) + { + return cache.getAllocatedReservationRequestSummary(securityToken, reservationRequestId); + } + + /** + * @param reservationId + * @return {@link Reservation} for given {@code reservationId} + */ + public Reservation getReservation(String reservationId) + { + return cache.getReservation(securityToken, reservationId); + } + + /** + * @param executable + * @return identifier of reservation request for given {@code executable} + */ + public String getReservationRequestIdByExecutable(Executable executable) + { + return cache.getReservationRequestIdByExecutable(securityToken, executable); + } + + /** + * @param executableId + * @return {@link Executable} for given {@code executableId} + */ + public Executable getExecutable(String executableId) + { + return cache.getExecutable(securityToken, executableId); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java new file mode 100644 index 000000000..88be5f657 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java @@ -0,0 +1,190 @@ +package cz.cesnet.shongo.controller.rest.api; + +import cz.cesnet.shongo.CommonReportSet; +import cz.cesnet.shongo.ParticipantRole; +import cz.cesnet.shongo.api.UserInformation; +import cz.cesnet.shongo.controller.api.*; +import cz.cesnet.shongo.controller.api.request.ListResponse; +import cz.cesnet.shongo.controller.api.rpc.ExecutableService; +import cz.cesnet.shongo.controller.rest.Cache; +import cz.cesnet.shongo.controller.rest.CacheProvider; +import cz.cesnet.shongo.controller.rest.models.UnsupportedApiException; +import cz.cesnet.shongo.controller.rest.models.detail.ParticipantConfigurationModel; +import cz.cesnet.shongo.controller.rest.models.detail.ParticipantModel; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; + +/** + * Rest controller for participant endpoints. + * + * @author Filip Karnis + */ +@RestController +@RequestMapping("/api/v1/reservation_requests/{id:.+}/participants") +public class ParticipantController { + + private final Cache cache; + private final ExecutableService executableService; + + public ParticipantController( + @Autowired Cache cache, + @Autowired ExecutableService executableService) + { + this.cache = cache; + this.executableService = executableService; + } + + @Operation(summary = "Lists reservation request participants.") + @GetMapping() + ListResponse listRequestParticipants( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id, + @RequestParam(value = "start", required = false) Integer start, + @RequestParam(value = "count", required = false) Integer count) + { + final CacheProvider cacheProvider = new CacheProvider(cache, securityToken); + + // Get room executable + String executableId = cache.getExecutableId(securityToken, id); + AbstractRoomExecutable roomExecutable = getRoomExecutable(securityToken, executableId); + + List participants = new LinkedList<>(); + + // Add reused room participants as read-only + if (roomExecutable instanceof UsedRoomExecutable) { + UsedRoomExecutable usedRoomExecutable = (UsedRoomExecutable) roomExecutable; + String reusedRoomExecutableId = usedRoomExecutable.getReusedRoomExecutableId(); + RoomExecutable reusedRoomExecutable = + (RoomExecutable) getRoomExecutable(securityToken, reusedRoomExecutableId); + List reusedRoomParticipants = + reusedRoomExecutable.getParticipantConfiguration().getParticipants(); + reusedRoomParticipants.sort(Comparator.comparing(o -> Integer.valueOf(o.getId()))); + reusedRoomParticipants.forEach(participant -> { + participant.setId((String) null); + participants.add(participant); + }); + } + + // Add room participants + List roomParticipants = roomExecutable.getParticipantConfiguration().getParticipants(); + roomParticipants.sort(Comparator.comparing(o -> Integer.valueOf(o.getId()))); + participants.addAll(roomParticipants); + + List items = participants.stream().map( + participant -> new ParticipantModel(participant, cacheProvider) + ).collect(Collectors.toList()); + return ListResponse.fromRequest(start, count, items); + } + + @Operation(summary = "Adds new participant to reservation request.") + @PostMapping() + void addParticipant( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable("id") String id, + @RequestParam(required = false) String userId, + @RequestParam(required = false) String name, + @RequestParam(required = false) String email, + @RequestParam ParticipantRole role) + { + String executableId = cache.getExecutableId(securityToken, id); + AbstractRoomExecutable roomExecutable = getRoomExecutable(securityToken, executableId); + + CacheProvider cacheProvider = new CacheProvider(cache, securityToken); + RoomExecutableParticipantConfiguration participantConfiguration = roomExecutable.getParticipantConfiguration(); + + // Initialize model from API + ParticipantConfigurationModel participantConfigurationModel = new ParticipantConfigurationModel(); + for (AbstractParticipant existingParticipant : participantConfiguration.getParticipants()) { + participantConfigurationModel.addParticipant(new ParticipantModel(existingParticipant, cacheProvider)); + } + // Modify model + final ParticipantModel newParticipant; + if (userId != null) { + UserInformation participantInformation = cacheProvider.getUserInformation(userId); + newParticipant = new ParticipantModel(participantInformation, cacheProvider); + } else { + newParticipant = new ParticipantModel(cacheProvider); + newParticipant.setType(ParticipantModel.Type.ANONYMOUS); + newParticipant.setName(name); + newParticipant.setEmail(email); + } + newParticipant.setRole(role); + participantConfigurationModel.addParticipant(newParticipant); + // Initialize API from model + participantConfiguration.clearParticipants(); + for (ParticipantModel participantModel : participantConfigurationModel.getParticipants()) { + participantConfiguration.addParticipant(participantModel.toApi()); + } + executableService.modifyExecutableConfiguration(securityToken, executableId, participantConfiguration); + cache.clearExecutable(executableId); + } + + @Operation(summary = "Adds new participant to reservation request.") + @PutMapping("/{participantId:.+}") + void updateParticipant( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable("id") String id, + @PathVariable("participantId") String participantId, + @RequestParam ParticipantRole role) + { + String executableId = cache.getExecutableId(securityToken, id); + AbstractRoomExecutable roomExecutable = getRoomExecutable(securityToken, executableId); + + RoomExecutableParticipantConfiguration participantConfiguration = roomExecutable.getParticipantConfiguration(); + + // Modify model + ParticipantModel oldParticipant = getParticipant(participantConfiguration, participantId, securityToken); + oldParticipant.setRole(role); + // Initialize API from model + participantConfiguration.removeParticipantById(participantId); + participantConfiguration.addParticipant(oldParticipant.toApi()); + + executableService.modifyExecutableConfiguration(securityToken, executableId, participantConfiguration); + cache.clearExecutable(executableId); + } + + @Operation(summary = "Removes participant from reservation request.") + @DeleteMapping("/{participantId:.+}") + void removeParticipant( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable("id") String id, + @PathVariable("participantId") String participantId) + { + String executableId = cache.getExecutableId(securityToken, id); + AbstractRoomExecutable roomExecutable = getRoomExecutable(securityToken, executableId); + RoomExecutableParticipantConfiguration participantConfiguration = roomExecutable.getParticipantConfiguration(); + ParticipantModel oldParticipant = getParticipant(participantConfiguration, participantId, securityToken); + participantConfiguration.removeParticipantById(oldParticipant.getId()); + executableService.modifyExecutableConfiguration(securityToken, executableId, participantConfiguration); + cache.clearExecutable(executableId); + } + + private AbstractRoomExecutable getRoomExecutable(SecurityToken securityToken, String executableId) + { + Executable executable = cache.getExecutable(securityToken, executableId); + if (executable instanceof AbstractRoomExecutable) { + return (AbstractRoomExecutable) executable; + } + else { + throw new UnsupportedApiException(executable); + } + } + + protected ParticipantModel getParticipant(RoomExecutableParticipantConfiguration participantConfiguration, + String participantId, SecurityToken securityToken) + { + AbstractParticipant participant = participantConfiguration.getParticipant(participantId); + if (participant == null) { + throw new CommonReportSet.ObjectNotExistsException("Participant", participantId); + } + return new ParticipantModel(participant, new CacheProvider(cache, securityToken)); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/CommonModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/CommonModel.java new file mode 100644 index 000000000..f280e8f03 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/CommonModel.java @@ -0,0 +1,96 @@ +package cz.cesnet.shongo.controller.rest.models; + +import cz.cesnet.shongo.controller.api.SecurityToken; +import org.slf4j.Logger; +import org.springframework.util.StringUtils; +import org.springframework.validation.BindingResult; +import org.springframework.validation.Errors; + +import java.util.regex.Pattern; + +/** + * Common validation methods. + * + * @author Martin Srom + */ +public class CommonModel +{ + /** + * Prefix for new unique identifiers. + */ + private static final String NEW_ID_PREFIX = "new-"; + + /** + * Email pattern. + */ + private static final Pattern EMAIL_PATTERN = + Pattern.compile("^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$"); + + /** + * Last auto-generated identifier index. + */ + private static int lastGeneratedId = 0; + + /** + * @param id to be checked + * @return true whether given {@code id} is auto-generated, false otherwise + */ + public synchronized static boolean isNewId(String id) + { + return id.startsWith(NEW_ID_PREFIX); + } + + /** + * @return new auto-generated identifier + */ + public synchronized static String getNewId() + { + return NEW_ID_PREFIX + ++lastGeneratedId; + } + + /** + * Validate email address. + * + * @param bindingResult + * @param field + * @param errorCode + */ + public static void validateEmail(BindingResult bindingResult, String field, String errorCode) + { + String value = (String) bindingResult.getFieldValue(field); + if (value != null) { + value = value.trim(); + } + if (StringUtils.isEmpty(value)) { + bindingResult.rejectValue(field, "validation.field.required"); + } + else if (!EMAIL_PATTERN.matcher(value).matches()) { + bindingResult.rejectValue(field, errorCode); + } + } + + /** + * @param string + * @return given {@code string} which can be used in double quoted string (e.g., "") + */ + public static String escapeDoubleQuotedString(String string) + { + if (string == null) { + return null; + } + string = string.replaceAll("\n", "\\\\n"); + string = string.replaceAll("\"", "\\\\\""); + return string; + } + + /** + * Log validation errors. + * + * @param logger + * @param errors + */ + public static void logValidationErrors(Logger logger, Errors errors, SecurityToken securityToken) + { + logger.info("Validation errors by {}: {}", securityToken.getUserInformation(), errors); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantConfigurationModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantConfigurationModel.java new file mode 100644 index 000000000..17dec1437 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantConfigurationModel.java @@ -0,0 +1,87 @@ +package cz.cesnet.shongo.controller.rest.models.detail; + +import cz.cesnet.shongo.ParticipantRole; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.LinkedList; +import java.util.List; + +/** + * Configuration of {@link ParticipantModel}s. + * + * @author Martin Srom + */ +public class ParticipantConfigurationModel +{ + private static Logger logger = LoggerFactory.getLogger(ParticipantConfigurationModel.class); + + /** + * List of participants. + */ + protected List participants = new LinkedList(); + + + /** + * @return {@link #participants} + */ + public List getParticipants() + { + return participants; + } + + + /** + * @param participant to be added to the {@link #participants} + */ + public void addParticipant(ParticipantModel participant) + { + if (participant.getType().equals(ParticipantModel.Type.USER)) { + String userId = participant.getUserId(); + for (ParticipantModel existingParticipant : participants) { + String existingUserId = existingParticipant.getUserId(); + ParticipantModel.Type existingType = existingParticipant.getType(); + if (existingType.equals(ParticipantModel.Type.USER) && existingUserId.equals(userId)) { + ParticipantRole existingRole = existingParticipant.getRole(); + if (existingRole.compareTo(participant.getRole()) >= 0) { + logger.warn("Skip adding {} because {} already exists.", participant, existingParticipant); + return; + } + else { + logger.warn("Removing {} because {} will be added.", existingParticipant, participant); + participants.remove(existingParticipant); + } + break; + } + } + } + participants.add(participant); + } + + /** + * @param participant to be removed from the {@link #participants} + */ + public void removeParticipant(ParticipantModel participant) + { + for (ParticipantModel existingParticipant : participants) { + if (participant.getId().equals(existingParticipant.getId())) { + participants.remove(existingParticipant); + break; + } + } + } + + /** + * @param participant to be removed from the {@link #participants} + */ + public ParticipantModel getParticipant(ParticipantModel participant) + { + for (ParticipantModel existingParticipant : participants) { + if (participant.getId().equals(existingParticipant.getId())) { + participants.remove(existingParticipant); + return participant; + } + } + return null; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java new file mode 100644 index 000000000..21ec21c51 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java @@ -0,0 +1,243 @@ +package cz.cesnet.shongo.controller.rest.models.detail; + +import cz.cesnet.shongo.ParticipantRole; +import cz.cesnet.shongo.TodoImplementException; +import cz.cesnet.shongo.api.UserInformation; +import cz.cesnet.shongo.controller.api.*; +import cz.cesnet.shongo.controller.rest.CacheProvider; +import cz.cesnet.shongo.controller.rest.models.CommonModel; +import cz.cesnet.shongo.controller.rest.models.UnsupportedApiException; + +/** + * Model for {@link AbstractParticipant}. + * + * @author Martin Srom + * @author Filip Karnis + */ +public class ParticipantModel //implements ReportModel.ContextSerializable +{ + private final CacheProvider cacheProvider; + + protected String id; + + private Type type; + + private UserInformation user; + + private String name; + + private String email; + + private ParticipantRole role; + + private String organization; + + public ParticipantModel(CacheProvider cacheProvider) + { + this.cacheProvider = cacheProvider; + this.type = Type.USER; + } + + public ParticipantModel(UserInformation userInformation, CacheProvider cacheProvider) + { + this.cacheProvider = cacheProvider; + this.type = Type.USER; + setUser(userInformation); + } + + public ParticipantModel(AbstractParticipant participant, CacheProvider cacheProvider) + { + this.id = participant.getId(); + this.cacheProvider = cacheProvider; + if (participant instanceof PersonParticipant) { + PersonParticipant personParticipant = (PersonParticipant) participant; + this.role = personParticipant.getRole(); + AbstractPerson person = personParticipant.getPerson(); + if (person instanceof UserPerson) { + UserPerson userPerson = (UserPerson) person; + setType(Type.USER); + setUserId(userPerson.getUserId()); +/* setName(userPerson.getName()); + setEmail(userPerson.getEmail()); + setOrganization(userPerson.getOrganization());*/ + } + else if (person instanceof AnonymousPerson) { + AnonymousPerson anonymousPerson = (AnonymousPerson) person; + type = Type.ANONYMOUS; + name = anonymousPerson.getName(); + email = anonymousPerson.getEmail(); + } + else { + throw new UnsupportedApiException(person.getClass()); + } + } + else { + throw new UnsupportedApiException(participant.getClass()); + } + } + + public AbstractParticipant toApi() + { + PersonParticipant personParticipant = new PersonParticipant(); + if (!isNew()) { + personParticipant.setId(id); + } + personParticipant.setRole(role); + switch (type) { + case USER: { + UserPerson userPerson = new UserPerson(); + if (user == null) { + throw new IllegalStateException("User must not be null."); + } + userPerson.setUserId(user.getUserId()); +/* userPerson.setName(name); + userPerson.setEmail(email); + userPerson.setOrganization(organization);*/ + personParticipant.setPerson(userPerson); + return personParticipant; + } + case ANONYMOUS: { + AnonymousPerson anonymousPerson = new AnonymousPerson(); + anonymousPerson.setName(name); + anonymousPerson.setEmail(email); + personParticipant.setPerson(anonymousPerson); + return personParticipant; + } + default: + throw new TodoImplementException(type); + } + } + + private boolean isNew() + { + return id == null || CommonModel.isNewId(id); + } + + public String getId() + { + return id; + } + + public void setNewId() + { + this.id = CommonModel.getNewId(); + } + + public void setNullId() + { + this.id = null; + } + + public Type getType() + { + return type; + } + + public void setType(Type type) + { + this.type = type; + } + + public String getUserId() + { + if (user == null) { + return null; + } + return user.getUserId(); + } + + public void setUserId(String userId) + { + if (userId == null || userId.isEmpty()) { + setUser(null); + return; + } + if (cacheProvider == null) { + throw new IllegalStateException(CacheProvider.class + " isn't set."); + } + setUser(cacheProvider.getUserInformation(userId)); + } + + public void setUser(UserInformation user) + { + this.user = user; + } + + public String getName() + { + if (user != null) { + return user.getFullName(); + } + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getEmail() + { + if (user != null) { + return user.getPrimaryEmail(); + } + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + public String getOrganization() { + if (user != null) { + return user.getOrganization(); + } + return organization; + } + + public void setOrganization(String organization) { + this.organization = organization; + } + + public ParticipantRole getRole() + { + return role; + } + + public void setRole(ParticipantRole role) + { + this.role = role; + } + +// @Override +// public String toContextString() +// { +// Map attributes = new LinkedHashMap(); +// attributes.put("ID", id); +// attributes.put("Type", type); +// attributes.put("User ID", user != null ? user.getUserId() : null); +// attributes.put("Name", name); +// attributes.put("Email", email); +// attributes.put("Role", role); +// attributes.put("Organization", organization); +// return ReportModel.formatAttributes(attributes); +// } + +// @Override +// public String toString() +// { +// switch (type) { +// case USER: +// return String.format("UserParticipant(userId: %s, role: %s)", getUserId(), role); +// default: +// return String.format("Participant(type: %s, role: %s)", type, role); +// } +// } + + public enum Type + { + USER, + ANONYMOUS + } +} From 2ce96098b6bf450070582d92daa68bce02d97a53 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 22 Apr 2022 17:48:01 +0200 Subject: [PATCH 012/134] Adding participant with RequestBody --- .../rest/api/ParticipantController.java | 17 +---- .../rest/models/detail/ParticipantModel.java | 68 ++++++------------- 2 files changed, 22 insertions(+), 63 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java index 88be5f657..5b9d79fd8 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java @@ -2,7 +2,6 @@ import cz.cesnet.shongo.CommonReportSet; import cz.cesnet.shongo.ParticipantRole; -import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.api.*; import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.rpc.ExecutableService; @@ -89,10 +88,7 @@ ListResponse listRequestParticipants( void addParticipant( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable("id") String id, - @RequestParam(required = false) String userId, - @RequestParam(required = false) String name, - @RequestParam(required = false) String email, - @RequestParam ParticipantRole role) + @RequestBody ParticipantModel newParticipant) { String executableId = cache.getExecutableId(securityToken, id); AbstractRoomExecutable roomExecutable = getRoomExecutable(securityToken, executableId); @@ -106,17 +102,6 @@ void addParticipant( participantConfigurationModel.addParticipant(new ParticipantModel(existingParticipant, cacheProvider)); } // Modify model - final ParticipantModel newParticipant; - if (userId != null) { - UserInformation participantInformation = cacheProvider.getUserInformation(userId); - newParticipant = new ParticipantModel(participantInformation, cacheProvider); - } else { - newParticipant = new ParticipantModel(cacheProvider); - newParticipant.setType(ParticipantModel.Type.ANONYMOUS); - newParticipant.setName(name); - newParticipant.setEmail(email); - } - newParticipant.setRole(role); participantConfigurationModel.addParticipant(newParticipant); // Initialize API from model participantConfiguration.clearParticipants(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java index 21ec21c51..b5cf0e971 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java @@ -7,6 +7,7 @@ import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.models.CommonModel; import cz.cesnet.shongo.controller.rest.models.UnsupportedApiException; +import lombok.NoArgsConstructor; /** * Model for {@link AbstractParticipant}. @@ -14,15 +15,16 @@ * @author Martin Srom * @author Filip Karnis */ +@NoArgsConstructor public class ParticipantModel //implements ReportModel.ContextSerializable { - private final CacheProvider cacheProvider; + private CacheProvider cacheProvider; protected String id; private Type type; - private UserInformation user; + private String userId; private String name; @@ -42,7 +44,7 @@ public ParticipantModel(UserInformation userInformation, CacheProvider cacheProv { this.cacheProvider = cacheProvider; this.type = Type.USER; - setUser(userInformation); + setUserId(userInformation.getUserId()); } public ParticipantModel(AbstractParticipant participant, CacheProvider cacheProvider) @@ -57,9 +59,10 @@ public ParticipantModel(AbstractParticipant participant, CacheProvider cacheProv UserPerson userPerson = (UserPerson) person; setType(Type.USER); setUserId(userPerson.getUserId()); -/* setName(userPerson.getName()); - setEmail(userPerson.getEmail()); - setOrganization(userPerson.getOrganization());*/ + UserInformation userInformation = cacheProvider.getUserInformation(userPerson.getUserId()); + setName(userInformation.getFullName()); + setEmail(userInformation.getEmail()); + setOrganization(userInformation.getOrganization()); } else if (person instanceof AnonymousPerson) { AnonymousPerson anonymousPerson = (AnonymousPerson) person; @@ -86,13 +89,10 @@ public AbstractParticipant toApi() switch (type) { case USER: { UserPerson userPerson = new UserPerson(); - if (user == null) { + if (userId == null) { throw new IllegalStateException("User must not be null."); } - userPerson.setUserId(user.getUserId()); -/* userPerson.setName(name); - userPerson.setEmail(email); - userPerson.setOrganization(organization);*/ + userPerson.setUserId(userId); personParticipant.setPerson(userPerson); return personParticipant; } @@ -140,34 +140,16 @@ public void setType(Type type) public String getUserId() { - if (user == null) { - return null; - } - return user.getUserId(); + return userId; } public void setUserId(String userId) { - if (userId == null || userId.isEmpty()) { - setUser(null); - return; - } - if (cacheProvider == null) { - throw new IllegalStateException(CacheProvider.class + " isn't set."); - } - setUser(cacheProvider.getUserInformation(userId)); - } - - public void setUser(UserInformation user) - { - this.user = user; + this.userId = userId; } public String getName() { - if (user != null) { - return user.getFullName(); - } return name; } @@ -178,9 +160,6 @@ public void setName(String name) public String getEmail() { - if (user != null) { - return user.getPrimaryEmail(); - } return email; } @@ -190,9 +169,6 @@ public void setEmail(String email) } public String getOrganization() { - if (user != null) { - return user.getOrganization(); - } return organization; } @@ -224,16 +200,14 @@ public void setRole(ParticipantRole role) // return ReportModel.formatAttributes(attributes); // } -// @Override -// public String toString() -// { -// switch (type) { -// case USER: -// return String.format("UserParticipant(userId: %s, role: %s)", getUserId(), role); -// default: -// return String.format("Participant(type: %s, role: %s)", type, role); -// } -// } + @Override + public String toString() + { + if (type == Type.USER) { + return String.format("UserParticipant(userId: %s, role: %s)", getUserId(), role); + } + return String.format("Participant(type: %s, role: %s)", type, role); + } public enum Type { From 6499f3edaafcfb6d91b96b000fa6118268aacdc7 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sat, 26 Mar 2022 01:41:58 +0100 Subject: [PATCH 013/134] Added roles endpoints --- .../rest/api/UserRoleController.java | 90 +++++++++ .../LastOwnerRoleNotDeletableException.java | 9 + .../rest/models/roles/UserRoleModel.java | 176 ++++++++++++++++++ 3 files changed, 275 insertions(+) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/UserRoleController.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/LastOwnerRoleNotDeletableException.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/roles/UserRoleModel.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/UserRoleController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/UserRoleController.java new file mode 100644 index 000000000..b624a32d8 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/UserRoleController.java @@ -0,0 +1,90 @@ +package cz.cesnet.shongo.controller.rest.api; + +import cz.cesnet.shongo.controller.ObjectRole; +import cz.cesnet.shongo.controller.api.AclEntry; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.request.AclEntryListRequest; +import cz.cesnet.shongo.controller.api.request.ListResponse; +import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; +import cz.cesnet.shongo.controller.rest.Cache; +import cz.cesnet.shongo.controller.rest.CacheProvider; +import cz.cesnet.shongo.controller.rest.error.LastOwnerRoleNotDeletableException; +import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; + +/** + * Rest controller for roles endpoints. + * + * @author Filip Karnis + */ +@RestController +@RequestMapping("/api/v1/reservation_requests/{id:.+}/roles") +public class UserRoleController { + + private final AuthorizationService authorizationService; + private final Cache cache; + + public UserRoleController(@Autowired AuthorizationService reservationService, @Autowired Cache cache) { + this.authorizationService = reservationService; + this.cache = cache; + } + + @Operation(summary = "Lists reservation request roles.") + @GetMapping() + ListResponse listRequestRoles( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable(value = "id") String objectId, + @RequestParam(value = "start", required = false) Integer start, + @RequestParam(value = "count", required = false) Integer count) + { + AclEntryListRequest request = new AclEntryListRequest(); + request.setSecurityToken(securityToken); + request.setStart(start); + request.setCount(count); + request.addObjectId(objectId); + ListResponse aclEntries = authorizationService.listAclEntries(request); + + CacheProvider cacheProvider = new CacheProvider(cache, securityToken); + List items = aclEntries.getItems().stream().map(item -> new UserRoleModel(item, cacheProvider)).collect(Collectors.toList()); + return ListResponse.fromRequest(start, count, items); + } + + @Operation(summary = "Creates new role for reservation request.") + @PostMapping() + void createRequestRoles( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable("id") String objectId, + @RequestBody UserRoleModel userRoleModel) + { + String reservationRequestId = cache.getReservationRequestId(securityToken, objectId); + userRoleModel.setObjectId(reservationRequestId); + userRoleModel.setDeletable(true); + authorizationService.createAclEntry(securityToken, userRoleModel.toApi()); + } + + @Operation(summary = "Deletes role for reservation request.") + @DeleteMapping("/{entityId:.+}") + void deleteRequestRoles( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable("id") String objectId, + @PathVariable("entityId") String entityId) + { + String reservationRequestId = cache.getReservationRequestId(securityToken, objectId); + AclEntryListRequest request = new AclEntryListRequest(); + request.setSecurityToken(securityToken); + request.addObjectId(reservationRequestId); + request.addRole(ObjectRole.OWNER); + ListResponse aclEntries = authorizationService.listAclEntries(request); + if (aclEntries.getItemCount() == 1 && aclEntries.getItem(0).getId().equals(entityId)) { + throw new LastOwnerRoleNotDeletableException(); + } + authorizationService.deleteAclEntry(securityToken, entityId); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/LastOwnerRoleNotDeletableException.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/LastOwnerRoleNotDeletableException.java new file mode 100644 index 000000000..474f1bd44 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/LastOwnerRoleNotDeletableException.java @@ -0,0 +1,9 @@ +package cz.cesnet.shongo.controller.rest.error; + +public class LastOwnerRoleNotDeletableException extends RuntimeException +{ + public LastOwnerRoleNotDeletableException() + { + super("Last OWNER role cannot be deleted."); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/roles/UserRoleModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/roles/UserRoleModel.java new file mode 100644 index 000000000..4f5bdfc4c --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/roles/UserRoleModel.java @@ -0,0 +1,176 @@ +package cz.cesnet.shongo.controller.rest.models.roles; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import cz.cesnet.shongo.TodoImplementException; +import cz.cesnet.shongo.api.UserInformation; +import cz.cesnet.shongo.controller.AclIdentityType; +import cz.cesnet.shongo.controller.ObjectRole; +import cz.cesnet.shongo.controller.api.AclEntry; +import cz.cesnet.shongo.controller.api.Group; +import cz.cesnet.shongo.controller.rest.CacheProvider; +import cz.cesnet.shongo.controller.rest.models.CommonModel; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Represents entity's role for specified resource. + * + * @author Martin Srom + * @author Filip Karnis + */ +@Data +@NoArgsConstructor +public class UserRoleModel +{ + private String id; + + @JsonProperty("type") + private AclIdentityType identityType; + + @JsonProperty("entityId") + private String identityPrincipalId; + + private String identityName; + + private String identityDescription; + + private String email; + + private String objectId; + + private ObjectRole role; + + private boolean deletable = false; + + @JsonIgnore + private CacheProvider cacheProvider; + + @JsonIgnore + private UserInformation user; + + @JsonIgnore + private Group group; + + public UserRoleModel(UserInformation userInformation) + { + this.identityType = AclIdentityType.USER; + this.identityPrincipalId = userInformation.getUserId(); + this.user = userInformation; + } + + public UserRoleModel(CacheProvider cacheProvider, AclIdentityType type) + { + this.cacheProvider = cacheProvider; + this.identityType = type; + } + + public UserRoleModel(AclEntry aclEntry, CacheProvider cacheProvider) + { + this.cacheProvider = cacheProvider; + fromApi(aclEntry); + } + + private boolean isNew() + { + return id == null || CommonModel.isNewId(id); + } + + public void setNewId() + { + this.id = CommonModel.getNewId(); + } + + public void setIdentityType(AclIdentityType identityType) + { + if (!identityType.equals(this.identityType)) { + user = null; + group = null; + } + this.identityType = identityType; + } + + public void setIdentityPrincipalId(String identityPrincipalId) + { + if (!identityPrincipalId.equals(this.identityPrincipalId)) { + user = null; + group = null; + } + this.identityPrincipalId = identityPrincipalId; + } + + private String getIdentityNameFromApi() + { + switch (identityType) { + case USER: + return getUser().getFullName(); + case GROUP: + return getGroup().getName(); + default: + throw new TodoImplementException(identityType); + } + } + + private String getIdentityDescriptionFromApi() + { + switch (identityType) { + case USER: + return getUser().getOrganization(); + case GROUP: + return getGroup().getDescription(); + default: + throw new TodoImplementException(identityType); + } + } + + @JsonIgnore + public UserInformation getUser() + { + if (user == null && identityType.equals(AclIdentityType.USER)) { + if (cacheProvider == null) { + throw new IllegalStateException("CacheProvider isn't set."); + } + user = cacheProvider.getUserInformation(identityPrincipalId); + } + return user; + } + + private Group getGroup() + { + if (group == null && identityType.equals(AclIdentityType.GROUP)) { + if (cacheProvider == null) { + throw new IllegalStateException("CacheProvider isn't set."); + } + group = cacheProvider.getGroup(identityPrincipalId); + } + return group; + } + + public void fromApi(AclEntry aclEntry) + { + this.id = aclEntry.getId(); + setIdentityType(aclEntry.getIdentityType()); + setIdentityPrincipalId(aclEntry.getIdentityPrincipalId()); + setIdentityName(getIdentityNameFromApi()); + setIdentityDescription(getIdentityDescriptionFromApi()); + if (AclIdentityType.USER.equals(identityType)) { + setEmail(getUser().getEmail()); + } + setObjectId(aclEntry.getObjectId()); + setRole(aclEntry.getRole()); + setDeletable(aclEntry.isDeletable()); + } + + public AclEntry toApi() + { + AclEntry aclEntry = new AclEntry(); + if (!isNew()) { + aclEntry.setId(id); + } + aclEntry.setIdentityType(identityType); + aclEntry.setIdentityPrincipalId(identityPrincipalId); + aclEntry.setObjectId(objectId); + aclEntry.setRole(role); + return aclEntry; + } +} From 88d533c1c7c4c1d0557e4d816e4e151173802127 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sat, 26 Mar 2022 19:31:00 +0100 Subject: [PATCH 014/134] Added recording endpoints --- .../rest/api/RecordingController.java | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RecordingController.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RecordingController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RecordingController.java new file mode 100644 index 000000000..b3cc35b11 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RecordingController.java @@ -0,0 +1,110 @@ +package cz.cesnet.shongo.controller.rest.api; + +import cz.cesnet.shongo.controller.api.ResourceRecording; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.request.ExecutableRecordingListRequest; +import cz.cesnet.shongo.controller.api.request.ListResponse; +import cz.cesnet.shongo.controller.api.rpc.ExecutableService; +import cz.cesnet.shongo.controller.api.rpc.ResourceControlService; +import cz.cesnet.shongo.controller.rest.Cache; +import cz.cesnet.shongo.controller.scheduler.SchedulerReportSet; +import io.swagger.v3.oas.annotations.Operation; +import org.joda.time.Duration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; + +/** + * Rest controller for recording endpoints. + * + * @author Filip Karnis + */ +@RestController +@RequestMapping("/api/v1/reservation_requests/{id:.+}/recordings") +public class RecordingController { + + private final Cache cache; + private final ExecutableService executableService; + private final ResourceControlService resourceControlService; + + public RecordingController( + @Autowired Cache cache, + @Autowired ExecutableService executableService, + @Autowired ResourceControlService resourceControlService) + { + this.cache = cache; + this.executableService = executableService; + this.resourceControlService = resourceControlService; + } + + @Operation(summary = "Lists reservation request recordings.") + @GetMapping() + Map listRequestRecordings( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id, + @RequestParam(value = "start", required = false) Integer start, + @RequestParam(value = "count", required = false) Integer count, + @RequestParam(value = "sort", required = false, defaultValue = "START") + ExecutableRecordingListRequest.Sort sort, + @RequestParam(value = "sort-desc", required = false, defaultValue = "true") boolean sortDescending) + throws SchedulerReportSet.RoomExecutableNotExistsException + { + String executableId = cache.getExecutableId(securityToken, id); + if (executableId == null) { + throw new SchedulerReportSet.RoomExecutableNotExistsException(); + } + ExecutableRecordingListRequest request = new ExecutableRecordingListRequest(); + request.setSecurityToken(securityToken); + request.setExecutableId(executableId); + request.setStart(start); + request.setCount(count); + request.setSort(sort); + request.setSortDescending(sortDescending); + + ListResponse response = executableService.listExecutableRecordings(request); + List items = new LinkedList<>(); + for (ResourceRecording recording : response.getItems()) { + Map item = new HashMap<>(); + item.put("id", recording.getId()); + item.put("resourceId", recording.getResourceId()); + item.put("name", recording.getName()); + item.put("description", recording.getDescription()); + item.put("beginDate", recording.getBeginDate()); + Duration duration = recording.getDuration(); + if (duration == null || duration.isShorterThan(Duration.standardMinutes(1))) { + item.put("duration", null); + } + else { + item.put("duration", duration.toPeriod()); + } + item.put("isPublic",recording.isPublic()); + item.put("downloadUrl", recording.getDownloadUrl()); + item.put("viewUrl", recording.getViewUrl()); + item.put("editUrl", recording.getEditUrl()); + item.put("filename",recording.getFileName()); + items.add(item); + } + Map data = new HashMap<>(); + data.put("start", response.getStart()); + data.put("count", response.getCount()); + data.put("items", items); + + return data; + } + + @Operation(summary = "Deletes recording from reservation request.") + @DeleteMapping("/{recordingId:.+}") + void deleteRequestRecording( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id, + @PathVariable String recordingId) + { + resourceControlService.deleteRecording(securityToken, id, recordingId); + } +} From a82b5df4607d04398a56d5d37e27bd5ed3f541b6 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sat, 26 Mar 2022 19:31:31 +0100 Subject: [PATCH 015/134] Added runtime endpoints --- .../shongo/controller/rest/RoomCache.java | 289 ++++++++++++++++++ .../rest/api/RuntimeController.java | 246 +++++++++++++++ 2 files changed, 535 insertions(+) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RuntimeController.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java new file mode 100644 index 000000000..d19634855 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java @@ -0,0 +1,289 @@ + +package cz.cesnet.shongo.controller.rest; + +import cz.cesnet.shongo.CommonReportSet; +import cz.cesnet.shongo.ExpirationMap; +import cz.cesnet.shongo.api.MediaData; +import cz.cesnet.shongo.api.Room; +import cz.cesnet.shongo.api.RoomParticipant; +import cz.cesnet.shongo.controller.api.Executable; +import cz.cesnet.shongo.controller.api.RoomExecutable; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.rpc.ExecutableService; +import cz.cesnet.shongo.controller.api.rpc.ResourceControlService; +import cz.cesnet.shongo.controller.rest.models.UnsupportedApiException; +import org.joda.time.Duration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.*; + +/** + * Cache of information for management of rooms. + * + * @author Martin Srom + */ +public class RoomCache +{ + private static Logger logger = LoggerFactory.getLogger(RoomCache.class); + + private final ResourceControlService resourceControlService; + + private final ExecutableService executableService; + + private final Cache cache; + + /** + * {@link RoomExecutable} by roomExecutableId. + */ + private final ExpirationMap roomExecutableCache = + new ExpirationMap(); + + /** + * {@link Room} by roomExecutableId". + */ + private final ExpirationMap roomCache = + new ExpirationMap(); + + /** + * Collection of {@link RoomParticipant}s by roomExecutableId. + */ + private final ExpirationMap> roomParticipantsCache = + new ExpirationMap>(); + + /** + * {@link RoomParticipant} by "roomExecutableId:participantId". + */ + private final ExpirationMap roomParticipantCache = + new ExpirationMap(); + + /** + * Participant snapshots in {@link MediaData} by "roomExecutableId:participantId". + */ + private final ExpirationMap roomParticipantSnapshotCache = + new ExpirationMap(); + + /** + * Constructor. + */ + public RoomCache( + @Autowired ResourceControlService resourceControlService, + @Autowired ExecutableService executableService, + @Autowired Cache cache) + { + this.resourceControlService = resourceControlService; + this.executableService = executableService; + this.cache = cache; + + // Set expiration durations + roomCache.setExpiration(Duration.standardSeconds(30)); + roomParticipantsCache.setExpiration(Duration.standardSeconds(15)); + roomExecutableCache.setExpiration(Duration.standardSeconds(15)); + roomParticipantSnapshotCache.setExpiration(Duration.standardSeconds(15)); + } + + /** + * @param securityToken + * @param roomExecutableId + * @return {@link Room} for given {@code roomExecutableId} + */ + public Room getRoom(SecurityToken securityToken, String roomExecutableId) + { + synchronized (roomCache) { + Room room = roomCache.get(roomExecutableId); + if (room == null) { + RoomExecutable roomExecutable = getRoomExecutable(securityToken, roomExecutableId); + String resourceId = roomExecutable.getResourceId(); + String resourceRoomId = roomExecutable.getRoomId(); + room = resourceControlService.getRoom(securityToken, resourceId, resourceRoomId); + roomCache.put(roomExecutableId, room); + } + return room; + } + } + + /** + * Modify given {@code room}. + * + * @param securityToken + * @param room + */ + public void modifyRoom(SecurityToken securityToken, String roomExecutableId, Room room) + { + synchronized (roomCache) { + RoomExecutable roomExecutable = getRoomExecutable(securityToken, roomExecutableId); + String resourceId = roomExecutable.getResourceId(); + if (!room.getId().equals(roomExecutable.getRoomId())) { + throw new IllegalArgumentException("Room doesn't correspond to given executable."); + } + resourceControlService.modifyRoom(securityToken, resourceId, room); + roomCache.put(roomExecutableId, room); + } + } + + /** + * @param securityToken + * @param roomExecutableId + * @return collection of {@link RoomParticipant}s for given {@code roomExecutableId} + */ + public List getRoomParticipants(SecurityToken securityToken, String roomExecutableId) + { + synchronized (roomParticipantsCache) { + List roomParticipants = roomParticipantsCache.get(roomExecutableId); + if (roomParticipants == null) { + RoomExecutable roomExecutable = getRoomExecutable(securityToken, roomExecutableId); + String resourceId = roomExecutable.getResourceId(); + String resourceRoomId = roomExecutable.getRoomId(); + roomParticipants = new LinkedList<>(); + synchronized (roomParticipantCache) { + for (RoomParticipant roomParticipant : resourceControlService.listRoomParticipants( + securityToken, resourceId, resourceRoomId)) { + roomParticipants.add(roomParticipant); + roomParticipantCache.put(roomExecutableId + ":" + roomParticipant.getId(), roomParticipant); + } + } + roomParticipantsCache.put(roomExecutableId, roomParticipants); + } + return roomParticipants; + } + } + + /** + * @param securityToken + * @param roomExecutableId + * @param roomParticipantId + * @return {@link RoomParticipant} for given {@code roomExecutableId} and {@code roomParticipantId} + */ + public RoomParticipant getRoomParticipant(SecurityToken securityToken, String roomExecutableId, String roomParticipantId) + { + String cacheId = roomExecutableId + ":" + roomParticipantId; + synchronized (roomParticipantCache) { + RoomParticipant roomParticipant = roomParticipantCache.get(cacheId); + if (roomParticipant == null) { + RoomExecutable roomExecutable = getRoomExecutable(securityToken, roomExecutableId); + String resourceId = roomExecutable.getResourceId(); + String resourceRoomId = roomExecutable.getRoomId(); + roomParticipant = resourceControlService.getRoomParticipant( + securityToken, resourceId, resourceRoomId, roomParticipantId); + if (roomParticipant == null) { + throw new CommonReportSet.ObjectNotExistsException("RoomParticipant", roomParticipantId); + } + roomParticipantCache.put(cacheId, roomParticipant); + } + return roomParticipant; + } + } + + /** + * @param securityToken + * @param roomExecutableId + * @param roomParticipant to be modified in the given {@code roomExecutableId} + */ + public void modifyRoomParticipant(SecurityToken securityToken, String roomExecutableId, RoomParticipant roomParticipant) + { + RoomExecutable roomExecutable = getRoomExecutable(securityToken, roomExecutableId); + String resourceId = roomExecutable.getResourceId(); + String resourceRoomId = roomExecutable.getRoomId(); + roomParticipant.setRoomId(resourceRoomId); + resourceControlService.modifyRoomParticipant(securityToken, resourceId, roomParticipant); + synchronized (roomParticipantCache) { + roomParticipantCache.remove(roomParticipant.getId()); + } + synchronized (roomParticipantsCache) { + roomParticipantsCache.remove(roomExecutableId); + } + } + + /** + * @param securityToken + * @param roomExecutableId + * @param roomParticipants configuration to which all room participants should be modified in the given {@code roomExecutableId} + */ + public void modifyRoomParticipants(SecurityToken securityToken, String roomExecutableId, RoomParticipant roomParticipants) + { + RoomExecutable roomExecutable = getRoomExecutable(securityToken, roomExecutableId); + String resourceId = roomExecutable.getResourceId(); + String resourceRoomId = roomExecutable.getRoomId(); + roomParticipants.setRoomId(resourceRoomId); + resourceControlService.modifyRoomParticipants(securityToken, resourceId, roomParticipants); + synchronized (roomParticipantCache) { + List participants = roomParticipantsCache.get(roomExecutableId); + if (participants != null) { + for (RoomParticipant roomParticipant : participants) { + roomParticipantCache.remove(roomExecutableId + ":" + roomParticipant.getId()); + } + } + } + synchronized (roomParticipantsCache) { + roomParticipantsCache.remove(roomExecutableId); + } + } + + /** + * @param securityToken + * @param roomExecutableId + * @param roomParticipantId + * @return {@link MediaData} snapshot of room participant + */ + public MediaData getRoomParticipantSnapshot(SecurityToken securityToken, String roomExecutableId, String roomParticipantId) + { + String cacheId = roomExecutableId + ":" + roomParticipantId; + synchronized (roomParticipantSnapshotCache) { + if (!roomParticipantSnapshotCache.contains(cacheId)) { + RoomExecutable roomExecutable = getRoomExecutable(securityToken, roomExecutableId); + String resourceId = roomExecutable.getResourceId(); + String resourceRoomId = roomExecutable.getRoomId(); + Set roomParticipantIds = new HashSet(); + roomParticipantIds.add(roomParticipantId); + Map participantSnapshots = resourceControlService.getRoomParticipantSnapshots( + securityToken, resourceId, resourceRoomId, roomParticipantIds); + MediaData roomParticipantSnapshot = participantSnapshots.get(roomParticipantId); + roomParticipantSnapshotCache.put(cacheId, roomParticipantSnapshot); + } + return roomParticipantSnapshotCache.get(cacheId); + } + } + + /** + * @param securityToken + * @param roomExecutableId + * @param roomParticipantId to be disconnected from given {@code roomExecutableId} + */ + public void disconnectRoomParticipant(SecurityToken securityToken, String roomExecutableId, String roomParticipantId) + { + RoomExecutable roomExecutable = getRoomExecutable(securityToken, roomExecutableId); + String resourceId = roomExecutable.getResourceId(); + String resourceRoomId = roomExecutable.getRoomId(); + resourceControlService.disconnectRoomParticipant(securityToken, resourceId, resourceRoomId, roomParticipantId); + synchronized (roomParticipantCache) { + roomParticipantCache.remove(roomParticipantId); + } + synchronized (roomParticipantsCache) { + roomParticipantsCache.remove(roomExecutableId); + } + } + + /** + * @param securityToken + * @param roomExecutableId + * @return {@link RoomExecutable} for given {@code roomExecutableId} + */ + public RoomExecutable getRoomExecutable(SecurityToken securityToken, String roomExecutableId) + { + synchronized (roomExecutableCache) { + RoomExecutable roomExecutable = roomExecutableCache.get(roomExecutableId); + if (roomExecutable == null) { + Executable executable = cache.getExecutable(securityToken, roomExecutableId); + if (executable instanceof RoomExecutable) { + roomExecutable = (RoomExecutable) executable; + } + else { + throw new UnsupportedApiException(executable); + } + roomExecutableCache.put(roomExecutableId, roomExecutable); + } + return roomExecutable; + } + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RuntimeController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RuntimeController.java new file mode 100644 index 000000000..5a7978908 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RuntimeController.java @@ -0,0 +1,246 @@ +package cz.cesnet.shongo.controller.rest.api; + +import cz.cesnet.shongo.api.Alias; +import cz.cesnet.shongo.api.MediaData; +import cz.cesnet.shongo.api.RoomParticipant; +import cz.cesnet.shongo.api.UserInformation; +import cz.cesnet.shongo.controller.api.ExecutionReport; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.request.ListResponse; +import cz.cesnet.shongo.controller.api.rpc.ExecutableService; +import cz.cesnet.shongo.controller.rest.Cache; +import cz.cesnet.shongo.controller.rest.CacheProvider; +import cz.cesnet.shongo.controller.rest.RoomCache; +import io.swagger.v3.oas.annotations.Operation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.*; + +import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; + +/** + * Rest controller for runtime endpoints. + * + * @author Filip Karnis + */ +@Slf4j +@RestController +@RequestMapping("/api/v1/reservation_requests/{id:.+}/runtime_management") +public class RuntimeController { + + private final Cache cache; + private final RoomCache roomCache; + private final ExecutableService executableService; + + public RuntimeController( + @Autowired Cache cache, + @Autowired RoomCache roomCache, + @Autowired ExecutableService executableService) + { + this.cache = cache; + this.executableService = executableService; + this.roomCache = roomCache; + } + + @Operation(summary = "Lists reservation request runtime participants.") + @GetMapping("/participants") + Map listRuntimeParticipants( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id, + @RequestParam(value = "start", required = false) Integer start, + @RequestParam(value = "count", required = false) Integer count, + @RequestParam(value = "sort", required = false, defaultValue = "DATETIME") String sort, + @RequestParam(value = "sort-desc", required = false, defaultValue = "true") boolean sortDescending) + { + String executableId = cache.getExecutableId(securityToken, id); + CacheProvider cacheProvider = new CacheProvider(cache, securityToken); + List roomParticipants = Collections.emptyList(); + try { + roomParticipants = roomCache.getRoomParticipants(securityToken, executableId); + } + catch (Exception exception) { + log.warn("Failed to load participants", exception); + } + ListResponse response = ListResponse.fromRequest(start, count, roomParticipants); + List items = new LinkedList<>(); + for (RoomParticipant roomParticipant : response.getItems()) { + UserInformation user = null; + String userId = roomParticipant.getUserId(); + if (userId != null) { + user = cacheProvider.getUserInformation(userId); + } + Alias alias = roomParticipant.getAlias(); + Map item = new HashMap<>(); + item.put("id", roomParticipant.getId()); + item.put("name", (user != null ? user.getFullName() : roomParticipant.getDisplayName())); + item.put("alias", (alias != null ? alias.getValue() : null)); + item.put("role", roomParticipant.getRole()); + item.put("email", (user != null ? user.getPrimaryEmail() : null)); + item.put("layout", roomParticipant.getLayout()); + item.put("microphoneEnabled", roomParticipant.getMicrophoneEnabled()); + item.put("microphoneLevel", roomParticipant.getMicrophoneLevel()); + item.put("videoEnabled", roomParticipant.getVideoEnabled()); + item.put("videoSnapshot", roomParticipant.isVideoSnapshot()); + items.add(item); + } + Map data = new HashMap<>(); + data.put("start", response.getStart()); + data.put("count", response.getCount()); + data.put("items", items); + return data; + } + + @Operation(summary = "Takes snapshot of reservation request runtime participant.") + @PostMapping("/participants/{participantId:.+}/snapshot") + ResponseEntity snapshotRuntimeParticipant( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id, + @PathVariable String participantId) + { + String executableId = cache.getExecutableId(securityToken, id); + try { + MediaData participantSnapshot = roomCache.getRoomParticipantSnapshot( + securityToken, executableId, participantId); + if (participantSnapshot != null) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.parseMediaType(participantSnapshot.getType().toString())); + headers.setCacheControl("no-cache, no-store, must-revalidate"); + headers.setPragma("no-cache"); + return new ResponseEntity<>(participantSnapshot.getData(), headers, HttpStatus.OK); + } + } + catch (Exception exception) { + log.warn("Failed to get participant snapshot", exception); + } + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + @Operation(summary = "Modifies reservation request runtime participant.") + @PatchMapping("/participants/{participantId:.+}") + void modifyRuntimeParticipant( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id, + @PathVariable String participantId, + @RequestBody Map body) + { + String executableId = cache.getExecutableId(securityToken, id); + RoomParticipant oldRoomParticipant = null; + if (!participantId.equals("*")) { + oldRoomParticipant = roomCache.getRoomParticipant(securityToken, executableId, participantId); + } + + // Parse body + String name = (String) body.get("name"); + Boolean microphoneEnabled = (Boolean) body.get("microphoneEnabled"); + Integer microphoneLevel = (Integer) body.get("microphoneLevel"); + Boolean videoEnabled = (Boolean) body.get("videoEnabled"); + + RoomParticipant roomParticipant = new RoomParticipant(participantId); + if (name != null) { + roomParticipant.setDisplayName(name); + } + if (microphoneLevel != null) { + roomParticipant.setMicrophoneLevel(microphoneLevel); + } + if (microphoneEnabled != null) { + if (oldRoomParticipant != null && oldRoomParticipant.getMicrophoneEnabled() == null) { + throw new IllegalStateException("Mute microphone is not available."); + } + roomParticipant.setMicrophoneEnabled(microphoneEnabled); + } + if (videoEnabled != null) { + if (oldRoomParticipant != null && oldRoomParticipant.getVideoEnabled() == null) { + throw new IllegalStateException("Disable video is not available."); + } + roomParticipant.setVideoEnabled(videoEnabled); + } + if (participantId.equals("*")) { + roomParticipant.setId((String) null); + roomCache.modifyRoomParticipants(securityToken, executableId, roomParticipant); + } + else { + roomCache.modifyRoomParticipant(securityToken, executableId, roomParticipant); + } + } + + @Operation(summary = "Disconnects reservation request runtime participant.") + @PostMapping("/participants/{participantId:.+}/disconnect") + void disconnectRuntimeParticipant( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id, + @PathVariable String participantId) + { + String executableId = cache.getExecutableId(securityToken, id); + roomCache.disconnectRoomParticipant(securityToken, executableId, participantId); + } + + @Operation(summary = "Starts recording of reservation request runtime.") + @PostMapping("/recording/start") + void startRequestRecording( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id, + @RequestParam(value = "executableId") String executableId, + @RequestParam(value = "executableServiceId") String executableServiceId) + { + Object result = null; + try { + result = executableService.activateExecutableService(securityToken, executableId, executableServiceId); + } + catch (Exception exception) { + log.warn("Start recording failed", exception); + } + if (Boolean.TRUE.equals(result) || Boolean.FALSE.equals(result)) { + cache.clearExecutable(executableId); + } + else { +// Locale locale = userSession.getLocale(); + String errorCode = "startingFailed"; + if (result instanceof ExecutionReport) { + ExecutionReport executionReport = (ExecutionReport) result; +// log.warn("Start recording failed: {}", executionReport.toString(locale, userSession.getTimeZone())); + log.warn("Start recording failed: {}", executionReport); + + // Detect further error + ExecutionReport.UserError userError = executionReport.toUserError(); + if (userError instanceof ExecutionReport.RecordingUnavailable) { + errorCode = "unavailable"; + } + } + throw new RuntimeException("Starting recording failed with error: " + errorCode); + } + } + + @Operation(summary = "Stops recording of reservation request runtime.") + @PostMapping("/recording/stop") + void stopRequestRecording( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id, + @RequestParam(value = "executableId") String executableId, + @RequestParam(value = "executableServiceId") String executableServiceId) + { + Object result = null; + try { + result = executableService.deactivateExecutableService(securityToken, executableId, executableServiceId); + } + catch (Exception exception) { + log.warn("Stop recording failed", exception); + } + if (Boolean.TRUE.equals(result)) { + cache.clearExecutable(executableId); + } + else { +// Locale locale = userSession.getLocale(); + if (result instanceof ExecutionReport) { + ExecutionReport executionReport = (ExecutionReport) result; +// log.warn("Stop recording failed: {}", executionReport.toString(locale, userSession.getTimeZone())); + } + } + cache.clearExecutable(executableId); + } +} From daf7fd47a2317340350ae83b99b42d457188b118 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 6 Apr 2022 18:17:03 +0200 Subject: [PATCH 016/134] Added resources endpoints --- .../controller/api/ResourceSummary.java | 49 +++ .../api/rpc/ResourceServiceImpl.java | 10 + .../rest/api/ResourceController.java | 138 +++++++ .../rest/models/resource/RModel.java | 12 + .../models/resource/ResourceCapacity.java | 269 +++++++++++++ .../resource/ResourceCapacityBucket.java | 98 +++++ .../resource/ResourceCapacityUtilization.java | 107 +++++ .../ResourceCapacityUtilizationModel.java | 22 ++ .../rest/models/resource/ResourceModel.java | 27 ++ .../models/resource/ResourcesUtilization.java | 367 ++++++++++++++++++ .../src/main/resources/sql/resource_list.sql | 5 +- 11 files changed, 1103 insertions(+), 1 deletion(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ResourceController.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/RModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacity.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityBucket.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilization.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilizationModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java index a2759c8d6..9d77ccd3e 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java @@ -19,6 +19,11 @@ public class ResourceSummary extends IdentifiedComplexType */ private String userId; + /** + * Type of the resource. + */ + private Type type; + /** * Name of the resource. */ @@ -29,6 +34,11 @@ public class ResourceSummary extends IdentifiedComplexType */ private Set technologies = new HashSet(); + /** + * Tags of the resource. + */ + private Set tags = new HashSet<>(); + /** * Parent resource shongo-id. */ @@ -103,6 +113,20 @@ public void setName(String name) this.name = name; } + /** + * @return {@link #type} + */ + public Type getType() { + return type; + } + + /** + * @param type sets the {@link #type} + */ + public void setType(Type type) { + this.type = type; + } + /** * @return {@link #technologies} */ @@ -130,6 +154,20 @@ public void addTechnology(Technology technology) this.technologies.add(technology); } + /** + * @return {@link #tags} + */ + public Set getTags() { + return tags; + } + + /** + * @param tag to be added to the {@link #tags} + */ + public void addTag(String tag) { + tags.add(tag); + } + /** * @return {@link #parentResourceId} */ @@ -230,6 +268,11 @@ public void setAllocationOrder(Integer allocationOrder) this.allocationOrder = allocationOrder; } + public enum Type { + ROOM_PROVIDER, + RESOURCE, + } + private static final String USER_ID = "userId"; private static final String NAME = "name"; private static final String TECHNOLOGIES = "technologies"; @@ -242,6 +285,8 @@ public void setAllocationOrder(Integer allocationOrder) private static final String DOMAIN_NAME = "domainName"; private static final String CONFIRM_BY_OWNER = "confirmByOwner"; public static final String REMOTE_CALENDAR_NAME = "remoteCalendarName"; + public static final String TYPE = "type"; + public static final String TAGS = "tags"; @Override public DataMap toData() @@ -259,6 +304,8 @@ public DataMap toData() dataMap.set(DOMAIN_NAME, domainName); dataMap.set(CONFIRM_BY_OWNER, confirmByOowner); dataMap.set(REMOTE_CALENDAR_NAME, remoteCalendarName); + dataMap.set(TYPE, type); + dataMap.set(TAGS, tags); return dataMap; } @@ -278,5 +325,7 @@ public void fromData(DataMap dataMap) domainName = dataMap.getString(DOMAIN_NAME); confirmByOowner = dataMap.getBool(CONFIRM_BY_OWNER); remoteCalendarName = dataMap.getString(REMOTE_CALENDAR_NAME); + type = dataMap.getEnum(TYPE, Type.class); + tags = dataMap.getSet(TAGS, String.class); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ResourceServiceImpl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ResourceServiceImpl.java index ecc479871..53c9d77eb 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ResourceServiceImpl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ResourceServiceImpl.java @@ -449,6 +449,16 @@ else if (capabilityClass.equals(RecordingCapability.class)) { resourceSummary.setCalendarUriKey(record[9].toString()); } resourceSummary.setConfirmByOowner((Boolean) record[10]); + String type = record[11].toString(); + resourceSummary.setType(ResourceSummary.Type.valueOf(type.trim())); + if (record[12] != null) { + String recordTags = record[12].toString(); + if (!recordTags.isEmpty()) { + for (String tag : recordTags.split(",")) { + resourceSummary.addTag(tag.trim()); + } + } + } response.addItem(resourceSummary); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ResourceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ResourceController.java new file mode 100644 index 000000000..ade920dea --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ResourceController.java @@ -0,0 +1,138 @@ +package cz.cesnet.shongo.controller.rest.api; + +import cz.cesnet.shongo.api.UserInformation; +import cz.cesnet.shongo.controller.ObjectPermission; +import cz.cesnet.shongo.controller.api.Resource; +import cz.cesnet.shongo.controller.api.ResourceSummary; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.request.ListResponse; +import cz.cesnet.shongo.controller.api.request.ResourceListRequest; +import cz.cesnet.shongo.controller.api.rpc.ResourceService; +import cz.cesnet.shongo.controller.rest.Cache; +import cz.cesnet.shongo.controller.rest.models.TechnologyModel; +import cz.cesnet.shongo.controller.rest.models.resource.*; +import io.swagger.v3.oas.annotations.Operation; +import org.joda.time.DateTime; +import org.joda.time.Interval; +import org.joda.time.Period; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.*; +import java.util.stream.Collectors; + +import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; +import static cz.cesnet.shongo.controller.rest.models.TimeInterval.DATETIME_FORMATTER; + +/** + * Rest controller for resource endpoints. + * + * @author Filip Karnis + */ +@RestController +@RequestMapping("/api/v1/resources") +public class ResourceController { + + private final Cache cache; + private final ResourceService resourceService; + + public ResourceController(@Autowired Cache cache, @Autowired ResourceService resourceService) + { + this.cache = cache; + this.resourceService = resourceService; + } + + /** + * Lists {@link Resource}s. + */ + @Operation(summary = "Lists available resources.") + @GetMapping() + List listResources( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @RequestParam(value = "technology", required = false) TechnologyModel technology, + @RequestParam(value = "tag", required = false) String tag) + { + ResourceListRequest resourceListRequest = new ResourceListRequest(); + resourceListRequest.setSecurityToken(securityToken); + resourceListRequest.setAllocatable(true); + if (technology != null) { + resourceListRequest.setTechnologies(technology.getTechnologies()); + } + if (tag != null) { + resourceListRequest.setTagName(tag); + } + + // Filter only reservable resources + resourceListRequest.setPermission(ObjectPermission.RESERVE_RESOURCE); + + ListResponse accessibleResources = resourceService.listResources(resourceListRequest); + + return accessibleResources.getItems() + .stream() + .map(ResourceModel::new) + // Filter only resources with either technology or tag + .filter(resource -> !(resource.getTechnology() == null && resource.getTags().isEmpty())) + .collect(Collectors.toList()); + } + + /** + * Gets {@link ResourceCapacityUtilization}s. + */ + @Operation(summary = "Returns resource utilization.") + @GetMapping("/utilization") + ResourceCapacityUtilizationModel getResourceUtilization( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @RequestParam(value = "period") Period period, + @RequestParam(value = "start") String startParam, + @RequestParam(value = "end") String endParam, + @RequestParam(value = "refresh", required = false) boolean refresh) + { + DateTime start = DATETIME_FORMATTER.parseDateTime(startParam); + DateTime end = DATETIME_FORMATTER.parseDateTime(endParam); + + ResourcesUtilization resourcesUtilization = cache.getResourcesUtilization(securityToken, refresh); + Map> utilization = + resourcesUtilization.getUtilization(new Interval(start, end), period); + + return new ResourceCapacityUtilizationModel(resourcesUtilization.getResourceCapacities(), utilization); + } + + /** + * Gets {@link ResourceCapacityUtilization}s. + */ + @Operation(summary = "Returns resource utilization.") + @GetMapping("/utilization2") + RModel getResourceUtilization( + @RequestAttribute(TOKEN) SecurityToken securityToken, +// @RequestParam(value = "interval") Interval interval, +// @RequestParam(value = "resourceCapacityClass") String resourceCapacityClassName, + @RequestParam(value = "resourceId") String resourceId) throws ClassNotFoundException + { +// Class resourceCapacityClass = (Class) +// Class.forName(ResourceCapacity.class.getCanonicalName() + "$" + resourceCapacityClassName); + ResourcesUtilization resourcesUtilization = + cache.getResourcesUtilization(securityToken, false); + ResourceCapacity resourceCapacity = +// resourcesUtilization.getResourceCapacity(resourceId, resourceCapacityClass); + resourcesUtilization.getResourceCapacity(resourceId, ResourceCapacity.Room.class); + ResourceCapacityUtilization resourceCapacityUtilization = +// resourcesUtilization.getUtilization(resourceCapacity, interval); + resourcesUtilization.getUtilization(resourceCapacity, new Interval(DateTime.parse("2020-12-09T10:10:30"), DateTime.parse("2023-12-09T10:10:30"))); +// ModelAndView modelAndView = new ModelAndView("resourceCapacityUtilizationDescription"); + + Map users = new HashMap(); + if (resourceCapacityUtilization != null) { + Collection userIds = resourceCapacityUtilization.getReservationUserIds(); + cache.fetchUserInformation(securityToken, userIds); + for (String userId : userIds) { + UserInformation userInformation = cache.getUserInformation(securityToken, userId); + users.put(userId, userInformation); + } + } +// modelAndView.addObject("users", users); +// modelAndView.addObject("interval", interval); +// modelAndView.addObject("resourceCapacity", resourceCapacity); +// modelAndView.addObject("resourceCapacityUtilization", resourceCapacityUtilization); + return new RModel(resourceCapacity, resourceCapacityUtilization); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/RModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/RModel.java new file mode 100644 index 000000000..e2a51af04 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/RModel.java @@ -0,0 +1,12 @@ +package cz.cesnet.shongo.controller.rest.models.resource; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class RModel { + + private ResourceCapacity resourceCapacity; + private ResourceCapacityUtilization resourceCapacityUtilization; +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacity.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacity.java new file mode 100644 index 000000000..13a122206 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacity.java @@ -0,0 +1,269 @@ +package cz.cesnet.shongo.controller.rest.models.resource; + +import cz.cesnet.shongo.TodoImplementException; +import cz.cesnet.shongo.controller.api.RecordingCapability; +import cz.cesnet.shongo.controller.api.ReservationSummary; +import cz.cesnet.shongo.controller.api.ResourceSummary; +import cz.cesnet.shongo.controller.api.RoomProviderCapability; +import org.apache.commons.lang.StringUtils; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Represents a type of capacity which can be utilized in a resource. + * + * Theoretically multiple different capacities can be utilized for a single resource + * (e.g., capacity of room licenses and recording capacity) + * + * @author Martin Srom + */ +public abstract class ResourceCapacity +{ + /** + * {@link ResourceSummary} of the resource in which the capacity is utilized. + */ + protected ResourceSummary resource; + + /** + * Constructor. + * + * @param resource sets the {@link #resource} + */ + public ResourceCapacity(ResourceSummary resource) + { + this.resource = resource; + } + + /** + * @return class name of this type of capacity + */ + public String getClassName() + { + return getClass().getSimpleName(); + } + + /** + * @return {@link ResourceSummary#getId()} for {@link #resource} + */ + public String getResourceId() + { + return resource.getId(); + } + + /** + * @return {@link ResourceSummary#getName()} for {@link #resource} + */ + public String getResourceName() + { + return resource.getName(); + } + + /** + * @return {@link ReservationSummary.Type} of reservations which allocate this resource capacity + * (e.g., capacity of room licenses are allocated by {@link ReservationSummary.Type#ROOM}) + */ + public abstract ReservationSummary.Type getReservationType(); + + /** + * @param utilization + * @return css classes which shall be rendered for given {@code utilization} + */ + public abstract String getCssClass(ResourceCapacityUtilization utilization); + + /** + * @param utilization + * @param t {@link FormatType} which should be used + * @param s {@link FormatStyle} which should be used + * @return html content which shall be rendered for given {@code utilization} + */ + public abstract String formatUtilization(ResourceCapacityUtilization utilization, FormatType t, FormatStyle s); + + /** + * Specifies what utilization should be formatted in {@link #formatUtilization}. + */ + public static enum FormatType + { + /** + * Maximum utilization of resource capacity (e.g., maximum number of utilized license count). + */ + MAXIMUM, + + /** + * Average utilization of resource capacity (e.g., average of all utilizations). + */ + AVERAGE + } + + /** + * Specifies how the utilization value should be formatted in {@link #formatUtilization}. + */ + public static enum FormatStyle + { + /** + * Absolute utilization (e.g., number of licenses). + */ + ABSOLUTE, + + /** + * Percentage utilization (e.g., percentage of utilization). + */ + RELATIVE + } + + /** + * Abstract {@link ResourceCapacity} for types with license count (e.g., recording capacity or room capacity). + */ + protected static abstract class LicenseCount extends ResourceCapacity + { + /** + * Total number of available licenses in the resource. + */ + protected Integer licenseCount; + + /** + * Constructor. + * + * @param resource sets the {@link #resource} + * @param licenseCount sets the {@link #licenseCount} + */ + public LicenseCount(ResourceSummary resource, Integer licenseCount) + { + super(resource); + + this.licenseCount = licenseCount; + } + + /** + * @return {@link #licenseCount} + */ + public Integer getLicenseCount() + { + return licenseCount; + } + + @Override + public String getCssClass(ResourceCapacityUtilization utilization) + { + Set cssClasses = new HashSet(); + int utilizedLicenseCount = getUtilizedLicenseCount(utilization, FormatType.MAXIMUM).intValue(); + int relativeUtilizedLicenseCount = (utilizedLicenseCount * 100) / licenseCount; + if (utilizedLicenseCount > 0) { + cssClasses.add("utilized"); + cssClasses.add("utilized" + ((relativeUtilizedLicenseCount / 10) * 10)); + } + return StringUtils.join(cssClasses, " "); + } + + @Override + public String formatUtilization(ResourceCapacityUtilization utilization, FormatType type, FormatStyle style) + { + Number utilizedLicenseCount = getUtilizedLicenseCount(utilization, type); + StringBuilder output = new StringBuilder(); + switch (style) { + case ABSOLUTE: + String value = String.format("%1$.1f", utilizedLicenseCount.doubleValue()); + if (value.endsWith(".0")) { + value = String.valueOf(utilizedLicenseCount.intValue()); + } + output.append(value); + break; + case RELATIVE: + output.append((int)((utilizedLicenseCount.doubleValue() * 100.0) / (double) licenseCount)); + output.append("%"); + break; + default: + throw new TodoImplementException(style); + } + return output.toString(); + } + + /** + * @param utilization + * @param type + * @return utilized license count for given {@code type} + */ + private Number getUtilizedLicenseCount(ResourceCapacityUtilization utilization, FormatType type) + { + if (utilization != null) { + switch (type) { + case MAXIMUM: + ResourceCapacityBucket peakBucket = utilization.getPeakBucket(); + if (peakBucket != null) { + return peakBucket.getLicenseCount(); + } + else { + return 0; + } + case AVERAGE: + List buckets = utilization.getBuckets(); + double totalLicenseCount = 0; + int bucketCount = 0; + for (ResourceCapacityBucket bucket : buckets) { + if (bucket.isEmpty()) { + continue; + } + totalLicenseCount += bucket.getLicenseCount(); + bucketCount++; + } + if (bucketCount == 0) { + return 0; + } + return totalLicenseCount / (double) bucketCount; + default: + throw new TodoImplementException(type); + } + } + else { + return 0; + } + } + } + + /** + * {@link ResourceCapacity} for resources which provides virtual rooms. + */ + public static class Room extends LicenseCount + { + /** + * Constructor. + * + * @param resource sets the {@link #resource} + * @param capability to be used for determining available license count + */ + public Room(ResourceSummary resource, RoomProviderCapability capability) + { + super(resource, capability.getLicenseCount()); + } + + @Override + public ReservationSummary.Type getReservationType() + { + return ReservationSummary.Type.ROOM; + } + } + + /** + * {@link ResourceCapacity} for resources which provides recording. + */ + public static class Recording extends LicenseCount + { + /** + * Constructor. + * + * @param resource sets the {@link #resource} + * @param capability to be used for determining available license count + */ + public Recording(ResourceSummary resource, RecordingCapability capability) + { + super(resource, capability.getLicenseCount()); + } + + @Override + public ReservationSummary.Type getReservationType() + { + return ReservationSummary.Type.RECORDING_SERVICE; + } + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityBucket.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityBucket.java new file mode 100644 index 000000000..63cd48957 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityBucket.java @@ -0,0 +1,98 @@ +package cz.cesnet.shongo.controller.rest.models.resource; + +import cz.cesnet.shongo.TodoImplementException; +import cz.cesnet.shongo.controller.api.ReservationSummary; +import cz.cesnet.shongo.controller.api.RoomReservation; +import cz.cesnet.shongo.util.RangeSet; +import org.joda.time.DateTime; + +/** + * Represents a special version of {@link RangeSet.Bucket} from which a sum of license count + * for all contained {@link ReservationSummary} can be determined. + * + * @author Martin Srom + */ +public class ResourceCapacityBucket extends RangeSet.Bucket + implements Comparable +{ + /** + * Sum of {@link RoomReservation#getLicenseCount()} + */ + private int licenseCount = 0; + + /** + * Constructor. + * + * @param rangeValue + */ + public ResourceCapacityBucket(DateTime rangeValue) + { + super(rangeValue); + } + + /** + * @return {@link #rangeValue} + */ + public DateTime getDateTime() + { + return getRangeValue(); + } + + /** + * @return {@link #licenseCount} + */ + public int getLicenseCount() + { + return licenseCount; + } + + @Override + public boolean add(ReservationSummary reservation) + { + if (super.add(reservation)) { + switch (reservation.getType()) { + case ROOM: + this.licenseCount += reservation.getRoomLicenseCount(); + break; + case RECORDING_SERVICE: + this.licenseCount++; + break; + default: + throw new TodoImplementException(reservation.getType()); + } + return true; + } + else { + return false; + } + } + + @Override + public boolean remove(Object object) + { + if (super.remove(object)) { + ReservationSummary reservation = (ReservationSummary) object; + switch (reservation.getType()) { + case ROOM: + this.licenseCount -= reservation.getRoomLicenseCount(); + break; + case RECORDING_SERVICE: + this.licenseCount--; + break; + default: + throw new TodoImplementException(reservation.getType()); + } + return true; + } + else { + return false; + } + } + + @Override + public int compareTo(ResourceCapacityBucket bucket) + { + // Bucket with higher utilization should before bucket with lower utilization + return -Double.compare(getLicenseCount(), bucket.getLicenseCount()); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilization.java new file mode 100644 index 000000000..759b09b22 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilization.java @@ -0,0 +1,107 @@ +package cz.cesnet.shongo.controller.rest.models.resource; + +import cz.cesnet.shongo.controller.api.ReservationSummary; +import org.joda.time.Interval; + +import java.util.*; + +/** + * Represents a utilization of {@link ResourceCapacity} for a specific interval. + *

+ * It can be initialized from list of {@link ResourceCapacityBucket}s (which contain all reservations in interval). + * From the buckets we can determine maximum utilization or compute average utilization. + * + * @author Martin Srom + */ +public class ResourceCapacityUtilization +{ + /** + * List of {@link ResourceCapacityBucket}s. Each bucket represents an interval in which {@link ResourceCapacity} is + * utilized in some way. Different buckets means different utilization. Buckets shall be sorted to be able to + * determine maximum utilization. + */ + private List buckets = new LinkedList(); + + /** + * List of {@link ReservationSummary} in all {@link #buckets}. + */ + private List reservations; + + /** + * Constructor. + * + * @param buckets sets the {@link #buckets} + */ + public ResourceCapacityUtilization(Collection buckets) + { + this.buckets.addAll(buckets); + + // Sort buckets (to be able to determine maximum utilization) + Collections.sort(this.buckets); + } + + /** + * @return first {@link ResourceCapacityBucket} from {@link #buckets} with maximum utilization. + */ + public ResourceCapacityBucket getPeakBucket() + { + if (buckets.size() > 0) { + return buckets.get(0); + } + else { + return null; + } + } + + /** + * @return {@link #buckets} + */ + public List getBuckets() + { + return Collections.unmodifiableList(buckets); + } + + /** + * @return {@link #reservations} + */ + public Collection getReservations() + { + if (this.reservations == null) { + Set reservations = new LinkedHashSet(); + for (ResourceCapacityBucket bucket : buckets) { + reservations.addAll(bucket); + } + this.reservations = new LinkedList(); + this.reservations.addAll(reservations); + Collections.sort(this.reservations, new Comparator() + { + @Override + public int compare(ReservationSummary reservation1, ReservationSummary reservation2) + { + Interval reservationSlot1 = reservation1.getSlot(); + Interval reservationSlot2 = reservation2.getSlot(); + + int result = reservationSlot1.getStart().compareTo(reservationSlot2.getStart()); + if (result != 0) { + return result; + } + + return reservationSlot1.getEnd().compareTo(reservationSlot2.getEnd()); + } + }); + } + return Collections.unmodifiableList(this.reservations); + } + + /** + * @return user-ids of {@link #reservations} + */ + public Collection getReservationUserIds() + { + Set userIds = new HashSet(); + for (ReservationSummary reservation : getReservations()) { + userIds.add(reservation.getUserId()); + } + return userIds; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilizationModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilizationModel.java new file mode 100644 index 000000000..74af981ca --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilizationModel.java @@ -0,0 +1,22 @@ +package cz.cesnet.shongo.controller.rest.models.resource; + +import lombok.Data; +import org.joda.time.Interval; + +import java.util.Collection; +import java.util.Map; + +@Data +public class ResourceCapacityUtilizationModel { + + private Collection resourceCapacitySet; + private Map> resourceCapacityUtilization; + + public ResourceCapacityUtilizationModel( + Collection resourceCapacitySet, + Map> resourceCapacityUtilization) + { + this.resourceCapacitySet = resourceCapacitySet; + this.resourceCapacityUtilization = resourceCapacityUtilization; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java new file mode 100644 index 000000000..84033f77f --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java @@ -0,0 +1,27 @@ +package cz.cesnet.shongo.controller.rest.models.resource; + +import cz.cesnet.shongo.controller.api.ResourceSummary; +import cz.cesnet.shongo.controller.rest.models.TechnologyModel; +import lombok.Data; + +import java.util.Set; + +@Data +public class ResourceModel { + + private String id; + private ResourceSummary.Type type; + private String name; + private String description; + private TechnologyModel technology; + private Set tags; + + public ResourceModel(ResourceSummary summary) { + this.id = summary.getId(); + this.type = summary.getType(); + this.name = summary.getName(); + this.description = summary.getDescription(); + this.technology = TechnologyModel.find(summary.getTechnologies()); + this.tags = summary.getTags(); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java new file mode 100644 index 000000000..a81c41442 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java @@ -0,0 +1,367 @@ +package cz.cesnet.shongo.controller.rest.models.resource; + +import cz.cesnet.shongo.TodoImplementException; +import cz.cesnet.shongo.controller.api.*; +import cz.cesnet.shongo.controller.api.request.ReservationListRequest; +import cz.cesnet.shongo.controller.api.request.ResourceListRequest; +import cz.cesnet.shongo.controller.api.rpc.ReservationService; +import cz.cesnet.shongo.controller.api.rpc.ResourceService; +import cz.cesnet.shongo.util.RangeSet; +import org.joda.time.DateTime; +import org.joda.time.Interval; +import org.joda.time.Period; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +/** + * Represents utilization of all types of capacities for all resources to which a single user has access. + * + * @author Martin Srom + */ +public class ResourcesUtilization +{ + private static Logger logger = LoggerFactory.getLogger(ResourcesUtilization.class); + + /** + * {@link SecurityToken} of user to which the {@link ResourcesUtilization} belongs. + */ + private final SecurityToken securityToken; + + /** + * {@link ReservationService} for retrieving reservations. + */ + private final ReservationService reservationService; + + /** + * List of {@link ResourceCapacity} to which the user has access. + */ + private final List resourceCapacities = new LinkedList(); + + /** + * Map of {@link ResourceCapacity} by class and by resource-id. + */ + private final Map, ResourceCapacity>> resourceCapacityMap = + new HashMap, ResourceCapacity>>(); + + /** + * Map of cached {@link ResourceCapacityUtilization} for {@link ResourceCapacity} and for {@link Interval}. + */ + private Map> resourceCapacityUtilizationMap = + new HashMap>(); + + /** + * Map of cached {@link ReservationSummary}s for {@link ResourceCapacity}. + */ + private Map> reservationSetMap = + new HashMap>(); + + /** + * {@link Interval} which is already cached in {@link #reservationSetMap}. + */ + private Interval reservationInterval; + + /** + * Constructor. + * + * @param securityToken sets the {@link #securityToken} + * @param resources to be used for fetching {@link #resourceCapacities} + * @param reservations sets the {@link #reservationService} + */ + public ResourcesUtilization(SecurityToken securityToken, ResourceService resources, ReservationService reservations) + { + this.securityToken = securityToken; + this.reservationService = reservations; + + // Fetch ResourceCapacities for all accessible resources + ResourceListRequest resourceListRequest = new ResourceListRequest(securityToken); + resourceListRequest.setSort(ResourceListRequest.Sort.NAME); + resourceListRequest.addCapabilityClass(RoomProviderCapability.class); + resourceListRequest.addCapabilityClass(RecordingCapability.class); + for (ResourceSummary resourceSummary : resources.listResources(resourceListRequest)) { + String resourceId = resourceSummary.getId(); + Resource resource = resources.getResource(securityToken, resourceId); + for (Capability capability : resource.getCapabilities()) { + if (capability instanceof RoomProviderCapability) { + RoomProviderCapability roomProviderCapability = (RoomProviderCapability) capability; + addResourceCapacity(new ResourceCapacity.Room(resourceSummary, roomProviderCapability)); + } + else if (capability instanceof RecordingCapability) { + RecordingCapability recordingCapability = (RecordingCapability) capability; + if (recordingCapability.getLicenseCount() != null) { + addResourceCapacity(new ResourceCapacity.Recording(resourceSummary, recordingCapability)); + } + } + } + } + } + + /** + * @return {@link #resourceCapacities} + */ + public Collection getResourceCapacities() + { + return Collections.unmodifiableList(resourceCapacities); + } + + /** + * @param resourceId + * @param capacityClass + * @return {@link ResourceCapacity} for given {@code resourceId} and {@code capacityClass} + */ + public ResourceCapacity getResourceCapacity(String resourceId, Class capacityClass) + { + Map, ResourceCapacity> resourceCapacitiesByClass = + resourceCapacityMap.get(resourceId); + if (resourceCapacitiesByClass == null) { + return null; + } + return resourceCapacitiesByClass.get(capacityClass); + } + + /** + * @param interval interval to be returned + * @param period by which the {@code interval} should be split and for each part should be {@link ResourceCapacityUtilization} computed + * @return map of {@link ResourceCapacityUtilization} by {@link ResourceCapacity}s and by {@link Interval}s + */ + public Map> getUtilization(Interval interval, + Period period) + { + Map> utilizationsByInterval = + new LinkedHashMap>(); + DateTime start = interval.getStart(); + DateTime maxEnd = interval.getEnd(); + while (start.isBefore(maxEnd)) { + DateTime end = start.plus(period); + if (end.isAfter(maxEnd)) { + end = maxEnd; + } + Interval utilizationInterval = new Interval(start, end); + Map utilizations = + new HashMap(); + for (ResourceCapacity resourceCapacity : resourceCapacities) { + ResourceCapacityUtilization utilization = + getUtilization(resourceCapacity, utilizationInterval, true, interval); + if (utilization != null) { + utilizations.put(resourceCapacity, utilization); + } + } + utilizationsByInterval.put(utilizationInterval, utilizations); + start = end; + } + return utilizationsByInterval; + } + + /** + * @param resourceCapacity + * @param interval + * @return {@link ResourceCapacityUtilization} for given {@code resourceCapacity} and {@code interval} + */ + public ResourceCapacityUtilization getUtilization(ResourceCapacity resourceCapacity, Interval interval) + { + return getUtilization(resourceCapacity, interval, false, interval); + } + + /** + * @param resourceCapacity to be added to the {@link #resourceCapacities} and {@link #resourceCapacityMap} + */ + private void addResourceCapacity(ResourceCapacity resourceCapacity) + { + if (resourceCapacities.add(resourceCapacity)) { + String resourceId = resourceCapacity.getResourceId(); + Class resourceCapacityClass = resourceCapacity.getClass(); + + Map, ResourceCapacity> resourceCapacitiesByClass = + resourceCapacityMap.get(resourceId); + if (resourceCapacitiesByClass == null) { + resourceCapacitiesByClass = new HashMap, ResourceCapacity>(); + resourceCapacityMap.put(resourceId, resourceCapacitiesByClass); + } + resourceCapacitiesByClass.put(resourceCapacityClass, resourceCapacity); + } + } + + /** + * Can be used for bulk fetching of {@link ReservationSummary}s by use {@code fetchAll} and {@code fetchInterval}. + * + * @param resourceCapacity + * @param interval + * @param fetchAll + * @param fetchInterval + * @return {@link ResourceCapacityUtilization} for given {@code resourceCapacity} and {@code interval} + */ + private ResourceCapacityUtilization getUtilization(ResourceCapacity resourceCapacity, Interval interval, + boolean fetchAll, Interval fetchInterval) + { + // Try to return cached utilization + Map utilizations = resourceCapacityUtilizationMap.get(interval); + if (utilizations == null) { + utilizations = new HashMap(); + resourceCapacityUtilizationMap.put(interval, utilizations); + } + if (utilizations.containsKey(resourceCapacity)) { + return utilizations.get(resourceCapacity); + } + + // Fetch reservations + RangeSet reservations; + if (fetchAll) { + reservations = getReservationsWithFetchAll(resourceCapacity, fetchInterval); + } + else { + reservations = getReservations(resourceCapacity, fetchInterval); + } + + // Prepare new utilization + ResourceCapacityUtilization utilization = null; + if (reservations != null) { + Collection buckets = + reservations.getBuckets(interval.getStart(), interval.getEnd(), ResourceCapacityBucket.class); + if (buckets.size() > 0) { + utilization = new ResourceCapacityUtilization(buckets); + } + } + + // Store the utilization to cache and return it + utilizations.put(resourceCapacity, utilization); + return utilization; + } + + /** + * Get {@link RangeSet} for given {@code resourceCapacity} + * by fetching {@link ReservationSummary}s for all {@link #resourceCapacities}. + *

+ * The newly fetched {@link ReservationSummary}s will stored in {@link #reservationSetMap} + * (because all {@link ResourceCapacity}s will be updated). + * + * @param resourceCapacity + * @param interval + * @return {@link RangeSet} of {@link ReservationSummary} + */ + private synchronized RangeSet getReservationsWithFetchAll( + ResourceCapacity resourceCapacity, Interval interval) + { + // Try to return cached reservations + if (this.reservationInterval != null && this.reservationInterval.contains(interval)) { + return this.reservationSetMap.get(resourceCapacity); + } + DateTime start = interval.getStart(); + DateTime end = interval.getEnd(); + + // Expand reservation cache at start + if (reservationInterval != null && + reservationInterval.isAfter(start) && reservationInterval.contains(end)) { + interval = new Interval(start, reservationInterval.getStart()); + reservationInterval = new Interval(start, reservationInterval.getEnd()); + } + // Expand reservation cache at end + else if (reservationInterval != null && + reservationInterval.isBefore(end) && reservationInterval.contains(start)) { + interval = new Interval(reservationInterval.getEnd(), end); + reservationInterval = new Interval(reservationInterval.getStart(), end); + } + // Load the whole reservation cache + else { + logger.info("Clearing cached reservations..."); + reservationSetMap.clear(); + reservationInterval = interval; + } + + // Fetch reservations for all resource capacities + logger.info("Loading reservations for {}...", interval); + ReservationListRequest reservationListRequest = new ReservationListRequest(securityToken); + for (ResourceCapacity currentResourceCapacity : resourceCapacities) { + reservationListRequest.addResourceId(currentResourceCapacity.getResourceId()); + } + reservationListRequest.addReservationType(ReservationSummary.Type.ROOM); + reservationListRequest.addReservationType(ReservationSummary.Type.RECORDING_SERVICE); + reservationListRequest.setInterval(interval); + for (ReservationSummary reservation : reservationService.listReservations(reservationListRequest)) { + Interval reservationSlot = reservation.getSlot(); + String reservationResourceId = reservation.getResourceId(); + ResourceCapacity reservationResourceCapacity = getResourceCapacity(reservationResourceId, reservation); + RangeSet reservationSet = reservationSetMap.get(reservationResourceCapacity); + if (reservationSet == null) { + reservationSet = new RangeSet() + { + @Override + protected Bucket createBucket(DateTime rangeValue) + { + return new ResourceCapacityBucket(rangeValue); + } + }; + reservationSetMap.put(reservationResourceCapacity, reservationSet); + } + reservationSet.add(reservation, reservationSlot.getStart(), reservationSlot.getEnd()); + } + return reservationSetMap.get(resourceCapacity); + } + + /** + * Get {@link RangeSet} for given {@code resourceCapacity} + * by fetching {@link ReservationSummary}s only for given {@code resourceCapacity}. + *

+ * The newly fetched {@link ReservationSummary}s won't be stored in {@link #reservationSetMap} + * (because all {@link ResourceCapacity} won't be updated). + * + * @param resourceCapacity + * @param interval + * @return {@link RangeSet} of {@link ReservationSummary} + */ + private RangeSet getReservations(ResourceCapacity resourceCapacity, + Interval interval) + { + // Try to return cached reservations + synchronized (this) { + if (this.reservationInterval != null && this.reservationInterval.contains(interval)) { + return this.reservationSetMap.get(resourceCapacity); + } + } + + // Fetch reservations for single resource capacity + RangeSet reservationSet = new RangeSet() + { + @Override + protected Bucket createBucket(DateTime rangeValue) + { + return new ResourceCapacityBucket(rangeValue); + } + }; + ReservationListRequest reservationListRequest = new ReservationListRequest(securityToken); + reservationListRequest.addResourceId(resourceCapacity.getResourceId()); + reservationListRequest.addReservationType(resourceCapacity.getReservationType()); + reservationListRequest.setInterval(interval); + for (ReservationSummary reservation : reservationService.listReservations(reservationListRequest)) { + Interval reservationSlot = reservation.getSlot(); + reservationSet.add(reservation, reservationSlot.getStart(), reservationSlot.getEnd()); + } + return reservationSet; + } + + /** + * @param resourceId + * @param reservation + * @return {@link ResourceCapacity} for given {@code resourceId} and {@code reservation} + */ + private ResourceCapacity getResourceCapacity(String resourceId, ReservationSummary reservation) + { + return getResourceCapacity(resourceId, getResourceCapacityClass(reservation.getType())); + } + + /** + * @param reservationType + * @return class of {@link ResourceCapacity} for given {@code reservationType} + */ + private Class getResourceCapacityClass(ReservationSummary.Type reservationType) + { + switch (reservationType) { + case ROOM: + return ResourceCapacity.Room.class; + case RECORDING_SERVICE: + return ResourceCapacity.Recording.class; + default: + throw new TodoImplementException(reservationType); + } + } +} diff --git a/shongo-controller/src/main/resources/sql/resource_list.sql b/shongo-controller/src/main/resources/sql/resource_list.sql index 3144861ac..5d223aec9 100644 --- a/shongo-controller/src/main/resources/sql/resource_list.sql +++ b/shongo-controller/src/main/resources/sql/resource_list.sql @@ -2,6 +2,7 @@ * Select query for list of resources. * * @author Martin Srom + * @author Filip Karnis */ SELECT resource_summary.id AS id, @@ -14,7 +15,9 @@ SELECT resource_summary.description AS description, resource_summary.calendar_public AS calendar_public, resource_summary.calendar_uri_key AS calendar_uri_key, - resource_summary.confirm_by_owner AS confirm_by_owner + resource_summary.confirm_by_owner AS confirm_by_owner, + resource_summary.type AS type, + resource_summary.tag_names AS tag_names FROM resource_summary WHERE ${filter} ORDER BY ${order} \ No newline at end of file From 9a633af08c31200019e0061b292edd0d2e53ccca Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 7 Apr 2022 01:31:17 +0200 Subject: [PATCH 017/134] Added reservation request create model --- .../api/ReservationRequestController.java | 125 +- .../reservationrequest/PeriodicityModel.java | 48 + .../rest/models/reservationrequest/RRR.java | 1814 +++++++++++++++++ 3 files changed, 1965 insertions(+), 22 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PeriodicityModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java index 1e8a2662b..7f8684d10 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java @@ -3,28 +3,52 @@ import cz.cesnet.shongo.Temporal; import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.ObjectPermission; -import cz.cesnet.shongo.controller.api.*; +import cz.cesnet.shongo.controller.ObjectRole; +import cz.cesnet.shongo.controller.api.AbstractRoomExecutable; +import cz.cesnet.shongo.controller.api.AllocationState; +import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.api.ResourceSummary; +import cz.cesnet.shongo.controller.api.SecurityToken; import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.request.ReservationRequestListRequest; +import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.api.rpc.ReservationService; import cz.cesnet.shongo.controller.rest.Cache; -import cz.cesnet.shongo.controller.rest.models.reservationrequest.*; +import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; +import cz.cesnet.shongo.controller.rest.models.reservationrequest.RRR; +import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestDetailModel; +import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestHistoryModel; +import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestModel; +import cz.cesnet.shongo.controller.rest.models.reservationrequest.SpecificationType; +import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; import cz.cesnet.shongo.controller.rest.models.room.RoomAuthorizedData; import io.swagger.v3.oas.annotations.Operation; +import org.joda.time.DateTime; import org.joda.time.Interval; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; -import org.joda.time.DateTime; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import static cz.cesnet.shongo.controller.api.ResourceSummary.Type.RESOURCE; +import static cz.cesnet.shongo.controller.api.ResourceSummary.Type.ROOM_PROVIDER; import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; +import static cz.cesnet.shongo.controller.rest.models.TimeInterval.DATETIME_FORMATTER; /** * Rest controller for reservation request endpoints. @@ -37,15 +61,18 @@ public class ReservationRequestController { private final Cache cache; private final ReservationService reservationService; + private final AuthorizationService authorizationService; private final ExecutableService executableService; public ReservationRequestController( @Autowired Cache cache, @Autowired ReservationService reservationService, + @Autowired AuthorizationService authorizationService, @Autowired ExecutableService executableService) { this.cache = cache; this.reservationService = reservationService; + this.authorizationService = authorizationService; this.executableService = executableService; } @@ -60,13 +87,14 @@ ListResponse listRequests( @RequestParam(value = "sort_desc", required = false, defaultValue = "true") boolean sortDescending, @RequestParam(value = "allocation_state", required = false) AllocationState allocationState, @RequestParam(value = "parentRequestId", required = false) String permanentRoomId, - @RequestParam(value = "specification_type", required = false) Set specificationTypes, @RequestParam(value = "technology", required = false) TechnologyModel technology, - @RequestParam(value = "interval_from", required = false) DateTime intervalFrom, - @RequestParam(value = "interval_to", required = false) DateTime intervalTo, + @RequestParam(value = "interval_from", required = false) String fromParam, + @RequestParam(value = "interval_to", required = false) String toParam, @RequestParam(value = "user_id", required = false) String userId, @RequestParam(value = "participant_user_id", required = false) String participantUserId, - @RequestParam(value = "search", required = false) String search) + @RequestParam(value = "search", required = false) String search, + @RequestParam(value = "type", required = false) Set reservationTypes, + @RequestParam(value = "resource", required = false) String resourceId) { ReservationRequestListRequest request = new ReservationRequestListRequest(); @@ -78,29 +106,39 @@ ListResponse listRequests( request.setAllocationState(allocationState); request.setParticipantUserId(participantUserId); request.setSearch(search); + request.setSpecificationResourceId(resourceId); + if (reservationTypes == null) { + reservationTypes = new HashSet<>(); + } if (permanentRoomId != null) { request.setReusedReservationRequestId(permanentRoomId); - specificationTypes.add(SpecificationType.PERMANENT_ROOM_CAPACITY); + reservationTypes.add(ReservationType.ROOM_CAPACITY); } - if (specificationTypes != null && !specificationTypes.isEmpty()) { - if (specificationTypes.contains(SpecificationType.ADHOC_ROOM)) { - request.addSpecificationType(ReservationRequestSummary.SpecificationType.ROOM); - } - if (specificationTypes.contains(SpecificationType.PERMANENT_ROOM)) { - request.addSpecificationType(ReservationRequestSummary.SpecificationType.PERMANENT_ROOM); - } - if (specificationTypes.contains(SpecificationType.PERMANENT_ROOM_CAPACITY)) { - request.addSpecificationType(ReservationRequestSummary.SpecificationType.USED_ROOM); - } - if (specificationTypes.contains(SpecificationType.MEETING_ROOM)) { - request.addSpecificationType(ReservationRequestSummary.SpecificationType.RESOURCE); - } + if (reservationTypes.contains(ReservationType.VIRTUAL_ROOM)) { + request.addSpecificationType(ReservationRequestSummary.SpecificationType.ROOM); + request.addSpecificationType(ReservationRequestSummary.SpecificationType.PERMANENT_ROOM); + } + if (reservationTypes.contains(ReservationType.ROOM_CAPACITY)) { + request.addSpecificationType(ReservationRequestSummary.SpecificationType.USED_ROOM); + } + if (reservationTypes.contains(ReservationType.PHYSICAL_RESOURCE)) { + request.addSpecificationType(ReservationRequestSummary.SpecificationType.RESOURCE); } + if (technology != null) { request.setSpecificationTechnologies(technology.getTechnologies()); } + + DateTime intervalFrom = null; + DateTime intervalTo = null; + if (fromParam != null) { + intervalFrom = DATETIME_FORMATTER.parseDateTime(fromParam); + } + if (toParam != null) { + intervalTo = DATETIME_FORMATTER.parseDateTime(toParam); + } if (intervalFrom != null || intervalTo != null) { if (intervalFrom == null) { intervalFrom = Temporal.DATETIME_INFINITY_START; @@ -130,6 +168,43 @@ ListResponse listRequests( return listResponse; } + @Operation(summary = "Creates reservation request.") + @PostMapping() + void createRequest( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @RequestBody RRR request) + { + CacheProvider cacheProvider = new CacheProvider(cache, securityToken); + UserInformation userInformation = securityToken.getUserInformation(); + + request.setCacheProvider(cacheProvider); + String resource = request.getResource(); + if (resource == null) { + request.setSpecificationType(SpecificationType.PERMANENT_ROOM_CAPACITY); + reservationService.createReservationRequest(securityToken, request.toApi()); + return; + } + ResourceSummary resourceSummary = cacheProvider.getResourceSummary(resource); + if (resourceSummary.getType() == ROOM_PROVIDER) { + request.setTechnology(TechnologyModel.find(resourceSummary.getTechnologies())); + request.setRoomResourceId(resource); + // TODO get SpecificationType from resource + request.setSpecificationType(SpecificationType.PERMANENT_ROOM); + // Add default participant + request.addRoomParticipant(userInformation, request.getDefaultOwnerParticipantRole()); + } else if (resourceSummary.getType() == RESOURCE) { + request.setPhysicalResourceId(resource); + request.setSpecificationType(SpecificationType.MEETING_ROOM); + } + + String reservationId = reservationService.createReservationRequest(securityToken, request.toApi()); + + // Create default role for the user + request.setId(reservationId); + UserRoleModel userRoleModel = request.addUserRole(userInformation, ObjectRole.OWNER); + authorizationService.createAclEntry(securityToken, userRoleModel.toApi()); + } + @Operation(summary = "Returns reservation request.") @GetMapping("/{id:.+}") ReservationRequestDetailModel getRequest( @@ -198,4 +273,10 @@ void revertRequest( { reservationService.revertReservationRequest(securityToken, id); } + + public enum ReservationType { + VIRTUAL_ROOM, + PHYSICAL_RESOURCE, + ROOM_CAPACITY + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PeriodicityModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PeriodicityModel.java new file mode 100644 index 000000000..0610626a0 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PeriodicityModel.java @@ -0,0 +1,48 @@ +package cz.cesnet.shongo.controller.rest.models.reservationrequest; + +import cz.cesnet.shongo.controller.api.PeriodicDateTimeSlot; +import lombok.Data; +import org.joda.time.LocalDate; + +import java.util.List; + +/** + * Represents periodicity of reservation. + * + * @author Filip Karnis + */ +@Data +public class PeriodicityModel { + + /** + * Type of the period + */ + private PeriodicDateTimeSlot.PeriodicityType type; + + /** + * Cycle of the period + */ + private int periodicityCycle; + + /** + * Days of the period for weekly period + */ + private PeriodicDateTimeSlot.DayOfWeek[] periodicDaysInWeek; + + /** + * End of the period + */ + private LocalDate periodicityEnd; + + /** + * Dates excluded from period + */ + private List excludeDates; + + /** + * Periodicity parameters for specific day in month (e.g. 2. friday in a month) + */ + private PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType monthPeriodicityType; + private PeriodicDateTimeSlot.DayOfWeek periodicityDayInMonth; + private int periodicityDayOrder; +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java new file mode 100644 index 000000000..185cfdfaa --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java @@ -0,0 +1,1814 @@ +package cz.cesnet.shongo.controller.rest.models.reservationrequest; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.google.common.base.Strings; +import cz.cesnet.shongo.AliasType; +import cz.cesnet.shongo.ParticipantRole; +import cz.cesnet.shongo.Temporal; +import cz.cesnet.shongo.TodoImplementException; +import cz.cesnet.shongo.api.*; +import cz.cesnet.shongo.controller.ObjectPermission; +import cz.cesnet.shongo.controller.ObjectRole; +import cz.cesnet.shongo.controller.ReservationRequestPurpose; +import cz.cesnet.shongo.controller.ReservationRequestReusement; +import cz.cesnet.shongo.controller.api.ReservationRequest; +import cz.cesnet.shongo.controller.api.*; +import cz.cesnet.shongo.controller.api.request.AclEntryListRequest; +import cz.cesnet.shongo.controller.api.request.ListResponse; +import cz.cesnet.shongo.controller.api.request.ReservationRequestListRequest; +import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; +import cz.cesnet.shongo.controller.api.rpc.ReservationService; +import cz.cesnet.shongo.controller.rest.Cache; +import cz.cesnet.shongo.controller.rest.CacheProvider; +import cz.cesnet.shongo.controller.rest.models.TechnologyModel; +import cz.cesnet.shongo.controller.rest.models.TimeInterval; +import cz.cesnet.shongo.controller.rest.models.UnsupportedApiException; +import cz.cesnet.shongo.controller.rest.models.detail.ParticipantModel; +import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; +import cz.cesnet.shongo.controller.rest.models.users.SettingsModel; +import cz.cesnet.shongo.util.SlotHelper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; +import org.joda.time.*; + +import java.util.*; + +/** + * Model for {@link AbstractReservationRequest}. + * + * @author Martin Srom + * @author Filip Karnis + */ +@Slf4j +public class RRR //implements ReportModel.ContextSerializable +{ + + @JsonIgnore + private CacheProvider cacheProvider; + + protected String id; + + protected String parentReservationRequestId; + + protected ReservationRequestType type; + + protected String description; + + protected ReservationRequestPurpose purpose = ReservationRequestPurpose.USER; + + protected DateTime dateTime; + + protected TechnologyModel technology; + + protected DateTimeZone timeZone = DateTimeZone.UTC; + + protected Integer durationCount; + + protected DurationType durationType; + + protected int slotBeforeMinutes; + + protected int slotAfterMinutes; + + protected PeriodicityModel periodicity; + + /** + * Exclude date from #excludeDates for highligting in user GUI + */ + protected LocalDate removedReservationDate; + + protected SpecificationType specificationType; + + protected String roomName; + + protected String e164Number; + + protected String roomReservationRequestId; + + protected ReservationRequestSummary permanentRoomReservationRequest; + + protected String roomResourceId; + + /** + * TODO:MR + * Temporary for meeting rooms, + */ + protected String physicalResourceId; + + protected Integer participantCount; + + protected String roomPin; + + protected String adminPin; + + protected String guestPin; + + protected boolean roomRecorded; + + protected String roomRecordingResourceId; + + protected AdobeConnectPermissions roomAccessMode; + + protected List userRoles = new LinkedList<>(); + + protected List roomParticipants = new LinkedList<>(); + + protected boolean roomParticipantNotificationEnabled = false; + + protected String roomMeetingName; + + protected String roomMeetingDescription; + + protected Interval collidingInterval; + + protected boolean collidingWithFirstSlot = false; + + protected boolean allowGuests = false; + + protected String resource; + + private TimeInterval slot; + + public TimeInterval getSlot() + { + return slot; + } + + public void setSlot(TimeInterval slot) + { + this.slot = slot; + } + + /** + * Create new {@link ReservationRequestModel} from scratch. + */ + public RRR() + { + periodicity = new PeriodicityModel(); + periodicity.setType(PeriodicDateTimeSlot.PeriodicityType.NONE); + periodicity.setPeriodicityCycle(1); + slot = new TimeInterval(); + slot.setStart(Temporal.roundDateTimeToMinutes(DateTime.now(), 1)); + } + + /** + * Create new {@link ReservationRequestModel} from scratch. + */ + public RRR(CacheProvider cacheProvider) + { + this(); + this.cacheProvider = cacheProvider; + } + + /** + * Create new {@link ReservationRequestModel} from scratch. + */ + public RRR(CacheProvider cacheProvider, SettingsModel userSettingsModel) + { + this(cacheProvider); + initByUserSettings(userSettingsModel); + } + + /** + * Create new {@link ReservationRequestModel} from existing {@code reservationRequest}. + * + * @param reservationRequest + */ + public RRR(AbstractReservationRequest reservationRequest, CacheProvider cacheProvider) + { + this.cacheProvider = cacheProvider; + fromApi(reservationRequest, cacheProvider); + + // Load permanent room + if (specificationType.equals(SpecificationType.PERMANENT_ROOM_CAPACITY) && cacheProvider != null) { + loadPermanentRoom(cacheProvider); + } + } + + /** + * @param userSettingsModel to be inited from + */ + public void initByUserSettings(SettingsModel userSettingsModel) + { + // TODO +// if (userSettingsModel.getSlotBefore() != null) { +// setSlotBeforeMinutes(userSettingsModel.getSlotBefore()); +// } +// if (userSettingsModel.getSlotAfter() != null) { +// setSlotAfterMinutes(userSettingsModel.getSlotAfter()); +// } + } + +// /** +// * @return this as {@link ReservationRequestDetailModel} +// */ +// public ReservationRequestDetailModel getDetail() +// { +// if (this instanceof ReservationRequestDetailModel) { +// return (ReservationRequestDetailModel) this; +// } +// return null; +// } +// +// /** +// * @return this as {@link ReservationRequestModificationModel} +// */ +// public ReservationRequestModificationModel getModification() +// { +// if (this instanceof ReservationRequestModificationModel) { +// return (ReservationRequestModificationModel) this; +// } +// return null; +// } + + public void setCacheProvider(CacheProvider cacheProvider) + { + this.cacheProvider = cacheProvider; + } + + public PeriodicityModel getPeriodicity() + { + return periodicity; + } + + public void setPeriodicity(PeriodicityModel periodicity) + { + this.periodicity = periodicity; + } + + public String getResource() + { + return resource; + } + + public void setResource(String resource) + { + this.resource = resource; + } + + public String getId() + { + return id; + } + + public void setId(String id) + { + this.id = id; + } + + public String getParentReservationRequestId() + { + return parentReservationRequestId; + } + + public ReservationRequestType getType() + { + return type; + } + + public void setType(ReservationRequestType type) + { + this.type = type; + } + + public DateTime getDateTime() + { + return dateTime; + } + + public String getDescription() + { + return description; + } + + public ReservationRequestPurpose getPurpose() { + return purpose; + } + + public void setPurpose(ReservationRequestPurpose purpose) { + this.purpose = purpose; + } + + public void setDescription(String description) + { + this.description = description; + } + + public TechnologyModel getTechnology() + { + return technology; + } + + public void setTechnology(TechnologyModel technology) + { + this.technology = technology; + } + + public DateTimeZone getTimeZone() + { + return timeZone; + } + + public void setTimeZone(DateTimeZone timeZone) + { + this.timeZone = timeZone; + } + + public LocalTime getStart() + { + return slot.getStart().toLocalTime(); + } + + public DateTime getRequestStart() { + return slot.getStart(); + } + + public DateTime getEnd() + { + return slot.getEnd(); + } + + public void setEnd(DateTime end) + { + slot.setEnd(end); + } + + public Integer getDurationCount() + { + return durationCount; + } + + public void setDurationCount(Integer durationCount) + { + this.durationCount = durationCount; + } + + public DurationType getDurationType() + { + return durationType; + } + + public void setDurationType(DurationType durationType) + { + this.durationType = durationType; + } + + public int getSlotBeforeMinutes() + { + return slotBeforeMinutes; + } + + public String getPhysicalResourceId() { + return physicalResourceId; + } + + public void setPhysicalResourceId(String physicalResourceId) { + this.physicalResourceId = physicalResourceId; + } + + public String getAdminPin() + { + return adminPin; + } + + public void setAdminPin(String adminPin) + { + this.adminPin = adminPin; + } + + public String getGuestPin() { + return guestPin; + } + + public void setGuestPin(String guestPin) { + this.guestPin = guestPin; + } + + public Interval getCollidingInterval() { + return collidingInterval; + } + + public void setCollidingInterval(Interval collidingInterval) { + this.collidingInterval = collidingInterval; + + collidingWithFirstSlot = false; + DateTime start = getRequestStart(); + if (getEnd() == null) { + switch (this.durationType) { + case MINUTE: + collidingWithFirstSlot = SlotHelper.areIntervalsColliding(start,this.durationCount,0,0,collidingInterval); + break; + case HOUR: + collidingWithFirstSlot = SlotHelper.areIntervalsColliding(start,0,this.durationCount,0,collidingInterval); + break; + case DAY: + collidingWithFirstSlot = SlotHelper.areIntervalsColliding(start,0,0,this.durationCount,collidingInterval); + break; + default: + return; + } + } else { + collidingWithFirstSlot = SlotHelper.areIntervalsColliding(start,getEnd(),collidingInterval); + } + } + + public void resetCollidingInterval() { + collidingInterval = null; + collidingWithFirstSlot = false; + } + + public void setCollidingWithFirstSlot(boolean collidingWithFirstSlot) { + this.collidingWithFirstSlot = collidingWithFirstSlot; + } + + public boolean isCollidingWithFirstSlot() { + return collidingWithFirstSlot; + } + + public String getMeetingRoomResourceName() + { + if (physicalResourceId != null) { + ResourceSummary resource = cacheProvider.getResourceSummary(physicalResourceId); + if (resource != null) { + return resource.getName(); + } + } + return null; + } + + public String getMeetingRoomResourceDescription() + { + ResourceSummary resource = cacheProvider.getResourceSummary(physicalResourceId); + if (resource != null) { + return resource.getDescription(); + } + return null; + } + + public String getMeetingRoomResourceDomain() + { + if (physicalResourceId != null) { + ResourceSummary resource = cacheProvider.getResourceSummary(physicalResourceId); + if (resource != null) { + return resource.getDomainName(); + } + } + return null; + } + + public Period getSlotBefore() + { + if (slotBeforeMinutes != 0) { + return Period.minutes(slotBeforeMinutes); + } + else { + return null; + } + } + + public void setSlotBeforeMinutes(int slotBeforeMinutes) + { + this.slotBeforeMinutes = slotBeforeMinutes; + } + + public int getSlotAfterMinutes() + { + return slotAfterMinutes; + } + + public Period getSlotAfter() + { + if (slotAfterMinutes != 0) { + return Period.minutes(slotAfterMinutes); + } + else { + return null; + } + } + + public void setSlotAfterMinutes(int slotAfterMinutes) + { + this.slotAfterMinutes = slotAfterMinutes; + } + + public LocalDate getStartDate() { + return slot.getStart().toLocalDate(); + } + + public void setStartDate(LocalDate startDate) { + this.slot.setStart(startDate.toDateTimeAtStartOfDay()); + } + + public SpecificationType getSpecificationType() + { + return specificationType; + } + + public void setSpecificationType(SpecificationType specificationType) + { + this.specificationType = specificationType; + } + + public String getRoomName() + { + return roomName; + } + + public void setRoomName(String roomName) + { + this.roomName = roomName; + } + + public String getE164Number() + { + return e164Number; + } + + public void setE164Number(String e164Number) + { + this.e164Number = e164Number; + } + + public String getRoomReservationRequestId() + { + return roomReservationRequestId; + } + + public void setRoomReservationRequestId(String roomReservationRequestId) + { + if (roomReservationRequestId == null + || !roomReservationRequestId.equals(this.roomReservationRequestId)) { + this.permanentRoomReservationRequest = null; + } + this.roomReservationRequestId = roomReservationRequestId; + } + + public void setPermanentRoomReservationRequestId(String permanentRoomReservationRequestId, + List permanentRooms) + { + this.roomReservationRequestId = permanentRoomReservationRequestId; + + if (permanentRoomReservationRequestId != null && getStart() != null) { + for (ReservationRequestSummary permanentRoomSummary : permanentRooms) { + if (permanentRoomSummary.getId().equals(permanentRoomReservationRequestId)) { + Reservation reservation = cacheProvider.getReservation(permanentRoomSummary.getLastReservationId()); + Interval permanentRoomSlot = reservation.getSlot(); + DateTime permanentRoomStart = permanentRoomSlot.getStart().plus(getSlotBefore()); + permanentRoomStart = Temporal.roundDateTimeToMinutes(permanentRoomStart, 1); + if (getRequestStart().isBefore(permanentRoomStart)) { + setStartDate(permanentRoomStart.toLocalDate()); + } + break; + } + } + } + } + + public ReservationRequestSummary getPermanentRoomReservationRequest() + { + return permanentRoomReservationRequest; + } + + public String getRoomResourceId() + { + return roomResourceId; + } + + public String getRoomResourceName() + { + ResourceSummary resource = cacheProvider.getResourceSummary(roomResourceId); + if (resource != null) { + return resource.getName(); + } + return null; + } + + public void setRoomResourceId(String roomResourceId) + { + this.roomResourceId = roomResourceId; + } + + public Integer getParticipantCount() + { + return participantCount; + } + + public void setParticipantCount(Integer participantCount) + { + this.participantCount = participantCount; + } + + public String getRoomPin() + { + return roomPin; + } + + public void setRoomPin(String roomPin) + { + this.roomPin = roomPin; + } + + public boolean isRoomRecorded() + { + return roomRecorded; + } + + public void setRoomRecorded(boolean roomRecorded) + { + this.roomRecorded = roomRecorded; + } + + public String getRoomRecordingResourceId() + { + return roomRecordingResourceId; + } + + public String getRoomRecordingResourceName() + { + ResourceSummary resource = cacheProvider.getResourceSummary(roomRecordingResourceId); + if (resource != null) { + return resource.getName(); + } + else { + return null; + } + } + + public void setRoomRecordingResourceId(String roomRecordingResourceId) + { + this.roomRecordingResourceId = roomRecordingResourceId; + } + + public AdobeConnectPermissions getRoomAccessMode() + { + return roomAccessMode; + } + + public void setRoomAccessMode(AdobeConnectPermissions roomAccessMode) + { + AdobeConnectPermissions.checkIfUsableByMeetings(roomAccessMode); + this.roomAccessMode = roomAccessMode; + } + + public List getUserRoles() + { + return userRoles; + } + + public UserRoleModel getUserRole(String userRoleId) + { + if (userRoleId == null) { + return null; + } + for (UserRoleModel userRole : userRoles) { + if (userRoleId.equals(userRole.getId())) { + return userRole; + } + } + return null; + } + + public UserRoleModel addUserRole(UserInformation userInformation, ObjectRole objectRole) + { + UserRoleModel userRole = new UserRoleModel(userInformation); + userRole.setObjectId(id); + userRole.setRole(objectRole); + userRoles.add(userRole); + return userRole; + } + + public void addUserRole(UserRoleModel userRole) + { + userRoles.add(userRole); + } + + public void removeUserRole(UserRoleModel userRole) + { + userRoles.remove(userRole); + } + + public boolean getAllowGuests() { + return allowGuests; + } + + public void setAllowGuests(boolean allowGuests) { + this.allowGuests = allowGuests; + } + + public List getRoomParticipants() + { + return roomParticipants; + } + + public void addRoomParticipant(ParticipantModel participantModel) + { + if (ParticipantModel.Type.USER.equals(participantModel.getType())) { + String userId = participantModel.getUserId(); + for (ParticipantModel existingParticipant : roomParticipants) { + String existingUserId = existingParticipant.getUserId(); + ParticipantModel.Type existingType = existingParticipant.getType(); + if (existingType.equals(ParticipantModel.Type.USER) && existingUserId.equals(userId)) { + ParticipantRole existingRole = existingParticipant.getRole(); + if (existingRole.compareTo(participantModel.getRole()) >= 0) { + log.warn("Skip adding {} because {} already exists.", participantModel, existingParticipant); + return; + } + else { + log.warn("Removing {} because {} will be added.", existingParticipant, participantModel); + roomParticipants.remove(existingParticipant); + } + break; + } + } + } + roomParticipants.add(participantModel); + } + + public ParticipantModel addRoomParticipant(UserInformation userInformation, ParticipantRole role) + { + ParticipantModel participantModel = new ParticipantModel(userInformation, cacheProvider); + participantModel.setNewId(); + participantModel.setRole(role); + participantModel.setEmail(participantModel.getEmail()); + participantModel.setName(participantModel.getName()); + participantModel.setOrganization(participantModel.getOrganization()); + addRoomParticipant(participantModel); + return participantModel; + } + + public String getRoomMeetingName() + { + return roomMeetingName; + } + + public void setRoomMeetingName(String roomMeetingName) + { + this.roomMeetingName = roomMeetingName; + } + + public String getRoomMeetingDescription() + { + return roomMeetingDescription; + } + + public void setRoomMeetingDescription(String roomMeetingDescription) + { + this.roomMeetingDescription = roomMeetingDescription; + } + + public boolean isRoomParticipantNotificationEnabled() + { + return roomParticipantNotificationEnabled; + } + + public void setRoomParticipantNotificationEnabled(boolean roomParticipantNotificationEnabled) + { + this.roomParticipantNotificationEnabled = roomParticipantNotificationEnabled; + } + + public LocalDate getRemovedReservationDate() + { + return removedReservationDate; + } + + public void setRemovedReservationDate(LocalDate removedReservationDate) + { + this.removedReservationDate = removedReservationDate; + } + + /** + * Load attributes from given {@code specification}. + * + * @param specification + */ + public void fromSpecificationApi(Specification specification, CacheProvider cacheProvider) + { + if (specification instanceof RoomSpecification) { + RoomSpecification roomSpecification = (RoomSpecification) specification; + + for (RoomSetting roomSetting : roomSpecification.getRoomSettings()) { + if (roomSetting instanceof H323RoomSetting) { + H323RoomSetting h323RoomSetting = (H323RoomSetting) roomSetting; + if (h323RoomSetting.getPin() != null) { + try { + String pin = h323RoomSetting.getPin(); + if (!pin.isEmpty()) { + roomPin = String.valueOf(Integer.parseInt(pin)); + } + } + catch (NumberFormatException exception) { + log.warn("Failed parsing pin", exception); + } + } + } + if (roomSetting instanceof AdobeConnectRoomSetting) { + AdobeConnectRoomSetting adobeConnectRoomSetting = (AdobeConnectRoomSetting) roomSetting; + roomPin = adobeConnectRoomSetting.getPin(); + roomAccessMode = adobeConnectRoomSetting.getAccessMode(); + } + if (roomSetting instanceof PexipRoomSetting) { + PexipRoomSetting pexipRoomSetting = (PexipRoomSetting) roomSetting; + guestPin = pexipRoomSetting.getGuestPin(); + adminPin = pexipRoomSetting.getHostPin(); + allowGuests = pexipRoomSetting.getAllowGuests(); + } + } + roomParticipants.clear(); + for (AbstractParticipant participant : roomSpecification.getParticipants()) { + roomParticipants.add(new ParticipantModel(participant, cacheProvider)); + } + + RoomEstablishment roomEstablishment = roomSpecification.getEstablishment(); + if (roomEstablishment != null) { + technology = TechnologyModel.find(roomEstablishment.getTechnologies()); + roomResourceId = roomEstablishment.getResourceId(); + + AliasSpecification roomNameAlias = + roomEstablishment.getAliasSpecificationByType(AliasType.ROOM_NAME); + AliasSpecification e164NumberAlias = + roomEstablishment.getAliasSpecificationByType(AliasType.H323_E164); + if (roomNameAlias != null) { + roomName = roomNameAlias.getValue(); + } + if (e164NumberAlias != null) { + e164Number = e164NumberAlias.getValue(); + } + } + + RoomAvailability roomAvailability = roomSpecification.getAvailability(); + if (roomAvailability != null) { + slotBeforeMinutes = roomAvailability.getSlotMinutesBefore(); + slotAfterMinutes = roomAvailability.getSlotMinutesAfter(); + participantCount = roomAvailability.getParticipantCount(); + roomParticipantNotificationEnabled = roomAvailability.isParticipantNotificationEnabled(); + roomMeetingName = roomAvailability.getMeetingName(); + roomMeetingDescription = roomAvailability.getMeetingDescription(); + for (ExecutableServiceSpecification service : roomAvailability.getServiceSpecifications()) { + if (service instanceof RecordingServiceSpecification) { + roomRecorded = service.isEnabled(); + roomRecordingResourceId = service.getResourceId(); + } + } + } + + if (roomEstablishment != null) { + if (roomAvailability != null) { + specificationType = SpecificationType.ADHOC_ROOM; + } + else { + specificationType = SpecificationType.PERMANENT_ROOM; + } + } + else { + specificationType = SpecificationType.PERMANENT_ROOM_CAPACITY; + } + } else if (specification instanceof ResourceSpecification) { + ResourceSpecification resourceSpecification = (ResourceSpecification) specification; + physicalResourceId = resourceSpecification.getResourceId(); + ReservationRequestSummary summary = cacheProvider.getAllocatedReservationRequestSummary(this.id); + specificationType = SpecificationType.fromReservationRequestSummary(summary); + } else { + throw new UnsupportedApiException(specification); + } + } + + /** + * Load attributes from given {@code abstractReservationRequest}. + * + * @param abstractReservationRequest from which the attributes should be loaded + */ + public void fromApi(AbstractReservationRequest abstractReservationRequest, CacheProvider cacheProvider) + { + id = abstractReservationRequest.getId(); + type = abstractReservationRequest.getType(); + dateTime = abstractReservationRequest.getDateTime(); + description = abstractReservationRequest.getDescription(); + + // Specification + Specification specification = abstractReservationRequest.getSpecification(); + fromSpecificationApi(specification, cacheProvider); + if (SpecificationType.PERMANENT_ROOM_CAPACITY.equals(specificationType)) { + roomReservationRequestId = abstractReservationRequest.getReusedReservationRequestId(); + } + + // Date/time slot and periodicity + Period duration = null; + if (abstractReservationRequest instanceof cz.cesnet.shongo.controller.api.ReservationRequest) { + cz.cesnet.shongo.controller.api.ReservationRequest reservationRequest = (cz.cesnet.shongo.controller.api.ReservationRequest) abstractReservationRequest; + periodicity.setType(PeriodicDateTimeSlot.PeriodicityType.NONE); + Interval slot = reservationRequest.getSlot(); + this.slot.setStart(slot.getStart()); + this.slot.setEnd(slot.getEnd()); + duration = slot.toPeriod(); + + parentReservationRequestId = reservationRequest.getParentReservationRequestId(); + } + else if (abstractReservationRequest instanceof ReservationRequestSet) { + if (specificationType.equals(SpecificationType.PERMANENT_ROOM)) { + throw new UnsupportedApiException("Periodicity is not allowed for permanent rooms."); + } + + ReservationRequestSet reservationRequestSet = (ReservationRequestSet) abstractReservationRequest; + List slots = reservationRequestSet.getSlots(); + boolean periodicityEndSet = false; + + int index = 0; + // Multiple slots awailable only for WEEKLY periodicity, check done few lines lower + periodicity.setPeriodicDaysInWeek(new PeriodicDateTimeSlot.DayOfWeek[slots.size()]); + // Set slot properties and periodicity + for (Object slot : slots) { + if (!(slot instanceof PeriodicDateTimeSlot)) { + throw new UnsupportedApiException("Only periodic date/time slots are allowed."); + } + PeriodicDateTimeSlot periodicSlot = (PeriodicDateTimeSlot) slot; + PeriodicDateTimeSlot.PeriodicityType periodicityType= PeriodicDateTimeSlot.PeriodicityType.fromPeriod(periodicSlot.getPeriod()); + int periodicityCycle = PeriodicDateTimeSlot.PeriodicityType.getPeriodCycle(periodicSlot.getPeriod()); + + // Allows multiple slots only for WEEKLY + if (!PeriodicDateTimeSlot.PeriodicityType.WEEKLY.equals(periodicityType) && slots.size() != 1) { + throw new UnsupportedApiException("Multiple periodic date/time slots are allowed only for week period."); + } + // Check if all slots have the same periodicity + if (periodicity.getType() == null) { + periodicity.setType(periodicityType); + periodicity.setPeriodicityCycle(periodicityCycle); + timeZone = periodicSlot.getTimeZone(); + duration = periodicSlot.getDuration(); + } + else if (!periodicity.getType().equals(periodicityType) || periodicity.getPeriodicityCycle() != periodicityCycle + || !periodicSlot.getDuration().equals(duration) || !timeZone.equals(periodicSlot.getTimeZone())) { + throw new UnsupportedApiException("Multiple periodic date/time slots with different periodicity are not allowed."); + } + + int dayIndex = (periodicSlot.getStart().getDayOfWeek() == 7 ? 1 : periodicSlot.getStart().getDayOfWeek() + 1); + if (getStartDate() == null || getStartDate().isAfter(periodicSlot.getStart().toLocalDate())) { + this.slot.setStart(periodicSlot.getStart().toDateTime(timeZone)); + this.slot.setEnd(periodicSlot.getStart().plus(duration)); + } + periodicity.getPeriodicDaysInWeek()[index] = PeriodicDateTimeSlot.DayOfWeek.fromDayIndex(dayIndex); + index++; + + if (PeriodicDateTimeSlot.PeriodicityType.MONTHLY.equals(periodicityType) && periodicity.getMonthPeriodicityType() == null) { + periodicity.setMonthPeriodicityType(periodicSlot.getMonthPeriodicityType()); + if (PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals(periodicity.getMonthPeriodicityType())) { + periodicity.setPeriodicityDayOrder(periodicSlot.getPeriodicityDayOrder()); + periodicity.setPeriodicityDayInMonth(periodicSlot.getPeriodicityDayInMonth()); + } + } + + ReadablePartial slotEnd = periodicSlot.getEnd(); + LocalDate periodicityEnd; + if (slotEnd == null) { + periodicityEnd = null; + } + else if (slotEnd instanceof LocalDate) { + periodicityEnd = (LocalDate) slotEnd; + } + else if (slotEnd instanceof Partial) { + Partial partial = (Partial) slotEnd; + DateTimeField[] partialFields = partial.getFields(); + if (!(partialFields.length == 3 + && partial.isSupported(DateTimeFieldType.year()) + && partial.isSupported(DateTimeFieldType.monthOfYear()) + && partial.isSupported(DateTimeFieldType.dayOfMonth()))) { + throw new UnsupportedApiException("Slot end %s.", slotEnd); + } + periodicityEnd = new LocalDate(partial.getValue(0), partial.getValue(1), partial.getValue(2)); + } + else { + throw new UnsupportedApiException("Slot end %s.", slotEnd); + } + + if (!periodicityEndSet) { + periodicity.setPeriodicityEnd(periodicityEnd); + periodicityEndSet = true; + } + else if ((periodicity.getPeriodicityEnd() == null && periodicity.getPeriodicityEnd() != periodicityEnd) + || (periodicity.getPeriodicityEnd() != null && !periodicity.getPeriodicityEnd().equals(periodicityEnd))) { + throw new UnsupportedApiException("Slot end %s is not same for all slots.", slotEnd); + } + + // Set exclude dates for slot + if (periodicSlot.getExcludeDates() != null) { + if (periodicity.getExcludeDates() == null) { + periodicity.setExcludeDates(new LinkedList<>()); + } + periodicity.getExcludeDates().addAll(periodicSlot.getExcludeDates()); + // Remove duplicates + periodicity.setExcludeDates(new ArrayList<>(new HashSet<>(periodicity.getExcludeDates()))); + } + } + } + else { + throw new UnsupportedApiException(abstractReservationRequest); + } + + // Room duration + if (!specificationType.equals(SpecificationType.PERMANENT_ROOM)) { + setDuration(duration); + } + } + + /** + * Load {@link #permanentRoomReservationRequest} by given {@code cacheProvider}. + * + * @param cacheProvider + */ + public ReservationRequestSummary loadPermanentRoom(CacheProvider cacheProvider) + { + if (StringUtils.isEmpty(roomReservationRequestId)) { + throw new UnsupportedApiException("Permanent room capacity should have permanent room set."); + } + permanentRoomReservationRequest = cacheProvider.getAllocatedReservationRequestSummary(roomReservationRequestId); + roomName = permanentRoomReservationRequest.getRoomName(); + technology = TechnologyModel.find(permanentRoomReservationRequest.getSpecificationTechnologies()); + addPermanentRoomParticipants(); + return permanentRoomReservationRequest; + } + + /** + * Store attributes to {@link Specification}. + * + * @return {@link Specification} with stored attributes + */ + public Specification toSpecificationApi() + { + Specification specification; + switch (specificationType) { + case ADHOC_ROOM: { + RoomSpecification roomSpecification = new RoomSpecification(); + // Room establishment + RoomEstablishment roomEstablishment = roomSpecification.createEstablishment(); + roomEstablishment.setTechnologies(technology.getTechnologies()); + // Room availability + RoomAvailability roomAvailability = roomSpecification.createAvailability(); + roomAvailability.setParticipantCount(participantCount); + roomAvailability.setParticipantNotificationEnabled(roomParticipantNotificationEnabled); + roomAvailability.setMeetingName(roomMeetingName); + roomAvailability.setMeetingDescription(roomMeetingDescription); + if (roomRecorded && !technology.equals(TechnologyModel.ADOBE_CONNECT)) { + roomAvailability.addServiceSpecification(RecordingServiceSpecification.forResource( + Strings.isNullOrEmpty(roomRecordingResourceId) ? null : roomRecordingResourceId, true)); + } + specification = roomSpecification; + break; + } + case PERMANENT_ROOM: { + RoomSpecification roomSpecification = new RoomSpecification(); + // Room establishment + RoomEstablishment roomEstablishment = roomSpecification.createEstablishment(); + roomEstablishment.setTechnologies(technology.getTechnologies()); + AliasSpecification roomNameSpecification = new AliasSpecification(); + roomNameSpecification.addTechnologies(technology.getTechnologies()); + roomNameSpecification.addAliasType(AliasType.ROOM_NAME); + roomNameSpecification.setValue(roomName); + roomEstablishment.addAliasSpecification(roomNameSpecification); + if (technology.equals(TechnologyModel.H323_SIP) || technology.equals(TechnologyModel.PEXIP)) { + AliasSpecification e164NumberSpecification = new AliasSpecification(AliasType.H323_E164); + if (!Strings.isNullOrEmpty(e164Number)) { + e164NumberSpecification.setValue(e164Number); + } + roomEstablishment.addAliasSpecification(e164NumberSpecification); + } + specification = roomSpecification; + break; + } + case PERMANENT_ROOM_CAPACITY: { + RoomSpecification roomSpecification = new RoomSpecification(); + // Room availability + RoomAvailability roomAvailability = roomSpecification.createAvailability(); + roomAvailability.setParticipantCount(participantCount); + roomAvailability.setParticipantNotificationEnabled(roomParticipantNotificationEnabled); + roomAvailability.setMeetingName(roomMeetingName); + roomAvailability.setMeetingDescription(roomMeetingDescription); + if (roomRecorded && !technology.equals(TechnologyModel.ADOBE_CONNECT)) { + roomAvailability.addServiceSpecification(RecordingServiceSpecification.forResource( + Strings.isNullOrEmpty(roomRecordingResourceId) ? null : roomRecordingResourceId, true)); + } + specification = roomSpecification; + break; + } + case VEHICLE: + case PARKING_PLACE: + case MEETING_ROOM: { + specification = new ResourceSpecification(physicalResourceId); + break; + } + default: + throw new TodoImplementException(specificationType); + } + + if (specification instanceof RoomSpecification) { + RoomSpecification roomSpecification = (RoomSpecification) specification; + + for (ParticipantModel participant : roomParticipants) { + if (participant.getId() == null) { + continue; + } + roomSpecification.addParticipant(participant.toApi()); + } + + if (TechnologyModel.PEXIP.equals(technology)) { + PexipRoomSetting pexipRoomSetting = new PexipRoomSetting(); + if (!allowGuests && !Strings.isNullOrEmpty(guestPin)) { + throw new IllegalStateException("Guests must be allowed in order to set a guest pin."); + } + pexipRoomSetting.setHostPin(adminPin); + pexipRoomSetting.setGuestPin(guestPin); + pexipRoomSetting.setAllowGuests(allowGuests); + roomSpecification.addRoomSetting(pexipRoomSetting); + } + + if (TechnologyModel.FREEPBX.equals(technology)) { + FreePBXRoomSetting freePBXRoomSetting = new FreePBXRoomSetting(); + freePBXRoomSetting.setAdminPin(adminPin); + freePBXRoomSetting.setUserPin(roomPin); + roomSpecification.addRoomSetting(freePBXRoomSetting); + } + + if (TechnologyModel.H323_SIP.equals(technology) && roomPin != null) { + H323RoomSetting h323RoomSetting = new H323RoomSetting(); + h323RoomSetting.setPin(roomPin); + roomSpecification.addRoomSetting(h323RoomSetting); + } + if (TechnologyModel.ADOBE_CONNECT.equals(technology)) { + AdobeConnectRoomSetting adobeConnectRoomSetting = new AdobeConnectRoomSetting(); + if (!Strings.isNullOrEmpty(roomPin)) { + adobeConnectRoomSetting.setPin(roomPin); + } + adobeConnectRoomSetting.setAccessMode(roomAccessMode); + roomSpecification.addRoomSetting(adobeConnectRoomSetting); + } + RoomEstablishment roomEstablishment = roomSpecification.getEstablishment(); + if (roomEstablishment != null) { + if (!Strings.isNullOrEmpty(roomResourceId)) { + roomEstablishment.setResourceId(roomResourceId); + } + } + RoomAvailability roomAvailability = roomSpecification.getAvailability(); + if (roomAvailability != null) { + roomAvailability.setSlotMinutesBefore(slotBeforeMinutes); + roomAvailability.setSlotMinutesAfter(slotAfterMinutes); + } + } + return specification; + } + + /** + * @return requested reservation duration as {@link Period} + */ + public Period getDuration() + { + // TODO filip + log.info(String.valueOf(slot)); + return new Period(slot.getStart(), slot.getEnd()); +// switch (specificationType) { +// case PERMANENT_ROOM: +// if (end == null) { +// throw new IllegalStateException("Slot end must be not empty for alias."); +// } +// return new Period(startDate.toDateTimeAtStartOfDay(timeZone), end.withZone(timeZone).withTime(23, 59, 59, 0)); +// case MEETING_ROOM: +// case PARKING_PLACE: +// case VEHICLE: +// case ADHOC_ROOM: +// case PERMANENT_ROOM_CAPACITY: +// if (durationCount == null || durationType == null) { +// if (end != null) { +// return new Period(startDate.toDateTime(start,timeZone), end); +// } +// else { +// throw new IllegalStateException("Slot duration should be not empty."); +// } +// } +// switch (durationType) { +// case MINUTE: +// return Period.minutes(durationCount); +// case HOUR: +// return Period.hours(durationCount); +// case DAY: +// return Period.days(durationCount); +// default: +// throw new TodoImplementException(durationType); +// } +// default: +// throw new TodoImplementException("Reservation request duration."); +// } + } + + /** + * @param duration + */ + public void setDuration(Period duration) + { + switch (specificationType) { + case PARKING_PLACE: + case VEHICLE: + case MEETING_ROOM: + case ADHOC_ROOM: + case PERMANENT_ROOM_CAPACITY: + int minutes; + try { + minutes = duration.toStandardMinutes().getMinutes(); + } + catch (UnsupportedOperationException exception) { + throw new UnsupportedApiException(duration.toString(), exception); + } + if ((minutes % (60 * 24)) == 0) { + durationCount = minutes / (60 * 24); + durationType = DurationType.DAY; + } + else if ((minutes % 60) == 0) { + durationCount = minutes / 60; + durationType = DurationType.HOUR; + } + else { + durationCount = minutes; + durationType = DurationType.MINUTE; + } + break; + default: + throw new TodoImplementException(specificationType); + } + } + + /** + * @return requested first reservation date/time slot as {@link Interval} + */ + public Interval getFirstSlot() + { + PeriodicDateTimeSlot first = getSlots(timeZone).first(); + DateTime start = first.getStart(); +// DateTime start = this.start; + if (timeZone != null) { + // Use specified time zone + LocalDateTime localDateTime = start.toLocalDateTime(); + start = localDateTime.toDateTime(timeZone); + } + switch (specificationType) { + case PERMANENT_ROOM: + return new Interval(getRequestStart().withTime(0, 0, 0, 0), getDuration()); + default: + return new Interval(start, getDuration()); + } + } + + public Period getPeriod() + { + Period period = null; + switch (periodicity.getType()) { + case DAILY: + period = Period.days(1); + break; + case WEEKLY: + period = Period.weeks(periodicity.getPeriodicityCycle()); + break; + case MONTHLY: + period = Period.months(periodicity.getPeriodicityCycle()); + break; + } + return period; + } + + /** + * + * @return all calculated slots + */ + public SortedSet getSlots(DateTimeZone timeZone) { + SortedSet slots = new TreeSet<>(); + if (PeriodicDateTimeSlot.PeriodicityType.NONE.equals(periodicity.getType())) { + DateTime requestStart = getRequestStart(); + if (SpecificationType.PERMANENT_ROOM.equals(getSpecificationType())) { + requestStart = requestStart.withTimeAtStartOfDay(); + } + PeriodicDateTimeSlot periodicDateTimeSlot = new PeriodicDateTimeSlot(requestStart, getDuration(), Period.ZERO); + periodicDateTimeSlot.setEnd(getStartDate()); + periodicDateTimeSlot.setTimeZone(getTimeZone()); + slots.add(periodicDateTimeSlot); + } else { + // Determine period + Period period = periodicity.getType().toPeriod(periodicity.getPeriodicityCycle()); + + if (PeriodicDateTimeSlot.PeriodicityType.WEEKLY.equals(periodicity.getType())) { + for (PeriodicDateTimeSlot.DayOfWeek day : periodicity.getPeriodicDaysInWeek()) { + DateTime nextSlotStart = getRequestStart(); + int dayIndex = (day.getDayIndex() == 1 ? 7 : day.getDayIndex() - 1); + while (nextSlotStart.getDayOfWeek() != dayIndex) { + nextSlotStart = nextSlotStart.plusDays(1); + } + PeriodicDateTimeSlot periodicDateTimeSlot = new PeriodicDateTimeSlot(); + periodicDateTimeSlot.setStart(nextSlotStart); + if (this.timeZone != null) { + periodicDateTimeSlot.setTimeZone(this.timeZone); + } + else if (timeZone != null) { + periodicDateTimeSlot.setTimeZone(timeZone); + } + periodicDateTimeSlot.setDuration(getDuration()); + periodicDateTimeSlot.setPeriod(period); + periodicDateTimeSlot.setEnd(periodicity.getPeriodicityEnd()); + slots.add(periodicDateTimeSlot); + } + } else { + PeriodicDateTimeSlot periodicDateTimeSlot = new PeriodicDateTimeSlot(); + periodicDateTimeSlot.setStart(getFirstSlotStart()); + if (this.timeZone != null) { + periodicDateTimeSlot.setTimeZone(this.timeZone); + } + else if (timeZone != null) { + periodicDateTimeSlot.setTimeZone(timeZone); + } + periodicDateTimeSlot.setDuration(getDuration()); + periodicDateTimeSlot.setPeriod(period); + periodicDateTimeSlot.setEnd(periodicity.getPeriodicityEnd()); + if (PeriodicDateTimeSlot.PeriodicityType.MONTHLY.equals(periodicity.getType())) { + if (PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals(periodicity.getMonthPeriodicityType())) { + periodicDateTimeSlot.setMonthPeriodicityType(periodicity.getMonthPeriodicityType()); + periodicDateTimeSlot.setPeriodicityDayOrder(periodicity.getPeriodicityDayOrder()); + periodicDateTimeSlot.setPeriodicityDayInMonth(periodicity.getPeriodicityDayInMonth()); + } else { + periodicDateTimeSlot.setMonthPeriodicityType(periodicity.getMonthPeriodicityType()); + } + } + slots.add(periodicDateTimeSlot); + } + } + + return Collections.unmodifiableSortedSet(slots); + } + + public DateTime getFirstSlotStart() + { + DateTime slotStart = getRequestStart(); + if (PeriodicDateTimeSlot.PeriodicityType.MONTHLY.equals(periodicity.getType()) + && PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals(periodicity.getMonthPeriodicityType())) { + slotStart = getMonthFirstSlotStart(slotStart); + } + return slotStart; + } + + /** + * Calculate slot start when #periodicityType is MONTHLY and for SPECIFIC_DAY for given #slotStart + * + * @param slotStart DateTime from which start + * @return DateTime for specific date of month + */ + private DateTime getMonthFirstSlotStart(DateTime slotStart) + { + if (!PeriodicDateTimeSlot.PeriodicityType.MONTHLY.equals(periodicity.getType()) + || !PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals(periodicity.getMonthPeriodicityType())) { + throw new IllegalStateException("Periodicity type has to be monthly for a specific day."); + } + if (periodicity.getPeriodicityDayInMonth() == null || (periodicity.getPeriodicityDayOrder() != -1 && + (periodicity.getPeriodicityDayOrder() < 1 || periodicity.getPeriodicityDayOrder() > 4)) + || periodicity.getPeriodicityEnd() == null) { + throw new IllegalStateException("For periodicity type MONTHLY must be set day of month."); + } + + while (slotStart.getDayOfWeek() != (periodicity.getPeriodicityDayInMonth().getDayIndex() == 1 ? 7 : periodicity.getPeriodicityDayInMonth().getDayIndex() - 1)) { + slotStart = slotStart.plusDays(1); + } + DateTime monthEnd = slotStart.plusMonths(1).minusDays(slotStart.getDayOfMonth() - 1);; + if (0 < periodicity.getPeriodicityDayOrder() && periodicity.getPeriodicityDayOrder() < 5) { + while ((slotStart.getDayOfMonth() % 7 == 0 ? slotStart.getDayOfMonth() / 7 : slotStart.getDayOfMonth() / 7 + 1) != periodicity.getPeriodicityDayOrder()) { + if (slotStart.plusDays(7).isBefore(monthEnd.plusMonths(1))) { + slotStart = slotStart.plusDays(7); + } + } + } + else if (periodicity.getPeriodicityDayOrder() == -1) { + while (true) { + if (!slotStart.plusDays(7).isAfter(monthEnd.minusDays(1))) { + slotStart = slotStart.plusDays(7); + } + else { + break; + } + } + } + else { + throw new TodoImplementException(); + } + return slotStart; + } + + public void updateSlotStartToFutureSlot() + { + try { + setStartDate(getFirstFutureSlotStart()); + } + catch (IllegalStateException ex) { + // Continue if model is not set properly yet + return; + } + } + + public LocalDate getFirstFutureSlotStart() + { + DateTime slotStart = getRequestStart(); + Period duration = getDuration(); + + Period period = getPeriod(); + while (slotStart.plus(duration).isBeforeNow() && period != null) { + switch (periodicity.getType()) { + case WEEKLY: + if (periodicity.getPeriodicDaysInWeek().length > 1) { + Set daysOfWeek = new HashSet<>(); + for (PeriodicDateTimeSlot.DayOfWeek day : periodicity.getPeriodicDaysInWeek()) { + daysOfWeek.add(day.getDayIndex() == 1 ? 7 : day.getDayIndex() - 1); + } + while (!daysOfWeek.contains(slotStart.getDayOfWeek()) || slotStart.plus(duration).isBeforeNow()) { + slotStart = slotStart.plusDays(1); + } + } else { + slotStart = slotStart.plus(period); + } + break; + case MONTHLY: + if (PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals(periodicity.getMonthPeriodicityType())) { + slotStart = getMonthFirstSlotStart(slotStart.plus(period).withDayOfMonth(1)); + break; + } else { + slotStart = slotStart.plus(period); + } + break; + case DAILY: + slotStart = slotStart.plus(period); + break; + default: + throw new TodoImplementException("Unsupported periodicity type: " + periodicity.getType()); + } + } + return slotStart.toLocalDate(); + } + + /** + * Store all attributes to {@link AbstractReservationRequest}. + * + * @return {@link AbstractReservationRequest} with stored attributes + */ + public AbstractReservationRequest toApi() + { + SortedSet slots = getSlots(DateTimeZone.UTC); + // Create reservation request + AbstractReservationRequest abstractReservationRequest; + if (periodicity.getType() == PeriodicDateTimeSlot.PeriodicityType.NONE) { + // Create single reservation request + cz.cesnet.shongo.controller.api.ReservationRequest reservationRequest = new ReservationRequest(); + PeriodicDateTimeSlot slot = slots.first(); + reservationRequest.setSlot(slot.getStart(), slot.getStart().plus(slot.getDuration())); + abstractReservationRequest = reservationRequest; + } + else { + // Create set of reservation requests + ReservationRequestSet reservationRequestSet = new ReservationRequestSet(); + reservationRequestSet.addAllSlots(slots); + if (periodicity.getExcludeDates() != null && !periodicity.getExcludeDates().isEmpty()) { + for (LocalDate excludeDate : periodicity.getExcludeDates()) { + for (PeriodicDateTimeSlot slot : slots) { + if (Temporal.dateFitsInterval(slot.getStart(), slot.getEnd(), excludeDate)) { + slot.addExcludeDate(excludeDate); + } + } + } + } + abstractReservationRequest = reservationRequestSet; + } + if (!Strings.isNullOrEmpty(id)) { + abstractReservationRequest.setId(id); + } + abstractReservationRequest.setPurpose(purpose); + abstractReservationRequest.setDescription(description); + if (specificationType.equals(SpecificationType.PERMANENT_ROOM)) { + abstractReservationRequest.setReusement(ReservationRequestReusement.OWNED); + } + else if (specificationType.equals(SpecificationType.PERMANENT_ROOM_CAPACITY)) { + abstractReservationRequest.setReusedReservationRequestId(roomReservationRequestId); + } + + // Create specification + Specification specification = toSpecificationApi(); + abstractReservationRequest.setSpecification(specification); + + // Set reservation request to be deleted by scheduler if foreign resource is specified + abstractReservationRequest.setIsSchedulerDeleted(!Strings.isNullOrEmpty(getMeetingRoomResourceDomain())); + + return abstractReservationRequest; + } + +// /** +// * @param reservationRequest +// * @return list of {@link Page}s for given {@code reservationRequest} +// */ +// public static List getPagesForBreadcrumb(ReservationRequestSummary reservationRequest) +// { +// SpecificationType specificationType = SpecificationType.fromReservationRequestSummary(reservationRequest); +// return getPagesForBreadcrumb(reservationRequest.getId(), specificationType, +// reservationRequest.getParentReservationRequestId(), reservationRequest.getReusedReservationRequestId()); +// } +// +// /** +// * @param specificationType +// * @param parentReservationRequestId +// * @param permanentRoomReservationRequestId +// * @return list of {@link Page}s for this reservation request +// */ +// public static List getPagesForBreadcrumb(String reservationRequestId, +// SpecificationType specificationType, String parentReservationRequestId, +// String permanentRoomReservationRequestId) +// { +// List pages = new LinkedList(); +// +// String titleCode; +// if (parentReservationRequestId != null) { +// if (specificationType.equals(SpecificationType.PERMANENT_ROOM_CAPACITY)) { +// // Add page for permanent room reservation request +// pages.add(new Page( +// ClientWebUrl.format(ClientWebUrl.DETAIL_VIEW, permanentRoomReservationRequestId), +// "navigation.detail")); +// +// // Add page for reservation request set +// pages.add(new Page( +// ClientWebUrl.format(ClientWebUrl.DETAIL_VIEW, parentReservationRequestId), +// "navigation.detail.capacity")); +// } +// else { +// // Add page for reservation request set +// pages.add(new Page( +// ClientWebUrl.format(ClientWebUrl.DETAIL_VIEW, parentReservationRequestId), +// "navigation.detail")); +// } +// +// // This reservation request is periodic event +// titleCode = "navigation.detail.event"; +// } +// else if (specificationType.equals(SpecificationType.PERMANENT_ROOM_CAPACITY)) { +// // Add page for permanent room reservation request +// pages.add(new Page( +// ClientWebUrl.format(ClientWebUrl.DETAIL_VIEW, permanentRoomReservationRequestId), +// "navigation.detail")); +// +// // This reservation request is capacity +// titleCode = "navigation.detail.capacity"; +// } +// else { +// titleCode = "navigation.detail"; +// } +// +// // Add page for this reservation request +// pages.add(new Page(ClientWebUrl.format(ClientWebUrl.DETAIL_VIEW, reservationRequestId), titleCode)); +// +// return pages; +// } +// +// @Override +// public String toContextString() +// { +// Map attributes = new LinkedHashMap(); +// attributes.put("ID", id); +// attributes.put("Type", specificationType); +// attributes.put("Description", description); +// attributes.put("Purpose", purpose); +// attributes.put("Technology", technology); +// attributes.put("Start", start); +// attributes.put("End", end); +// attributes.put("Duration count", durationCount); +// attributes.put("Duration type", durationType); +// attributes.put("Periodicity type", periodicityType); +// attributes.put("Periodicity end", periodicityEnd); +// attributes.put("Room name", roomName); +// attributes.put("Permanent room", permanentRoomReservationRequestId); +// attributes.put("Participant count", roomParticipantCount); +// attributes.put("PIN", roomPin); +// attributes.put("Access mode", roomAccessMode); +// attributes.put("Exclude dates", excludeDates); +// return ReportModel.formatAttributes(attributes); +// } + +// /** +// * @param participantId +// * @return {@link ParticipantModel} with given {@code participantId} +// */ +// public ParticipantModel getParticipant(String participantId) +// { +// ParticipantModel participant = null; +// for (ParticipantModel possibleParticipant : roomParticipants) { +// if (participantId.equals(possibleParticipant.getId())) { +// participant = possibleParticipant; +// } +// } +// if (participant == null) { +// throw new IllegalArgumentException("Participant " + participantId + " doesn't exist."); +// } +// return participant; +// } +// +// /** +// * @param userId +// * @param role +// * @return true wheter {@link #roomParticipants} contains user participant with given {@code userId} and {@code role} +// */ +// public boolean hasUserParticipant(String userId, ParticipantRole role) +// { +// for (ParticipantModel participant : roomParticipants) { +// if (participant.getType().equals(ParticipantModel.Type.USER) && participant.getUserId().equals(userId) +// && role.equals(participant.getRole())) { +// return true; +// } +// } +// return false; +// } +// +// /** +// * Add new participant. +// * +// * @param participant +// * @param bindingResult +// */ +// public boolean createParticipant(ParticipantModel participant, BindingResult bindingResult, +// SecurityToken securityToken) +// { +// participant.validate(bindingResult); +// if (bindingResult.hasErrors()) { +// CommonModel.logValidationErrors(logger, bindingResult, securityToken); +// return false; +// } +// participant.setNewId(); +// addRoomParticipant(participant); +// return true; +// } +// +// /** +// * Modify existing participant +// * +// * @param participantId +// * @param participant +// * @param bindingResult +// */ +// public boolean modifyParticipant(String participantId, ParticipantModel participant, BindingResult bindingResult, +// SecurityToken securityToken) +// { +// participant.validate(bindingResult); +// if (bindingResult.hasErrors()) { +// CommonModel.logValidationErrors(logger, bindingResult, securityToken); +// return false; +// } +// ParticipantModel oldParticipant = getParticipant(participantId); +// roomParticipants.remove(oldParticipant); +// addRoomParticipant(participant); +// return true; +// } +// +// /** +// * Delete existing participant. +// * +// * @param participantId +// */ +// public void deleteParticipant(String participantId) +// { +// ParticipantModel participant = getParticipant(participantId); +// roomParticipants.remove(participant); +// } +// + /** + * Add all {@link ParticipantModel} from {@link #permanentRoomReservationRequest} to {@link #roomParticipants} + */ + private void addPermanentRoomParticipants() + { + if (permanentRoomReservationRequest == null) { + throw new IllegalStateException("Permanent room reservation request must not be null"); + } + String permanentRoomReservationId = permanentRoomReservationRequest.getAllocatedReservationId(); + Reservation permanentRoomReservation = cacheProvider.getReservation(permanentRoomReservationId); + String permanentRoomId = permanentRoomReservation.getExecutable().getId(); + AbstractRoomExecutable permanentRoom = (AbstractRoomExecutable) cacheProvider.getExecutable(permanentRoomId); + RoomExecutableParticipantConfiguration permanentRoomParticipants = permanentRoom.getParticipantConfiguration(); + + // Remove all participants without identifier (old permanent room participants) + roomParticipants.removeIf(roomParticipant -> roomParticipant.getId() == null); + // Add all permanent room participants + int index = 0; + for (AbstractParticipant participant : permanentRoomParticipants.getParticipants()) { + ParticipantModel roomParticipant = new ParticipantModel(participant, cacheProvider); + roomParticipant.setNullId(); + roomParticipants.add(index++, roomParticipant); + } + } + + /** + * Load user roles into this {@link ReservationRequestModel}. + * + * @param securityToken + * @param authorizationService + */ + public void loadUserRoles(SecurityToken securityToken, AuthorizationService authorizationService) + { + if (id == null) { + throw new IllegalStateException("Id mustn't be null."); + } + // Add user roles + AclEntryListRequest userRoleRequest = new AclEntryListRequest(); + userRoleRequest.setSecurityToken(securityToken); + userRoleRequest.addObjectId(id); + for (AclEntry aclEntry : authorizationService.listAclEntries(userRoleRequest)) { + addUserRole(new UserRoleModel(aclEntry, cacheProvider)); + } + } + + /** + * @return default automatically added {@link ParticipantRole} for owner + */ + public ParticipantRole getDefaultOwnerParticipantRole() + { + if (TechnologyModel.H323_SIP.equals(technology)) { + return ParticipantRole.PARTICIPANT; + } + else { + return ParticipantRole.ADMINISTRATOR; + } + } + + /** + * Type of duration unit. + */ + public static enum DurationType + { + MINUTE, + HOUR, + DAY + } + + /** + * @param reservationService + * @param securityToken + * @param cache + * @return list of reservation requests for permanent rooms + */ + public static List getPermanentRooms(ReservationService reservationService, + SecurityToken securityToken, Cache cache) + { + ReservationRequestListRequest request = new ReservationRequestListRequest(); + request.setSecurityToken(securityToken); + request.addSpecificationType(ReservationRequestSummary.SpecificationType.PERMANENT_ROOM); + List reservationRequests = new LinkedList(); + + ListResponse response = reservationService.listReservationRequests(request); + if (response.getItemCount() > 0) { + Set reservationRequestIds = new HashSet(); + for (ReservationRequestSummary reservationRequestSummary : response) { + reservationRequestIds.add(reservationRequestSummary.getId()); + } + cache.fetchObjectPermissions(securityToken, reservationRequestIds); + + for (ReservationRequestSummary reservationRequestSummary : response) { + ExecutableState executableState = reservationRequestSummary.getExecutableState(); + if (executableState == null || (!executableState.isAvailable() && !executableState.equals(ExecutableState.NOT_STARTED))) { + continue; + } + Set objectPermissions = cache.getObjectPermissions(securityToken, + reservationRequestSummary.getId()); + if (!objectPermissions.contains(ObjectPermission.PROVIDE_RESERVATION_REQUEST)) { + continue; + } + reservationRequests.add(reservationRequestSummary); + } + } + return reservationRequests; + } + + /** + * @param reservationRequestId + * @param reservationService + * @param securityToken + * @return list of deletion dependencies for reservation request with given {@code reservationRequestId} + */ + public static List getDeleteDependencies(String reservationRequestId, + ReservationService reservationService, SecurityToken securityToken) + { + // List reservation requests which reuse the reservation request to be deleted + ReservationRequestListRequest reservationRequestListRequest = new ReservationRequestListRequest(); + reservationRequestListRequest.setSecurityToken(securityToken); + reservationRequestListRequest.setReusedReservationRequestId(reservationRequestId); + ListResponse reservationRequests = + reservationService.listReservationRequests(reservationRequestListRequest); + return reservationRequests.getItems(); + } + + @Override + public String toString() + { + return "ReservationRequestModel{" + + "id='" + id + '\'' + + ", description='" + description + '\'' + + ", type=" + type + + ", dateTime=" + dateTime + + ", technology=" + technology + + ", start=" + getStart() + + ", end=" + getEnd() + + ", excludeDates=" + periodicity.getExcludeDates() + + ", roomName='" + roomName + '\'' + + ", roomResourceId='" + roomResourceId + '\'' + + ", physicalResourceId='" + physicalResourceId + '\'' + + '}'; + } +} From 0af66f2aaa9e4d8348b362422234ba78db3b81c1 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 28 Apr 2022 19:58:48 +0200 Subject: [PATCH 018/134] Changed ADHOC_ROOM and PERMANENT_ROOM to VIRTUAL_ROOM --- .../api/ReservationRequestController.java | 2 +- .../rest/models/reservationrequest/RRR.java | 38 ++++--------------- .../ReservationRequestModel.java | 5 +-- .../ReservationRequestState.java | 14 +------ .../reservationrequest/SpecificationType.java | 10 +---- 5 files changed, 13 insertions(+), 56 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java index 7f8684d10..2148e238d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java @@ -189,7 +189,7 @@ void createRequest( request.setTechnology(TechnologyModel.find(resourceSummary.getTechnologies())); request.setRoomResourceId(resource); // TODO get SpecificationType from resource - request.setSpecificationType(SpecificationType.PERMANENT_ROOM); + request.setSpecificationType(SpecificationType.VIRTUAL_ROOM); // Add default participant request.addRoomParticipant(userInformation, request.getDefaultOwnerParticipantRole()); } else if (resourceSummary.getType() == RESOURCE) { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java index 185cfdfaa..1cf23ecef 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java @@ -852,12 +852,7 @@ public void fromSpecificationApi(Specification specification, CacheProvider cach } if (roomEstablishment != null) { - if (roomAvailability != null) { - specificationType = SpecificationType.ADHOC_ROOM; - } - else { - specificationType = SpecificationType.PERMANENT_ROOM; - } + specificationType = SpecificationType.VIRTUAL_ROOM; } else { specificationType = SpecificationType.PERMANENT_ROOM_CAPACITY; @@ -904,7 +899,7 @@ public void fromApi(AbstractReservationRequest abstractReservationRequest, Cache parentReservationRequestId = reservationRequest.getParentReservationRequestId(); } else if (abstractReservationRequest instanceof ReservationRequestSet) { - if (specificationType.equals(SpecificationType.PERMANENT_ROOM)) { + if (specificationType.equals(SpecificationType.VIRTUAL_ROOM)) { throw new UnsupportedApiException("Periodicity is not allowed for permanent rooms."); } @@ -1004,7 +999,7 @@ else if ((periodicity.getPeriodicityEnd() == null && periodicity.getPeriodicityE } // Room duration - if (!specificationType.equals(SpecificationType.PERMANENT_ROOM)) { + if (!specificationType.equals(SpecificationType.VIRTUAL_ROOM)) { setDuration(duration); } } @@ -1035,25 +1030,7 @@ public Specification toSpecificationApi() { Specification specification; switch (specificationType) { - case ADHOC_ROOM: { - RoomSpecification roomSpecification = new RoomSpecification(); - // Room establishment - RoomEstablishment roomEstablishment = roomSpecification.createEstablishment(); - roomEstablishment.setTechnologies(technology.getTechnologies()); - // Room availability - RoomAvailability roomAvailability = roomSpecification.createAvailability(); - roomAvailability.setParticipantCount(participantCount); - roomAvailability.setParticipantNotificationEnabled(roomParticipantNotificationEnabled); - roomAvailability.setMeetingName(roomMeetingName); - roomAvailability.setMeetingDescription(roomMeetingDescription); - if (roomRecorded && !technology.equals(TechnologyModel.ADOBE_CONNECT)) { - roomAvailability.addServiceSpecification(RecordingServiceSpecification.forResource( - Strings.isNullOrEmpty(roomRecordingResourceId) ? null : roomRecordingResourceId, true)); - } - specification = roomSpecification; - break; - } - case PERMANENT_ROOM: { + case VIRTUAL_ROOM: { RoomSpecification roomSpecification = new RoomSpecification(); // Room establishment RoomEstablishment roomEstablishment = roomSpecification.createEstablishment(); @@ -1205,7 +1182,6 @@ public void setDuration(Period duration) case PARKING_PLACE: case VEHICLE: case MEETING_ROOM: - case ADHOC_ROOM: case PERMANENT_ROOM_CAPACITY: int minutes; try { @@ -1246,7 +1222,7 @@ public Interval getFirstSlot() start = localDateTime.toDateTime(timeZone); } switch (specificationType) { - case PERMANENT_ROOM: + case VIRTUAL_ROOM: return new Interval(getRequestStart().withTime(0, 0, 0, 0), getDuration()); default: return new Interval(start, getDuration()); @@ -1278,7 +1254,7 @@ public SortedSet getSlots(DateTimeZone timeZone) { SortedSet slots = new TreeSet<>(); if (PeriodicDateTimeSlot.PeriodicityType.NONE.equals(periodicity.getType())) { DateTime requestStart = getRequestStart(); - if (SpecificationType.PERMANENT_ROOM.equals(getSpecificationType())) { + if (SpecificationType.VIRTUAL_ROOM.equals(getSpecificationType())) { requestStart = requestStart.withTimeAtStartOfDay(); } PeriodicDateTimeSlot periodicDateTimeSlot = new PeriodicDateTimeSlot(requestStart, getDuration(), Period.ZERO); @@ -1479,7 +1455,7 @@ public AbstractReservationRequest toApi() } abstractReservationRequest.setPurpose(purpose); abstractReservationRequest.setDescription(description); - if (specificationType.equals(SpecificationType.PERMANENT_ROOM)) { + if (specificationType.equals(SpecificationType.VIRTUAL_ROOM)) { abstractReservationRequest.setReusement(ReservationRequestReusement.OWNED); } else if (specificationType.equals(SpecificationType.PERMANENT_ROOM_CAPACITY)) { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java index 3e6947661..abc0e1574 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java @@ -3,7 +3,6 @@ import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.ObjectPermission; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; -import cz.cesnet.shongo.controller.api.ReservationRequestType; import cz.cesnet.shongo.controller.rest.models.TimeInterval; import lombok.Data; import lombok.NoArgsConstructor; @@ -32,7 +31,7 @@ public class ReservationRequestModel { private String ownerEmail; private TimeInterval slot; private Boolean isDeprecated; - private ReservationRequestType type; + private SpecificationType type; private VirtualRoomModel virtualRoomData; private RoomCapacityModel roomCapacityData; private String lastReservationId; @@ -51,7 +50,7 @@ public ReservationRequestModel( this.ownerName = ownerInformation.getFullName(); this.ownerEmail = ownerInformation.getEmail(); this.slot = new TimeInterval(summary.getEarliestSlot()); - this.type = summary.getType(); + this.type = SpecificationType.fromReservationRequestSummary(summary); this.virtualRoomData = new VirtualRoomModel(summary); this.roomCapacityData = new RoomCapacityModel(summary); this.lastReservationId = summary.getLastReservationId(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java index df11175bc..4311fde3e 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java @@ -139,7 +139,7 @@ public static ReservationRequestState fromApi(AllocationState allocationState, E case ALLOCATED: if (executableState != null) { switch (specificationType) { - case PERMANENT_ROOM: + case VIRTUAL_ROOM: switch (executableState) { case STARTED: if (usageExecutableState != null && usageExecutableState.isAvailable()) { @@ -168,18 +168,6 @@ public static ReservationRequestState fromApi(AllocationState allocationState, E default: return ALLOCATED; } - case ADHOC_ROOM: - switch (executableState) { - case STARTED: - return ALLOCATED_STARTED; - case STOPPED: - case STOPPING_FAILED: - return ALLOCATED_FINISHED; - case STARTING_FAILED: - return FAILED; - default: - return ALLOCATED; - } } } return ALLOCATED; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java index 0dd231bd9..6690447fb 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java @@ -12,15 +12,10 @@ */ public enum SpecificationType { - /** - * For ad-hoc room. - */ - ADHOC_ROOM(true, false), - /** * For permanent room. */ - PERMANENT_ROOM(true, false), + VIRTUAL_ROOM(true, false), /** * For permanent room capacity. @@ -89,9 +84,8 @@ public static SpecificationType fromReservationRequestSummary(ReservationRequest switch (reservationRequestSummary.getSpecificationType()) { case ROOM: - return ADHOC_ROOM; case PERMANENT_ROOM: - return PERMANENT_ROOM; + return VIRTUAL_ROOM; case USED_ROOM: return PERMANENT_ROOM_CAPACITY; case RESOURCE: From af5962eb75e886dbbdc46fb32c892ff8406a1b34 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Mon, 2 May 2022 01:39:19 +0200 Subject: [PATCH 019/134] Request type create --- .../api/ReservationRequestController.java | 52 +-- .../rest/models/reservationrequest/RRR.java | 434 +++--------------- .../ReservationRequestState.java | 2 +- .../reservationrequest/SpecificationType.java | 11 +- 4 files changed, 89 insertions(+), 410 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java index 2148e238d..36703be9b 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java @@ -17,11 +17,7 @@ import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; -import cz.cesnet.shongo.controller.rest.models.reservationrequest.RRR; -import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestDetailModel; -import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestHistoryModel; -import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestModel; -import cz.cesnet.shongo.controller.rest.models.reservationrequest.SpecificationType; +import cz.cesnet.shongo.controller.rest.models.reservationrequest.*; import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; import cz.cesnet.shongo.controller.rest.models.room.RoomAuthorizedData; import io.swagger.v3.oas.annotations.Operation; @@ -45,8 +41,6 @@ import java.util.Set; import java.util.stream.Collectors; -import static cz.cesnet.shongo.controller.api.ResourceSummary.Type.RESOURCE; -import static cz.cesnet.shongo.controller.api.ResourceSummary.Type.ROOM_PROVIDER; import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; import static cz.cesnet.shongo.controller.rest.models.TimeInterval.DATETIME_FORMATTER; @@ -175,34 +169,40 @@ void createRequest( @RequestBody RRR request) { CacheProvider cacheProvider = new CacheProvider(cache, securityToken); - UserInformation userInformation = securityToken.getUserInformation(); - request.setCacheProvider(cacheProvider); - String resource = request.getResource(); - if (resource == null) { - request.setSpecificationType(SpecificationType.PERMANENT_ROOM_CAPACITY); - reservationService.createReservationRequest(securityToken, request.toApi()); - return; - } - ResourceSummary resourceSummary = cacheProvider.getResourceSummary(resource); - if (resourceSummary.getType() == ROOM_PROVIDER) { + + String resourceId = request.getResourceId(); + if (resourceId != null) { + ResourceSummary resourceSummary = cacheProvider.getResourceSummary(resourceId); request.setTechnology(TechnologyModel.find(resourceSummary.getTechnologies())); - request.setRoomResourceId(resource); - // TODO get SpecificationType from resource - request.setSpecificationType(SpecificationType.VIRTUAL_ROOM); + } + UserInformation userInformation = securityToken.getUserInformation(); + + if (request.getSpecificationType() == SpecificationType.VIRTUAL_ROOM) { // Add default participant request.addRoomParticipant(userInformation, request.getDefaultOwnerParticipantRole()); - } else if (resourceSummary.getType() == RESOURCE) { - request.setPhysicalResourceId(resource); - request.setSpecificationType(SpecificationType.MEETING_ROOM); + + // Create VIRTUAL_ROOM + String reservationId = reservationService.createReservationRequest(securityToken, request.toApi()); + + // Add default role + request.setId(reservationId); + UserRoleModel userRoleModel = request.addUserRole(userInformation, ObjectRole.OWNER); + authorizationService.createAclEntry(securityToken, userRoleModel.toApi()); + + // Set request to ROOM_CAPACITY for created VIRTUAL_ROOM + request.setSpecificationType(SpecificationType.ROOM_CAPACITY); + request.setRoomReservationRequestId(reservationId); } String reservationId = reservationService.createReservationRequest(securityToken, request.toApi()); + request.setId(reservationId); // Create default role for the user - request.setId(reservationId); - UserRoleModel userRoleModel = request.addUserRole(userInformation, ObjectRole.OWNER); - authorizationService.createAclEntry(securityToken, userRoleModel.toApi()); + if (request.getSpecificationType() != SpecificationType.ROOM_CAPACITY) { + UserRoleModel userRoleModel = request.addUserRole(userInformation, ObjectRole.OWNER); + authorizationService.createAclEntry(securityToken, userRoleModel.toApi()); + } } @Operation(summary = "Returns reservation request.") diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java index 1cf23ecef..fe1eca577 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java @@ -1,6 +1,7 @@ package cz.cesnet.shongo.controller.rest.models.reservationrequest; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Strings; import cz.cesnet.shongo.AliasType; import cz.cesnet.shongo.ParticipantRole; @@ -27,6 +28,7 @@ import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; import cz.cesnet.shongo.controller.rest.models.users.SettingsModel; import cz.cesnet.shongo.util.SlotHelper; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.joda.time.*; @@ -40,6 +42,7 @@ * @author Filip Karnis */ @Slf4j +@Data public class RRR //implements ReportModel.ContextSerializable { @@ -50,6 +53,7 @@ public class RRR //implements ReportModel.ContextSerializable protected String parentReservationRequestId; + @JsonProperty("requestType") protected ReservationRequestType type; protected String description; @@ -77,6 +81,7 @@ public class RRR //implements ReportModel.ContextSerializable */ protected LocalDate removedReservationDate; + @JsonProperty("type") protected SpecificationType specificationType; protected String roomName; @@ -87,14 +92,6 @@ public class RRR //implements ReportModel.ContextSerializable protected ReservationRequestSummary permanentRoomReservationRequest; - protected String roomResourceId; - - /** - * TODO:MR - * Temporary for meeting rooms, - */ - protected String physicalResourceId; - protected Integer participantCount; protected String roomPin; @@ -125,20 +122,11 @@ public class RRR //implements ReportModel.ContextSerializable protected boolean allowGuests = false; - protected String resource; + @JsonProperty("resource") + protected String resourceId; private TimeInterval slot; - public TimeInterval getSlot() - { - return slot; - } - - public void setSlot(TimeInterval slot) - { - this.slot = slot; - } - /** * Create new {@link ReservationRequestModel} from scratch. */ @@ -180,7 +168,7 @@ public RRR(AbstractReservationRequest reservationRequest, CacheProvider cachePro fromApi(reservationRequest, cacheProvider); // Load permanent room - if (specificationType.equals(SpecificationType.PERMANENT_ROOM_CAPACITY) && cacheProvider != null) { + if (specificationType.equals(SpecificationType.ROOM_CAPACITY) && cacheProvider != null) { loadPermanentRoom(cacheProvider); } } @@ -221,99 +209,6 @@ public void initByUserSettings(SettingsModel userSettingsModel) // return null; // } - public void setCacheProvider(CacheProvider cacheProvider) - { - this.cacheProvider = cacheProvider; - } - - public PeriodicityModel getPeriodicity() - { - return periodicity; - } - - public void setPeriodicity(PeriodicityModel periodicity) - { - this.periodicity = periodicity; - } - - public String getResource() - { - return resource; - } - - public void setResource(String resource) - { - this.resource = resource; - } - - public String getId() - { - return id; - } - - public void setId(String id) - { - this.id = id; - } - - public String getParentReservationRequestId() - { - return parentReservationRequestId; - } - - public ReservationRequestType getType() - { - return type; - } - - public void setType(ReservationRequestType type) - { - this.type = type; - } - - public DateTime getDateTime() - { - return dateTime; - } - - public String getDescription() - { - return description; - } - - public ReservationRequestPurpose getPurpose() { - return purpose; - } - - public void setPurpose(ReservationRequestPurpose purpose) { - this.purpose = purpose; - } - - public void setDescription(String description) - { - this.description = description; - } - - public TechnologyModel getTechnology() - { - return technology; - } - - public void setTechnology(TechnologyModel technology) - { - this.technology = technology; - } - - public DateTimeZone getTimeZone() - { - return timeZone; - } - - public void setTimeZone(DateTimeZone timeZone) - { - this.timeZone = timeZone; - } - public LocalTime getStart() { return slot.getStart().toLocalTime(); @@ -333,61 +228,6 @@ public void setEnd(DateTime end) slot.setEnd(end); } - public Integer getDurationCount() - { - return durationCount; - } - - public void setDurationCount(Integer durationCount) - { - this.durationCount = durationCount; - } - - public DurationType getDurationType() - { - return durationType; - } - - public void setDurationType(DurationType durationType) - { - this.durationType = durationType; - } - - public int getSlotBeforeMinutes() - { - return slotBeforeMinutes; - } - - public String getPhysicalResourceId() { - return physicalResourceId; - } - - public void setPhysicalResourceId(String physicalResourceId) { - this.physicalResourceId = physicalResourceId; - } - - public String getAdminPin() - { - return adminPin; - } - - public void setAdminPin(String adminPin) - { - this.adminPin = adminPin; - } - - public String getGuestPin() { - return guestPin; - } - - public void setGuestPin(String guestPin) { - this.guestPin = guestPin; - } - - public Interval getCollidingInterval() { - return collidingInterval; - } - public void setCollidingInterval(Interval collidingInterval) { this.collidingInterval = collidingInterval; @@ -417,20 +257,12 @@ public void resetCollidingInterval() { collidingWithFirstSlot = false; } - public void setCollidingWithFirstSlot(boolean collidingWithFirstSlot) { - this.collidingWithFirstSlot = collidingWithFirstSlot; - } - - public boolean isCollidingWithFirstSlot() { - return collidingWithFirstSlot; - } - public String getMeetingRoomResourceName() { - if (physicalResourceId != null) { - ResourceSummary resource = cacheProvider.getResourceSummary(physicalResourceId); - if (resource != null) { - return resource.getName(); + if (resourceId != null) { + ResourceSummary resourceSummary = cacheProvider.getResourceSummary(resourceId); + if (resourceSummary != null) { + return resourceSummary.getName(); } } return null; @@ -438,19 +270,19 @@ public String getMeetingRoomResourceName() public String getMeetingRoomResourceDescription() { - ResourceSummary resource = cacheProvider.getResourceSummary(physicalResourceId); - if (resource != null) { - return resource.getDescription(); + ResourceSummary resourceSummary = cacheProvider.getResourceSummary(resourceId); + if (resourceSummary != null) { + return resourceSummary.getDescription(); } return null; } public String getMeetingRoomResourceDomain() { - if (physicalResourceId != null) { - ResourceSummary resource = cacheProvider.getResourceSummary(physicalResourceId); - if (resource != null) { - return resource.getDomainName(); + if (resourceId != null) { + ResourceSummary resourceSummary = cacheProvider.getResourceSummary(resourceId); + if (resourceSummary != null) { + return resourceSummary.getDomainName(); } } return null; @@ -466,16 +298,6 @@ public Period getSlotBefore() } } - public void setSlotBeforeMinutes(int slotBeforeMinutes) - { - this.slotBeforeMinutes = slotBeforeMinutes; - } - - public int getSlotAfterMinutes() - { - return slotAfterMinutes; - } - public Period getSlotAfter() { if (slotAfterMinutes != 0) { @@ -486,11 +308,6 @@ public Period getSlotAfter() } } - public void setSlotAfterMinutes(int slotAfterMinutes) - { - this.slotAfterMinutes = slotAfterMinutes; - } - public LocalDate getStartDate() { return slot.getStart().toLocalDate(); } @@ -499,41 +316,6 @@ public void setStartDate(LocalDate startDate) { this.slot.setStart(startDate.toDateTimeAtStartOfDay()); } - public SpecificationType getSpecificationType() - { - return specificationType; - } - - public void setSpecificationType(SpecificationType specificationType) - { - this.specificationType = specificationType; - } - - public String getRoomName() - { - return roomName; - } - - public void setRoomName(String roomName) - { - this.roomName = roomName; - } - - public String getE164Number() - { - return e164Number; - } - - public void setE164Number(String e164Number) - { - this.e164Number = e164Number; - } - - public String getRoomReservationRequestId() - { - return roomReservationRequestId; - } - public void setRoomReservationRequestId(String roomReservationRequestId) { if (roomReservationRequestId == null @@ -544,7 +326,7 @@ public void setRoomReservationRequestId(String roomReservationRequestId) } public void setPermanentRoomReservationRequestId(String permanentRoomReservationRequestId, - List permanentRooms) + List permanentRooms) { this.roomReservationRequestId = permanentRoomReservationRequestId; @@ -564,65 +346,15 @@ public void setPermanentRoomReservationRequestId(String permanentRoomReservation } } - public ReservationRequestSummary getPermanentRoomReservationRequest() - { - return permanentRoomReservationRequest; - } - - public String getRoomResourceId() - { - return roomResourceId; - } - public String getRoomResourceName() { - ResourceSummary resource = cacheProvider.getResourceSummary(roomResourceId); - if (resource != null) { - return resource.getName(); + ResourceSummary resourceSummary = cacheProvider.getResourceSummary(resourceId); + if (resourceSummary != null) { + return resourceSummary.getName(); } return null; } - public void setRoomResourceId(String roomResourceId) - { - this.roomResourceId = roomResourceId; - } - - public Integer getParticipantCount() - { - return participantCount; - } - - public void setParticipantCount(Integer participantCount) - { - this.participantCount = participantCount; - } - - public String getRoomPin() - { - return roomPin; - } - - public void setRoomPin(String roomPin) - { - this.roomPin = roomPin; - } - - public boolean isRoomRecorded() - { - return roomRecorded; - } - - public void setRoomRecorded(boolean roomRecorded) - { - this.roomRecorded = roomRecorded; - } - - public String getRoomRecordingResourceId() - { - return roomRecordingResourceId; - } - public String getRoomRecordingResourceName() { ResourceSummary resource = cacheProvider.getResourceSummary(roomRecordingResourceId); @@ -634,27 +366,12 @@ public String getRoomRecordingResourceName() } } - public void setRoomRecordingResourceId(String roomRecordingResourceId) - { - this.roomRecordingResourceId = roomRecordingResourceId; - } - - public AdobeConnectPermissions getRoomAccessMode() - { - return roomAccessMode; - } - public void setRoomAccessMode(AdobeConnectPermissions roomAccessMode) { AdobeConnectPermissions.checkIfUsableByMeetings(roomAccessMode); this.roomAccessMode = roomAccessMode; } - public List getUserRoles() - { - return userRoles; - } - public UserRoleModel getUserRole(String userRoleId) { if (userRoleId == null) { @@ -687,19 +404,6 @@ public void removeUserRole(UserRoleModel userRole) userRoles.remove(userRole); } - public boolean getAllowGuests() { - return allowGuests; - } - - public void setAllowGuests(boolean allowGuests) { - this.allowGuests = allowGuests; - } - - public List getRoomParticipants() - { - return roomParticipants; - } - public void addRoomParticipant(ParticipantModel participantModel) { if (ParticipantModel.Type.USER.equals(participantModel.getType())) { @@ -736,46 +440,6 @@ public ParticipantModel addRoomParticipant(UserInformation userInformation, Part return participantModel; } - public String getRoomMeetingName() - { - return roomMeetingName; - } - - public void setRoomMeetingName(String roomMeetingName) - { - this.roomMeetingName = roomMeetingName; - } - - public String getRoomMeetingDescription() - { - return roomMeetingDescription; - } - - public void setRoomMeetingDescription(String roomMeetingDescription) - { - this.roomMeetingDescription = roomMeetingDescription; - } - - public boolean isRoomParticipantNotificationEnabled() - { - return roomParticipantNotificationEnabled; - } - - public void setRoomParticipantNotificationEnabled(boolean roomParticipantNotificationEnabled) - { - this.roomParticipantNotificationEnabled = roomParticipantNotificationEnabled; - } - - public LocalDate getRemovedReservationDate() - { - return removedReservationDate; - } - - public void setRemovedReservationDate(LocalDate removedReservationDate) - { - this.removedReservationDate = removedReservationDate; - } - /** * Load attributes from given {@code specification}. * @@ -821,7 +485,7 @@ public void fromSpecificationApi(Specification specification, CacheProvider cach RoomEstablishment roomEstablishment = roomSpecification.getEstablishment(); if (roomEstablishment != null) { technology = TechnologyModel.find(roomEstablishment.getTechnologies()); - roomResourceId = roomEstablishment.getResourceId(); + resourceId = roomEstablishment.getResourceId(); AliasSpecification roomNameAlias = roomEstablishment.getAliasSpecificationByType(AliasType.ROOM_NAME); @@ -855,11 +519,11 @@ public void fromSpecificationApi(Specification specification, CacheProvider cach specificationType = SpecificationType.VIRTUAL_ROOM; } else { - specificationType = SpecificationType.PERMANENT_ROOM_CAPACITY; + specificationType = SpecificationType.ROOM_CAPACITY; } } else if (specification instanceof ResourceSpecification) { ResourceSpecification resourceSpecification = (ResourceSpecification) specification; - physicalResourceId = resourceSpecification.getResourceId(); + resourceId = resourceSpecification.getResourceId(); ReservationRequestSummary summary = cacheProvider.getAllocatedReservationRequestSummary(this.id); specificationType = SpecificationType.fromReservationRequestSummary(summary); } else { @@ -882,7 +546,7 @@ public void fromApi(AbstractReservationRequest abstractReservationRequest, Cache // Specification Specification specification = abstractReservationRequest.getSpecification(); fromSpecificationApi(specification, cacheProvider); - if (SpecificationType.PERMANENT_ROOM_CAPACITY.equals(specificationType)) { + if (SpecificationType.ROOM_CAPACITY.equals(specificationType)) { roomReservationRequestId = abstractReservationRequest.getReusedReservationRequestId(); } @@ -963,9 +627,9 @@ else if (slotEnd instanceof Partial) { Partial partial = (Partial) slotEnd; DateTimeField[] partialFields = partial.getFields(); if (!(partialFields.length == 3 - && partial.isSupported(DateTimeFieldType.year()) - && partial.isSupported(DateTimeFieldType.monthOfYear()) - && partial.isSupported(DateTimeFieldType.dayOfMonth()))) { + && partial.isSupported(DateTimeFieldType.year()) + && partial.isSupported(DateTimeFieldType.monthOfYear()) + && partial.isSupported(DateTimeFieldType.dayOfMonth()))) { throw new UnsupportedApiException("Slot end %s.", slotEnd); } periodicityEnd = new LocalDate(partial.getValue(0), partial.getValue(1), partial.getValue(2)); @@ -1050,7 +714,7 @@ public Specification toSpecificationApi() specification = roomSpecification; break; } - case PERMANENT_ROOM_CAPACITY: { + case ROOM_CAPACITY: { RoomSpecification roomSpecification = new RoomSpecification(); // Room availability RoomAvailability roomAvailability = roomSpecification.createAvailability(); @@ -1065,10 +729,11 @@ public Specification toSpecificationApi() specification = roomSpecification; break; } + case PHYSICAL_RESOURCE: case VEHICLE: case PARKING_PLACE: case MEETING_ROOM: { - specification = new ResourceSpecification(physicalResourceId); + specification = new ResourceSpecification(resourceId); break; } default: @@ -1118,8 +783,8 @@ public Specification toSpecificationApi() } RoomEstablishment roomEstablishment = roomSpecification.getEstablishment(); if (roomEstablishment != null) { - if (!Strings.isNullOrEmpty(roomResourceId)) { - roomEstablishment.setResourceId(roomResourceId); + if (!Strings.isNullOrEmpty(resourceId)) { + roomEstablishment.setResourceId(resourceId); } } RoomAvailability roomAvailability = roomSpecification.getAvailability(); @@ -1179,10 +844,11 @@ public Period getDuration() public void setDuration(Period duration) { switch (specificationType) { + case PHYSICAL_RESOURCE: case PARKING_PLACE: case VEHICLE: case MEETING_ROOM: - case PERMANENT_ROOM_CAPACITY: + case ROOM_CAPACITY: int minutes; try { minutes = duration.toStandardMinutes().getMinutes(); @@ -1335,8 +1001,11 @@ private DateTime getMonthFirstSlotStart(DateTime slotStart) || !PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals(periodicity.getMonthPeriodicityType())) { throw new IllegalStateException("Periodicity type has to be monthly for a specific day."); } - if (periodicity.getPeriodicityDayInMonth() == null || (periodicity.getPeriodicityDayOrder() != -1 && - (periodicity.getPeriodicityDayOrder() < 1 || periodicity.getPeriodicityDayOrder() > 4)) + if (periodicity.getPeriodicityDayInMonth() == null || + ( + periodicity.getPeriodicityDayOrder() != -1 && + (periodicity.getPeriodicityDayOrder() < 1 || periodicity.getPeriodicityDayOrder() > 4) + ) || periodicity.getPeriodicityEnd() == null) { throw new IllegalStateException("For periodicity type MONTHLY must be set day of month."); } @@ -1428,7 +1097,13 @@ public AbstractReservationRequest toApi() SortedSet slots = getSlots(DateTimeZone.UTC); // Create reservation request AbstractReservationRequest abstractReservationRequest; - if (periodicity.getType() == PeriodicDateTimeSlot.PeriodicityType.NONE) { + if (specificationType == SpecificationType.VIRTUAL_ROOM) { + cz.cesnet.shongo.controller.api.ReservationRequest reservationRequest = new ReservationRequest(); + PeriodicDateTimeSlot slot = slots.first(); + reservationRequest.setSlot(slot.getStart(), slot.getStart().plus(Duration.standardDays(730))); + abstractReservationRequest = reservationRequest; + } + else if (periodicity.getType() == PeriodicDateTimeSlot.PeriodicityType.NONE) { // Create single reservation request cz.cesnet.shongo.controller.api.ReservationRequest reservationRequest = new ReservationRequest(); PeriodicDateTimeSlot slot = slots.first(); @@ -1458,7 +1133,7 @@ public AbstractReservationRequest toApi() if (specificationType.equals(SpecificationType.VIRTUAL_ROOM)) { abstractReservationRequest.setReusement(ReservationRequestReusement.OWNED); } - else if (specificationType.equals(SpecificationType.PERMANENT_ROOM_CAPACITY)) { + else if (specificationType.equals(SpecificationType.ROOM_CAPACITY)) { abstractReservationRequest.setReusedReservationRequestId(roomReservationRequestId); } @@ -1707,7 +1382,7 @@ public ParticipantRole getDefaultOwnerParticipantRole() /** * Type of duration unit. */ - public static enum DurationType + public enum DurationType { MINUTE, HOUR, @@ -1721,7 +1396,7 @@ public static enum DurationType * @return list of reservation requests for permanent rooms */ public static List getPermanentRooms(ReservationService reservationService, - SecurityToken securityToken, Cache cache) + SecurityToken securityToken, Cache cache) { ReservationRequestListRequest request = new ReservationRequestListRequest(); request.setSecurityToken(securityToken); @@ -1759,7 +1434,7 @@ public static List getPermanentRooms(ReservationServi * @return list of deletion dependencies for reservation request with given {@code reservationRequestId} */ public static List getDeleteDependencies(String reservationRequestId, - ReservationService reservationService, SecurityToken securityToken) + ReservationService reservationService, SecurityToken securityToken) { // List reservation requests which reuse the reservation request to be deleted ReservationRequestListRequest reservationRequestListRequest = new ReservationRequestListRequest(); @@ -1783,8 +1458,7 @@ public String toString() ", end=" + getEnd() + ", excludeDates=" + periodicity.getExcludeDates() + ", roomName='" + roomName + '\'' + - ", roomResourceId='" + roomResourceId + '\'' + - ", physicalResourceId='" + physicalResourceId + '\'' + + ", resourceId='" + resourceId + '\'' + '}'; } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java index 4311fde3e..5023088c3 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java @@ -156,7 +156,7 @@ public static ReservationRequestState fromApi(AllocationState allocationState, E default: return ALLOCATED; } - case PERMANENT_ROOM_CAPACITY: + case ROOM_CAPACITY: switch (executableState) { case STARTED: return ALLOCATED_STARTED; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java index 6690447fb..9b7663009 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java @@ -18,9 +18,14 @@ public enum SpecificationType VIRTUAL_ROOM(true, false), /** - * For permanent room capacity. + * For room capacity. */ - PERMANENT_ROOM_CAPACITY(false, false), + ROOM_CAPACITY(false, false), + + /** + * For physical resource. + */ + PHYSICAL_RESOURCE(false, true), /** * For meeting room. @@ -87,7 +92,7 @@ public static SpecificationType fromReservationRequestSummary(ReservationRequest case PERMANENT_ROOM: return VIRTUAL_ROOM; case USED_ROOM: - return PERMANENT_ROOM_CAPACITY; + return ROOM_CAPACITY; case RESOURCE: String resourceTags = reservationRequestSummary.getResourceTags(); String parkTagName = configuration.getParkingPlaceTagName(); From ef78018dc6668a6292896d7321ace3f36b2195a1 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 22 Apr 2022 16:17:50 +0200 Subject: [PATCH 020/134] Edited history model to use creator name instead of id --- .../controller/rest/api/ReservationRequestController.java | 5 ++++- .../reservationrequest/ReservationRequestHistoryModel.java | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java index 36703be9b..1a7aa1173 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java @@ -211,11 +211,14 @@ ReservationRequestDetailModel getRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id) { + CacheProvider cacheProvider = new CacheProvider(cache, securityToken); ReservationRequestSummary summary = cache.getReservationRequestSummary(securityToken, id); List history = reservationService.getReservationRequestHistory(securityToken, id) - .stream().map(ReservationRequestHistoryModel::new).collect(Collectors.toList()); + .stream() + .map(item -> new ReservationRequestHistoryModel(item, cacheProvider)) + .collect(Collectors.toList()); String roomId = cache.getExecutableId(securityToken, id); RoomAuthorizedData authorizedData = null; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java index 5ffd1bc4d..959c261f5 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java @@ -3,6 +3,7 @@ import cz.cesnet.shongo.controller.api.AllocationState; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; import cz.cesnet.shongo.controller.api.ReservationRequestType; +import cz.cesnet.shongo.controller.rest.CacheProvider; import lombok.Data; import org.joda.time.DateTime; @@ -21,10 +22,10 @@ public class ReservationRequestHistoryModel { private AllocationState allocationState; private ReservationRequestState state; - public ReservationRequestHistoryModel(ReservationRequestSummary summary) { + public ReservationRequestHistoryModel(ReservationRequestSummary summary, CacheProvider cacheProvider) { this.id = summary.getId(); this.createdAt = summary.getDateTime(); - this.createdBy = summary.getUserId(); + this.createdBy = cacheProvider.getUserInformation(summary.getUserId()).getFullName(); this.type = summary.getType(); this.allocationState = summary.getAllocationState(); this.state = ReservationRequestState.fromApi(summary); From d50fc1a761f50145f8a1c650dfb0ba1efa22e428 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 28 Apr 2022 19:53:40 +0200 Subject: [PATCH 021/134] Added report controller --- .../cesnet/shongo/controller/EmailSender.java | 8 +++ .../shongo/controller/rest/ErrorHandler.java | 54 +++++++++++++++++++ .../controller/rest/api/ReportController.java | 49 +++++++++++++++++ .../controller/rest/auth/SecurityConfig.java | 6 ++- .../rest/models/report/MetaModel.java | 12 +++++ .../rest/models/report/ReportModel.java | 36 +++++++++++++ .../resources/WEB-INF/rest-api-servlet.xml | 3 ++ 7 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReportController.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/MetaModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/ReportModel.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/EmailSender.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/EmailSender.java index 28ed3c661..8ee1ecaa4 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/EmailSender.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/EmailSender.java @@ -271,6 +271,14 @@ public Email(String recipient, Collection replyTo, String subject, Strin setContent(content); } + public Email(Collection recipient, String replyTo, String subject, String content) + { + addRecipients(recipient); + addReplyTo(replyTo); + setSubject(subject); + setContent(content); + } + public void addRecipient(String recipient) { try { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java new file mode 100644 index 000000000..3fe2527fc --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java @@ -0,0 +1,54 @@ +package cz.cesnet.shongo.controller.rest; + +import cz.cesnet.shongo.controller.Controller; +import cz.cesnet.shongo.controller.ControllerConfiguration; +import cz.cesnet.shongo.controller.EmailSender; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.mail.MessagingException; +import java.util.Collection; + +/** + * Error handler. + * + * @author Martin Srom + * @author Filip Karnis + */ +@Slf4j +public class ErrorHandler +{ + + private final Controller controller; + private final ControllerConfiguration configuration; + + public ErrorHandler(@Autowired Controller controller) + { + this.controller = controller; + this.configuration = controller.getConfiguration(); + } + + /** + * Send email to administrators. + * + * @param replyTo + * @param subject + * @param content + * @return result + */ + public void sendEmailToAdministrator(String replyTo, String subject, String content) throws MessagingException { + Collection administratorEmails = configuration.getAdministratorEmails(); + if (administratorEmails.size() == 0) { + log.warn("Administrator email for sending error reports is not configured."); + return; + } + + try { + EmailSender.Email email = new EmailSender.Email(administratorEmails, replyTo, subject, content); + controller.getEmailSender().sendEmail(email); + } catch (MessagingException e) { + log.error("Failed to send email '" + subject + "':\n" + content, e); + throw e; + } + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReportController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReportController.java new file mode 100644 index 000000000..e4570b1a8 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReportController.java @@ -0,0 +1,49 @@ +package cz.cesnet.shongo.controller.rest.api; + +import cz.cesnet.shongo.controller.rest.ErrorHandler; +import cz.cesnet.shongo.controller.rest.models.report.ReportModel; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirements; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.mail.MessagingException; + +/** + * Rest controller for report endpoints. + * + * @author Filip Karnis + */ +@Slf4j +@SecurityRequirements +@RestController +@RequestMapping("/api/v1/report") +public class ReportController { + + private final ErrorHandler errorHandler; + + public ReportController(@Autowired ErrorHandler errorHandler) + { + this.errorHandler = errorHandler; + } + + /** + * Handle problem report. + */ + @Operation(summary = "Report a problem to administrators.") + @PostMapping + public void reportProblem( + @RequestBody ReportModel reportModel) throws MessagingException + { + String emailReplyTo = reportModel.getEmail(); + String emailSubject = reportModel.getEmailSubject(); + String emailContent = reportModel.getEmailContent(); + + log.info("Sending problem report: {}", reportModel); + errorHandler.sendEmailToAdministrator(emailReplyTo, emailSubject, emailContent); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java index 839370287..384ec5806 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java @@ -27,7 +27,8 @@ public void configure(WebSecurity web) { web.ignoring() .antMatchers("/domain/**") .antMatchers("/v3/api-docs") - .antMatchers("/swagger-ui/**"); + .antMatchers("/swagger-ui/**") + .antMatchers("/**/report"); } @Override @@ -38,7 +39,8 @@ protected void configure(HttpSecurity http) throws Exception { .authorizeRequests() .antMatchers("/domain/**").permitAll() .antMatchers("/v3/api-docs").permitAll() - .antMatchers("/swagger-ui/**").permitAll(); + .antMatchers("/swagger-ui/**").permitAll() + .antMatchers("/**/report").permitAll(); } @Bean diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/MetaModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/MetaModel.java new file mode 100644 index 000000000..a2667c42a --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/MetaModel.java @@ -0,0 +1,12 @@ +package cz.cesnet.shongo.controller.rest.models.report; + +import cz.cesnet.shongo.controller.rest.models.users.SettingsModel; +import lombok.Data; +import org.joda.time.DateTimeZone; + +@Data +public class MetaModel { + + private DateTimeZone timeZone; + private SettingsModel settings; +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/ReportModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/ReportModel.java new file mode 100644 index 000000000..3e76e70b9 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/ReportModel.java @@ -0,0 +1,36 @@ +package cz.cesnet.shongo.controller.rest.models.report; + +import cz.cesnet.shongo.controller.api.SecurityToken; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +/** + * Represents a report problem model. + * + * @author Martin Srom + * @author Filip Karnis + */ +@Slf4j +@Data +public class ReportModel +{ + + private String email; + + private String message; + + private String emailSubject = "Problem report"; + + /** + * Meta information about the report. + */ + private MetaModel meta; + + public String getEmailContent() + { + return "From: " + getEmail() + "\n\n" + + message + "\n\n" + + "--------------------------------------------------------------------------------\n\n" + + meta; + } +} diff --git a/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml b/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml index 3648f4aab..c8b94190f 100644 --- a/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml +++ b/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml @@ -50,4 +50,7 @@ + + + From c44f67ea36e9c56d29dc328012dd764c17a6250c Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Mon, 2 May 2022 20:33:36 +0200 Subject: [PATCH 022/134] Added physicalResourceData to reservation request --- .../api/ReservationRequestController.java | 15 +++++++++-- .../PhysicalResourceData.java | 25 +++++++++++++++++++ .../ReservationRequestDetailModel.java | 6 +++-- .../ReservationRequestModel.java | 9 +++++-- .../reservationrequest/SpecificationType.java | 15 +++++++++++ 5 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PhysicalResourceData.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java index 1a7aa1173..632c5f820 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java @@ -155,7 +155,12 @@ ListResponse listRequests( ListResponse listResponse = new ListResponse<>(); listResponse.addAll(response.getItems().stream().map(item -> { UserInformation user = cache.getUserInformation(securityToken, item.getUserId()); - return new ReservationRequestModel(item, permissionsByReservationRequestId, user); + String resource = item.getResourceId(); + ResourceSummary resourceSummary = null; + if (resource != null) { + resourceSummary = cache.getResourceSummary(securityToken, resource); + } + return new ReservationRequestModel(item, permissionsByReservationRequestId, user, resourceSummary); }).collect(Collectors.toList())); listResponse.setStart(response.getStart()); listResponse.setCount(response.getCount()); @@ -235,8 +240,14 @@ ReservationRequestDetailModel getRequest( UserInformation ownerInformation = cache.getUserInformation(securityToken, summary.getUserId()); + String resourceId = summary.getResourceId(); + ResourceSummary resourceSummary = null; + if (resourceId != null) { + resourceSummary = cacheProvider.getResourceSummary(resourceId); + } + return new ReservationRequestDetailModel( - summary, permissionsByReservationRequestId, ownerInformation, authorizedData, history + summary, permissionsByReservationRequestId, ownerInformation, authorizedData, history, resourceSummary ); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PhysicalResourceData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PhysicalResourceData.java new file mode 100644 index 000000000..76dcb2a30 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PhysicalResourceData.java @@ -0,0 +1,25 @@ +package cz.cesnet.shongo.controller.rest.models.reservationrequest; + +import cz.cesnet.shongo.controller.api.ResourceSummary; +import lombok.Data; + +@Data +public class PhysicalResourceData +{ + + private String resourceId; + private String resourceName; + private String resourceDescription; + private String periodicity; + + public static PhysicalResourceData fromApi(ResourceSummary summary) { + if (summary == null) { + return null; + } + PhysicalResourceData physicalResourceData = new PhysicalResourceData(); + physicalResourceData.setResourceId(summary.getId()); + physicalResourceData.setResourceName(summary.getName()); + physicalResourceData.setResourceDescription(summary.getDescription()); + return physicalResourceData; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java index 927d66768..dd6e3ac55 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java @@ -5,6 +5,7 @@ import cz.cesnet.shongo.controller.api.AllocationState; import cz.cesnet.shongo.controller.api.ExecutableState; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.api.ResourceSummary; import cz.cesnet.shongo.controller.rest.models.room.RoomAuthorizedData; import lombok.Data; import lombok.EqualsAndHashCode; @@ -33,9 +34,10 @@ public ReservationRequestDetailModel( Map> permissionsByReservationRequestId, UserInformation ownerInformation, RoomAuthorizedData authorizedData, - List history) + List history, + ResourceSummary resourceSummary) { - super(summary, permissionsByReservationRequestId, ownerInformation); + super(summary, permissionsByReservationRequestId, ownerInformation, resourceSummary); this.allocationState = summary.getAllocationState(); this.executableState = summary.getExecutableState(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java index abc0e1574..e1079e3f6 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java @@ -3,9 +3,11 @@ import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.ObjectPermission; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.api.ResourceSummary; import cz.cesnet.shongo.controller.rest.models.TimeInterval; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.joda.time.DateTime; import java.util.Map; @@ -33,6 +35,7 @@ public class ReservationRequestModel { private Boolean isDeprecated; private SpecificationType type; private VirtualRoomModel virtualRoomData; + private PhysicalResourceData physicalResourceData; private RoomCapacityModel roomCapacityData; private String lastReservationId; private Integer futureSlotCount; @@ -40,7 +43,8 @@ public class ReservationRequestModel { public ReservationRequestModel( ReservationRequestSummary summary, Map> permissionsByReservationRequestId, - UserInformation ownerInformation) + UserInformation ownerInformation, + ResourceSummary resourceSummary) { this.id = summary.getId(); this.description = summary.getDescription(); @@ -50,8 +54,9 @@ public ReservationRequestModel( this.ownerName = ownerInformation.getFullName(); this.ownerEmail = ownerInformation.getEmail(); this.slot = new TimeInterval(summary.getEarliestSlot()); - this.type = SpecificationType.fromReservationRequestSummary(summary); + this.type = SpecificationType.fromReservationRequestSummary(summary, true); this.virtualRoomData = new VirtualRoomModel(summary); + this.physicalResourceData = PhysicalResourceData.fromApi(resourceSummary); this.roomCapacityData = new RoomCapacityModel(summary); this.lastReservationId = summary.getLastReservationId(); this.futureSlotCount = summary.getFutureSlotCount(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java index 9b7663009..4569e735d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java @@ -9,6 +9,7 @@ * Type of specification for a reservation request. * * @author Martin Srom + * @author Filip Karnis */ public enum SpecificationType { @@ -84,6 +85,17 @@ public boolean isPhysical() { * @return {@link SpecificationType} from given {@code reservationRequestSummary} */ public static SpecificationType fromReservationRequestSummary(ReservationRequestSummary reservationRequestSummary) + { + return fromReservationRequestSummary(reservationRequestSummary, false); + } + + /** + * @param reservationRequestSummary + * @return {@link SpecificationType} from given {@code reservationRequestSummary} + */ + public static SpecificationType fromReservationRequestSummary( + ReservationRequestSummary reservationRequestSummary, + boolean onlyGeneralType) { ControllerConfiguration configuration = getControllerConfiguration(); @@ -94,6 +106,9 @@ public static SpecificationType fromReservationRequestSummary(ReservationRequest case USED_ROOM: return ROOM_CAPACITY; case RESOURCE: + if (onlyGeneralType) { + return PHYSICAL_RESOURCE; + } String resourceTags = reservationRequestSummary.getResourceTags(); String parkTagName = configuration.getParkingPlaceTagName(); String vehicleTagName = configuration.getVehicleTagName(); From 5c6512e5751db1e42413dce3a31b1988806e5830 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Mon, 2 May 2022 22:56:08 +0200 Subject: [PATCH 023/134] Added ExceptionHandler --- .../cesnet/shongo/controller/rest/Cache.java | 2 +- .../shongo/controller/rest/RoomCache.java | 2 +- .../rest/api/ParticipantController.java | 2 +- .../error/ControllerExceptionHandler.java | 62 +++++++++++++++++++ .../controller/rest/error/ErrorModel.java | 23 +++++++ .../LastOwnerRoleNotDeletableException.java | 5 ++ .../ObjectInaccessibleException.java | 4 +- .../UnsupportedApiException.java | 2 +- .../rest/models/detail/ParticipantModel.java | 2 +- .../rest/models/reservationrequest/RRR.java | 2 +- 10 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ControllerExceptionHandler.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ErrorModel.java rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{ => error}/ObjectInaccessibleException.java (81%) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{models => error}/UnsupportedApiException.java (92%) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java index bb55dcee8..f88572389 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java @@ -13,7 +13,7 @@ import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.api.rpc.ReservationService; import cz.cesnet.shongo.controller.api.rpc.ResourceService; -import cz.cesnet.shongo.controller.rest.models.UnsupportedApiException; +import cz.cesnet.shongo.controller.rest.error.ObjectInaccessibleException; import cz.cesnet.shongo.controller.rest.models.resource.ResourcesUtilization; import org.joda.time.DateTime; import org.joda.time.Duration; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java index d19634855..34065492e 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java @@ -11,7 +11,7 @@ import cz.cesnet.shongo.controller.api.SecurityToken; import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.api.rpc.ResourceControlService; -import cz.cesnet.shongo.controller.rest.models.UnsupportedApiException; +import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import org.joda.time.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java index 5b9d79fd8..0380f0995 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java @@ -7,7 +7,7 @@ import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; -import cz.cesnet.shongo.controller.rest.models.UnsupportedApiException; +import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import cz.cesnet.shongo.controller.rest.models.detail.ParticipantConfigurationModel; import cz.cesnet.shongo.controller.rest.models.detail.ParticipantModel; import io.swagger.v3.oas.annotations.Operation; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ControllerExceptionHandler.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ControllerExceptionHandler.java new file mode 100644 index 000000000..07ba65bc9 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ControllerExceptionHandler.java @@ -0,0 +1,62 @@ +package cz.cesnet.shongo.controller.rest.error; + +import cz.cesnet.shongo.CommonReportSet; +import cz.cesnet.shongo.TodoImplementException; +import cz.cesnet.shongo.controller.ControllerReportSet.ReservationRequestNotDeletableException; +import cz.cesnet.shongo.controller.ControllerReportSet.ReservationRequestDeletedException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import javax.mail.MessagingException; + +/** + * Handler for exceptions thrown while processing Rest request. + * + * @author Filip Karnis + */ +@RestControllerAdvice +public class ControllerExceptionHandler +{ + + @ExceptionHandler(TodoImplementException.class) + public ResponseEntity handleTodo(TodoImplementException e) { + return ErrorModel.createResponseFromException(e, HttpStatus.NOT_IMPLEMENTED); + } + + @ExceptionHandler(UnsupportedOperationException.class) + public ResponseEntity handleUnsupportedOperation(UnsupportedOperationException e) { + return ErrorModel.createResponseFromException(e, HttpStatus.NOT_IMPLEMENTED); + } + + @ExceptionHandler(MessagingException.class) + public ResponseEntity handleMassaging(MessagingException e) { + return ErrorModel.createResponseFromException(e, HttpStatus.SERVICE_UNAVAILABLE); + } + + @ExceptionHandler(LastOwnerRoleNotDeletableException.class) + public ResponseEntity handleLastOwnerRoleNotDeletable(LastOwnerRoleNotDeletableException e) { + return ErrorModel.createResponseFromException(e, HttpStatus.FORBIDDEN); + } + + @ExceptionHandler(ReservationRequestNotDeletableException.class) + public ResponseEntity handleReservationRequestNotDeletable(ReservationRequestNotDeletableException e) { + return ErrorModel.createResponseFromException(e, HttpStatus.FORBIDDEN); + } + + @ExceptionHandler(ObjectInaccessibleException.class) + public ResponseEntity handleObjectInaccessible(ObjectInaccessibleException e) { + return ErrorModel.createResponseFromException(e, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(ReservationRequestDeletedException.class) + public ResponseEntity handleReservationRequestDeleted(ReservationRequestDeletedException e) { + return ErrorModel.createResponseFromException(e, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(CommonReportSet.ObjectNotExistsException.class) + public ResponseEntity handleObjectNotExists(CommonReportSet.ObjectNotExistsException e) { + return ErrorModel.createResponseFromException(e, HttpStatus.NOT_FOUND); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ErrorModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ErrorModel.java new file mode 100644 index 000000000..487404ca2 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ErrorModel.java @@ -0,0 +1,23 @@ +package cz.cesnet.shongo.controller.rest.error; + +import lombok.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +/** + * Represents an error for rest error response. + * + * @author Filip Karnis + */ +@Value +public class ErrorModel +{ + + String error; + + public static ResponseEntity createResponseFromException(Exception e, HttpStatus httpStatus) + { + ErrorModel errorModel = new ErrorModel(e.getMessage()); + return new ResponseEntity<>(errorModel, httpStatus); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/LastOwnerRoleNotDeletableException.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/LastOwnerRoleNotDeletableException.java index 474f1bd44..55f2900dc 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/LastOwnerRoleNotDeletableException.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/LastOwnerRoleNotDeletableException.java @@ -1,5 +1,10 @@ package cz.cesnet.shongo.controller.rest.error; +/** + * Last owner role is not deletable. + * + * @author Filip Karnis + */ public class LastOwnerRoleNotDeletableException extends RuntimeException { public LastOwnerRoleNotDeletableException() diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ObjectInaccessibleException.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ObjectInaccessibleException.java similarity index 81% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ObjectInaccessibleException.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ObjectInaccessibleException.java index 384d9b9d7..2f2e1d045 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ObjectInaccessibleException.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ObjectInaccessibleException.java @@ -1,7 +1,7 @@ -package cz.cesnet.shongo.controller.rest; +package cz.cesnet.shongo.controller.rest.error; /** - * TODO: + * Object is inaccessible. * * @author Martin Srom */ diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/UnsupportedApiException.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/UnsupportedApiException.java similarity index 92% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/UnsupportedApiException.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/UnsupportedApiException.java index fc4fa3198..e55cc8da8 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/UnsupportedApiException.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/UnsupportedApiException.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest.models; +package cz.cesnet.shongo.controller.rest.error; /** * Unsupported API data. diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java index b5cf0e971..5045be971 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java @@ -6,7 +6,7 @@ import cz.cesnet.shongo.controller.api.*; import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.models.CommonModel; -import cz.cesnet.shongo.controller.rest.models.UnsupportedApiException; +import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import lombok.NoArgsConstructor; /** diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java index fe1eca577..9a6741eb8 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java @@ -23,7 +23,7 @@ import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; import cz.cesnet.shongo.controller.rest.models.TimeInterval; -import cz.cesnet.shongo.controller.rest.models.UnsupportedApiException; +import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import cz.cesnet.shongo.controller.rest.models.detail.ParticipantModel; import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; import cz.cesnet.shongo.controller.rest.models.users.SettingsModel; From b57051199956a8bd9d7e5dafcdd64b1ba140a648 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Tue, 3 May 2022 01:00:24 +0200 Subject: [PATCH 024/134] Added modifyRequest endpoint --- .../api/ReservationRequestController.java | 42 +++++++++++++++++++ .../rest/models/reservationrequest/RRR.java | 4 +- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java index 632c5f820..7f266a0db 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java @@ -4,6 +4,7 @@ import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.ObjectPermission; import cz.cesnet.shongo.controller.ObjectRole; +import cz.cesnet.shongo.controller.api.AbstractReservationRequest; import cz.cesnet.shongo.controller.api.AbstractRoomExecutable; import cz.cesnet.shongo.controller.api.AllocationState; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; @@ -28,6 +29,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -251,6 +253,46 @@ ReservationRequestDetailModel getRequest( ); } + @Operation(summary = "Modifies reservation request.") + @PutMapping("/{id}") + void modifyRequest( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable("id") String id, + @RequestBody RRR request) + { + CacheProvider cacheProvider = new CacheProvider(cache, securityToken); + + AbstractReservationRequest originalRequest = reservationService.getReservationRequest(securityToken, id); + RRR modifiedRequest = new RRR(originalRequest, cacheProvider); + + if (request.getRoomName() != null) { + modifiedRequest.setRoomName(request.getRoomName()); + } + if (request.getDescription() != null) { + modifiedRequest.setDescription(request.getDescription()); + } + if (request.getSlot() != null) { + modifiedRequest.setSlot(request.getSlot()); + } + if (request.getPeriodicity() != null) { + modifiedRequest.setPeriodicity(request.getPeriodicity()); + } + if (request.getResourceId() != null) { + ResourceSummary resourceSummary = cacheProvider.getResourceSummary(request.getResourceId()); + modifiedRequest.setTechnology(TechnologyModel.find(resourceSummary.getTechnologies())); + } + if (request.getAdminPin() != null) { + modifiedRequest.setAdminPin(request.getAdminPin()); + } + if (request.getParticipantCount() != null) { + modifiedRequest.setParticipantCount(request.getParticipantCount()); + } + modifiedRequest.setAllowGuests(request.isAllowGuests()); + modifiedRequest.setRoomRecorded(request.isRoomRecorded()); + + reservationService.modifyReservationRequest(securityToken, modifiedRequest.toApi()); + } + @Operation(summary = "Accepts reservation request.") @PostMapping("/{id:.+}/accept") void acceptRequest( diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java index 9a6741eb8..f696ad61b 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java @@ -164,7 +164,7 @@ public RRR(CacheProvider cacheProvider, SettingsModel userSettingsModel) */ public RRR(AbstractReservationRequest reservationRequest, CacheProvider cacheProvider) { - this.cacheProvider = cacheProvider; + this(cacheProvider); fromApi(reservationRequest, cacheProvider); // Load permanent room @@ -525,7 +525,7 @@ public void fromSpecificationApi(Specification specification, CacheProvider cach ResourceSpecification resourceSpecification = (ResourceSpecification) specification; resourceId = resourceSpecification.getResourceId(); ReservationRequestSummary summary = cacheProvider.getAllocatedReservationRequestSummary(this.id); - specificationType = SpecificationType.fromReservationRequestSummary(summary); + specificationType = SpecificationType.fromReservationRequestSummary(summary, true); } else { throw new UnsupportedApiException(specification); } From 81f21f6abe8ea5dfcc244432f20b8caf03bd3ff0 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 11 May 2022 22:13:17 +0200 Subject: [PATCH 025/134] Added delete multiple requests endpoint --- .../rest/api/ReservationRequestController.java | 9 +++++++++ .../controller/rest/models/resource/RModel.java | 12 ------------ 2 files changed, 9 insertions(+), 12 deletions(-) delete mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/RModel.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java index 7f266a0db..ceccc4bff 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java @@ -321,6 +321,15 @@ void deleteRequest( reservationService.deleteReservationRequest(securityToken, id); } + @Operation(summary = "Deletes multiple reservation requests.") + @DeleteMapping() + void deleteRequests( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @RequestBody List ids) + { + ids.forEach(id -> reservationService.deleteReservationRequest(securityToken, id)); + } + @Operation(summary = "Reverts reservation request modifications.") @PostMapping("/{id:.+}/revert") void revertRequest( diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/RModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/RModel.java deleted file mode 100644 index e2a51af04..000000000 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/RModel.java +++ /dev/null @@ -1,12 +0,0 @@ -package cz.cesnet.shongo.controller.rest.models.resource; - -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -public class RModel { - - private ResourceCapacity resourceCapacity; - private ResourceCapacityUtilization resourceCapacityUtilization; -} From 6229e1f25a43d583ab0260f0deafdf18826e6d9d Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 11 May 2022 22:15:07 +0200 Subject: [PATCH 026/134] Added capacity utilization endpoints --- .../rest/api/ResourceController.java | 96 ++++++++++--------- .../controller/rest/models/TimeInterval.java | 13 ++- .../models/resource/ReservationModel.java | 28 ++++++ .../rest/models/resource/ResourceModel.java | 4 +- .../ResourceUtilizationDetailModel.java | 39 ++++++++ .../resource/ResourceUtilizationModel.java | 49 ++++++++++ .../models/resource/ResourcesUtilization.java | 4 +- .../controller/rest/models/resource/Unit.java | 18 ++++ 8 files changed, 200 insertions(+), 51 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ReservationModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationDetailModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/Unit.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ResourceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ResourceController.java index ade920dea..788c016c8 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ResourceController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ResourceController.java @@ -1,6 +1,5 @@ package cz.cesnet.shongo.controller.rest.api; -import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.ObjectPermission; import cz.cesnet.shongo.controller.api.Resource; import cz.cesnet.shongo.controller.api.ResourceSummary; @@ -12,23 +11,25 @@ import cz.cesnet.shongo.controller.rest.models.TechnologyModel; import cz.cesnet.shongo.controller.rest.models.resource.*; import io.swagger.v3.oas.annotations.Operation; +import lombok.extern.slf4j.Slf4j; import org.joda.time.DateTime; import org.joda.time.Interval; import org.joda.time.Period; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; import java.util.*; import java.util.stream.Collectors; import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; -import static cz.cesnet.shongo.controller.rest.models.TimeInterval.DATETIME_FORMATTER; /** * Rest controller for resource endpoints. * * @author Filip Karnis */ +@Slf4j @RestController @RequestMapping("/api/v1/resources") public class ResourceController { @@ -46,14 +47,13 @@ public ResourceController(@Autowired Cache cache, @Autowired ResourceService res * Lists {@link Resource}s. */ @Operation(summary = "Lists available resources.") - @GetMapping() + @GetMapping List listResources( @RequestAttribute(TOKEN) SecurityToken securityToken, @RequestParam(value = "technology", required = false) TechnologyModel technology, @RequestParam(value = "tag", required = false) String tag) { - ResourceListRequest resourceListRequest = new ResourceListRequest(); - resourceListRequest.setSecurityToken(securityToken); + ResourceListRequest resourceListRequest = new ResourceListRequest(securityToken); resourceListRequest.setAllocatable(true); if (technology != null) { resourceListRequest.setTechnologies(technology.getTechnologies()); @@ -69,7 +69,11 @@ List listResources( return accessibleResources.getItems() .stream() - .map(ResourceModel::new) + .map(resourceSummary -> { + // TODO hasCapacity + Resource resource = resourceService.getResource(securityToken, resourceSummary.getId()); + return new ResourceModel(resourceSummary, !resource.getCapabilities().isEmpty()); + }) // Filter only resources with either technology or tag .filter(resource -> !(resource.getTechnology() == null && resource.getTags().isEmpty())) .collect(Collectors.toList()); @@ -79,60 +83,60 @@ List listResources( * Gets {@link ResourceCapacityUtilization}s. */ @Operation(summary = "Returns resource utilization.") - @GetMapping("/utilization") - ResourceCapacityUtilizationModel getResourceUtilization( + @GetMapping("/capacity_utilizations") + ListResponse listResourcesUtilization( @RequestAttribute(TOKEN) SecurityToken securityToken, - @RequestParam(value = "period") Period period, - @RequestParam(value = "start") String startParam, - @RequestParam(value = "end") String endParam, - @RequestParam(value = "refresh", required = false) boolean refresh) + @RequestParam(value = "interval_from") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + DateTime intervalFrom, + @RequestParam(value = "interval_to") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + DateTime intervalTo, + @RequestParam Unit unit, + @RequestParam(required = false) int start, + @RequestParam(required = false) int count, + @RequestParam(required = false) boolean refresh) { - DateTime start = DATETIME_FORMATTER.parseDateTime(startParam); - DateTime end = DATETIME_FORMATTER.parseDateTime(endParam); - + Period period = unit.getPeriod(); ResourcesUtilization resourcesUtilization = cache.getResourcesUtilization(securityToken, refresh); Map> utilization = - resourcesUtilization.getUtilization(new Interval(start, end), period); - - return new ResourceCapacityUtilizationModel(resourcesUtilization.getResourceCapacities(), utilization); + resourcesUtilization.getUtilization(new Interval(intervalFrom, intervalTo), period); + List items = new ArrayList<>(); + utilization.forEach((interval, resourceCapacityUtilizations) -> { + items.add(ResourceUtilizationModel.fromApi(interval, resourceCapacityUtilizations)); + }); + return ListResponse.fromRequest(start, count, items); } /** - * Gets {@link ResourceCapacityUtilization}s. + * Lists {@link Resource}s. */ - @Operation(summary = "Returns resource utilization.") - @GetMapping("/utilization2") - RModel getResourceUtilization( + @Operation(summary = "Gets resource utilization.") + @GetMapping("/{id}/capacity_utilizations") + ResourceUtilizationDetailModel getResourceUtilization( @RequestAttribute(TOKEN) SecurityToken securityToken, -// @RequestParam(value = "interval") Interval interval, -// @RequestParam(value = "resourceCapacityClass") String resourceCapacityClassName, - @RequestParam(value = "resourceId") String resourceId) throws ClassNotFoundException + @PathVariable("id") String resourceId, + @RequestParam String resource, + @RequestParam(value = "interval_from") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) DateTime intervalFrom, + @RequestParam(value = "interval_to") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) DateTime intervalTo) + throws ClassNotFoundException { -// Class resourceCapacityClass = (Class) -// Class.forName(ResourceCapacity.class.getCanonicalName() + "$" + resourceCapacityClassName); + @SuppressWarnings("unchecked") + Class resourceCapacityClass = (Class) + Class.forName(ResourceCapacity.class.getCanonicalName() + "$" + "Room"); ResourcesUtilization resourcesUtilization = cache.getResourcesUtilization(securityToken, false); ResourceCapacity resourceCapacity = -// resourcesUtilization.getResourceCapacity(resourceId, resourceCapacityClass); - resourcesUtilization.getResourceCapacity(resourceId, ResourceCapacity.Room.class); + resourcesUtilization.getResourceCapacity(resourceId, resourceCapacityClass); ResourceCapacityUtilization resourceCapacityUtilization = -// resourcesUtilization.getUtilization(resourceCapacity, interval); - resourcesUtilization.getUtilization(resourceCapacity, new Interval(DateTime.parse("2020-12-09T10:10:30"), DateTime.parse("2023-12-09T10:10:30"))); -// ModelAndView modelAndView = new ModelAndView("resourceCapacityUtilizationDescription"); + resourcesUtilization.getUtilization(resourceCapacity, new Interval(intervalFrom, intervalTo)); - Map users = new HashMap(); - if (resourceCapacityUtilization != null) { - Collection userIds = resourceCapacityUtilization.getReservationUserIds(); - cache.fetchUserInformation(securityToken, userIds); - for (String userId : userIds) { - UserInformation userInformation = cache.getUserInformation(securityToken, userId); - users.put(userId, userInformation); - } - } -// modelAndView.addObject("users", users); -// modelAndView.addObject("interval", interval); -// modelAndView.addObject("resourceCapacity", resourceCapacity); -// modelAndView.addObject("resourceCapacityUtilization", resourceCapacityUtilization); - return new RModel(resourceCapacity, resourceCapacityUtilization); + ResourceCapacity.Room roomCapacity = (ResourceCapacity.Room) resourceCapacity; + List reservations = (resourceCapacityUtilization != null) + ? resourceCapacityUtilization.getReservations() + .stream().map(res -> + ReservationModel.fromApi(res, cache.getUserInformation(securityToken, res.getUserId())) + ).collect(Collectors.toList()) + : Collections.emptyList(); + return ResourceUtilizationDetailModel.fromApi( + resourceCapacityUtilization, roomCapacity, new Interval(intervalFrom, intervalTo), reservations); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java index 8e61a0481..567b329b5 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java @@ -1,5 +1,6 @@ package cz.cesnet.shongo.controller.rest.models; +import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import lombok.NoArgsConstructor; import org.joda.time.DateTime; @@ -11,13 +12,23 @@ @NoArgsConstructor public class TimeInterval { - public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + private static final String ISO_8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormat.forPattern(ISO_8601_PATTERN); + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = ISO_8601_PATTERN) private DateTime start; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = ISO_8601_PATTERN) private DateTime end; public TimeInterval(Interval interval) { this.start = interval.getStart(); this.end = interval.getEnd(); } + + public static TimeInterval fromApi(Interval interval) { + TimeInterval timeInterval = new TimeInterval(); + timeInterval.setStart(interval.getStart()); + timeInterval.setEnd(interval.getEnd()); + return timeInterval; + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ReservationModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ReservationModel.java new file mode 100644 index 000000000..e1c4fbe20 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ReservationModel.java @@ -0,0 +1,28 @@ +package cz.cesnet.shongo.controller.rest.models.resource; + +import cz.cesnet.shongo.api.UserInformation; +import cz.cesnet.shongo.controller.api.ReservationSummary; +import cz.cesnet.shongo.controller.rest.models.TimeInterval; +import lombok.Data; + +@Data +public class ReservationModel +{ + + private String id; + private TimeInterval slot; + private int licenseCount; + private String requestId; + private UserInformation user; + + public static ReservationModel fromApi(ReservationSummary reservationSummary, UserInformation user) + { + ReservationModel reservationModel = new ReservationModel(); + reservationModel.setId(reservationSummary.getId()); + reservationModel.setSlot(TimeInterval.fromApi(reservationSummary.getSlot())); + reservationModel.setLicenseCount(reservationSummary.getRoomLicenseCount()); + reservationModel.setRequestId(reservationSummary.getReservationRequestId()); + reservationModel.setUser(user); + return reservationModel; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java index 84033f77f..6a97eb90c 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java @@ -15,13 +15,15 @@ public class ResourceModel { private String description; private TechnologyModel technology; private Set tags; + private boolean hasCapacity; - public ResourceModel(ResourceSummary summary) { + public ResourceModel(ResourceSummary summary, boolean hasCapacity) { this.id = summary.getId(); this.type = summary.getType(); this.name = summary.getName(); this.description = summary.getDescription(); this.technology = TechnologyModel.find(summary.getTechnologies()); this.tags = summary.getTags(); + this.hasCapacity = hasCapacity; } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationDetailModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationDetailModel.java new file mode 100644 index 000000000..e9210080e --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationDetailModel.java @@ -0,0 +1,39 @@ +package cz.cesnet.shongo.controller.rest.models.resource; + +import cz.cesnet.shongo.controller.rest.models.TimeInterval; +import lombok.Data; +import org.joda.time.Interval; + +import java.util.List; + +@Data +public class ResourceUtilizationDetailModel +{ + + private String id; + private String name; + private int totalCapacity; + private int usedCapacity; + private TimeInterval interval; + private List reservations; + + public static ResourceUtilizationDetailModel fromApi( + ResourceCapacityUtilization resourceCapacityUtilization, + ResourceCapacity.Room roomCapacity, + Interval interval, + List reservations) + { + int licenseCount = (resourceCapacityUtilization != null) + ? resourceCapacityUtilization.getPeakBucket().getLicenseCount() + : 0; + + ResourceUtilizationDetailModel resourceUtilizationDetailModel = new ResourceUtilizationDetailModel(); + resourceUtilizationDetailModel.setId(roomCapacity.getResourceId()); + resourceUtilizationDetailModel.setName(roomCapacity.getResourceName()); + resourceUtilizationDetailModel.setTotalCapacity(roomCapacity.getLicenseCount()); + resourceUtilizationDetailModel.setUsedCapacity(licenseCount); + resourceUtilizationDetailModel.setInterval(TimeInterval.fromApi(interval)); + resourceUtilizationDetailModel.setReservations(reservations); + return resourceUtilizationDetailModel; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java new file mode 100644 index 000000000..637df936e --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java @@ -0,0 +1,49 @@ +package cz.cesnet.shongo.controller.rest.models.resource; + +import cz.cesnet.shongo.controller.rest.models.TimeInterval; +import lombok.Data; +import org.joda.time.Interval; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Data +public class ResourceUtilizationModel +{ + + private TimeInterval interval; + private List resources; + + @Data + public static class UtilizationModel + { + + private String id; + private String name; + private int totalCapacity; + private int usedCapacity; + } + + public static ResourceUtilizationModel fromApi( + Interval interval, + Map resourceCapacityUtilizations) + { + List resources = new ArrayList<>(); + resourceCapacityUtilizations.forEach((resourceCapacity, resourceCapacityUtilization) -> { + ResourceCapacity.Room roomCapacity = (ResourceCapacity.Room) resourceCapacity; + + UtilizationModel utilizationModel = new UtilizationModel(); + utilizationModel.setId(roomCapacity.getResourceId()); + utilizationModel.setName(roomCapacity.getResourceName()); + utilizationModel.setTotalCapacity(roomCapacity.getLicenseCount()); + utilizationModel.setUsedCapacity((resourceCapacityUtilization != null) ? resourceCapacityUtilization.getPeakBucket().getLicenseCount() : 0); + resources.add(utilizationModel); + }); + + ResourceUtilizationModel resourceUtilizationModel = new ResourceUtilizationModel(); + resourceUtilizationModel.setInterval(TimeInterval.fromApi(interval)); + resourceUtilizationModel.setResources(resources); + return resourceUtilizationModel; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java index a81c41442..f039f8837 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java @@ -143,9 +143,7 @@ public Map> getUtil for (ResourceCapacity resourceCapacity : resourceCapacities) { ResourceCapacityUtilization utilization = getUtilization(resourceCapacity, utilizationInterval, true, interval); - if (utilization != null) { - utilizations.put(resourceCapacity, utilization); - } + utilizations.put(resourceCapacity, utilization); } utilizationsByInterval.put(utilizationInterval, utilizations); start = end; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/Unit.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/Unit.java new file mode 100644 index 000000000..d0a66d877 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/Unit.java @@ -0,0 +1,18 @@ +package cz.cesnet.shongo.controller.rest.models.resource; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.joda.time.Period; + +@Getter +@AllArgsConstructor +public enum Unit +{ + + DAY(Period.days(1)), + WEEK(Period.weeks(1)), + MONTH(Period.months(1)), + YEAR(Period.years(1)); + + private final Period period; +} From c763af4b18734ea08011d39600bc509890b489fa Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 12 May 2022 01:25:09 +0200 Subject: [PATCH 027/134] Changed edit request http method from put to patch --- .../controller/rest/api/ReservationRequestController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java index ceccc4bff..5721a7a1c 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java @@ -27,9 +27,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -254,7 +254,7 @@ ReservationRequestDetailModel getRequest( } @Operation(summary = "Modifies reservation request.") - @PutMapping("/{id}") + @PatchMapping("/{id}") void modifyRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable("id") String id, From 10e27f4c1e5052215fde156ef18496eb1489b30c Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 12 May 2022 01:25:41 +0200 Subject: [PATCH 028/134] Fixed default participant creation --- .../rest/api/ReservationRequestController.java | 1 + .../rest/models/reservationrequest/RRR.java | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java index 5721a7a1c..3a01ac954 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java @@ -200,6 +200,7 @@ void createRequest( // Set request to ROOM_CAPACITY for created VIRTUAL_ROOM request.setSpecificationType(SpecificationType.ROOM_CAPACITY); request.setRoomReservationRequestId(reservationId); + request.clearRoomParticipants(); } String reservationId = reservationService.createReservationRequest(securityToken, request.toApi()); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java index f696ad61b..4b3c30754 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java @@ -433,13 +433,18 @@ public ParticipantModel addRoomParticipant(UserInformation userInformation, Part ParticipantModel participantModel = new ParticipantModel(userInformation, cacheProvider); participantModel.setNewId(); participantModel.setRole(role); - participantModel.setEmail(participantModel.getEmail()); - participantModel.setName(participantModel.getName()); - participantModel.setOrganization(participantModel.getOrganization()); + participantModel.setEmail(userInformation.getEmail()); + participantModel.setName(userInformation.getFullName()); + participantModel.setOrganization(userInformation.getOrganization()); addRoomParticipant(participantModel); return participantModel; } + public void clearRoomParticipants() + { + roomParticipants.clear(); + } + /** * Load attributes from given {@code specification}. * From 1911eb3f9daedca15eba369c066f5b22f849e643 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 12 May 2022 01:28:46 +0200 Subject: [PATCH 029/134] Renamed api package to controllers, detail package to participant --- .../{api => controllers}/ParticipantController.java | 6 +++--- .../{api => controllers}/RecordingController.java | 2 +- .../rest/{api => controllers}/ReportController.java | 2 +- .../ReservationRequestController.java | 8 ++++---- .../{api => controllers}/ResourceController.java | 2 +- .../rest/{api => controllers}/RoomController.java | 2 +- .../rest/{api => controllers}/RuntimeController.java | 2 +- .../rest/{api => controllers}/UserController.java | 4 +--- .../{api => controllers}/UserRoleController.java | 2 +- .../ParticipantConfigurationModel.java | 2 +- .../{detail => participant}/ParticipantModel.java | 2 +- .../{RRR.java => ReservationRequestCreateModel.java} | 12 ++++++------ 12 files changed, 22 insertions(+), 24 deletions(-) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{api => controllers}/ParticipantController.java (97%) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{api => controllers}/RecordingController.java (98%) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{api => controllers}/ReportController.java (96%) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{api => controllers}/ReservationRequestController.java (98%) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{api => controllers}/ResourceController.java (99%) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{api => controllers}/RoomController.java (98%) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{api => controllers}/RuntimeController.java (99%) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{api => controllers}/UserController.java (98%) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{api => controllers}/UserRoleController.java (98%) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/{detail => participant}/ParticipantConfigurationModel.java (97%) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/{detail => participant}/ParticipantModel.java (98%) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/{RRR.java => ReservationRequestCreateModel.java} (99%) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java similarity index 97% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java index 0380f0995..96be72a08 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ParticipantController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest.api; +package cz.cesnet.shongo.controller.rest.controllers; import cz.cesnet.shongo.CommonReportSet; import cz.cesnet.shongo.ParticipantRole; @@ -8,8 +8,8 @@ import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; -import cz.cesnet.shongo.controller.rest.models.detail.ParticipantConfigurationModel; -import cz.cesnet.shongo.controller.rest.models.detail.ParticipantModel; +import cz.cesnet.shongo.controller.rest.models.participant.ParticipantConfigurationModel; +import cz.cesnet.shongo.controller.rest.models.participant.ParticipantModel; import io.swagger.v3.oas.annotations.Operation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RecordingController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java similarity index 98% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RecordingController.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java index b3cc35b11..8255ec3f6 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RecordingController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest.api; +package cz.cesnet.shongo.controller.rest.controllers; import cz.cesnet.shongo.controller.api.ResourceRecording; import cz.cesnet.shongo.controller.api.SecurityToken; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReportController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java similarity index 96% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReportController.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java index e4570b1a8..6633dd386 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReportController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest.api; +package cz.cesnet.shongo.controller.rest.controllers; import cz.cesnet.shongo.controller.rest.ErrorHandler; import cz.cesnet.shongo.controller.rest.models.report.ReportModel; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java similarity index 98% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 3a01ac954..1f3a19196 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest.api; +package cz.cesnet.shongo.controller.rest.controllers; import cz.cesnet.shongo.Temporal; import cz.cesnet.shongo.api.UserInformation; @@ -173,7 +173,7 @@ ListResponse listRequests( @PostMapping() void createRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, - @RequestBody RRR request) + @RequestBody ReservationRequestCreateModel request) { CacheProvider cacheProvider = new CacheProvider(cache, securityToken); request.setCacheProvider(cacheProvider); @@ -259,12 +259,12 @@ ReservationRequestDetailModel getRequest( void modifyRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable("id") String id, - @RequestBody RRR request) + @RequestBody ReservationRequestCreateModel request) { CacheProvider cacheProvider = new CacheProvider(cache, securityToken); AbstractReservationRequest originalRequest = reservationService.getReservationRequest(securityToken, id); - RRR modifiedRequest = new RRR(originalRequest, cacheProvider); + ReservationRequestCreateModel modifiedRequest = new ReservationRequestCreateModel(originalRequest, cacheProvider); if (request.getRoomName() != null) { modifiedRequest.setRoomName(request.getRoomName()); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ResourceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java similarity index 99% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ResourceController.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java index 788c016c8..1bd6612cc 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/ResourceController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest.api; +package cz.cesnet.shongo.controller.rest.controllers; import cz.cesnet.shongo.controller.ObjectPermission; import cz.cesnet.shongo.controller.api.Resource; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RoomController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java similarity index 98% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RoomController.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java index 40b3855fa..065475777 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RoomController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest.api; +package cz.cesnet.shongo.controller.rest.controllers; import cz.cesnet.shongo.controller.api.AbstractRoomExecutable; import cz.cesnet.shongo.controller.api.ExecutableSummary; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RuntimeController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java similarity index 99% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RuntimeController.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java index 5a7978908..742d8387f 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/RuntimeController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest.api; +package cz.cesnet.shongo.controller.rest.controllers; import cz.cesnet.shongo.api.Alias; import cz.cesnet.shongo.api.MediaData; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/UserController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java similarity index 98% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/UserController.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java index 896c77bf6..57942e489 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/UserController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest.api; +package cz.cesnet.shongo.controller.rest.controllers; import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.SystemPermission; @@ -12,12 +12,10 @@ import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.models.users.SettingsModel; import io.swagger.v3.oas.annotations.Operation; -import org.joda.time.DateTimeZone; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; -import java.util.Locale; import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/UserRoleController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java similarity index 98% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/UserRoleController.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java index b624a32d8..4a59770fc 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/api/UserRoleController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest.api; +package cz.cesnet.shongo.controller.rest.controllers; import cz.cesnet.shongo.controller.ObjectRole; import cz.cesnet.shongo.controller.api.AclEntry; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantConfigurationModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantConfigurationModel.java similarity index 97% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantConfigurationModel.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantConfigurationModel.java index 17dec1437..e729c374b 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantConfigurationModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantConfigurationModel.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest.models.detail; +package cz.cesnet.shongo.controller.rest.models.participant; import cz.cesnet.shongo.ParticipantRole; import org.slf4j.Logger; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java similarity index 98% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java index 5045be971..9151d1edc 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/detail/ParticipantModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest.models.detail; +package cz.cesnet.shongo.controller.rest.models.participant; import cz.cesnet.shongo.ParticipantRole; import cz.cesnet.shongo.TodoImplementException; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java similarity index 99% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java index 4b3c30754..9ac7af8f7 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RRR.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java @@ -24,7 +24,7 @@ import cz.cesnet.shongo.controller.rest.models.TechnologyModel; import cz.cesnet.shongo.controller.rest.models.TimeInterval; import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; -import cz.cesnet.shongo.controller.rest.models.detail.ParticipantModel; +import cz.cesnet.shongo.controller.rest.models.participant.ParticipantModel; import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; import cz.cesnet.shongo.controller.rest.models.users.SettingsModel; import cz.cesnet.shongo.util.SlotHelper; @@ -43,7 +43,7 @@ */ @Slf4j @Data -public class RRR //implements ReportModel.ContextSerializable +public class ReservationRequestCreateModel //implements ReportModel.ContextSerializable { @JsonIgnore @@ -130,7 +130,7 @@ public class RRR //implements ReportModel.ContextSerializable /** * Create new {@link ReservationRequestModel} from scratch. */ - public RRR() + public ReservationRequestCreateModel() { periodicity = new PeriodicityModel(); periodicity.setType(PeriodicDateTimeSlot.PeriodicityType.NONE); @@ -142,7 +142,7 @@ public RRR() /** * Create new {@link ReservationRequestModel} from scratch. */ - public RRR(CacheProvider cacheProvider) + public ReservationRequestCreateModel(CacheProvider cacheProvider) { this(); this.cacheProvider = cacheProvider; @@ -151,7 +151,7 @@ public RRR(CacheProvider cacheProvider) /** * Create new {@link ReservationRequestModel} from scratch. */ - public RRR(CacheProvider cacheProvider, SettingsModel userSettingsModel) + public ReservationRequestCreateModel(CacheProvider cacheProvider, SettingsModel userSettingsModel) { this(cacheProvider); initByUserSettings(userSettingsModel); @@ -162,7 +162,7 @@ public RRR(CacheProvider cacheProvider, SettingsModel userSettingsModel) * * @param reservationRequest */ - public RRR(AbstractReservationRequest reservationRequest, CacheProvider cacheProvider) + public ReservationRequestCreateModel(AbstractReservationRequest reservationRequest, CacheProvider cacheProvider) { this(cacheProvider); fromApi(reservationRequest, cacheProvider); From c9627920a2280061c0e35af9d8c16fd7366f1685 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 12 May 2022 17:47:40 +0200 Subject: [PATCH 030/134] Moved api path constants to ClientWebUrl class --- .../shongo/controller/rest/ClientWebUrl.java | 45 ++++++++++++++++ .../controllers/ParticipantController.java | 21 ++++---- .../rest/controllers/RecordingController.java | 7 +-- .../rest/controllers/ReportController.java | 3 +- .../ReservationRequestController.java | 53 ++++++++++--------- .../rest/controllers/ResourceController.java | 7 +-- .../rest/controllers/RoomController.java | 11 ++-- .../rest/controllers/RuntimeController.java | 15 +++--- .../rest/controllers/UserController.java | 15 +++--- .../rest/controllers/UserRoleController.java | 23 ++++---- 10 files changed, 127 insertions(+), 73 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java new file mode 100644 index 000000000..7e1fb7fd5 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java @@ -0,0 +1,45 @@ +package cz.cesnet.shongo.controller.rest; + +public class ClientWebUrl +{ + + public static final String API_PREFIX = "/api/v1"; + public static final String ID_SUFFIX = "/{id:.+}"; + public static final String ENTITY_SUFFIX = "/{entityId:.+}"; + + public static final String REPORT = API_PREFIX + "/report"; + + public static final String USERS_AND_GROUPS = API_PREFIX; + public static final String SETTINGS = "/settings"; + public static final String USERS_LIST = "/users"; + public static final String USERS_DETAIL = "/users/{userId:.+}"; + public static final String GROUPS_LIST = "/groups"; + public static final String GROUPS_DETAIL = "/groups/{groupId:.+}"; + + public static final String RESOURCES = API_PREFIX + "/resources"; + public static final String CAPACITY_UTILIZATION = "/capacity_utilizations"; + public static final String CAPACITY_UTILIZATION_DETAIL = "/{id}/capacity_utilizations"; + + public static final String RESERVATION_REQUESTS = API_PREFIX + "/reservation_requests"; + public static final String RESERVATION_REQUESTS_ACCEPT = ID_SUFFIX + "/accept"; + public static final String RESERVATION_REQUESTS_REJECT = ID_SUFFIX + "/reject"; + public static final String RESERVATION_REQUESTS_REVERT = ID_SUFFIX + "/revert"; + + public static final String PARTICIPANTS = API_PREFIX + "/reservation_requests/{id:.+}/participants"; + public static final String PARTICIPANTS_ID_SUFFIX = "/{participantId:.+}"; + + public static final String RECORDINGS = API_PREFIX + "/reservation_requests/{id:.+}/recordings"; + public static final String RECORDINGS_ID_SUFFIX = "/{recordingId:.+}"; + + public static final String ROOMS = API_PREFIX + "/rooms"; + + public static final String RUNTIME_MANAGEMENT = API_PREFIX + "/reservation_requests/{id:.+}/runtime_management"; + public static final String RUNTIME_MANAGEMENT_PARTICIPANTS = "/participants"; + public static final String RUNTIME_MANAGEMENT_PARTICIPANTS_MODIFY = "/participants/{participantId:.+}"; + public static final String RUNTIME_MANAGEMENT_PARTICIPANTS_DISCONNECT = "/participants/{participantId:.+}/disconnect"; + public static final String RUNTIME_MANAGEMENT_PARTICIPANTS_SNAPSHOT = "/participants/{participantId:.+}/snapshot"; + public static final String RUNTIME_MANAGEMENT_RECORDING_START = "/recording/start"; + public static final String RUNTIME_MANAGEMENT_RECORDING_STOP = "/recording/stop"; + + public static final String ROLES = API_PREFIX + "/reservation_requests/{id:.+}/roles"; +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java index 96be72a08..870760cd6 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java @@ -7,6 +7,7 @@ import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; +import cz.cesnet.shongo.controller.rest.ClientWebUrl; import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import cz.cesnet.shongo.controller.rest.models.participant.ParticipantConfigurationModel; import cz.cesnet.shongo.controller.rest.models.participant.ParticipantModel; @@ -27,7 +28,7 @@ * @author Filip Karnis */ @RestController -@RequestMapping("/api/v1/reservation_requests/{id:.+}/participants") +@RequestMapping(ClientWebUrl.PARTICIPANTS) public class ParticipantController { private final Cache cache; @@ -42,7 +43,7 @@ public ParticipantController( } @Operation(summary = "Lists reservation request participants.") - @GetMapping() + @GetMapping ListResponse listRequestParticipants( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -84,10 +85,10 @@ ListResponse listRequestParticipants( } @Operation(summary = "Adds new participant to reservation request.") - @PostMapping() + @PostMapping void addParticipant( @RequestAttribute(TOKEN) SecurityToken securityToken, - @PathVariable("id") String id, + @PathVariable String id, @RequestBody ParticipantModel newParticipant) { String executableId = cache.getExecutableId(securityToken, id); @@ -113,11 +114,11 @@ void addParticipant( } @Operation(summary = "Adds new participant to reservation request.") - @PutMapping("/{participantId:.+}") + @PutMapping(ClientWebUrl.PARTICIPANTS_ID_SUFFIX) void updateParticipant( @RequestAttribute(TOKEN) SecurityToken securityToken, - @PathVariable("id") String id, - @PathVariable("participantId") String participantId, + @PathVariable String id, + @PathVariable String participantId, @RequestParam ParticipantRole role) { String executableId = cache.getExecutableId(securityToken, id); @@ -137,11 +138,11 @@ void updateParticipant( } @Operation(summary = "Removes participant from reservation request.") - @DeleteMapping("/{participantId:.+}") + @DeleteMapping(ClientWebUrl.PARTICIPANTS_ID_SUFFIX) void removeParticipant( @RequestAttribute(TOKEN) SecurityToken securityToken, - @PathVariable("id") String id, - @PathVariable("participantId") String participantId) + @PathVariable String id, + @PathVariable String participantId) { String executableId = cache.getExecutableId(securityToken, id); AbstractRoomExecutable roomExecutable = getRoomExecutable(securityToken, executableId); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java index 8255ec3f6..c0071405d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java @@ -7,6 +7,7 @@ import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.api.rpc.ResourceControlService; import cz.cesnet.shongo.controller.rest.Cache; +import cz.cesnet.shongo.controller.rest.ClientWebUrl; import cz.cesnet.shongo.controller.scheduler.SchedulerReportSet; import io.swagger.v3.oas.annotations.Operation; import org.joda.time.Duration; @@ -26,7 +27,7 @@ * @author Filip Karnis */ @RestController -@RequestMapping("/api/v1/reservation_requests/{id:.+}/recordings") +@RequestMapping(ClientWebUrl.RECORDINGS) public class RecordingController { private final Cache cache; @@ -44,7 +45,7 @@ public RecordingController( } @Operation(summary = "Lists reservation request recordings.") - @GetMapping() + @GetMapping Map listRequestRecordings( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -99,7 +100,7 @@ Map listRequestRecordings( } @Operation(summary = "Deletes recording from reservation request.") - @DeleteMapping("/{recordingId:.+}") + @DeleteMapping(ClientWebUrl.RECORDINGS_ID_SUFFIX) void deleteRequestRecording( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java index 6633dd386..67fef5232 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java @@ -1,5 +1,6 @@ package cz.cesnet.shongo.controller.rest.controllers; +import cz.cesnet.shongo.controller.rest.ClientWebUrl; import cz.cesnet.shongo.controller.rest.ErrorHandler; import cz.cesnet.shongo.controller.rest.models.report.ReportModel; import io.swagger.v3.oas.annotations.Operation; @@ -21,7 +22,7 @@ @Slf4j @SecurityRequirements @RestController -@RequestMapping("/api/v1/report") +@RequestMapping(ClientWebUrl.REPORT) public class ReportController { private final ErrorHandler errorHandler; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 1f3a19196..3ab1e94be 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -17,6 +17,7 @@ import cz.cesnet.shongo.controller.api.rpc.ReservationService; import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; +import cz.cesnet.shongo.controller.rest.ClientWebUrl; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; import cz.cesnet.shongo.controller.rest.models.reservationrequest.*; import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; @@ -52,7 +53,7 @@ * @author Filip Karnis */ @RestController -@RequestMapping("/api/v1/reservation_requests") +@RequestMapping(ClientWebUrl.RESERVATION_REQUESTS) public class ReservationRequestController { private final Cache cache; @@ -73,7 +74,7 @@ public ReservationRequestController( } @Operation(summary = "Lists reservation requests.") - @GetMapping() + @GetMapping ListResponse listRequests( @RequestAttribute(TOKEN) SecurityToken securityToken, @RequestParam(value = "start", required = false) Integer start, @@ -170,7 +171,7 @@ ListResponse listRequests( } @Operation(summary = "Creates reservation request.") - @PostMapping() + @PostMapping void createRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @RequestBody ReservationRequestCreateModel request) @@ -214,7 +215,7 @@ void createRequest( } @Operation(summary = "Returns reservation request.") - @GetMapping("/{id:.+}") + @GetMapping(ClientWebUrl.ID_SUFFIX) ReservationRequestDetailModel getRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id) @@ -255,10 +256,10 @@ ReservationRequestDetailModel getRequest( } @Operation(summary = "Modifies reservation request.") - @PatchMapping("/{id}") + @PatchMapping(ClientWebUrl.ID_SUFFIX) void modifyRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, - @PathVariable("id") String id, + @PathVariable String id, @RequestBody ReservationRequestCreateModel request) { CacheProvider cacheProvider = new CacheProvider(cache, securityToken); @@ -294,45 +295,45 @@ void modifyRequest( reservationService.modifyReservationRequest(securityToken, modifiedRequest.toApi()); } - @Operation(summary = "Accepts reservation request.") - @PostMapping("/{id:.+}/accept") - void acceptRequest( + @Operation(summary = "Deletes reservation request.") + @DeleteMapping(ClientWebUrl.ID_SUFFIX) + void deleteRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id) { - reservationService.confirmReservationRequest(securityToken, id, true); + reservationService.deleteReservationRequest(securityToken, id); } - @Operation(summary = "Rejects reservation request.") - @PostMapping("/{id:.+}/reject") - void rejectRequest( + @Operation(summary = "Deletes multiple reservation requests.") + @DeleteMapping + void deleteRequests( @RequestAttribute(TOKEN) SecurityToken securityToken, - @PathVariable String id, - @RequestParam(required = false) String reason) + @RequestBody List ids) { - reservationService.denyReservationRequest(securityToken, id, reason); + ids.forEach(id -> reservationService.deleteReservationRequest(securityToken, id)); } - @Operation(summary = "Deletes reservation request.") - @DeleteMapping("/{id:.+}") - void deleteRequest( + @Operation(summary = "Accepts reservation request.") + @PostMapping(ClientWebUrl.RESERVATION_REQUESTS_ACCEPT) + void acceptRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id) { - reservationService.deleteReservationRequest(securityToken, id); + reservationService.confirmReservationRequest(securityToken, id, true); } - @Operation(summary = "Deletes multiple reservation requests.") - @DeleteMapping() - void deleteRequests( + @Operation(summary = "Rejects reservation request.") + @PostMapping(ClientWebUrl.RESERVATION_REQUESTS_REJECT) + void rejectRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, - @RequestBody List ids) + @PathVariable String id, + @RequestParam(required = false) String reason) { - ids.forEach(id -> reservationService.deleteReservationRequest(securityToken, id)); + reservationService.denyReservationRequest(securityToken, id, reason); } @Operation(summary = "Reverts reservation request modifications.") - @PostMapping("/{id:.+}/revert") + @PostMapping(ClientWebUrl.RESERVATION_REQUESTS_REVERT) void revertRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java index 1bd6612cc..852a9ccae 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java @@ -8,6 +8,7 @@ import cz.cesnet.shongo.controller.api.request.ResourceListRequest; import cz.cesnet.shongo.controller.api.rpc.ResourceService; import cz.cesnet.shongo.controller.rest.Cache; +import cz.cesnet.shongo.controller.rest.ClientWebUrl; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; import cz.cesnet.shongo.controller.rest.models.resource.*; import io.swagger.v3.oas.annotations.Operation; @@ -31,7 +32,7 @@ */ @Slf4j @RestController -@RequestMapping("/api/v1/resources") +@RequestMapping(ClientWebUrl.RESOURCES) public class ResourceController { private final Cache cache; @@ -83,7 +84,7 @@ List listResources( * Gets {@link ResourceCapacityUtilization}s. */ @Operation(summary = "Returns resource utilization.") - @GetMapping("/capacity_utilizations") + @GetMapping(ClientWebUrl.CAPACITY_UTILIZATION) ListResponse listResourcesUtilization( @RequestAttribute(TOKEN) SecurityToken securityToken, @RequestParam(value = "interval_from") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) @@ -110,7 +111,7 @@ ListResponse listResourcesUtilization( * Lists {@link Resource}s. */ @Operation(summary = "Gets resource utilization.") - @GetMapping("/{id}/capacity_utilizations") + @GetMapping(ClientWebUrl.CAPACITY_UTILIZATION_DETAIL) ResourceUtilizationDetailModel getResourceUtilization( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable("id") String resourceId, diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java index 065475777..1c4437abf 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java @@ -7,6 +7,7 @@ import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.rest.Cache; +import cz.cesnet.shongo.controller.rest.ClientWebUrl; import cz.cesnet.shongo.controller.rest.models.room.RoomAuthorizedData; import cz.cesnet.shongo.controller.rest.models.room.RoomModel; import io.swagger.v3.oas.annotations.Operation; @@ -23,7 +24,7 @@ * @author Filip Karnis */ @RestController -@RequestMapping("/api/v1/rooms") +@RequestMapping(ClientWebUrl.ROOMS) public class RoomController { private final Cache cache; @@ -35,7 +36,7 @@ public RoomController(@Autowired Cache cache, @Autowired ExecutableService execu } @Operation(summary = "Lists rooms (executables).") - @GetMapping("") + @GetMapping public ListResponse listRooms( @RequestAttribute(TOKEN) SecurityToken securityToken, @RequestParam(value = "start", required = false) Integer start, @@ -73,12 +74,12 @@ public ListResponse listRooms( } @Operation(summary = "Gets room's (executable's) authorized data.") - @GetMapping("/{objectId}") + @GetMapping(ClientWebUrl.ID_SUFFIX) public RoomAuthorizedData getRoom( @RequestAttribute(TOKEN) SecurityToken securityToken, - @PathVariable("objectId") String objectId) + @PathVariable String id) { - String roomId = cache.getExecutableId(securityToken, objectId); + String roomId = cache.getExecutableId(securityToken, id); AbstractRoomExecutable roomExecutable = (AbstractRoomExecutable) executableService.getExecutable(securityToken, roomId); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java index 742d8387f..aaed05926 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java @@ -10,6 +10,7 @@ import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; +import cz.cesnet.shongo.controller.rest.ClientWebUrl; import cz.cesnet.shongo.controller.rest.RoomCache; import io.swagger.v3.oas.annotations.Operation; import lombok.extern.slf4j.Slf4j; @@ -31,7 +32,7 @@ */ @Slf4j @RestController -@RequestMapping("/api/v1/reservation_requests/{id:.+}/runtime_management") +@RequestMapping(ClientWebUrl.RUNTIME_MANAGEMENT) public class RuntimeController { private final Cache cache; @@ -49,7 +50,7 @@ public RuntimeController( } @Operation(summary = "Lists reservation request runtime participants.") - @GetMapping("/participants") + @GetMapping(ClientWebUrl.RUNTIME_MANAGEMENT_PARTICIPANTS) Map listRuntimeParticipants( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -97,7 +98,7 @@ Map listRuntimeParticipants( } @Operation(summary = "Takes snapshot of reservation request runtime participant.") - @PostMapping("/participants/{participantId:.+}/snapshot") + @PostMapping(ClientWebUrl.RUNTIME_MANAGEMENT_PARTICIPANTS_SNAPSHOT) ResponseEntity snapshotRuntimeParticipant( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -122,7 +123,7 @@ ResponseEntity snapshotRuntimeParticipant( } @Operation(summary = "Modifies reservation request runtime participant.") - @PatchMapping("/participants/{participantId:.+}") + @PatchMapping(ClientWebUrl.RUNTIME_MANAGEMENT_PARTICIPANTS_MODIFY) void modifyRuntimeParticipant( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -170,7 +171,7 @@ void modifyRuntimeParticipant( } @Operation(summary = "Disconnects reservation request runtime participant.") - @PostMapping("/participants/{participantId:.+}/disconnect") + @PostMapping(ClientWebUrl.RUNTIME_MANAGEMENT_PARTICIPANTS_DISCONNECT) void disconnectRuntimeParticipant( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -181,7 +182,7 @@ void disconnectRuntimeParticipant( } @Operation(summary = "Starts recording of reservation request runtime.") - @PostMapping("/recording/start") + @PostMapping(ClientWebUrl.RUNTIME_MANAGEMENT_RECORDING_START) void startRequestRecording( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -217,7 +218,7 @@ void startRequestRecording( } @Operation(summary = "Stops recording of reservation request runtime.") - @PostMapping("/recording/stop") + @PostMapping(ClientWebUrl.RUNTIME_MANAGEMENT_RECORDING_STOP) void stopRequestRecording( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java index 57942e489..8ae8419cc 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java @@ -10,6 +10,7 @@ import cz.cesnet.shongo.controller.api.request.UserListRequest; import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; import cz.cesnet.shongo.controller.rest.Cache; +import cz.cesnet.shongo.controller.rest.ClientWebUrl; import cz.cesnet.shongo.controller.rest.models.users.SettingsModel; import io.swagger.v3.oas.annotations.Operation; import org.springframework.beans.factory.annotation.Autowired; @@ -25,7 +26,7 @@ * @author Filip Karnis */ @RestController -@RequestMapping("/api/v1") +@RequestMapping(ClientWebUrl.USERS_AND_GROUPS) public class UserController { private final AuthorizationService authorizationService; @@ -43,7 +44,7 @@ public UserController(@Autowired AuthorizationService reservationService, @Autow * @return list of {@link UserInformation}s */ @Operation(summary = "Lists users.") - @GetMapping("/users") + @GetMapping(ClientWebUrl.USERS_LIST) public ListResponse getUsers( @RequestAttribute(TOKEN) SecurityToken securityToken, @RequestParam(value = "filter", required = false) String filter, @@ -66,7 +67,7 @@ public ListResponse getUsers( * @return {@link UserInformation} */ @Operation(summary = "Returns information about user.") - @GetMapping("/users/{userId:.+}") + @GetMapping(ClientWebUrl.USERS_DETAIL) public UserInformation getUser( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String userId) @@ -80,7 +81,7 @@ public UserInformation getUser( * @return {@link SettingsModel} */ @Operation(summary = "Returns user's settings.") - @GetMapping("/settings") + @GetMapping(ClientWebUrl.SETTINGS) public SettingsModel getUserSettings(@RequestAttribute(TOKEN) SecurityToken securityToken) { UserSettings settings = authorizationService.getUserSettings(securityToken); @@ -94,7 +95,7 @@ public SettingsModel getUserSettings(@RequestAttribute(TOKEN) SecurityToken secu * @param newSettings new settings of user */ @Operation(summary = "Updates user's settings.") - @PutMapping("/settings") + @PutMapping(ClientWebUrl.SETTINGS) public SettingsModel updateUserSettings( @RequestAttribute(TOKEN) SecurityToken securityToken, @RequestBody UserSettings newSettings) @@ -122,7 +123,7 @@ public SettingsModel updateUserSettings( * @return list of {@link Group}s */ @Operation(summary = "Lists groups.") - @GetMapping("/groups") + @GetMapping(ClientWebUrl.GROUPS_LIST) public ListResponse getGroups( @RequestAttribute(TOKEN) SecurityToken securityToken, @RequestParam(value = "filter", required = false) String filter) @@ -141,7 +142,7 @@ public ListResponse getGroups( * @return {@link Group} */ @Operation(summary = "Returns information about group.") - @GetMapping("/groups/{groupId:.+}") + @GetMapping(ClientWebUrl.GROUPS_DETAIL) public Group getGroup( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String groupId) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java index 4a59770fc..630d2a94d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java @@ -8,6 +8,7 @@ import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; +import cz.cesnet.shongo.controller.rest.ClientWebUrl; import cz.cesnet.shongo.controller.rest.error.LastOwnerRoleNotDeletableException; import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; import io.swagger.v3.oas.annotations.Operation; @@ -25,7 +26,7 @@ * @author Filip Karnis */ @RestController -@RequestMapping("/api/v1/reservation_requests/{id:.+}/roles") +@RequestMapping(ClientWebUrl.ROLES) public class UserRoleController { private final AuthorizationService authorizationService; @@ -37,10 +38,10 @@ public UserRoleController(@Autowired AuthorizationService reservationService, @A } @Operation(summary = "Lists reservation request roles.") - @GetMapping() + @GetMapping ListResponse listRequestRoles( @RequestAttribute(TOKEN) SecurityToken securityToken, - @PathVariable(value = "id") String objectId, + @PathVariable String id, @RequestParam(value = "start", required = false) Integer start, @RequestParam(value = "count", required = false) Integer count) { @@ -48,7 +49,7 @@ ListResponse listRequestRoles( request.setSecurityToken(securityToken); request.setStart(start); request.setCount(count); - request.addObjectId(objectId); + request.addObjectId(id); ListResponse aclEntries = authorizationService.listAclEntries(request); CacheProvider cacheProvider = new CacheProvider(cache, securityToken); @@ -57,26 +58,26 @@ ListResponse listRequestRoles( } @Operation(summary = "Creates new role for reservation request.") - @PostMapping() + @PostMapping void createRequestRoles( @RequestAttribute(TOKEN) SecurityToken securityToken, - @PathVariable("id") String objectId, + @PathVariable String id, @RequestBody UserRoleModel userRoleModel) { - String reservationRequestId = cache.getReservationRequestId(securityToken, objectId); + String reservationRequestId = cache.getReservationRequestId(securityToken, id); userRoleModel.setObjectId(reservationRequestId); userRoleModel.setDeletable(true); authorizationService.createAclEntry(securityToken, userRoleModel.toApi()); } @Operation(summary = "Deletes role for reservation request.") - @DeleteMapping("/{entityId:.+}") + @DeleteMapping(ClientWebUrl.ENTITY_SUFFIX) void deleteRequestRoles( @RequestAttribute(TOKEN) SecurityToken securityToken, - @PathVariable("id") String objectId, - @PathVariable("entityId") String entityId) + @PathVariable String id, + @PathVariable String entityId) { - String reservationRequestId = cache.getReservationRequestId(securityToken, objectId); + String reservationRequestId = cache.getReservationRequestId(securityToken, id); AclEntryListRequest request = new AclEntryListRequest(); request.setSecurityToken(securityToken); request.addObjectId(reservationRequestId); From d256b0ceee695b89fdd9fa9cf8fb750a99d913eb Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 12 May 2022 18:15:08 +0200 Subject: [PATCH 031/134] Added CORS origin congiguration --- .../shongo/controller/ControllerConfiguration.java | 10 ++++++++++ .../shongo/controller/rest/auth/SecurityConfig.java | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java index 0709cb166..eb0c51e98 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java @@ -19,6 +19,7 @@ import java.util.*; import java.util.regex.MatchResult; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * Configuration for the {@link Controller}. @@ -67,6 +68,7 @@ public class ControllerConfiguration extends CombinedConfiguration public static final String REST_API = "rest-api"; public static final String REST_API_HOST = "rest-api.host"; public static final String REST_API_PORT = "rest-api.port"; + public static final String REST_API_ORIGIN = "rest-api.origin"; public static final String REST_API_SSL_KEY_STORE = REST_API + ".ssl-key-store"; public static final String REST_API_SSL_KEY_STORE_TYPE = REST_API + ".ssl-key-store-type"; public static final String REST_API_SSL_KEY_STORE_PASSWORD = REST_API + ".ssl-key-store-password"; @@ -531,6 +533,14 @@ public Integer getRESTApiPort() return port != null ? port : 9999; } + /** + * @return list of allowed origins for CORS configuration. + */ + public synchronized List getRESTApiAllowedOrigins() + { + return getList(REST_API_ORIGIN).stream().map(origin -> (String) origin).collect(Collectors.toList()); + } + public String getRESTApiSslKeyStore() { String sslKeyStore = getString(ControllerConfiguration.REST_API_SSL_KEY_STORE); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java index 384ec5806..90a3e3585 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java @@ -1,5 +1,6 @@ package cz.cesnet.shongo.controller.rest.auth; +import cz.cesnet.shongo.controller.Controller; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -47,8 +48,13 @@ protected void configure(HttpSecurity http) throws Exception { CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration().applyPermitDefaultValues(); configuration.setAllowedMethods(List.of(CorsConfiguration.ALL)); + configuration.setAllowedOrigins(allowedOrigins()); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } + + private List allowedOrigins() { + return Controller.getInstance().getConfiguration().getRESTApiAllowedOrigins(); + } } From 1c8c3d9a5d4ee2c962c33a1ae92111ce3b45a1f6 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 12 May 2022 18:18:59 +0200 Subject: [PATCH 032/134] Moved AuthFilter, SecurityConfig, and OpenApiConfig to config package --- .../shongo/controller/rest/{ => config}/OpenApiConfig.java | 2 +- .../controller/rest/{auth => config/security}/AuthFilter.java | 2 +- .../rest/{auth => config/security}/SecurityConfig.java | 2 +- .../controller/rest/controllers/ParticipantController.java | 2 +- .../shongo/controller/rest/controllers/RecordingController.java | 2 +- .../rest/controllers/ReservationRequestController.java | 2 +- .../shongo/controller/rest/controllers/ResourceController.java | 2 +- .../shongo/controller/rest/controllers/RoomController.java | 2 +- .../shongo/controller/rest/controllers/RuntimeController.java | 2 +- .../shongo/controller/rest/controllers/UserController.java | 2 +- .../shongo/controller/rest/controllers/UserRoleController.java | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{ => config}/OpenApiConfig.java (98%) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{auth => config/security}/AuthFilter.java (97%) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{auth => config/security}/SecurityConfig.java (97%) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/OpenApiConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java similarity index 98% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/OpenApiConfig.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java index 866abaffa..4c6c06a5d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/OpenApiConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest; +package cz.cesnet.shongo.controller.rest.config; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/AuthFilter.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/AuthFilter.java similarity index 97% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/AuthFilter.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/AuthFilter.java index af51ab5a6..0545e6c82 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/AuthFilter.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/AuthFilter.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest.auth; +package cz.cesnet.shongo.controller.rest.config.security; import cz.cesnet.shongo.controller.ControllerReportSet; import cz.cesnet.shongo.controller.api.SecurityToken; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java similarity index 97% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java index 90a3e3585..36790b0d6 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/auth/SecurityConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java @@ -1,4 +1,4 @@ -package cz.cesnet.shongo.controller.rest.auth; +package cz.cesnet.shongo.controller.rest.config.security; import cz.cesnet.shongo.controller.Controller; import org.springframework.context.annotation.Bean; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java index 870760cd6..c40839372 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java @@ -20,7 +20,7 @@ import java.util.List; import java.util.stream.Collectors; -import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; +import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; /** * Rest controller for participant endpoints. diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java index c0071405d..47474bdde 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java @@ -19,7 +19,7 @@ import java.util.List; import java.util.Map; -import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; +import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; /** * Rest controller for recording endpoints. diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 3ab1e94be..a7c537563 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -44,7 +44,7 @@ import java.util.Set; import java.util.stream.Collectors; -import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; +import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; import static cz.cesnet.shongo.controller.rest.models.TimeInterval.DATETIME_FORMATTER; /** diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java index 852a9ccae..997581a58 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java @@ -23,7 +23,7 @@ import java.util.*; import java.util.stream.Collectors; -import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; +import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; /** * Rest controller for resource endpoints. diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java index 1c4437abf..a9b7bb720 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java @@ -16,7 +16,7 @@ import java.util.stream.Collectors; -import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; +import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; /** * Rest controller for room endpoints. diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java index aaed05926..729107dbf 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java @@ -23,7 +23,7 @@ import java.util.*; -import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; +import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; /** * Rest controller for runtime endpoints. diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java index 8ae8419cc..80a609207 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java @@ -18,7 +18,7 @@ import java.util.List; -import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; +import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; /** * Rest controller for user endpoints. diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java index 630d2a94d..e57314299 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java @@ -18,7 +18,7 @@ import java.util.List; import java.util.stream.Collectors; -import static cz.cesnet.shongo.controller.rest.auth.AuthFilter.TOKEN; +import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; /** * Rest controller for roles endpoints. From 7067c3edf83f3982b8836f030d38f77f1a308bda Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 12 May 2022 18:54:57 +0200 Subject: [PATCH 033/134] Code clean up --- .../cesnet/shongo/controller/rest/Cache.java | 132 ++-- .../shongo/controller/rest/CacheProvider.java | 4 +- .../shongo/controller/rest/ErrorHandler.java | 6 +- .../shongo/controller/rest/RESTApiServer.java | 27 +- .../shongo/controller/rest/RoomCache.java | 46 +- .../controller/rest/config/OpenApiConfig.java | 31 +- .../rest/config/security/AuthFilter.java | 11 +- .../rest/config/security/SecurityConfig.java | 15 +- .../controllers/ParticipantController.java | 5 +- .../rest/controllers/RecordingController.java | 7 +- .../rest/controllers/ReportController.java | 3 +- .../ReservationRequestController.java | 9 +- .../rest/controllers/ResourceController.java | 8 +- .../rest/controllers/RoomController.java | 6 +- .../rest/controllers/RuntimeController.java | 3 +- .../rest/controllers/UserController.java | 6 +- .../rest/controllers/UserRoleController.java | 11 +- .../error/ControllerExceptionHandler.java | 26 +- .../controller/rest/models/CommonModel.java | 46 -- .../rest/models/TechnologyModel.java | 42 +- .../controller/rest/models/TimeInterval.java | 13 +- .../ParticipantConfigurationModel.java | 11 +- .../models/participant/ParticipantModel.java | 36 +- .../rest/models/report/MetaModel.java | 3 +- .../rest/models/report/ReportModel.java | 1 - .../reservationrequest/PeriodicityModel.java | 3 +- .../PhysicalResourceData.java | 3 +- .../ReservationRequestCreateModel.java | 577 +++++++----------- .../ReservationRequestDetailModel.java | 3 +- .../ReservationRequestHistoryModel.java | 6 +- .../ReservationRequestModel.java | 6 +- .../ReservationRequestState.java | 62 +- .../reservationrequest/RoomCapacityModel.java | 6 +- .../reservationrequest/SpecificationType.java | 48 +- .../reservationrequest/VirtualRoomModel.java | 6 +- .../models/resource/ResourceCapacity.java | 18 +- .../resource/ResourceCapacityUtilization.java | 27 +- .../ResourceCapacityUtilizationModel.java | 3 +- .../rest/models/resource/ResourceModel.java | 6 +- .../resource/ResourceUtilizationModel.java | 23 +- .../models/resource/ResourcesUtilization.java | 45 +- .../rest/models/room/RoomAuthorizedData.java | 6 +- .../rest/models/room/RoomModel.java | 8 +- .../rest/models/room/RoomState.java | 61 +- .../controller/rest/models/room/RoomType.java | 20 +- .../rest/models/users/SettingsModel.java | 6 +- 46 files changed, 643 insertions(+), 808 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java index f88572389..f3601178c 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java @@ -15,10 +15,9 @@ import cz.cesnet.shongo.controller.api.rpc.ResourceService; import cz.cesnet.shongo.controller.rest.error.ObjectInaccessibleException; import cz.cesnet.shongo.controller.rest.models.resource.ResourcesUtilization; +import lombok.extern.slf4j.Slf4j; import org.joda.time.DateTime; import org.joda.time.Duration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; @@ -32,9 +31,9 @@ * @author Filip Karnis * @author Martin Srom */ +@Slf4j public class Cache { - private static Logger logger = LoggerFactory.getLogger(Cache.class); /** * Expiration of user information/permissions in minutes. @@ -49,83 +48,53 @@ public class Cache private final ExecutableService executableService; + /** + * @see ResourcesUtilization + */ + private final ExpirationMap resourcesUtilizationByToken = new ExpirationMap<>(); + /** * {@link UserInformation}s by {@link SecurityToken}. */ - private ExpirationMap> userPermissionsByToken = new ExpirationMap<>(); + private final ExpirationMap> userPermissionsByToken = new ExpirationMap<>(); /** * {@link UserInformation}s by user-ids. */ - private ExpirationMap userInformationByUserId = - new ExpirationMap(); + private final ExpirationMap userInformationByUserId = new ExpirationMap<>(); /** * {@link Group}s by group-ids. */ - private ExpirationMap groupByGroupId = - new ExpirationMap(); + private final ExpirationMap groupByGroupId = new ExpirationMap<>(); /** * {@link UserState}s by {@link SecurityToken}. */ - private ExpirationMap userStateByToken = new ExpirationMap(); - + private final ExpirationMap userStateByToken = new ExpirationMap(); /** * {@link ResourceSummary} by identifier. */ - private ExpirationMap resourceById = - new ExpirationMap(); - + private final ExpirationMap resourceById = new ExpirationMap<>(); /** * {@link ReservationRequestSummary} by identifier. */ - private ExpirationMap reservationRequestById = - new ExpirationMap(); + private final ExpirationMap reservationRequestById = new ExpirationMap<>(); /** * {@link Reservation} by identifier. */ - private ExpirationMap reservationById = - new ExpirationMap(); + private final ExpirationMap reservationById = new ExpirationMap<>(); /** * Ids of resources with public calendar by their calendarUriKey */ - private ExpirationMap resourceIdsWithPublicCalendarByUriKey = - new ExpirationMap(); + private final ExpirationMap resourceIdsWithPublicCalendarByUriKey = new ExpirationMap<>(); /** * {@link Reservation} by identifier. */ - private ExpirationMap executableById = - new ExpirationMap(); - - /** - * @see ResourcesUtilization - */ - private final ExpirationMap resourcesUtilizationByToken = - new ExpirationMap(); - - /** - * Cached information for single user. - */ - private static class UserState - { - /** - * Set of permissions which the user has for object. - */ - private ExpirationMap> objectPermissionsByObject = - new ExpirationMap>(); - - /** - * Constructor. - */ - public UserState() - { - objectPermissionsByObject.setExpiration(Duration.standardMinutes(USER_EXPIRATION_MINUTES)); - } - } + private final ExpirationMap executableById = new ExpirationMap<>(); /** * Constructor. @@ -154,13 +123,26 @@ public Cache( resourceIdsWithPublicCalendarByUriKey.setExpiration(Duration.standardMinutes(10)); } + /** + * @param userId + * @return {@link UserInformation} for not existing user with given {@code userId} + */ + private static UserInformation createNotExistingUserInformation(String userId) + { + UserInformation userInformation = new UserInformation(); + userInformation.setUserId(userId); + userInformation.setFirstName("Non-Existent-User"); + userInformation.setLastName("(" + userId + ")"); + return userInformation; + } + /** * Method called each 5 minutes to clear expired items. */ @Scheduled(fixedDelay = (USER_EXPIRATION_MINUTES * 60 * 1000)) public synchronized void clearExpired() { - logger.debug("Clearing expired user cache..."); + log.debug("Clearing expired user cache..."); DateTime dateTimeNow = DateTime.now(); userPermissionsByToken.clearExpired(dateTimeNow); userInformationByUserId.clearExpired(dateTimeNow); @@ -232,7 +214,7 @@ public synchronized UserInformation getUserInformation(SecurityToken securityTok userInformation = response.getItem(0); } catch (ControllerReportSet.UserNotExistsException exception) { - logger.warn("User with id '" + userId + "' doesn't exist.", exception); + log.warn("User with id '" + userId + "' doesn't exist.", exception); userInformation = createNotExistingUserInformation(userId); } userInformationByUserId.put(userId, userInformation); @@ -249,7 +231,7 @@ public synchronized void fetchUserInformation(SecurityToken securityToken, Colle for (String userId : userIds) { if (!userInformationByUserId.contains(userId)) { if (missingUserIds == null) { - missingUserIds = new HashSet(); + missingUserIds = new HashSet<>(); } missingUserIds.add(userId); } @@ -270,11 +252,10 @@ public synchronized void fetchUserInformation(SecurityToken securityToken, Colle } catch (ControllerReportSet.UserNotExistsException exception) { String userId = exception.getUser(); - logger.warn("User with id '" + userId + "' doesn't exist.", exception); + log.warn("User with id '" + userId + "' doesn't exist.", exception); UserInformation userInformation = createNotExistingUserInformation(userId); userInformationByUserId.put(userId, userInformation); missingUserIds.remove(userId); - continue; } } } @@ -326,7 +307,7 @@ public synchronized Set getObjectPermissions(SecurityToken sec if (objectPermissions == null) { Map permissionsByObject = authorizationService.listObjectPermissions( new ObjectPermissionListRequest(securityToken, objectId)); - objectPermissions = new HashSet(); + objectPermissions = new HashSet<>(); objectPermissions.addAll(permissionsByObject.get(objectId).getObjectPermissions()); userState.objectPermissionsByObject.put(objectId, objectPermissions); } @@ -341,8 +322,8 @@ public synchronized Set getObjectPermissions(SecurityToken sec public Map> getReservationRequestsPermissions(SecurityToken securityToken, Collection reservationRequests) { - Map> permissionsByReservationRequestId = new HashMap>(); - Set reservationRequestIds = new HashSet(); + Map> permissionsByReservationRequestId = new HashMap<>(); + Set reservationRequestIds = new HashSet<>(); for (ReservationRequestSummary reservationRequest : reservationRequests) { String reservationRequestId = reservationRequest.getId(); Set objectPermissions = @@ -364,7 +345,7 @@ public Map> getReservationRequestsPermissions(Secu * @param securityToken of the requesting user * @param objectId of the object * @return set of {@link ObjectPermission} for requesting user and given {@code objectId} - * or null if the {@link ObjectPermission}s aren't cached + * or null if the {@link ObjectPermission}s aren't cached */ public synchronized Set getObjectPermissionsWithoutFetching( SecurityToken securityToken, String objectId) @@ -383,7 +364,7 @@ public synchronized Set getObjectPermissionsWithoutFetching( public synchronized Map> fetchObjectPermissions( SecurityToken securityToken, Set objectIds) { - Map> result = new HashMap>(); + Map> result = new HashMap<>(); if (objectIds.isEmpty()) { return result; } @@ -394,7 +375,7 @@ public synchronized Map> fetchObjectPermissions( String objectId = entry.getKey(); Set objectPermissions = userState.objectPermissionsByObject.get(objectId); if (objectPermissions == null) { - objectPermissions = new HashSet(); + objectPermissions = new HashSet<>(); userState.objectPermissionsByObject.put(objectId, objectPermissions); } objectPermissions.clear(); @@ -474,7 +455,7 @@ public synchronized void fetchReservationRequests(SecurityToken securityToken, S for (String reservationRequestId : reservationRequestIds) { if (!reservationRequestById.contains(reservationRequestId)) { if (missingReservationRequestIds == null) { - missingReservationRequestIds = new HashSet(); + missingReservationRequestIds = new HashSet<>(); } missingReservationRequestIds.add(reservationRequestId); } @@ -614,7 +595,7 @@ public String getExecutableId(SecurityToken securityToken, String objectId) ReservationRequestSummary request = getAllocatedReservationRequestSummary(securityToken, objectId); String reservationId = request.getAllocatedReservationId(); if (reservationId == null) { - logger.info("Reservation doesn't exist."); + log.info("Reservation doesn't exist."); return null; } objectId = reservationId; @@ -624,7 +605,7 @@ public String getExecutableId(SecurityToken securityToken, String objectId) Reservation reservation = getReservation(securityToken, objectId); Executable executable = reservation.getExecutable(); if (executable == null) { - logger.info("Reservation " + objectId + " doesn't have executable."); + log.info("Reservation " + objectId + " doesn't have executable."); return null; } return executable.getId(); @@ -650,7 +631,7 @@ public synchronized Executable getExecutable(SecurityToken securityToken, String /** * @param securityToken for which the {@link ResourcesUtilization} shall be returned - * @param forceRefresh specifies whether a fresh version should be returned + * @param forceRefresh specifies whether a fresh version should be returned * @return {@link ResourcesUtilization} for given {@code securityToken} */ public ResourcesUtilization getResourcesUtilization(SecurityToken securityToken, boolean forceRefresh) @@ -678,8 +659,8 @@ public String getResourceIdWithUriKey(String uriKey) resourceIdsWithPublicCalendarByUriKey.clearExpired(DateTime.now()); //Check if resource really exists if (resourceIdsWithPublicCalendarByUriKey.size() == 0) { - for (ResourceSummary resourceSummary: resourceService.getResourceIdsWithPublicCalendar()) { - resourceIdsWithPublicCalendarByUriKey.put(resourceSummary.getCalendarUriKey(),resourceSummary.getId()); + for (ResourceSummary resourceSummary : resourceService.getResourceIdsWithPublicCalendar()) { + resourceIdsWithPublicCalendarByUriKey.put(resourceSummary.getCalendarUriKey(), resourceSummary.getId()); } } if (!resourceIdsWithPublicCalendarByUriKey.contains(uriKey)) { @@ -689,15 +670,22 @@ public String getResourceIdWithUriKey(String uriKey) } /** - * @param userId - * @return {@link UserInformation} for not existing user with given {@code userId} + * Cached information for single user. */ - private static UserInformation createNotExistingUserInformation(String userId) + private static class UserState { - UserInformation userInformation = new UserInformation(); - userInformation.setUserId(userId); - userInformation.setFirstName("Non-Existent-User"); - userInformation.setLastName("(" + userId + ")"); - return userInformation; + /** + * Set of permissions which the user has for object. + */ + private final ExpirationMap> objectPermissionsByObject = + new ExpirationMap<>(); + + /** + * Constructor. + */ + public UserState() + { + objectPermissionsByObject.setExpiration(Duration.standardMinutes(USER_EXPIRATION_MINUTES)); + } } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/CacheProvider.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/CacheProvider.java index cf3308591..06d4d8080 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/CacheProvider.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/CacheProvider.java @@ -18,12 +18,12 @@ public class CacheProvider /** * {@link Cache} to be used for retrieving {@link UserInformation}. */ - private Cache cache; + private final Cache cache; /** * {@link SecurityToken} to be used for retrieving {@link UserInformation} by the {@link #cache}. */ - private SecurityToken securityToken; + private final SecurityToken securityToken; /** * Constructor. diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java index 3fe2527fc..715245163 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java @@ -36,7 +36,8 @@ public ErrorHandler(@Autowired Controller controller) * @param content * @return result */ - public void sendEmailToAdministrator(String replyTo, String subject, String content) throws MessagingException { + public void sendEmailToAdministrator(String replyTo, String subject, String content) throws MessagingException + { Collection administratorEmails = configuration.getAdministratorEmails(); if (administratorEmails.size() == 0) { log.warn("Administrator email for sending error reports is not configured."); @@ -46,7 +47,8 @@ public void sendEmailToAdministrator(String replyTo, String subject, String cont try { EmailSender.Email email = new EmailSender.Email(administratorEmails, replyTo, subject, content); controller.getEmailSender().sendEmail(email); - } catch (MessagingException e) { + } + catch (MessagingException e) { log.error("Failed to send email '" + subject + "':\n" + content, e); throw e; } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java index ed72d7129..1ff2dfaa7 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java @@ -25,12 +25,15 @@ import java.security.cert.CertificateException; import java.util.EnumSet; -public class RESTApiServer { +public class RESTApiServer +{ private static final String SERVLET_PATH = "/"; private static final String SERVLET_NAME = "rest-api"; private static final String INTER_DOMAIN_API_PATH = "/domain/**"; - public static Server start(ControllerConfiguration configuration) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { + public static Server start(ControllerConfiguration configuration) + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException + { Server restServer = new Server(); ConfiguredSSLContext.getInstance().loadConfiguration(configuration); String resourceBase = RESTApiServer.getResourceBase(); @@ -51,14 +54,16 @@ public static Server start(ControllerConfiguration configuration) throws KeyStor try { restServer.start(); - } catch (Exception exception) { + } + catch (Exception exception) { throw new RuntimeException(exception); } return restServer; } - private static String getResourceBase() { + private static String getResourceBase() + { URL resourceBaseUrl = Controller.class.getClassLoader().getResource("WEB-INF"); if (resourceBaseUrl == null) { throw new RuntimeException("WEB-INF is not in classpath."); @@ -66,7 +71,12 @@ private static String getResourceBase() { return resourceBaseUrl.toExternalForm().replace("/WEB-INF", "/"); } - private static ServerConnector createHTTPConnector(ControllerConfiguration configuration, WebAppContext webAppContext, Server server) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { + private static ServerConnector createHTTPConnector( + ControllerConfiguration configuration, + WebAppContext webAppContext, + Server server) + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException + { final HttpConfiguration http_config = new HttpConfiguration(); http_config.setSecurePort(configuration.getRESTApiPort()); ServerConnector serverConnector; @@ -99,7 +109,8 @@ private static ServerConnector createHTTPConnector(ControllerConfiguration confi // Enable SSL client filter by certificates EnumSet filterTypes = EnumSet.of(DispatcherType.REQUEST); webAppContext.addFilter(SSLClientCertFilter.class, INTER_DOMAIN_API_PATH, filterTypes); - } else { + } + else { EnumSet filterTypes = EnumSet.of(DispatcherType.REQUEST); webAppContext.addFilter(BasicAuthFilter.class, INTER_DOMAIN_API_PATH, filterTypes); } @@ -107,7 +118,8 @@ private static ServerConnector createHTTPConnector(ControllerConfiguration confi serverConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, "http/1.1"), new HttpConnectionFactory(https_config)); - } else { + } + else { http_config.setSecureScheme(HttpScheme.HTTP.asString()); serverConnector = new ServerConnector(server, new HttpConnectionFactory(http_config)); } @@ -117,7 +129,6 @@ private static ServerConnector createHTTPConnector(ControllerConfiguration confi serverConnector.setHost(host); } serverConnector.setPort(configuration.getRESTApiPort()); -// serverConnector.setIdleTimeout(configuration.getInterDomainCommandTimeout()); return serverConnector; } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java index 34065492e..5d73cdf55 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java @@ -1,4 +1,3 @@ - package cz.cesnet.shongo.controller.rest; import cz.cesnet.shongo.CommonReportSet; @@ -13,8 +12,6 @@ import cz.cesnet.shongo.controller.api.rpc.ResourceControlService; import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import org.joda.time.Duration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.*; @@ -26,7 +23,6 @@ */ public class RoomCache { - private static Logger logger = LoggerFactory.getLogger(RoomCache.class); private final ResourceControlService resourceControlService; @@ -37,32 +33,27 @@ public class RoomCache /** * {@link RoomExecutable} by roomExecutableId. */ - private final ExpirationMap roomExecutableCache = - new ExpirationMap(); + private final ExpirationMap roomExecutableCache = new ExpirationMap<>(); /** * {@link Room} by roomExecutableId". */ - private final ExpirationMap roomCache = - new ExpirationMap(); + private final ExpirationMap roomCache = new ExpirationMap<>(); /** * Collection of {@link RoomParticipant}s by roomExecutableId. */ - private final ExpirationMap> roomParticipantsCache = - new ExpirationMap>(); + private final ExpirationMap> roomParticipantsCache = new ExpirationMap<>(); /** * {@link RoomParticipant} by "roomExecutableId:participantId". */ - private final ExpirationMap roomParticipantCache = - new ExpirationMap(); + private final ExpirationMap roomParticipantCache = new ExpirationMap<>(); /** * Participant snapshots in {@link MediaData} by "roomExecutableId:participantId". */ - private final ExpirationMap roomParticipantSnapshotCache = - new ExpirationMap(); + private final ExpirationMap roomParticipantSnapshotCache = new ExpirationMap<>(); /** * Constructor. @@ -155,7 +146,10 @@ public List getRoomParticipants(SecurityToken securityToken, St * @param roomParticipantId * @return {@link RoomParticipant} for given {@code roomExecutableId} and {@code roomParticipantId} */ - public RoomParticipant getRoomParticipant(SecurityToken securityToken, String roomExecutableId, String roomParticipantId) + public RoomParticipant getRoomParticipant( + SecurityToken securityToken, + String roomExecutableId, + String roomParticipantId) { String cacheId = roomExecutableId + ":" + roomParticipantId; synchronized (roomParticipantCache) { @@ -178,9 +172,12 @@ public RoomParticipant getRoomParticipant(SecurityToken securityToken, String ro /** * @param securityToken * @param roomExecutableId - * @param roomParticipant to be modified in the given {@code roomExecutableId} + * @param roomParticipant to be modified in the given {@code roomExecutableId} */ - public void modifyRoomParticipant(SecurityToken securityToken, String roomExecutableId, RoomParticipant roomParticipant) + public void modifyRoomParticipant( + SecurityToken securityToken, + String roomExecutableId, + RoomParticipant roomParticipant) { RoomExecutable roomExecutable = getRoomExecutable(securityToken, roomExecutableId); String resourceId = roomExecutable.getResourceId(); @@ -200,7 +197,10 @@ public void modifyRoomParticipant(SecurityToken securityToken, String roomExecut * @param roomExecutableId * @param roomParticipants configuration to which all room participants should be modified in the given {@code roomExecutableId} */ - public void modifyRoomParticipants(SecurityToken securityToken, String roomExecutableId, RoomParticipant roomParticipants) + public void modifyRoomParticipants( + SecurityToken securityToken, + String roomExecutableId, + RoomParticipant roomParticipants) { RoomExecutable roomExecutable = getRoomExecutable(securityToken, roomExecutableId); String resourceId = roomExecutable.getResourceId(); @@ -226,7 +226,10 @@ public void modifyRoomParticipants(SecurityToken securityToken, String roomExecu * @param roomParticipantId * @return {@link MediaData} snapshot of room participant */ - public MediaData getRoomParticipantSnapshot(SecurityToken securityToken, String roomExecutableId, String roomParticipantId) + public MediaData getRoomParticipantSnapshot( + SecurityToken securityToken, + String roomExecutableId, + String roomParticipantId) { String cacheId = roomExecutableId + ":" + roomParticipantId; synchronized (roomParticipantSnapshotCache) { @@ -250,7 +253,10 @@ public MediaData getRoomParticipantSnapshot(SecurityToken securityToken, String * @param roomExecutableId * @param roomParticipantId to be disconnected from given {@code roomExecutableId} */ - public void disconnectRoomParticipant(SecurityToken securityToken, String roomExecutableId, String roomParticipantId) + public void disconnectRoomParticipant( + SecurityToken securityToken, + String roomExecutableId, + String roomParticipantId) { RoomExecutable roomExecutable = getRoomExecutable(securityToken, roomExecutableId); String resourceId = roomExecutable.getResourceId(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java index 4c6c06a5d..e16d48c57 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java @@ -6,9 +6,9 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.security.SecurityScheme; import org.springframework.context.annotation.ComponentScan; -import org.springframework.core.io.Resource; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.core.io.Resource; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -43,13 +43,15 @@ org.springdoc.core.SwaggerUiOAuthProperties.class, org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.class }) -class OpenApiConfig implements WebMvcConfigurer { +class OpenApiConfig implements WebMvcConfigurer +{ private static final String PET_STORE_URL = "https://petstore.swagger.io/v2/swagger.json"; private static final String SPRINGDOC_OPENAPI_URL = "/v3/api-docs"; @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { + public void addResourceHandlers(ResourceHandlerRegistry registry) + { registry.addResourceHandler("/**/*.html") .addResourceLocations("classpath:/META-INF/resources/webjars/") .resourceChain(false) @@ -61,31 +63,40 @@ public void addResourceHandlers(ResourceHandlerRegistry registry) { /** * Replaces the default openapi config URL in swagger with the one generated by springdoc. */ - public static class IndexPageTransformer implements ResourceTransformer { + public static class IndexPageTransformer implements ResourceTransformer + { @Override - public Resource transform(HttpServletRequest request, Resource resource, - ResourceTransformerChain transformerChain) throws IOException { + public Resource transform( + HttpServletRequest request, + Resource resource, + ResourceTransformerChain transformerChain) + throws IOException + { if (resource.getURL().toString().endsWith("/index.html")) { String html = getHtmlContent(resource); html = overwritePetStore(html); return new TransformedResource(resource, html.getBytes()); - } else { + } + else { return resource; } } - private String overwritePetStore(String html) { + private String overwritePetStore(String html) + { return html.replace(PET_STORE_URL, SPRINGDOC_OPENAPI_URL); } - private String getHtmlContent(Resource resource) { + private String getHtmlContent(Resource resource) + { try { InputStream inputStream = resource.getInputStream(); java.util.Scanner s = new java.util.Scanner(inputStream).useDelimiter("\\A"); String content = s.next(); inputStream.close(); return content; - } catch (IOException e) { + } + catch (IOException e) { throw new RuntimeException(e); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/AuthFilter.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/AuthFilter.java index 0545e6c82..f224efa70 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/AuthFilter.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/AuthFilter.java @@ -18,15 +18,17 @@ * * @author Filip Karnis */ -public class AuthFilter extends GenericFilterBean { +public class AuthFilter extends GenericFilterBean +{ + public static final String TOKEN = "TOKEN"; private static final String AUTHORIZATION_HEADER = "Authorization"; private static final String BEARER = "Bearer"; - public static final String TOKEN = "TOKEN"; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws ControllerReportSet.SecurityInvalidTokenException, ServletException, IOException { + throws ControllerReportSet.SecurityInvalidTokenException, ServletException, IOException + { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; Authorization authorization = Authorization.getInstance(); @@ -50,7 +52,8 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha try { authorization.validate(securityToken); httpRequest.setAttribute(TOKEN, securityToken); - } catch (ControllerReportSet.SecurityInvalidTokenException e) { + } + catch (ControllerReportSet.SecurityInvalidTokenException e) { httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Request unauthorized."); return; } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java index 36790b0d6..fa69e0124 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java @@ -21,10 +21,12 @@ */ @Configuration @EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { +public class SecurityConfig extends WebSecurityConfigurerAdapter +{ @Override - public void configure(WebSecurity web) { + public void configure(WebSecurity web) + { web.ignoring() .antMatchers("/domain/**") .antMatchers("/v3/api-docs") @@ -33,7 +35,8 @@ public void configure(WebSecurity web) { } @Override - protected void configure(HttpSecurity http) throws Exception { + protected void configure(HttpSecurity http) throws Exception + { http.cors().and().csrf().disable(); AuthFilter authFilter = new AuthFilter(); http.addFilterAt(authFilter, BasicAuthenticationFilter.class) @@ -45,7 +48,8 @@ protected void configure(HttpSecurity http) throws Exception { } @Bean - CorsConfigurationSource corsConfigurationSource() { + CorsConfigurationSource corsConfigurationSource() + { CorsConfiguration configuration = new CorsConfiguration().applyPermitDefaultValues(); configuration.setAllowedMethods(List.of(CorsConfiguration.ALL)); configuration.setAllowedOrigins(allowedOrigins()); @@ -54,7 +58,8 @@ CorsConfigurationSource corsConfigurationSource() { return source; } - private List allowedOrigins() { + private List allowedOrigins() + { return Controller.getInstance().getConfiguration().getRESTApiAllowedOrigins(); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java index c40839372..4e5d8a9fb 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java @@ -29,7 +29,8 @@ */ @RestController @RequestMapping(ClientWebUrl.PARTICIPANTS) -public class ParticipantController { +public class ParticipantController +{ private final Cache cache; private final ExecutableService executableService; @@ -165,7 +166,7 @@ private AbstractRoomExecutable getRoomExecutable(SecurityToken securityToken, St } protected ParticipantModel getParticipant(RoomExecutableParticipantConfiguration participantConfiguration, - String participantId, SecurityToken securityToken) + String participantId, SecurityToken securityToken) { AbstractParticipant participant = participantConfiguration.getParticipant(participantId); if (participant == null) { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java index 47474bdde..662bf0ab9 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java @@ -28,7 +28,8 @@ */ @RestController @RequestMapping(ClientWebUrl.RECORDINGS) -public class RecordingController { +public class RecordingController +{ private final Cache cache; private final ExecutableService executableService; @@ -84,11 +85,11 @@ Map listRequestRecordings( else { item.put("duration", duration.toPeriod()); } - item.put("isPublic",recording.isPublic()); + item.put("isPublic", recording.isPublic()); item.put("downloadUrl", recording.getDownloadUrl()); item.put("viewUrl", recording.getViewUrl()); item.put("editUrl", recording.getEditUrl()); - item.put("filename",recording.getFileName()); + item.put("filename", recording.getFileName()); items.add(item); } Map data = new HashMap<>(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java index 67fef5232..d97b7743d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java @@ -23,7 +23,8 @@ @SecurityRequirements @RestController @RequestMapping(ClientWebUrl.REPORT) -public class ReportController { +public class ReportController +{ private final ErrorHandler errorHandler; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index a7c537563..337027d2c 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -54,7 +54,8 @@ */ @RestController @RequestMapping(ClientWebUrl.RESERVATION_REQUESTS) -public class ReservationRequestController { +public class ReservationRequestController +{ private final Cache cache; private final ReservationService reservationService; @@ -265,7 +266,8 @@ void modifyRequest( CacheProvider cacheProvider = new CacheProvider(cache, securityToken); AbstractReservationRequest originalRequest = reservationService.getReservationRequest(securityToken, id); - ReservationRequestCreateModel modifiedRequest = new ReservationRequestCreateModel(originalRequest, cacheProvider); + ReservationRequestCreateModel modifiedRequest = + new ReservationRequestCreateModel(originalRequest, cacheProvider); if (request.getRoomName() != null) { modifiedRequest.setRoomName(request.getRoomName()); @@ -341,7 +343,8 @@ void revertRequest( reservationService.revertReservationRequest(securityToken, id); } - public enum ReservationType { + public enum ReservationType + { VIRTUAL_ROOM, PHYSICAL_RESOURCE, ROOM_CAPACITY diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java index 997581a58..2c82dcfc3 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java @@ -20,7 +20,10 @@ import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; @@ -33,7 +36,8 @@ @Slf4j @RestController @RequestMapping(ClientWebUrl.RESOURCES) -public class ResourceController { +public class ResourceController +{ private final Cache cache; private final ResourceService resourceService; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java index a9b7bb720..2761decd2 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java @@ -25,12 +25,14 @@ */ @RestController @RequestMapping(ClientWebUrl.ROOMS) -public class RoomController { +public class RoomController +{ private final Cache cache; private final ExecutableService executableService; - public RoomController(@Autowired Cache cache, @Autowired ExecutableService executableService) { + public RoomController(@Autowired Cache cache, @Autowired ExecutableService executableService) + { this.cache = cache; this.executableService = executableService; } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java index 729107dbf..08dfbb8c4 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java @@ -33,7 +33,8 @@ @Slf4j @RestController @RequestMapping(ClientWebUrl.RUNTIME_MANAGEMENT) -public class RuntimeController { +public class RuntimeController +{ private final Cache cache; private final RoomCache roomCache; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java index 80a609207..336d7a97c 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java @@ -27,12 +27,14 @@ */ @RestController @RequestMapping(ClientWebUrl.USERS_AND_GROUPS) -public class UserController { +public class UserController +{ private final AuthorizationService authorizationService; private final Cache cache; - public UserController(@Autowired AuthorizationService reservationService, @Autowired Cache cache) { + public UserController(@Autowired AuthorizationService reservationService, @Autowired Cache cache) + { this.authorizationService = reservationService; this.cache = cache; } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java index e57314299..c7ac6b15b 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java @@ -27,12 +27,14 @@ */ @RestController @RequestMapping(ClientWebUrl.ROLES) -public class UserRoleController { +public class UserRoleController +{ private final AuthorizationService authorizationService; private final Cache cache; - public UserRoleController(@Autowired AuthorizationService reservationService, @Autowired Cache cache) { + public UserRoleController(@Autowired AuthorizationService reservationService, @Autowired Cache cache) + { this.authorizationService = reservationService; this.cache = cache; } @@ -53,7 +55,10 @@ ListResponse listRequestRoles( ListResponse aclEntries = authorizationService.listAclEntries(request); CacheProvider cacheProvider = new CacheProvider(cache, securityToken); - List items = aclEntries.getItems().stream().map(item -> new UserRoleModel(item, cacheProvider)).collect(Collectors.toList()); + List items = aclEntries.getItems() + .stream() + .map(item -> new UserRoleModel(item, cacheProvider)) + .collect(Collectors.toList()); return ListResponse.fromRequest(start, count, items); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ControllerExceptionHandler.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ControllerExceptionHandler.java index 07ba65bc9..87717622b 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ControllerExceptionHandler.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ControllerExceptionHandler.java @@ -2,8 +2,8 @@ import cz.cesnet.shongo.CommonReportSet; import cz.cesnet.shongo.TodoImplementException; -import cz.cesnet.shongo.controller.ControllerReportSet.ReservationRequestNotDeletableException; import cz.cesnet.shongo.controller.ControllerReportSet.ReservationRequestDeletedException; +import cz.cesnet.shongo.controller.ControllerReportSet.ReservationRequestNotDeletableException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -21,42 +21,50 @@ public class ControllerExceptionHandler { @ExceptionHandler(TodoImplementException.class) - public ResponseEntity handleTodo(TodoImplementException e) { + public ResponseEntity handleTodo(TodoImplementException e) + { return ErrorModel.createResponseFromException(e, HttpStatus.NOT_IMPLEMENTED); } @ExceptionHandler(UnsupportedOperationException.class) - public ResponseEntity handleUnsupportedOperation(UnsupportedOperationException e) { + public ResponseEntity handleUnsupportedOperation(UnsupportedOperationException e) + { return ErrorModel.createResponseFromException(e, HttpStatus.NOT_IMPLEMENTED); } @ExceptionHandler(MessagingException.class) - public ResponseEntity handleMassaging(MessagingException e) { + public ResponseEntity handleMassaging(MessagingException e) + { return ErrorModel.createResponseFromException(e, HttpStatus.SERVICE_UNAVAILABLE); } @ExceptionHandler(LastOwnerRoleNotDeletableException.class) - public ResponseEntity handleLastOwnerRoleNotDeletable(LastOwnerRoleNotDeletableException e) { + public ResponseEntity handleLastOwnerRoleNotDeletable(LastOwnerRoleNotDeletableException e) + { return ErrorModel.createResponseFromException(e, HttpStatus.FORBIDDEN); } @ExceptionHandler(ReservationRequestNotDeletableException.class) - public ResponseEntity handleReservationRequestNotDeletable(ReservationRequestNotDeletableException e) { + public ResponseEntity handleReservationRequestNotDeletable(ReservationRequestNotDeletableException e) + { return ErrorModel.createResponseFromException(e, HttpStatus.FORBIDDEN); } @ExceptionHandler(ObjectInaccessibleException.class) - public ResponseEntity handleObjectInaccessible(ObjectInaccessibleException e) { + public ResponseEntity handleObjectInaccessible(ObjectInaccessibleException e) + { return ErrorModel.createResponseFromException(e, HttpStatus.NOT_FOUND); } @ExceptionHandler(ReservationRequestDeletedException.class) - public ResponseEntity handleReservationRequestDeleted(ReservationRequestDeletedException e) { + public ResponseEntity handleReservationRequestDeleted(ReservationRequestDeletedException e) + { return ErrorModel.createResponseFromException(e, HttpStatus.NOT_FOUND); } @ExceptionHandler(CommonReportSet.ObjectNotExistsException.class) - public ResponseEntity handleObjectNotExists(CommonReportSet.ObjectNotExistsException e) { + public ResponseEntity handleObjectNotExists(CommonReportSet.ObjectNotExistsException e) + { return ErrorModel.createResponseFromException(e, HttpStatus.NOT_FOUND); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/CommonModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/CommonModel.java index f280e8f03..8c8ed9fdb 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/CommonModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/CommonModel.java @@ -1,13 +1,5 @@ package cz.cesnet.shongo.controller.rest.models; -import cz.cesnet.shongo.controller.api.SecurityToken; -import org.slf4j.Logger; -import org.springframework.util.StringUtils; -import org.springframework.validation.BindingResult; -import org.springframework.validation.Errors; - -import java.util.regex.Pattern; - /** * Common validation methods. * @@ -20,12 +12,6 @@ public class CommonModel */ private static final String NEW_ID_PREFIX = "new-"; - /** - * Email pattern. - */ - private static final Pattern EMAIL_PATTERN = - Pattern.compile("^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$"); - /** * Last auto-generated identifier index. */ @@ -48,27 +34,6 @@ public synchronized static String getNewId() return NEW_ID_PREFIX + ++lastGeneratedId; } - /** - * Validate email address. - * - * @param bindingResult - * @param field - * @param errorCode - */ - public static void validateEmail(BindingResult bindingResult, String field, String errorCode) - { - String value = (String) bindingResult.getFieldValue(field); - if (value != null) { - value = value.trim(); - } - if (StringUtils.isEmpty(value)) { - bindingResult.rejectValue(field, "validation.field.required"); - } - else if (!EMAIL_PATTERN.matcher(value).matches()) { - bindingResult.rejectValue(field, errorCode); - } - } - /** * @param string * @return given {@code string} which can be used in double quoted string (e.g., "") @@ -82,15 +47,4 @@ public static String escapeDoubleQuotedString(String string) string = string.replaceAll("\"", "\\\\\""); return string; } - - /** - * Log validation errors. - * - * @param logger - * @param errors - */ - public static void logValidationErrors(Logger logger, Errors errors, SecurityToken securityToken) - { - logger.info("Validation errors by {}: {}", securityToken.getUserInformation(), errors); - } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TechnologyModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TechnologyModel.java index c9e110241..b793bbda7 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TechnologyModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TechnologyModel.java @@ -2,6 +2,7 @@ import cz.cesnet.shongo.Technology; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -24,7 +25,7 @@ public enum TechnologyModel ADOBE_CONNECT("views.technologyModel.ADOBE_CONNECT", Technology.ADOBE_CONNECT), /** - * {@link Technology#FREEPBX} + * {@link Technology#FREEPBX} */ FREEPBX("views.technologyModel.FREEPBX", Technology.FREEPBX), /** @@ -46,35 +47,16 @@ public enum TechnologyModel /** * Constructor. * - * @param titleCode sets the {@link #titleCode} + * @param titleCode sets the {@link #titleCode} * @param technologies sets the {@link #technologies} */ TechnologyModel(String titleCode, Technology... technologies) { this.titleCode = titleCode; - Set technologySet = new HashSet(); - for (Technology technology : technologies) { - technologySet.add(technology); - } + Set technologySet = new HashSet<>(Arrays.asList(technologies)); this.technologies = Collections.unmodifiableSet(technologySet); } - /** - * @return {@link #titleCode} - */ - public String getTitleCode() - { - return titleCode; - } - - /** - * @return {@link #technologies} - */ - public Set getTechnologies() - { - return technologies; - } - /** * @param technologies which must the returned {@link TechnologyModel} contain * @return {@link TechnologyModel} which contains all given {@code technologies} @@ -98,4 +80,20 @@ else if (PEXIP.technologies.containsAll(technologies)) { } return null; } + + /** + * @return {@link #titleCode} + */ + public String getTitleCode() + { + return titleCode; + } + + /** + * @return {@link #technologies} + */ + public Set getTechnologies() + { + return technologies; + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java index 567b329b5..382ee6bc4 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java @@ -2,15 +2,14 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; -import lombok.NoArgsConstructor; import org.joda.time.DateTime; import org.joda.time.Interval; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; @Data -@NoArgsConstructor -public class TimeInterval { +public class TimeInterval +{ private static final String ISO_8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormat.forPattern(ISO_8601_PATTERN); @@ -20,12 +19,8 @@ public class TimeInterval { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = ISO_8601_PATTERN) private DateTime end; - public TimeInterval(Interval interval) { - this.start = interval.getStart(); - this.end = interval.getEnd(); - } - - public static TimeInterval fromApi(Interval interval) { + public static TimeInterval fromApi(Interval interval) + { TimeInterval timeInterval = new TimeInterval(); timeInterval.setStart(interval.getStart()); timeInterval.setEnd(interval.getEnd()); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantConfigurationModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantConfigurationModel.java index e729c374b..e8f16e798 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantConfigurationModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantConfigurationModel.java @@ -1,8 +1,7 @@ package cz.cesnet.shongo.controller.rest.models.participant; import cz.cesnet.shongo.ParticipantRole; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import java.util.LinkedList; import java.util.List; @@ -12,14 +11,14 @@ * * @author Martin Srom */ +@Slf4j public class ParticipantConfigurationModel { - private static Logger logger = LoggerFactory.getLogger(ParticipantConfigurationModel.class); /** * List of participants. */ - protected List participants = new LinkedList(); + protected List participants = new LinkedList<>(); /** @@ -44,11 +43,11 @@ public void addParticipant(ParticipantModel participant) if (existingType.equals(ParticipantModel.Type.USER) && existingUserId.equals(userId)) { ParticipantRole existingRole = existingParticipant.getRole(); if (existingRole.compareTo(participant.getRole()) >= 0) { - logger.warn("Skip adding {} because {} already exists.", participant, existingParticipant); + log.warn("Skip adding {} because {} already exists.", participant, existingParticipant); return; } else { - logger.warn("Removing {} because {} will be added.", existingParticipant, participant); + log.warn("Removing {} because {} will be added.", existingParticipant, participant); participants.remove(existingParticipant); } break; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java index 9151d1edc..53c81d86a 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java @@ -5,8 +5,8 @@ import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.api.*; import cz.cesnet.shongo.controller.rest.CacheProvider; -import cz.cesnet.shongo.controller.rest.models.CommonModel; import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; +import cz.cesnet.shongo.controller.rest.models.CommonModel; import lombok.NoArgsConstructor; /** @@ -16,12 +16,10 @@ * @author Filip Karnis */ @NoArgsConstructor -public class ParticipantModel //implements ReportModel.ContextSerializable +public class ParticipantModel { - private CacheProvider cacheProvider; protected String id; - private Type type; private String userId; @@ -34,15 +32,8 @@ public class ParticipantModel //implements ReportModel.ContextSerializable private String organization; - public ParticipantModel(CacheProvider cacheProvider) - { - this.cacheProvider = cacheProvider; - this.type = Type.USER; - } - - public ParticipantModel(UserInformation userInformation, CacheProvider cacheProvider) + public ParticipantModel(UserInformation userInformation) { - this.cacheProvider = cacheProvider; this.type = Type.USER; setUserId(userInformation.getUserId()); } @@ -50,7 +41,6 @@ public ParticipantModel(UserInformation userInformation, CacheProvider cacheProv public ParticipantModel(AbstractParticipant participant, CacheProvider cacheProvider) { this.id = participant.getId(); - this.cacheProvider = cacheProvider; if (participant instanceof PersonParticipant) { PersonParticipant personParticipant = (PersonParticipant) participant; this.role = personParticipant.getRole(); @@ -168,11 +158,13 @@ public void setEmail(String email) this.email = email; } - public String getOrganization() { + public String getOrganization() + { return organization; } - public void setOrganization(String organization) { + public void setOrganization(String organization) + { this.organization = organization; } @@ -186,20 +178,6 @@ public void setRole(ParticipantRole role) this.role = role; } -// @Override -// public String toContextString() -// { -// Map attributes = new LinkedHashMap(); -// attributes.put("ID", id); -// attributes.put("Type", type); -// attributes.put("User ID", user != null ? user.getUserId() : null); -// attributes.put("Name", name); -// attributes.put("Email", email); -// attributes.put("Role", role); -// attributes.put("Organization", organization); -// return ReportModel.formatAttributes(attributes); -// } - @Override public String toString() { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/MetaModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/MetaModel.java index a2667c42a..ca672d645 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/MetaModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/MetaModel.java @@ -5,7 +5,8 @@ import org.joda.time.DateTimeZone; @Data -public class MetaModel { +public class MetaModel +{ private DateTimeZone timeZone; private SettingsModel settings; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/ReportModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/ReportModel.java index 3e76e70b9..89289b431 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/ReportModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/ReportModel.java @@ -1,6 +1,5 @@ package cz.cesnet.shongo.controller.rest.models.report; -import cz.cesnet.shongo.controller.api.SecurityToken; import lombok.Data; import lombok.extern.slf4j.Slf4j; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PeriodicityModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PeriodicityModel.java index 0610626a0..fbe1a06f1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PeriodicityModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PeriodicityModel.java @@ -12,7 +12,8 @@ * @author Filip Karnis */ @Data -public class PeriodicityModel { +public class PeriodicityModel +{ /** * Type of the period diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PhysicalResourceData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PhysicalResourceData.java index 76dcb2a30..e6eb844e0 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PhysicalResourceData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/PhysicalResourceData.java @@ -12,7 +12,8 @@ public class PhysicalResourceData private String resourceDescription; private String periodicity; - public static PhysicalResourceData fromApi(ResourceSummary summary) { + public static PhysicalResourceData fromApi(ResourceSummary summary) + { if (summary == null) { return null; } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java index 9ac7af8f7..b203f6a91 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java @@ -12,7 +12,6 @@ import cz.cesnet.shongo.controller.ObjectRole; import cz.cesnet.shongo.controller.ReservationRequestPurpose; import cz.cesnet.shongo.controller.ReservationRequestReusement; -import cz.cesnet.shongo.controller.api.ReservationRequest; import cz.cesnet.shongo.controller.api.*; import cz.cesnet.shongo.controller.api.request.AclEntryListRequest; import cz.cesnet.shongo.controller.api.request.ListResponse; @@ -21,9 +20,9 @@ import cz.cesnet.shongo.controller.api.rpc.ReservationService; import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; +import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; import cz.cesnet.shongo.controller.rest.models.TimeInterval; -import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import cz.cesnet.shongo.controller.rest.models.participant.ParticipantModel; import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; import cz.cesnet.shongo.controller.rest.models.users.SettingsModel; @@ -43,7 +42,7 @@ */ @Slf4j @Data -public class ReservationRequestCreateModel //implements ReportModel.ContextSerializable +public class ReservationRequestCreateModel { @JsonIgnore @@ -148,15 +147,6 @@ public ReservationRequestCreateModel(CacheProvider cacheProvider) this.cacheProvider = cacheProvider; } - /** - * Create new {@link ReservationRequestModel} from scratch. - */ - public ReservationRequestCreateModel(CacheProvider cacheProvider, SettingsModel userSettingsModel) - { - this(cacheProvider); - initByUserSettings(userSettingsModel); - } - /** * Create new {@link ReservationRequestModel} from existing {@code reservationRequest}. * @@ -174,47 +164,73 @@ public ReservationRequestCreateModel(AbstractReservationRequest reservationReque } /** - * @param userSettingsModel to be inited from + * @param reservationService + * @param securityToken + * @param cache + * @return list of reservation requests for permanent rooms */ - public void initByUserSettings(SettingsModel userSettingsModel) + public static List getPermanentRooms( + ReservationService reservationService, + SecurityToken securityToken, + Cache cache) { - // TODO -// if (userSettingsModel.getSlotBefore() != null) { -// setSlotBeforeMinutes(userSettingsModel.getSlotBefore()); -// } -// if (userSettingsModel.getSlotAfter() != null) { -// setSlotAfterMinutes(userSettingsModel.getSlotAfter()); -// } + ReservationRequestListRequest request = new ReservationRequestListRequest(); + request.setSecurityToken(securityToken); + request.addSpecificationType(ReservationRequestSummary.SpecificationType.PERMANENT_ROOM); + List reservationRequests = new LinkedList<>(); + + ListResponse response = reservationService.listReservationRequests(request); + if (response.getItemCount() > 0) { + Set reservationRequestIds = new HashSet<>(); + for (ReservationRequestSummary reservationRequestSummary : response) { + reservationRequestIds.add(reservationRequestSummary.getId()); + } + cache.fetchObjectPermissions(securityToken, reservationRequestIds); + + for (ReservationRequestSummary reservationRequestSummary : response) { + ExecutableState executableState = reservationRequestSummary.getExecutableState(); + if (executableState == null || (!executableState.isAvailable() && !executableState.equals( + ExecutableState.NOT_STARTED))) { + continue; + } + Set objectPermissions = cache.getObjectPermissions(securityToken, + reservationRequestSummary.getId()); + if (!objectPermissions.contains(ObjectPermission.PROVIDE_RESERVATION_REQUEST)) { + continue; + } + reservationRequests.add(reservationRequestSummary); + } + } + return reservationRequests; } -// /** -// * @return this as {@link ReservationRequestDetailModel} -// */ -// public ReservationRequestDetailModel getDetail() -// { -// if (this instanceof ReservationRequestDetailModel) { -// return (ReservationRequestDetailModel) this; -// } -// return null; -// } -// -// /** -// * @return this as {@link ReservationRequestModificationModel} -// */ -// public ReservationRequestModificationModel getModification() -// { -// if (this instanceof ReservationRequestModificationModel) { -// return (ReservationRequestModificationModel) this; -// } -// return null; -// } + /** + * @param reservationRequestId + * @param reservationService + * @param securityToken + * @return list of deletion dependencies for reservation request with given {@code reservationRequestId} + */ + public static List getDeleteDependencies( + String reservationRequestId, + ReservationService reservationService, + SecurityToken securityToken) + { + // List reservation requests which reuse the reservation request to be deleted + ReservationRequestListRequest reservationRequestListRequest = new ReservationRequestListRequest(); + reservationRequestListRequest.setSecurityToken(securityToken); + reservationRequestListRequest.setReusedReservationRequestId(reservationRequestId); + ListResponse reservationRequests = + reservationService.listReservationRequests(reservationRequestListRequest); + return reservationRequests.getItems(); + } public LocalTime getStart() { return slot.getStart().toLocalTime(); } - public DateTime getRequestStart() { + public DateTime getRequestStart() + { return slot.getStart(); } @@ -228,7 +244,8 @@ public void setEnd(DateTime end) slot.setEnd(end); } - public void setCollidingInterval(Interval collidingInterval) { + public void setCollidingInterval(Interval collidingInterval) + { this.collidingInterval = collidingInterval; collidingWithFirstSlot = false; @@ -236,23 +253,28 @@ public void setCollidingInterval(Interval collidingInterval) { if (getEnd() == null) { switch (this.durationType) { case MINUTE: - collidingWithFirstSlot = SlotHelper.areIntervalsColliding(start,this.durationCount,0,0,collidingInterval); + collidingWithFirstSlot = SlotHelper.areIntervalsColliding(start, this.durationCount, 0, 0, + collidingInterval); break; case HOUR: - collidingWithFirstSlot = SlotHelper.areIntervalsColliding(start,0,this.durationCount,0,collidingInterval); + collidingWithFirstSlot = SlotHelper.areIntervalsColliding(start, 0, this.durationCount, 0, + collidingInterval); break; case DAY: - collidingWithFirstSlot = SlotHelper.areIntervalsColliding(start,0,0,this.durationCount,collidingInterval); + collidingWithFirstSlot = SlotHelper.areIntervalsColliding(start, 0, 0, this.durationCount, + collidingInterval); break; default: return; } - } else { - collidingWithFirstSlot = SlotHelper.areIntervalsColliding(start,getEnd(),collidingInterval); + } + else { + collidingWithFirstSlot = SlotHelper.areIntervalsColliding(start, getEnd(), collidingInterval); } } - public void resetCollidingInterval() { + public void resetCollidingInterval() + { collidingInterval = null; collidingWithFirstSlot = false; } @@ -308,11 +330,13 @@ public Period getSlotAfter() } } - public LocalDate getStartDate() { + public LocalDate getStartDate() + { return slot.getStart().toLocalDate(); } - public void setStartDate(LocalDate startDate) { + public void setStartDate(LocalDate startDate) + { this.slot.setStart(startDate.toDateTimeAtStartOfDay()); } @@ -430,7 +454,7 @@ public void addRoomParticipant(ParticipantModel participantModel) public ParticipantModel addRoomParticipant(UserInformation userInformation, ParticipantRole role) { - ParticipantModel participantModel = new ParticipantModel(userInformation, cacheProvider); + ParticipantModel participantModel = new ParticipantModel(userInformation); participantModel.setNewId(); participantModel.setRole(role); participantModel.setEmail(userInformation.getEmail()); @@ -526,12 +550,14 @@ public void fromSpecificationApi(Specification specification, CacheProvider cach else { specificationType = SpecificationType.ROOM_CAPACITY; } - } else if (specification instanceof ResourceSpecification) { + } + else if (specification instanceof ResourceSpecification) { ResourceSpecification resourceSpecification = (ResourceSpecification) specification; resourceId = resourceSpecification.getResourceId(); ReservationRequestSummary summary = cacheProvider.getAllocatedReservationRequestSummary(this.id); specificationType = SpecificationType.fromReservationRequestSummary(summary, true); - } else { + } + else { throw new UnsupportedApiException(specification); } } @@ -585,12 +611,14 @@ else if (abstractReservationRequest instanceof ReservationRequestSet) { throw new UnsupportedApiException("Only periodic date/time slots are allowed."); } PeriodicDateTimeSlot periodicSlot = (PeriodicDateTimeSlot) slot; - PeriodicDateTimeSlot.PeriodicityType periodicityType= PeriodicDateTimeSlot.PeriodicityType.fromPeriod(periodicSlot.getPeriod()); + PeriodicDateTimeSlot.PeriodicityType periodicityType = + PeriodicDateTimeSlot.PeriodicityType.fromPeriod(periodicSlot.getPeriod()); int periodicityCycle = PeriodicDateTimeSlot.PeriodicityType.getPeriodCycle(periodicSlot.getPeriod()); // Allows multiple slots only for WEEKLY if (!PeriodicDateTimeSlot.PeriodicityType.WEEKLY.equals(periodicityType) && slots.size() != 1) { - throw new UnsupportedApiException("Multiple periodic date/time slots are allowed only for week period."); + throw new UnsupportedApiException( + "Multiple periodic date/time slots are allowed only for week period."); } // Check if all slots have the same periodicity if (periodicity.getType() == null) { @@ -599,12 +627,16 @@ else if (abstractReservationRequest instanceof ReservationRequestSet) { timeZone = periodicSlot.getTimeZone(); duration = periodicSlot.getDuration(); } - else if (!periodicity.getType().equals(periodicityType) || periodicity.getPeriodicityCycle() != periodicityCycle - || !periodicSlot.getDuration().equals(duration) || !timeZone.equals(periodicSlot.getTimeZone())) { - throw new UnsupportedApiException("Multiple periodic date/time slots with different periodicity are not allowed."); + else if (!periodicity.getType() + .equals(periodicityType) || periodicity.getPeriodicityCycle() != periodicityCycle + || !periodicSlot.getDuration().equals(duration) || !timeZone.equals( + periodicSlot.getTimeZone())) { + throw new UnsupportedApiException( + "Multiple periodic date/time slots with different periodicity are not allowed."); } - int dayIndex = (periodicSlot.getStart().getDayOfWeek() == 7 ? 1 : periodicSlot.getStart().getDayOfWeek() + 1); + int dayIndex = (periodicSlot.getStart().getDayOfWeek() == 7 ? 1 : periodicSlot.getStart() + .getDayOfWeek() + 1); if (getStartDate() == null || getStartDate().isAfter(periodicSlot.getStart().toLocalDate())) { this.slot.setStart(periodicSlot.getStart().toDateTime(timeZone)); this.slot.setEnd(periodicSlot.getStart().plus(duration)); @@ -612,9 +644,11 @@ else if (!periodicity.getType().equals(periodicityType) || periodicity.getPeriod periodicity.getPeriodicDaysInWeek()[index] = PeriodicDateTimeSlot.DayOfWeek.fromDayIndex(dayIndex); index++; - if (PeriodicDateTimeSlot.PeriodicityType.MONTHLY.equals(periodicityType) && periodicity.getMonthPeriodicityType() == null) { + if (PeriodicDateTimeSlot.PeriodicityType.MONTHLY.equals(periodicityType) + && periodicity.getMonthPeriodicityType() == null) { periodicity.setMonthPeriodicityType(periodicSlot.getMonthPeriodicityType()); - if (PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals(periodicity.getMonthPeriodicityType())) { + if (PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals( + periodicity.getMonthPeriodicityType())) { periodicity.setPeriodicityDayOrder(periodicSlot.getPeriodicityDayOrder()); periodicity.setPeriodicityDayInMonth(periodicSlot.getPeriodicityDayInMonth()); } @@ -648,7 +682,8 @@ else if (slotEnd instanceof Partial) { periodicityEndSet = true; } else if ((periodicity.getPeriodicityEnd() == null && periodicity.getPeriodicityEnd() != periodicityEnd) - || (periodicity.getPeriodicityEnd() != null && !periodicity.getPeriodicityEnd().equals(periodicityEnd))) { + || (periodicity.getPeriodicityEnd() != null && !periodicity.getPeriodicityEnd() + .equals(periodicityEnd))) { throw new UnsupportedApiException("Slot end %s is not same for all slots.", slotEnd); } @@ -806,41 +841,7 @@ public Specification toSpecificationApi() */ public Period getDuration() { - // TODO filip - log.info(String.valueOf(slot)); return new Period(slot.getStart(), slot.getEnd()); -// switch (specificationType) { -// case PERMANENT_ROOM: -// if (end == null) { -// throw new IllegalStateException("Slot end must be not empty for alias."); -// } -// return new Period(startDate.toDateTimeAtStartOfDay(timeZone), end.withZone(timeZone).withTime(23, 59, 59, 0)); -// case MEETING_ROOM: -// case PARKING_PLACE: -// case VEHICLE: -// case ADHOC_ROOM: -// case PERMANENT_ROOM_CAPACITY: -// if (durationCount == null || durationType == null) { -// if (end != null) { -// return new Period(startDate.toDateTime(start,timeZone), end); -// } -// else { -// throw new IllegalStateException("Slot duration should be not empty."); -// } -// } -// switch (durationType) { -// case MINUTE: -// return Period.minutes(durationCount); -// case HOUR: -// return Period.hours(durationCount); -// case DAY: -// return Period.days(durationCount); -// default: -// throw new TodoImplementException(durationType); -// } -// default: -// throw new TodoImplementException("Reservation request duration."); -// } } /** @@ -886,18 +887,15 @@ public Interval getFirstSlot() { PeriodicDateTimeSlot first = getSlots(timeZone).first(); DateTime start = first.getStart(); -// DateTime start = this.start; if (timeZone != null) { // Use specified time zone LocalDateTime localDateTime = start.toLocalDateTime(); start = localDateTime.toDateTime(timeZone); } - switch (specificationType) { - case VIRTUAL_ROOM: - return new Interval(getRequestStart().withTime(0, 0, 0, 0), getDuration()); - default: - return new Interval(start, getDuration()); + if (specificationType == SpecificationType.VIRTUAL_ROOM) { + return new Interval(getRequestStart().withTime(0, 0, 0, 0), getDuration()); } + return new Interval(start, getDuration()); } public Period getPeriod() @@ -918,21 +916,23 @@ public Period getPeriod() } /** - * * @return all calculated slots */ - public SortedSet getSlots(DateTimeZone timeZone) { + public SortedSet getSlots(DateTimeZone timeZone) + { SortedSet slots = new TreeSet<>(); if (PeriodicDateTimeSlot.PeriodicityType.NONE.equals(periodicity.getType())) { DateTime requestStart = getRequestStart(); if (SpecificationType.VIRTUAL_ROOM.equals(getSpecificationType())) { requestStart = requestStart.withTimeAtStartOfDay(); } - PeriodicDateTimeSlot periodicDateTimeSlot = new PeriodicDateTimeSlot(requestStart, getDuration(), Period.ZERO); + PeriodicDateTimeSlot periodicDateTimeSlot = + new PeriodicDateTimeSlot(requestStart, getDuration(), Period.ZERO); periodicDateTimeSlot.setEnd(getStartDate()); periodicDateTimeSlot.setTimeZone(getTimeZone()); slots.add(periodicDateTimeSlot); - } else { + } + else { // Determine period Period period = periodicity.getType().toPeriod(periodicity.getPeriodicityCycle()); @@ -956,7 +956,8 @@ else if (timeZone != null) { periodicDateTimeSlot.setEnd(periodicity.getPeriodicityEnd()); slots.add(periodicDateTimeSlot); } - } else { + } + else { PeriodicDateTimeSlot periodicDateTimeSlot = new PeriodicDateTimeSlot(); periodicDateTimeSlot.setStart(getFirstSlotStart()); if (this.timeZone != null) { @@ -969,11 +970,13 @@ else if (timeZone != null) { periodicDateTimeSlot.setPeriod(period); periodicDateTimeSlot.setEnd(periodicity.getPeriodicityEnd()); if (PeriodicDateTimeSlot.PeriodicityType.MONTHLY.equals(periodicity.getType())) { - if (PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals(periodicity.getMonthPeriodicityType())) { + if (PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals( + periodicity.getMonthPeriodicityType())) { periodicDateTimeSlot.setMonthPeriodicityType(periodicity.getMonthPeriodicityType()); periodicDateTimeSlot.setPeriodicityDayOrder(periodicity.getPeriodicityDayOrder()); periodicDateTimeSlot.setPeriodicityDayInMonth(periodicity.getPeriodicityDayInMonth()); - } else { + } + else { periodicDateTimeSlot.setMonthPeriodicityType(periodicity.getMonthPeriodicityType()); } } @@ -988,7 +991,8 @@ public DateTime getFirstSlotStart() { DateTime slotStart = getRequestStart(); if (PeriodicDateTimeSlot.PeriodicityType.MONTHLY.equals(periodicity.getType()) - && PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals(periodicity.getMonthPeriodicityType())) { + && PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals( + periodicity.getMonthPeriodicityType())) { slotStart = getMonthFirstSlotStart(slotStart); } return slotStart; @@ -1003,7 +1007,8 @@ public DateTime getFirstSlotStart() private DateTime getMonthFirstSlotStart(DateTime slotStart) { if (!PeriodicDateTimeSlot.PeriodicityType.MONTHLY.equals(periodicity.getType()) - || !PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals(periodicity.getMonthPeriodicityType())) { + || !PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals( + periodicity.getMonthPeriodicityType())) { throw new IllegalStateException("Periodicity type has to be monthly for a specific day."); } if (periodicity.getPeriodicityDayInMonth() == null || @@ -1015,12 +1020,15 @@ private DateTime getMonthFirstSlotStart(DateTime slotStart) throw new IllegalStateException("For periodicity type MONTHLY must be set day of month."); } - while (slotStart.getDayOfWeek() != (periodicity.getPeriodicityDayInMonth().getDayIndex() == 1 ? 7 : periodicity.getPeriodicityDayInMonth().getDayIndex() - 1)) { + while (slotStart.getDayOfWeek() != (periodicity.getPeriodicityDayInMonth().getDayIndex() == 1 ? 7 + : periodicity.getPeriodicityDayInMonth().getDayIndex() - 1)) { slotStart = slotStart.plusDays(1); } - DateTime monthEnd = slotStart.plusMonths(1).minusDays(slotStart.getDayOfMonth() - 1);; + DateTime monthEnd = slotStart.plusMonths(1).minusDays(slotStart.getDayOfMonth() - 1); if (0 < periodicity.getPeriodicityDayOrder() && periodicity.getPeriodicityDayOrder() < 5) { - while ((slotStart.getDayOfMonth() % 7 == 0 ? slotStart.getDayOfMonth() / 7 : slotStart.getDayOfMonth() / 7 + 1) != periodicity.getPeriodicityDayOrder()) { + while ((slotStart.getDayOfMonth() % 7 == 0 + ? slotStart.getDayOfMonth() / 7 + : slotStart.getDayOfMonth() / 7 + 1) != periodicity.getPeriodicityDayOrder()) { if (slotStart.plusDays(7).isBefore(monthEnd.plusMonths(1))) { slotStart = slotStart.plusDays(7); } @@ -1042,17 +1050,80 @@ else if (periodicity.getPeriodicityDayOrder() == -1) { return slotStart; } - public void updateSlotStartToFutureSlot() + /** + * @param participantId + * @return {@link ParticipantModel} with given {@code participantId} + */ + public ParticipantModel getParticipant(String participantId) { - try { - setStartDate(getFirstFutureSlotStart()); + ParticipantModel participant = null; + for (ParticipantModel possibleParticipant : roomParticipants) { + if (participantId.equals(possibleParticipant.getId())) { + participant = possibleParticipant; + } + } + if (participant == null) { + throw new IllegalArgumentException("Participant " + participantId + " doesn't exist."); } - catch (IllegalStateException ex) { - // Continue if model is not set properly yet - return; + return participant; + } + + /** + * @param userId + * @param role + * @return true wheter {@link #roomParticipants} contains user participant with given {@code userId} and {@code role} + */ + public boolean hasUserParticipant(String userId, ParticipantRole role) + { + for (ParticipantModel participant : roomParticipants) { + if (participant.getType().equals(ParticipantModel.Type.USER) && participant.getUserId().equals(userId) + && role.equals(participant.getRole())) { + return true; + } } + return false; } + /** + * Add new participant. + * + * @param participant + * @param bindingResult + */ + public boolean createParticipant(ParticipantModel participant, SecurityToken securityToken) + { + participant.setNewId(); + addRoomParticipant(participant); + return true; + } + + /** + * Modify existing participant + * + * @param participantId + * @param participant + * @param bindingResult + */ + public boolean modifyParticipant(String participantId, ParticipantModel participant, SecurityToken securityToken) + { + ParticipantModel oldParticipant = getParticipant(participantId); + roomParticipants.remove(oldParticipant); + addRoomParticipant(participant); + return true; + } + + /** + * Delete existing participant. + * + * @param participantId + */ + public void deleteParticipant(String participantId) + { + ParticipantModel participant = getParticipant(participantId); + roomParticipants.remove(participant); + } + + public LocalDate getFirstFutureSlotStart() { DateTime slotStart = getRequestStart(); @@ -1067,18 +1138,22 @@ public LocalDate getFirstFutureSlotStart() for (PeriodicDateTimeSlot.DayOfWeek day : periodicity.getPeriodicDaysInWeek()) { daysOfWeek.add(day.getDayIndex() == 1 ? 7 : day.getDayIndex() - 1); } - while (!daysOfWeek.contains(slotStart.getDayOfWeek()) || slotStart.plus(duration).isBeforeNow()) { + while (!daysOfWeek.contains(slotStart.getDayOfWeek()) || slotStart.plus(duration) + .isBeforeNow()) { slotStart = slotStart.plusDays(1); } - } else { + } + else { slotStart = slotStart.plus(period); } break; case MONTHLY: - if (PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals(periodicity.getMonthPeriodicityType())) { + if (PeriodicDateTimeSlot.PeriodicityType.MonthPeriodicityType.SPECIFIC_DAY.equals( + periodicity.getMonthPeriodicityType())) { slotStart = getMonthFirstSlotStart(slotStart.plus(period).withDayOfMonth(1)); break; - } else { + } + else { slotStart = slotStart.plus(period); } break; @@ -1152,180 +1227,6 @@ else if (specificationType.equals(SpecificationType.ROOM_CAPACITY)) { return abstractReservationRequest; } -// /** -// * @param reservationRequest -// * @return list of {@link Page}s for given {@code reservationRequest} -// */ -// public static List getPagesForBreadcrumb(ReservationRequestSummary reservationRequest) -// { -// SpecificationType specificationType = SpecificationType.fromReservationRequestSummary(reservationRequest); -// return getPagesForBreadcrumb(reservationRequest.getId(), specificationType, -// reservationRequest.getParentReservationRequestId(), reservationRequest.getReusedReservationRequestId()); -// } -// -// /** -// * @param specificationType -// * @param parentReservationRequestId -// * @param permanentRoomReservationRequestId -// * @return list of {@link Page}s for this reservation request -// */ -// public static List getPagesForBreadcrumb(String reservationRequestId, -// SpecificationType specificationType, String parentReservationRequestId, -// String permanentRoomReservationRequestId) -// { -// List pages = new LinkedList(); -// -// String titleCode; -// if (parentReservationRequestId != null) { -// if (specificationType.equals(SpecificationType.PERMANENT_ROOM_CAPACITY)) { -// // Add page for permanent room reservation request -// pages.add(new Page( -// ClientWebUrl.format(ClientWebUrl.DETAIL_VIEW, permanentRoomReservationRequestId), -// "navigation.detail")); -// -// // Add page for reservation request set -// pages.add(new Page( -// ClientWebUrl.format(ClientWebUrl.DETAIL_VIEW, parentReservationRequestId), -// "navigation.detail.capacity")); -// } -// else { -// // Add page for reservation request set -// pages.add(new Page( -// ClientWebUrl.format(ClientWebUrl.DETAIL_VIEW, parentReservationRequestId), -// "navigation.detail")); -// } -// -// // This reservation request is periodic event -// titleCode = "navigation.detail.event"; -// } -// else if (specificationType.equals(SpecificationType.PERMANENT_ROOM_CAPACITY)) { -// // Add page for permanent room reservation request -// pages.add(new Page( -// ClientWebUrl.format(ClientWebUrl.DETAIL_VIEW, permanentRoomReservationRequestId), -// "navigation.detail")); -// -// // This reservation request is capacity -// titleCode = "navigation.detail.capacity"; -// } -// else { -// titleCode = "navigation.detail"; -// } -// -// // Add page for this reservation request -// pages.add(new Page(ClientWebUrl.format(ClientWebUrl.DETAIL_VIEW, reservationRequestId), titleCode)); -// -// return pages; -// } -// -// @Override -// public String toContextString() -// { -// Map attributes = new LinkedHashMap(); -// attributes.put("ID", id); -// attributes.put("Type", specificationType); -// attributes.put("Description", description); -// attributes.put("Purpose", purpose); -// attributes.put("Technology", technology); -// attributes.put("Start", start); -// attributes.put("End", end); -// attributes.put("Duration count", durationCount); -// attributes.put("Duration type", durationType); -// attributes.put("Periodicity type", periodicityType); -// attributes.put("Periodicity end", periodicityEnd); -// attributes.put("Room name", roomName); -// attributes.put("Permanent room", permanentRoomReservationRequestId); -// attributes.put("Participant count", roomParticipantCount); -// attributes.put("PIN", roomPin); -// attributes.put("Access mode", roomAccessMode); -// attributes.put("Exclude dates", excludeDates); -// return ReportModel.formatAttributes(attributes); -// } - -// /** -// * @param participantId -// * @return {@link ParticipantModel} with given {@code participantId} -// */ -// public ParticipantModel getParticipant(String participantId) -// { -// ParticipantModel participant = null; -// for (ParticipantModel possibleParticipant : roomParticipants) { -// if (participantId.equals(possibleParticipant.getId())) { -// participant = possibleParticipant; -// } -// } -// if (participant == null) { -// throw new IllegalArgumentException("Participant " + participantId + " doesn't exist."); -// } -// return participant; -// } -// -// /** -// * @param userId -// * @param role -// * @return true wheter {@link #roomParticipants} contains user participant with given {@code userId} and {@code role} -// */ -// public boolean hasUserParticipant(String userId, ParticipantRole role) -// { -// for (ParticipantModel participant : roomParticipants) { -// if (participant.getType().equals(ParticipantModel.Type.USER) && participant.getUserId().equals(userId) -// && role.equals(participant.getRole())) { -// return true; -// } -// } -// return false; -// } -// -// /** -// * Add new participant. -// * -// * @param participant -// * @param bindingResult -// */ -// public boolean createParticipant(ParticipantModel participant, BindingResult bindingResult, -// SecurityToken securityToken) -// { -// participant.validate(bindingResult); -// if (bindingResult.hasErrors()) { -// CommonModel.logValidationErrors(logger, bindingResult, securityToken); -// return false; -// } -// participant.setNewId(); -// addRoomParticipant(participant); -// return true; -// } -// -// /** -// * Modify existing participant -// * -// * @param participantId -// * @param participant -// * @param bindingResult -// */ -// public boolean modifyParticipant(String participantId, ParticipantModel participant, BindingResult bindingResult, -// SecurityToken securityToken) -// { -// participant.validate(bindingResult); -// if (bindingResult.hasErrors()) { -// CommonModel.logValidationErrors(logger, bindingResult, securityToken); -// return false; -// } -// ParticipantModel oldParticipant = getParticipant(participantId); -// roomParticipants.remove(oldParticipant); -// addRoomParticipant(participant); -// return true; -// } -// -// /** -// * Delete existing participant. -// * -// * @param participantId -// */ -// public void deleteParticipant(String participantId) -// { -// ParticipantModel participant = getParticipant(participantId); -// roomParticipants.remove(participant); -// } -// /** * Add all {@link ParticipantModel} from {@link #permanentRoomReservationRequest} to {@link #roomParticipants} */ @@ -1384,72 +1285,6 @@ public ParticipantRole getDefaultOwnerParticipantRole() } } - /** - * Type of duration unit. - */ - public enum DurationType - { - MINUTE, - HOUR, - DAY - } - - /** - * @param reservationService - * @param securityToken - * @param cache - * @return list of reservation requests for permanent rooms - */ - public static List getPermanentRooms(ReservationService reservationService, - SecurityToken securityToken, Cache cache) - { - ReservationRequestListRequest request = new ReservationRequestListRequest(); - request.setSecurityToken(securityToken); - request.addSpecificationType(ReservationRequestSummary.SpecificationType.PERMANENT_ROOM); - List reservationRequests = new LinkedList(); - - ListResponse response = reservationService.listReservationRequests(request); - if (response.getItemCount() > 0) { - Set reservationRequestIds = new HashSet(); - for (ReservationRequestSummary reservationRequestSummary : response) { - reservationRequestIds.add(reservationRequestSummary.getId()); - } - cache.fetchObjectPermissions(securityToken, reservationRequestIds); - - for (ReservationRequestSummary reservationRequestSummary : response) { - ExecutableState executableState = reservationRequestSummary.getExecutableState(); - if (executableState == null || (!executableState.isAvailable() && !executableState.equals(ExecutableState.NOT_STARTED))) { - continue; - } - Set objectPermissions = cache.getObjectPermissions(securityToken, - reservationRequestSummary.getId()); - if (!objectPermissions.contains(ObjectPermission.PROVIDE_RESERVATION_REQUEST)) { - continue; - } - reservationRequests.add(reservationRequestSummary); - } - } - return reservationRequests; - } - - /** - * @param reservationRequestId - * @param reservationService - * @param securityToken - * @return list of deletion dependencies for reservation request with given {@code reservationRequestId} - */ - public static List getDeleteDependencies(String reservationRequestId, - ReservationService reservationService, SecurityToken securityToken) - { - // List reservation requests which reuse the reservation request to be deleted - ReservationRequestListRequest reservationRequestListRequest = new ReservationRequestListRequest(); - reservationRequestListRequest.setSecurityToken(securityToken); - reservationRequestListRequest.setReusedReservationRequestId(reservationRequestId); - ListResponse reservationRequests = - reservationService.listReservationRequests(reservationRequestListRequest); - return reservationRequests.getItems(); - } - @Override public String toString() { @@ -1466,4 +1301,14 @@ public String toString() ", resourceId='" + resourceId + '\'' + '}'; } + + /** + * Type of duration unit. + */ + public enum DurationType + { + MINUTE, + HOUR, + DAY + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java index dd6e3ac55..bc6fdcd1c 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java @@ -21,7 +21,8 @@ */ @Data @EqualsAndHashCode(callSuper = true) -public class ReservationRequestDetailModel extends ReservationRequestModel { +public class ReservationRequestDetailModel extends ReservationRequestModel +{ private AllocationState allocationState; private ExecutableState executableState; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java index 959c261f5..a1bb85aec 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java @@ -13,7 +13,8 @@ * @author Filip Karnis */ @Data -public class ReservationRequestHistoryModel { +public class ReservationRequestHistoryModel +{ private String id; private DateTime createdAt; @@ -22,7 +23,8 @@ public class ReservationRequestHistoryModel { private AllocationState allocationState; private ReservationRequestState state; - public ReservationRequestHistoryModel(ReservationRequestSummary summary, CacheProvider cacheProvider) { + public ReservationRequestHistoryModel(ReservationRequestSummary summary, CacheProvider cacheProvider) + { this.id = summary.getId(); this.createdAt = summary.getDateTime(); this.createdBy = cacheProvider.getUserInformation(summary.getUserId()).getFullName(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java index e1079e3f6..c5457c537 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java @@ -7,7 +7,6 @@ import cz.cesnet.shongo.controller.rest.models.TimeInterval; import lombok.Data; import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.joda.time.DateTime; import java.util.Map; @@ -20,7 +19,8 @@ */ @Data @NoArgsConstructor -public class ReservationRequestModel { +public class ReservationRequestModel +{ private String id; private String description; @@ -53,7 +53,7 @@ public ReservationRequestModel( this.state = ReservationRequestState.fromApi(summary); this.ownerName = ownerInformation.getFullName(); this.ownerEmail = ownerInformation.getEmail(); - this.slot = new TimeInterval(summary.getEarliestSlot()); + this.slot = TimeInterval.fromApi(summary.getEarliestSlot()); this.type = SpecificationType.fromReservationRequestSummary(summary, true); this.virtualRoomData = new VirtualRoomModel(summary); this.physicalResourceData = PhysicalResourceData.fromApi(resourceSummary); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java index 5023088c3..e6427eeb1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java @@ -76,41 +76,11 @@ public enum ReservationRequestState * * @param allocated sets the {@link #allocated} */ - private ReservationRequestState(boolean allocated) + ReservationRequestState(boolean allocated) { this.allocated = allocated; } - /** - * @return {@link #allocated} - */ - public boolean isAllocated() - { - return allocated; - } - - public String getMessage(MessageSource messageSource, Locale locale, SpecificationType specificationType) - { - return messageSource.getMessage( - "views.reservationRequest.state." + specificationType + "." + this, null, locale); - } - - public String getHelp(MessageSource messageSource, Locale locale, SpecificationType specificationType, - String reservationId) - { - String helpMessage = "views.reservationRequest.stateHelp." + specificationType + "." + this; - if (this.equals(FAILED) && reservationId != null) { - return messageSource.getMessage(helpMessage + ".hasReservation", null, locale); - } - return messageSource.getMessage(helpMessage, null, locale); - } - - public String getHelp(MessageSource messageSource, Locale locale, SpecificationType specificationType) - { - String helpMessageCode = "views.reservationRequest.stateHelp." + specificationType + "." + this; - return messageSource.getMessage(helpMessageCode, null, locale); - } - public static ReservationRequestState fromApi(ReservationRequestSummary reservationRequest) { return fromApi(reservationRequest.getAllocationState(), reservationRequest.getExecutableState(), @@ -186,4 +156,34 @@ public static ReservationRequestState fromApi(AllocationState allocationState, E return NOT_ALLOCATED; } } + + /** + * @return {@link #allocated} + */ + public boolean isAllocated() + { + return allocated; + } + + public String getMessage(MessageSource messageSource, Locale locale, SpecificationType specificationType) + { + return messageSource.getMessage( + "views.reservationRequest.state." + specificationType + "." + this, null, locale); + } + + public String getHelp(MessageSource messageSource, Locale locale, SpecificationType specificationType, + String reservationId) + { + String helpMessage = "views.reservationRequest.stateHelp." + specificationType + "." + this; + if (this.equals(FAILED) && reservationId != null) { + return messageSource.getMessage(helpMessage + ".hasReservation", null, locale); + } + return messageSource.getMessage(helpMessage, null, locale); + } + + public String getHelp(MessageSource messageSource, Locale locale, SpecificationType specificationType) + { + String helpMessageCode = "views.reservationRequest.stateHelp." + specificationType + "." + this; + return messageSource.getMessage(helpMessageCode, null, locale); + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RoomCapacityModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RoomCapacityModel.java index 8eaa4ecd6..29a150d7b 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RoomCapacityModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RoomCapacityModel.java @@ -4,7 +4,8 @@ import lombok.Data; @Data -public class RoomCapacityModel { +public class RoomCapacityModel +{ private String roomReservationRequestId; private Integer capacityParticipantCount; @@ -12,7 +13,8 @@ public class RoomCapacityModel { private Boolean hasRoomRecordings; private Boolean isRecordingActive; - public RoomCapacityModel(ReservationRequestSummary summary) { + public RoomCapacityModel(ReservationRequestSummary summary) + { this.roomReservationRequestId = summary.getReusedReservationRequestId(); this.capacityParticipantCount = summary.getRoomParticipantCount(); this.hasRoomRecordingService = summary.hasRoomRecordingService(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java index 4569e735d..1911ed3f5 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java @@ -56,7 +56,7 @@ public enum SpecificationType /** * Constructor. * - * @param isRoom sets the {@link #isRoom} + * @param isRoom sets the {@link #isRoom} * @param isPhysical */ SpecificationType(boolean isRoom, boolean isPhysical) @@ -65,21 +65,6 @@ public enum SpecificationType this.isPhysical = isPhysical; } - /** - * @return {@link #isRoom} - */ - public boolean isRoom() - { - return isRoom; - } - - /** - * @return {@link #isPhysical} - */ - public boolean isPhysical() { - return isPhysical; - } - /** * @param reservationRequestSummary * @return {@link SpecificationType} from given {@code reservationRequestSummary} @@ -115,7 +100,8 @@ public static SpecificationType fromReservationRequestSummary( if (resourceTags != null) { if (parkTagName != null && resourceTags.contains(parkTagName)) { return PARKING_PLACE; - } else if (vehicleTagName != null && resourceTags.contains(vehicleTagName)) { + } + else if (vehicleTagName != null && resourceTags.contains(vehicleTagName)) { return VEHICLE; } } @@ -135,19 +121,39 @@ public static SpecificationType fromString(String string) if (string == null) { return null; - } else if (string.equals(configuration.getMeetingRoomTagName())) { + } + else if (string.equals(configuration.getMeetingRoomTagName())) { return MEETING_ROOM; - } else if (string.equals(configuration.getVehicleTagName())) { + } + else if (string.equals(configuration.getVehicleTagName())) { return VEHICLE; - } else if (string.equals(configuration.getParkingPlaceTagName())) { + } + else if (string.equals(configuration.getParkingPlaceTagName())) { return PARKING_PLACE; } throw new TodoImplementException("SpecificationType.fromString for " + string); } - private static ControllerConfiguration getControllerConfiguration() { + private static ControllerConfiguration getControllerConfiguration() + { Controller controller = Controller.getInstance(); return controller.getConfiguration(); } + + /** + * @return {@link #isRoom} + */ + public boolean isRoom() + { + return isRoom; + } + + /** + * @return {@link #isPhysical} + */ + public boolean isPhysical() + { + return isPhysical; + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/VirtualRoomModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/VirtualRoomModel.java index 4e3b386da..e5b2df822 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/VirtualRoomModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/VirtualRoomModel.java @@ -9,14 +9,16 @@ import java.util.Set; @Data -public class VirtualRoomModel { +public class VirtualRoomModel +{ private String roomName; private ExecutableState state; private TechnologyModel technology; private Boolean hasRoomRecordings; - public VirtualRoomModel(ReservationRequestSummary summary) { + public VirtualRoomModel(ReservationRequestSummary summary) + { this.roomName = summary.getRoomName(); this.hasRoomRecordings = summary.hasRoomRecordings(); this.state = summary.getExecutableState(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacity.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacity.java index 13a122206..5f1ddc32a 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacity.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacity.java @@ -13,7 +13,7 @@ /** * Represents a type of capacity which can be utilized in a resource. - * + *

* Theoretically multiple different capacities can be utilized for a single resource * (e.g., capacity of room licenses and recording capacity) * @@ -74,8 +74,8 @@ public String getResourceName() /** * @param utilization - * @param t {@link FormatType} which should be used - * @param s {@link FormatStyle} which should be used + * @param t {@link FormatType} which should be used + * @param s {@link FormatStyle} which should be used * @return html content which shall be rendered for given {@code utilization} */ public abstract String formatUtilization(ResourceCapacityUtilization utilization, FormatType t, FormatStyle s); @@ -83,7 +83,7 @@ public String getResourceName() /** * Specifies what utilization should be formatted in {@link #formatUtilization}. */ - public static enum FormatType + public enum FormatType { /** * Maximum utilization of resource capacity (e.g., maximum number of utilized license count). @@ -99,7 +99,7 @@ public static enum FormatType /** * Specifies how the utilization value should be formatted in {@link #formatUtilization}. */ - public static enum FormatStyle + public enum FormatStyle { /** * Absolute utilization (e.g., number of licenses). @@ -125,7 +125,7 @@ protected static abstract class LicenseCount extends ResourceCapacity /** * Constructor. * - * @param resource sets the {@link #resource} + * @param resource sets the {@link #resource} * @param licenseCount sets the {@link #licenseCount} */ public LicenseCount(ResourceSummary resource, Integer licenseCount) @@ -170,7 +170,7 @@ public String formatUtilization(ResourceCapacityUtilization utilization, FormatT output.append(value); break; case RELATIVE: - output.append((int)((utilizedLicenseCount.doubleValue() * 100.0) / (double) licenseCount)); + output.append((int) ((utilizedLicenseCount.doubleValue() * 100.0) / (double) licenseCount)); output.append("%"); break; default: @@ -229,7 +229,7 @@ public static class Room extends LicenseCount /** * Constructor. * - * @param resource sets the {@link #resource} + * @param resource sets the {@link #resource} * @param capability to be used for determining available license count */ public Room(ResourceSummary resource, RoomProviderCapability capability) @@ -252,7 +252,7 @@ public static class Recording extends LicenseCount /** * Constructor. * - * @param resource sets the {@link #resource} + * @param resource sets the {@link #resource} * @param capability to be used for determining available license count */ public Recording(ResourceSummary resource, RecordingCapability capability) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilization.java index 759b09b22..11519fc52 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilization.java @@ -20,7 +20,7 @@ public class ResourceCapacityUtilization * utilized in some way. Different buckets means different utilization. Buckets shall be sorted to be able to * determine maximum utilization. */ - private List buckets = new LinkedList(); + private final List buckets = new LinkedList<>(); /** * List of {@link ReservationSummary} in all {@link #buckets}. @@ -71,23 +71,18 @@ public Collection getReservations() for (ResourceCapacityBucket bucket : buckets) { reservations.addAll(bucket); } - this.reservations = new LinkedList(); + this.reservations = new LinkedList<>(); this.reservations.addAll(reservations); - Collections.sort(this.reservations, new Comparator() - { - @Override - public int compare(ReservationSummary reservation1, ReservationSummary reservation2) - { - Interval reservationSlot1 = reservation1.getSlot(); - Interval reservationSlot2 = reservation2.getSlot(); + this.reservations.sort((reservation1, reservation2) -> { + Interval reservationSlot1 = reservation1.getSlot(); + Interval reservationSlot2 = reservation2.getSlot(); - int result = reservationSlot1.getStart().compareTo(reservationSlot2.getStart()); - if (result != 0) { - return result; - } - - return reservationSlot1.getEnd().compareTo(reservationSlot2.getEnd()); + int result = reservationSlot1.getStart().compareTo(reservationSlot2.getStart()); + if (result != 0) { + return result; } + + return reservationSlot1.getEnd().compareTo(reservationSlot2.getEnd()); }); } return Collections.unmodifiableList(this.reservations); @@ -98,7 +93,7 @@ public int compare(ReservationSummary reservation1, ReservationSummary reservati */ public Collection getReservationUserIds() { - Set userIds = new HashSet(); + Set userIds = new HashSet<>(); for (ReservationSummary reservation : getReservations()) { userIds.add(reservation.getUserId()); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilizationModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilizationModel.java index 74af981ca..8ad302f6f 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilizationModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilizationModel.java @@ -7,7 +7,8 @@ import java.util.Map; @Data -public class ResourceCapacityUtilizationModel { +public class ResourceCapacityUtilizationModel +{ private Collection resourceCapacitySet; private Map> resourceCapacityUtilization; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java index 6a97eb90c..c4bf8b0f8 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java @@ -7,7 +7,8 @@ import java.util.Set; @Data -public class ResourceModel { +public class ResourceModel +{ private String id; private ResourceSummary.Type type; @@ -17,7 +18,8 @@ public class ResourceModel { private Set tags; private boolean hasCapacity; - public ResourceModel(ResourceSummary summary, boolean hasCapacity) { + public ResourceModel(ResourceSummary summary, boolean hasCapacity) + { this.id = summary.getId(); this.type = summary.getType(); this.name = summary.getName(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java index 637df936e..1d35db5fa 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java @@ -15,16 +15,6 @@ public class ResourceUtilizationModel private TimeInterval interval; private List resources; - @Data - public static class UtilizationModel - { - - private String id; - private String name; - private int totalCapacity; - private int usedCapacity; - } - public static ResourceUtilizationModel fromApi( Interval interval, Map resourceCapacityUtilizations) @@ -37,7 +27,8 @@ public static ResourceUtilizationModel fromApi( utilizationModel.setId(roomCapacity.getResourceId()); utilizationModel.setName(roomCapacity.getResourceName()); utilizationModel.setTotalCapacity(roomCapacity.getLicenseCount()); - utilizationModel.setUsedCapacity((resourceCapacityUtilization != null) ? resourceCapacityUtilization.getPeakBucket().getLicenseCount() : 0); + utilizationModel.setUsedCapacity((resourceCapacityUtilization != null) + ? resourceCapacityUtilization.getPeakBucket().getLicenseCount() : 0); resources.add(utilizationModel); }); @@ -46,4 +37,14 @@ public static ResourceUtilizationModel fromApi( resourceUtilizationModel.setResources(resources); return resourceUtilizationModel; } + + @Data + public static class UtilizationModel + { + + private String id; + private String name; + private int totalCapacity; + private int usedCapacity; + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java index f039f8837..40ee6d06b 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java @@ -7,11 +7,10 @@ import cz.cesnet.shongo.controller.api.rpc.ReservationService; import cz.cesnet.shongo.controller.api.rpc.ResourceService; import cz.cesnet.shongo.util.RangeSet; +import lombok.extern.slf4j.Slf4j; import org.joda.time.DateTime; import org.joda.time.Interval; import org.joda.time.Period; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.*; @@ -20,9 +19,9 @@ * * @author Martin Srom */ +@Slf4j public class ResourcesUtilization { - private static Logger logger = LoggerFactory.getLogger(ResourcesUtilization.class); /** * {@link SecurityToken} of user to which the {@link ResourcesUtilization} belongs. @@ -43,19 +42,19 @@ public class ResourcesUtilization * Map of {@link ResourceCapacity} by class and by resource-id. */ private final Map, ResourceCapacity>> resourceCapacityMap = - new HashMap, ResourceCapacity>>(); + new HashMap<>(); /** * Map of cached {@link ResourceCapacityUtilization} for {@link ResourceCapacity} and for {@link Interval}. */ - private Map> resourceCapacityUtilizationMap = - new HashMap>(); + private final Map> resourceCapacityUtilizationMap = + new HashMap<>(); /** * Map of cached {@link ReservationSummary}s for {@link ResourceCapacity}. */ - private Map> reservationSetMap = - new HashMap>(); + private final Map> reservationSetMap = + new HashMap<>(); /** * {@link Interval} which is already cached in {@link #reservationSetMap}. @@ -125,11 +124,12 @@ public ResourceCapacity getResourceCapacity(String resourceId, Class> getUtilization(Interval interval, - Period period) + public Map> getUtilization( + Interval interval, + Period period) { Map> utilizationsByInterval = - new LinkedHashMap>(); + new LinkedHashMap<>(); DateTime start = interval.getStart(); DateTime maxEnd = interval.getEnd(); while (start.isBefore(maxEnd)) { @@ -173,7 +173,7 @@ private void addResourceCapacity(ResourceCapacity resourceCapacity) Map, ResourceCapacity> resourceCapacitiesByClass = resourceCapacityMap.get(resourceId); if (resourceCapacitiesByClass == null) { - resourceCapacitiesByClass = new HashMap, ResourceCapacity>(); + resourceCapacitiesByClass = new HashMap<>(); resourceCapacityMap.put(resourceId, resourceCapacitiesByClass); } resourceCapacitiesByClass.put(resourceCapacityClass, resourceCapacity); @@ -189,13 +189,15 @@ private void addResourceCapacity(ResourceCapacity resourceCapacity) * @param fetchInterval * @return {@link ResourceCapacityUtilization} for given {@code resourceCapacity} and {@code interval} */ - private ResourceCapacityUtilization getUtilization(ResourceCapacity resourceCapacity, Interval interval, - boolean fetchAll, Interval fetchInterval) + private ResourceCapacityUtilization getUtilization( + ResourceCapacity resourceCapacity, + Interval interval, + boolean fetchAll, Interval fetchInterval) { // Try to return cached utilization Map utilizations = resourceCapacityUtilizationMap.get(interval); if (utilizations == null) { - utilizations = new HashMap(); + utilizations = new HashMap<>(); resourceCapacityUtilizationMap.put(interval, utilizations); } if (utilizations.containsKey(resourceCapacity)) { @@ -261,13 +263,13 @@ else if (reservationInterval != null && } // Load the whole reservation cache else { - logger.info("Clearing cached reservations..."); + log.info("Clearing cached reservations..."); reservationSetMap.clear(); reservationInterval = interval; } // Fetch reservations for all resource capacities - logger.info("Loading reservations for {}...", interval); + log.info("Loading reservations for {}...", interval); ReservationListRequest reservationListRequest = new ReservationListRequest(securityToken); for (ResourceCapacity currentResourceCapacity : resourceCapacities) { reservationListRequest.addResourceId(currentResourceCapacity.getResourceId()); @@ -281,7 +283,7 @@ else if (reservationInterval != null && ResourceCapacity reservationResourceCapacity = getResourceCapacity(reservationResourceId, reservation); RangeSet reservationSet = reservationSetMap.get(reservationResourceCapacity); if (reservationSet == null) { - reservationSet = new RangeSet() + reservationSet = new RangeSet<>() { @Override protected Bucket createBucket(DateTime rangeValue) @@ -307,8 +309,9 @@ protected Bucket createBucket(DateTime rangeValue) * @param interval * @return {@link RangeSet} of {@link ReservationSummary} */ - private RangeSet getReservations(ResourceCapacity resourceCapacity, - Interval interval) + private RangeSet getReservations( + ResourceCapacity resourceCapacity, + Interval interval) { // Try to return cached reservations synchronized (this) { @@ -318,7 +321,7 @@ private RangeSet getReservations(ResourceCapacity } // Fetch reservations for single resource capacity - RangeSet reservationSet = new RangeSet() + RangeSet reservationSet = new RangeSet<>() { @Override protected Bucket createBucket(DateTime rangeValue) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomAuthorizedData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomAuthorizedData.java index e08fe281b..81132399c 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomAuthorizedData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomAuthorizedData.java @@ -12,7 +12,8 @@ * @author Filip Karnis */ @Data -public class RoomAuthorizedData { +public class RoomAuthorizedData +{ private String pin; private String adminPin; @@ -20,7 +21,8 @@ public class RoomAuthorizedData { private Boolean allowGuests; private List aliases; - public RoomAuthorizedData(AbstractRoomExecutable roomExecutable) { + public RoomAuthorizedData(AbstractRoomExecutable roomExecutable) + { pin = roomExecutable.getPin(); adminPin = roomExecutable.getAdminPin(); guestPin = roomExecutable.getGuestPin(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomModel.java index 32dd27a0c..aeb048e5d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomModel.java @@ -12,7 +12,8 @@ * @author Filip Karnis */ @Data -public class RoomModel { +public class RoomModel +{ private String id; private ExecutableSummary.Type type; @@ -26,7 +27,8 @@ public class RoomModel { private int licenceCount; private int usageCount; - public RoomModel(ExecutableSummary summary) { + public RoomModel(ExecutableSummary summary) + { this.id = summary.getId(); this.type = summary.getType(); this.name = summary.getRoomName(); @@ -42,7 +44,7 @@ public RoomModel(ExecutableSummary summary) { if (slot == null) { slot = summary.getSlot(); } - this.slot = new TimeInterval(slot); + this.slot = TimeInterval.fromApi(slot); boolean isDeprecated; switch (state) { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomState.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomState.java index 41b157b2f..c52a65b16 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomState.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomState.java @@ -64,41 +64,16 @@ public enum RoomState this.isAvailable = isAvailable; } - /** - * @return {@link #isStarted} - */ - public boolean isStarted() - { - return isStarted; - } - - /** - * @return {@link #isAvailable} - */ - public boolean isAvailable() - { - return isAvailable; - } - - public String getMessage(MessageSource messageSource, Locale locale, RoomType roomType) - { - return messageSource.getMessage( - "views.executable.roomState." + roomType + "." + this, null, locale); - } - - public String getHelp(MessageSource messageSource, Locale locale, RoomType roomType) - { - return messageSource.getMessage( - "views.executable.roomStateHelp." + roomType + "." + this, null, locale); - } - /** * @param roomState * @param roomLicenseCount * @param roomUsageState * @return {@link RoomState} */ - public static RoomState fromRoomState(ExecutableState roomState, Integer roomLicenseCount, ExecutableState roomUsageState) + public static RoomState fromRoomState( + ExecutableState roomState, + Integer roomLicenseCount, + ExecutableState roomUsageState) { switch (roomState) { case NOT_STARTED: @@ -134,4 +109,32 @@ public static RoomState fromRoomState(ExecutableState roomState) { return fromRoomState(roomState, 1, null); } + + /** + * @return {@link #isStarted} + */ + public boolean isStarted() + { + return isStarted; + } + + /** + * @return {@link #isAvailable} + */ + public boolean isAvailable() + { + return isAvailable; + } + + public String getMessage(MessageSource messageSource, Locale locale, RoomType roomType) + { + return messageSource.getMessage( + "views.executable.roomState." + roomType + "." + this, null, locale); + } + + public String getHelp(MessageSource messageSource, Locale locale, RoomType roomType) + { + return messageSource.getMessage( + "views.executable.roomStateHelp." + roomType + "." + this, null, locale); + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomType.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomType.java index 62fe44036..b139b1625 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomType.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomType.java @@ -13,15 +13,11 @@ */ public enum RoomType { - /** - * Ad-hoc room. - */ - ADHOC_ROOM, /** * Permanent room. */ - PERMANENT_ROOM, + VIRTUAL_ROOM, /** * Used room. @@ -35,12 +31,7 @@ public enum RoomType public static RoomType fromExecutableSummary(ExecutableSummary executableSummary) { if (executableSummary.getType().equals(ExecutableSummary.Type.ROOM)) { - if (executableSummary.getRoomLicenseCount() == 0) { - return PERMANENT_ROOM; - } - else { - return ADHOC_ROOM; - } + return VIRTUAL_ROOM; } else if (executableSummary.getType().equals(ExecutableSummary.Type.USED_ROOM)) { return USED_ROOM; @@ -57,12 +48,7 @@ else if (executableSummary.getType().equals(ExecutableSummary.Type.USED_ROOM)) { public static RoomType fromRoomExecutable(AbstractRoomExecutable roomExecutable) { if (roomExecutable instanceof RoomExecutable) { - if (roomExecutable.getLicenseCount() == 0) { - return PERMANENT_ROOM; - } - else { - return ADHOC_ROOM; - } + return VIRTUAL_ROOM; } else if (roomExecutable instanceof UsedRoomExecutable) { return USED_ROOM; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/users/SettingsModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/users/SettingsModel.java index e5df519df..7b3093477 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/users/SettingsModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/users/SettingsModel.java @@ -11,7 +11,8 @@ @Data @NoArgsConstructor -public class SettingsModel { +public class SettingsModel +{ private Boolean useWebService; private Locale locale; @@ -20,7 +21,8 @@ public class SettingsModel { private Boolean administrationMode; private List permissions; - public SettingsModel(UserSettings userSettings, List permissions) { + public SettingsModel(UserSettings userSettings, List permissions) + { this.useWebService = userSettings.isUseWebService(); this.locale = userSettings.getLocale(); this.homeTimeZone = userSettings.getHomeTimeZone(); From 0ed0b5fde04476f1693b4132cfdf48b433773601 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sat, 14 May 2022 13:45:11 +0200 Subject: [PATCH 034/134] Removed 's' from capacity_utilizations path, and removed interval attribute from ResourceUtilizationDetailModel --- .../cz/cesnet/shongo/controller/rest/ClientWebUrl.java | 4 ++-- .../controller/rest/controllers/ResourceController.java | 9 ++++----- .../models/resource/ResourceUtilizationDetailModel.java | 5 ----- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java index 7e1fb7fd5..8e4843863 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java @@ -17,8 +17,8 @@ public class ClientWebUrl public static final String GROUPS_DETAIL = "/groups/{groupId:.+}"; public static final String RESOURCES = API_PREFIX + "/resources"; - public static final String CAPACITY_UTILIZATION = "/capacity_utilizations"; - public static final String CAPACITY_UTILIZATION_DETAIL = "/{id}/capacity_utilizations"; + public static final String CAPACITY_UTILIZATION = "/capacity_utilization"; + public static final String CAPACITY_UTILIZATION_DETAIL = "/{id}/capacity_utilization"; public static final String RESERVATION_REQUESTS = API_PREFIX + "/reservation_requests"; public static final String RESERVATION_REQUESTS_ACCEPT = ID_SUFFIX + "/accept"; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java index 2c82dcfc3..a1d964508 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java @@ -75,7 +75,7 @@ List listResources( return accessibleResources.getItems() .stream() .map(resourceSummary -> { - // TODO hasCapacity + // Find out whether the resource has capacity Resource resource = resourceService.getResource(securityToken, resourceSummary.getId()); return new ResourceModel(resourceSummary, !resource.getCapabilities().isEmpty()); }) @@ -105,8 +105,8 @@ ListResponse listResourcesUtilization( Map> utilization = resourcesUtilization.getUtilization(new Interval(intervalFrom, intervalTo), period); List items = new ArrayList<>(); - utilization.forEach((interval, resourceCapacityUtilizations) -> { - items.add(ResourceUtilizationModel.fromApi(interval, resourceCapacityUtilizations)); + utilization.forEach((interval, resourceCapacityUtilization) -> { + items.add(ResourceUtilizationModel.fromApi(interval, resourceCapacityUtilization)); }); return ListResponse.fromRequest(start, count, items); } @@ -119,7 +119,6 @@ ListResponse listResourcesUtilization( ResourceUtilizationDetailModel getResourceUtilization( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable("id") String resourceId, - @RequestParam String resource, @RequestParam(value = "interval_from") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) DateTime intervalFrom, @RequestParam(value = "interval_to") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) DateTime intervalTo) throws ClassNotFoundException @@ -142,6 +141,6 @@ ResourceUtilizationDetailModel getResourceUtilization( ).collect(Collectors.toList()) : Collections.emptyList(); return ResourceUtilizationDetailModel.fromApi( - resourceCapacityUtilization, roomCapacity, new Interval(intervalFrom, intervalTo), reservations); + resourceCapacityUtilization, roomCapacity, reservations); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationDetailModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationDetailModel.java index e9210080e..4af31d091 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationDetailModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationDetailModel.java @@ -1,8 +1,6 @@ package cz.cesnet.shongo.controller.rest.models.resource; -import cz.cesnet.shongo.controller.rest.models.TimeInterval; import lombok.Data; -import org.joda.time.Interval; import java.util.List; @@ -14,13 +12,11 @@ public class ResourceUtilizationDetailModel private String name; private int totalCapacity; private int usedCapacity; - private TimeInterval interval; private List reservations; public static ResourceUtilizationDetailModel fromApi( ResourceCapacityUtilization resourceCapacityUtilization, ResourceCapacity.Room roomCapacity, - Interval interval, List reservations) { int licenseCount = (resourceCapacityUtilization != null) @@ -32,7 +28,6 @@ public static ResourceUtilizationDetailModel fromApi( resourceUtilizationDetailModel.setName(roomCapacity.getResourceName()); resourceUtilizationDetailModel.setTotalCapacity(roomCapacity.getLicenseCount()); resourceUtilizationDetailModel.setUsedCapacity(licenseCount); - resourceUtilizationDetailModel.setInterval(TimeInterval.fromApi(interval)); resourceUtilizationDetailModel.setReservations(reservations); return resourceUtilizationDetailModel; } From 860eeaf7bbb6ca43dc8682ae583c89749a95519b Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sat, 14 May 2022 17:26:11 +0200 Subject: [PATCH 035/134] Updated joda time to version 2.9.9 to fix confilted versions --- shongo-common-api/pom.xml | 2 +- .../java/org/joda/time/format/PeriodCzechAffix.java | 12 ++++++++++++ shongo-controller/pom.xml | 11 ----------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/shongo-common-api/pom.xml b/shongo-common-api/pom.xml index 5df700ca2..590d779f8 100644 --- a/shongo-common-api/pom.xml +++ b/shongo-common-api/pom.xml @@ -26,7 +26,7 @@ joda-time joda-time - 2.3 + 2.9.9 diff --git a/shongo-common-api/src/main/java/org/joda/time/format/PeriodCzechAffix.java b/shongo-common-api/src/main/java/org/joda/time/format/PeriodCzechAffix.java index db5220594..6f553e12d 100644 --- a/shongo-common-api/src/main/java/org/joda/time/format/PeriodCzechAffix.java +++ b/shongo-common-api/src/main/java/org/joda/time/format/PeriodCzechAffix.java @@ -135,4 +135,16 @@ else if (o1.length() < o2.length()) { } return ~position; } + + @Override + public String[] getAffixes() + { + return new String[] { iSingularText, iFewText, iPluralText }; + } + + @Override + public void finish(Set set) + { + set.add(this); + } } diff --git a/shongo-controller/pom.xml b/shongo-controller/pom.xml index f6f14667f..575988d68 100644 --- a/shongo-controller/pom.xml +++ b/shongo-controller/pom.xml @@ -110,18 +110,7 @@ 1.4.0 - - - org.codehaus.jackson - jackson-mapper-asl - 1.9.13 - - - com.fasterxml.jackson.core - jackson-core - ${jackson.version} - com.fasterxml.jackson.datatype jackson-datatype-joda From f292c95ae04a31ba7f40f516f543fcefbd7200dd Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sat, 14 May 2022 17:41:14 +0200 Subject: [PATCH 036/134] Use @DateTimeFormat to format interval query parameters --- .../ReservationRequestController.java | 16 +++++----------- .../controller/rest/models/TimeInterval.java | 3 --- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 337027d2c..0749f7194 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -26,6 +26,7 @@ import org.joda.time.DateTime; import org.joda.time.Interval; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -45,7 +46,6 @@ import java.util.stream.Collectors; import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; -import static cz.cesnet.shongo.controller.rest.models.TimeInterval.DATETIME_FORMATTER; /** * Rest controller for reservation request endpoints. @@ -86,8 +86,10 @@ ListResponse listRequests( @RequestParam(value = "allocation_state", required = false) AllocationState allocationState, @RequestParam(value = "parentRequestId", required = false) String permanentRoomId, @RequestParam(value = "technology", required = false) TechnologyModel technology, - @RequestParam(value = "interval_from", required = false) String fromParam, - @RequestParam(value = "interval_to", required = false) String toParam, + @RequestParam(value = "interval_from", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + DateTime intervalFrom, + @RequestParam(value = "interval_to", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + DateTime intervalTo, @RequestParam(value = "user_id", required = false) String userId, @RequestParam(value = "participant_user_id", required = false) String participantUserId, @RequestParam(value = "search", required = false) String search, @@ -129,14 +131,6 @@ ListResponse listRequests( request.setSpecificationTechnologies(technology.getTechnologies()); } - DateTime intervalFrom = null; - DateTime intervalTo = null; - if (fromParam != null) { - intervalFrom = DATETIME_FORMATTER.parseDateTime(fromParam); - } - if (toParam != null) { - intervalTo = DATETIME_FORMATTER.parseDateTime(toParam); - } if (intervalFrom != null || intervalTo != null) { if (intervalFrom == null) { intervalFrom = Temporal.DATETIME_INFINITY_START; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java index 382ee6bc4..03701ef8e 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java @@ -4,15 +4,12 @@ import lombok.Data; import org.joda.time.DateTime; import org.joda.time.Interval; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.DateTimeFormatter; @Data public class TimeInterval { private static final String ISO_8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; - public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormat.forPattern(ISO_8601_PATTERN); @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = ISO_8601_PATTERN) private DateTime start; From 869eb7e65060c815c163bd53e7f69069498f8adf Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sat, 14 May 2022 17:43:21 +0200 Subject: [PATCH 037/134] Format datetimes to ISO format --- .../cz/cesnet/shongo/controller/rest/models/TimeInterval.java | 2 +- .../reservationrequest/ReservationRequestCreateModel.java | 4 ++++ .../reservationrequest/ReservationRequestHistoryModel.java | 4 ++++ .../models/reservationrequest/ReservationRequestModel.java | 4 ++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java index 03701ef8e..291a191e9 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java @@ -9,7 +9,7 @@ public class TimeInterval { - private static final String ISO_8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + public static final String ISO_8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = ISO_8601_PATTERN) private DateTime start; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java index b203f6a91..ceba5409b 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java @@ -1,5 +1,6 @@ package cz.cesnet.shongo.controller.rest.models.reservationrequest; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Strings; @@ -34,6 +35,8 @@ import java.util.*; +import static cz.cesnet.shongo.controller.rest.models.TimeInterval.ISO_8601_PATTERN; + /** * Model for {@link AbstractReservationRequest}. * @@ -59,6 +62,7 @@ public class ReservationRequestCreateModel protected ReservationRequestPurpose purpose = ReservationRequestPurpose.USER; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = ISO_8601_PATTERN) protected DateTime dateTime; protected TechnologyModel technology; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java index a1bb85aec..bf7e86d98 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestHistoryModel.java @@ -1,5 +1,6 @@ package cz.cesnet.shongo.controller.rest.models.reservationrequest; +import com.fasterxml.jackson.annotation.JsonFormat; import cz.cesnet.shongo.controller.api.AllocationState; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; import cz.cesnet.shongo.controller.api.ReservationRequestType; @@ -7,6 +8,8 @@ import lombok.Data; import org.joda.time.DateTime; +import static cz.cesnet.shongo.controller.rest.models.TimeInterval.ISO_8601_PATTERN; + /** * Represents reservation request's history. * @@ -17,6 +20,7 @@ public class ReservationRequestHistoryModel { private String id; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = ISO_8601_PATTERN) private DateTime createdAt; private String createdBy; private ReservationRequestType type; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java index c5457c537..41858314f 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java @@ -1,5 +1,6 @@ package cz.cesnet.shongo.controller.rest.models.reservationrequest; +import com.fasterxml.jackson.annotation.JsonFormat; import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.ObjectPermission; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; @@ -12,6 +13,8 @@ import java.util.Map; import java.util.Set; +import static cz.cesnet.shongo.controller.rest.models.TimeInterval.ISO_8601_PATTERN; + /** * Represents reservation request. * @@ -24,6 +27,7 @@ public class ReservationRequestModel private String id; private String description; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = ISO_8601_PATTERN) private DateTime createdAt; private String parentRequestId; private ReservationRequestState state; From 35164428f99572475455397fab69a64a438e7752 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sat, 14 May 2022 17:46:48 +0200 Subject: [PATCH 038/134] Set default response status to CREATED for creation endpoints --- .../rest/controllers/ParticipantController.java | 2 ++ .../controllers/ReservationRequestController.java | 13 +++---------- .../rest/controllers/UserRoleController.java | 2 ++ 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java index 4e5d8a9fb..f0b742254 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java @@ -13,6 +13,7 @@ import cz.cesnet.shongo.controller.rest.models.participant.ParticipantModel; import io.swagger.v3.oas.annotations.Operation; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import java.util.Comparator; @@ -85,6 +86,7 @@ ListResponse listRequestParticipants( return ListResponse.fromRequest(start, count, items); } + @ResponseStatus(HttpStatus.CREATED) @Operation(summary = "Adds new participant to reservation request.") @PostMapping void addParticipant( diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 0749f7194..6784b0661 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -27,16 +27,8 @@ import org.joda.time.Interval; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestAttribute; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.HashSet; @@ -165,6 +157,7 @@ ListResponse listRequests( return listResponse; } + @ResponseStatus(HttpStatus.CREATED) @Operation(summary = "Creates reservation request.") @PostMapping void createRequest( diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java index c7ac6b15b..c4dd98b25 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java @@ -13,6 +13,7 @@ import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; import io.swagger.v3.oas.annotations.Operation; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -62,6 +63,7 @@ ListResponse listRequestRoles( return ListResponse.fromRequest(start, count, items); } + @ResponseStatus(HttpStatus.CREATED) @Operation(summary = "Creates new role for reservation request.") @PostMapping void createRequestRoles( From 03450c65c5c76d438cf67de9afcb786e839254c9 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sat, 14 May 2022 18:28:57 +0200 Subject: [PATCH 039/134] Renamed roomPin to userPin in ReservationRequestCreateModel --- .../ReservationRequestCreateModel.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java index ceba5409b..a7ea22869 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java @@ -26,7 +26,6 @@ import cz.cesnet.shongo.controller.rest.models.TimeInterval; import cz.cesnet.shongo.controller.rest.models.participant.ParticipantModel; import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; -import cz.cesnet.shongo.controller.rest.models.users.SettingsModel; import cz.cesnet.shongo.util.SlotHelper; import lombok.Data; import lombok.extern.slf4j.Slf4j; @@ -97,7 +96,7 @@ public class ReservationRequestCreateModel protected Integer participantCount; - protected String roomPin; + protected String userPin; protected String adminPin; @@ -490,7 +489,7 @@ public void fromSpecificationApi(Specification specification, CacheProvider cach try { String pin = h323RoomSetting.getPin(); if (!pin.isEmpty()) { - roomPin = String.valueOf(Integer.parseInt(pin)); + userPin = String.valueOf(Integer.parseInt(pin)); } } catch (NumberFormatException exception) { @@ -500,7 +499,7 @@ public void fromSpecificationApi(Specification specification, CacheProvider cach } if (roomSetting instanceof AdobeConnectRoomSetting) { AdobeConnectRoomSetting adobeConnectRoomSetting = (AdobeConnectRoomSetting) roomSetting; - roomPin = adobeConnectRoomSetting.getPin(); + userPin = adobeConnectRoomSetting.getPin(); roomAccessMode = adobeConnectRoomSetting.getAccessMode(); } if (roomSetting instanceof PexipRoomSetting) { @@ -808,19 +807,19 @@ public Specification toSpecificationApi() if (TechnologyModel.FREEPBX.equals(technology)) { FreePBXRoomSetting freePBXRoomSetting = new FreePBXRoomSetting(); freePBXRoomSetting.setAdminPin(adminPin); - freePBXRoomSetting.setUserPin(roomPin); + freePBXRoomSetting.setUserPin(userPin); roomSpecification.addRoomSetting(freePBXRoomSetting); } - if (TechnologyModel.H323_SIP.equals(technology) && roomPin != null) { + if (TechnologyModel.H323_SIP.equals(technology) && userPin != null) { H323RoomSetting h323RoomSetting = new H323RoomSetting(); - h323RoomSetting.setPin(roomPin); + h323RoomSetting.setPin(userPin); roomSpecification.addRoomSetting(h323RoomSetting); } if (TechnologyModel.ADOBE_CONNECT.equals(technology)) { AdobeConnectRoomSetting adobeConnectRoomSetting = new AdobeConnectRoomSetting(); - if (!Strings.isNullOrEmpty(roomPin)) { - adobeConnectRoomSetting.setPin(roomPin); + if (!Strings.isNullOrEmpty(userPin)) { + adobeConnectRoomSetting.setPin(userPin); } adobeConnectRoomSetting.setAccessMode(roomAccessMode); roomSpecification.addRoomSetting(adobeConnectRoomSetting); @@ -1092,7 +1091,6 @@ public boolean hasUserParticipant(String userId, ParticipantRole role) * Add new participant. * * @param participant - * @param bindingResult */ public boolean createParticipant(ParticipantModel participant, SecurityToken securityToken) { @@ -1106,7 +1104,6 @@ public boolean createParticipant(ParticipantModel participant, SecurityToken sec * * @param participantId * @param participant - * @param bindingResult */ public boolean modifyParticipant(String participantId, ParticipantModel participant, SecurityToken securityToken) { From b8e55a6d294fdfbe46165732a051215e9b30cec2 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sat, 14 May 2022 18:35:10 +0200 Subject: [PATCH 040/134] Removed complete path to ReservationRequest from ReservationRequestCreateModel --- .../reservationrequest/ReservationRequestCreateModel.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java index a7ea22869..e550c1111 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java @@ -586,8 +586,8 @@ public void fromApi(AbstractReservationRequest abstractReservationRequest, Cache // Date/time slot and periodicity Period duration = null; - if (abstractReservationRequest instanceof cz.cesnet.shongo.controller.api.ReservationRequest) { - cz.cesnet.shongo.controller.api.ReservationRequest reservationRequest = (cz.cesnet.shongo.controller.api.ReservationRequest) abstractReservationRequest; + if (abstractReservationRequest instanceof ReservationRequest) { + ReservationRequest reservationRequest = (ReservationRequest) abstractReservationRequest; periodicity.setType(PeriodicDateTimeSlot.PeriodicityType.NONE); Interval slot = reservationRequest.getSlot(); this.slot.setStart(slot.getStart()); @@ -1179,14 +1179,14 @@ public AbstractReservationRequest toApi() // Create reservation request AbstractReservationRequest abstractReservationRequest; if (specificationType == SpecificationType.VIRTUAL_ROOM) { - cz.cesnet.shongo.controller.api.ReservationRequest reservationRequest = new ReservationRequest(); + ReservationRequest reservationRequest = new ReservationRequest(); PeriodicDateTimeSlot slot = slots.first(); reservationRequest.setSlot(slot.getStart(), slot.getStart().plus(Duration.standardDays(730))); abstractReservationRequest = reservationRequest; } else if (periodicity.getType() == PeriodicDateTimeSlot.PeriodicityType.NONE) { // Create single reservation request - cz.cesnet.shongo.controller.api.ReservationRequest reservationRequest = new ReservationRequest(); + ReservationRequest reservationRequest = new ReservationRequest(); PeriodicDateTimeSlot slot = slots.first(); reservationRequest.setSlot(slot.getStart(), slot.getStart().plus(slot.getDuration())); abstractReservationRequest = reservationRequest; From 21e28cb84fddc965c17d4c4fc2cb4106d02ad423 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sun, 15 May 2022 22:00:57 +0200 Subject: [PATCH 041/134] Get virtual room data from reusedReservationRequest if available --- .../controllers/ReservationRequestController.java | 13 +++++++++++-- .../ReservationRequestDetailModel.java | 3 ++- .../reservationrequest/ReservationRequestModel.java | 3 ++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 6784b0661..2eba222b6 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -150,7 +150,8 @@ ListResponse listRequests( if (resource != null) { resourceSummary = cache.getResourceSummary(securityToken, resource); } - return new ReservationRequestModel(item, permissionsByReservationRequestId, user, resourceSummary); + VirtualRoomModel virtualRoom = new VirtualRoomModel(item); + return new ReservationRequestModel(item, virtualRoom, permissionsByReservationRequestId, user, resourceSummary); }).collect(Collectors.toList())); listResponse.setStart(response.getStart()); listResponse.setCount(response.getCount()); @@ -237,9 +238,17 @@ ReservationRequestDetailModel getRequest( if (resourceId != null) { resourceSummary = cacheProvider.getResourceSummary(resourceId); } + VirtualRoomModel virtualRoomData = new VirtualRoomModel(summary); + + // If the request is a ROOM_CAPACITY, then get virtual room data from the room + String virtualRoomId = summary.getReusedReservationRequestId(); + if (virtualRoomId != null) { + ReservationRequestSummary summaryVirtualRoom = cache.getReservationRequestSummary(securityToken, virtualRoomId); + virtualRoomData = new VirtualRoomModel(summaryVirtualRoom); + } return new ReservationRequestDetailModel( - summary, permissionsByReservationRequestId, ownerInformation, authorizedData, history, resourceSummary + summary, virtualRoomData, permissionsByReservationRequestId, ownerInformation, authorizedData, history, resourceSummary ); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java index bc6fdcd1c..1c28777e2 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java @@ -32,13 +32,14 @@ public class ReservationRequestDetailModel extends ReservationRequestModel public ReservationRequestDetailModel( ReservationRequestSummary summary, + VirtualRoomModel virtualRoom, Map> permissionsByReservationRequestId, UserInformation ownerInformation, RoomAuthorizedData authorizedData, List history, ResourceSummary resourceSummary) { - super(summary, permissionsByReservationRequestId, ownerInformation, resourceSummary); + super(summary, virtualRoom, permissionsByReservationRequestId, ownerInformation, resourceSummary); this.allocationState = summary.getAllocationState(); this.executableState = summary.getExecutableState(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java index 41858314f..91f0c256b 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java @@ -46,6 +46,7 @@ public class ReservationRequestModel public ReservationRequestModel( ReservationRequestSummary summary, + VirtualRoomModel virtualRoomData, Map> permissionsByReservationRequestId, UserInformation ownerInformation, ResourceSummary resourceSummary) @@ -59,7 +60,7 @@ public ReservationRequestModel( this.ownerEmail = ownerInformation.getEmail(); this.slot = TimeInterval.fromApi(summary.getEarliestSlot()); this.type = SpecificationType.fromReservationRequestSummary(summary, true); - this.virtualRoomData = new VirtualRoomModel(summary); + this.virtualRoomData = virtualRoomData; this.physicalResourceData = PhysicalResourceData.fromApi(resourceSummary); this.roomCapacityData = new RoomCapacityModel(summary); this.lastReservationId = summary.getLastReservationId(); From 695c3ae891751b96fb9647aa64a0c8c1dff458b4 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sun, 15 May 2022 23:16:51 +0200 Subject: [PATCH 042/134] Completed Javadoc, utilized lombok where possible --- .../cesnet/shongo/controller/rest/Cache.java | 3 +- .../shongo/controller/rest/ClientWebUrl.java | 17 +++- .../shongo/controller/rest/ErrorHandler.java | 5 +- .../rest/models/TechnologyModel.java | 19 +---- .../controller/rest/models/TimeInterval.java | 5 ++ .../models/participant/ParticipantModel.java | 79 +------------------ .../rest/models/report/MetaModel.java | 3 + .../ReservationRequestDetailModel.java | 4 +- .../ReservationRequestModel.java | 2 +- .../ReservationRequestState.java | 48 ++--------- .../reservationrequest/RoomCapacityModel.java | 6 +- .../reservationrequest/SpecificationType.java | 32 +------- .../reservationrequest/VirtualRoomModel.java | 5 ++ .../models/resource/ReservationModel.java | 5 ++ .../ResourceCapacityUtilizationModel.java | 23 ------ .../rest/models/resource/ResourceModel.java | 5 ++ .../ResourceUtilizationDetailModel.java | 5 ++ .../resource/ResourceUtilizationModel.java | 5 ++ .../controller/rest/models/resource/Unit.java | 5 ++ .../rest/models/roles/UserRoleModel.java | 2 - .../rest/models/room/RoomAuthorizedData.java | 2 +- .../rest/models/room/RoomModel.java | 2 +- .../rest/models/room/RoomState.java | 47 ++--------- .../rest/models/users/SettingsModel.java | 5 ++ 24 files changed, 92 insertions(+), 242 deletions(-) delete mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilizationModel.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java index f3601178c..562f09336 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java @@ -307,8 +307,7 @@ public synchronized Set getObjectPermissions(SecurityToken sec if (objectPermissions == null) { Map permissionsByObject = authorizationService.listObjectPermissions( new ObjectPermissionListRequest(securityToken, objectId)); - objectPermissions = new HashSet<>(); - objectPermissions.addAll(permissionsByObject.get(objectId).getObjectPermissions()); + objectPermissions = new HashSet<>(permissionsByObject.get(objectId).getObjectPermissions()); userState.objectPermissionsByObject.put(objectId, objectPermissions); } return objectPermissions; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java index 8e4843863..39720d3d8 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java @@ -1,5 +1,10 @@ package cz.cesnet.shongo.controller.rest; +/** + * Web URL paths for REST endpoints. + * + * @author Filip Karnis + */ public class ClientWebUrl { @@ -9,6 +14,7 @@ public class ClientWebUrl public static final String REPORT = API_PREFIX + "/report"; + // Users and groups public static final String USERS_AND_GROUPS = API_PREFIX; public static final String SETTINGS = "/settings"; public static final String USERS_LIST = "/users"; @@ -16,23 +22,32 @@ public class ClientWebUrl public static final String GROUPS_LIST = "/groups"; public static final String GROUPS_DETAIL = "/groups/{groupId:.+}"; + // Resources public static final String RESOURCES = API_PREFIX + "/resources"; public static final String CAPACITY_UTILIZATION = "/capacity_utilization"; public static final String CAPACITY_UTILIZATION_DETAIL = "/{id}/capacity_utilization"; + // Reservation requests public static final String RESERVATION_REQUESTS = API_PREFIX + "/reservation_requests"; public static final String RESERVATION_REQUESTS_ACCEPT = ID_SUFFIX + "/accept"; public static final String RESERVATION_REQUESTS_REJECT = ID_SUFFIX + "/reject"; public static final String RESERVATION_REQUESTS_REVERT = ID_SUFFIX + "/revert"; + // Participants public static final String PARTICIPANTS = API_PREFIX + "/reservation_requests/{id:.+}/participants"; public static final String PARTICIPANTS_ID_SUFFIX = "/{participantId:.+}"; + // Roles + public static final String ROLES = API_PREFIX + "/reservation_requests/{id:.+}/roles"; + + // Recordings public static final String RECORDINGS = API_PREFIX + "/reservation_requests/{id:.+}/recordings"; public static final String RECORDINGS_ID_SUFFIX = "/{recordingId:.+}"; + // Rooms public static final String ROOMS = API_PREFIX + "/rooms"; + // Runtime management public static final String RUNTIME_MANAGEMENT = API_PREFIX + "/reservation_requests/{id:.+}/runtime_management"; public static final String RUNTIME_MANAGEMENT_PARTICIPANTS = "/participants"; public static final String RUNTIME_MANAGEMENT_PARTICIPANTS_MODIFY = "/participants/{participantId:.+}"; @@ -40,6 +55,4 @@ public class ClientWebUrl public static final String RUNTIME_MANAGEMENT_PARTICIPANTS_SNAPSHOT = "/participants/{participantId:.+}/snapshot"; public static final String RUNTIME_MANAGEMENT_RECORDING_START = "/recording/start"; public static final String RUNTIME_MANAGEMENT_RECORDING_STOP = "/recording/stop"; - - public static final String ROLES = API_PREFIX + "/reservation_requests/{id:.+}/roles"; } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java index 715245163..f6633dce1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java @@ -4,7 +4,6 @@ import cz.cesnet.shongo.controller.ControllerConfiguration; import cz.cesnet.shongo.controller.EmailSender; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import javax.mail.MessagingException; import java.util.Collection; @@ -22,9 +21,9 @@ public class ErrorHandler private final Controller controller; private final ControllerConfiguration configuration; - public ErrorHandler(@Autowired Controller controller) + public ErrorHandler() { - this.controller = controller; + this.controller = Controller.getInstance(); this.configuration = controller.getConfiguration(); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TechnologyModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TechnologyModel.java index b793bbda7..22af5d013 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TechnologyModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TechnologyModel.java @@ -1,6 +1,7 @@ package cz.cesnet.shongo.controller.rest.models; import cz.cesnet.shongo.Technology; +import lombok.Getter; import java.util.Arrays; import java.util.Collections; @@ -11,7 +12,9 @@ * Technology of the alias/room reservation request or executable. * * @author Martin Srom + * @author Filip Karnis */ +@Getter public enum TechnologyModel { @@ -80,20 +83,4 @@ else if (PEXIP.technologies.containsAll(technologies)) { } return null; } - - /** - * @return {@link #titleCode} - */ - public String getTitleCode() - { - return titleCode; - } - - /** - * @return {@link #technologies} - */ - public Set getTechnologies() - { - return technologies; - } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java index 291a191e9..57e7a8d48 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/TimeInterval.java @@ -5,6 +5,11 @@ import org.joda.time.DateTime; import org.joda.time.Interval; +/** + * Represents {@link Interval} for REST API. + * + * @author Filip Karnis + */ @Data public class TimeInterval { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java index 53c81d86a..dd455d2bf 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java @@ -7,7 +7,7 @@ import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import cz.cesnet.shongo.controller.rest.models.CommonModel; -import lombok.NoArgsConstructor; +import lombok.Data; /** * Model for {@link AbstractParticipant}. @@ -15,11 +15,12 @@ * @author Martin Srom * @author Filip Karnis */ -@NoArgsConstructor +@Data public class ParticipantModel { protected String id; + private Type type; private String userId; @@ -103,11 +104,6 @@ private boolean isNew() return id == null || CommonModel.isNewId(id); } - public String getId() - { - return id; - } - public void setNewId() { this.id = CommonModel.getNewId(); @@ -118,75 +114,6 @@ public void setNullId() this.id = null; } - public Type getType() - { - return type; - } - - public void setType(Type type) - { - this.type = type; - } - - public String getUserId() - { - return userId; - } - - public void setUserId(String userId) - { - this.userId = userId; - } - - public String getName() - { - return name; - } - - public void setName(String name) - { - this.name = name; - } - - public String getEmail() - { - return email; - } - - public void setEmail(String email) - { - this.email = email; - } - - public String getOrganization() - { - return organization; - } - - public void setOrganization(String organization) - { - this.organization = organization; - } - - public ParticipantRole getRole() - { - return role; - } - - public void setRole(ParticipantRole role) - { - this.role = role; - } - - @Override - public String toString() - { - if (type == Type.USER) { - return String.format("UserParticipant(userId: %s, role: %s)", getUserId(), role); - } - return String.format("Participant(type: %s, role: %s)", type, role); - } - public enum Type { USER, diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/MetaModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/MetaModel.java index ca672d645..3bc30fb6a 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/MetaModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/report/MetaModel.java @@ -4,6 +4,9 @@ import lombok.Data; import org.joda.time.DateTimeZone; +/** + * Represents meta data for {@link ReportModel}. + */ @Data public class MetaModel { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java index 1c28777e2..59f837e47 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java @@ -16,6 +16,8 @@ /** * Represents reservation request's detail info. + * It contains additional information about reservation request + * including {@link RoomAuthorizedData} and {@link List} {@link ReservationRequestHistoryModel}. * * @author Filip Karnis */ @@ -26,7 +28,7 @@ public class ReservationRequestDetailModel extends ReservationRequestModel private AllocationState allocationState; private ExecutableState executableState; - private Boolean notifyParticipants; // TODO + private Boolean notifyParticipants; private RoomAuthorizedData authorizedData; private List history; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java index 91f0c256b..62121cd60 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java @@ -16,7 +16,7 @@ import static cz.cesnet.shongo.controller.rest.models.TimeInterval.ISO_8601_PATTERN; /** - * Represents reservation request. + * Represents {@link cz.cesnet.shongo.controller.api.AbstractReservationRequest}. * * @author Filip Karnis */ diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java index e6427eeb1..c86f0a50a 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestState.java @@ -4,15 +4,17 @@ import cz.cesnet.shongo.controller.api.ExecutableState; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; import cz.cesnet.shongo.controller.api.ReservationRequestType; -import org.springframework.context.MessageSource; - -import java.util.Locale; +import lombok.AllArgsConstructor; +import lombok.Getter; /** * Represents a reservation request state. * * @author Martin Srom + * @author Filip Karnis */ +@Getter +@AllArgsConstructor public enum ReservationRequestState { /** @@ -71,16 +73,6 @@ public enum ReservationRequestState */ private final boolean allocated; - /** - * Constructor. - * - * @param allocated sets the {@link #allocated} - */ - ReservationRequestState(boolean allocated) - { - this.allocated = allocated; - } - public static ReservationRequestState fromApi(ReservationRequestSummary reservationRequest) { return fromApi(reservationRequest.getAllocationState(), reservationRequest.getExecutableState(), @@ -156,34 +148,4 @@ public static ReservationRequestState fromApi(AllocationState allocationState, E return NOT_ALLOCATED; } } - - /** - * @return {@link #allocated} - */ - public boolean isAllocated() - { - return allocated; - } - - public String getMessage(MessageSource messageSource, Locale locale, SpecificationType specificationType) - { - return messageSource.getMessage( - "views.reservationRequest.state." + specificationType + "." + this, null, locale); - } - - public String getHelp(MessageSource messageSource, Locale locale, SpecificationType specificationType, - String reservationId) - { - String helpMessage = "views.reservationRequest.stateHelp." + specificationType + "." + this; - if (this.equals(FAILED) && reservationId != null) { - return messageSource.getMessage(helpMessage + ".hasReservation", null, locale); - } - return messageSource.getMessage(helpMessage, null, locale); - } - - public String getHelp(MessageSource messageSource, Locale locale, SpecificationType specificationType) - { - String helpMessageCode = "views.reservationRequest.stateHelp." + specificationType + "." + this; - return messageSource.getMessage(helpMessageCode, null, locale); - } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RoomCapacityModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RoomCapacityModel.java index 29a150d7b..a5b4894a9 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RoomCapacityModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/RoomCapacityModel.java @@ -3,6 +3,11 @@ import cz.cesnet.shongo.controller.api.ReservationRequestSummary; import lombok.Data; +/** + * Represents capacity information for {@link cz.cesnet.shongo.controller.api.RoomAvailability}. + * + * @author Filip Karnis + */ @Data public class RoomCapacityModel { @@ -19,6 +24,5 @@ public RoomCapacityModel(ReservationRequestSummary summary) this.capacityParticipantCount = summary.getRoomParticipantCount(); this.hasRoomRecordingService = summary.hasRoomRecordingService(); this.hasRoomRecordings = summary.hasRoomRecordings(); - // TODO this.isRecordingActive = summary.; } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java index 1911ed3f5..77c45d0f7 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java @@ -4,6 +4,8 @@ import cz.cesnet.shongo.controller.Controller; import cz.cesnet.shongo.controller.ControllerConfiguration; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import lombok.AllArgsConstructor; +import lombok.Getter; /** * Type of specification for a reservation request. @@ -11,6 +13,8 @@ * @author Martin Srom * @author Filip Karnis */ +@Getter +@AllArgsConstructor public enum SpecificationType { /** @@ -53,18 +57,6 @@ public enum SpecificationType */ private final boolean isPhysical; - /** - * Constructor. - * - * @param isRoom sets the {@link #isRoom} - * @param isPhysical - */ - SpecificationType(boolean isRoom, boolean isPhysical) - { - this.isRoom = isRoom; - this.isPhysical = isPhysical; - } - /** * @param reservationRequestSummary * @return {@link SpecificationType} from given {@code reservationRequestSummary} @@ -140,20 +132,4 @@ private static ControllerConfiguration getControllerConfiguration() Controller controller = Controller.getInstance(); return controller.getConfiguration(); } - - /** - * @return {@link #isRoom} - */ - public boolean isRoom() - { - return isRoom; - } - - /** - * @return {@link #isPhysical} - */ - public boolean isPhysical() - { - return isPhysical; - } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/VirtualRoomModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/VirtualRoomModel.java index e5b2df822..b65d7e80f 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/VirtualRoomModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/VirtualRoomModel.java @@ -8,6 +8,11 @@ import java.util.Set; +/** + * Represents data about virtual room of {@link cz.cesnet.shongo.controller.api.ReservationRequestSummary}. + * + * @author Filip Karnis + */ @Data public class VirtualRoomModel { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ReservationModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ReservationModel.java index e1c4fbe20..9bbd5c7d2 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ReservationModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ReservationModel.java @@ -5,6 +5,11 @@ import cz.cesnet.shongo.controller.rest.models.TimeInterval; import lombok.Data; +/** + * Represents a {@link ReservationSummary}. + * + * @author Filip Karnis + */ @Data public class ReservationModel { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilizationModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilizationModel.java deleted file mode 100644 index 8ad302f6f..000000000 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilizationModel.java +++ /dev/null @@ -1,23 +0,0 @@ -package cz.cesnet.shongo.controller.rest.models.resource; - -import lombok.Data; -import org.joda.time.Interval; - -import java.util.Collection; -import java.util.Map; - -@Data -public class ResourceCapacityUtilizationModel -{ - - private Collection resourceCapacitySet; - private Map> resourceCapacityUtilization; - - public ResourceCapacityUtilizationModel( - Collection resourceCapacitySet, - Map> resourceCapacityUtilization) - { - this.resourceCapacitySet = resourceCapacitySet; - this.resourceCapacityUtilization = resourceCapacityUtilization; - } -} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java index c4bf8b0f8..dde0caf89 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java @@ -6,6 +6,11 @@ import java.util.Set; +/** + * Represents {@link ResourceSummary}. + * + * @author Filip Karnis + */ @Data public class ResourceModel { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationDetailModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationDetailModel.java index 4af31d091..c850693bd 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationDetailModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationDetailModel.java @@ -4,6 +4,11 @@ import java.util.List; +/** + * Represents a utilization of {@link ResourceCapacity} in a specific interval. + * + * @author Filip Karnis + */ @Data public class ResourceUtilizationDetailModel { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java index 1d35db5fa..0e56b9797 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java @@ -8,6 +8,11 @@ import java.util.List; import java.util.Map; +/** + * Represents a utilization of all {@link ResourceCapacity} in a specific interval. + * + * @author Filip Karnis + */ @Data public class ResourceUtilizationModel { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/Unit.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/Unit.java index d0a66d877..2393acc12 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/Unit.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/Unit.java @@ -4,6 +4,11 @@ import lombok.Getter; import org.joda.time.Period; +/** + * Represents a unit of time to use as a period for utilization of {@link ResourceCapacity}. + * + * @author Filip Karnis + */ @Getter @AllArgsConstructor public enum Unit diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/roles/UserRoleModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/roles/UserRoleModel.java index 4f5bdfc4c..2697f649e 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/roles/UserRoleModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/roles/UserRoleModel.java @@ -11,7 +11,6 @@ import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.models.CommonModel; import lombok.Data; -import lombok.NoArgsConstructor; /** * Represents entity's role for specified resource. @@ -20,7 +19,6 @@ * @author Filip Karnis */ @Data -@NoArgsConstructor public class UserRoleModel { private String id; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomAuthorizedData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomAuthorizedData.java index 81132399c..01a2af7f1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomAuthorizedData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomAuthorizedData.java @@ -7,7 +7,7 @@ import java.util.List; /** - * Represents authorized data for room (executable). + * Represents authorized data for {@link AbstractRoomExecutable}. * * @author Filip Karnis */ diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomModel.java index aeb048e5d..e5801cd3e 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomModel.java @@ -7,7 +7,7 @@ import org.joda.time.Interval; /** - * Represents room (executable). + * Represents {@link ExecutableSummary}. * * @author Filip Karnis */ diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomState.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomState.java index c52a65b16..1a5361ef1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomState.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/room/RoomState.java @@ -2,15 +2,17 @@ import cz.cesnet.shongo.TodoImplementException; import cz.cesnet.shongo.controller.api.ExecutableState; -import org.springframework.context.MessageSource; - -import java.util.Locale; +import lombok.AllArgsConstructor; +import lombok.Getter; /** * Represents a room state. * * @author Martin Srom + * @author Filip Karnis */ +@Getter +@AllArgsConstructor public enum RoomState { /** @@ -53,17 +55,6 @@ public enum RoomState */ private final boolean isAvailable; - /** - * Constructor. - * - * @param isAvailable sets the {@link #isAvailable} - */ - RoomState(boolean isStarted, boolean isAvailable) - { - this.isStarted = isStarted; - this.isAvailable = isAvailable; - } - /** * @param roomState * @param roomLicenseCount @@ -109,32 +100,4 @@ public static RoomState fromRoomState(ExecutableState roomState) { return fromRoomState(roomState, 1, null); } - - /** - * @return {@link #isStarted} - */ - public boolean isStarted() - { - return isStarted; - } - - /** - * @return {@link #isAvailable} - */ - public boolean isAvailable() - { - return isAvailable; - } - - public String getMessage(MessageSource messageSource, Locale locale, RoomType roomType) - { - return messageSource.getMessage( - "views.executable.roomState." + roomType + "." + this, null, locale); - } - - public String getHelp(MessageSource messageSource, Locale locale, RoomType roomType) - { - return messageSource.getMessage( - "views.executable.roomStateHelp." + roomType + "." + this, null, locale); - } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/users/SettingsModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/users/SettingsModel.java index 7b3093477..f78e7415a 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/users/SettingsModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/users/SettingsModel.java @@ -9,6 +9,11 @@ import java.util.List; import java.util.Locale; +/** + * Represents {@link UserSettings}. + * + * @author Filip Karnis + */ @Data @NoArgsConstructor public class SettingsModel From 5162e6217912839988209f993125626a8127aea9 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sun, 15 May 2022 23:27:42 +0200 Subject: [PATCH 043/134] Added RecordingModel --- .../rest/controllers/RecordingController.java | 37 ++----------- .../rest/models/recording/RecordingModel.java | 54 +++++++++++++++++++ 2 files changed, 59 insertions(+), 32 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/recording/RecordingModel.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java index 662bf0ab9..d3625a3e1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java @@ -8,16 +8,14 @@ import cz.cesnet.shongo.controller.api.rpc.ResourceControlService; import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.ClientWebUrl; +import cz.cesnet.shongo.controller.rest.models.recording.RecordingModel; import cz.cesnet.shongo.controller.scheduler.SchedulerReportSet; import io.swagger.v3.oas.annotations.Operation; -import org.joda.time.Duration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; -import java.util.HashMap; -import java.util.LinkedList; import java.util.List; -import java.util.Map; +import java.util.stream.Collectors; import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; @@ -47,7 +45,7 @@ public RecordingController( @Operation(summary = "Lists reservation request recordings.") @GetMapping - Map listRequestRecordings( + ListResponse listRequestRecordings( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @RequestParam(value = "start", required = false) Integer start, @@ -70,34 +68,9 @@ Map listRequestRecordings( request.setSortDescending(sortDescending); ListResponse response = executableService.listExecutableRecordings(request); - List items = new LinkedList<>(); - for (ResourceRecording recording : response.getItems()) { - Map item = new HashMap<>(); - item.put("id", recording.getId()); - item.put("resourceId", recording.getResourceId()); - item.put("name", recording.getName()); - item.put("description", recording.getDescription()); - item.put("beginDate", recording.getBeginDate()); - Duration duration = recording.getDuration(); - if (duration == null || duration.isShorterThan(Duration.standardMinutes(1))) { - item.put("duration", null); - } - else { - item.put("duration", duration.toPeriod()); - } - item.put("isPublic", recording.isPublic()); - item.put("downloadUrl", recording.getDownloadUrl()); - item.put("viewUrl", recording.getViewUrl()); - item.put("editUrl", recording.getEditUrl()); - item.put("filename", recording.getFileName()); - items.add(item); - } - Map data = new HashMap<>(); - data.put("start", response.getStart()); - data.put("count", response.getCount()); - data.put("items", items); - return data; + List items = response.getItems().stream().map(RecordingModel::new).collect(Collectors.toList()); + return ListResponse.fromRequest(response.getStart(), response.getCount(), items); } @Operation(summary = "Deletes recording from reservation request.") diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/recording/RecordingModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/recording/RecordingModel.java new file mode 100644 index 000000000..ea5eff3d4 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/recording/RecordingModel.java @@ -0,0 +1,54 @@ +package cz.cesnet.shongo.controller.rest.models.recording; + +import com.fasterxml.jackson.annotation.JsonFormat; +import cz.cesnet.shongo.controller.api.ResourceRecording; +import lombok.Data; +import org.joda.time.DateTime; +import org.joda.time.Duration; +import org.joda.time.Period; + +import static cz.cesnet.shongo.controller.rest.models.TimeInterval.ISO_8601_PATTERN; + +/** + * Represents a recording. + * + * @author Filip Karnis + */ +@Data +public class RecordingModel +{ + + private String id; + private String name; + private String description; + private String resourceId; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = ISO_8601_PATTERN) + private DateTime beginDate; + private Period duration; + private Boolean isPublic; + private String downloadUrl; + private String viewUrl; + private String editUrl; + private String filename; + + public RecordingModel(ResourceRecording recording) + { + this.id = recording.getId(); + this.name = recording.getName(); + this.description = recording.getDescription(); + this.resourceId = recording.getResourceId(); + this.beginDate = recording.getBeginDate(); + Duration duration = recording.getDuration(); + if (duration == null || duration.isShorterThan(Duration.standardMinutes(1))) { + this.duration = null; + } + else { + this.duration = duration.toPeriod(); + } + this.isPublic = recording.isPublic(); + this.downloadUrl = recording.getDownloadUrl(); + this.viewUrl = recording.getViewUrl(); + this.editUrl = recording.getEditUrl(); + this.filename = recording.getFileName(); + } +} From d10a19201993eedd1266b7c6ecad30029887d1c3 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Mon, 16 May 2022 00:07:25 +0200 Subject: [PATCH 044/134] Added RuntimeParticipantModel --- .../rest/controllers/RuntimeController.java | 60 ++++++------------- .../RuntimeParticipantModel.java | 51 ++++++++++++++++ 2 files changed, 68 insertions(+), 43 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/runtimemanagement/RuntimeParticipantModel.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java index 08dfbb8c4..02efbb98b 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java @@ -1,9 +1,7 @@ package cz.cesnet.shongo.controller.rest.controllers; -import cz.cesnet.shongo.api.Alias; import cz.cesnet.shongo.api.MediaData; import cz.cesnet.shongo.api.RoomParticipant; -import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.api.ExecutionReport; import cz.cesnet.shongo.controller.api.SecurityToken; import cz.cesnet.shongo.controller.api.request.ListResponse; @@ -12,6 +10,7 @@ import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.ClientWebUrl; import cz.cesnet.shongo.controller.rest.RoomCache; +import cz.cesnet.shongo.controller.rest.models.runtimemanagement.RuntimeParticipantModel; import io.swagger.v3.oas.annotations.Operation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -22,6 +21,7 @@ import org.springframework.web.bind.annotation.*; import java.util.*; +import java.util.stream.Collectors; import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; @@ -52,13 +52,11 @@ public RuntimeController( @Operation(summary = "Lists reservation request runtime participants.") @GetMapping(ClientWebUrl.RUNTIME_MANAGEMENT_PARTICIPANTS) - Map listRuntimeParticipants( + ListResponse listRuntimeParticipants( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, - @RequestParam(value = "start", required = false) Integer start, - @RequestParam(value = "count", required = false) Integer count, - @RequestParam(value = "sort", required = false, defaultValue = "DATETIME") String sort, - @RequestParam(value = "sort-desc", required = false, defaultValue = "true") boolean sortDescending) + @RequestParam(required = false) Integer start, + @RequestParam(required = false) Integer count) { String executableId = cache.getExecutableId(securityToken, id); CacheProvider cacheProvider = new CacheProvider(cache, securityToken); @@ -69,33 +67,12 @@ Map listRuntimeParticipants( catch (Exception exception) { log.warn("Failed to load participants", exception); } - ListResponse response = ListResponse.fromRequest(start, count, roomParticipants); - List items = new LinkedList<>(); - for (RoomParticipant roomParticipant : response.getItems()) { - UserInformation user = null; - String userId = roomParticipant.getUserId(); - if (userId != null) { - user = cacheProvider.getUserInformation(userId); - } - Alias alias = roomParticipant.getAlias(); - Map item = new HashMap<>(); - item.put("id", roomParticipant.getId()); - item.put("name", (user != null ? user.getFullName() : roomParticipant.getDisplayName())); - item.put("alias", (alias != null ? alias.getValue() : null)); - item.put("role", roomParticipant.getRole()); - item.put("email", (user != null ? user.getPrimaryEmail() : null)); - item.put("layout", roomParticipant.getLayout()); - item.put("microphoneEnabled", roomParticipant.getMicrophoneEnabled()); - item.put("microphoneLevel", roomParticipant.getMicrophoneLevel()); - item.put("videoEnabled", roomParticipant.getVideoEnabled()); - item.put("videoSnapshot", roomParticipant.isVideoSnapshot()); - items.add(item); - } - Map data = new HashMap<>(); - data.put("start", response.getStart()); - data.put("count", response.getCount()); - data.put("items", items); - return data; + + List items = roomParticipants + .stream() + .map(roomParticipant -> new RuntimeParticipantModel(roomParticipant, cacheProvider)) + .collect(Collectors.toList()); + return ListResponse.fromRequest(start, count, items); } @Operation(summary = "Takes snapshot of reservation request runtime participant.") @@ -129,7 +106,7 @@ void modifyRuntimeParticipant( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @PathVariable String participantId, - @RequestBody Map body) + @RequestBody RuntimeParticipantModel body) { String executableId = cache.getExecutableId(securityToken, id); RoomParticipant oldRoomParticipant = null; @@ -138,10 +115,10 @@ void modifyRuntimeParticipant( } // Parse body - String name = (String) body.get("name"); - Boolean microphoneEnabled = (Boolean) body.get("microphoneEnabled"); - Integer microphoneLevel = (Integer) body.get("microphoneLevel"); - Boolean videoEnabled = (Boolean) body.get("videoEnabled"); + String name = body.getName(); + Boolean microphoneEnabled = body.getMicrophoneEnabled(); + Integer microphoneLevel = body.getMicrophoneLevel(); + Boolean videoEnabled = body.getVideoEnabled(); RoomParticipant roomParticipant = new RoomParticipant(participantId); if (name != null) { @@ -201,11 +178,9 @@ void startRequestRecording( cache.clearExecutable(executableId); } else { -// Locale locale = userSession.getLocale(); String errorCode = "startingFailed"; if (result instanceof ExecutionReport) { ExecutionReport executionReport = (ExecutionReport) result; -// log.warn("Start recording failed: {}", executionReport.toString(locale, userSession.getTimeZone())); log.warn("Start recording failed: {}", executionReport); // Detect further error @@ -237,10 +212,9 @@ void stopRequestRecording( cache.clearExecutable(executableId); } else { -// Locale locale = userSession.getLocale(); if (result instanceof ExecutionReport) { ExecutionReport executionReport = (ExecutionReport) result; -// log.warn("Stop recording failed: {}", executionReport.toString(locale, userSession.getTimeZone())); + log.warn("Stop recording failed: {}", executionReport); } } cache.clearExecutable(executableId); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/runtimemanagement/RuntimeParticipantModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/runtimemanagement/RuntimeParticipantModel.java new file mode 100644 index 000000000..f935a31bc --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/runtimemanagement/RuntimeParticipantModel.java @@ -0,0 +1,51 @@ +package cz.cesnet.shongo.controller.rest.models.runtimemanagement; + +import cz.cesnet.shongo.ParticipantRole; +import cz.cesnet.shongo.api.Alias; +import cz.cesnet.shongo.api.RoomLayout; +import cz.cesnet.shongo.api.RoomParticipant; +import cz.cesnet.shongo.api.UserInformation; +import cz.cesnet.shongo.controller.rest.CacheProvider; +import lombok.Data; + +/** + * Represents a {@link RoomParticipant}. + * + * @author Filip Karnis + */ +@Data +public class RuntimeParticipantModel +{ + + private String id; + private String name; + private String email; + private String alias; + private ParticipantRole role; + private RoomLayout layout; + private Boolean microphoneEnabled; + private Integer microphoneLevel; + private Boolean videoEnabled; + private Boolean videoSnapshot; + + public RuntimeParticipantModel(RoomParticipant roomParticipant, CacheProvider cacheProvider) + { + UserInformation user = null; + String userId = roomParticipant.getUserId(); + if (userId != null) { + user = cacheProvider.getUserInformation(userId); + } + + this.id = roomParticipant.getId(); + this.name = (user != null ? user.getFullName() : roomParticipant.getDisplayName()); + this.email = (user != null ? user.getPrimaryEmail() : null); + Alias alias = roomParticipant.getAlias(); + this.alias = (alias != null ? alias.getValue() : null); + this.role = roomParticipant.getRole(); + this.layout = roomParticipant.getLayout(); + this.microphoneEnabled = roomParticipant.getMicrophoneEnabled(); + this.microphoneLevel = roomParticipant.getMicrophoneLevel(); + this.videoEnabled = roomParticipant.getVideoEnabled(); + this.videoSnapshot = roomParticipant.isVideoSnapshot(); + } +} From 60dd272fd19aa21418f047e6b95d5f061740a500 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 18 May 2022 15:04:02 +0200 Subject: [PATCH 045/134] Set SSL keystore type as optional --- .../cesnet/shongo/controller/rest/RESTApiServer.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java index 1ff2dfaa7..a17508399 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java @@ -81,7 +81,8 @@ private static ServerConnector createHTTPConnector( http_config.setSecurePort(configuration.getRESTApiPort()); ServerConnector serverConnector; - if (configuration.hasRESTApiPKI()) { + final String sslKeyStore = configuration.getRESTApiSslKeyStore(); + if (sslKeyStore != null) { http_config.setSecureScheme(HttpScheme.HTTPS.asString()); final HttpConfiguration https_config = new HttpConfiguration(http_config); @@ -89,9 +90,12 @@ private static ServerConnector createHTTPConnector( final SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(configuration.getRESTApiSslKeyStore()); - sslContextFactory.setKeyStoreType(configuration.getRESTApiSslKeyStoreType()); + sslContextFactory.setKeyStorePath(sslKeyStore); sslContextFactory.setKeyStorePassword(configuration.getRESTApiSslKeyStorePassword()); + String keystoreType = configuration.getRESTApiSslKeyStoreType(); + if (!Strings.isNullOrEmpty(keystoreType)) { + sslContextFactory.setKeyStoreType(configuration.getRESTApiSslKeyStoreType()); + } if (configuration.isInterDomainConfigured()) { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); From 5221449bb8220988192e690d67f21938e97b3392 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 18 May 2022 15:57:51 +0200 Subject: [PATCH 046/134] Fixed getRpcUrl() method to deal with default rpc parameters --- .../controller/ControllerConfiguration.java | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java index eb0c51e98..2d42f1580 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java @@ -16,7 +16,10 @@ import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; import java.util.regex.MatchResult; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -275,7 +278,6 @@ public Period getPeriod(String key) } /** - * @return timeout to receive response when performing commands from agent */ public Duration getJadeCommandTimeout() @@ -311,15 +313,21 @@ public int getRpcPort() /** * @return XML-RPC url */ - public URL getRpcUrl() throws MalformedURLException { + public URL getRpcUrl() throws MalformedURLException + { int rpcPort; try { rpcPort = getRpcPort(); - } catch (NoSuchElementException e) { + } + catch (NoSuchElementException e) { rpcPort = 8181; } - String urlString = String.format("http://%s:%d", getRpcHost(false), rpcPort); + String scheme = (getRpcSslKeyStore() != null) ? "https" : "http"; + String rpcHost = getRpcHost(true); + String urlString = (rpcHost != null) + ? String.format("%s://%s:%d", scheme, rpcHost, rpcPort) + : String.format("%s://%s:%d", scheme, getRESTApiHost(), rpcPort); return new URL(urlString); } @@ -550,22 +558,21 @@ public String getRESTApiSslKeyStore() return sslKeyStore; } - public String getRESTApiSslKeyStoreType() { + public String getRESTApiSslKeyStoreType() + { return getString(ControllerConfiguration.REST_API_SSL_KEY_STORE_TYPE); } - public String getRESTApiSslKeyStorePassword() { + public String getRESTApiSslKeyStorePassword() + { return getString(ControllerConfiguration.REST_API_SSL_KEY_STORE_PASSWORD); } public boolean hasRESTApiPKI() { - if (Strings.isNullOrEmpty(getRESTApiSslKeyStore()) - || Strings.isNullOrEmpty(getRESTApiSslKeyStoreType()) - || Strings.isNullOrEmpty(getRESTApiSslKeyStorePassword())) { - return false; - } - return true; + return !Strings.isNullOrEmpty(getRESTApiSslKeyStore()) + && !Strings.isNullOrEmpty(getRESTApiSslKeyStoreType()) + && !Strings.isNullOrEmpty(getRESTApiSslKeyStorePassword()); } /** @@ -626,21 +633,24 @@ public String getCalDAVEncodedBasicAuth() /** * @return name of tag for meeting rooms */ - public String getMeetingRoomTagName() { + public String getMeetingRoomTagName() + { return getString(MEETING_ROOM_TAG); } /** * @return name of tag for cars */ - public String getVehicleTagName() { + public String getVehicleTagName() + { return getString(VEHICLE_TAG); } /** * @return name of tag for parking places */ - public String getParkingPlaceTagName() { + public String getParkingPlaceTagName() + { return getString(PARKING_PLACE_TAG); } } From 8dca09307db18fb7a620f1b2668ff09631bd0adc Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Mon, 3 Oct 2022 20:30:33 +0200 Subject: [PATCH 047/134] Added no args constructor to ParticipantModel, UserRoleModel and RuntimeParticipantModel --- .../controller/rest/models/participant/ParticipantModel.java | 2 ++ .../shongo/controller/rest/models/roles/UserRoleModel.java | 2 ++ .../rest/models/runtimemanagement/RuntimeParticipantModel.java | 2 ++ 3 files changed, 6 insertions(+) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java index dd455d2bf..34bb075c4 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java @@ -8,6 +8,7 @@ import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import cz.cesnet.shongo.controller.rest.models.CommonModel; import lombok.Data; +import lombok.NoArgsConstructor; /** * Model for {@link AbstractParticipant}. @@ -16,6 +17,7 @@ * @author Filip Karnis */ @Data +@NoArgsConstructor public class ParticipantModel { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/roles/UserRoleModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/roles/UserRoleModel.java index 2697f649e..4f5bdfc4c 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/roles/UserRoleModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/roles/UserRoleModel.java @@ -11,6 +11,7 @@ import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.models.CommonModel; import lombok.Data; +import lombok.NoArgsConstructor; /** * Represents entity's role for specified resource. @@ -19,6 +20,7 @@ * @author Filip Karnis */ @Data +@NoArgsConstructor public class UserRoleModel { private String id; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/runtimemanagement/RuntimeParticipantModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/runtimemanagement/RuntimeParticipantModel.java index f935a31bc..e49bad420 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/runtimemanagement/RuntimeParticipantModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/runtimemanagement/RuntimeParticipantModel.java @@ -7,6 +7,7 @@ import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.rest.CacheProvider; import lombok.Data; +import lombok.NoArgsConstructor; /** * Represents a {@link RoomParticipant}. @@ -14,6 +15,7 @@ * @author Filip Karnis */ @Data +@NoArgsConstructor public class RuntimeParticipantModel { From a8efff2cd03cdeb9fc8f8b11ac426ffc7451c72a Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 18 May 2022 15:04:42 +0200 Subject: [PATCH 048/134] Accept new server authorization response format --- .../authorization/Authorization.java | 1 + .../authorization/ServerAuthorization.java | 38 +++++++++++++------ .../authorization/UserAuthorizationData.java | 10 +++++ .../rest/controllers/UserController.java | 2 + 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java index 1a4a17b0e..f5b1d92c2 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java @@ -702,6 +702,7 @@ public UserSessionSettings getUserSessionSettings(SecurityToken securityToken) /** * @param userSessionSettings to be updated */ + // TODO toto sa zavola public void updateUserSessionSettings(UserSessionSettings userSessionSettings) { SecurityToken securityToken = userSessionSettings.getSecurityToken(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java index 5992f9169..dc06aae7b 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java @@ -33,6 +33,7 @@ import java.lang.reflect.Method; import java.math.BigInteger; import java.security.SecureRandom; +import java.text.SimpleDateFormat; import java.util.*; /** @@ -993,10 +994,10 @@ private T handleAuthorizationRequestError(Exception exception) private static UserData createUserDataFromWebServiceData(JsonNode data) { // Required fields - if (!data.has("id")) { + if (!data.has("sub")) { throw new IllegalArgumentException("User data must contain identifier."); } - if (!data.has("first_name") || !data.has("last_name")) { + if (!data.has("given_name") || !data.has("family_name")) { throw new IllegalArgumentException("User data must contain given and family name."); } @@ -1004,23 +1005,23 @@ private static UserData createUserDataFromWebServiceData(JsonNode data) // Common user data UserInformation userInformation = userData.getUserInformation(); - userInformation.setUserId(data.get("id").asText()); - userInformation.setFirstName(data.get("first_name").asText()); - userInformation.setLastName(data.get("last_name").asText()); + userInformation.setUserId(data.get("sub").asText()); + userInformation.setFirstName(data.get("given_name").asText()); + userInformation.setLastName(data.get("family_name").asText()); if (data.has("organization")) { JsonNode organization = data.get("organization"); if (!organization.isNull()) { userInformation.setOrganization(organization.asText()); } } - if (data.has("mail")) { - JsonNode email = data.get("mail"); + if (data.has("email")) { + JsonNode email = data.get("email"); if (!email.isNull()) { userInformation.setEmail(email.asText()); } } - if (data.has("principal_names")) { - Iterator principalNameIterator = data.get("principal_names").elements(); + if (data.has("voperson_external_id")) { + Iterator principalNameIterator = data.get("voperson_external_id").elements(); while (principalNameIterator.hasNext()) { JsonNode principalName = principalNameIterator.next(); userInformation.addPrincipalName(principalName.asText()); @@ -1028,8 +1029,8 @@ private static UserData createUserDataFromWebServiceData(JsonNode data) } // Additional user data - if (data.has("language")) { - JsonNode language = data.get("language"); + if (data.has("locale")) { + JsonNode language = data.get("locale"); if (!language.isNull()) { Locale locale = new Locale(language.asText()); userData.setLocale(locale); @@ -1061,6 +1062,21 @@ private static UserData createUserDataFromWebServiceData(JsonNode data) } } + // for OpenID Connect + if (data.has("isCesnetEligibleLastSeen")) { + DateTime dateTime; + ObjectMapper mapper = new ObjectMapper(); + mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss")); + try { + String date = data.get("isCesnetEligibleLastSeen").asText(); + dateTime = new DateTime(mapper.readValue("\"" + date + "\"", Date.class)); + } + catch (IOException e) { + throw new RuntimeException(e); + } + userData.setUserAuthorizationData( + new UserAuthorizationData(UserAuthorizationData.getLoaFromDate(dateTime))); + } // for AuthN Server v0.6.4 and newer if (data.has("authn_provider") && data.has("authn_instant") && data.has("loa")) { long instant = Long.valueOf(data.get("authn_instant").asText()) * 1000; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/UserAuthorizationData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/UserAuthorizationData.java index 53cdec1f4..add3b74ff 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/UserAuthorizationData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/UserAuthorizationData.java @@ -82,4 +82,14 @@ public int getLoa() { return loa; } + + public static int getLoaFromDate(DateTime dateTime) { + if (dateTime.isAfter(DateTime.now().minusYears(1))) { + return LOA_EXTENDED; + } + if (dateTime.isAfter(DateTime.now().minusYears(2))) { + return LOA_BASIC; + } + return LOA_NONE; + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java index 336d7a97c..8c8c371db 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java @@ -87,6 +87,8 @@ public UserInformation getUser( public SettingsModel getUserSettings(@RequestAttribute(TOKEN) SecurityToken securityToken) { UserSettings settings = authorizationService.getUserSettings(securityToken); + // TODO: Find a better place to authorize the user as an administrator + authorizationService.updateUserSettings(securityToken, settings); List permissions = cache.getSystemPermissions(securityToken); return new SettingsModel(settings, permissions); } From da0d99d53aca6dd5d3fdcdca72749b12a1064e85 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 19 Oct 2022 19:22:59 +0200 Subject: [PATCH 049/134] Fixed getting room capacities filtered by resource id --- .../ReservationRequestController.java | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 2eba222b6..d896417e9 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -88,6 +88,32 @@ ListResponse listRequests( @RequestParam(value = "type", required = false) Set reservationTypes, @RequestParam(value = "resource", required = false) String resourceId) { + if (reservationTypes == null) { + reservationTypes = new HashSet<>(); + } + + // room capacity does not have resource_id + // filter VIRTUAL_ROOMS by resource_id and then call recursively with parentRequestId + if (reservationTypes.contains(ReservationType.ROOM_CAPACITY) && resourceId != null) { + Set virtualRoomReservationTypes = new HashSet<>(List.of(ReservationType.VIRTUAL_ROOM)); + ListResponse response = listRequests(securityToken, start, count, sort, + sortDescending, allocationState, permanentRoomId, technology, intervalFrom, intervalTo, userId, + participantUserId, search, virtualRoomReservationTypes, resourceId); + + DateTime finalIntervalFrom = intervalFrom; + DateTime finalIntervalTo = intervalTo; + Set capacityReservationTypes = new HashSet<>(List.of(ReservationType.ROOM_CAPACITY)); + List capacities = response.getItems() + .stream() + .map(room -> listRequests(securityToken, start, count, sort, sortDescending, allocationState, + room.getId(), technology, finalIntervalFrom, finalIntervalTo, userId, participantUserId, + search, capacityReservationTypes, null).getItems()) + .flatMap(List::stream) + .collect(Collectors.toList()); + + return ListResponse.fromRequest(start, count, capacities); + } + ReservationRequestListRequest request = new ReservationRequestListRequest(); request.setSecurityToken(securityToken); @@ -100,9 +126,6 @@ ListResponse listRequests( request.setSearch(search); request.setSpecificationResourceId(resourceId); - if (reservationTypes == null) { - reservationTypes = new HashSet<>(); - } if (permanentRoomId != null) { request.setReusedReservationRequestId(permanentRoomId); reservationTypes.add(ReservationType.ROOM_CAPACITY); From cbb7386dd0151871f12e99936aa641984e7e266e Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 19 Oct 2022 19:41:21 +0200 Subject: [PATCH 050/134] Modified shongo-deployment scripts to not use client-web --- shongo-deployment/README.md | 5 +- shongo-deployment/bin/shongo-check.sh | 11 +--- .../bin/shongo-client-web-logins.sh | 51 ------------------ .../service/shongo-client-web.sh | 7 --- shongo-deployment/service/shongo-install.sh | 53 ------------------- shongo-deployment/service/shongo-uninstall.sh | 6 --- shongo-deployment/service/shongo.sh | 3 -- 7 files changed, 3 insertions(+), 133 deletions(-) delete mode 100755 shongo-deployment/bin/shongo-client-web-logins.sh delete mode 100755 shongo-deployment/service/shongo-client-web.sh diff --git a/shongo-deployment/README.md b/shongo-deployment/README.md index 8848d0128..7838cd359 100644 --- a/shongo-deployment/README.md +++ b/shongo-deployment/README.md @@ -6,7 +6,7 @@ This directory can contain runtime files for Shongo components. To install Shongo use the following command: - service/shongo-install.sh shongo-controller shongo-connector shongo-client-web + service/shongo-install.sh shongo-controller shongo-connector To uninstall Shongo use the following command: @@ -17,7 +17,6 @@ To uninstall Shongo use the following command: Create the following configuration files to configure Shongo components: * shongo-client-cli.cfg.xml to configure Command-Line Client -* shongo-client-web.cfg.xml to configure Web Interface * shongo-client-connector.cfg.xml to configure connectors * shongo-client-connector.auth.xml to configure authentication of connectors * shongo-client-controller.auth.xml to configure Controller @@ -30,4 +29,4 @@ Use the following command to run Command-Line Client: Use the following command to check status of Shongo component (useful for Nagios NRPE plugin): - bin/shongo-check.sh [shongo-connector|shongo-controller|shongo-client-web] + bin/shongo-check.sh [shongo-connector|shongo-controller] diff --git a/shongo-deployment/bin/shongo-check.sh b/shongo-deployment/bin/shongo-check.sh index ca215a27b..c568c41cd 100755 --- a/shongo-deployment/bin/shongo-check.sh +++ b/shongo-deployment/bin/shongo-check.sh @@ -2,7 +2,7 @@ # # Check Shongo applications. Can be used in nagios NRPE plugin. # -# check_shongo.sh +# check_shongo.sh # BIN=$(dirname $0) @@ -65,12 +65,6 @@ function check_connector fi } -function check_client_web -{ - echo TODO: check client web - exit 3 -} - OPTIND=0 while getopts "c:" option; do case $option in @@ -85,9 +79,6 @@ case $1 in shongo-connector) check_connector $2 ;; - shongo-client-web) - check_client_web - ;; *) check_controller ;; diff --git a/shongo-deployment/bin/shongo-client-web-logins.sh b/shongo-deployment/bin/shongo-client-web-logins.sh deleted file mode 100755 index 6c466a96f..000000000 --- a/shongo-deployment/bin/shongo-client-web-logins.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -# Parse arguments -while test $# -gt 0 -do - case "$1" in - --unique) - UNIQUE=1 - ;; - --help) - cat < $TMP_FILE - -# Show requested results -if [[ $UNIQUE ]]; then - cat $TMP_FILE | sort | uniq -else - cat $TMP_FILE -fi -rm $TMP_FILE \ No newline at end of file diff --git a/shongo-deployment/service/shongo-client-web.sh b/shongo-deployment/service/shongo-client-web.sh deleted file mode 100755 index 672f12b7c..000000000 --- a/shongo-deployment/service/shongo-client-web.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -NAME=shongo-client-web -BIN="java -Dfile.encoding=UTF-8 -jar ../shongo-client-web/target/shongo-client-web-:VERSION:.jar --daemon" -BIN_STARTED="ClientWeb successfully started" - -source $(dirname $0)/shongo-service.sh \ No newline at end of file diff --git a/shongo-deployment/service/shongo-install.sh b/shongo-deployment/service/shongo-install.sh index e017723eb..2408ac96c 100755 --- a/shongo-deployment/service/shongo-install.sh +++ b/shongo-deployment/service/shongo-install.sh @@ -14,7 +14,6 @@ fi # Parse arguments SHONGO_CONTROLLER=false SHONGO_CONNECTOR=false -SHONGO_CLIENT_WEB=false for argument in "$@" do case "$argument" in @@ -24,9 +23,6 @@ do shongo-connector) SHONGO_CONNECTOR=true ;; - shongo-client-web) - SHONGO_CLIENT_WEB=true - ;; esac done @@ -35,7 +31,6 @@ if [[ $# -eq 0 ]] ; then echo Installing all shongo services... SHONGO_CONTROLLER=true SHONGO_CONNECTOR=true - SHONGO_CLIENT_WEB=true fi ################################################################################ @@ -110,42 +105,6 @@ update-rc.d shongo-connector defaults 91 10 fi -################################################################################ -# -# Install shongo-client-web service -# -if [ "$SHONGO_CLIENT_WEB" = true ] ; then - -echo Installing shongo-client-web... -cat > $SERVICE_DIR/shongo-client-web < Date: Wed, 19 Oct 2022 20:08:22 +0200 Subject: [PATCH 051/134] Added administrator mode value to user setting update endpoint --- .../shongo/controller/rest/controllers/UserController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java index 8c8c371db..7c51cc1a1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java @@ -113,6 +113,7 @@ public SettingsModel updateUserSettings( userSettings.setHomeTimeZone(newSettings.getHomeTimeZone()); } userSettings.setCurrentTimeZone(newSettings.getCurrentTimeZone()); + userSettings.setAdministrationMode(newSettings.getAdministrationMode()); authorizationService.updateUserSettings(securityToken, userSettings); cache.clearUserPermissions(securityToken); From e733804972954b65b8c819ae8644e47b1208bf4c Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sat, 29 Oct 2022 20:50:42 +0200 Subject: [PATCH 052/134] Change Reservation request history type to ReservationRequestSummary --- .../ReservationRequestController.java | 29 +++++++++++++++---- .../ReservationRequestDetailModel.java | 4 +-- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index d896417e9..8991940b6 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -235,12 +235,6 @@ ReservationRequestDetailModel getRequest( CacheProvider cacheProvider = new CacheProvider(cache, securityToken); ReservationRequestSummary summary = cache.getReservationRequestSummary(securityToken, id); - List history = - reservationService.getReservationRequestHistory(securityToken, id) - .stream() - .map(item -> new ReservationRequestHistoryModel(item, cacheProvider)) - .collect(Collectors.toList()); - String roomId = cache.getExecutableId(securityToken, id); RoomAuthorizedData authorizedData = null; if (roomId != null) { @@ -263,6 +257,29 @@ ReservationRequestDetailModel getRequest( } VirtualRoomModel virtualRoomData = new VirtualRoomModel(summary); + List historySummaries = reservationService.getReservationRequestHistory(securityToken, id); + Map> permissionsByReservationHistory = + cache.getReservationRequestsPermissions(securityToken, historySummaries); + List history = + historySummaries + .stream() + .map(item -> { + UserInformation user = cache.getUserInformation(securityToken, item.getUserId()); + String resource = item.getResourceId(); + ResourceSummary resourceSum = null; + if (resource != null) { + resourceSum = cacheProvider.getResourceSummary(resource); + } + return new ReservationRequestModel( + item, + new VirtualRoomModel(item), + permissionsByReservationHistory, + user, + resourceSum + ); + }) + .collect(Collectors.toList()); + // If the request is a ROOM_CAPACITY, then get virtual room data from the room String virtualRoomId = summary.getReusedReservationRequestId(); if (virtualRoomId != null) { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java index 59f837e47..cd66501e5 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java @@ -30,7 +30,7 @@ public class ReservationRequestDetailModel extends ReservationRequestModel private ExecutableState executableState; private Boolean notifyParticipants; private RoomAuthorizedData authorizedData; - private List history; + private List history; public ReservationRequestDetailModel( ReservationRequestSummary summary, @@ -38,7 +38,7 @@ public ReservationRequestDetailModel( Map> permissionsByReservationRequestId, UserInformation ownerInformation, RoomAuthorizedData authorizedData, - List history, + List history, ResourceSummary resourceSummary) { super(summary, virtualRoom, permissionsByReservationRequestId, ownerInformation, resourceSummary); From 0eba1f05a52787ac00f45245d83b244a3d949f34 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 6 Jan 2023 19:09:19 +0100 Subject: [PATCH 053/134] Added list reservation requests filter support for multiple resources --- .../Shongo/ClientCli/ReservationService.pm | 8 ++++-- .../web/controllers/ResourceController.java | 2 +- .../ReservationRequestListRequest.java | 23 ++++++++-------- .../api/rpc/ReservationServiceImpl.java | 27 +++++++++++-------- .../ReservationRequestController.java | 9 ++++--- 5 files changed, 40 insertions(+), 29 deletions(-) diff --git a/shongo-client-cli/src/main/perl/Shongo/ClientCli/ReservationService.pm b/shongo-client-cli/src/main/perl/Shongo/ClientCli/ReservationService.pm index d3e17620b..06a58ffff 100644 --- a/shongo-client-cli/src/main/perl/Shongo/ClientCli/ReservationService.pm +++ b/shongo-client-cli/src/main/perl/Shongo/ClientCli/ReservationService.pm @@ -78,7 +78,7 @@ sub populate() 'list-reservation-requests' => { desc => 'List summary of all existing reservation requests', options => 'technology=s search=s resource=s', - args => '[-technology ][-search ][-resource ]', + args => '[-technology ][-search ][-resource ]', method => sub { my ($shell, $params, @args) = @_; list_reservation_requests($params->{'options'}); @@ -251,7 +251,11 @@ sub list_reservation_requests() } } if ( defined($options->{'resource'}) ) { - $request->{'specificationResourceId'} = $options->{'resource'}; + $request->{'specificationResourceIds'} = []; + foreach my $resource (split(/,/, $options->{'resource'})) { + $resource =~ s/(^ +)|( +$)//g; + push(@{$request->{'specificationResourceIds'}}, $resource); + } } my $application = Shongo::ClientCli->instance(); my $response = $application->secure_hash_request('Reservation.listReservationRequests', $request); diff --git a/shongo-client-web/src/main/java/cz/cesnet/shongo/client/web/controllers/ResourceController.java b/shongo-client-web/src/main/java/cz/cesnet/shongo/client/web/controllers/ResourceController.java index 336f144e9..cd18e9bd0 100644 --- a/shongo-client-web/src/main/java/cz/cesnet/shongo/client/web/controllers/ResourceController.java +++ b/shongo-client-web/src/main/java/cz/cesnet/shongo/client/web/controllers/ResourceController.java @@ -283,7 +283,7 @@ public Map handleReservationRequestsConfirmationData( Interval interval = Temporal.roundIntervalToDays(new Interval(intervalFrom, intervalTo)); requestListRequest.setInterval(interval); if (resourceId != null) { - requestListRequest.setSpecificationResourceId(resourceId); + requestListRequest.setSpecificationResourceIds(new HashSet<>(Collections.singleton(resourceId))); } else { throw new TodoImplementException("list request for confirmation generaly"); } diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/request/ReservationRequestListRequest.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/request/ReservationRequestListRequest.java index a82861dbf..ccdd7a858 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/request/ReservationRequestListRequest.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/request/ReservationRequestListRequest.java @@ -6,7 +6,6 @@ import cz.cesnet.shongo.controller.api.AllocationState; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; import cz.cesnet.shongo.controller.api.SecurityToken; -import org.joda.time.DateTime; import org.joda.time.Interval; import java.util.HashSet; @@ -45,9 +44,9 @@ public class ReservationRequestListRequest extends SortableListRequest specificationResourceIds = new HashSet<>(); /** * Restricts the {@link ListResponse} to contain only reservation requests which reuse reservation request with @@ -212,19 +211,19 @@ public void addSpecificationType(ReservationRequestSummary.SpecificationType spe } /** - * @return {@link #specificationResourceId} + * @return {@link #specificationResourceIds} */ - public String getSpecificationResourceId() + public Set getSpecificationResourceIds() { - return specificationResourceId; + return specificationResourceIds; } /** - * @param specificationResourceId sets the {@link #specificationResourceId} + * @param specificationResourceIds sets the {@link #specificationResourceIds} */ - public void setSpecificationResourceId(String specificationResourceId) + public void setSpecificationResourceIds(Set specificationResourceIds) { - this.specificationResourceId = specificationResourceId; + this.specificationResourceIds = specificationResourceIds; } /** @@ -377,7 +376,7 @@ public static enum Sort private static final String PARENT_RESERVATION_REQUEST_ID = "parentReservationRequestId"; private static final String SPECIFICATION_TYPES = "specificationTypes"; private static final String SPECIFICATION_TECHNOLOGIES = "specificationTechnologies"; - private static final String SPECIFICATION_RESOURCE_ID = "specificationResourceId"; + private static final String SPECIFICATION_RESOURCE_IDS = "specificationResourceIds"; private static final String REUSED_RESERVATION_REQUEST_ID = "reusedReservationRequestId"; private static final String ALLOCATION_STATE = "allocationState"; private static final String INTERVAL = "interval"; @@ -395,7 +394,7 @@ public DataMap toData() dataMap.set(PARENT_RESERVATION_REQUEST_ID, parentReservationRequestId); dataMap.set(SPECIFICATION_TYPES, specificationTypes); dataMap.set(SPECIFICATION_TECHNOLOGIES, specificationTechnologies); - dataMap.set(SPECIFICATION_RESOURCE_ID, specificationResourceId); + dataMap.set(SPECIFICATION_RESOURCE_IDS, specificationResourceIds); dataMap.set(REUSED_RESERVATION_REQUEST_ID, reusedReservationRequestId); dataMap.set(ALLOCATION_STATE, allocationState); dataMap.set(INTERVAL, interval); @@ -416,7 +415,7 @@ public void fromData(DataMap dataMap) specificationTypes = (Set) dataMap.getSet(SPECIFICATION_TYPES, ReservationRequestSummary.SpecificationType.class); specificationTechnologies = dataMap.getSet(SPECIFICATION_TECHNOLOGIES, Technology.class); - specificationResourceId = dataMap.getString(SPECIFICATION_RESOURCE_ID); + specificationResourceIds = dataMap.getSet(SPECIFICATION_RESOURCE_IDS, String.class); reusedReservationRequestId = dataMap.getString(REUSED_RESERVATION_REQUEST_ID); allocationState = dataMap.getEnum(ALLOCATION_STATE, AllocationState.class); interval = dataMap.getInterval(INTERVAL); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java index a0b4cd879..04421a2f0 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java @@ -38,6 +38,7 @@ import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import java.util.*; +import java.util.stream.Collectors; /** * Implementation of {@link ReservationService}. @@ -659,7 +660,7 @@ public Boolean confirmReservationRequest(SecurityToken securityToken, String res requestListRequest.setInterval(reservationRequest.getSlot()); requestListRequest.setIntervalDateOnly(false); requestListRequest.setAllocationState(AllocationState.CONFIRM_AWAITING); - requestListRequest.setSpecificationResourceId(resourceId); + requestListRequest.setSpecificationResourceIds(new HashSet<>(Collections.singleton(resourceId))); ListResponse listResponse = listOwnedResourcesReservationRequests(requestListRequest); try { @@ -1102,11 +1103,13 @@ public ListResponse listReservationRequests(Reservati } // Filter specification resource id - String specificationResourceId = request.getSpecificationResourceId(); - if (specificationResourceId != null) { - queryFilter.addFilter("specification_summary.resource_id = :resource_id"); - queryFilter.addFilterParameter("resource_id", - ObjectIdentifier.parseLocalId(specificationResourceId, ObjectType.RESOURCE)); + Set specificationResourceIds = request.getSpecificationResourceIds(); + if (!specificationResourceIds.isEmpty()) { + Set resourceIds = specificationResourceIds.stream() + .map(id -> ObjectIdentifier.parseLocalId(id, ObjectType.RESOURCE)) + .collect(Collectors.toSet()); + queryFilter.addFilter("specification_summary.resource_id IN :resource_ids"); + queryFilter.addFilterParameter("resource_ids", resourceIds); } String reusedReservationRequestId = request.getReusedReservationRequestId(); @@ -1274,11 +1277,13 @@ public ListResponse listOwnedResourcesReservationRequ } // Filter specification resource id - String specificationResourceId = request.getSpecificationResourceId(); - if (specificationResourceId != null) { - queryFilter.addFilter("specification_summary.resource_id = :resource_id"); - queryFilter.addFilterParameter("resource_id", - ObjectIdentifier.parseLocalId(specificationResourceId, ObjectType.RESOURCE)); + Set specificationResourceIds = request.getSpecificationResourceIds(); + if (!specificationResourceIds.isEmpty()) { + Set resourceIds = specificationResourceIds.stream() + .map(id -> ObjectIdentifier.parseLocalId(id, ObjectType.RESOURCE)) + .collect(Collectors.toSet()); + queryFilter.addFilter("specification_summary.resource_id IN :resource_ids"); + queryFilter.addFilterParameter("resource_ids", resourceIds); } // List only latest versions of a reservation requests (no it's modifications or deleted requests) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 8991940b6..aa0f18a91 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -86,15 +86,18 @@ ListResponse listRequests( @RequestParam(value = "participant_user_id", required = false) String participantUserId, @RequestParam(value = "search", required = false) String search, @RequestParam(value = "type", required = false) Set reservationTypes, - @RequestParam(value = "resource", required = false) String resourceId) + @RequestParam(value = "resource", required = false) Set resourceId) { if (reservationTypes == null) { reservationTypes = new HashSet<>(); } + if (resourceId == null) { + resourceId = new HashSet<>(); + } // room capacity does not have resource_id // filter VIRTUAL_ROOMS by resource_id and then call recursively with parentRequestId - if (reservationTypes.contains(ReservationType.ROOM_CAPACITY) && resourceId != null) { + if (reservationTypes.contains(ReservationType.ROOM_CAPACITY) && !resourceId.isEmpty()) { Set virtualRoomReservationTypes = new HashSet<>(List.of(ReservationType.VIRTUAL_ROOM)); ListResponse response = listRequests(securityToken, start, count, sort, sortDescending, allocationState, permanentRoomId, technology, intervalFrom, intervalTo, userId, @@ -124,7 +127,7 @@ ListResponse listRequests( request.setAllocationState(allocationState); request.setParticipantUserId(participantUserId); request.setSearch(search); - request.setSpecificationResourceId(resourceId); + request.setSpecificationResourceIds(resourceId); if (permanentRoomId != null) { request.setReusedReservationRequestId(permanentRoomId); From 5ced7e27de6b2f9bddcbef76736b8aa877b4c17f Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 11 Jan 2023 19:45:50 +0100 Subject: [PATCH 054/134] Fix list reservation request to accept multiple specification types with specified resource --- .../controllers/ReservationRequestController.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index aa0f18a91..75bee41f7 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -36,6 +36,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; @@ -99,14 +100,14 @@ ListResponse listRequests( // filter VIRTUAL_ROOMS by resource_id and then call recursively with parentRequestId if (reservationTypes.contains(ReservationType.ROOM_CAPACITY) && !resourceId.isEmpty()) { Set virtualRoomReservationTypes = new HashSet<>(List.of(ReservationType.VIRTUAL_ROOM)); - ListResponse response = listRequests(securityToken, start, count, sort, + ListResponse virtualRooms = listRequests(securityToken, start, count, sort, sortDescending, allocationState, permanentRoomId, technology, intervalFrom, intervalTo, userId, participantUserId, search, virtualRoomReservationTypes, resourceId); DateTime finalIntervalFrom = intervalFrom; DateTime finalIntervalTo = intervalTo; Set capacityReservationTypes = new HashSet<>(List.of(ReservationType.ROOM_CAPACITY)); - List capacities = response.getItems() + List response = virtualRooms.getItems() .stream() .map(room -> listRequests(securityToken, start, count, sort, sortDescending, allocationState, room.getId(), technology, finalIntervalFrom, finalIntervalTo, userId, participantUserId, @@ -114,7 +115,15 @@ ListResponse listRequests( .flatMap(List::stream) .collect(Collectors.toList()); - return ListResponse.fromRequest(start, count, capacities); + if (reservationTypes.size() > 1) { + reservationTypes.remove(ReservationType.ROOM_CAPACITY); + List other = listRequests(securityToken, start, count, sort, sortDescending, + allocationState, permanentRoomId, technology, intervalFrom, intervalTo, userId, participantUserId, + search, reservationTypes, resourceId).getItems(); + response.addAll(other); + } + + return ListResponse.fromRequest(start, count, response); } ReservationRequestListRequest request = new ReservationRequestListRequest(); From 1d7d9921e7bc56cbae2729e1393c04c3f7d10086 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 19 May 2023 15:49:15 +0200 Subject: [PATCH 055/134] Replace constructors with lombok's @RequiredArgsConstructor --- .../cesnet/shongo/controller/rest/Cache.java | 60 +++++++------------ .../shongo/controller/rest/RoomCache.java | 37 ++++-------- .../controllers/ParticipantController.java | 11 +--- .../rest/controllers/RecordingController.java | 13 +--- .../rest/controllers/ReportController.java | 8 +-- .../ReservationRequestController.java | 15 +---- .../rest/controllers/ResourceController.java | 9 +-- .../rest/controllers/RoomController.java | 9 +-- .../rest/controllers/RuntimeController.java | 13 +--- .../rest/controllers/UserController.java | 9 +-- .../rest/controllers/UserRoleController.java | 9 +-- 11 files changed, 52 insertions(+), 141 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java index 562f09336..46362d0df 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java @@ -15,10 +15,10 @@ import cz.cesnet.shongo.controller.api.rpc.ResourceService; import cz.cesnet.shongo.controller.rest.error.ObjectInaccessibleException; import cz.cesnet.shongo.controller.rest.models.resource.ResourcesUtilization; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.joda.time.DateTime; import org.joda.time.Duration; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import java.util.*; @@ -32,6 +32,7 @@ * @author Martin Srom */ @Slf4j +@RequiredArgsConstructor public class Cache { @@ -51,77 +52,60 @@ public class Cache /** * @see ResourcesUtilization */ - private final ExpirationMap resourcesUtilizationByToken = new ExpirationMap<>(); + private final ExpirationMap resourcesUtilizationByToken = + new ExpirationMap<>(Duration.standardMinutes(10)); /** * {@link UserInformation}s by {@link SecurityToken}. */ - private final ExpirationMap> userPermissionsByToken = new ExpirationMap<>(); + private final ExpirationMap> userPermissionsByToken = + new ExpirationMap<>(Duration.standardMinutes(5)); /** * {@link UserInformation}s by user-ids. */ - private final ExpirationMap userInformationByUserId = new ExpirationMap<>(); + private final ExpirationMap userInformationByUserId = + new ExpirationMap<>(Duration.standardMinutes(USER_EXPIRATION_MINUTES)); /** * {@link Group}s by group-ids. */ - private final ExpirationMap groupByGroupId = new ExpirationMap<>(); + private final ExpirationMap groupByGroupId = + new ExpirationMap<>(Duration.standardMinutes(USER_EXPIRATION_MINUTES)); /** * {@link UserState}s by {@link SecurityToken}. */ - private final ExpirationMap userStateByToken = new ExpirationMap(); + private final ExpirationMap userStateByToken = + new ExpirationMap<>(Duration.standardHours(1)); /** * {@link ResourceSummary} by identifier. */ - private final ExpirationMap resourceById = new ExpirationMap<>(); + private final ExpirationMap resourceById = + new ExpirationMap<>(Duration.standardHours(1)); /** * {@link ReservationRequestSummary} by identifier. */ - private final ExpirationMap reservationRequestById = new ExpirationMap<>(); + private final ExpirationMap reservationRequestById = + new ExpirationMap<>(Duration.standardMinutes(5)); /** * {@link Reservation} by identifier. */ - private final ExpirationMap reservationById = new ExpirationMap<>(); + private final ExpirationMap reservationById = + new ExpirationMap<>(Duration.standardMinutes(5)); /** * Ids of resources with public calendar by their calendarUriKey */ - private final ExpirationMap resourceIdsWithPublicCalendarByUriKey = new ExpirationMap<>(); + private final ExpirationMap resourceIdsWithPublicCalendarByUriKey = + new ExpirationMap<>(Duration.standardMinutes(10)); /** * {@link Reservation} by identifier. */ - private final ExpirationMap executableById = new ExpirationMap<>(); - - /** - * Constructor. - */ - public Cache( - @Autowired AuthorizationService authorizationService, - @Autowired ResourceService resourceService, - @Autowired ReservationService reservationService, - @Autowired ExecutableService executableService) - { - this.authorizationService = authorizationService; - this.resourceService = resourceService; - this.reservationService = reservationService; - this.executableService = executableService; - - // Set expiration durations - userPermissionsByToken.setExpiration(Duration.standardMinutes(5)); - userInformationByUserId.setExpiration(Duration.standardMinutes(USER_EXPIRATION_MINUTES)); - groupByGroupId.setExpiration(Duration.standardMinutes(USER_EXPIRATION_MINUTES)); - userStateByToken.setExpiration(Duration.standardHours(1)); - resourceById.setExpiration(Duration.standardHours(1)); - reservationRequestById.setExpiration(Duration.standardMinutes(5)); - reservationById.setExpiration(Duration.standardMinutes(5)); - executableById.setExpiration(Duration.standardSeconds(10)); - resourcesUtilizationByToken.setExpiration(Duration.standardMinutes(10)); - resourceIdsWithPublicCalendarByUriKey.setExpiration(Duration.standardMinutes(10)); - } + private final ExpirationMap executableById = + new ExpirationMap<>(Duration.standardSeconds(10)); /** * @param userId diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java index 5d73cdf55..04daf20c3 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java @@ -11,8 +11,8 @@ import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.api.rpc.ResourceControlService; import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; +import lombok.RequiredArgsConstructor; import org.joda.time.Duration; -import org.springframework.beans.factory.annotation.Autowired; import java.util.*; @@ -21,6 +21,7 @@ * * @author Martin Srom */ +@RequiredArgsConstructor public class RoomCache { @@ -33,46 +34,32 @@ public class RoomCache /** * {@link RoomExecutable} by roomExecutableId. */ - private final ExpirationMap roomExecutableCache = new ExpirationMap<>(); + private final ExpirationMap roomExecutableCache = + new ExpirationMap<>(Duration.standardSeconds(15)); /** * {@link Room} by roomExecutableId". */ - private final ExpirationMap roomCache = new ExpirationMap<>(); + private final ExpirationMap roomCache = + new ExpirationMap<>(Duration.standardSeconds(30)); /** * Collection of {@link RoomParticipant}s by roomExecutableId. */ - private final ExpirationMap> roomParticipantsCache = new ExpirationMap<>(); + private final ExpirationMap> roomParticipantsCache = + new ExpirationMap<>(Duration.standardSeconds(15)); /** * {@link RoomParticipant} by "roomExecutableId:participantId". */ - private final ExpirationMap roomParticipantCache = new ExpirationMap<>(); + private final ExpirationMap roomParticipantCache = + new ExpirationMap<>(); /** * Participant snapshots in {@link MediaData} by "roomExecutableId:participantId". */ - private final ExpirationMap roomParticipantSnapshotCache = new ExpirationMap<>(); - - /** - * Constructor. - */ - public RoomCache( - @Autowired ResourceControlService resourceControlService, - @Autowired ExecutableService executableService, - @Autowired Cache cache) - { - this.resourceControlService = resourceControlService; - this.executableService = executableService; - this.cache = cache; - - // Set expiration durations - roomCache.setExpiration(Duration.standardSeconds(30)); - roomParticipantsCache.setExpiration(Duration.standardSeconds(15)); - roomExecutableCache.setExpiration(Duration.standardSeconds(15)); - roomParticipantSnapshotCache.setExpiration(Duration.standardSeconds(15)); - } + private final ExpirationMap roomParticipantSnapshotCache = + new ExpirationMap<>(Duration.standardSeconds(15)); /** * @param securityToken diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java index f0b742254..0cb49d74e 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java @@ -12,7 +12,7 @@ import cz.cesnet.shongo.controller.rest.models.participant.ParticipantConfigurationModel; import cz.cesnet.shongo.controller.rest.models.participant.ParticipantModel; import io.swagger.v3.oas.annotations.Operation; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; @@ -30,20 +30,13 @@ */ @RestController @RequestMapping(ClientWebUrl.PARTICIPANTS) +@RequiredArgsConstructor public class ParticipantController { private final Cache cache; private final ExecutableService executableService; - public ParticipantController( - @Autowired Cache cache, - @Autowired ExecutableService executableService) - { - this.cache = cache; - this.executableService = executableService; - } - @Operation(summary = "Lists reservation request participants.") @GetMapping ListResponse listRequestParticipants( diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java index d3625a3e1..ea667c65d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java @@ -11,7 +11,7 @@ import cz.cesnet.shongo.controller.rest.models.recording.RecordingModel; import cz.cesnet.shongo.controller.scheduler.SchedulerReportSet; import io.swagger.v3.oas.annotations.Operation; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -26,6 +26,7 @@ */ @RestController @RequestMapping(ClientWebUrl.RECORDINGS) +@RequiredArgsConstructor public class RecordingController { @@ -33,16 +34,6 @@ public class RecordingController private final ExecutableService executableService; private final ResourceControlService resourceControlService; - public RecordingController( - @Autowired Cache cache, - @Autowired ExecutableService executableService, - @Autowired ResourceControlService resourceControlService) - { - this.cache = cache; - this.executableService = executableService; - this.resourceControlService = resourceControlService; - } - @Operation(summary = "Lists reservation request recordings.") @GetMapping ListResponse listRequestRecordings( diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java index d97b7743d..5f7ea3de4 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java @@ -5,8 +5,8 @@ import cz.cesnet.shongo.controller.rest.models.report.ReportModel; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirements; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -23,16 +23,12 @@ @SecurityRequirements @RestController @RequestMapping(ClientWebUrl.REPORT) +@RequiredArgsConstructor public class ReportController { private final ErrorHandler errorHandler; - public ReportController(@Autowired ErrorHandler errorHandler) - { - this.errorHandler = errorHandler; - } - /** * Handle problem report. */ diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 75bee41f7..431af57a0 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -23,9 +23,9 @@ import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; import cz.cesnet.shongo.controller.rest.models.room.RoomAuthorizedData; import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; import org.joda.time.DateTime; import org.joda.time.Interval; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; @@ -47,6 +47,7 @@ */ @RestController @RequestMapping(ClientWebUrl.RESERVATION_REQUESTS) +@RequiredArgsConstructor public class ReservationRequestController { @@ -55,18 +56,6 @@ public class ReservationRequestController private final AuthorizationService authorizationService; private final ExecutableService executableService; - public ReservationRequestController( - @Autowired Cache cache, - @Autowired ReservationService reservationService, - @Autowired AuthorizationService authorizationService, - @Autowired ExecutableService executableService) - { - this.cache = cache; - this.reservationService = reservationService; - this.authorizationService = authorizationService; - this.executableService = executableService; - } - @Operation(summary = "Lists reservation requests.") @GetMapping ListResponse listRequests( diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java index a1d964508..0bd333316 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java @@ -12,11 +12,11 @@ import cz.cesnet.shongo.controller.rest.models.TechnologyModel; import cz.cesnet.shongo.controller.rest.models.resource.*; import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.joda.time.DateTime; import org.joda.time.Interval; import org.joda.time.Period; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; @@ -36,18 +36,13 @@ @Slf4j @RestController @RequestMapping(ClientWebUrl.RESOURCES) +@RequiredArgsConstructor public class ResourceController { private final Cache cache; private final ResourceService resourceService; - public ResourceController(@Autowired Cache cache, @Autowired ResourceService resourceService) - { - this.cache = cache; - this.resourceService = resourceService; - } - /** * Lists {@link Resource}s. */ diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java index 2761decd2..e991ee168 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java @@ -11,7 +11,7 @@ import cz.cesnet.shongo.controller.rest.models.room.RoomAuthorizedData; import cz.cesnet.shongo.controller.rest.models.room.RoomModel; import io.swagger.v3.oas.annotations.Operation; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import java.util.stream.Collectors; @@ -25,18 +25,13 @@ */ @RestController @RequestMapping(ClientWebUrl.ROOMS) +@RequiredArgsConstructor public class RoomController { private final Cache cache; private final ExecutableService executableService; - public RoomController(@Autowired Cache cache, @Autowired ExecutableService executableService) - { - this.cache = cache; - this.executableService = executableService; - } - @Operation(summary = "Lists rooms (executables).") @GetMapping public ListResponse listRooms( diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java index 02efbb98b..d24452844 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java @@ -12,8 +12,8 @@ import cz.cesnet.shongo.controller.rest.RoomCache; import cz.cesnet.shongo.controller.rest.models.runtimemanagement.RuntimeParticipantModel; import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -33,6 +33,7 @@ @Slf4j @RestController @RequestMapping(ClientWebUrl.RUNTIME_MANAGEMENT) +@RequiredArgsConstructor public class RuntimeController { @@ -40,16 +41,6 @@ public class RuntimeController private final RoomCache roomCache; private final ExecutableService executableService; - public RuntimeController( - @Autowired Cache cache, - @Autowired RoomCache roomCache, - @Autowired ExecutableService executableService) - { - this.cache = cache; - this.executableService = executableService; - this.roomCache = roomCache; - } - @Operation(summary = "Lists reservation request runtime participants.") @GetMapping(ClientWebUrl.RUNTIME_MANAGEMENT_PARTICIPANTS) ListResponse listRuntimeParticipants( diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java index 7c51cc1a1..e8cb2ed86 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java @@ -13,7 +13,7 @@ import cz.cesnet.shongo.controller.rest.ClientWebUrl; import cz.cesnet.shongo.controller.rest.models.users.SettingsModel; import io.swagger.v3.oas.annotations.Operation; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -27,18 +27,13 @@ */ @RestController @RequestMapping(ClientWebUrl.USERS_AND_GROUPS) +@RequiredArgsConstructor public class UserController { private final AuthorizationService authorizationService; private final Cache cache; - public UserController(@Autowired AuthorizationService reservationService, @Autowired Cache cache) - { - this.authorizationService = reservationService; - this.cache = cache; - } - /** * Handle request for list of {@link UserInformation}s which contains given {@code filter} text in any field. * diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java index c4dd98b25..3a18682d8 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java @@ -12,7 +12,7 @@ import cz.cesnet.shongo.controller.rest.error.LastOwnerRoleNotDeletableException; import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; import io.swagger.v3.oas.annotations.Operation; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; @@ -28,18 +28,13 @@ */ @RestController @RequestMapping(ClientWebUrl.ROLES) +@RequiredArgsConstructor public class UserRoleController { private final AuthorizationService authorizationService; private final Cache cache; - public UserRoleController(@Autowired AuthorizationService reservationService, @Autowired Cache cache) - { - this.authorizationService = reservationService; - this.cache = cache; - } - @Operation(summary = "Lists reservation request roles.") @GetMapping ListResponse listRequestRoles( From 6359405f544276e6393fc60300af4472c0822355 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 19 May 2023 15:54:19 +0200 Subject: [PATCH 056/134] Code cleanup --- .../cesnet/shongo/controller/rest/Cache.java | 23 ++++++- .../shongo/controller/rest/RESTApiServer.java | 8 ++- .../shongo/controller/rest/RoomCache.java | 9 +-- .../controller/rest/config/OpenApiConfig.java | 6 +- .../controllers/ParticipantController.java | 20 +++++- .../rest/controllers/RecordingController.java | 8 ++- .../ReservationRequestController.java | 41 +++++++++--- .../rest/controllers/ResourceController.java | 16 ++++- .../rest/controllers/RoomController.java | 7 ++- .../rest/controllers/RuntimeController.java | 15 ++++- .../rest/controllers/UserController.java | 9 ++- .../rest/controllers/UserRoleController.java | 11 +++- .../models/participant/ParticipantModel.java | 6 +- .../ReservationRequestCreateModel.java | 63 ++++++++++++++++--- .../resource/ResourceCapacityUtilization.java | 8 ++- .../models/resource/ResourcesUtilization.java | 16 ++++- 16 files changed, 222 insertions(+), 44 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java index 46362d0df..3ec0e2c11 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java @@ -7,8 +7,20 @@ import cz.cesnet.shongo.controller.ControllerReportSet; import cz.cesnet.shongo.controller.ObjectPermission; import cz.cesnet.shongo.controller.SystemPermission; -import cz.cesnet.shongo.controller.api.*; -import cz.cesnet.shongo.controller.api.request.*; +import cz.cesnet.shongo.controller.api.AllocationState; +import cz.cesnet.shongo.controller.api.Executable; +import cz.cesnet.shongo.controller.api.Group; +import cz.cesnet.shongo.controller.api.ObjectPermissionSet; +import cz.cesnet.shongo.controller.api.Reservation; +import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.api.ResourceSummary; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.request.GroupListRequest; +import cz.cesnet.shongo.controller.api.request.ListResponse; +import cz.cesnet.shongo.controller.api.request.ObjectPermissionListRequest; +import cz.cesnet.shongo.controller.api.request.ReservationRequestListRequest; +import cz.cesnet.shongo.controller.api.request.ResourceListRequest; +import cz.cesnet.shongo.controller.api.request.UserListRequest; import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.api.rpc.ReservationService; @@ -21,7 +33,12 @@ import org.joda.time.Duration; import org.springframework.scheduling.annotation.Scheduled; -import java.util.*; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java index a17508399..f12c524f1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java @@ -8,7 +8,13 @@ import cz.cesnet.shongo.ssl.ConfiguredSSLContext; import cz.cesnet.shongo.ssl.SSLCommunication; import org.eclipse.jetty.http.HttpScheme; -import org.eclipse.jetty.server.*; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.ssl.SslContextFactory; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java index 04daf20c3..c16505f46 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java @@ -8,13 +8,16 @@ import cz.cesnet.shongo.controller.api.Executable; import cz.cesnet.shongo.controller.api.RoomExecutable; import cz.cesnet.shongo.controller.api.SecurityToken; -import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.api.rpc.ResourceControlService; import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import lombok.RequiredArgsConstructor; import org.joda.time.Duration; -import java.util.*; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * Cache of information for management of rooms. @@ -27,8 +30,6 @@ public class RoomCache private final ResourceControlService resourceControlService; - private final ExecutableService executableService; - private final Cache cache; /** diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java index e16d48c57..2fcfc2db8 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java @@ -12,7 +12,11 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.springframework.web.servlet.resource.*; +import org.springframework.web.servlet.resource.PathResourceResolver; +import org.springframework.web.servlet.resource.ResourceTransformer; +import org.springframework.web.servlet.resource.ResourceTransformerChain; +import org.springframework.web.servlet.resource.TransformedResource; +import org.springframework.web.servlet.resource.WebJarsResourceResolver; import javax.servlet.http.HttpServletRequest; import java.io.IOException; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java index 0cb49d74e..84a7fd7de 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java @@ -2,7 +2,13 @@ import cz.cesnet.shongo.CommonReportSet; import cz.cesnet.shongo.ParticipantRole; -import cz.cesnet.shongo.controller.api.*; +import cz.cesnet.shongo.controller.api.AbstractParticipant; +import cz.cesnet.shongo.controller.api.AbstractRoomExecutable; +import cz.cesnet.shongo.controller.api.Executable; +import cz.cesnet.shongo.controller.api.RoomExecutable; +import cz.cesnet.shongo.controller.api.RoomExecutableParticipantConfiguration; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.UsedRoomExecutable; import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.rest.Cache; @@ -14,7 +20,17 @@ import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; import java.util.Comparator; import java.util.LinkedList; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java index ea667c65d..2b68cd57a 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java @@ -12,7 +12,13 @@ import cz.cesnet.shongo.controller.scheduler.SchedulerReportSet; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.stream.Collectors; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 431af57a0..ecbe7110d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -19,7 +19,11 @@ import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.ClientWebUrl; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; -import cz.cesnet.shongo.controller.rest.models.reservationrequest.*; +import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestCreateModel; +import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestDetailModel; +import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestModel; +import cz.cesnet.shongo.controller.rest.models.reservationrequest.SpecificationType; +import cz.cesnet.shongo.controller.rest.models.reservationrequest.VirtualRoomModel; import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; import cz.cesnet.shongo.controller.rest.models.room.RoomAuthorizedData; import io.swagger.v3.oas.annotations.Operation; @@ -28,7 +32,17 @@ import org.joda.time.Interval; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.HashSet; @@ -36,7 +50,6 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; @@ -106,9 +119,10 @@ ListResponse listRequests( if (reservationTypes.size() > 1) { reservationTypes.remove(ReservationType.ROOM_CAPACITY); - List other = listRequests(securityToken, start, count, sort, sortDescending, - allocationState, permanentRoomId, technology, intervalFrom, intervalTo, userId, participantUserId, - search, reservationTypes, resourceId).getItems(); + List other = listRequests( + securityToken, start, count, sort, sortDescending, allocationState, permanentRoomId, technology, + intervalFrom, intervalTo, userId, participantUserId, search, reservationTypes, resourceId + ).getItems(); response.addAll(other); } @@ -175,7 +189,9 @@ ListResponse listRequests( resourceSummary = cache.getResourceSummary(securityToken, resource); } VirtualRoomModel virtualRoom = new VirtualRoomModel(item); - return new ReservationRequestModel(item, virtualRoom, permissionsByReservationRequestId, user, resourceSummary); + return new ReservationRequestModel( + item, virtualRoom, permissionsByReservationRequestId, user, resourceSummary + ); }).collect(Collectors.toList())); listResponse.setStart(response.getStart()); listResponse.setCount(response.getCount()); @@ -258,7 +274,9 @@ ReservationRequestDetailModel getRequest( } VirtualRoomModel virtualRoomData = new VirtualRoomModel(summary); - List historySummaries = reservationService.getReservationRequestHistory(securityToken, id); + List historySummaries = reservationService.getReservationRequestHistory( + securityToken, id + ); Map> permissionsByReservationHistory = cache.getReservationRequestsPermissions(securityToken, historySummaries); List history = @@ -284,12 +302,15 @@ ReservationRequestDetailModel getRequest( // If the request is a ROOM_CAPACITY, then get virtual room data from the room String virtualRoomId = summary.getReusedReservationRequestId(); if (virtualRoomId != null) { - ReservationRequestSummary summaryVirtualRoom = cache.getReservationRequestSummary(securityToken, virtualRoomId); + ReservationRequestSummary summaryVirtualRoom = cache.getReservationRequestSummary( + securityToken, virtualRoomId + ); virtualRoomData = new VirtualRoomModel(summaryVirtualRoom); } return new ReservationRequestDetailModel( - summary, virtualRoomData, permissionsByReservationRequestId, ownerInformation, authorizedData, history, resourceSummary + summary, virtualRoomData, permissionsByReservationRequestId, ownerInformation, authorizedData, history, + resourceSummary ); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java index 0bd333316..78924707e 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java @@ -10,7 +10,14 @@ import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.ClientWebUrl; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; -import cz.cesnet.shongo.controller.rest.models.resource.*; +import cz.cesnet.shongo.controller.rest.models.resource.ReservationModel; +import cz.cesnet.shongo.controller.rest.models.resource.ResourceCapacity; +import cz.cesnet.shongo.controller.rest.models.resource.ResourceCapacityUtilization; +import cz.cesnet.shongo.controller.rest.models.resource.ResourceModel; +import cz.cesnet.shongo.controller.rest.models.resource.ResourceUtilizationDetailModel; +import cz.cesnet.shongo.controller.rest.models.resource.ResourceUtilizationModel; +import cz.cesnet.shongo.controller.rest.models.resource.ResourcesUtilization; +import cz.cesnet.shongo.controller.rest.models.resource.Unit; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -18,7 +25,12 @@ import org.joda.time.Interval; import org.joda.time.Period; import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.Collections; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java index e991ee168..bc1e5f4b7 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java @@ -12,7 +12,12 @@ import cz.cesnet.shongo.controller.rest.models.room.RoomModel; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import java.util.stream.Collectors; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java index d24452844..66931bb01 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java @@ -18,9 +18,18 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; -import java.util.*; +import java.util.Collections; +import java.util.List; import java.util.stream.Collectors; import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; @@ -109,7 +118,7 @@ void modifyRuntimeParticipant( String name = body.getName(); Boolean microphoneEnabled = body.getMicrophoneEnabled(); Integer microphoneLevel = body.getMicrophoneLevel(); - Boolean videoEnabled = body.getVideoEnabled(); + Boolean videoEnabled = body.getVideoEnabled(); RoomParticipant roomParticipant = new RoomParticipant(participantId); if (name != null) { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java index e8cb2ed86..1afdeccbf 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java @@ -14,7 +14,14 @@ import cz.cesnet.shongo.controller.rest.models.users.SettingsModel; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import java.util.List; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java index 3a18682d8..8d0859c80 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java @@ -14,7 +14,16 @@ import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.stream.Collectors; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java index 34bb075c4..c24c9b47b 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/participant/ParticipantModel.java @@ -3,7 +3,11 @@ import cz.cesnet.shongo.ParticipantRole; import cz.cesnet.shongo.TodoImplementException; import cz.cesnet.shongo.api.UserInformation; -import cz.cesnet.shongo.controller.api.*; +import cz.cesnet.shongo.controller.api.AbstractParticipant; +import cz.cesnet.shongo.controller.api.AbstractPerson; +import cz.cesnet.shongo.controller.api.AnonymousPerson; +import cz.cesnet.shongo.controller.api.PersonParticipant; +import cz.cesnet.shongo.controller.api.UserPerson; import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import cz.cesnet.shongo.controller.rest.models.CommonModel; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java index e550c1111..ed54694fc 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java @@ -8,12 +8,39 @@ import cz.cesnet.shongo.ParticipantRole; import cz.cesnet.shongo.Temporal; import cz.cesnet.shongo.TodoImplementException; -import cz.cesnet.shongo.api.*; +import cz.cesnet.shongo.api.AdobeConnectPermissions; +import cz.cesnet.shongo.api.AdobeConnectRoomSetting; +import cz.cesnet.shongo.api.FreePBXRoomSetting; +import cz.cesnet.shongo.api.H323RoomSetting; +import cz.cesnet.shongo.api.PexipRoomSetting; +import cz.cesnet.shongo.api.RoomSetting; +import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.ObjectPermission; import cz.cesnet.shongo.controller.ObjectRole; import cz.cesnet.shongo.controller.ReservationRequestPurpose; import cz.cesnet.shongo.controller.ReservationRequestReusement; -import cz.cesnet.shongo.controller.api.*; +import cz.cesnet.shongo.controller.api.AbstractParticipant; +import cz.cesnet.shongo.controller.api.AbstractReservationRequest; +import cz.cesnet.shongo.controller.api.AbstractRoomExecutable; +import cz.cesnet.shongo.controller.api.AclEntry; +import cz.cesnet.shongo.controller.api.AliasSpecification; +import cz.cesnet.shongo.controller.api.ExecutableServiceSpecification; +import cz.cesnet.shongo.controller.api.ExecutableState; +import cz.cesnet.shongo.controller.api.PeriodicDateTimeSlot; +import cz.cesnet.shongo.controller.api.RecordingServiceSpecification; +import cz.cesnet.shongo.controller.api.Reservation; +import cz.cesnet.shongo.controller.api.ReservationRequest; +import cz.cesnet.shongo.controller.api.ReservationRequestSet; +import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.api.ReservationRequestType; +import cz.cesnet.shongo.controller.api.ResourceSpecification; +import cz.cesnet.shongo.controller.api.ResourceSummary; +import cz.cesnet.shongo.controller.api.RoomAvailability; +import cz.cesnet.shongo.controller.api.RoomEstablishment; +import cz.cesnet.shongo.controller.api.RoomExecutableParticipantConfiguration; +import cz.cesnet.shongo.controller.api.RoomSpecification; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.Specification; import cz.cesnet.shongo.controller.api.request.AclEntryListRequest; import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.request.ReservationRequestListRequest; @@ -30,9 +57,27 @@ import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; -import org.joda.time.*; - -import java.util.*; +import org.joda.time.DateTime; +import org.joda.time.DateTimeField; +import org.joda.time.DateTimeFieldType; +import org.joda.time.DateTimeZone; +import org.joda.time.Duration; +import org.joda.time.Interval; +import org.joda.time.LocalDate; +import org.joda.time.LocalDateTime; +import org.joda.time.LocalTime; +import org.joda.time.Partial; +import org.joda.time.Period; +import org.joda.time.ReadablePartial; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import static cz.cesnet.shongo.controller.rest.models.TimeInterval.ISO_8601_PATTERN; @@ -47,9 +92,6 @@ public class ReservationRequestCreateModel { - @JsonIgnore - private CacheProvider cacheProvider; - protected String id; protected String parentReservationRequestId; @@ -129,6 +171,9 @@ public class ReservationRequestCreateModel private TimeInterval slot; + @JsonIgnore + private CacheProvider cacheProvider; + /** * Create new {@link ReservationRequestModel} from scratch. */ @@ -267,8 +312,6 @@ public void setCollidingInterval(Interval collidingInterval) collidingWithFirstSlot = SlotHelper.areIntervalsColliding(start, 0, 0, this.durationCount, collidingInterval); break; - default: - return; } } else { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilization.java index 11519fc52..51b41855a 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacityUtilization.java @@ -3,7 +3,13 @@ import cz.cesnet.shongo.controller.api.ReservationSummary; import org.joda.time.Interval; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; /** * Represents a utilization of {@link ResourceCapacity} for a specific interval. diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java index 40ee6d06b..9a3fe237e 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java @@ -1,7 +1,13 @@ package cz.cesnet.shongo.controller.rest.models.resource; import cz.cesnet.shongo.TodoImplementException; -import cz.cesnet.shongo.controller.api.*; +import cz.cesnet.shongo.controller.api.Capability; +import cz.cesnet.shongo.controller.api.RecordingCapability; +import cz.cesnet.shongo.controller.api.ReservationSummary; +import cz.cesnet.shongo.controller.api.Resource; +import cz.cesnet.shongo.controller.api.ResourceSummary; +import cz.cesnet.shongo.controller.api.RoomProviderCapability; +import cz.cesnet.shongo.controller.api.SecurityToken; import cz.cesnet.shongo.controller.api.request.ReservationListRequest; import cz.cesnet.shongo.controller.api.request.ResourceListRequest; import cz.cesnet.shongo.controller.api.rpc.ReservationService; @@ -12,7 +18,13 @@ import org.joda.time.Interval; import org.joda.time.Period; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; /** * Represents utilization of all types of capacities for all resources to which a single user has access. From a15d5cd020ccdebaa1f0bbdbbfe27227e3b35114 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 26 May 2023 16:23:33 +0200 Subject: [PATCH 057/134] Rename ClientWebUrl to RestApiPath --- .../rest/{ClientWebUrl.java => RestApiPath.java} | 4 ++-- .../rest/controllers/ParticipantController.java | 8 ++++---- .../rest/controllers/RecordingController.java | 6 +++--- .../rest/controllers/ReportController.java | 4 ++-- .../ReservationRequestController.java | 16 ++++++++-------- .../rest/controllers/ResourceController.java | 8 ++++---- .../rest/controllers/RoomController.java | 6 +++--- .../rest/controllers/RuntimeController.java | 16 ++++++++-------- .../rest/controllers/UserController.java | 16 ++++++++-------- .../rest/controllers/UserRoleController.java | 6 +++--- 10 files changed, 45 insertions(+), 45 deletions(-) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{ClientWebUrl.java => RestApiPath.java} (97%) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java similarity index 97% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java index 39720d3d8..d53755f2f 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ClientWebUrl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java @@ -1,11 +1,11 @@ package cz.cesnet.shongo.controller.rest; /** - * Web URL paths for REST endpoints. + * URL paths for REST endpoints. * * @author Filip Karnis */ -public class ClientWebUrl +public class RestApiPath { public static final String API_PREFIX = "/api/v1"; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java index 84a7fd7de..4ddfde6ba 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java @@ -13,7 +13,7 @@ import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; -import cz.cesnet.shongo.controller.rest.ClientWebUrl; +import cz.cesnet.shongo.controller.rest.RestApiPath; import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import cz.cesnet.shongo.controller.rest.models.participant.ParticipantConfigurationModel; import cz.cesnet.shongo.controller.rest.models.participant.ParticipantModel; @@ -45,7 +45,7 @@ * @author Filip Karnis */ @RestController -@RequestMapping(ClientWebUrl.PARTICIPANTS) +@RequestMapping(RestApiPath.PARTICIPANTS) @RequiredArgsConstructor public class ParticipantController { @@ -126,7 +126,7 @@ void addParticipant( } @Operation(summary = "Adds new participant to reservation request.") - @PutMapping(ClientWebUrl.PARTICIPANTS_ID_SUFFIX) + @PutMapping(RestApiPath.PARTICIPANTS_ID_SUFFIX) void updateParticipant( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -150,7 +150,7 @@ void updateParticipant( } @Operation(summary = "Removes participant from reservation request.") - @DeleteMapping(ClientWebUrl.PARTICIPANTS_ID_SUFFIX) + @DeleteMapping(RestApiPath.PARTICIPANTS_ID_SUFFIX) void removeParticipant( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java index 2b68cd57a..e5a8a0d34 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java @@ -7,7 +7,7 @@ import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.api.rpc.ResourceControlService; import cz.cesnet.shongo.controller.rest.Cache; -import cz.cesnet.shongo.controller.rest.ClientWebUrl; +import cz.cesnet.shongo.controller.rest.RestApiPath; import cz.cesnet.shongo.controller.rest.models.recording.RecordingModel; import cz.cesnet.shongo.controller.scheduler.SchedulerReportSet; import io.swagger.v3.oas.annotations.Operation; @@ -31,7 +31,7 @@ * @author Filip Karnis */ @RestController -@RequestMapping(ClientWebUrl.RECORDINGS) +@RequestMapping(RestApiPath.RECORDINGS) @RequiredArgsConstructor public class RecordingController { @@ -71,7 +71,7 @@ ListResponse listRequestRecordings( } @Operation(summary = "Deletes recording from reservation request.") - @DeleteMapping(ClientWebUrl.RECORDINGS_ID_SUFFIX) + @DeleteMapping(RestApiPath.RECORDINGS_ID_SUFFIX) void deleteRequestRecording( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java index 5f7ea3de4..5a62faafc 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReportController.java @@ -1,7 +1,7 @@ package cz.cesnet.shongo.controller.rest.controllers; -import cz.cesnet.shongo.controller.rest.ClientWebUrl; import cz.cesnet.shongo.controller.rest.ErrorHandler; +import cz.cesnet.shongo.controller.rest.RestApiPath; import cz.cesnet.shongo.controller.rest.models.report.ReportModel; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirements; @@ -22,7 +22,7 @@ @Slf4j @SecurityRequirements @RestController -@RequestMapping(ClientWebUrl.REPORT) +@RequestMapping(RestApiPath.REPORT) @RequiredArgsConstructor public class ReportController { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index ecbe7110d..e54ee8bfa 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -17,7 +17,7 @@ import cz.cesnet.shongo.controller.api.rpc.ReservationService; import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; -import cz.cesnet.shongo.controller.rest.ClientWebUrl; +import cz.cesnet.shongo.controller.rest.RestApiPath; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestCreateModel; import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestDetailModel; @@ -59,7 +59,7 @@ * @author Filip Karnis */ @RestController -@RequestMapping(ClientWebUrl.RESERVATION_REQUESTS) +@RequestMapping(RestApiPath.RESERVATION_REQUESTS) @RequiredArgsConstructor public class ReservationRequestController { @@ -244,7 +244,7 @@ void createRequest( } @Operation(summary = "Returns reservation request.") - @GetMapping(ClientWebUrl.ID_SUFFIX) + @GetMapping(RestApiPath.ID_SUFFIX) ReservationRequestDetailModel getRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id) @@ -315,7 +315,7 @@ ReservationRequestDetailModel getRequest( } @Operation(summary = "Modifies reservation request.") - @PatchMapping(ClientWebUrl.ID_SUFFIX) + @PatchMapping(RestApiPath.ID_SUFFIX) void modifyRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -356,7 +356,7 @@ void modifyRequest( } @Operation(summary = "Deletes reservation request.") - @DeleteMapping(ClientWebUrl.ID_SUFFIX) + @DeleteMapping(RestApiPath.ID_SUFFIX) void deleteRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id) @@ -374,7 +374,7 @@ void deleteRequests( } @Operation(summary = "Accepts reservation request.") - @PostMapping(ClientWebUrl.RESERVATION_REQUESTS_ACCEPT) + @PostMapping(RestApiPath.RESERVATION_REQUESTS_ACCEPT) void acceptRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id) @@ -383,7 +383,7 @@ void acceptRequest( } @Operation(summary = "Rejects reservation request.") - @PostMapping(ClientWebUrl.RESERVATION_REQUESTS_REJECT) + @PostMapping(RestApiPath.RESERVATION_REQUESTS_REJECT) void rejectRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -393,7 +393,7 @@ void rejectRequest( } @Operation(summary = "Reverts reservation request modifications.") - @PostMapping(ClientWebUrl.RESERVATION_REQUESTS_REVERT) + @PostMapping(RestApiPath.RESERVATION_REQUESTS_REVERT) void revertRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java index 78924707e..9eef3c1f6 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java @@ -8,7 +8,7 @@ import cz.cesnet.shongo.controller.api.request.ResourceListRequest; import cz.cesnet.shongo.controller.api.rpc.ResourceService; import cz.cesnet.shongo.controller.rest.Cache; -import cz.cesnet.shongo.controller.rest.ClientWebUrl; +import cz.cesnet.shongo.controller.rest.RestApiPath; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; import cz.cesnet.shongo.controller.rest.models.resource.ReservationModel; import cz.cesnet.shongo.controller.rest.models.resource.ResourceCapacity; @@ -47,7 +47,7 @@ */ @Slf4j @RestController -@RequestMapping(ClientWebUrl.RESOURCES) +@RequestMapping(RestApiPath.RESOURCES) @RequiredArgsConstructor public class ResourceController { @@ -95,7 +95,7 @@ List listResources( * Gets {@link ResourceCapacityUtilization}s. */ @Operation(summary = "Returns resource utilization.") - @GetMapping(ClientWebUrl.CAPACITY_UTILIZATION) + @GetMapping(RestApiPath.CAPACITY_UTILIZATION) ListResponse listResourcesUtilization( @RequestAttribute(TOKEN) SecurityToken securityToken, @RequestParam(value = "interval_from") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) @@ -122,7 +122,7 @@ ListResponse listResourcesUtilization( * Lists {@link Resource}s. */ @Operation(summary = "Gets resource utilization.") - @GetMapping(ClientWebUrl.CAPACITY_UTILIZATION_DETAIL) + @GetMapping(RestApiPath.CAPACITY_UTILIZATION_DETAIL) ResourceUtilizationDetailModel getResourceUtilization( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable("id") String resourceId, diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java index bc1e5f4b7..c9841bd4e 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java @@ -7,7 +7,7 @@ import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.rest.Cache; -import cz.cesnet.shongo.controller.rest.ClientWebUrl; +import cz.cesnet.shongo.controller.rest.RestApiPath; import cz.cesnet.shongo.controller.rest.models.room.RoomAuthorizedData; import cz.cesnet.shongo.controller.rest.models.room.RoomModel; import io.swagger.v3.oas.annotations.Operation; @@ -29,7 +29,7 @@ * @author Filip Karnis */ @RestController -@RequestMapping(ClientWebUrl.ROOMS) +@RequestMapping(RestApiPath.ROOMS) @RequiredArgsConstructor public class RoomController { @@ -76,7 +76,7 @@ public ListResponse listRooms( } @Operation(summary = "Gets room's (executable's) authorized data.") - @GetMapping(ClientWebUrl.ID_SUFFIX) + @GetMapping(RestApiPath.ID_SUFFIX) public RoomAuthorizedData getRoom( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java index 66931bb01..11e5b25f5 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java @@ -8,7 +8,7 @@ import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; -import cz.cesnet.shongo.controller.rest.ClientWebUrl; +import cz.cesnet.shongo.controller.rest.RestApiPath; import cz.cesnet.shongo.controller.rest.RoomCache; import cz.cesnet.shongo.controller.rest.models.runtimemanagement.RuntimeParticipantModel; import io.swagger.v3.oas.annotations.Operation; @@ -41,7 +41,7 @@ */ @Slf4j @RestController -@RequestMapping(ClientWebUrl.RUNTIME_MANAGEMENT) +@RequestMapping(RestApiPath.RUNTIME_MANAGEMENT) @RequiredArgsConstructor public class RuntimeController { @@ -51,7 +51,7 @@ public class RuntimeController private final ExecutableService executableService; @Operation(summary = "Lists reservation request runtime participants.") - @GetMapping(ClientWebUrl.RUNTIME_MANAGEMENT_PARTICIPANTS) + @GetMapping(RestApiPath.RUNTIME_MANAGEMENT_PARTICIPANTS) ListResponse listRuntimeParticipants( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -76,7 +76,7 @@ ListResponse listRuntimeParticipants( } @Operation(summary = "Takes snapshot of reservation request runtime participant.") - @PostMapping(ClientWebUrl.RUNTIME_MANAGEMENT_PARTICIPANTS_SNAPSHOT) + @PostMapping(RestApiPath.RUNTIME_MANAGEMENT_PARTICIPANTS_SNAPSHOT) ResponseEntity snapshotRuntimeParticipant( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -101,7 +101,7 @@ ResponseEntity snapshotRuntimeParticipant( } @Operation(summary = "Modifies reservation request runtime participant.") - @PatchMapping(ClientWebUrl.RUNTIME_MANAGEMENT_PARTICIPANTS_MODIFY) + @PatchMapping(RestApiPath.RUNTIME_MANAGEMENT_PARTICIPANTS_MODIFY) void modifyRuntimeParticipant( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -149,7 +149,7 @@ void modifyRuntimeParticipant( } @Operation(summary = "Disconnects reservation request runtime participant.") - @PostMapping(ClientWebUrl.RUNTIME_MANAGEMENT_PARTICIPANTS_DISCONNECT) + @PostMapping(RestApiPath.RUNTIME_MANAGEMENT_PARTICIPANTS_DISCONNECT) void disconnectRuntimeParticipant( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -160,7 +160,7 @@ void disconnectRuntimeParticipant( } @Operation(summary = "Starts recording of reservation request runtime.") - @PostMapping(ClientWebUrl.RUNTIME_MANAGEMENT_RECORDING_START) + @PostMapping(RestApiPath.RUNTIME_MANAGEMENT_RECORDING_START) void startRequestRecording( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @@ -194,7 +194,7 @@ void startRequestRecording( } @Operation(summary = "Stops recording of reservation request runtime.") - @PostMapping(ClientWebUrl.RUNTIME_MANAGEMENT_RECORDING_STOP) + @PostMapping(RestApiPath.RUNTIME_MANAGEMENT_RECORDING_STOP) void stopRequestRecording( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java index 1afdeccbf..c7684a5f1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java @@ -10,7 +10,7 @@ import cz.cesnet.shongo.controller.api.request.UserListRequest; import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; import cz.cesnet.shongo.controller.rest.Cache; -import cz.cesnet.shongo.controller.rest.ClientWebUrl; +import cz.cesnet.shongo.controller.rest.RestApiPath; import cz.cesnet.shongo.controller.rest.models.users.SettingsModel; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; @@ -33,7 +33,7 @@ * @author Filip Karnis */ @RestController -@RequestMapping(ClientWebUrl.USERS_AND_GROUPS) +@RequestMapping(RestApiPath.USERS_AND_GROUPS) @RequiredArgsConstructor public class UserController { @@ -48,7 +48,7 @@ public class UserController * @return list of {@link UserInformation}s */ @Operation(summary = "Lists users.") - @GetMapping(ClientWebUrl.USERS_LIST) + @GetMapping(RestApiPath.USERS_LIST) public ListResponse getUsers( @RequestAttribute(TOKEN) SecurityToken securityToken, @RequestParam(value = "filter", required = false) String filter, @@ -71,7 +71,7 @@ public ListResponse getUsers( * @return {@link UserInformation} */ @Operation(summary = "Returns information about user.") - @GetMapping(ClientWebUrl.USERS_DETAIL) + @GetMapping(RestApiPath.USERS_DETAIL) public UserInformation getUser( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String userId) @@ -85,7 +85,7 @@ public UserInformation getUser( * @return {@link SettingsModel} */ @Operation(summary = "Returns user's settings.") - @GetMapping(ClientWebUrl.SETTINGS) + @GetMapping(RestApiPath.SETTINGS) public SettingsModel getUserSettings(@RequestAttribute(TOKEN) SecurityToken securityToken) { UserSettings settings = authorizationService.getUserSettings(securityToken); @@ -101,7 +101,7 @@ public SettingsModel getUserSettings(@RequestAttribute(TOKEN) SecurityToken secu * @param newSettings new settings of user */ @Operation(summary = "Updates user's settings.") - @PutMapping(ClientWebUrl.SETTINGS) + @PutMapping(RestApiPath.SETTINGS) public SettingsModel updateUserSettings( @RequestAttribute(TOKEN) SecurityToken securityToken, @RequestBody UserSettings newSettings) @@ -130,7 +130,7 @@ public SettingsModel updateUserSettings( * @return list of {@link Group}s */ @Operation(summary = "Lists groups.") - @GetMapping(ClientWebUrl.GROUPS_LIST) + @GetMapping(RestApiPath.GROUPS_LIST) public ListResponse getGroups( @RequestAttribute(TOKEN) SecurityToken securityToken, @RequestParam(value = "filter", required = false) String filter) @@ -149,7 +149,7 @@ public ListResponse getGroups( * @return {@link Group} */ @Operation(summary = "Returns information about group.") - @GetMapping(ClientWebUrl.GROUPS_DETAIL) + @GetMapping(RestApiPath.GROUPS_DETAIL) public Group getGroup( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String groupId) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java index 8d0859c80..82a45f7de 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java @@ -8,7 +8,7 @@ import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; -import cz.cesnet.shongo.controller.rest.ClientWebUrl; +import cz.cesnet.shongo.controller.rest.RestApiPath; import cz.cesnet.shongo.controller.rest.error.LastOwnerRoleNotDeletableException; import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; import io.swagger.v3.oas.annotations.Operation; @@ -36,7 +36,7 @@ * @author Filip Karnis */ @RestController -@RequestMapping(ClientWebUrl.ROLES) +@RequestMapping(RestApiPath.ROLES) @RequiredArgsConstructor public class UserRoleController { @@ -82,7 +82,7 @@ void createRequestRoles( } @Operation(summary = "Deletes role for reservation request.") - @DeleteMapping(ClientWebUrl.ENTITY_SUFFIX) + @DeleteMapping(RestApiPath.ENTITY_SUFFIX) void deleteRequestRoles( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, From 70168c2261df8a74940505451822539d4a3dc4f2 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 26 May 2023 16:42:48 +0200 Subject: [PATCH 058/134] Move rest bean configuration from xml file to annotations --- .../cesnet/shongo/controller/rest/Cache.java | 2 + .../shongo/controller/rest/ErrorHandler.java | 10 ++-- .../shongo/controller/rest/RESTApiServer.java | 45 +++++++-------- .../shongo/controller/rest/RoomCache.java | 2 + .../rest/config/ControllerConfig.java | 30 ++++++++++ .../controller/rest/config/ServiceConfig.java | 49 ++++++++++++++++ .../rest/config/security/SecurityConfig.java | 8 ++- .../resources/WEB-INF/rest-api-servlet.xml | 56 ------------------- 8 files changed, 112 insertions(+), 90 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/ControllerConfig.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/ServiceConfig.java delete mode 100644 shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java index 3ec0e2c11..d958adb28 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java @@ -32,6 +32,7 @@ import org.joda.time.DateTime; import org.joda.time.Duration; import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; import java.util.Collection; import java.util.HashMap; @@ -49,6 +50,7 @@ * @author Martin Srom */ @Slf4j +@Service @RequiredArgsConstructor public class Cache { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java index f6633dce1..195e982a4 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/ErrorHandler.java @@ -3,7 +3,9 @@ import cz.cesnet.shongo.controller.Controller; import cz.cesnet.shongo.controller.ControllerConfiguration; import cz.cesnet.shongo.controller.EmailSender; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; import javax.mail.MessagingException; import java.util.Collection; @@ -15,18 +17,14 @@ * @author Filip Karnis */ @Slf4j +@Service +@RequiredArgsConstructor public class ErrorHandler { private final Controller controller; private final ControllerConfiguration configuration; - public ErrorHandler() - { - this.controller = Controller.getInstance(); - this.configuration = controller.getConfiguration(); - } - /** * Send email to administrators. * diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java index f12c524f1..5cc9c7450 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RESTApiServer.java @@ -1,7 +1,6 @@ package cz.cesnet.shongo.controller.rest; import com.google.common.base.Strings; -import cz.cesnet.shongo.controller.Controller; import cz.cesnet.shongo.controller.ControllerConfiguration; import cz.cesnet.shongo.controller.domains.BasicAuthFilter; import cz.cesnet.shongo.controller.domains.SSLClientCertFilter; @@ -16,15 +15,16 @@ import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.eclipse.jetty.webapp.WebAppContext; +import org.springframework.web.context.ContextLoaderListener; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.filter.DelegatingFilterProxy; import org.springframework.web.servlet.DispatcherServlet; import javax.servlet.DispatcherType; import java.io.IOException; -import java.net.URL; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; @@ -42,21 +42,23 @@ public static Server start(ControllerConfiguration configuration) { Server restServer = new Server(); ConfiguredSSLContext.getInstance().loadConfiguration(configuration); - String resourceBase = RESTApiServer.getResourceBase(); - WebAppContext webAppContext = new WebAppContext(); - webAppContext.addServlet(new ServletHolder(SERVLET_NAME, DispatcherServlet.class), SERVLET_PATH); - webAppContext.setResourceBase(resourceBase); - webAppContext.setParentLoaderPriority(true); - webAppContext.addFilter( - new FilterHolder(new DelegatingFilterProxy("springSecurityFilterChain")), - "/*", EnumSet.allOf(DispatcherType.class) - ); + AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); + context.scan("cz.cesnet.shongo.controller"); - ServerConnector httpsConnector = createHTTPConnector(configuration, webAppContext, restServer); + ServletContextHandler servletContextHandler = new ServletContextHandler(); + servletContextHandler.addEventListener(new ContextLoaderListener(context)); + + ServletHolder servletHolder = new ServletHolder(SERVLET_NAME, new DispatcherServlet(context)); + servletContextHandler.addServlet(servletHolder, SERVLET_PATH); + + FilterHolder springSecurityFilter = new FilterHolder(new DelegatingFilterProxy("springSecurityFilterChain")); + servletContextHandler.addFilter(springSecurityFilter, "/*", EnumSet.allOf(DispatcherType.class)); + + ServerConnector httpsConnector = createHTTPConnector(configuration, servletContextHandler, restServer); restServer.setConnectors(new Connector[]{httpsConnector}); - restServer.setHandler(webAppContext); + restServer.setHandler(servletContextHandler); try { restServer.start(); @@ -68,18 +70,9 @@ public static Server start(ControllerConfiguration configuration) return restServer; } - private static String getResourceBase() - { - URL resourceBaseUrl = Controller.class.getClassLoader().getResource("WEB-INF"); - if (resourceBaseUrl == null) { - throw new RuntimeException("WEB-INF is not in classpath."); - } - return resourceBaseUrl.toExternalForm().replace("/WEB-INF", "/"); - } - private static ServerConnector createHTTPConnector( ControllerConfiguration configuration, - WebAppContext webAppContext, + ServletContextHandler contextHandler, Server server) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { @@ -118,11 +111,11 @@ private static ServerConnector createHTTPConnector( sslContextFactory.setNeedClientAuth(true); // Enable SSL client filter by certificates EnumSet filterTypes = EnumSet.of(DispatcherType.REQUEST); - webAppContext.addFilter(SSLClientCertFilter.class, INTER_DOMAIN_API_PATH, filterTypes); + contextHandler.addFilter(SSLClientCertFilter.class, INTER_DOMAIN_API_PATH, filterTypes); } else { EnumSet filterTypes = EnumSet.of(DispatcherType.REQUEST); - webAppContext.addFilter(BasicAuthFilter.class, INTER_DOMAIN_API_PATH, filterTypes); + contextHandler.addFilter(BasicAuthFilter.class, INTER_DOMAIN_API_PATH, filterTypes); } } serverConnector = new ServerConnector(server, diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java index c16505f46..cc27040df 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java @@ -12,6 +12,7 @@ import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import lombok.RequiredArgsConstructor; import org.joda.time.Duration; +import org.springframework.stereotype.Service; import java.util.HashSet; import java.util.LinkedList; @@ -24,6 +25,7 @@ * * @author Martin Srom */ +@Service @RequiredArgsConstructor public class RoomCache { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/ControllerConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/ControllerConfig.java new file mode 100644 index 000000000..98cfac95f --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/ControllerConfig.java @@ -0,0 +1,30 @@ +package cz.cesnet.shongo.controller.rest.config; + +import cz.cesnet.shongo.controller.Controller; +import cz.cesnet.shongo.controller.ControllerClient; +import cz.cesnet.shongo.controller.ControllerConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ControllerConfig +{ + + @Bean + public Controller controller() + { + return Controller.getInstance(); + } + + @Bean + public ControllerConfiguration configuration() + { + return controller().getConfiguration(); + } + + @Bean + public ControllerClient controllerClient() throws Exception + { + return new ControllerClient(configuration().getRpcUrl()); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/ServiceConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/ServiceConfig.java new file mode 100644 index 000000000..2dcf7719f --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/ServiceConfig.java @@ -0,0 +1,49 @@ +package cz.cesnet.shongo.controller.rest.config; + +import cz.cesnet.shongo.controller.ControllerClient; +import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; +import cz.cesnet.shongo.controller.api.rpc.ExecutableService; +import cz.cesnet.shongo.controller.api.rpc.ReservationService; +import cz.cesnet.shongo.controller.api.rpc.ResourceControlService; +import cz.cesnet.shongo.controller.api.rpc.ResourceService; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@RequiredArgsConstructor +public class ServiceConfig +{ + + private final ControllerClient controllerClient; + + @Bean + public ReservationService reservationService() + { + return controllerClient.getService(ReservationService.class); + } + + @Bean + public AuthorizationService authorizationService() + { + return controllerClient.getService(AuthorizationService.class); + } + + @Bean + public ExecutableService executableService() + { + return controllerClient.getService(ExecutableService.class); + } + + @Bean + public ResourceService resourceService() + { + return controllerClient.getService(ResourceService.class); + } + + @Bean + public ResourceControlService resourceControlService() + { + return controllerClient.getService(ResourceControlService.class); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java index fa69e0124..2629167f1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java @@ -1,6 +1,7 @@ package cz.cesnet.shongo.controller.rest.config.security; -import cz.cesnet.shongo.controller.Controller; +import cz.cesnet.shongo.controller.ControllerConfiguration; +import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -21,9 +22,12 @@ */ @Configuration @EnableWebSecurity +@RequiredArgsConstructor public class SecurityConfig extends WebSecurityConfigurerAdapter { + private final ControllerConfiguration configuration; + @Override public void configure(WebSecurity web) { @@ -60,6 +64,6 @@ CorsConfigurationSource corsConfigurationSource() private List allowedOrigins() { - return Controller.getInstance().getConfiguration().getRESTApiAllowedOrigins(); + return configuration.getRESTApiAllowedOrigins(); } } diff --git a/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml b/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml deleted file mode 100644 index c8b94190f..000000000 --- a/shongo-controller/src/main/resources/WEB-INF/rest-api-servlet.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 063e8eb0dbafe1f4313ca8165dac6002acf38b2d Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 25 Aug 2023 15:46:00 +0200 Subject: [PATCH 059/134] add RECORDING_SERVICE to ResourceSummary type --- .../java/cz/cesnet/shongo/controller/api/ResourceSummary.java | 1 + 1 file changed, 1 insertion(+) diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java index 9d77ccd3e..8452a6431 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java @@ -270,6 +270,7 @@ public void setAllocationOrder(Integer allocationOrder) public enum Type { ROOM_PROVIDER, + RECORDING_SERVICE, RESOURCE, } From 1a0765e65cbe6a513190533bc4b529de27babad6 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 14 Jun 2023 20:29:14 +0200 Subject: [PATCH 060/134] WIP: domain interceptor --- .../controller/rest/config/WebMvcConfig.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/WebMvcConfig.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/WebMvcConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/WebMvcConfig.java new file mode 100644 index 000000000..4ab57ff59 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/WebMvcConfig.java @@ -0,0 +1,18 @@ +package cz.cesnet.shongo.controller.rest.config; + +import cz.cesnet.shongo.controller.domains.InterDomainControllerLogInterceptor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebMvcConfig implements WebMvcConfigurer +{ + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry + .addInterceptor(new InterDomainControllerLogInterceptor()) + .addPathPatterns("/domain/**"); + } +} From 355e25c81aebeb9c616c9ea93b9a3275cb72fd42 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 31 Aug 2023 14:32:21 +0200 Subject: [PATCH 061/134] fix recording endpoints --- .../shongo/controller/rest/controllers/RuntimeController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java index 11e5b25f5..6477d8275 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java @@ -164,9 +164,9 @@ void disconnectRuntimeParticipant( void startRequestRecording( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, - @RequestParam(value = "executableId") String executableId, @RequestParam(value = "executableServiceId") String executableServiceId) { + String executableId = cache.getExecutableId(securityToken, id); Object result = null; try { result = executableService.activateExecutableService(securityToken, executableId, executableServiceId); @@ -198,9 +198,9 @@ void startRequestRecording( void stopRequestRecording( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, - @RequestParam(value = "executableId") String executableId, @RequestParam(value = "executableServiceId") String executableServiceId) { + String executableId = cache.getExecutableId(securityToken, id); Object result = null; try { result = executableService.deactivateExecutableService(securityToken, executableId, executableServiceId); From 53bf581cee1274c1c81afc471b946ed06ae10205 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 8 Sep 2023 11:44:54 +0200 Subject: [PATCH 062/134] fix recording option for reservation request --- .../rest/controllers/ReservationRequestController.java | 2 +- .../ReservationRequestCreateModel.java | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index e54ee8bfa..a15181dd4 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -350,7 +350,7 @@ void modifyRequest( modifiedRequest.setParticipantCount(request.getParticipantCount()); } modifiedRequest.setAllowGuests(request.isAllowGuests()); - modifiedRequest.setRoomRecorded(request.isRoomRecorded()); + modifiedRequest.setRecord(request.isRecord()); reservationService.modifyReservationRequest(securityToken, modifiedRequest.toApi()); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java index ed54694fc..174a51437 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java @@ -144,7 +144,7 @@ public class ReservationRequestCreateModel protected String guestPin; - protected boolean roomRecorded; + protected boolean record; protected String roomRecordingResourceId; @@ -584,7 +584,7 @@ public void fromSpecificationApi(Specification specification, CacheProvider cach roomMeetingDescription = roomAvailability.getMeetingDescription(); for (ExecutableServiceSpecification service : roomAvailability.getServiceSpecifications()) { if (service instanceof RecordingServiceSpecification) { - roomRecorded = service.isEnabled(); + record = service.isEnabled(); roomRecordingResourceId = service.getResourceId(); } } @@ -808,9 +808,8 @@ public Specification toSpecificationApi() roomAvailability.setParticipantNotificationEnabled(roomParticipantNotificationEnabled); roomAvailability.setMeetingName(roomMeetingName); roomAvailability.setMeetingDescription(roomMeetingDescription); - if (roomRecorded && !technology.equals(TechnologyModel.ADOBE_CONNECT)) { - roomAvailability.addServiceSpecification(RecordingServiceSpecification.forResource( - Strings.isNullOrEmpty(roomRecordingResourceId) ? null : roomRecordingResourceId, true)); + if (record && !TechnologyModel.ADOBE_CONNECT.equals(technology)) { + roomAvailability.addServiceSpecification(new RecordingServiceSpecification(true)); } specification = roomSpecification; break; From 26d8f9b6f17171593f1e7c00b4d6969078c3eb8c Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 22 Sep 2023 14:37:21 +0200 Subject: [PATCH 063/134] fix wrong data in roomCapacityModel --- .../controllers/ReservationRequestController.java | 12 +++++------- .../ReservationRequestDetailModel.java | 4 ++-- .../reservationrequest/ReservationRequestModel.java | 6 +++--- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index a15181dd4..6ba2dc91a 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -188,9 +188,8 @@ ListResponse listRequests( if (resource != null) { resourceSummary = cache.getResourceSummary(securityToken, resource); } - VirtualRoomModel virtualRoom = new VirtualRoomModel(item); return new ReservationRequestModel( - item, virtualRoom, permissionsByReservationRequestId, user, resourceSummary + item, item, permissionsByReservationRequestId, user, resourceSummary ); }).collect(Collectors.toList())); listResponse.setStart(response.getStart()); @@ -272,7 +271,7 @@ ReservationRequestDetailModel getRequest( if (resourceId != null) { resourceSummary = cacheProvider.getResourceSummary(resourceId); } - VirtualRoomModel virtualRoomData = new VirtualRoomModel(summary); + ReservationRequestSummary virtualRoomSummary = summary; List historySummaries = reservationService.getReservationRequestHistory( securityToken, id @@ -291,7 +290,7 @@ ReservationRequestDetailModel getRequest( } return new ReservationRequestModel( item, - new VirtualRoomModel(item), + item, permissionsByReservationHistory, user, resourceSum @@ -302,14 +301,13 @@ ReservationRequestDetailModel getRequest( // If the request is a ROOM_CAPACITY, then get virtual room data from the room String virtualRoomId = summary.getReusedReservationRequestId(); if (virtualRoomId != null) { - ReservationRequestSummary summaryVirtualRoom = cache.getReservationRequestSummary( + virtualRoomSummary = cache.getReservationRequestSummary( securityToken, virtualRoomId ); - virtualRoomData = new VirtualRoomModel(summaryVirtualRoom); } return new ReservationRequestDetailModel( - summary, virtualRoomData, permissionsByReservationRequestId, ownerInformation, authorizedData, history, + summary, virtualRoomSummary, permissionsByReservationRequestId, ownerInformation, authorizedData, history, resourceSummary ); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java index cd66501e5..3a3887823 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java @@ -34,14 +34,14 @@ public class ReservationRequestDetailModel extends ReservationRequestModel public ReservationRequestDetailModel( ReservationRequestSummary summary, - VirtualRoomModel virtualRoom, + ReservationRequestSummary virtualRoomSummary, Map> permissionsByReservationRequestId, UserInformation ownerInformation, RoomAuthorizedData authorizedData, List history, ResourceSummary resourceSummary) { - super(summary, virtualRoom, permissionsByReservationRequestId, ownerInformation, resourceSummary); + super(summary, virtualRoomSummary, permissionsByReservationRequestId, ownerInformation, resourceSummary); this.allocationState = summary.getAllocationState(); this.executableState = summary.getExecutableState(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java index 62121cd60..a65efe479 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java @@ -46,7 +46,7 @@ public class ReservationRequestModel public ReservationRequestModel( ReservationRequestSummary summary, - VirtualRoomModel virtualRoomData, + ReservationRequestSummary virtualRoomSummary, Map> permissionsByReservationRequestId, UserInformation ownerInformation, ResourceSummary resourceSummary) @@ -60,9 +60,9 @@ public ReservationRequestModel( this.ownerEmail = ownerInformation.getEmail(); this.slot = TimeInterval.fromApi(summary.getEarliestSlot()); this.type = SpecificationType.fromReservationRequestSummary(summary, true); - this.virtualRoomData = virtualRoomData; + this.virtualRoomData = new VirtualRoomModel(virtualRoomSummary); this.physicalResourceData = PhysicalResourceData.fromApi(resourceSummary); - this.roomCapacityData = new RoomCapacityModel(summary); + this.roomCapacityData = new RoomCapacityModel(virtualRoomSummary); this.lastReservationId = summary.getLastReservationId(); this.futureSlotCount = summary.getFutureSlotCount(); From c164cf445412972c779f3e9f8540cdbb3c429b7c Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 3 Nov 2023 18:32:33 +0100 Subject: [PATCH 064/134] [controller] add lombok dependency --- pom.xml | 10 ++++++++++ shongo-controller-api/pom.xml | 7 +++++++ shongo-controller/pom.xml | 7 +++++++ 3 files changed, 24 insertions(+) diff --git a/pom.xml b/pom.xml index 457a53d6f..1d92be6f9 100644 --- a/pom.xml +++ b/pom.xml @@ -97,6 +97,16 @@ + + + + org.projectlombok + lombok + 1.18.22 + + + + diff --git a/shongo-controller-api/pom.xml b/shongo-controller-api/pom.xml index bce341e6b..439072995 100644 --- a/shongo-controller-api/pom.xml +++ b/shongo-controller-api/pom.xml @@ -40,6 +40,13 @@ jackson-core 2.10.1 + + + + org.projectlombok + lombok + provided + diff --git a/shongo-controller/pom.xml b/shongo-controller/pom.xml index a3266ef6b..79cf72936 100644 --- a/shongo-controller/pom.xml +++ b/shongo-controller/pom.xml @@ -201,6 +201,13 @@ ical4j-zoneinfo-outlook 1.0.3 + + + + org.projectlombok + lombok + provided + From 63de60894c102803346ff06017ef80d85ab0239c Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 15 Feb 2023 21:34:52 +0100 Subject: [PATCH 065/134] [controller] tag can now hold additional data --- .../perl/Shongo/ClientCli/ResourceService.pm | 32 ++++++++- shongo-common/pom.xml | 7 ++ .../cesnet/shongo/hibernate/package-info.java | 4 +- .../cz/cesnet/shongo/controller/api/Tag.java | 36 ++++++++-- .../cesnet/shongo/controller/api/TagType.java | 23 +++++++ .../controller/booking/resource/Tag.java | 67 ++++++++++++++++++- .../controller/AbstractDatabaseTest.java | 2 +- .../controller/booking/resource/TagTest.java | 41 ++++++++++++ .../migrations/2023-02-15/migration.sql | 10 +++ 9 files changed, 210 insertions(+), 12 deletions(-) create mode 100644 shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/TagType.java create mode 100644 shongo-deployment/migrations/2023-02-15/migration.sql diff --git a/shongo-client-cli/src/main/perl/Shongo/ClientCli/ResourceService.pm b/shongo-client-cli/src/main/perl/Shongo/ClientCli/ResourceService.pm index 65f2efd25..5e8029439 100644 --- a/shongo-client-cli/src/main/perl/Shongo/ClientCli/ResourceService.pm +++ b/shongo-client-cli/src/main/perl/Shongo/ClientCli/ResourceService.pm @@ -15,6 +15,15 @@ use Shongo::ClientCli::API::Resource; use Shongo::ClientCli::API::DeviceResource; use Shongo::ClientCli::API::Alias; +# +# Tag types +# +our $TagType = ordered_hash( + 'DEFAULT' => 'Default', + 'NOTIFY_EMAIL' => 'Notify Email', + 'RESERVATION_DATA' => 'Reservation Data', +); + # # Populate shell by options for management of resources. # @@ -477,6 +486,19 @@ sub create_tag() 'title' => 'Tag name', } ); + $tag->add_attribute( + 'type', { + 'required' => 1, + 'title' => 'Tag type', + 'type' => 'enum', + 'enum' => $Shongo::ClientCli::ResourceService::TagType, + } + ); + $tag->add_attribute( + 'data', { + 'title' => 'Tag data', + } + ); my $id = $tag->create($attributes, $options); if ( defined($id) ) { @@ -514,13 +536,17 @@ sub list_tags() 'columns' => [ {'field' => 'id', 'title' => 'Identifier'}, {'field' => 'name', 'title' => 'Name'}, + {'field' => 'type', 'title' => 'Type'}, + {'field' => 'data', 'title' => 'Data'}, ], 'data' => [] }; - foreach my $resource (@{$response}) { + foreach my $tag (@{$response}) { push(@{$table->{'data'}}, { - 'id' => $resource->{'id'}, - 'name' => $resource->{'name'}, + 'id' => $tag->{'id'}, + 'name' => $tag->{'name'}, + 'type' => $tag->{'type'}, + 'data' => $tag->{'data'}, }); } console_print_table($table); diff --git a/shongo-common/pom.xml b/shongo-common/pom.xml index 8f5d00137..60713a072 100644 --- a/shongo-common/pom.xml +++ b/shongo-common/pom.xml @@ -53,6 +53,13 @@ + + + + io.hypersistence + hypersistence-utils-hibernate-52 + 3.2.0 + diff --git a/shongo-common/src/main/java/cz/cesnet/shongo/hibernate/package-info.java b/shongo-common/src/main/java/cz/cesnet/shongo/hibernate/package-info.java index 645097e3b..67d3ab6bd 100644 --- a/shongo-common/src/main/java/cz/cesnet/shongo/hibernate/package-info.java +++ b/shongo-common/src/main/java/cz/cesnet/shongo/hibernate/package-info.java @@ -11,9 +11,11 @@ @TypeDef(name = PersistentLocalDate.NAME, typeClass = PersistentLocalDate.class), @TypeDef(name = PersistentPeriod.NAME, typeClass = PersistentPeriod.class), @TypeDef(name = PersistentInterval.NAME, typeClass = PersistentInterval.class), - @TypeDef(name = PersistentReadablePartial.NAME, typeClass = PersistentReadablePartial.class) + @TypeDef(name = PersistentReadablePartial.NAME, typeClass = PersistentReadablePartial.class), + @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class), }) package cz.cesnet.shongo.hibernate; +import io.hypersistence.utils.hibernate.type.json.JsonBinaryType; import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDefs; diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/Tag.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/Tag.java index d7284b6cd..364116229 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/Tag.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/Tag.java @@ -3,6 +3,8 @@ import cz.cesnet.shongo.api.DataMap; import cz.cesnet.shongo.api.IdentifiedComplexType; +import java.util.Objects; + /** * * @author Ondřej Pavelka @@ -10,6 +12,8 @@ public class Tag extends IdentifiedComplexType { String name; + TagType type = TagType.DEFAULT; + String data; public String getName() { return name; @@ -19,13 +23,37 @@ public void setName(String name) { this.name = name; } + public TagType getType() + { + return type; + } + + public void setType(TagType type) + { + this.type = type; + } + + public String getData() + { + return data; + } + + public void setData(String data) + { + this.data = data; + } + private static final String NAME = "name"; + private static final String TYPE = "type"; + private static final String DATA = "data"; @Override public DataMap toData() { DataMap dataMap = super.toData(); dataMap.set(NAME,name); + dataMap.set(TYPE, type); + dataMap.set(DATA, data); return dataMap; } @@ -34,6 +62,8 @@ public void fromData(DataMap dataMap) { super.fromData(dataMap); name = dataMap.getString(NAME); + type = dataMap.getEnumRequired(TYPE, TagType.class); + data = dataMap.getString(DATA); } @Override @@ -43,14 +73,12 @@ public boolean equals(Object o) if (o == null || getClass() != o.getClass()) return false; Tag tag = (Tag) o; - - return name.equals(tag.name); - + return Objects.equals(name, tag.name) && type == tag.type && Objects.equals(data, tag.data); } @Override public int hashCode() { - return name.hashCode(); + return Objects.hash(name, type, data); } } diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/TagType.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/TagType.java new file mode 100644 index 000000000..03664d500 --- /dev/null +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/TagType.java @@ -0,0 +1,23 @@ +package cz.cesnet.shongo.controller.api; + +/** + * Type of {@link cz.cesnet.shongo.controller.booking.resource.Tag}. + */ +public enum TagType +{ + /** + * Simple tag. Does not do anything special. + */ + DEFAULT, + + /** + * Sends notifications to the email addresses specified in this {@link cz.cesnet.shongo.controller.booking.resource.Tag}. + */ + NOTIFY_EMAIL, + + /** + * Adds additional information specified in {@link cz.cesnet.shongo.controller.booking.resource.Tag} + * to {@link cz.cesnet.shongo.controller.booking.reservation.Reservation}. + */ + RESERVATION_DATA, +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/resource/Tag.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/resource/Tag.java index f3243b416..6df57e70d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/resource/Tag.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/resource/Tag.java @@ -1,28 +1,71 @@ package cz.cesnet.shongo.controller.booking.resource; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import cz.cesnet.shongo.SimplePersistentObject; import cz.cesnet.shongo.api.AbstractComplexType; +import cz.cesnet.shongo.controller.api.TagType; import cz.cesnet.shongo.controller.booking.ObjectIdentifier; +import org.hibernate.annotations.Type; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Transient; /** * @author: Ondřej Pavelka */ @Entity public class Tag extends SimplePersistentObject { + + @Transient + private final ObjectMapper objectMapper = new ObjectMapper(); + private String name; + private TagType type; + + private JsonNode data; + @Column(length = AbstractComplexType.DEFAULT_COLUMN_LENGTH, unique = true) - public String getName() { + public String getName() + { return name; } - public void setName(String name) { + public void setName(String name) + { this.name = name; } + @Column(nullable = false, length = AbstractComplexType.ENUM_COLUMN_LENGTH) + @Enumerated(EnumType.STRING) + public TagType getType() + { + return type; + } + + public void setType(TagType type) + { + this.type = type; + } + + // @Type and @Column both needed, because HSQLDB does not support JSONB type + @Type(type = "jsonb") + @Column(columnDefinition = "text") + public JsonNode getData() + { + return data; + } + + public void setData(JsonNode data) + { + this.data = data; + } + /** * @return tag converted capability to API */ @@ -37,6 +80,17 @@ public void toApi(cz.cesnet.shongo.controller.api.Tag tagApi) { tagApi.setId(ObjectIdentifier.formatId(this)); tagApi.setName(name); + tagApi.setType(type); + if (data == null) { + tagApi.setData(""); + } + else { + try { + tagApi.setData(objectMapper.writeValueAsString(data)); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Failed to parse data", e); + } + } } /** @@ -53,6 +107,13 @@ public static Tag createFromApi(cz.cesnet.shongo.controller.api.Tag tagApi) public void fromApi(cz.cesnet.shongo.controller.api.Tag tagApi) { this.setName(tagApi.getName()); + this.setType(tagApi.getType()); + if (tagApi.getData() != null) { + try { + setData(objectMapper.readTree(tagApi.getData())); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Data is not a valid JSON", e); + } + } } - } diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractDatabaseTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractDatabaseTest.java index d9dde912d..56470c926 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractDatabaseTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractDatabaseTest.java @@ -24,7 +24,7 @@ public abstract class AbstractDatabaseTest * Connection. */ protected static String connectionDriver = "org.hsqldb.jdbcDriver"; - protected static String connectionUrl = "jdbc:hsqldb:mem:test; shutdown=true;"; + protected static String connectionUrl = "jdbc:hsqldb:mem:test; shutdown=true; sql.syntax_pgs=true;"; /** * Enable driver for debugging SQL. diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/resource/TagTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/resource/TagTest.java index 788125d87..55dd073a2 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/resource/TagTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/resource/TagTest.java @@ -21,6 +21,47 @@ * @author: Ondřej Pavelka */ public class TagTest extends AbstractControllerTest { + + @Test + public void testCreateTag() + { + final String tagName1 = "testTag1"; + final String tagName2 = "testTag2"; + final TagType tagType2 = TagType.NOTIFY_EMAIL; + final String tagData2 = "[\"karnis@cesnet.cz\",\"filip.karnis@cesnet.cz\"]"; + + ResourceService resourceService = getResourceService(); + + // tag1 init + cz.cesnet.shongo.controller.api.Tag tag1 = new cz.cesnet.shongo.controller.api.Tag(); + tag1.setName(tagName1); + + // tag2 init + cz.cesnet.shongo.controller.api.Tag tag2 = new cz.cesnet.shongo.controller.api.Tag(); + tag2.setName(tagName2); + tag2.setType(tagType2); + tag2.setData(tagData2); + + String tagId1 = resourceService.createTag(SECURITY_TOKEN_ROOT, tag1); + String tagId2 = resourceService.createTag(SECURITY_TOKEN_ROOT, tag2); + + cz.cesnet.shongo.controller.api.Tag getResult1 = resourceService.getTag(SECURITY_TOKEN_ROOT, tagId1); + cz.cesnet.shongo.controller.api.Tag getResult2 = resourceService.getTag(SECURITY_TOKEN_ROOT, tagId2); + + Assert.assertNotNull(getResult1); + Assert.assertNotNull(getResult2); + + Assert.assertEquals(tagId1, getResult1.getId()); + Assert.assertEquals(tagName1, getResult1.getName()); + Assert.assertEquals(TagType.DEFAULT, getResult1.getType()); + Assert.assertEquals("", getResult1.getData()); + + Assert.assertEquals(tagId2, getResult2.getId()); + Assert.assertEquals(tagName2, getResult2.getName()); + Assert.assertEquals(tagType2, getResult2.getType()); + Assert.assertEquals(tagData2, getResult2.getData()); + } + @Test public void testCreateTagsAcl() throws Exception { diff --git a/shongo-deployment/migrations/2023-02-15/migration.sql b/shongo-deployment/migrations/2023-02-15/migration.sql new file mode 100644 index 000000000..40825533c --- /dev/null +++ b/shongo-deployment/migrations/2023-02-15/migration.sql @@ -0,0 +1,10 @@ +/** + * 2023-02-15: tag can now hold additional data + */ +BEGIN TRANSACTION; + +ALTER TABLE tag + ADD COLUMN type varchar(255) NOT NULL DEFAULT 'DEFAULT', + ADD COLUMN data jsonb; + +COMMIT TRANSACTION; From 40fe0096d87902dba0dce7b9268b22cec0ed69b0 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sat, 11 Feb 2023 16:41:04 +0100 Subject: [PATCH 066/134] [controller] add auxData to reservation request --- .../API/ReservationRequestAbstract.pm | 4 + .../Shongo/ClientCli/ReservationService.pm | 6 +- .../api/AbstractReservationRequest.java | 24 ++++++ .../request/AbstractReservationRequest.java | 29 +++++++ .../booking/request/auxdata/AuxData.java | 17 +++++ .../ReservationRequestModificationTest.java | 75 +++++++++++++++++++ .../migrations/2023-02-24/migration.sql | 17 +++++ 7 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxData.java create mode 100644 shongo-deployment/migrations/2023-02-24/migration.sql diff --git a/shongo-client-cli/src/main/perl/Shongo/ClientCli/API/ReservationRequestAbstract.pm b/shongo-client-cli/src/main/perl/Shongo/ClientCli/API/ReservationRequestAbstract.pm index 427f39823..ca4f178c9 100644 --- a/shongo-client-cli/src/main/perl/Shongo/ClientCli/API/ReservationRequestAbstract.pm +++ b/shongo-client-cli/src/main/perl/Shongo/ClientCli/API/ReservationRequestAbstract.pm @@ -89,6 +89,10 @@ sub new() 'OWNED' => 'Owned' ) }); + $self->add_attribute('auxData', { + 'title' => 'Auxiliary data', + 'optional' => 1, + }); return $self; } diff --git a/shongo-client-cli/src/main/perl/Shongo/ClientCli/ReservationService.pm b/shongo-client-cli/src/main/perl/Shongo/ClientCli/ReservationService.pm index d3e17620b..1fa47c09d 100644 --- a/shongo-client-cli/src/main/perl/Shongo/ClientCli/ReservationService.pm +++ b/shongo-client-cli/src/main/perl/Shongo/ClientCli/ReservationService.pm @@ -274,7 +274,8 @@ sub list_reservation_requests() {'field' => 'technology', 'title' => 'Technology'}, {'field' => 'allocationState', 'title' => 'Allocation'}, {'field' => 'executableState', 'title' => 'Executable'}, - {'field' => 'description', 'title' => 'Description'} + {'field' => 'description', 'title' => 'Description'}, + {'field' => 'auxData', 'title' => 'Auxiliary Data'}, ], 'data' => [] }; @@ -321,7 +322,8 @@ sub list_reservation_requests() 'technology' => $technologies, 'allocationState' => Shongo::ClientCli::API::ReservationRequest::format_state($reservation_request->{'allocationState'}), 'executableState' => Shongo::ClientCli::API::ReservationRequest::format_state($reservation_request->{'executableState'}), - 'description' => $reservation_request->{'description'} + 'description' => $reservation_request->{'description'}, + 'auxData' => $reservation_request->{'auxData'}, }); } console_print_table($table); diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AbstractReservationRequest.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AbstractReservationRequest.java index 3ecbde003..183c43e48 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AbstractReservationRequest.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AbstractReservationRequest.java @@ -75,6 +75,11 @@ public abstract class AbstractReservationRequest extends IdentifiedComplexType */ private boolean isSchedulerDeleted = false; + /** + * Auxiliary data. This data are specified by the {@link Tag}s of {@link Resource} which is requested for reservation. + */ + private String auxData; + /** * Constructor. */ @@ -291,6 +296,22 @@ public void setIsSchedulerDeleted(boolean isSchedulerDeleted) this.isSchedulerDeleted = isSchedulerDeleted; } + /** + * @return {@link #auxData} + */ + public String getAuxData() + { + return auxData; + } + + /** + * @param auxData sets the {@link #auxData} + */ + public void setAuxData(String auxData) + { + this.auxData = auxData; + } + private static final String TYPE = "type"; private static final String DATETIME = "dateTime"; private static final String USER_ID = "userId"; @@ -303,6 +324,7 @@ public void setIsSchedulerDeleted(boolean isSchedulerDeleted) private static final String REUSED_RESERVATION_REQUEST_MANDATORY = "reusedReservationRequestMandatory"; private static final String REUSEMENT = "reusement"; private static final String IS_SCHEDULER_DELETED = "isSchedulerDeleted"; + public static final String AUX_DATA = "auxData"; @Override public DataMap toData() @@ -320,6 +342,7 @@ public DataMap toData() dataMap.set(REUSED_RESERVATION_REQUEST_MANDATORY, reusedReservationRequestMandatory); dataMap.set(REUSEMENT, reusement); dataMap.set(IS_SCHEDULER_DELETED, isSchedulerDeleted); + dataMap.set(AUX_DATA, auxData); return dataMap; } @@ -339,5 +362,6 @@ public void fromData(DataMap dataMap) reusedReservationRequestMandatory = dataMap.getBool(REUSED_RESERVATION_REQUEST_MANDATORY); reusement = dataMap.getEnum(REUSEMENT, ReservationRequestReusement.class); isSchedulerDeleted = dataMap.getBool(IS_SCHEDULER_DELETED); + auxData = dataMap.getString(AUX_DATA); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequest.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequest.java index a26ff68e1..80f99a5af 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequest.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequest.java @@ -12,6 +12,8 @@ import cz.cesnet.shongo.controller.api.Controller; import cz.cesnet.shongo.controller.booking.Allocation; import cz.cesnet.shongo.controller.booking.ObjectIdentifier; +import cz.cesnet.shongo.controller.booking.resource.Resource; +import cz.cesnet.shongo.controller.booking.resource.Tag; import cz.cesnet.shongo.controller.booking.specification.Specification; import cz.cesnet.shongo.controller.scheduler.Scheduler; import cz.cesnet.shongo.controller.api.ReservationRequestType; @@ -20,6 +22,7 @@ import cz.cesnet.shongo.report.Report; import cz.cesnet.shongo.report.ReportableSimple; import cz.cesnet.shongo.util.ObjectHelper; +import org.hibernate.annotations.Type; import org.joda.time.DateTime; import org.joda.time.Period; @@ -115,6 +118,11 @@ public abstract class AbstractReservationRequest extends PersistentObject implem */ private ReservationRequestReusement reusement; + /** + * Auxiliary data. This data are specified by the {@link Tag}s of {@link Resource} which is requested for reservation. + */ + private String auxData; + @Id @SequenceGenerator(name = "reservation_request_id", sequenceName = "reservation_request_id_seq", allocationSize = 1) @GeneratedValue(strategy = GenerationType.AUTO, generator = "reservation_request_id") @@ -385,6 +393,24 @@ public void setReusement(ReservationRequestReusement reusement) this.reusement = reusement; } + /** + * @return {@link #auxData} + */ + @Type(type = "jsonb") + @Column(name = "aux_data", columnDefinition = "text") + public String getAuxData() + { + return auxData; + } + + /** + * @param auxData sets the {@link #auxData} + */ + public void setAuxData(String auxData) + { + this.auxData = auxData; + } + /** * Validate {@link AbstractReservationRequest}. * @@ -448,6 +474,7 @@ public boolean synchronizeFrom(AbstractReservationRequest reservationRequest, En setReusedAllocation(reservationRequest.getReusedAllocation()); setReusedAllocationMandatory(reservationRequest.isReusedAllocationMandatory()); setReusement(reservationRequest.getReusement()); + setAuxData(reservationRequest.getAuxData()); Specification oldSpecification = getSpecification(); Specification newSpecification = reservationRequest.getSpecification(); @@ -550,6 +577,7 @@ protected void toApi(cz.cesnet.shongo.controller.api.AbstractReservationRequest ObjectIdentifier.formatId(reusedAllocation.getReservationRequest()), reusedAllocationMandatory); } api.setReusement(getReusement()); + api.setAuxData(getAuxData()); // Reservation request is deleted if (state.equals(State.DELETED)) { @@ -604,6 +632,7 @@ else if (getSpecification() != null && getSpecification().equalsId(specification } setReusedAllocationMandatory(api.isReusedReservationRequestMandatory()); setReusement(api.getReusement()); + setAuxData(api.getAuxData()); } /** diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxData.java new file mode 100644 index 000000000..6ea31d02b --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxData.java @@ -0,0 +1,17 @@ +package cz.cesnet.shongo.controller.booking.request.auxdata; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AuxData +{ + + private String tagName; + private boolean enabled; + private JsonNode data; +} diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestModificationTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestModificationTest.java index f58598701..ec996996e 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestModificationTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestModificationTest.java @@ -1,5 +1,8 @@ package cz.cesnet.shongo.controller.booking.request; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; import cz.cesnet.shongo.AliasType; import cz.cesnet.shongo.Technology; import cz.cesnet.shongo.api.Alias; @@ -13,6 +16,7 @@ import cz.cesnet.shongo.controller.api.ReservationRequestSet; import cz.cesnet.shongo.controller.api.rpc.ReservationService; import cz.cesnet.shongo.controller.booking.datetime.AbsoluteDateTimeSlot; +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxData; import org.joda.time.Interval; import org.junit.Assert; import org.junit.Test; @@ -27,6 +31,77 @@ */ public class ReservationRequestModificationTest extends AbstractControllerTest { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + public void testModifyAttributes() throws JsonProcessingException { + Resource resource = new Resource(); + resource.setName("resource"); + resource.setAllocatable(true); + String resourceId = createResource(resource); + + ArrayNode data = objectMapper.createArrayNode(); + data.add("karnis@cesnet.cz"); + data.add("filip.karnis@cesnet.cz"); + AuxData aux = new AuxData("test", true, data); + String auxData = objectMapper.writeValueAsString(aux); + + ReservationRequest reservationRequest = new ReservationRequest(); + reservationRequest.setDescription("request"); + reservationRequest.setSlot("2012-01-01T12:00", "PT2H"); + reservationRequest.setPurpose(ReservationRequestPurpose.SCIENCE); + reservationRequest.setSpecification(new ResourceSpecification(resourceId)); + + String id1 = getReservationService().createReservationRequest(SECURITY_TOKEN, reservationRequest); + ReservationRequest reservationRequestGet = getReservationRequest(id1, ReservationRequest.class); + + Assert.assertEquals(ReservationRequestType.NEW, reservationRequestGet.getType()); + Assert.assertEquals(reservationRequest.getPurpose(), reservationRequestGet.getPurpose()); + Assert.assertEquals(reservationRequest.getDescription(), reservationRequestGet.getDescription()); + Assert.assertEquals(reservationRequest.getInterDomain(), reservationRequestGet.getInterDomain()); + Assert.assertEquals(reservationRequest.getReusedReservationRequestId(), reservationRequestGet.getReusedReservationRequestId()); + Assert.assertEquals(ReservationRequestReusement.NONE, reservationRequestGet.getReusement()); + Assert.assertEquals(reservationRequest.getAuxData(), reservationRequestGet.getAuxData()); + Assert.assertEquals(reservationRequest.getParentReservationRequestId(), reservationRequestGet.getParentReservationRequestId()); + Assert.assertEquals(reservationRequest.getSlot(), reservationRequestGet.getSlot()); + Assert.assertEquals(reservationRequest.getReservationIds(), reservationRequestGet.getReservationIds()); + + // Modify reservation request by retrieved instance of reservation request + reservationRequestGet.setPurpose(ReservationRequestPurpose.EDUCATION); + reservationRequestGet.setPriority(5); + reservationRequestGet.setDescription("requestModified"); + reservationRequestGet.setSpecification(new AliasSpecification(Technology.ADOBE_CONNECT)); + reservationRequestGet.setReusement(ReservationRequestReusement.OWNED); + reservationRequestGet.setAuxData(auxData); + reservationRequestGet.setSlot("2012-01-01T13:00", "PT1H"); + + String id2 = getReservationService().modifyReservationRequest(SECURITY_TOKEN, reservationRequestGet); + ReservationRequest reservationRequestGet2 = getReservationRequest(id2, ReservationRequest.class); + + Assert.assertEquals(ReservationRequestType.MODIFIED, reservationRequestGet2.getType()); + Assert.assertEquals(reservationRequestGet.getPurpose(), reservationRequestGet2.getPurpose()); + Assert.assertEquals(reservationRequestGet.getPriority(), reservationRequestGet2.getPriority()); + Assert.assertEquals(reservationRequestGet.getDescription(), reservationRequestGet2.getDescription()); + Assert.assertEquals(reservationRequestGet.getInterDomain(), reservationRequestGet2.getInterDomain()); + Assert.assertEquals(reservationRequestGet.getReusedReservationRequestId(), reservationRequestGet2.getReusedReservationRequestId()); + Assert.assertEquals(reservationRequestGet.getReusement(), reservationRequestGet2.getReusement()); + Assert.assertEquals(reservationRequestGet.getAuxData(), reservationRequestGet2.getAuxData()); + Assert.assertEquals(reservationRequestGet.getParentReservationRequestId(), reservationRequestGet2.getParentReservationRequestId()); + Assert.assertEquals(reservationRequestGet.getSlot(), reservationRequestGet2.getSlot()); + Assert.assertEquals(reservationRequestGet.getAllocationState(), reservationRequestGet2.getAllocationState()); + Assert.assertEquals(reservationRequestGet.getReservationIds(), reservationRequestGet2.getReservationIds()); + + // Modify again + reservationRequestGet2.setReusedReservationRequestId(id2); + + String id3 = getReservationService().modifyReservationRequest(SECURITY_TOKEN, reservationRequestGet2); + ReservationRequest reservationRequestGet3 = getReservationRequest(id3, ReservationRequest.class); + + // Check that reused reservation request points to id3 since id2 was modified to id3 + Assert.assertEquals(id3, reservationRequestGet3.getReusedReservationRequestId()); + } + @Test public void testExtension() throws Exception { diff --git a/shongo-deployment/migrations/2023-02-24/migration.sql b/shongo-deployment/migrations/2023-02-24/migration.sql new file mode 100644 index 000000000..8f97bec87 --- /dev/null +++ b/shongo-deployment/migrations/2023-02-24/migration.sql @@ -0,0 +1,17 @@ +/** + * 2023-02-24: add auxData to reservation request + */ +BEGIN TRANSACTION; + +ALTER TABLE abstract_reservation_request ADD COLUMN aux_data jsonb DEFAULT '[]'::jsonb NOT NULL; + +-- Has to be here, otherwise JPA creates TABLE instead of VIEW +CREATE VIEW arr_aux_data AS +SELECT + arr.*, + jsonb_array_elements(aux_data)->>'tagName' AS tag_name, + (jsonb_array_elements(aux_data)->>'enabled')::boolean AS enabled, + jsonb_array_elements(aux_data)->'data' AS data +FROM abstract_reservation_request arr; + +COMMIT TRANSACTION; From 7859d4d1c945d5cb982f4bbe76185e24aba65cfa Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 1 Mar 2023 16:31:41 +0100 Subject: [PATCH 067/134] [controller] add aux_data to reservation_request_summary --- .../api/ReservationRequestSummary.java | 24 +++++++++++++++++++ .../api/rpc/ReservationServiceImpl.java | 3 +++ .../src/main/resources/sql/hsqldb/init.sql | 1 + .../main/resources/sql/postgresql/init.sql | 1 + .../sql/reservation_request_list.sql | 3 ++- 5 files changed, 31 insertions(+), 1 deletion(-) diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationRequestSummary.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationRequestSummary.java index d72930c69..dbc4e6474 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationRequestSummary.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationRequestSummary.java @@ -127,6 +127,11 @@ public class ReservationRequestSummary extends IdentifiedComplexType */ private boolean allowCache = true; + /** + * Auxiliary data. This data are specified by the {@link Tag}s of {@link Resource} which is requested for reservation. + */ + private String auxData; + /** * @return {@link #resourceTags} */ @@ -496,6 +501,22 @@ public void setAllowCache(boolean allowCache) this.allowCache = allowCache; } + /** + * @return {@link #auxData} + */ + public String getAuxData() + { + return auxData; + } + + /** + * @param auxData sets the {@link #auxData} + */ + public void setAuxData(String auxData) + { + this.auxData = auxData; + } + private static final String PARENT_RESERVATION_REQUEST_ID = "parentReservationRequestId"; private static final String TYPE = "type"; private static final String DATETIME = "dateTime"; @@ -518,6 +539,7 @@ public void setAllowCache(boolean allowCache) private static final String ROOM_HAS_RECORDINGS = "roomHasRecordings"; private static final String ALLOW_CACHE = "allowCache"; private static final String RESOURCE_TAGS = "resourceTags"; + private static final String AUX_DATA = "auxData"; @Override public DataMap toData() @@ -545,6 +567,7 @@ public DataMap toData() dataMap.set(ROOM_HAS_RECORDINGS, roomHasRecordings); dataMap.set(ALLOW_CACHE, allowCache); dataMap.set(RESOURCE_TAGS, resourceTags); + dataMap.set(AUX_DATA, auxData); return dataMap; } @@ -574,6 +597,7 @@ public void fromData(DataMap dataMap) roomHasRecordings = dataMap.getBool(ROOM_HAS_RECORDINGS); allowCache = dataMap.getBool(ALLOW_CACHE); resourceTags = dataMap.getString(RESOURCE_TAGS); + auxData = dataMap.getString(AUX_DATA); } /** diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java index a0b4cd879..d621176a1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java @@ -2043,6 +2043,9 @@ else if (type.equals("RESOURCE")) { if (record[26] != null) { reservationRequestSummary.setResourceTags((String) record[26]); } + if (record[27] != null) { + reservationRequestSummary.setAuxData((String) record[27]); + } return reservationRequestSummary; } diff --git a/shongo-controller/src/main/resources/sql/hsqldb/init.sql b/shongo-controller/src/main/resources/sql/hsqldb/init.sql index 861e651c5..ac21d3f0e 100644 --- a/shongo-controller/src/main/resources/sql/hsqldb/init.sql +++ b/shongo-controller/src/main/resources/sql/hsqldb/init.sql @@ -97,6 +97,7 @@ SELECT reused_allocation.abstract_reservation_request_id AS reused_reservation_request_id, abstract_reservation_request.modified_reservation_request_id AS modified_reservation_request_id, abstract_reservation_request.allocation_id AS allocation_id, + abstract_reservation_request.aux_data AS aux_data, NULL AS child_id, NULL AS future_child_count, reservation_request.slot_start AS slot_start, diff --git a/shongo-controller/src/main/resources/sql/postgresql/init.sql b/shongo-controller/src/main/resources/sql/postgresql/init.sql index b219e9a29..f3abf6593 100644 --- a/shongo-controller/src/main/resources/sql/postgresql/init.sql +++ b/shongo-controller/src/main/resources/sql/postgresql/init.sql @@ -340,6 +340,7 @@ FROM ( reused_allocation.abstract_reservation_request_id AS reused_reservation_request_id, abstract_reservation_request.modified_reservation_request_id AS modified_reservation_request_id, abstract_reservation_request.allocation_id AS allocation_id, + abstract_reservation_request.aux_data #>> '{}' AS aux_data, reservation_request_set_earliest_child.child_id AS child_id, reservation_request_set_earliest_child.future_child_count AS future_child_count, COALESCE(reservation_request.slot_start, reservation_request_set_earliest_child.slot_start) AS slot_start, diff --git a/shongo-controller/src/main/resources/sql/reservation_request_list.sql b/shongo-controller/src/main/resources/sql/reservation_request_list.sql index dcd6a76dd..398033d0d 100644 --- a/shongo-controller/src/main/resources/sql/reservation_request_list.sql +++ b/shongo-controller/src/main/resources/sql/reservation_request_list.sql @@ -37,7 +37,8 @@ SELECT foreign_resources.foreign_resource_id, domain.name as domain_name, reservation_request_summary.allowCache as allowCache, - resource_summary.tag_names as tag_names + resource_summary.tag_names as tag_names, + reservation_request_summary.aux_data as aux_data FROM reservation_request_summary LEFT JOIN reservation_request ON reservation_request.id = reservation_request_summary.id LEFT JOIN specification_summary ON specification_summary.id = reservation_request_summary.specification_id From 310ee550139e8015a9c83ad9fecf99cf5b9c728b Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Tue, 31 Oct 2023 01:13:20 +0100 Subject: [PATCH 068/134] revert room capacity data source --- .../rest/models/reservationrequest/ReservationRequestModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java index a65efe479..cb2dda537 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java @@ -62,7 +62,7 @@ public ReservationRequestModel( this.type = SpecificationType.fromReservationRequestSummary(summary, true); this.virtualRoomData = new VirtualRoomModel(virtualRoomSummary); this.physicalResourceData = PhysicalResourceData.fromApi(resourceSummary); - this.roomCapacityData = new RoomCapacityModel(virtualRoomSummary); + this.roomCapacityData = new RoomCapacityModel(summary); this.lastReservationId = summary.getLastReservationId(); this.futureSlotCount = summary.getFutureSlotCount(); From 1ae5cc769ac16523bdc0b6a7f0b81c8b4838fbf9 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 22 Sep 2023 14:45:45 +0200 Subject: [PATCH 069/134] fix start/stop recording endpoints --- .../rest/controllers/RuntimeController.java | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java index 6477d8275..18b734d74 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java @@ -3,13 +3,16 @@ import cz.cesnet.shongo.api.MediaData; import cz.cesnet.shongo.api.RoomParticipant; import cz.cesnet.shongo.controller.api.ExecutionReport; +import cz.cesnet.shongo.controller.api.RecordingService; import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.request.ExecutableServiceListRequest; import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.RestApiPath; import cz.cesnet.shongo.controller.rest.RoomCache; +import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import cz.cesnet.shongo.controller.rest.models.runtimemanagement.RuntimeParticipantModel; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; @@ -163,10 +166,11 @@ void disconnectRuntimeParticipant( @PostMapping(RestApiPath.RUNTIME_MANAGEMENT_RECORDING_START) void startRequestRecording( @RequestAttribute(TOKEN) SecurityToken securityToken, - @PathVariable String id, - @RequestParam(value = "executableServiceId") String executableServiceId) + @PathVariable String id) { String executableId = cache.getExecutableId(securityToken, id); + String executableServiceId = getExecutableServiceId(securityToken, executableId); + Object result = null; try { result = executableService.activateExecutableService(securityToken, executableId, executableServiceId); @@ -197,10 +201,11 @@ void startRequestRecording( @PostMapping(RestApiPath.RUNTIME_MANAGEMENT_RECORDING_STOP) void stopRequestRecording( @RequestAttribute(TOKEN) SecurityToken securityToken, - @PathVariable String id, - @RequestParam(value = "executableServiceId") String executableServiceId) + @PathVariable String id) { String executableId = cache.getExecutableId(securityToken, id); + String executableServiceId = getExecutableServiceId(securityToken, executableId); + Object result = null; try { result = executableService.deactivateExecutableService(securityToken, executableId, executableServiceId); @@ -219,4 +224,18 @@ void stopRequestRecording( } cache.clearExecutable(executableId); } + + private String getExecutableServiceId(SecurityToken securityToken, String executableId) + { + ExecutableServiceListRequest request = new ExecutableServiceListRequest(securityToken, executableId, RecordingService.class); + List services = executableService.listExecutableServices(request).getItems(); + log.debug("Found recording services: {}", services); + if (services.size() > 1) { + throw new UnsupportedApiException("Room " + executableId + " has multiple recording services."); + } + if (!services.isEmpty()) { + return services.get(0).getId(); + } + return null; + } } From e4556e4d592985957da98648f31ef8cbd4e12eeb Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Mon, 2 Oct 2023 00:13:07 +0200 Subject: [PATCH 070/134] fix recording model duration format --- .../controller/rest/models/recording/RecordingModel.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/recording/RecordingModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/recording/RecordingModel.java index ea5eff3d4..4d16ca8e9 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/recording/RecordingModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/recording/RecordingModel.java @@ -5,7 +5,6 @@ import lombok.Data; import org.joda.time.DateTime; import org.joda.time.Duration; -import org.joda.time.Period; import static cz.cesnet.shongo.controller.rest.models.TimeInterval.ISO_8601_PATTERN; @@ -24,7 +23,7 @@ public class RecordingModel private String resourceId; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = ISO_8601_PATTERN) private DateTime beginDate; - private Period duration; + private Long duration; private Boolean isPublic; private String downloadUrl; private String viewUrl; @@ -43,7 +42,7 @@ public RecordingModel(ResourceRecording recording) this.duration = null; } else { - this.duration = duration.toPeriod(); + this.duration = duration.getMillis(); } this.isPublic = recording.isPublic(); this.downloadUrl = recording.getDownloadUrl(); From 5b4daed2a354f33ab3205d54e232dc0ab437746c Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 6 Oct 2023 00:20:43 +0200 Subject: [PATCH 071/134] fix (recording) resource utilization model --- .../models/resource/ResourceUtilizationModel.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java index 0e56b9797..175c00289 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java @@ -26,12 +26,13 @@ public static ResourceUtilizationModel fromApi( { List resources = new ArrayList<>(); resourceCapacityUtilizations.forEach((resourceCapacity, resourceCapacityUtilization) -> { - ResourceCapacity.Room roomCapacity = (ResourceCapacity.Room) resourceCapacity; - UtilizationModel utilizationModel = new UtilizationModel(); - utilizationModel.setId(roomCapacity.getResourceId()); - utilizationModel.setName(roomCapacity.getResourceName()); - utilizationModel.setTotalCapacity(roomCapacity.getLicenseCount()); + utilizationModel.setId(resourceCapacity.getResourceId()); + utilizationModel.setName(resourceCapacity.getResourceName()); + if (resourceCapacity instanceof ResourceCapacity.LicenseCount) { + ResourceCapacity.LicenseCount licenseCount = (ResourceCapacity.LicenseCount) resourceCapacity; + utilizationModel.setTotalCapacity(licenseCount.getLicenseCount()); + } utilizationModel.setUsedCapacity((resourceCapacityUtilization != null) ? resourceCapacityUtilization.getPeakBucket().getLicenseCount() : 0); resources.add(utilizationModel); From 95082b82ba2b967513c79c9cd9a197632f00b19c Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 6 Oct 2023 00:22:46 +0200 Subject: [PATCH 072/134] return new reservation request id in create and modify reservation request endpoints --- .../controllers/ReservationRequestController.java | 9 ++++++--- .../shongo/controller/rest/models/IdModel.java | 13 +++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/IdModel.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 6ba2dc91a..f5d5d2cd6 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -18,6 +18,7 @@ import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.RestApiPath; +import cz.cesnet.shongo.controller.rest.models.IdModel; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestCreateModel; import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestDetailModel; @@ -200,7 +201,7 @@ ListResponse listRequests( @ResponseStatus(HttpStatus.CREATED) @Operation(summary = "Creates reservation request.") @PostMapping - void createRequest( + IdModel createRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @RequestBody ReservationRequestCreateModel request) { @@ -240,6 +241,7 @@ void createRequest( UserRoleModel userRoleModel = request.addUserRole(userInformation, ObjectRole.OWNER); authorizationService.createAclEntry(securityToken, userRoleModel.toApi()); } + return new IdModel(reservationId); } @Operation(summary = "Returns reservation request.") @@ -314,7 +316,7 @@ ReservationRequestDetailModel getRequest( @Operation(summary = "Modifies reservation request.") @PatchMapping(RestApiPath.ID_SUFFIX) - void modifyRequest( + IdModel modifyRequest( @RequestAttribute(TOKEN) SecurityToken securityToken, @PathVariable String id, @RequestBody ReservationRequestCreateModel request) @@ -350,7 +352,8 @@ void modifyRequest( modifiedRequest.setAllowGuests(request.isAllowGuests()); modifiedRequest.setRecord(request.isRecord()); - reservationService.modifyReservationRequest(securityToken, modifiedRequest.toApi()); + String newId = reservationService.modifyReservationRequest(securityToken, modifiedRequest.toApi()); + return new IdModel(newId); } @Operation(summary = "Deletes reservation request.") diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/IdModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/IdModel.java new file mode 100644 index 000000000..b25f04d6d --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/IdModel.java @@ -0,0 +1,13 @@ +package cz.cesnet.shongo.controller.rest.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class IdModel { + + private String id; +} From 8a3f9343f7a6f42acd953833811f2942157ddb40 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 6 Oct 2023 00:42:55 +0200 Subject: [PATCH 073/134] resolved isRecordingActive --- .../controllers/ReservationRequestController.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index f5d5d2cd6..3b93ae88c 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -7,9 +7,11 @@ import cz.cesnet.shongo.controller.api.AbstractReservationRequest; import cz.cesnet.shongo.controller.api.AbstractRoomExecutable; import cz.cesnet.shongo.controller.api.AllocationState; +import cz.cesnet.shongo.controller.api.RecordingService; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; import cz.cesnet.shongo.controller.api.ResourceSummary; import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.request.ExecutableServiceListRequest; import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.request.ReservationRequestListRequest; import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; @@ -255,10 +257,17 @@ ReservationRequestDetailModel getRequest( String roomId = cache.getExecutableId(securityToken, id); RoomAuthorizedData authorizedData = null; + boolean isRecordingActive = false; if (roomId != null) { AbstractRoomExecutable roomExecutable = (AbstractRoomExecutable) executableService.getExecutable(securityToken, roomId); authorizedData = new RoomAuthorizedData(roomExecutable); + + ExecutableServiceListRequest request = new ExecutableServiceListRequest(securityToken, roomId, RecordingService.class); + List executableServices = executableService.listExecutableServices(request).getItems(); + if (!executableServices.isEmpty()) { + isRecordingActive = executableServices.get(0).isActive(); + } } List requests = new ArrayList<>(); @@ -308,10 +317,12 @@ ReservationRequestDetailModel getRequest( ); } - return new ReservationRequestDetailModel( + ReservationRequestDetailModel detailModel = new ReservationRequestDetailModel( summary, virtualRoomSummary, permissionsByReservationRequestId, ownerInformation, authorizedData, history, resourceSummary ); + detailModel.getRoomCapacityData().setIsRecordingActive(isRecordingActive); + return detailModel; } @Operation(summary = "Modifies reservation request.") From c1af5fea77b89b0c1d7daa82b807f5251a460019 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 6 Oct 2023 18:56:40 +0200 Subject: [PATCH 074/134] fix delete recording request --- .../rest/controllers/RecordingController.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java index e5a8a0d34..f487de73f 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java @@ -1,8 +1,10 @@ package cz.cesnet.shongo.controller.rest.controllers; +import cz.cesnet.shongo.controller.api.RecordingService; import cz.cesnet.shongo.controller.api.ResourceRecording; import cz.cesnet.shongo.controller.api.SecurityToken; import cz.cesnet.shongo.controller.api.request.ExecutableRecordingListRequest; +import cz.cesnet.shongo.controller.api.request.ExecutableServiceListRequest; import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.api.rpc.ResourceControlService; @@ -77,6 +79,13 @@ void deleteRequestRecording( @PathVariable String id, @PathVariable String recordingId) { - resourceControlService.deleteRecording(securityToken, id, recordingId); + String executableId = cache.getExecutableId(securityToken, id); + ExecutableServiceListRequest request = new ExecutableServiceListRequest(securityToken, executableId, RecordingService.class); + List executableServices = executableService.listExecutableServices(request).getItems(); + if (executableServices.isEmpty()) { + throw new IllegalArgumentException("No recording service found for executable " + executableId); + } + String resId = ((RecordingService) executableServices.get(0)).getResourceId(); + resourceControlService.deleteRecording(securityToken, resId, recordingId); } } From c9948492157eb47c7e054590dd5e19a1c8475814 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 19 Oct 2023 19:36:29 +0200 Subject: [PATCH 075/134] reduce reservation request expiration time from cache --- .../src/main/java/cz/cesnet/shongo/controller/rest/Cache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java index d958adb28..1a0ebda80 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java @@ -106,7 +106,7 @@ public class Cache * {@link ReservationRequestSummary} by identifier. */ private final ExpirationMap reservationRequestById = - new ExpirationMap<>(Duration.standardMinutes(5)); + new ExpirationMap<>(Duration.standardSeconds(15)); /** * {@link Reservation} by identifier. From dc49449d876e5b6da9239a7f2932eef23443c6ec Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 30 Mar 2023 03:23:16 +0200 Subject: [PATCH 076/134] [controller] add new tag data to reservation request summary --- .../client/web/models/SpecificationType.java | 20 ++++++++++++------- .../api/ReservationRequestSummary.java | 15 ++++++++++---- .../cz/cesnet/shongo/controller/api/Tag.java | 13 ++++++++++++ .../api/rpc/ReservationServiceImpl.java | 7 ++++++- .../src/main/resources/sql/hsqldb/init.sql | 2 +- .../main/resources/sql/postgresql/init.sql | 2 +- .../sql/reservation_request_list.sql | 2 +- 7 files changed, 46 insertions(+), 15 deletions(-) diff --git a/shongo-client-web/src/main/java/cz/cesnet/shongo/client/web/models/SpecificationType.java b/shongo-client-web/src/main/java/cz/cesnet/shongo/client/web/models/SpecificationType.java index 4683796c4..f74309bf7 100644 --- a/shongo-client-web/src/main/java/cz/cesnet/shongo/client/web/models/SpecificationType.java +++ b/shongo-client-web/src/main/java/cz/cesnet/shongo/client/web/models/SpecificationType.java @@ -4,6 +4,10 @@ import cz.cesnet.shongo.client.web.ClientWebConfiguration; import cz.cesnet.shongo.client.web.support.MessageProvider; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.api.Tag; + +import java.util.List; +import java.util.stream.Collectors; /** * Type of specification for a reservation request. @@ -102,15 +106,17 @@ public static SpecificationType fromReservationRequestSummary(ReservationRequest case USED_ROOM: return PERMANENT_ROOM_CAPACITY; case RESOURCE: - String resourceTags = reservationRequestSummary.getResourceTags(); + List resourceTags = reservationRequestSummary.getResourceTags() + .stream() + .map(Tag::getName) + .collect(Collectors.toList()); String parkTagName = ClientWebConfiguration.getInstance().getParkingPlaceTagName(); String vehicleTagName = ClientWebConfiguration.getInstance().getVehicleTagName(); - if (resourceTags != null) { - if (parkTagName != null && resourceTags.contains(parkTagName)) { - return PARKING_PLACE; - } else if (vehicleTagName != null && resourceTags.contains(vehicleTagName)) { - return VEHICLE; - } + if (parkTagName != null && resourceTags.contains(parkTagName)) { + return PARKING_PLACE; + } + else if (vehicleTagName != null && resourceTags.contains(vehicleTagName)) { + return VEHICLE; } return MEETING_ROOM; default: diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationRequestSummary.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationRequestSummary.java index dbc4e6474..dc0be659a 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationRequestSummary.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationRequestSummary.java @@ -110,7 +110,7 @@ public class ReservationRequestSummary extends IdentifiedComplexType /** * Resource tags. */ - private String resourceTags; + private List resourceTags = new ArrayList<>(); /** * Specifies whether room has recording service. @@ -135,17 +135,24 @@ public class ReservationRequestSummary extends IdentifiedComplexType /** * @return {@link #resourceTags} */ - public String getResourceTags() { + public List getResourceTags() { return resourceTags; } /** * @param resourceTags sets the {@link #resourceTags} */ - public void setResourceTags(String resourceTags) { + public void setResourceTags(List resourceTags) { this.resourceTags = resourceTags; } + /** + * @param resourceTag adds tag to {@link #resourceTags} + */ + public void addResourceTag(Tag resourceTag) { + this.resourceTags.add(resourceTag); + } + /** * @return {@link #parentReservationRequestId} */ @@ -596,7 +603,7 @@ public void fromData(DataMap dataMap) roomHasRecordingService = dataMap.getBool(ROOM_HAS_RECORDING_SERVICE); roomHasRecordings = dataMap.getBool(ROOM_HAS_RECORDINGS); allowCache = dataMap.getBool(ALLOW_CACHE); - resourceTags = dataMap.getString(RESOURCE_TAGS); + resourceTags = dataMap.getList(RESOURCE_TAGS, Tag.class); auxData = dataMap.getString(AUX_DATA); } diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/Tag.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/Tag.java index 364116229..b746fb7af 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/Tag.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/Tag.java @@ -43,6 +43,19 @@ public void setData(String data) this.data = data; } + public static Tag fromConcat(String concat) + { + String[] parts = concat.split(",", 4); + Tag tag = new Tag(); + tag.setId(parts[0]); + tag.setName(parts[1]); + tag.setType(TagType.valueOf(parts[2])); + if (parts.length > 3) { + tag.setData(parts[3]); + } + return tag; + } + private static final String NAME = "name"; private static final String TYPE = "type"; private static final String DATA = "data"; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java index d621176a1..498f03fe3 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java @@ -8,6 +8,7 @@ import cz.cesnet.shongo.controller.api.*; import cz.cesnet.shongo.controller.api.Reservation; import cz.cesnet.shongo.controller.api.Specification; +import cz.cesnet.shongo.controller.api.Tag; import cz.cesnet.shongo.controller.api.request.*; import cz.cesnet.shongo.controller.authorization.Authorization; import cz.cesnet.shongo.controller.authorization.AuthorizationManager; @@ -2041,7 +2042,11 @@ else if (type.equals("RESOURCE")) { reservationRequestSummary.setAllowCache((Boolean) record[25]); } if (record[26] != null) { - reservationRequestSummary.setResourceTags((String) record[26]); + String resourceTags = (String) record[26]; + Arrays.stream(resourceTags.split("\\|")) + .map(String::trim) + .map(Tag::fromConcat) + .forEach(reservationRequestSummary::addResourceTag); } if (record[27] != null) { reservationRequestSummary.setAuxData((String) record[27]); diff --git a/shongo-controller/src/main/resources/sql/hsqldb/init.sql b/shongo-controller/src/main/resources/sql/hsqldb/init.sql index ac21d3f0e..a0b50e5cf 100644 --- a/shongo-controller/src/main/resources/sql/hsqldb/init.sql +++ b/shongo-controller/src/main/resources/sql/hsqldb/init.sql @@ -30,7 +30,7 @@ SELECT WHEN (SELECT resource_id FROM capability INNER JOIN recording_capability on recording_capability.id = capability.id WHERE resource_id = resource.id) IS NOT NULL THEN 'RECORDING_SERVICE' ELSE 'RESOURCE' END AS type, - GROUP_CONCAT(tag.name SEPARATOR ',') AS tag_names + GROUP_CONCAT(CONCAT(tag.id, ',', tag.name, ',', tag.type, ',', tag.data) SEPARATOR '|') AS tags FROM resource LEFT JOIN device_resource ON device_resource.id = resource.id LEFT JOIN device_resource_technologies ON device_resource_technologies.device_resource_id = device_resource.id diff --git a/shongo-controller/src/main/resources/sql/postgresql/init.sql b/shongo-controller/src/main/resources/sql/postgresql/init.sql index f3abf6593..760fb4fca 100644 --- a/shongo-controller/src/main/resources/sql/postgresql/init.sql +++ b/shongo-controller/src/main/resources/sql/postgresql/init.sql @@ -112,7 +112,7 @@ SELECT WHEN resource.id IN (SELECT resource_id FROM capability INNER JOIN recording_capability on recording_capability.id = capability.id) THEN 'RECORDING_SERVICE' ELSE 'RESOURCE' END AS type, - string_agg(tag.name, ',') AS tag_names + string_agg(tag.id || ',' || tag.name || ',' || tag.type || ',' || COALESCE(tag.data #>> '{}', ''), '|') AS tags FROM resource LEFT JOIN device_resource ON device_resource.id = resource.id LEFT JOIN device_resource_technologies ON device_resource_technologies.device_resource_id = device_resource.id diff --git a/shongo-controller/src/main/resources/sql/reservation_request_list.sql b/shongo-controller/src/main/resources/sql/reservation_request_list.sql index 398033d0d..60ecd03c9 100644 --- a/shongo-controller/src/main/resources/sql/reservation_request_list.sql +++ b/shongo-controller/src/main/resources/sql/reservation_request_list.sql @@ -37,7 +37,7 @@ SELECT foreign_resources.foreign_resource_id, domain.name as domain_name, reservation_request_summary.allowCache as allowCache, - resource_summary.tag_names as tag_names, + resource_summary.tags as tags, reservation_request_summary.aux_data as aux_data FROM reservation_request_summary LEFT JOIN reservation_request ON reservation_request.id = reservation_request_summary.id From 37b250956a5ffa247bc22211c1cdac666cb78d5a Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Mon, 19 Jun 2023 18:42:55 +0200 Subject: [PATCH 077/134] [controller] add notifications for emails from tags --- .../request/auxdata/AuxDataException.java | 15 ++++ .../request/auxdata/AuxDataFilter.java | 15 ++++ .../request/auxdata/AuxDataService.java | 78 ++++++++++++++++++ .../auxdata/tagdata/NotifyEmailAuxData.java | 31 +++++++ .../auxdata/tagdata/ReservationAuxData.java | 20 +++++ .../request/auxdata/tagdata/TagData.java | 55 +++++++++++++ .../booking/resource/ResourceManager.java | 21 +++++ .../notification/ReservationNotification.java | 82 +++++++++++++++++++ 8 files changed, 317 insertions(+) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataException.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataFilter.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataService.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/NotifyEmailAuxData.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/ReservationAuxData.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataException.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataException.java new file mode 100644 index 000000000..feb1363f1 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataException.java @@ -0,0 +1,15 @@ +package cz.cesnet.shongo.controller.booking.request.auxdata; + +public class AuxDataException extends Exception +{ + + public AuxDataException(String message) + { + super(message); + } + + public AuxDataException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataFilter.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataFilter.java new file mode 100644 index 000000000..9c03fea12 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataFilter.java @@ -0,0 +1,15 @@ +package cz.cesnet.shongo.controller.booking.request.auxdata; + +import cz.cesnet.shongo.controller.api.TagType; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class AuxDataFilter +{ + + private final String tagName; + private final TagType tagType; + private final Boolean enabled; +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataService.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataService.java new file mode 100644 index 000000000..dd9a4d111 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataService.java @@ -0,0 +1,78 @@ +package cz.cesnet.shongo.controller.booking.request.auxdata; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import cz.cesnet.shongo.TodoImplementException; +import cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest; +import cz.cesnet.shongo.controller.booking.request.auxdata.tagdata.TagData; +import cz.cesnet.shongo.controller.booking.resource.Resource; +import cz.cesnet.shongo.controller.booking.resource.ResourceManager; +import cz.cesnet.shongo.controller.booking.resource.ResourceSpecification; +import cz.cesnet.shongo.controller.booking.resource.Tag; +import cz.cesnet.shongo.controller.booking.room.RoomSpecification; +import cz.cesnet.shongo.controller.booking.specification.Specification; + +import javax.persistence.EntityManager; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class AuxDataService +{ + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + public static > List getTagData( + AbstractReservationRequest reservationRequest, + AuxDataFilter filter, EntityManager entityManager + ) throws AuxDataException, JsonProcessingException + { + List> tagData = getAllTagData(reservationRequest, entityManager); + + return tagData + .stream() + .filter(data -> data.filter(filter)) + .map(data -> (T) data) + .collect(Collectors.toList()); + } + + private static List> getAllTagData( + AbstractReservationRequest reservationRequest, + EntityManager entityManager + ) throws AuxDataException, JsonProcessingException + { + if (reservationRequest.getAuxData() == null) { + throw new AuxDataException("AuxData is null"); + } + + List auxData = objectMapper.readValue(reservationRequest.getAuxData(), new TypeReference<>() {}); + + Resource resource = getResource(reservationRequest); + List resourceTags = new ResourceManager(entityManager).getResourceTags(resource); + + List> tagData = new ArrayList<>(); + for (Tag tag : resourceTags) { + for (AuxData auxData1 : auxData) { + if (auxData1.getTagName().equals(tag.getName())) { + tagData.add(TagData.create(tag, auxData1)); + } + } + } + return tagData; + } + + private static Resource getResource(AbstractReservationRequest reservationRequest) + { + Specification specification = reservationRequest.getSpecification(); + if (specification instanceof ResourceSpecification) { + return ((ResourceSpecification) specification).getResource(); + } + else if (specification instanceof RoomSpecification) { + return ((RoomSpecification) specification).getDeviceResource(); + } + else { + throw new TodoImplementException(specification.getClass()); + } + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/NotifyEmailAuxData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/NotifyEmailAuxData.java new file mode 100644 index 000000000..d6a0bdeed --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/NotifyEmailAuxData.java @@ -0,0 +1,31 @@ +package cz.cesnet.shongo.controller.booking.request.auxdata.tagdata; + +import com.fasterxml.jackson.databind.JsonNode; +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxData; +import cz.cesnet.shongo.controller.booking.resource.Tag; + +import java.util.ArrayList; +import java.util.List; + +public class NotifyEmailAuxData extends TagData> +{ + + public NotifyEmailAuxData(Tag tag, AuxData auxData) + { + super(tag, auxData); + } + + @Override + public List getData() + { + List emails = new ArrayList<>(); + + for (JsonNode child : tag.getData()) { + emails.add(child.asText()); + } + for (JsonNode child : aux.getData()) { + emails.add(child.asText()); + } + return emails; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/ReservationAuxData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/ReservationAuxData.java new file mode 100644 index 000000000..a19d5487b --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/ReservationAuxData.java @@ -0,0 +1,20 @@ +package cz.cesnet.shongo.controller.booking.request.auxdata.tagdata; + +import cz.cesnet.shongo.TodoImplementException; +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxData; +import cz.cesnet.shongo.controller.booking.resource.Tag; + +public class ReservationAuxData extends TagData +{ + + public ReservationAuxData(Tag tag, AuxData auxData) + { + super(tag, auxData); + } + + @Override + public String getData() + { + throw new TodoImplementException("Not implemented"); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java new file mode 100644 index 000000000..df8221982 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java @@ -0,0 +1,55 @@ +package cz.cesnet.shongo.controller.booking.request.auxdata.tagdata; + +import cz.cesnet.shongo.TodoImplementException; +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxData; +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataFilter; +import cz.cesnet.shongo.controller.booking.resource.Tag; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Getter +@ToString +@RequiredArgsConstructor +public abstract class TagData +{ + + protected final Tag tag; + protected final AuxData aux; + + public abstract T getData(); + + public static TagData create(Tag tag, AuxData auxData) + { + switch (tag.getType()) { + case NOTIFY_EMAIL: + return new NotifyEmailAuxData(tag, auxData); + case RESERVATION_DATA: + return new ReservationAuxData(tag, auxData); + default: + throw new TodoImplementException("Not implemented for tag type: " + tag.getType()); + } + } + + public boolean filter(AuxDataFilter filter) + { + if (filter.getTagName() != null) { + if (!filter.getTagName().equals(tag.getName())) { + return false; + } + } + if (filter.getTagType() != null) { + if (!filter.getTagType().equals(tag.getType())) { + return false; + } + } + if (filter.getEnabled() != null) { + if (!filter.getEnabled().equals(aux.isEnabled())) { + return false; + } + } + return true; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/resource/ResourceManager.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/resource/ResourceManager.java index bcab61e34..6e3573745 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/resource/ResourceManager.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/resource/ResourceManager.java @@ -27,6 +27,7 @@ import javax.persistence.TypedQuery; import javax.persistence.criteria.*; import java.util.List; +import java.util.stream.Collectors; /** * Manager for {@link Resource}. @@ -686,6 +687,26 @@ public List getForeignResourceTags(ForeignResources foreignResource return typedQuery.getResultList(); } + /** + * Returns list of {@link Tag} for given {@link Resource} + * @param resource + * @return + */ + public List getResourceTags(Resource resource) + { + CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); + + CriteriaQuery query = criteriaBuilder.createQuery(ResourceTag.class); + Root resourceTagRoot = query.from(ResourceTag.class); + javax.persistence.criteria.Predicate param1 = criteriaBuilder.equal(resourceTagRoot.get("resource"), resource.getId()); + query.select(resourceTagRoot).where(param1); + + TypedQuery typedQuery = entityManager.createQuery(query); + List resourceTags = typedQuery.getResultList(); + + return resourceTags.stream().map(ResourceTag::getTag).collect(Collectors.toList()); + } + /** * List {@link ResourceTag} by given {@link Tag} id * @param tagId diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/notification/ReservationNotification.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/notification/ReservationNotification.java index d04a9363d..92e9083ae 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/notification/ReservationNotification.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/notification/ReservationNotification.java @@ -1,17 +1,23 @@ package cz.cesnet.shongo.controller.notification; +import com.fasterxml.jackson.core.JsonProcessingException; import cz.cesnet.shongo.AliasType; import cz.cesnet.shongo.PersonInformation; import cz.cesnet.shongo.TodoImplementException; import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.LocalDomain; import cz.cesnet.shongo.controller.ObjectRole; +import cz.cesnet.shongo.controller.api.TagType; import cz.cesnet.shongo.controller.authorization.AuthorizationManager; import cz.cesnet.shongo.controller.booking.Allocation; import cz.cesnet.shongo.controller.booking.ObjectIdentifier; import cz.cesnet.shongo.controller.booking.alias.Alias; import cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest; +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataException; +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataFilter; +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataService; +import cz.cesnet.shongo.controller.booking.request.auxdata.tagdata.NotifyEmailAuxData; import cz.cesnet.shongo.controller.booking.reservation.Reservation; import cz.cesnet.shongo.controller.booking.resource.Resource; import cz.cesnet.shongo.controller.booking.room.RoomEndpoint; @@ -22,6 +28,7 @@ import javax.persistence.EntityManager; import java.util.*; +import java.util.stream.Collectors; /** * {@link ConfigurableNotification} for a {@link Reservation}. @@ -63,6 +70,7 @@ private ReservationNotification(Reservation reservation, // Add administrators as recipients addAdministratorRecipientsForReservation(reservation.getTargetReservation(), authorizationManager); + addRecipientsFromNotificationTags(reservationRequest, entityManager); // Add child targets for (Reservation childReservation : reservation.getChildReservations()) { @@ -70,6 +78,43 @@ private ReservationNotification(Reservation reservation, } } + private void addRecipientsFromNotificationTags(AbstractReservationRequest reservationRequest, + EntityManager entityManager) + { + AuxDataFilter filter = AuxDataFilter.builder() + .tagType(TagType.NOTIFY_EMAIL) + .enabled(true) + .build(); + + List notifyEmailAuxData; + try { + notifyEmailAuxData = AuxDataService.getTagData(reservationRequest, filter, entityManager); + } catch (JsonProcessingException e) { + logger.error("Error while parsing auxData", e); + return; + } catch (AuxDataException e) { + logger.warn("Error while getting notify email aux data for reservation request {}.", reservationRequest.getId(), e); + return; + } + + List tagPersonInformationList = notifyEmailAuxData + .stream() + .map(this::notifyEmailDataToPersonInformation) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + logger.debug("Adding tag recipients: {}", tagPersonInformationList); + tagPersonInformationList.forEach(personInformation -> addRecipient(personInformation, true)); + } + + private Collection notifyEmailDataToPersonInformation(NotifyEmailAuxData notifyEmailAuxData) + { + return notifyEmailAuxData + .getData() + .stream() + .map(email -> new TagPersonInformation(notifyEmailAuxData.getTag().getName(), email)) + .collect(Collectors.toList()); + } + public String getId() { return id; @@ -389,4 +434,41 @@ public String getType() return "DELETED"; } } + + private static class TagPersonInformation implements PersonInformation + { + + private final String name; + private final String email; + + public TagPersonInformation(String name, String email) + { + this.name = name; + this.email = email; + } + + @Override + public String getFullName() + { + return name; + } + + @Override + public String getRootOrganization() + { + return null; + } + + @Override + public String getPrimaryEmail() + { + return email; + } + + @Override + public String toString() + { + return "Tag[" + name + "] (" + email + ")"; + } + } } From 81f046cfdff2d87c871b700a427b50842bac1518 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 3 Nov 2023 19:17:50 +0100 Subject: [PATCH 078/134] [controller] add aux data API --- .../api/AbstractReservationRequest.java | 11 ++- .../shongo/controller/api/AuxiliaryData.java | 94 +++++++++++++++++++ .../request/AbstractReservationRequest.java | 43 ++++++++- .../booking/request/auxdata/AuxData.java | 22 ++++- .../request/auxdata/AuxDataService.java | 17 ++-- .../ReservationRequestModificationTest.java | 24 ++--- 6 files changed, 175 insertions(+), 36 deletions(-) create mode 100644 shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AuxiliaryData.java diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AbstractReservationRequest.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AbstractReservationRequest.java index 183c43e48..48fe8d986 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AbstractReservationRequest.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AbstractReservationRequest.java @@ -7,6 +7,9 @@ import cz.cesnet.shongo.controller.api.rpc.ReservationService; import org.joda.time.DateTime; +import java.util.ArrayList; +import java.util.List; + /** * Request for reservation of resources. * @@ -78,7 +81,7 @@ public abstract class AbstractReservationRequest extends IdentifiedComplexType /** * Auxiliary data. This data are specified by the {@link Tag}s of {@link Resource} which is requested for reservation. */ - private String auxData; + private List auxData = new ArrayList<>(); /** * Constructor. @@ -299,7 +302,7 @@ public void setIsSchedulerDeleted(boolean isSchedulerDeleted) /** * @return {@link #auxData} */ - public String getAuxData() + public List getAuxData() { return auxData; } @@ -307,7 +310,7 @@ public String getAuxData() /** * @param auxData sets the {@link #auxData} */ - public void setAuxData(String auxData) + public void setAuxData(List auxData) { this.auxData = auxData; } @@ -362,6 +365,6 @@ public void fromData(DataMap dataMap) reusedReservationRequestMandatory = dataMap.getBool(REUSED_RESERVATION_REQUEST_MANDATORY); reusement = dataMap.getEnum(REUSEMENT, ReservationRequestReusement.class); isSchedulerDeleted = dataMap.getBool(IS_SCHEDULER_DELETED); - auxData = dataMap.getString(AUX_DATA); + auxData = dataMap.getList(AUX_DATA, AuxiliaryData.class); } } diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AuxiliaryData.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AuxiliaryData.java new file mode 100644 index 000000000..b65a15807 --- /dev/null +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AuxiliaryData.java @@ -0,0 +1,94 @@ +package cz.cesnet.shongo.controller.api; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import cz.cesnet.shongo.api.AbstractComplexType; +import cz.cesnet.shongo.api.DataMap; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class AuxiliaryData extends AbstractComplexType +{ + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private String tagName; + private boolean enabled; + @ToString.Exclude + @EqualsAndHashCode.Exclude + private String data = objectMapper.nullNode().toString(); + + public AuxiliaryData(String tagName, boolean enabled, String data) + { + setTagName(tagName); + setEnabled(enabled); + setData(data); + } + + @JsonIgnore + @ToString.Include(name = "data") + @EqualsAndHashCode.Include + public JsonNode getDataAsJsonNode() + { + if (data == null) { + return null; + } + + try { + return objectMapper.readTree(data); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public void setData(String data) + { + if (data == null) { + this.data = objectMapper.nullNode().toString(); + return; + } + + this.data = data; + } + + public void setData(JsonNode data) + { + try { + this.data = objectMapper.writeValueAsString(data); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + @Override + @JsonIgnore + public String getClassName() { + return super.getClassName(); + } + + @Override + public DataMap toData() + { + DataMap dataMap = super.toData(); + dataMap.set("tagName", tagName); + dataMap.set("enabled", enabled); + dataMap.set("data", data); + return dataMap; + } + + @Override + public void fromData(DataMap dataMap) + { + super.fromData(dataMap); + tagName = dataMap.getString("tagName"); + enabled = dataMap.getBoolean("enabled"); + data = dataMap.getString("data"); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequest.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequest.java index 80f99a5af..630f7407f 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequest.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequest.java @@ -1,5 +1,8 @@ package cz.cesnet.shongo.controller.booking.request; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import cz.cesnet.shongo.CommonReportSet; import cz.cesnet.shongo.PersistentObject; import cz.cesnet.shongo.TodoImplementException; @@ -9,9 +12,11 @@ import cz.cesnet.shongo.controller.ObjectType; import cz.cesnet.shongo.controller.ReservationRequestPurpose; import cz.cesnet.shongo.controller.ReservationRequestReusement; +import cz.cesnet.shongo.controller.api.AuxiliaryData; import cz.cesnet.shongo.controller.api.Controller; import cz.cesnet.shongo.controller.booking.Allocation; import cz.cesnet.shongo.controller.booking.ObjectIdentifier; +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxData; import cz.cesnet.shongo.controller.booking.resource.Resource; import cz.cesnet.shongo.controller.booking.resource.Tag; import cz.cesnet.shongo.controller.booking.specification.Specification; @@ -28,7 +33,9 @@ import javax.persistence.*; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * Represents a base class for all reservation requests which contains common attributes. @@ -40,6 +47,10 @@ @Inheritance(strategy = InheritanceType.JOINED) public abstract class AbstractReservationRequest extends PersistentObject implements ReportableSimple { + + @Transient + private final ObjectMapper objectMapper = new ObjectMapper(); + /** * Date/time when the {@link AbstractReservationRequest} was created. */ @@ -403,6 +414,18 @@ public String getAuxData() return auxData; } + /** + * @return {@link #auxData} + */ + @Transient + public List getAuxDataList() throws JsonProcessingException + { + if (auxData == null) { + return null; + } + return objectMapper.readValue(getAuxData(), new TypeReference<>(){}); + } + /** * @param auxData sets the {@link #auxData} */ @@ -411,6 +434,19 @@ public void setAuxData(String auxData) this.auxData = auxData; } + /** + * @param auxDataApi sets the {@link #auxData} + */ + public void setAuxData(List auxDataApi) + { + List auxData = auxDataApi.stream().map(AuxData::fromApi).collect(Collectors.toList()); + try { + setAuxData(objectMapper.writeValueAsString(auxData)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + /** * Validate {@link AbstractReservationRequest}. * @@ -577,7 +613,12 @@ protected void toApi(cz.cesnet.shongo.controller.api.AbstractReservationRequest ObjectIdentifier.formatId(reusedAllocation.getReservationRequest()), reusedAllocationMandatory); } api.setReusement(getReusement()); - api.setAuxData(getAuxData()); + try { + api.setAuxData(getAuxDataList().stream().map(AuxData::toApi).collect(Collectors.toList())); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } catch (NullPointerException ignored) { + } // Reservation request is deleted if (state.equals(State.DELETED)) { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxData.java index 6ea31d02b..fcc352427 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxData.java @@ -1,17 +1,31 @@ package cz.cesnet.shongo.controller.booking.request.auxdata; import com.fasterxml.jackson.databind.JsonNode; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; @Data -@NoArgsConstructor -@AllArgsConstructor public class AuxData { private String tagName; private boolean enabled; private JsonNode data; + + public cz.cesnet.shongo.controller.api.AuxiliaryData toApi() + { + cz.cesnet.shongo.controller.api.AuxiliaryData api = new cz.cesnet.shongo.controller.api.AuxiliaryData(); + api.setTagName(getTagName()); + api.setEnabled(isEnabled()); + api.setData(getData()); + return api; + } + + public static AuxData fromApi(cz.cesnet.shongo.controller.api.AuxiliaryData api) + { + AuxData auxData = new AuxData(); + auxData.setTagName(api.getTagName()); + auxData.setEnabled(api.isEnabled()); + auxData.setData(api.getDataAsJsonNode()); + return auxData; + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataService.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataService.java index dd9a4d111..6b27c9f30 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataService.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataService.java @@ -1,9 +1,6 @@ package cz.cesnet.shongo.controller.booking.request.auxdata; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import cz.cesnet.shongo.TodoImplementException; import cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest; import cz.cesnet.shongo.controller.booking.request.auxdata.tagdata.TagData; import cz.cesnet.shongo.controller.booking.resource.Resource; @@ -21,8 +18,6 @@ public class AuxDataService { - private static final ObjectMapper objectMapper = new ObjectMapper(); - public static > List getTagData( AbstractReservationRequest reservationRequest, AuxDataFilter filter, EntityManager entityManager @@ -46,9 +41,12 @@ private static List> getAllTagData( throw new AuxDataException("AuxData is null"); } - List auxData = objectMapper.readValue(reservationRequest.getAuxData(), new TypeReference<>() {}); + List auxData = reservationRequest.getAuxDataList(); Resource resource = getResource(reservationRequest); + if (resource == null) { + throw new AuxDataException("Resource is null"); + } List resourceTags = new ResourceManager(entityManager).getResourceTags(resource); List> tagData = new ArrayList<>(); @@ -62,7 +60,7 @@ private static List> getAllTagData( return tagData; } - private static Resource getResource(AbstractReservationRequest reservationRequest) + private static Resource getResource(AbstractReservationRequest reservationRequest) throws AuxDataException { Specification specification = reservationRequest.getSpecification(); if (specification instanceof ResourceSpecification) { @@ -71,8 +69,7 @@ private static Resource getResource(AbstractReservationRequest reservationReques else if (specification instanceof RoomSpecification) { return ((RoomSpecification) specification).getDeviceResource(); } - else { - throw new TodoImplementException(specification.getClass()); - } + throw new AuxDataException(String.format("Specification: %s does not have a resource (so neither tags)", + specification.getClass())); } } diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestModificationTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestModificationTest.java index ec996996e..d766573e9 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestModificationTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestModificationTest.java @@ -1,8 +1,5 @@ package cz.cesnet.shongo.controller.booking.request; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; import cz.cesnet.shongo.AliasType; import cz.cesnet.shongo.Technology; import cz.cesnet.shongo.api.Alias; @@ -11,18 +8,14 @@ import cz.cesnet.shongo.controller.ReservationRequestPurpose; import cz.cesnet.shongo.controller.ReservationRequestReusement; import cz.cesnet.shongo.controller.api.*; -import cz.cesnet.shongo.controller.api.AbstractReservationRequest; +import cz.cesnet.shongo.controller.api.AuxiliaryData; import cz.cesnet.shongo.controller.api.ReservationRequest; -import cz.cesnet.shongo.controller.api.ReservationRequestSet; import cz.cesnet.shongo.controller.api.rpc.ReservationService; -import cz.cesnet.shongo.controller.booking.datetime.AbsoluteDateTimeSlot; -import cz.cesnet.shongo.controller.booking.request.auxdata.AuxData; import org.joda.time.Interval; import org.junit.Assert; import org.junit.Test; import java.util.List; -import java.util.Locale; /** * Tests for reallocation of reservations. @@ -32,21 +25,13 @@ public class ReservationRequestModificationTest extends AbstractControllerTest { - private final ObjectMapper objectMapper = new ObjectMapper(); - @Test - public void testModifyAttributes() throws JsonProcessingException { + public void testModifyAttributes() { Resource resource = new Resource(); resource.setName("resource"); resource.setAllocatable(true); String resourceId = createResource(resource); - ArrayNode data = objectMapper.createArrayNode(); - data.add("karnis@cesnet.cz"); - data.add("filip.karnis@cesnet.cz"); - AuxData aux = new AuxData("test", true, data); - String auxData = objectMapper.writeValueAsString(aux); - ReservationRequest reservationRequest = new ReservationRequest(); reservationRequest.setDescription("request"); reservationRequest.setSlot("2012-01-01T12:00", "PT2H"); @@ -73,6 +58,11 @@ public void testModifyAttributes() throws JsonProcessingException { reservationRequestGet.setDescription("requestModified"); reservationRequestGet.setSpecification(new AliasSpecification(Technology.ADOBE_CONNECT)); reservationRequestGet.setReusement(ReservationRequestReusement.OWNED); + List auxData = List.of( + new AuxiliaryData("tag1", true, "[\"karnis@cenet.cz\", \"filip.karnis@cesnet.cz\"]"), + new AuxiliaryData("tag2", false, "[\"shouldnotbe@used\"]"), + new AuxiliaryData("tag3", true, null) + ); reservationRequestGet.setAuxData(auxData); reservationRequestGet.setSlot("2012-01-01T13:00", "PT1H"); From 2ee039e8edfe7e6a69473d3e57423068a8638cc6 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 3 Nov 2023 19:18:11 +0100 Subject: [PATCH 079/134] [client-cli] add aux data to CLI --- .../Shongo/ClientCli/API/AuxiliaryData.pm | 45 +++++++++++++++++++ .../API/ReservationRequestAbstract.pm | 8 +++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 shongo-client-cli/src/main/perl/Shongo/ClientCli/API/AuxiliaryData.pm diff --git a/shongo-client-cli/src/main/perl/Shongo/ClientCli/API/AuxiliaryData.pm b/shongo-client-cli/src/main/perl/Shongo/ClientCli/API/AuxiliaryData.pm new file mode 100644 index 000000000..76a29a3b2 --- /dev/null +++ b/shongo-client-cli/src/main/perl/Shongo/ClientCli/API/AuxiliaryData.pm @@ -0,0 +1,45 @@ +# +# Auxiliary data for ReservationRequestAbstract +# +# @author Filip Karnis +# +package Shongo::ClientCli::API::AuxiliaryData; +use base qw(Shongo::ClientCli::API::Object); + +use strict; +use warnings; + +use Shongo::Common; +use Shongo::Console; + +# +# Create a new instance of auxiliary data +# +# @static +# +sub new() +{ + my $class = shift; + my (%attributes) = @_; + my $self = Shongo::ClientCli::API::Object->new(@_); + bless $self, $class; + + $self->set_object_class('AuxData'); + $self->set_object_name('Auxiliary Data'); + $self->add_attribute('tagName', { + 'required' => 1, + 'type' => 'string', + }); + $self->add_attribute('enabled', { + 'required' => 1, + 'type' => 'bool', + }); + $self->add_attribute('data', { + 'required' => 0, + 'type' => 'string', + }); + + return $self; +} + +1; diff --git a/shongo-client-cli/src/main/perl/Shongo/ClientCli/API/ReservationRequestAbstract.pm b/shongo-client-cli/src/main/perl/Shongo/ClientCli/API/ReservationRequestAbstract.pm index ca4f178c9..013de7872 100644 --- a/shongo-client-cli/src/main/perl/Shongo/ClientCli/API/ReservationRequestAbstract.pm +++ b/shongo-client-cli/src/main/perl/Shongo/ClientCli/API/ReservationRequestAbstract.pm @@ -13,6 +13,7 @@ use Shongo::Common; use Shongo::Console; use Shongo::ClientCli::API::ReservationRequest; use Shongo::ClientCli::API::ReservationRequestSet; +use Shongo::ClientCli::API::AuxiliaryData; # Enumeration of reservation request purpose our $Purpose = ordered_hash( @@ -90,7 +91,12 @@ sub new() ) }); $self->add_attribute('auxData', { - 'title' => 'Auxiliary data', + 'type' => 'collection', + 'item' => { + 'title' => 'Auxiliary Data', + 'class' => 'Shongo::ClientCli::API::AuxiliaryData', + 'short' => 1, + }, 'optional' => 1, }); From f66472a02d9d143005b5e363000fab94e49d4ce2 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 28 Jul 2023 22:01:19 +0200 Subject: [PATCH 080/134] [controller] merge aux data with tag data in sql query instead of in Java --- .../AbstractReservationRequestAuxData.java | 30 +++++++ .../request/ReservationRequestManager.java | 78 +++++++++++++++++++ .../request/auxdata/AuxDataException.java | 15 ---- .../request/auxdata/AuxDataMerged.java | 20 +++++ .../request/auxdata/AuxDataService.java | 75 ------------------ .../auxdata/tagdata/NotifyEmailAuxData.java | 11 ++- .../auxdata/tagdata/ReservationAuxData.java | 7 +- .../request/auxdata/tagdata/TagData.java | 22 +++--- .../notification/ReservationNotification.java | 39 ++++------ .../controller/scheduler/Scheduler.java | 2 +- .../main/resources/sql/postgresql/init.sql | 9 +++ 11 files changed, 172 insertions(+), 136 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequestAuxData.java delete mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataException.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataMerged.java delete mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataService.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequestAuxData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequestAuxData.java new file mode 100644 index 000000000..92ebe267b --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequestAuxData.java @@ -0,0 +1,30 @@ +package cz.cesnet.shongo.controller.booking.request; + +import com.fasterxml.jackson.databind.JsonNode; +import cz.cesnet.shongo.controller.booking.specification.Specification; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.Immutable; +import org.hibernate.annotations.Type; + +import javax.persistence.*; + +@Getter +@Setter +@Entity +@Immutable +@Table(name = "arr_aux_data") +public class AbstractReservationRequestAuxData +{ + + @Id + private Long id; + + private String tagName; + private Boolean enabled; + @Type(type = "jsonb") + @Column(columnDefinition = "text") + private JsonNode data; + @ManyToOne(cascade = CascadeType.ALL, optional = false, fetch = FetchType.LAZY) + private Specification specification; +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java index 1b657cbac..272f2a0b1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java @@ -1,8 +1,10 @@ package cz.cesnet.shongo.controller.booking.request; +import com.fasterxml.jackson.databind.JsonNode; import cz.cesnet.shongo.AbstractManager; import cz.cesnet.shongo.CommonReportSet; import cz.cesnet.shongo.controller.ControllerReportSetHelper; +import cz.cesnet.shongo.controller.api.TagType; import cz.cesnet.shongo.controller.authorization.AuthorizationManager; import cz.cesnet.shongo.controller.booking.Allocation; import cz.cesnet.shongo.controller.booking.compartment.CompartmentSpecification; @@ -10,6 +12,9 @@ import cz.cesnet.shongo.controller.booking.participant.InvitedPersonParticipant; import cz.cesnet.shongo.controller.booking.participant.AbstractParticipant; import cz.cesnet.shongo.controller.booking.participant.PersonParticipant; +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataFilter; +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataMerged; +import cz.cesnet.shongo.controller.booking.request.auxdata.tagdata.TagData; import cz.cesnet.shongo.controller.booking.specification.Specification; import cz.cesnet.shongo.controller.booking.reservation.Reservation; import cz.cesnet.shongo.controller.booking.reservation.ReservationManager; @@ -19,7 +24,9 @@ import javax.persistence.EntityManager; import javax.persistence.NoResultException; +import javax.persistence.TypedQuery; import java.util.*; +import java.util.stream.Collectors; /** * Manager for {@link AbstractReservationRequest}. @@ -635,4 +642,75 @@ public List detachReports(ReservationRequest reservationRequest reservationRequest.clearReports(); return reports; } + + /** + * Creates {@link TagData} for given {@link AbstractReservationRequest} and its corresponding + * {@link cz.cesnet.shongo.controller.booking.resource.Tag}s. + * + * @param reservationRequest reservation request for which the {@link TagData} shall be created + * @param filter filter for data desired + * @return specific implementation of {@link TagData} based on {@link TagType} + * @param TagData implementation for corresponding {@link TagType} + */ + public > List getTagData(AbstractReservationRequest reservationRequest, AuxDataFilter filter) + { + return getAuxData(reservationRequest, filter) + .stream() + .map(TagData::create) + .map(data -> (T) data) + .collect(Collectors.toList()); + } + + /** + * Merge {@link cz.cesnet.shongo.controller.booking.request.auxdata.AuxData} from {@link AbstractReservationRequest} + * and data from its corresponding {@link cz.cesnet.shongo.controller.booking.resource.Tag}s. + * + * @param reservationRequest reservation request for which the data shall be merged + * @param filter filter for data desired + * @return merged data + */ + private List getAuxData(AbstractReservationRequest reservationRequest, AuxDataFilter filter) + { + String queryString = "SELECT arr.tagName, rt.tag.type, arr.enabled, arr.data, rt.tag.data" + + " FROM AbstractReservationRequestAuxData arr" + + " JOIN ResourceSpecification res_spec ON res_spec.id = arr.specification.id" + + " JOIN ResourceTag rt ON rt.resource.id = res_spec.resource.id" + + " WHERE rt.tag.name = arr.tagName" + + " AND arr.id = :id"; + if (filter.getTagName() != null) { + queryString += " AND rt.tag.name = :tagName"; + } + if (filter.getTagType() != null) { + queryString += " AND rt.tag.type = :type"; + } + if (filter.getEnabled() != null) { + queryString += " AND arr.enabled = :enabled"; + } + + TypedQuery query = entityManager.createQuery(queryString, Object[].class) + .setParameter("id", reservationRequest.getId()); + if (filter.getTagName() != null) { + query.setParameter("tagName", filter.getTagName()); + } + if (filter.getTagType() != null) { + query.setParameter("type", filter.getTagType()); + } + if (filter.getEnabled() != null) { + query.setParameter("enabled", filter.getEnabled()); + } + + return query + .getResultList() + .stream() + .map(record -> { + AuxDataMerged auxDataMerged = new AuxDataMerged(); + auxDataMerged.setTagName((String) record[0]); + auxDataMerged.setType((TagType) record[1]); + auxDataMerged.setEnabled((Boolean) record[2]); + auxDataMerged.setAuxData((JsonNode) record[3]); + auxDataMerged.setData((JsonNode) record[4]); + return auxDataMerged; + }) + .collect(Collectors.toList()); + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataException.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataException.java deleted file mode 100644 index feb1363f1..000000000 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataException.java +++ /dev/null @@ -1,15 +0,0 @@ -package cz.cesnet.shongo.controller.booking.request.auxdata; - -public class AuxDataException extends Exception -{ - - public AuxDataException(String message) - { - super(message); - } - - public AuxDataException(String message, Throwable cause) - { - super(message, cause); - } -} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataMerged.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataMerged.java new file mode 100644 index 000000000..799ffe5a3 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataMerged.java @@ -0,0 +1,20 @@ +package cz.cesnet.shongo.controller.booking.request.auxdata; + +import com.fasterxml.jackson.databind.JsonNode; +import cz.cesnet.shongo.controller.api.TagType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AuxDataMerged +{ + + private String tagName; + private TagType type; + private Boolean enabled; + private JsonNode data; + private JsonNode auxData; +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataService.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataService.java deleted file mode 100644 index 6b27c9f30..000000000 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataService.java +++ /dev/null @@ -1,75 +0,0 @@ -package cz.cesnet.shongo.controller.booking.request.auxdata; - -import com.fasterxml.jackson.core.JsonProcessingException; -import cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest; -import cz.cesnet.shongo.controller.booking.request.auxdata.tagdata.TagData; -import cz.cesnet.shongo.controller.booking.resource.Resource; -import cz.cesnet.shongo.controller.booking.resource.ResourceManager; -import cz.cesnet.shongo.controller.booking.resource.ResourceSpecification; -import cz.cesnet.shongo.controller.booking.resource.Tag; -import cz.cesnet.shongo.controller.booking.room.RoomSpecification; -import cz.cesnet.shongo.controller.booking.specification.Specification; - -import javax.persistence.EntityManager; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -public class AuxDataService -{ - - public static > List getTagData( - AbstractReservationRequest reservationRequest, - AuxDataFilter filter, EntityManager entityManager - ) throws AuxDataException, JsonProcessingException - { - List> tagData = getAllTagData(reservationRequest, entityManager); - - return tagData - .stream() - .filter(data -> data.filter(filter)) - .map(data -> (T) data) - .collect(Collectors.toList()); - } - - private static List> getAllTagData( - AbstractReservationRequest reservationRequest, - EntityManager entityManager - ) throws AuxDataException, JsonProcessingException - { - if (reservationRequest.getAuxData() == null) { - throw new AuxDataException("AuxData is null"); - } - - List auxData = reservationRequest.getAuxDataList(); - - Resource resource = getResource(reservationRequest); - if (resource == null) { - throw new AuxDataException("Resource is null"); - } - List resourceTags = new ResourceManager(entityManager).getResourceTags(resource); - - List> tagData = new ArrayList<>(); - for (Tag tag : resourceTags) { - for (AuxData auxData1 : auxData) { - if (auxData1.getTagName().equals(tag.getName())) { - tagData.add(TagData.create(tag, auxData1)); - } - } - } - return tagData; - } - - private static Resource getResource(AbstractReservationRequest reservationRequest) throws AuxDataException - { - Specification specification = reservationRequest.getSpecification(); - if (specification instanceof ResourceSpecification) { - return ((ResourceSpecification) specification).getResource(); - } - else if (specification instanceof RoomSpecification) { - return ((RoomSpecification) specification).getDeviceResource(); - } - throw new AuxDataException(String.format("Specification: %s does not have a resource (so neither tags)", - specification.getClass())); - } -} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/NotifyEmailAuxData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/NotifyEmailAuxData.java index d6a0bdeed..5f012f7f2 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/NotifyEmailAuxData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/NotifyEmailAuxData.java @@ -1,8 +1,7 @@ package cz.cesnet.shongo.controller.booking.request.auxdata.tagdata; import com.fasterxml.jackson.databind.JsonNode; -import cz.cesnet.shongo.controller.booking.request.auxdata.AuxData; -import cz.cesnet.shongo.controller.booking.resource.Tag; +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataMerged; import java.util.ArrayList; import java.util.List; @@ -10,9 +9,9 @@ public class NotifyEmailAuxData extends TagData> { - public NotifyEmailAuxData(Tag tag, AuxData auxData) + public NotifyEmailAuxData(AuxDataMerged auxData) { - super(tag, auxData); + super(auxData); } @Override @@ -20,10 +19,10 @@ public List getData() { List emails = new ArrayList<>(); - for (JsonNode child : tag.getData()) { + for (JsonNode child : auxData.getAuxData()) { emails.add(child.asText()); } - for (JsonNode child : aux.getData()) { + for (JsonNode child : auxData.getData()) { emails.add(child.asText()); } return emails; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/ReservationAuxData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/ReservationAuxData.java index a19d5487b..b7d616fa7 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/ReservationAuxData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/ReservationAuxData.java @@ -1,15 +1,14 @@ package cz.cesnet.shongo.controller.booking.request.auxdata.tagdata; import cz.cesnet.shongo.TodoImplementException; -import cz.cesnet.shongo.controller.booking.request.auxdata.AuxData; -import cz.cesnet.shongo.controller.booking.resource.Tag; +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataMerged; public class ReservationAuxData extends TagData { - public ReservationAuxData(Tag tag, AuxData auxData) + public ReservationAuxData(AuxDataMerged auxData) { - super(tag, auxData); + super(auxData); } @Override diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java index df8221982..76e890a6b 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java @@ -1,9 +1,8 @@ package cz.cesnet.shongo.controller.booking.request.auxdata.tagdata; import cz.cesnet.shongo.TodoImplementException; -import cz.cesnet.shongo.controller.booking.request.auxdata.AuxData; import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataFilter; -import cz.cesnet.shongo.controller.booking.resource.Tag; +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataMerged; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.ToString; @@ -16,37 +15,36 @@ public abstract class TagData { - protected final Tag tag; - protected final AuxData aux; + protected final AuxDataMerged auxData; public abstract T getData(); - public static TagData create(Tag tag, AuxData auxData) + public static TagData create(AuxDataMerged auxData) { - switch (tag.getType()) { + switch (auxData.getType()) { case NOTIFY_EMAIL: - return new NotifyEmailAuxData(tag, auxData); + return new NotifyEmailAuxData(auxData); case RESERVATION_DATA: - return new ReservationAuxData(tag, auxData); + return new ReservationAuxData(auxData); default: - throw new TodoImplementException("Not implemented for tag type: " + tag.getType()); + throw new TodoImplementException("Not implemented for tag type: " + auxData.getType()); } } public boolean filter(AuxDataFilter filter) { if (filter.getTagName() != null) { - if (!filter.getTagName().equals(tag.getName())) { + if (!filter.getTagName().equals(auxData.getTagName())) { return false; } } if (filter.getTagType() != null) { - if (!filter.getTagType().equals(tag.getType())) { + if (!filter.getTagType().equals(auxData.getType())) { return false; } } if (filter.getEnabled() != null) { - if (!filter.getEnabled().equals(aux.isEnabled())) { + if (!filter.getEnabled().equals(auxData.getEnabled())) { return false; } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/notification/ReservationNotification.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/notification/ReservationNotification.java index 92e9083ae..44096943a 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/notification/ReservationNotification.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/notification/ReservationNotification.java @@ -1,7 +1,6 @@ package cz.cesnet.shongo.controller.notification; -import com.fasterxml.jackson.core.JsonProcessingException; import cz.cesnet.shongo.AliasType; import cz.cesnet.shongo.PersonInformation; import cz.cesnet.shongo.TodoImplementException; @@ -14,9 +13,8 @@ import cz.cesnet.shongo.controller.booking.ObjectIdentifier; import cz.cesnet.shongo.controller.booking.alias.Alias; import cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest; -import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataException; +import cz.cesnet.shongo.controller.booking.request.ReservationRequestManager; import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataFilter; -import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataService; import cz.cesnet.shongo.controller.booking.request.auxdata.tagdata.NotifyEmailAuxData; import cz.cesnet.shongo.controller.booking.reservation.Reservation; import cz.cesnet.shongo.controller.booking.resource.Resource; @@ -52,7 +50,9 @@ public abstract class ReservationNotification extends AbstractReservationRequest private Map childTargetByReservation = new LinkedHashMap(); private ReservationNotification(Reservation reservation, - AbstractReservationRequest reservationRequest, AuthorizationManager authorizationManager) + AbstractReservationRequest reservationRequest, + AuthorizationManager authorizationManager, + ReservationRequestManager reservationRequestManager) { super(reservationRequest); @@ -70,7 +70,7 @@ private ReservationNotification(Reservation reservation, // Add administrators as recipients addAdministratorRecipientsForReservation(reservation.getTargetReservation(), authorizationManager); - addRecipientsFromNotificationTags(reservationRequest, entityManager); + addRecipientsFromNotificationTags(reservationRequest, reservationRequestManager); // Add child targets for (Reservation childReservation : reservation.getChildReservations()) { @@ -79,23 +79,14 @@ private ReservationNotification(Reservation reservation, } private void addRecipientsFromNotificationTags(AbstractReservationRequest reservationRequest, - EntityManager entityManager) + ReservationRequestManager reservationRequestManager) { AuxDataFilter filter = AuxDataFilter.builder() .tagType(TagType.NOTIFY_EMAIL) .enabled(true) .build(); - List notifyEmailAuxData; - try { - notifyEmailAuxData = AuxDataService.getTagData(reservationRequest, filter, entityManager); - } catch (JsonProcessingException e) { - logger.error("Error while parsing auxData", e); - return; - } catch (AuxDataException e) { - logger.warn("Error while getting notify email aux data for reservation request {}.", reservationRequest.getId(), e); - return; - } + List notifyEmailAuxData = reservationRequestManager.getTagData(reservationRequest, filter); List tagPersonInformationList = notifyEmailAuxData .stream() @@ -111,7 +102,7 @@ private Collection notifyEmailDataToPersonInformation(Noti return notifyEmailAuxData .getData() .stream() - .map(email -> new TagPersonInformation(notifyEmailAuxData.getTag().getName(), email)) + .map(email -> new TagPersonInformation(notifyEmailAuxData.getAuxData().getTagName(), email)) .collect(Collectors.toList()); } @@ -384,9 +375,10 @@ public static class New extends ReservationNotification { private Long previousReservationId; - public New(Reservation reservation, Reservation previousReservation, AuthorizationManager authorizationManager) + public New(Reservation reservation, Reservation previousReservation, AuthorizationManager authorizationManager, + ReservationRequestManager reservationRequestManager) { - super(reservation, getReservationRequest(reservation), authorizationManager); + super(reservation, getReservationRequest(reservation), authorizationManager, reservationRequestManager); this.previousReservationId = (previousReservation != null ? previousReservation.getId() : null); } @@ -418,14 +410,15 @@ protected String getTitleReservationId(RenderContext renderContext) public static class Deleted extends ReservationNotification { public Deleted(Reservation reservation, AbstractReservationRequest reservationRequest, - AuthorizationManager authorizationManager) + AuthorizationManager authorizationManager, ReservationRequestManager reservationRequestManager) { - super(reservation, reservationRequest, authorizationManager); + super(reservation, reservationRequest, authorizationManager, reservationRequestManager); } - public Deleted(Reservation reservation, AuthorizationManager authorizationManager) + public Deleted(Reservation reservation, AuthorizationManager authorizationManager, + ReservationRequestManager reservationRequestManager) { - super(reservation, getReservationRequest(reservation), authorizationManager); + super(reservation, getReservationRequest(reservation), authorizationManager, reservationRequestManager); } @Override diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/scheduler/Scheduler.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/scheduler/Scheduler.java index 54b95b6d9..e5995e98d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/scheduler/Scheduler.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/scheduler/Scheduler.java @@ -590,7 +590,7 @@ private void allocateReservationRequest(ReservationRequest reservationRequest, S // Create notification contextState.addNotification(new ReservationNotification.New( - allocatedReservation, previousReservation, authorizationManager)); + allocatedReservation, previousReservation, authorizationManager, reservationRequestManager)); // Update reservation request if (context.getRequestWantedState() != null) { diff --git a/shongo-controller/src/main/resources/sql/postgresql/init.sql b/shongo-controller/src/main/resources/sql/postgresql/init.sql index 760fb4fca..4b120d52c 100644 --- a/shongo-controller/src/main/resources/sql/postgresql/init.sql +++ b/shongo-controller/src/main/resources/sql/postgresql/init.sql @@ -13,6 +13,7 @@ DROP VIEW IF EXISTS reservation_request_earliest_usage; DROP VIEW IF EXISTS reservation_summary; DROP VIEW IF EXISTS executable_summary_view; DROP VIEW IF EXISTS room_endpoint_earliest_usage; +DROP VIEW IF EXISTS arr_aux_data; /** * Create missing foreign keys' indexes. @@ -541,3 +542,11 @@ ORDER BY executable.id, alias.id; CREATE TABLE executable_summary AS SELECT * FROM executable_summary_view; CREATE TABLE specification_summary AS SELECT * FROM specification_summary_view; + +CREATE VIEW arr_aux_data AS +SELECT + arr.*, + jsonb_array_elements(aux_data)->>'tagName' AS tag_name, + (jsonb_array_elements(aux_data)->>'enabled')::boolean AS enabled, + jsonb_array_elements(aux_data)->'data' AS data +FROM abstract_reservation_request arr; From be28b662dc9d41e0fcfa25f60da2f71a2168be99 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 28 Jul 2023 23:16:21 +0200 Subject: [PATCH 081/134] [controller] add data validation to AuxData --- .../request/ReservationRequestManager.java | 13 ++++--- .../request/auxdata/AuxDataMerged.java | 16 ++++----- .../auxdata/tagdata/DefaultAuxData.java | 18 ++++++++++ .../auxdata/tagdata/NotifyEmailAuxData.java | 34 ++++++++++++++++++- .../auxdata/tagdata/ReservationAuxData.java | 18 +++++++--- .../request/auxdata/tagdata/TagData.java | 15 +++++--- 6 files changed, 88 insertions(+), 26 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/DefaultAuxData.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java index 272f2a0b1..3516db3b0 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java @@ -703,13 +703,12 @@ private List getAuxData(AbstractReservationRequest reservationReq .getResultList() .stream() .map(record -> { - AuxDataMerged auxDataMerged = new AuxDataMerged(); - auxDataMerged.setTagName((String) record[0]); - auxDataMerged.setType((TagType) record[1]); - auxDataMerged.setEnabled((Boolean) record[2]); - auxDataMerged.setAuxData((JsonNode) record[3]); - auxDataMerged.setData((JsonNode) record[4]); - return auxDataMerged; + final String tagName = (String) record[0]; + final TagType type = (TagType) record[1]; + final Boolean enabled = (Boolean) record[2]; + final JsonNode auxData = (JsonNode) record[3]; + final JsonNode data = (JsonNode) record[4]; + return new AuxDataMerged(tagName, type, enabled, data, auxData); }) .collect(Collectors.toList()); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataMerged.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataMerged.java index 799ffe5a3..d501d2bf5 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataMerged.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataMerged.java @@ -3,18 +3,16 @@ import com.fasterxml.jackson.databind.JsonNode; import cz.cesnet.shongo.controller.api.TagType; import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.Value; -@Data -@NoArgsConstructor +@Value @AllArgsConstructor public class AuxDataMerged { - private String tagName; - private TagType type; - private Boolean enabled; - private JsonNode data; - private JsonNode auxData; + String tagName; + TagType type; + Boolean enabled; + JsonNode data; + JsonNode auxData; } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/DefaultAuxData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/DefaultAuxData.java new file mode 100644 index 000000000..59504529d --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/DefaultAuxData.java @@ -0,0 +1,18 @@ +package cz.cesnet.shongo.controller.booking.request.auxdata.tagdata; + +import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataMerged; + +public class DefaultAuxData extends TagData +{ + + public DefaultAuxData(AuxDataMerged auxData) + { + super(auxData); + } + + @Override + protected Void constructData() + { + return null; + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/NotifyEmailAuxData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/NotifyEmailAuxData.java index 5f012f7f2..d59d7623f 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/NotifyEmailAuxData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/NotifyEmailAuxData.java @@ -1,22 +1,37 @@ package cz.cesnet.shongo.controller.booking.request.auxdata.tagdata; import com.fasterxml.jackson.databind.JsonNode; +import cz.cesnet.shongo.controller.api.TagType; import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataMerged; +import lombok.extern.slf4j.Slf4j; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; import java.util.ArrayList; import java.util.List; +@Slf4j public class NotifyEmailAuxData extends TagData> { public NotifyEmailAuxData(AuxDataMerged auxData) { super(auxData); + if (!TagType.NOTIFY_EMAIL.equals(auxData.getType())) { + throw new IllegalArgumentException("AuxData is not of type NOTIFY_EMAIL"); + } } @Override - public List getData() + protected List constructData() { + if (!auxData.getData().isArray()) { + throw new IllegalArgumentException("Tag data is not an array"); + } + if (!auxData.getAuxData().isArray()) { + throw new IllegalArgumentException("AuxData data is not an array"); + } + List emails = new ArrayList<>(); for (JsonNode child : auxData.getAuxData()) { @@ -25,6 +40,23 @@ public List getData() for (JsonNode child : auxData.getData()) { emails.add(child.asText()); } + emails.forEach(email -> { + if (!isValidEmailAddress(email)) { + throw new IllegalArgumentException("Invalid email address: " + email); + } + }); + return emails; } + + public static boolean isValidEmailAddress(String email) { + try { + InternetAddress emailAddr = new InternetAddress(email); + emailAddr.validate(); + } catch (AddressException ex) { + log.info("Invalid email address: " + email, ex); + return false; + } + return true; + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/ReservationAuxData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/ReservationAuxData.java index b7d616fa7..f5483db41 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/ReservationAuxData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/ReservationAuxData.java @@ -1,19 +1,29 @@ package cz.cesnet.shongo.controller.booking.request.auxdata.tagdata; -import cz.cesnet.shongo.TodoImplementException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import cz.cesnet.shongo.controller.api.TagType; import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataMerged; -public class ReservationAuxData extends TagData +import java.util.Map; + +public class ReservationAuxData extends TagData> { public ReservationAuxData(AuxDataMerged auxData) { super(auxData); + if (!TagType.RESERVATION_DATA.equals(auxData.getType())) { + throw new IllegalArgumentException("AuxData is not of type RESERVATION_DATA"); + } } @Override - public String getData() + protected Map constructData() { - throw new TodoImplementException("Not implemented"); + Map tagMap = new ObjectMapper().convertValue(auxData.getData(), new TypeReference<>() {}); + Map auxMap = new ObjectMapper().convertValue(auxData.getAuxData(), new TypeReference<>() {}); + tagMap.putAll(auxMap); + return tagMap; } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java index 76e890a6b..01744c8f0 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java @@ -4,24 +4,29 @@ import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataFilter; import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataMerged; import lombok.Getter; -import lombok.RequiredArgsConstructor; import lombok.ToString; -import lombok.extern.slf4j.Slf4j; -@Slf4j @Getter @ToString -@RequiredArgsConstructor public abstract class TagData { protected final AuxDataMerged auxData; + protected final T data; - public abstract T getData(); + protected TagData(AuxDataMerged auxData) + { + this.auxData = auxData; + this.data = constructData(); + } + + protected abstract T constructData(); public static TagData create(AuxDataMerged auxData) { switch (auxData.getType()) { + case DEFAULT: + return new DefaultAuxData(auxData); case NOTIFY_EMAIL: return new NotifyEmailAuxData(auxData); case RESERVATION_DATA: From 4706aa251243881798482666823c4728b565999e Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Tue, 7 Nov 2023 23:52:28 +0100 Subject: [PATCH 082/134] [controller] change AuxData and Tag data to use JsonNode instead of String --- .../java/cz/cesnet/shongo/api/Converter.java | 33 ++++++++++++++ .../java/cz/cesnet/shongo/api/DataMap.java | 11 +++++ .../onto/CustomBeanOntologyBuilder.java | 4 ++ .../shongo/controller/api/AuxiliaryData.java | 44 +++---------------- .../api/ReservationRequestSummary.java | 18 ++++++-- .../cz/cesnet/shongo/controller/api/Tag.java | 29 +++++++++--- .../booking/request/auxdata/AuxData.java | 2 +- .../controller/booking/resource/Tag.java | 25 +---------- .../ReservationRequestModificationTest.java | 10 +++-- .../controller/booking/resource/TagTest.java | 12 +++-- 10 files changed, 110 insertions(+), 78 deletions(-) diff --git a/shongo-common-api/src/main/java/cz/cesnet/shongo/api/Converter.java b/shongo-common-api/src/main/java/cz/cesnet/shongo/api/Converter.java index a8a26a9ea..e25a235df 100644 --- a/shongo-common-api/src/main/java/cz/cesnet/shongo/api/Converter.java +++ b/shongo-common-api/src/main/java/cz/cesnet/shongo/api/Converter.java @@ -1,5 +1,8 @@ package cz.cesnet.shongo.api; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import cz.cesnet.shongo.CommonReportSet; import cz.cesnet.shongo.Temporal; import cz.cesnet.shongo.TodoImplementException; @@ -35,6 +38,8 @@ public class Converter private static final DateTimeFormatter DATE_TIME_FORMATTER = ISODateTimeFormat.dateTimeParser(); + private static final ObjectMapper objectMapper = new ObjectMapper(); + /** * Convert given {@code value} to {@link String}. * @@ -64,6 +69,9 @@ else if (value instanceof Period ) { else if (value instanceof Interval ) { return convertIntervalToString((Interval) value); } + else if (value instanceof JsonNode) { + return convertJsonNodeToString((JsonNode) value); + } else { throw new TodoImplementException(value.getClass()); } @@ -776,6 +784,31 @@ public static List convertToList(Object value, Class componentClass) return list; } + public static String convertJsonNodeToString(JsonNode jsonNode) + { + if (jsonNode == null) { + return ""; + } else { + try { + return objectMapper.writeValueAsString(jsonNode); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + } + + public static JsonNode convertToJsonNode(String value) + { + if (value.isEmpty()) { + return null; + } + try { + return objectMapper.readTree(value); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + /** * Convert given {@code value} to {@link List} value with items of any of given {@code componentClasses}. * diff --git a/shongo-common-api/src/main/java/cz/cesnet/shongo/api/DataMap.java b/shongo-common-api/src/main/java/cz/cesnet/shongo/api/DataMap.java index 612f96732..c32adbae5 100644 --- a/shongo-common-api/src/main/java/cz/cesnet/shongo/api/DataMap.java +++ b/shongo-common-api/src/main/java/cz/cesnet/shongo/api/DataMap.java @@ -1,5 +1,6 @@ package cz.cesnet.shongo.api; +import com.fasterxml.jackson.databind.JsonNode; import cz.cesnet.shongo.CommonReportSet; import cz.cesnet.shongo.TodoImplementException; import org.joda.time.*; @@ -127,6 +128,11 @@ public void set(String property, ReadablePartial readablePartial) setNotNull(property, Converter.convertReadablePartialToString(readablePartial)); } + public void set(String property, JsonNode jsonNode) + { + setNotNull(property, Converter.convertJsonNodeToString(jsonNode)); + } + public void set(String property, Collection collection) { setNotNull(property, collection); @@ -338,6 +344,11 @@ public ReadablePartial getReadablePartial(String property) return Converter.convertToReadablePartial(data.get(property)); } + public JsonNode getJsonNode(String property) + { + return Converter.convertToJsonNode(getString(property)); + } + public List getList(String property, Class componentClass) { return Converter.convertToList(data.get(property), componentClass); diff --git a/shongo-common-api/src/main/java/jade/content/onto/CustomBeanOntologyBuilder.java b/shongo-common-api/src/main/java/jade/content/onto/CustomBeanOntologyBuilder.java index f6c48abab..ea898afba 100644 --- a/shongo-common-api/src/main/java/jade/content/onto/CustomBeanOntologyBuilder.java +++ b/shongo-common-api/src/main/java/jade/content/onto/CustomBeanOntologyBuilder.java @@ -450,6 +450,10 @@ private ObjectSchema doAddSchema(Class clazz, boolean buildHierarchy) throws return doAddFlatSchema(clazz); } } + catch (StringIndexOutOfBoundsException e) { + logger.info("skip class " + clazz + " because of " + e); + return null; + } catch (BeanOntologyException boe) { throw boe; diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AuxiliaryData.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AuxiliaryData.java index b65a15807..5632c0b69 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AuxiliaryData.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AuxiliaryData.java @@ -1,7 +1,6 @@ package cz.cesnet.shongo.controller.api; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import cz.cesnet.shongo.api.AbstractComplexType; @@ -9,7 +8,6 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import lombok.ToString; @Data @NoArgsConstructor @@ -21,49 +19,19 @@ public class AuxiliaryData extends AbstractComplexType private String tagName; private boolean enabled; - @ToString.Exclude - @EqualsAndHashCode.Exclude - private String data = objectMapper.nullNode().toString(); + private JsonNode data; - public AuxiliaryData(String tagName, boolean enabled, String data) + public AuxiliaryData(String tagName, boolean enabled, JsonNode data) { setTagName(tagName); setEnabled(enabled); setData(data); } - @JsonIgnore - @ToString.Include(name = "data") - @EqualsAndHashCode.Include - public JsonNode getDataAsJsonNode() - { - if (data == null) { - return null; - } - - try { - return objectMapper.readTree(data); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - public void setData(String data) - { - if (data == null) { - this.data = objectMapper.nullNode().toString(); - return; - } - + public void setData(JsonNode data) { this.data = data; - } - - public void setData(JsonNode data) - { - try { - this.data = objectMapper.writeValueAsString(data); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); + if (data == null) { + this.data = objectMapper.nullNode(); } } @@ -89,6 +57,6 @@ public void fromData(DataMap dataMap) super.fromData(dataMap); tagName = dataMap.getString("tagName"); enabled = dataMap.getBoolean("enabled"); - data = dataMap.getString("data"); + data = dataMap.getJsonNode("data"); } } diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationRequestSummary.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationRequestSummary.java index dc0be659a..efd92bb21 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationRequestSummary.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationRequestSummary.java @@ -1,6 +1,8 @@ package cz.cesnet.shongo.controller.api; +import com.fasterxml.jackson.databind.JsonNode; import cz.cesnet.shongo.Technology; +import cz.cesnet.shongo.api.Converter; import cz.cesnet.shongo.api.DataMap; import cz.cesnet.shongo.api.IdentifiedComplexType; import cz.cesnet.shongo.controller.ReservationRequestPurpose; @@ -130,7 +132,7 @@ public class ReservationRequestSummary extends IdentifiedComplexType /** * Auxiliary data. This data are specified by the {@link Tag}s of {@link Resource} which is requested for reservation. */ - private String auxData; + private JsonNode auxData; /** * @return {@link #resourceTags} @@ -511,7 +513,7 @@ public void setAllowCache(boolean allowCache) /** * @return {@link #auxData} */ - public String getAuxData() + public JsonNode getAuxData() { return auxData; } @@ -519,11 +521,19 @@ public String getAuxData() /** * @param auxData sets the {@link #auxData} */ - public void setAuxData(String auxData) + public void setAuxData(JsonNode auxData) { this.auxData = auxData; } + /** + * @param auxData sets the {@link #auxData} + */ + public void setAuxData(String auxData) + { + this.auxData = Converter.convertToJsonNode(auxData); + } + private static final String PARENT_RESERVATION_REQUEST_ID = "parentReservationRequestId"; private static final String TYPE = "type"; private static final String DATETIME = "dateTime"; @@ -604,7 +614,7 @@ public void fromData(DataMap dataMap) roomHasRecordings = dataMap.getBool(ROOM_HAS_RECORDINGS); allowCache = dataMap.getBool(ALLOW_CACHE); resourceTags = dataMap.getList(RESOURCE_TAGS, Tag.class); - auxData = dataMap.getString(AUX_DATA); + auxData = dataMap.getJsonNode(AUX_DATA); } /** diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/Tag.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/Tag.java index b746fb7af..1953a1e0f 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/Tag.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/Tag.java @@ -1,5 +1,9 @@ package cz.cesnet.shongo.controller.api; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import cz.cesnet.shongo.api.DataMap; import cz.cesnet.shongo.api.IdentifiedComplexType; @@ -11,9 +15,11 @@ */ public class Tag extends IdentifiedComplexType { + private static final ObjectMapper objectMapper = new ObjectMapper(); + String name; TagType type = TagType.DEFAULT; - String data; + JsonNode data; public String getName() { return name; @@ -33,12 +39,12 @@ public void setType(TagType type) this.type = type; } - public String getData() + public JsonNode getData() { return data; } - public void setData(String data) + public void setData(JsonNode data) { this.data = data; } @@ -51,11 +57,21 @@ public static Tag fromConcat(String concat) tag.setName(parts[1]); tag.setType(TagType.valueOf(parts[2])); if (parts.length > 3) { - tag.setData(parts[3]); + try { + tag.setData(objectMapper.readTree(parts[3])); + } catch (JsonProcessingException e) { + throw new RuntimeException("Error parsing tag data", e); + } } return tag; } + @Override + @JsonIgnore + public String getClassName() { + return super.getClassName(); + } + private static final String NAME = "name"; private static final String TYPE = "type"; private static final String DATA = "data"; @@ -76,7 +92,10 @@ public void fromData(DataMap dataMap) super.fromData(dataMap); name = dataMap.getString(NAME); type = dataMap.getEnumRequired(TYPE, TagType.class); - data = dataMap.getString(DATA); + JsonNode jsonNode = dataMap.getJsonNode(DATA); + if (jsonNode != null) { + data = jsonNode; + } } @Override diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxData.java index fcc352427..4035862d9 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxData.java @@ -25,7 +25,7 @@ public static AuxData fromApi(cz.cesnet.shongo.controller.api.AuxiliaryData api) AuxData auxData = new AuxData(); auxData.setTagName(api.getTagName()); auxData.setEnabled(api.isEnabled()); - auxData.setData(api.getDataAsJsonNode()); + auxData.setData(api.getData()); return auxData; } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/resource/Tag.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/resource/Tag.java index 6df57e70d..a56e37112 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/resource/Tag.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/resource/Tag.java @@ -1,8 +1,6 @@ package cz.cesnet.shongo.controller.booking.resource; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import cz.cesnet.shongo.SimplePersistentObject; import cz.cesnet.shongo.api.AbstractComplexType; import cz.cesnet.shongo.controller.api.TagType; @@ -13,7 +11,6 @@ import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; -import javax.persistence.Transient; /** * @author: Ondřej Pavelka @@ -21,9 +18,6 @@ @Entity public class Tag extends SimplePersistentObject { - @Transient - private final ObjectMapper objectMapper = new ObjectMapper(); - private String name; private TagType type; @@ -81,16 +75,7 @@ public void toApi(cz.cesnet.shongo.controller.api.Tag tagApi) tagApi.setId(ObjectIdentifier.formatId(this)); tagApi.setName(name); tagApi.setType(type); - if (data == null) { - tagApi.setData(""); - } - else { - try { - tagApi.setData(objectMapper.writeValueAsString(data)); - } catch (JsonProcessingException e) { - throw new IllegalArgumentException("Failed to parse data", e); - } - } + tagApi.setData(data); } /** @@ -108,12 +93,6 @@ public void fromApi(cz.cesnet.shongo.controller.api.Tag tagApi) { this.setName(tagApi.getName()); this.setType(tagApi.getType()); - if (tagApi.getData() != null) { - try { - setData(objectMapper.readTree(tagApi.getData())); - } catch (JsonProcessingException e) { - throw new IllegalArgumentException("Data is not a valid JSON", e); - } - } + this.setData(tagApi.getData()); } } diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestModificationTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestModificationTest.java index d766573e9..2a8b912b4 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestModificationTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestModificationTest.java @@ -1,5 +1,7 @@ package cz.cesnet.shongo.controller.booking.request; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import cz.cesnet.shongo.AliasType; import cz.cesnet.shongo.Technology; import cz.cesnet.shongo.api.Alias; @@ -25,8 +27,10 @@ public class ReservationRequestModificationTest extends AbstractControllerTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + @Test - public void testModifyAttributes() { + public void testModifyAttributes() throws JsonProcessingException { Resource resource = new Resource(); resource.setName("resource"); resource.setAllocatable(true); @@ -59,8 +63,8 @@ public void testModifyAttributes() { reservationRequestGet.setSpecification(new AliasSpecification(Technology.ADOBE_CONNECT)); reservationRequestGet.setReusement(ReservationRequestReusement.OWNED); List auxData = List.of( - new AuxiliaryData("tag1", true, "[\"karnis@cenet.cz\", \"filip.karnis@cesnet.cz\"]"), - new AuxiliaryData("tag2", false, "[\"shouldnotbe@used\"]"), + new AuxiliaryData("tag1", true, objectMapper.readTree("[\"karnis@cenet.cz\", \"filip.karnis@cesnet.cz\"]")), + new AuxiliaryData("tag2", false, objectMapper.readTree("[\"shouldnotbe@used\"]")), new AuxiliaryData("tag3", true, null) ); reservationRequestGet.setAuxData(auxData); diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/resource/TagTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/resource/TagTest.java index 55dd073a2..dd26f7ac7 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/resource/TagTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/booking/resource/TagTest.java @@ -1,5 +1,8 @@ package cz.cesnet.shongo.controller.booking.resource; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import cz.cesnet.shongo.api.util.DeviceAddress; import cz.cesnet.shongo.controller.*; import cz.cesnet.shongo.controller.api.*; @@ -23,12 +26,13 @@ public class TagTest extends AbstractControllerTest { @Test - public void testCreateTag() - { + public void testCreateTag() throws JsonProcessingException { + final ObjectMapper objectMapper = new ObjectMapper(); + final String tagName1 = "testTag1"; final String tagName2 = "testTag2"; final TagType tagType2 = TagType.NOTIFY_EMAIL; - final String tagData2 = "[\"karnis@cesnet.cz\",\"filip.karnis@cesnet.cz\"]"; + final JsonNode tagData2 = objectMapper.readTree("[\"karnis@cesnet.cz\",\"filip.karnis@cesnet.cz\"]"); ResourceService resourceService = getResourceService(); @@ -54,7 +58,7 @@ public void testCreateTag() Assert.assertEquals(tagId1, getResult1.getId()); Assert.assertEquals(tagName1, getResult1.getName()); Assert.assertEquals(TagType.DEFAULT, getResult1.getType()); - Assert.assertEquals("", getResult1.getData()); + Assert.assertNull(getResult1.getData()); Assert.assertEquals(tagId2, getResult2.getId()); Assert.assertEquals(tagName2, getResult2.getName()); From 2ab86f121045a8f03834b28f16071b0a0fa8756c Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 8 Nov 2023 22:24:57 +0100 Subject: [PATCH 083/134] [controller] add TagData api --- .../shongo/controller/api/AuxDataFilter.java | 37 +++++++++++++++ .../cesnet/shongo/controller/api/TagData.java | 46 +++++++++++++++++++ .../api/rpc/ReservationService.java | 8 ++++ .../api/rpc/ReservationServiceImpl.java | 29 ++++++++++++ .../request/ReservationRequestManager.java | 18 ++++---- .../request/auxdata/AuxDataFilter.java | 15 ------ .../request/auxdata/tagdata/TagData.java | 19 +++++++- .../notification/ReservationNotification.java | 11 ++--- 8 files changed, 152 insertions(+), 31 deletions(-) create mode 100644 shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AuxDataFilter.java create mode 100644 shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/TagData.java delete mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataFilter.java diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AuxDataFilter.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AuxDataFilter.java new file mode 100644 index 000000000..0315cac05 --- /dev/null +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/AuxDataFilter.java @@ -0,0 +1,37 @@ +package cz.cesnet.shongo.controller.api; + +import cz.cesnet.shongo.api.AbstractComplexType; +import cz.cesnet.shongo.api.DataMap; +import lombok.Data; + +@Data +public class AuxDataFilter extends AbstractComplexType +{ + + private String tagName; + private TagType tagType; + private Boolean enabled; + + private static final String TAG_NAME = "tagName"; + private static final String TAG_TYPE = "tagType"; + private static final String ENABLED = "enabled"; + + @Override + public DataMap toData() + { + DataMap dataMap = super.toData(); + dataMap.set(TAG_NAME, tagName); + dataMap.set(TAG_TYPE, tagType); + dataMap.set(ENABLED, enabled); + return dataMap; + } + + @Override + public void fromData(DataMap dataMap) + { + super.fromData(dataMap); + tagName = dataMap.getString(TAG_NAME); + tagType = dataMap.getEnum(TAG_TYPE, TagType.class); + enabled = dataMap.getBoolean(ENABLED); + } +} diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/TagData.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/TagData.java new file mode 100644 index 000000000..d6b8c84de --- /dev/null +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/TagData.java @@ -0,0 +1,46 @@ +package cz.cesnet.shongo.controller.api; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.JsonNode; +import cz.cesnet.shongo.api.AbstractComplexType; +import cz.cesnet.shongo.api.DataMap; +import lombok.Data; + +@Data +public class TagData extends AbstractComplexType +{ + + private String name; + private TagType type; + private JsonNode data; + + private static final String NAME = "name"; + private static final String TYPE = "type"; + private static final String DATA = "data"; + + @Override + public DataMap toData() + { + DataMap dataMap = super.toData(); + dataMap.set(NAME, name); + dataMap.set(TYPE, type); + dataMap.set(DATA, data); + return dataMap; + } + + @Override + public void fromData(DataMap dataMap) + { + super.fromData(dataMap); + name = dataMap.getString(NAME); + type = dataMap.getEnum(TYPE, TagType.class); + data = dataMap.getJsonNode(DATA); + } + + @Override + @JsonIgnore + public String getClassName() + { + return super.getClassName(); + } +} diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationService.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationService.java index 41fcc6cbe..1eb21e016 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationService.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationService.java @@ -3,6 +3,8 @@ import cz.cesnet.shongo.api.rpc.Service; import cz.cesnet.shongo.controller.api.*; import cz.cesnet.shongo.controller.api.request.*; +import cz.cesnet.shongo.controller.api.AuxDataFilter; +import cz.cesnet.shongo.controller.api.TagData; import java.util.Collection; import java.util.List; @@ -192,4 +194,10 @@ public List getReservationRequestHistory(SecurityToke */ @API public String getCachedResourceReservationsICalendar(ReservationListRequest request); + + @API + /** + * Returns TagData (merged data from tags and aux data) for given reservation request. + */ + public List> getReservationRequestTagData(SecurityToken token, String reservationRequestId, AuxDataFilter filter); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java index 498f03fe3..ab70ae163 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java @@ -10,6 +10,7 @@ import cz.cesnet.shongo.controller.api.Specification; import cz.cesnet.shongo.controller.api.Tag; import cz.cesnet.shongo.controller.api.request.*; +import cz.cesnet.shongo.controller.api.TagData; import cz.cesnet.shongo.controller.authorization.Authorization; import cz.cesnet.shongo.controller.authorization.AuthorizationManager; import cz.cesnet.shongo.controller.booking.Allocation; @@ -19,6 +20,7 @@ import cz.cesnet.shongo.controller.booking.request.*; import cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest; import cz.cesnet.shongo.controller.booking.request.ReservationRequest; +import cz.cesnet.shongo.controller.api.AuxDataFilter; import cz.cesnet.shongo.controller.booking.reservation.*; import cz.cesnet.shongo.controller.booking.resource.*; import cz.cesnet.shongo.controller.booking.resource.Resource; @@ -1829,6 +1831,33 @@ public String getCachedResourceReservationsICalendar (ReservationListRequest req } + @Override + public List> getReservationRequestTagData(SecurityToken securityToken, String reservationRequestId, AuxDataFilter filter) { + authorization.validate(securityToken); + checkNotNull("reservationRequestId", reservationRequestId); + + EntityManager entityManager = entityManagerFactory.createEntityManager(); + ReservationRequestManager reservationRequestManager = new ReservationRequestManager(entityManager); + ObjectIdentifier objectId = ObjectIdentifier.parse(reservationRequestId, ObjectType.RESERVATION_REQUEST); + + try { + cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest reservationRequest = + reservationRequestManager.get(objectId.getPersistenceId()); + + if (!authorization.hasObjectPermission(securityToken, reservationRequest, ObjectPermission.READ)) { + ControllerReportSetHelper.throwSecurityNotAuthorizedFault("read reservation request %s", objectId); + } + + return reservationRequestManager.getTagData(objectId.getPersistenceId(), filter) + .stream() + .map(tagData -> tagData.toApi()) + .collect(Collectors.toList()); + } + finally { + entityManager.close(); + } + } + /** * Check whether resource with given resourceId is cached and it has public calendar. * diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java index 3516db3b0..20a2a8c97 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java @@ -12,7 +12,7 @@ import cz.cesnet.shongo.controller.booking.participant.InvitedPersonParticipant; import cz.cesnet.shongo.controller.booking.participant.AbstractParticipant; import cz.cesnet.shongo.controller.booking.participant.PersonParticipant; -import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataFilter; +import cz.cesnet.shongo.controller.api.AuxDataFilter; import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataMerged; import cz.cesnet.shongo.controller.booking.request.auxdata.tagdata.TagData; import cz.cesnet.shongo.controller.booking.specification.Specification; @@ -647,14 +647,14 @@ public List detachReports(ReservationRequest reservationRequest * Creates {@link TagData} for given {@link AbstractReservationRequest} and its corresponding * {@link cz.cesnet.shongo.controller.booking.resource.Tag}s. * - * @param reservationRequest reservation request for which the {@link TagData} shall be created - * @param filter filter for data desired + * @param reservationRequestId reservation request id for which the {@link TagData} shall be created + * @param filter filter for data desired * @return specific implementation of {@link TagData} based on {@link TagType} * @param TagData implementation for corresponding {@link TagType} */ - public > List getTagData(AbstractReservationRequest reservationRequest, AuxDataFilter filter) + public > List getTagData(Long reservationRequestId, AuxDataFilter filter) { - return getAuxData(reservationRequest, filter) + return getAuxData(reservationRequestId, filter) .stream() .map(TagData::create) .map(data -> (T) data) @@ -665,11 +665,11 @@ public > List getTagData(AbstractReservationRequest rese * Merge {@link cz.cesnet.shongo.controller.booking.request.auxdata.AuxData} from {@link AbstractReservationRequest} * and data from its corresponding {@link cz.cesnet.shongo.controller.booking.resource.Tag}s. * - * @param reservationRequest reservation request for which the data shall be merged - * @param filter filter for data desired + * @param reservationRequestId reservation request id for which the data shall be merged + * @param filter filter for data desired * @return merged data */ - private List getAuxData(AbstractReservationRequest reservationRequest, AuxDataFilter filter) + private List getAuxData(Long reservationRequestId, AuxDataFilter filter) { String queryString = "SELECT arr.tagName, rt.tag.type, arr.enabled, arr.data, rt.tag.data" + " FROM AbstractReservationRequestAuxData arr" + @@ -688,7 +688,7 @@ private List getAuxData(AbstractReservationRequest reservationReq } TypedQuery query = entityManager.createQuery(queryString, Object[].class) - .setParameter("id", reservationRequest.getId()); + .setParameter("id", reservationRequestId); if (filter.getTagName() != null) { query.setParameter("tagName", filter.getTagName()); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataFilter.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataFilter.java deleted file mode 100644 index 9c03fea12..000000000 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/AuxDataFilter.java +++ /dev/null @@ -1,15 +0,0 @@ -package cz.cesnet.shongo.controller.booking.request.auxdata; - -import cz.cesnet.shongo.controller.api.TagType; -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder -public class AuxDataFilter -{ - - private final String tagName; - private final TagType tagType; - private final Boolean enabled; -} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java index 01744c8f0..5e94570e0 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/auxdata/tagdata/TagData.java @@ -1,7 +1,8 @@ package cz.cesnet.shongo.controller.booking.request.auxdata.tagdata; +import com.fasterxml.jackson.databind.ObjectMapper; import cz.cesnet.shongo.TodoImplementException; -import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataFilter; +import cz.cesnet.shongo.controller.api.AuxDataFilter; import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataMerged; import lombok.Getter; import lombok.ToString; @@ -11,6 +12,8 @@ public abstract class TagData { + private static final ObjectMapper objectMapper = new ObjectMapper(); + protected final AuxDataMerged auxData; protected final T data; @@ -55,4 +58,18 @@ public boolean filter(AuxDataFilter filter) } return true; } + + public static cz.cesnet.shongo.controller.api.TagData toApi(TagData tagData) + { + cz.cesnet.shongo.controller.api.TagData tagDataApi = new cz.cesnet.shongo.controller.api.TagData<>(); + tagDataApi.setName(tagData.getAuxData().getTagName()); + tagDataApi.setType(tagData.getAuxData().getType()); + tagDataApi.setData(objectMapper.valueToTree(tagData.getData())); + return tagDataApi; + } + + public cz.cesnet.shongo.controller.api.TagData toApi() + { + return toApi(this); + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/notification/ReservationNotification.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/notification/ReservationNotification.java index 44096943a..5875524e8 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/notification/ReservationNotification.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/notification/ReservationNotification.java @@ -14,7 +14,7 @@ import cz.cesnet.shongo.controller.booking.alias.Alias; import cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest; import cz.cesnet.shongo.controller.booking.request.ReservationRequestManager; -import cz.cesnet.shongo.controller.booking.request.auxdata.AuxDataFilter; +import cz.cesnet.shongo.controller.api.AuxDataFilter; import cz.cesnet.shongo.controller.booking.request.auxdata.tagdata.NotifyEmailAuxData; import cz.cesnet.shongo.controller.booking.reservation.Reservation; import cz.cesnet.shongo.controller.booking.resource.Resource; @@ -81,12 +81,11 @@ private ReservationNotification(Reservation reservation, private void addRecipientsFromNotificationTags(AbstractReservationRequest reservationRequest, ReservationRequestManager reservationRequestManager) { - AuxDataFilter filter = AuxDataFilter.builder() - .tagType(TagType.NOTIFY_EMAIL) - .enabled(true) - .build(); + AuxDataFilter filter = new AuxDataFilter(); + filter.setTagType(TagType.NOTIFY_EMAIL); + filter.setEnabled(true); - List notifyEmailAuxData = reservationRequestManager.getTagData(reservationRequest, filter); + List notifyEmailAuxData = reservationRequestManager.getTagData(reservationRequest.getId(), filter); List tagPersonInformationList = notifyEmailAuxData .stream() From 4f0c4aa96309e460f7de72fc9deafcb8f4639ac2 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Tue, 7 Nov 2023 22:10:42 +0100 Subject: [PATCH 084/134] merge aux data with rest implementation --- .../shongo/controller/api/ResourceSummary.java | 8 ++++---- .../api/rpc/ResourceServiceImpl.java | 11 +++++------ .../ReservationRequestModel.java | 3 +++ .../reservationrequest/SpecificationType.java | 18 ++++++++++-------- .../rest/models/resource/ResourceModel.java | 3 ++- .../src/main/resources/sql/resource_list.sql | 2 +- 6 files changed, 25 insertions(+), 20 deletions(-) diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java index 8452a6431..2ca7e1fa5 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java @@ -37,7 +37,7 @@ public class ResourceSummary extends IdentifiedComplexType /** * Tags of the resource. */ - private Set tags = new HashSet<>(); + private Set tags = new HashSet<>(); /** * Parent resource shongo-id. @@ -157,14 +157,14 @@ public void addTechnology(Technology technology) /** * @return {@link #tags} */ - public Set getTags() { + public Set getTags() { return tags; } /** * @param tag to be added to the {@link #tags} */ - public void addTag(String tag) { + public void addTag(Tag tag) { tags.add(tag); } @@ -327,6 +327,6 @@ public void fromData(DataMap dataMap) confirmByOowner = dataMap.getBool(CONFIRM_BY_OWNER); remoteCalendarName = dataMap.getString(REMOTE_CALENDAR_NAME); type = dataMap.getEnum(TYPE, Type.class); - tags = dataMap.getSet(TAGS, String.class); + tags = dataMap.getSet(TAGS, Tag.class); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ResourceServiceImpl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ResourceServiceImpl.java index 53c9d77eb..c66dd78f2 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ResourceServiceImpl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ResourceServiceImpl.java @@ -452,12 +452,11 @@ else if (capabilityClass.equals(RecordingCapability.class)) { String type = record[11].toString(); resourceSummary.setType(ResourceSummary.Type.valueOf(type.trim())); if (record[12] != null) { - String recordTags = record[12].toString(); - if (!recordTags.isEmpty()) { - for (String tag : recordTags.split(",")) { - resourceSummary.addTag(tag.trim()); - } - } + String resourceTags = (String) record[12]; + Arrays.stream(resourceTags.split("\\|")) + .map(String::trim) + .map(Tag::fromConcat) + .forEach(resourceSummary::addTag); } response.addItem(resourceSummary); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java index cb2dda537..d2e1b60f0 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java @@ -1,6 +1,7 @@ package cz.cesnet.shongo.controller.rest.models.reservationrequest; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.JsonNode; import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.ObjectPermission; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; @@ -43,6 +44,7 @@ public class ReservationRequestModel private RoomCapacityModel roomCapacityData; private String lastReservationId; private Integer futureSlotCount; + private JsonNode auxData; public ReservationRequestModel( ReservationRequestSummary summary, @@ -65,6 +67,7 @@ public ReservationRequestModel( this.roomCapacityData = new RoomCapacityModel(summary); this.lastReservationId = summary.getLastReservationId(); this.futureSlotCount = summary.getFutureSlotCount(); + this.auxData = summary.getAuxData(); Set objectPermissions = permissionsByReservationRequestId.get(id); this.isWritable = objectPermissions.contains(ObjectPermission.WRITE); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java index 77c45d0f7..c6cfdbbb3 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java @@ -4,9 +4,14 @@ import cz.cesnet.shongo.controller.Controller; import cz.cesnet.shongo.controller.ControllerConfiguration; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.api.Tag; import lombok.AllArgsConstructor; import lombok.Getter; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + /** * Type of specification for a reservation request. * @@ -86,16 +91,13 @@ public static SpecificationType fromReservationRequestSummary( if (onlyGeneralType) { return PHYSICAL_RESOURCE; } - String resourceTags = reservationRequestSummary.getResourceTags(); + Set resourceTags = reservationRequestSummary.getResourceTags().stream().map(Tag::getName).collect(Collectors.toSet()); String parkTagName = configuration.getParkingPlaceTagName(); String vehicleTagName = configuration.getVehicleTagName(); - if (resourceTags != null) { - if (parkTagName != null && resourceTags.contains(parkTagName)) { - return PARKING_PLACE; - } - else if (vehicleTagName != null && resourceTags.contains(vehicleTagName)) { - return VEHICLE; - } + if (parkTagName != null && resourceTags.contains(parkTagName)) { + return PARKING_PLACE; + } else if (vehicleTagName != null && resourceTags.contains(vehicleTagName)) { + return VEHICLE; } return MEETING_ROOM; default: diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java index dde0caf89..b080032a8 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java @@ -1,6 +1,7 @@ package cz.cesnet.shongo.controller.rest.models.resource; import cz.cesnet.shongo.controller.api.ResourceSummary; +import cz.cesnet.shongo.controller.api.Tag; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; import lombok.Data; @@ -20,7 +21,7 @@ public class ResourceModel private String name; private String description; private TechnologyModel technology; - private Set tags; + private Set tags; private boolean hasCapacity; public ResourceModel(ResourceSummary summary, boolean hasCapacity) diff --git a/shongo-controller/src/main/resources/sql/resource_list.sql b/shongo-controller/src/main/resources/sql/resource_list.sql index 5d223aec9..ab40aa9f9 100644 --- a/shongo-controller/src/main/resources/sql/resource_list.sql +++ b/shongo-controller/src/main/resources/sql/resource_list.sql @@ -17,7 +17,7 @@ SELECT resource_summary.calendar_uri_key AS calendar_uri_key, resource_summary.confirm_by_owner AS confirm_by_owner, resource_summary.type AS type, - resource_summary.tag_names AS tag_names + resource_summary.tags AS tags FROM resource_summary WHERE ${filter} ORDER BY ${order} \ No newline at end of file From 914c0ac94a603956b4bbb0de6d797bac02c8715c Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Tue, 14 Nov 2023 22:18:13 +0100 Subject: [PATCH 085/134] add tag controller --- .../shongo/controller/rest/RestApiPath.java | 3 ++ .../rest/controllers/TagController.java | 40 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/TagController.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java index d53755f2f..37ef28aa1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java @@ -22,6 +22,9 @@ public class RestApiPath public static final String GROUPS_LIST = "/groups"; public static final String GROUPS_DETAIL = "/groups/{groupId:.+}"; + // Tags + public static final String TAGS = API_PREFIX + "/tags"; + // Resources public static final String RESOURCES = API_PREFIX + "/resources"; public static final String CAPACITY_UTILIZATION = "/capacity_utilization"; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/TagController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/TagController.java new file mode 100644 index 000000000..5f6bfa45d --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/TagController.java @@ -0,0 +1,40 @@ +package cz.cesnet.shongo.controller.rest.controllers; + +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.Tag; +import cz.cesnet.shongo.controller.api.request.TagListRequest; +import cz.cesnet.shongo.controller.api.rpc.ResourceService; +import cz.cesnet.shongo.controller.rest.RestApiPath; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; + +@RestController +@RequestMapping(RestApiPath.TAGS) +@RequiredArgsConstructor +public class TagController { + + private final ResourceService resourceService; + + /** + * Lists {@link Tag}s. + */ + @Operation(summary = "Lists available tags.") + @GetMapping + List listTags( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @RequestAttribute(value = "resourceId", required = false) String resourceId + ) { + TagListRequest request = new TagListRequest(securityToken); + request.setResourceId(resourceId); + + return resourceService.listTags(request); + } +} From 575b77f98a4b18e1a2792f2332e64f3671ce7caf Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 8 Nov 2023 22:24:57 +0100 Subject: [PATCH 086/134] add tagdata endpoint --- .../cesnet/shongo/controller/rest/RestApiPath.java | 1 + .../controllers/ReservationRequestController.java | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java index 37ef28aa1..df180defb 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java @@ -35,6 +35,7 @@ public class RestApiPath public static final String RESERVATION_REQUESTS_ACCEPT = ID_SUFFIX + "/accept"; public static final String RESERVATION_REQUESTS_REJECT = ID_SUFFIX + "/reject"; public static final String RESERVATION_REQUESTS_REVERT = ID_SUFFIX + "/revert"; + public static final String RESERVATION_REQUESTS_AUX_DATA = ID_SUFFIX + "/tag_data"; // Participants public static final String PARTICIPANTS = API_PREFIX + "/reservation_requests/{id:.+}/participants"; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 3b93ae88c..42a926fe0 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -14,9 +14,11 @@ import cz.cesnet.shongo.controller.api.request.ExecutableServiceListRequest; import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.request.ReservationRequestListRequest; +import cz.cesnet.shongo.controller.api.TagData; import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.api.rpc.ReservationService; +import cz.cesnet.shongo.controller.api.AuxDataFilter; import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.RestApiPath; @@ -26,13 +28,13 @@ import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestDetailModel; import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestModel; import cz.cesnet.shongo.controller.rest.models.reservationrequest.SpecificationType; -import cz.cesnet.shongo.controller.rest.models.reservationrequest.VirtualRoomModel; import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; import cz.cesnet.shongo.controller.rest.models.room.RoomAuthorizedData; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.joda.time.DateTime; import org.joda.time.Interval; +import org.springdoc.api.annotations.ParameterObject; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.DeleteMapping; @@ -413,6 +415,15 @@ void revertRequest( reservationService.revertReservationRequest(securityToken, id); } + @GetMapping(RestApiPath.RESERVATION_REQUESTS_AUX_DATA) + List> getTagData( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @PathVariable String id, + @ParameterObject AuxDataFilter filter) + { + return reservationService.getReservationRequestTagData(securityToken, id, filter); + } + public enum ReservationType { VIRTUAL_ROOM, From 870187585932e9ecb9c03764aff690cbe7facb15 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Mon, 27 Nov 2023 22:06:50 +0100 Subject: [PATCH 087/134] add auxdata to reservation request model --- .../rest/controllers/ReservationRequestController.java | 3 +++ .../reservationrequest/ReservationRequestCreateModel.java | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 42a926fe0..7d9bd31e7 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -362,6 +362,9 @@ IdModel modifyRequest( if (request.getParticipantCount() != null) { modifiedRequest.setParticipantCount(request.getParticipantCount()); } + if (request.getAuxData() != null) { + modifiedRequest.setAuxData(request.getAuxData()); + } modifiedRequest.setAllowGuests(request.isAllowGuests()); modifiedRequest.setRecord(request.isRecord()); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java index 174a51437..251f7a811 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java @@ -24,6 +24,7 @@ import cz.cesnet.shongo.controller.api.AbstractRoomExecutable; import cz.cesnet.shongo.controller.api.AclEntry; import cz.cesnet.shongo.controller.api.AliasSpecification; +import cz.cesnet.shongo.controller.api.AuxiliaryData; import cz.cesnet.shongo.controller.api.ExecutableServiceSpecification; import cz.cesnet.shongo.controller.api.ExecutableState; import cz.cesnet.shongo.controller.api.PeriodicDateTimeSlot; @@ -174,6 +175,8 @@ public class ReservationRequestCreateModel @JsonIgnore private CacheProvider cacheProvider; + List auxData = new ArrayList<>(); + /** * Create new {@link ReservationRequestModel} from scratch. */ @@ -619,6 +622,7 @@ public void fromApi(AbstractReservationRequest abstractReservationRequest, Cache type = abstractReservationRequest.getType(); dateTime = abstractReservationRequest.getDateTime(); description = abstractReservationRequest.getDescription(); + auxData = abstractReservationRequest.getAuxData(); // Specification Specification specification = abstractReservationRequest.getSpecification(); @@ -1267,6 +1271,8 @@ else if (specificationType.equals(SpecificationType.ROOM_CAPACITY)) { // Set reservation request to be deleted by scheduler if foreign resource is specified abstractReservationRequest.setIsSchedulerDeleted(!Strings.isNullOrEmpty(getMeetingRoomResourceDomain())); + abstractReservationRequest.setAuxData(auxData); + return abstractReservationRequest; } From 4b1302821def165eda0059dc91800ab707413cc6 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Mon, 27 Nov 2023 22:11:55 +0100 Subject: [PATCH 088/134] add reservation request sorting by slot_start --- .../api/request/ReservationRequestListRequest.java | 3 ++- .../controller/api/rpc/ReservationServiceImpl.java | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/request/ReservationRequestListRequest.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/request/ReservationRequestListRequest.java index ccdd7a858..90fccb57e 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/request/ReservationRequestListRequest.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/request/ReservationRequestListRequest.java @@ -364,7 +364,8 @@ public static enum Sort DATETIME, REUSED_RESERVATION_REQUEST, ROOM_PARTICIPANT_COUNT, - SLOT, + SLOT_START, + SLOT_END, SLOT_NEAREST, STATE, TECHNOLOGY, diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java index 42cee7e85..9d5a0c1b5 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java @@ -1206,7 +1206,10 @@ else if (reusedReservationRequestId.equals(ReservationRequestListRequest.FILTER_ case ROOM_PARTICIPANT_COUNT: queryOrderBy = "specification_summary.room_participant_count"; break; - case SLOT: + case SLOT_START: + queryOrderBy = "reservation_request_summary.slot_start"; + break; + case SLOT_END: queryOrderBy = "reservation_request_summary.slot_end"; break; case SLOT_NEAREST: @@ -1331,7 +1334,10 @@ public ListResponse listOwnedResourcesReservationRequ case ROOM_PARTICIPANT_COUNT: queryOrderBy = "specification_summary.room_participant_count"; break; - case SLOT: + case SLOT_START: + queryOrderBy = "reservation_request_summary.slot_start"; + break; + case SLOT_END: queryOrderBy = "reservation_request_summary.slot_end"; break; case SLOT_NEAREST: From e71a6639173c817d205869c4162070aa2ac4538c Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Mon, 27 Nov 2023 23:51:47 +0100 Subject: [PATCH 089/134] fix cors for unauthenticated endpoints --- .../controller/rest/config/OpenApiConfig.java | 2 -- .../rest/config/security/SecurityConfig.java | 34 +------------------ .../rest/config/security/WebConfig.java | 32 +++++++++++++++++ 3 files changed, 33 insertions(+), 35 deletions(-) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/WebConfig.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java index 2fcfc2db8..4dfb6b6d2 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/OpenApiConfig.java @@ -9,7 +9,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.io.Resource; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.resource.PathResourceResolver; @@ -28,7 +27,6 @@ * @author Filip Karnis */ @Configuration -@EnableWebMvc @OpenAPIDefinition( info = @Info(title = "Shongo API", version = "v1"), security = @SecurityRequirement(name = "bearerAuth") diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java index 2629167f1..155c56276 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java @@ -1,19 +1,11 @@ package cz.cesnet.shongo.controller.rest.config.security; -import cz.cesnet.shongo.controller.ControllerConfiguration; -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; - -import java.util.List; /** * Configures spring security for REST api server. @@ -22,12 +14,9 @@ */ @Configuration @EnableWebSecurity -@RequiredArgsConstructor public class SecurityConfig extends WebSecurityConfigurerAdapter { - private final ControllerConfiguration configuration; - @Override public void configure(WebSecurity web) { @@ -43,27 +32,6 @@ protected void configure(HttpSecurity http) throws Exception { http.cors().and().csrf().disable(); AuthFilter authFilter = new AuthFilter(); - http.addFilterAt(authFilter, BasicAuthenticationFilter.class) - .authorizeRequests() - .antMatchers("/domain/**").permitAll() - .antMatchers("/v3/api-docs").permitAll() - .antMatchers("/swagger-ui/**").permitAll() - .antMatchers("/**/report").permitAll(); - } - - @Bean - CorsConfigurationSource corsConfigurationSource() - { - CorsConfiguration configuration = new CorsConfiguration().applyPermitDefaultValues(); - configuration.setAllowedMethods(List.of(CorsConfiguration.ALL)); - configuration.setAllowedOrigins(allowedOrigins()); - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", configuration); - return source; - } - - private List allowedOrigins() - { - return configuration.getRESTApiAllowedOrigins(); + http.addFilterAt(authFilter, BasicAuthenticationFilter.class); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/WebConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/WebConfig.java new file mode 100644 index 000000000..f89010826 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/WebConfig.java @@ -0,0 +1,32 @@ +package cz.cesnet.shongo.controller.rest.config.security; + +import cz.cesnet.shongo.controller.ControllerConfiguration; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +import java.util.List; + +@Configuration +@EnableWebMvc +@RequiredArgsConstructor +public class WebConfig extends WebMvcConfigurerAdapter { + + private final ControllerConfiguration configuration; + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins(allowedOrigins().toArray(new String[0])) + .allowedMethods("*") + .allowedHeaders("*") + .maxAge(3600); + } + + private List allowedOrigins() + { + return configuration.getRESTApiAllowedOrigins(); + } +} From 7ef87888a4ad0b77d16b0e1087b3ed9f98be5fbf Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sun, 3 Dec 2023 05:12:38 +0100 Subject: [PATCH 090/134] fix getting of merged aux data for rooms and capacities --- .../AbstractReservationRequestAuxData.java | 24 ++++++++++++------- .../request/ReservationRequestManager.java | 7 ++++-- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequestAuxData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequestAuxData.java index 92ebe267b..7150f1a48 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequestAuxData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequestAuxData.java @@ -1,7 +1,6 @@ package cz.cesnet.shongo.controller.booking.request; import com.fasterxml.jackson.databind.JsonNode; -import cz.cesnet.shongo.controller.booking.specification.Specification; import lombok.Getter; import lombok.Setter; import org.hibernate.annotations.Immutable; @@ -14,17 +13,26 @@ @Entity @Immutable @Table(name = "arr_aux_data") -public class AbstractReservationRequestAuxData +public class AbstractReservationRequestAuxData extends AbstractReservationRequest { - @Id - private Long id; - private String tagName; private Boolean enabled; + private JsonNode data; + @Type(type = "jsonb") @Column(columnDefinition = "text") - private JsonNode data; - @ManyToOne(cascade = CascadeType.ALL, optional = false, fetch = FetchType.LAZY) - private Specification specification; + public JsonNode getData() { + return data; + } + + @Override + public AbstractReservationRequest clone(EntityManager entityManager) { + throw new RuntimeException("Not implemented"); + } + + @Override + protected cz.cesnet.shongo.controller.api.AbstractReservationRequest createApi() { + throw new RuntimeException("Not implemented"); + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java index 20a2a8c97..287784c80 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/ReservationRequestManager.java @@ -673,8 +673,11 @@ private List getAuxData(Long reservationRequestId, AuxDataFilter { String queryString = "SELECT arr.tagName, rt.tag.type, arr.enabled, arr.data, rt.tag.data" + " FROM AbstractReservationRequestAuxData arr" + - " JOIN ResourceSpecification res_spec ON res_spec.id = arr.specification.id" + - " JOIN ResourceTag rt ON rt.resource.id = res_spec.resource.id" + + " LEFT OUTER JOIN ResourceSpecification res_spec ON res_spec.id = arr.specification.id" + + // Needed for reused allocation (capacity) + " LEFT OUTER JOIN AbstractReservationRequest reusedRequest ON reusedRequest.id = arr.reusedAllocation.reservationRequest.id" + + " LEFT OUTER JOIN RoomSpecification room_spec ON room_spec.id = reusedRequest.specification.id OR room_spec.id = arr.specification.id" + + " JOIN ResourceTag rt ON rt.resource.id = res_spec.resource.id OR rt.resource.id = room_spec.deviceResource.id" + " WHERE rt.tag.name = arr.tagName" + " AND arr.id = :id"; if (filter.getTagName() != null) { From 454fbd088eb78b196f610ade2fe830842d820c37 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 8 Dec 2023 14:46:54 +0100 Subject: [PATCH 091/134] add type attribute to resource utilization --- .../rest/models/resource/ResourceUtilizationModel.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java index 175c00289..5a693260a 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceUtilizationModel.java @@ -1,5 +1,6 @@ package cz.cesnet.shongo.controller.rest.models.resource; +import cz.cesnet.shongo.controller.api.ReservationSummary; import cz.cesnet.shongo.controller.rest.models.TimeInterval; import lombok.Data; import org.joda.time.Interval; @@ -35,6 +36,7 @@ public static ResourceUtilizationModel fromApi( } utilizationModel.setUsedCapacity((resourceCapacityUtilization != null) ? resourceCapacityUtilization.getPeakBucket().getLicenseCount() : 0); + utilizationModel.setType(resourceCapacity.getReservationType()); resources.add(utilizationModel); }); @@ -52,5 +54,6 @@ public static class UtilizationModel private String name; private int totalCapacity; private int usedCapacity; + private ReservationSummary.Type type; } } From 4ea9d2ae59e6596211b01a6c9154db66c1bf9381 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 8 Dec 2023 14:48:33 +0100 Subject: [PATCH 092/134] add device specification type --- .../shongo/controller/ControllerConfiguration.java | 9 +++++++++ .../ReservationRequestCreateModel.java | 2 ++ .../models/reservationrequest/SpecificationType.java | 12 +++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java index 2d42f1580..4ced27264 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java @@ -119,6 +119,7 @@ public class ControllerConfiguration extends CombinedConfiguration public static final String VEHICLE_TAG = "tags.vehicle"; public static final String PARKING_PLACE_TAG = "tags.parking-place"; public static final String MEETING_ROOM_TAG = "tags.meeting-room"; + public static final String DEVICE_TAG = "tags.device"; /** * Period in which the executor works. @@ -653,4 +654,12 @@ public String getParkingPlaceTagName() { return getString(PARKING_PLACE_TAG); } + + /** + * @return name of tag for devices + */ + public String getDeviceTagName() + { + return getString(DEVICE_TAG); + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java index 251f7a811..74a6b5bc9 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java @@ -821,6 +821,7 @@ public Specification toSpecificationApi() case PHYSICAL_RESOURCE: case VEHICLE: case PARKING_PLACE: + case DEVICE: case MEETING_ROOM: { specification = new ResourceSpecification(resourceId); break; @@ -902,6 +903,7 @@ public void setDuration(Period duration) case PHYSICAL_RESOURCE: case PARKING_PLACE: case VEHICLE: + case DEVICE: case MEETING_ROOM: case ROOM_CAPACITY: int minutes; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java index c6cfdbbb3..db2cb65c2 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/SpecificationType.java @@ -50,7 +50,12 @@ public enum SpecificationType /** * For vehicles. */ - VEHICLE(false, true); + VEHICLE(false, true), + + /** + * For device. + */ + DEVICE(false, true); /** * Specifies whether it is a room. @@ -94,10 +99,13 @@ public static SpecificationType fromReservationRequestSummary( Set resourceTags = reservationRequestSummary.getResourceTags().stream().map(Tag::getName).collect(Collectors.toSet()); String parkTagName = configuration.getParkingPlaceTagName(); String vehicleTagName = configuration.getVehicleTagName(); + String deviceTagName = configuration.getDeviceTagName(); if (parkTagName != null && resourceTags.contains(parkTagName)) { return PARKING_PLACE; } else if (vehicleTagName != null && resourceTags.contains(vehicleTagName)) { return VEHICLE; + } else if (deviceTagName != null && resourceTags.contains(deviceTagName)) { + return DEVICE; } return MEETING_ROOM; default: @@ -124,6 +132,8 @@ else if (string.equals(configuration.getVehicleTagName())) { } else if (string.equals(configuration.getParkingPlaceTagName())) { return PARKING_PLACE; + } else if (string.equals(configuration.getDeviceTagName())) { + return DEVICE; } throw new TodoImplementException("SpecificationType.fromString for " + string); From 8535ef037332c8e7caa4d72388fc4a1a336a9c3b Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sun, 10 Dec 2023 22:05:23 +0100 Subject: [PATCH 093/134] fix ontology builder issue with JsonNode --- .../java/jade/content/onto/CustomBeanOntologyBuilder.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shongo-common-api/src/main/java/jade/content/onto/CustomBeanOntologyBuilder.java b/shongo-common-api/src/main/java/jade/content/onto/CustomBeanOntologyBuilder.java index ea898afba..25edd133a 100644 --- a/shongo-common-api/src/main/java/jade/content/onto/CustomBeanOntologyBuilder.java +++ b/shongo-common-api/src/main/java/jade/content/onto/CustomBeanOntologyBuilder.java @@ -70,6 +70,10 @@ private static boolean isGetter(Method method) c = methodName.charAt(2); } else { + if (methodName.length() < 4) { + // it is too short + return false; + } c = methodName.charAt(3); } if (!Character.isUpperCase(c) && '_' != c) { @@ -450,10 +454,6 @@ private ObjectSchema doAddSchema(Class clazz, boolean buildHierarchy) throws return doAddFlatSchema(clazz); } } - catch (StringIndexOutOfBoundsException e) { - logger.info("skip class " + clazz + " because of " + e); - return null; - } catch (BeanOntologyException boe) { throw boe; From a21ba8b1719a86b9a0a7d8e62c98c08bcdbe8768 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Sun, 10 Dec 2023 22:51:31 +0100 Subject: [PATCH 094/134] add rest api configuration to admin guide --- shongo-doc/admin-guide/configuration.xml | 58 ++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/shongo-doc/admin-guide/configuration.xml b/shongo-doc/admin-guide/configuration.xml index f2d8de6c5..906fe8ef8 100644 --- a/shongo-doc/admin-guide/configuration.xml +++ b/shongo-doc/admin-guide/configuration.xml @@ -496,6 +496,57 @@ service postgresql restart + +REST API settings + + + OPTIONAL rest-api/host + + Specifies on which host should the REST API listen. + If not set, the default host localhost will be used. + + + + OPTIONAL rest-api/port + + Specifies port on which the REST API will listen. + If not set, the default port 9999 will be used. + + + + OPTIONAL rest-api/origin + + Specifies one allowed HTTP request origins. + Used for Cross-Origin Resource Sharing (CORS). + + + + OPTIONAL rest-api/ssl-key-store + + Specifies filename of Java KeyStore (JKS) with SSL certificate to use for secured REST API (HTTP) communication. + If not set the SSL is not used for REST API communication. + + + + OPTIONAL rest-api/ssl-key-store-password + + Specifies password for JKS. + + + + + Example: + + 127.0.0.1 + 8080 + https://meetings.cesnet.cz + shongo-dev.cesnet.cz:4200 + +]]> + + + SSL settings SSL settings can be used to turn off or alter hostname verification for specified hostnames. @@ -572,6 +623,13 @@ service postgresql restart (password) + + meetings.cesnet.cz + 8080 + https://meetings.cesnet.cz + shongo-dev.cesnet.cz:4200 + + no-reply@meetings.cesnet.cz rs.cesnet.cz From dc93a2c0b7267c48c04ac1f2778ddd354f28e00d Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 15 Dec 2023 16:35:04 +0100 Subject: [PATCH 095/134] revert AbsractReservationRequestAuxData extending AbsractReservationRequest --- .../AbstractReservationRequestAuxData.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequestAuxData.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequestAuxData.java index 7150f1a48..3a45dc49f 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequestAuxData.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/booking/request/AbstractReservationRequestAuxData.java @@ -1,6 +1,8 @@ package cz.cesnet.shongo.controller.booking.request; import com.fasterxml.jackson.databind.JsonNode; +import cz.cesnet.shongo.controller.booking.Allocation; +import cz.cesnet.shongo.controller.booking.specification.Specification; import lombok.Getter; import lombok.Setter; import org.hibernate.annotations.Immutable; @@ -13,26 +15,21 @@ @Entity @Immutable @Table(name = "arr_aux_data") -public class AbstractReservationRequestAuxData extends AbstractReservationRequest +public class AbstractReservationRequestAuxData { + @Id + private Long id; + private String tagName; private Boolean enabled; - private JsonNode data; - @Type(type = "jsonb") @Column(columnDefinition = "text") - public JsonNode getData() { - return data; - } - - @Override - public AbstractReservationRequest clone(EntityManager entityManager) { - throw new RuntimeException("Not implemented"); - } - - @Override - protected cz.cesnet.shongo.controller.api.AbstractReservationRequest createApi() { - throw new RuntimeException("Not implemented"); - } + private JsonNode data; + @ManyToOne(cascade = CascadeType.ALL, optional = false, fetch = FetchType.LAZY) + private Specification specification; + @ManyToOne + @JoinColumn(name = "reused_allocation_id") + @Access(AccessType.FIELD) + private Allocation reusedAllocation; } From 6ae8d3cd8a731d4f048f84c20d6d7fe4932862e8 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 15 Dec 2023 16:35:48 +0100 Subject: [PATCH 096/134] add default rest config --- .../src/main/resources/controller-default.cfg.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/shongo-controller/src/main/resources/controller-default.cfg.xml b/shongo-controller/src/main/resources/controller-default.cfg.xml index 3128939ea..a97db9787 100644 --- a/shongo-controller/src/main/resources/controller-default.cfg.xml +++ b/shongo-controller/src/main/resources/controller-default.cfg.xml @@ -62,6 +62,15 @@ PT33S + + 127.0.0.1 + 8001 + http://localhost + https://localhost + + + + From 1188990c476be3ff40f500286b4e63d638603c2d Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 12 Jan 2024 14:55:16 +0100 Subject: [PATCH 097/134] forbid report by an unauthorized user --- .../shongo/controller/rest/config/security/SecurityConfig.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java index 155c56276..834ba6b0f 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/SecurityConfig.java @@ -23,8 +23,7 @@ public void configure(WebSecurity web) web.ignoring() .antMatchers("/domain/**") .antMatchers("/v3/api-docs") - .antMatchers("/swagger-ui/**") - .antMatchers("/**/report"); + .antMatchers("/swagger-ui/**"); } @Override From 4235a5624a10e75d155e8705f48b40c2962dd056 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 17 Jan 2024 23:11:22 +0100 Subject: [PATCH 098/134] clean resource utilization --- .../rest/controllers/ResourceController.java | 28 ++-- .../models/resource/ResourceCapacity.java | 137 ------------------ 2 files changed, 10 insertions(+), 155 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java index 9eef3c1f6..17af90b40 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java @@ -35,7 +35,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; @@ -107,14 +106,14 @@ ListResponse listResourcesUtilization( @RequestParam(required = false) int count, @RequestParam(required = false) boolean refresh) { + Interval interval = new Interval(intervalFrom, intervalTo); Period period = unit.getPeriod(); ResourcesUtilization resourcesUtilization = cache.getResourcesUtilization(securityToken, refresh); - Map> utilization = - resourcesUtilization.getUtilization(new Interval(intervalFrom, intervalTo), period); + var utilization = resourcesUtilization.getUtilization(interval, period); List items = new ArrayList<>(); - utilization.forEach((interval, resourceCapacityUtilization) -> { - items.add(ResourceUtilizationModel.fromApi(interval, resourceCapacityUtilization)); - }); + utilization.forEach((utilizationInterval, resourceCapacityUtilization) -> + items.add(ResourceUtilizationModel.fromApi(utilizationInterval, resourceCapacityUtilization)) + ); return ListResponse.fromRequest(start, count, items); } @@ -128,26 +127,19 @@ ResourceUtilizationDetailModel getResourceUtilization( @PathVariable("id") String resourceId, @RequestParam(value = "interval_from") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) DateTime intervalFrom, @RequestParam(value = "interval_to") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) DateTime intervalTo) - throws ClassNotFoundException { - @SuppressWarnings("unchecked") - Class resourceCapacityClass = (Class) - Class.forName(ResourceCapacity.class.getCanonicalName() + "$" + "Room"); - ResourcesUtilization resourcesUtilization = - cache.getResourcesUtilization(securityToken, false); - ResourceCapacity resourceCapacity = - resourcesUtilization.getResourceCapacity(resourceId, resourceCapacityClass); + Class resourceCapacityClass = ResourceCapacity.Room.class; + ResourcesUtilization resourcesUtilization = cache.getResourcesUtilization(securityToken, false); + ResourceCapacity resourceCapacity = resourcesUtilization.getResourceCapacity(resourceId, resourceCapacityClass); ResourceCapacityUtilization resourceCapacityUtilization = resourcesUtilization.getUtilization(resourceCapacity, new Interval(intervalFrom, intervalTo)); ResourceCapacity.Room roomCapacity = (ResourceCapacity.Room) resourceCapacity; List reservations = (resourceCapacityUtilization != null) - ? resourceCapacityUtilization.getReservations() - .stream().map(res -> + ? resourceCapacityUtilization.getReservations().stream().map(res -> ReservationModel.fromApi(res, cache.getUserInformation(securityToken, res.getUserId())) ).collect(Collectors.toList()) : Collections.emptyList(); - return ResourceUtilizationDetailModel.fromApi( - resourceCapacityUtilization, roomCapacity, reservations); + return ResourceUtilizationDetailModel.fromApi(resourceCapacityUtilization, roomCapacity, reservations); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacity.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacity.java index 5f1ddc32a..28329ed08 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacity.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceCapacity.java @@ -1,15 +1,9 @@ package cz.cesnet.shongo.controller.rest.models.resource; -import cz.cesnet.shongo.TodoImplementException; import cz.cesnet.shongo.controller.api.RecordingCapability; import cz.cesnet.shongo.controller.api.ReservationSummary; import cz.cesnet.shongo.controller.api.ResourceSummary; import cz.cesnet.shongo.controller.api.RoomProviderCapability; -import org.apache.commons.lang.StringUtils; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; /** * Represents a type of capacity which can be utilized in a resource. @@ -36,14 +30,6 @@ public ResourceCapacity(ResourceSummary resource) this.resource = resource; } - /** - * @return class name of this type of capacity - */ - public String getClassName() - { - return getClass().getSimpleName(); - } - /** * @return {@link ResourceSummary#getId()} for {@link #resource} */ @@ -66,52 +52,6 @@ public String getResourceName() */ public abstract ReservationSummary.Type getReservationType(); - /** - * @param utilization - * @return css classes which shall be rendered for given {@code utilization} - */ - public abstract String getCssClass(ResourceCapacityUtilization utilization); - - /** - * @param utilization - * @param t {@link FormatType} which should be used - * @param s {@link FormatStyle} which should be used - * @return html content which shall be rendered for given {@code utilization} - */ - public abstract String formatUtilization(ResourceCapacityUtilization utilization, FormatType t, FormatStyle s); - - /** - * Specifies what utilization should be formatted in {@link #formatUtilization}. - */ - public enum FormatType - { - /** - * Maximum utilization of resource capacity (e.g., maximum number of utilized license count). - */ - MAXIMUM, - - /** - * Average utilization of resource capacity (e.g., average of all utilizations). - */ - AVERAGE - } - - /** - * Specifies how the utilization value should be formatted in {@link #formatUtilization}. - */ - public enum FormatStyle - { - /** - * Absolute utilization (e.g., number of licenses). - */ - ABSOLUTE, - - /** - * Percentage utilization (e.g., percentage of utilization). - */ - RELATIVE - } - /** * Abstract {@link ResourceCapacity} for types with license count (e.g., recording capacity or room capacity). */ @@ -142,83 +82,6 @@ public Integer getLicenseCount() { return licenseCount; } - - @Override - public String getCssClass(ResourceCapacityUtilization utilization) - { - Set cssClasses = new HashSet(); - int utilizedLicenseCount = getUtilizedLicenseCount(utilization, FormatType.MAXIMUM).intValue(); - int relativeUtilizedLicenseCount = (utilizedLicenseCount * 100) / licenseCount; - if (utilizedLicenseCount > 0) { - cssClasses.add("utilized"); - cssClasses.add("utilized" + ((relativeUtilizedLicenseCount / 10) * 10)); - } - return StringUtils.join(cssClasses, " "); - } - - @Override - public String formatUtilization(ResourceCapacityUtilization utilization, FormatType type, FormatStyle style) - { - Number utilizedLicenseCount = getUtilizedLicenseCount(utilization, type); - StringBuilder output = new StringBuilder(); - switch (style) { - case ABSOLUTE: - String value = String.format("%1$.1f", utilizedLicenseCount.doubleValue()); - if (value.endsWith(".0")) { - value = String.valueOf(utilizedLicenseCount.intValue()); - } - output.append(value); - break; - case RELATIVE: - output.append((int) ((utilizedLicenseCount.doubleValue() * 100.0) / (double) licenseCount)); - output.append("%"); - break; - default: - throw new TodoImplementException(style); - } - return output.toString(); - } - - /** - * @param utilization - * @param type - * @return utilized license count for given {@code type} - */ - private Number getUtilizedLicenseCount(ResourceCapacityUtilization utilization, FormatType type) - { - if (utilization != null) { - switch (type) { - case MAXIMUM: - ResourceCapacityBucket peakBucket = utilization.getPeakBucket(); - if (peakBucket != null) { - return peakBucket.getLicenseCount(); - } - else { - return 0; - } - case AVERAGE: - List buckets = utilization.getBuckets(); - double totalLicenseCount = 0; - int bucketCount = 0; - for (ResourceCapacityBucket bucket : buckets) { - if (bucket.isEmpty()) { - continue; - } - totalLicenseCount += bucket.getLicenseCount(); - bucketCount++; - } - if (bucketCount == 0) { - return 0; - } - return totalLicenseCount / (double) bucketCount; - default: - throw new TodoImplementException(type); - } - } - else { - return 0; - } - } } /** From d8446338b10d4a0fcdcdc3c9f899ea6bed1f1bd0 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 18 Jan 2024 01:20:53 +0100 Subject: [PATCH 099/134] move ResourceModel.hasCapacity retrieving to sql --- .../shongo/controller/api/ResourceSummary.java | 18 ++++++++++++++++++ .../api/rpc/ResourceServiceImpl.java | 1 + .../rest/controllers/ResourceController.java | 6 +----- .../rest/models/resource/ResourceModel.java | 4 ++-- .../src/main/resources/sql/hsqldb/init.sql | 4 +++- .../src/main/resources/sql/postgresql/init.sql | 4 +++- .../src/main/resources/sql/resource_list.sql | 3 ++- 7 files changed, 30 insertions(+), 10 deletions(-) diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java index 2ca7e1fa5..25ffaec2c 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ResourceSummary.java @@ -81,6 +81,11 @@ public class ResourceSummary extends IdentifiedComplexType */ private boolean confirmByOowner; + /** + * Specifies whether resource has capacity. + */ + private boolean hasCapacity; + /** * @return {@link #userId} */ @@ -252,6 +257,16 @@ public void setConfirmByOowner(boolean confirmByOowner) this.confirmByOowner = confirmByOowner; } + public boolean hasCapacity() + { + return hasCapacity; + } + + public void setHasCapacity(boolean hasCapacity) + { + this.hasCapacity = hasCapacity; + } + public String getRemoteCalendarName() { return remoteCalendarName; } @@ -285,6 +300,7 @@ public enum Type { private static final String CALENDAR_URI_KEY = "calendarUriKey"; private static final String DOMAIN_NAME = "domainName"; private static final String CONFIRM_BY_OWNER = "confirmByOwner"; + private static final String HAS_CAPACITY = "hasCapacity"; public static final String REMOTE_CALENDAR_NAME = "remoteCalendarName"; public static final String TYPE = "type"; public static final String TAGS = "tags"; @@ -304,6 +320,7 @@ public DataMap toData() dataMap.set(CALENDAR_URI_KEY, calendarUriKey); dataMap.set(DOMAIN_NAME, domainName); dataMap.set(CONFIRM_BY_OWNER, confirmByOowner); + dataMap.set(HAS_CAPACITY, hasCapacity); dataMap.set(REMOTE_CALENDAR_NAME, remoteCalendarName); dataMap.set(TYPE, type); dataMap.set(TAGS, tags); @@ -325,6 +342,7 @@ public void fromData(DataMap dataMap) calendarUriKey = dataMap.getString(CALENDAR_URI_KEY); domainName = dataMap.getString(DOMAIN_NAME); confirmByOowner = dataMap.getBool(CONFIRM_BY_OWNER); + hasCapacity = dataMap.getBool(HAS_CAPACITY); remoteCalendarName = dataMap.getString(REMOTE_CALENDAR_NAME); type = dataMap.getEnum(TYPE, Type.class); tags = dataMap.getSet(TAGS, Tag.class); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ResourceServiceImpl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ResourceServiceImpl.java index c66dd78f2..a1f744a9d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ResourceServiceImpl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ResourceServiceImpl.java @@ -458,6 +458,7 @@ else if (capabilityClass.equals(RecordingCapability.class)) { .map(Tag::fromConcat) .forEach(resourceSummary::addTag); } + resourceSummary.setHasCapacity((Boolean) record[13]); response.addItem(resourceSummary); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java index 17af90b40..ab262d61f 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java @@ -80,11 +80,7 @@ List listResources( return accessibleResources.getItems() .stream() - .map(resourceSummary -> { - // Find out whether the resource has capacity - Resource resource = resourceService.getResource(securityToken, resourceSummary.getId()); - return new ResourceModel(resourceSummary, !resource.getCapabilities().isEmpty()); - }) + .map(ResourceModel::new) // Filter only resources with either technology or tag .filter(resource -> !(resource.getTechnology() == null && resource.getTags().isEmpty())) .collect(Collectors.toList()); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java index b080032a8..d0d4354c9 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourceModel.java @@ -24,7 +24,7 @@ public class ResourceModel private Set tags; private boolean hasCapacity; - public ResourceModel(ResourceSummary summary, boolean hasCapacity) + public ResourceModel(ResourceSummary summary) { this.id = summary.getId(); this.type = summary.getType(); @@ -32,6 +32,6 @@ public ResourceModel(ResourceSummary summary, boolean hasCapacity) this.description = summary.getDescription(); this.technology = TechnologyModel.find(summary.getTechnologies()); this.tags = summary.getTags(); - this.hasCapacity = hasCapacity; + this.hasCapacity = summary.hasCapacity(); } } diff --git a/shongo-controller/src/main/resources/sql/hsqldb/init.sql b/shongo-controller/src/main/resources/sql/hsqldb/init.sql index a0b50e5cf..644ed9010 100644 --- a/shongo-controller/src/main/resources/sql/hsqldb/init.sql +++ b/shongo-controller/src/main/resources/sql/hsqldb/init.sql @@ -30,12 +30,14 @@ SELECT WHEN (SELECT resource_id FROM capability INNER JOIN recording_capability on recording_capability.id = capability.id WHERE resource_id = resource.id) IS NOT NULL THEN 'RECORDING_SERVICE' ELSE 'RESOURCE' END AS type, - GROUP_CONCAT(CONCAT(tag.id, ',', tag.name, ',', tag.type, ',', tag.data) SEPARATOR '|') AS tags + GROUP_CONCAT(CONCAT(tag.id, ',', tag.name, ',', tag.type, ',', tag.data) SEPARATOR '|') AS tags, + MAX(CASE WHEN capability.resource_id IS NOT NULL THEN TRUE ELSE FALSE END) AS has_capacity FROM resource LEFT JOIN device_resource ON device_resource.id = resource.id LEFT JOIN device_resource_technologies ON device_resource_technologies.device_resource_id = device_resource.id LEFT JOIN resource_tag ON resource.id = resource_tag.resource_id LEFT JOIN tag ON resource_tag.tag_id = tag.id +LEFT JOIN capability ON capability.resource_id = resource.id GROUP BY resource.id; /** diff --git a/shongo-controller/src/main/resources/sql/postgresql/init.sql b/shongo-controller/src/main/resources/sql/postgresql/init.sql index 4b120d52c..d1a5f9589 100644 --- a/shongo-controller/src/main/resources/sql/postgresql/init.sql +++ b/shongo-controller/src/main/resources/sql/postgresql/init.sql @@ -113,12 +113,14 @@ SELECT WHEN resource.id IN (SELECT resource_id FROM capability INNER JOIN recording_capability on recording_capability.id = capability.id) THEN 'RECORDING_SERVICE' ELSE 'RESOURCE' END AS type, - string_agg(tag.id || ',' || tag.name || ',' || tag.type || ',' || COALESCE(tag.data #>> '{}', ''), '|') AS tags + string_agg(tag.id || ',' || tag.name || ',' || tag.type || ',' || COALESCE(tag.data #>> '{}', ''), '|') AS tags, + bool_or(capability.resource_id IS NOT NULL) AS has_capacity FROM resource LEFT JOIN device_resource ON device_resource.id = resource.id LEFT JOIN device_resource_technologies ON device_resource_technologies.device_resource_id = device_resource.id LEFT JOIN resource_tag ON resource.id = resource_tag.resource_id LEFT JOIN tag ON resource_tag.tag_id = tag.id +LEFT JOIN capability ON capability.resource_id = resource.id GROUP BY resource.id; /** diff --git a/shongo-controller/src/main/resources/sql/resource_list.sql b/shongo-controller/src/main/resources/sql/resource_list.sql index ab40aa9f9..08677e3f3 100644 --- a/shongo-controller/src/main/resources/sql/resource_list.sql +++ b/shongo-controller/src/main/resources/sql/resource_list.sql @@ -17,7 +17,8 @@ SELECT resource_summary.calendar_uri_key AS calendar_uri_key, resource_summary.confirm_by_owner AS confirm_by_owner, resource_summary.type AS type, - resource_summary.tags AS tags + resource_summary.tags AS tags, + resource_summary.has_capacity AS has_capacity FROM resource_summary WHERE ${filter} ORDER BY ${order} \ No newline at end of file From a4fa3a59b15c825929cedc10b27559a07a232bc4 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 18 Jan 2024 01:37:19 +0100 Subject: [PATCH 100/134] add resource caching --- .../cesnet/shongo/controller/rest/Cache.java | 30 +++++++++++++++---- .../models/resource/ResourcesUtilization.java | 12 ++++++-- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java index 1a0ebda80..2f15cd760 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java @@ -13,6 +13,7 @@ import cz.cesnet.shongo.controller.api.ObjectPermissionSet; import cz.cesnet.shongo.controller.api.Reservation; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.api.Resource; import cz.cesnet.shongo.controller.api.ResourceSummary; import cz.cesnet.shongo.controller.api.SecurityToken; import cz.cesnet.shongo.controller.api.request.GroupListRequest; @@ -100,7 +101,12 @@ public class Cache /** * {@link ResourceSummary} by identifier. */ - private final ExpirationMap resourceById = + private final ExpirationMap resourceSummaryById = + new ExpirationMap<>(Duration.standardHours(1)); + /** + * {@link Resource} by identifier. + */ + private final ExpirationMap resourceById = new ExpirationMap<>(Duration.standardHours(1)); /** * {@link ReservationRequestSummary} by identifier. @@ -153,6 +159,7 @@ public synchronized void clearExpired() for (UserState userState : userStateByToken) { userState.objectPermissionsByObject.clearExpired(dateTimeNow); } + resourceSummaryById.clearExpired(dateTimeNow); resourceById.clearExpired(dateTimeNow); reservationRequestById.clearExpired(dateTimeNow); reservationById.clearExpired(dateTimeNow); @@ -387,6 +394,15 @@ public synchronized Map> fetchObjectPermissions( return result; } + public synchronized Resource getResource(SecurityToken securityToken, String resourceId) { + Resource resource = resourceById.get(resourceId); + if (resource == null) { + resource = resourceService.getResource(securityToken, resourceId); + resourceById.put(resourceId, resource); + } + return resource; + } + /** * @param securityToken to be used for fetching the {@link ResourceSummary}s * @param resourceIds resource-ids to be fetched @@ -395,7 +411,7 @@ public synchronized void fetchResourceSummaries(SecurityToken securityToken, Col { Set missingResourceIds = null; for (String resourceId : resourceIds) { - if (!resourceById.contains(resourceId)) { + if (!resourceSummaryById.contains(resourceId)) { if (missingResourceIds == null) { missingResourceIds = new HashSet(); } @@ -411,7 +427,7 @@ public synchronized void fetchResourceSummaries(SecurityToken securityToken, Col ListResponse response = resourceService.listResources(request); for (ResourceSummary resource : response.getItems()) { String resourceId = resource.getId(); - resourceById.put(resourceId, resource); + resourceSummaryById.put(resourceId, resource); missingResourceIds.remove(resourceId); } if (missingResourceIds.size() > 0) { @@ -428,7 +444,7 @@ public synchronized void fetchResourceSummaries(SecurityToken securityToken, Col */ public ResourceSummary getResourceSummary(SecurityToken securityToken, String resourceId) { - ResourceSummary resourceSummary = resourceById.get(resourceId); + ResourceSummary resourceSummary = resourceSummaryById.get(resourceId); if (resourceSummary == null) { ResourceListRequest request = new ResourceListRequest(); request.setSecurityToken(securityToken); @@ -436,7 +452,7 @@ public ResourceSummary getResourceSummary(SecurityToken securityToken, String re ListResponse response = resourceService.listResources(request); if (response.getItemCount() == 1) { resourceSummary = response.getItem(0); - resourceById.put(resourceSummary.getId(), resourceSummary); + resourceSummaryById.put(resourceSummary.getId(), resourceSummary); } } if (resourceSummary == null) { @@ -641,7 +657,9 @@ public ResourcesUtilization getResourcesUtilization(SecurityToken securityToken, synchronized (resourcesUtilizationByToken) { ResourcesUtilization resourcesUtilization = resourcesUtilizationByToken.get(securityToken); if (resourcesUtilization == null || forceRefresh) { - resourcesUtilization = new ResourcesUtilization(securityToken, resourceService, reservationService); + resourcesUtilization = new ResourcesUtilization( + securityToken, reservationService, resourceService, this + ); resourcesUtilizationByToken.put(securityToken, resourcesUtilization); } return resourcesUtilization; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java index 9a3fe237e..5bebb4ea3 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java @@ -12,6 +12,7 @@ import cz.cesnet.shongo.controller.api.request.ResourceListRequest; import cz.cesnet.shongo.controller.api.rpc.ReservationService; import cz.cesnet.shongo.controller.api.rpc.ResourceService; +import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.util.RangeSet; import lombok.extern.slf4j.Slf4j; import org.joda.time.DateTime; @@ -77,10 +78,15 @@ public class ResourcesUtilization * Constructor. * * @param securityToken sets the {@link #securityToken} - * @param resources to be used for fetching {@link #resourceCapacities} * @param reservations sets the {@link #reservationService} + * @param resources to be used for fetching resources + * @param cache to be used for fetching {@link #resourceCapacities} */ - public ResourcesUtilization(SecurityToken securityToken, ResourceService resources, ReservationService reservations) + public ResourcesUtilization( + SecurityToken securityToken, + ReservationService reservations, + ResourceService resources, + Cache cache) { this.securityToken = securityToken; this.reservationService = reservations; @@ -92,7 +98,7 @@ public ResourcesUtilization(SecurityToken securityToken, ResourceService resourc resourceListRequest.addCapabilityClass(RecordingCapability.class); for (ResourceSummary resourceSummary : resources.listResources(resourceListRequest)) { String resourceId = resourceSummary.getId(); - Resource resource = resources.getResource(securityToken, resourceId); + Resource resource = cache.getResource(securityToken, resourceId); for (Capability capability : resource.getCapabilities()) { if (capability instanceof RoomProviderCapability) { RoomProviderCapability roomProviderCapability = (RoomProviderCapability) capability; From a7411959e57163118d9e4bca631603d8173c59e6 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 24 Jan 2024 22:59:55 +0100 Subject: [PATCH 101/134] get room name from alias --- .../src/main/resources/sql/reservation_request_list.sql | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/shongo-controller/src/main/resources/sql/reservation_request_list.sql b/shongo-controller/src/main/resources/sql/reservation_request_list.sql index 60ecd03c9..44d8c0219 100644 --- a/shongo-controller/src/main/resources/sql/reservation_request_list.sql +++ b/shongo-controller/src/main/resources/sql/reservation_request_list.sql @@ -26,10 +26,7 @@ SELECT specification_summary.room_participant_count AS room_participant_count, executable_summary.room_has_recording_service AS room_has_recording_service, executable_summary.room_has_recordings AS room_has_recordings, - CASE - WHEN specification_summary.alias_room_name IS NOT NULL THEN specification_summary.alias_room_name - ELSE reused_specification_summary.alias_room_name - END AS alias_room_name, + executable_summary.room_name AS alias_room_name, specification_summary.resource_id resource_id, reservation_request_summary.usage_executable_state AS usage_executable_state, reservation_request_summary.future_child_count, From 6dcb89085db14cf89baf0450b3f2faea13bcabb8 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Mon, 20 Nov 2023 02:36:32 +0100 Subject: [PATCH 102/134] fix test execution from IDE InterDomainTest was failing in custom test execution, because it was using relative paths to resource files. Fixed by getting absolute path of resource. --- .../shongo/controller/domains/InterDomainTest.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/domains/InterDomainTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/domains/InterDomainTest.java index 7ee2b8164..07fcd410f 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/domains/InterDomainTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/domains/InterDomainTest.java @@ -27,7 +27,8 @@ public class InterDomainTest extends AbstractControllerTest private static final Integer INTERDOMAIN_LOCAL_PORT = 8443; private static final String INTERDOMAIN_LOCAL_PASSWORD = "shongo_test"; private static final String INTERDOMAIN_LOCAL_PASSWORD_HASH = SSLCommunication.hashPassword(INTERDOMAIN_LOCAL_PASSWORD.getBytes()); - private static final String TEST_CERT_PATH = "./shongo-controller/src/test/resources/keystore/server.crt"; + private static final String TEST_KEY_STORE_PATH = "keystore/server.p12"; + private static final String TEST_CERT_PATH = "keystore/server.crt"; private Domain loopbackDomain; private Long loopbackDomainId; @@ -37,7 +38,7 @@ public void before() throws Exception { System.setProperty(ControllerConfiguration.REST_API_HOST, INTERDOMAIN_LOCAL_HOST); System.setProperty(ControllerConfiguration.REST_API_PORT, INTERDOMAIN_LOCAL_PORT.toString()); - System.setProperty(ControllerConfiguration.REST_API_SSL_KEY_STORE, "./shongo-controller/src/test/resources/keystore/server.p12"); + System.setProperty(ControllerConfiguration.REST_API_SSL_KEY_STORE, getProjectResourcePath(TEST_KEY_STORE_PATH)); System.setProperty(ControllerConfiguration.REST_API_SSL_KEY_STORE_PASSWORD, "shongo"); System.setProperty(ControllerConfiguration.REST_API_SSL_KEY_STORE_TYPE, "PKCS12"); System.setProperty(ControllerConfiguration.INTERDOMAIN_PKI_CLIENT_AUTH, "false"); @@ -51,7 +52,7 @@ public void before() throws Exception loopbackDomain.setName(LocalDomain.getLocalDomainName()); loopbackDomain.setOrganization("CESNET z.s.p.o."); loopbackDomain.setAllocatable(true); - loopbackDomain.setCertificatePath(TEST_CERT_PATH); + loopbackDomain.setCertificatePath(getProjectResourcePath(TEST_CERT_PATH)); DeviceAddress deviceAddress = new DeviceAddress(INTERDOMAIN_LOCAL_HOST, INTERDOMAIN_LOCAL_PORT); loopbackDomain.setDomainAddress(deviceAddress); loopbackDomain.setPasswordHash(INTERDOMAIN_LOCAL_PASSWORD_HASH); @@ -449,4 +450,9 @@ protected DomainService getDomainService() { return InterDomainAgent.getInstance().getDomainService(); } + + private String getProjectResourcePath(String relativePath) + { + return Objects.requireNonNull(getClass().getClassLoader().getResource(relativePath)).getPath(); + } } From 73cb0a6e7cf872f940822cbd3e04eeac572abcd1 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 9 Feb 2024 13:48:58 +0100 Subject: [PATCH 103/134] extend reservation request cache expiration --- .../main/java/cz/cesnet/shongo/controller/rest/Cache.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java index 2f15cd760..57fa7f794 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java @@ -112,7 +112,7 @@ public class Cache * {@link ReservationRequestSummary} by identifier. */ private final ExpirationMap reservationRequestById = - new ExpirationMap<>(Duration.standardSeconds(15)); + new ExpirationMap<>(Duration.standardMinutes(5)); /** * {@link Reservation} by identifier. @@ -541,7 +541,11 @@ public synchronized ReservationRequestSummary getReservationRequestSummaryNotCac ListResponse response = reservationService.listReservationRequests(request); if (response.getItemCount() > 0) { ReservationRequestSummary reservationRequest = response.getItem(0); - if (reservationRequest.isAllowCache()) { + DateTime start = reservationRequest.getEarliestSlot().getStart(); + DateTime end = reservationRequest.getEarliestSlot().getEnd(); + final boolean startsSoon = start.isAfterNow() && start.isBefore(DateTime.now().plusMinutes(5)); + final boolean happensNow = start.isBeforeNow() && end.isAfterNow(); + if (reservationRequest.isAllowCache() && !(startsSoon || happensNow)) { reservationRequestById.put(reservationRequest.getId(), reservationRequest); } return reservationRequest; From c51b36d81a4882aab90e276a5c0b9d3fcf7a1c8a Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Mon, 11 Mar 2024 17:57:33 +0100 Subject: [PATCH 104/134] fix missing technology for listing room capacity request --- .../rest/controllers/ReservationRequestController.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 7d9bd31e7..6fb21e516 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -118,7 +118,14 @@ ListResponse listRequests( .stream() .map(room -> listRequests(securityToken, start, count, sort, sortDescending, allocationState, room.getId(), technology, finalIntervalFrom, finalIntervalTo, userId, participantUserId, - search, capacityReservationTypes, null).getItems()) + search, capacityReservationTypes, null) + .getItems() + .stream() + .peek(capacities -> capacities.getVirtualRoomData() + .setTechnology(room.getVirtualRoomData().getTechnology()) + ) + .collect(Collectors.toList()) + ) .flatMap(List::stream) .collect(Collectors.toList()); From ce095fdfca2c09dbaf531f1316b1e1f5df0d567b Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Fri, 9 Feb 2024 13:58:16 +0100 Subject: [PATCH 105/134] rename Cache to RestCache Renamed cz.cesnet.shongo.controller.rest.Cache to cz.cesnet.shongo.controller.rest.RestCache to prevent confusion with cz.cesnet.shongo.controller.cache.Cache Both for people and future DI using Spring. --- .../cz/cesnet/shongo/controller/rest/CacheProvider.java | 8 ++++---- .../shongo/controller/rest/{Cache.java => RestCache.java} | 8 ++++---- .../java/cz/cesnet/shongo/controller/rest/RoomCache.java | 2 +- .../rest/controllers/ParticipantController.java | 4 ++-- .../controller/rest/controllers/RecordingController.java | 4 ++-- .../rest/controllers/ReservationRequestController.java | 4 ++-- .../controller/rest/controllers/ResourceController.java | 4 ++-- .../controller/rest/controllers/RoomController.java | 4 ++-- .../controller/rest/controllers/RuntimeController.java | 4 ++-- .../controller/rest/controllers/UserController.java | 4 ++-- .../controller/rest/controllers/UserRoleController.java | 4 ++-- .../reservationrequest/ReservationRequestCreateModel.java | 4 ++-- .../rest/models/resource/ResourcesUtilization.java | 4 ++-- 13 files changed, 29 insertions(+), 29 deletions(-) rename shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/{Cache.java => RestCache.java} (99%) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/CacheProvider.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/CacheProvider.java index 06d4d8080..6408a5df1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/CacheProvider.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/CacheProvider.java @@ -9,16 +9,16 @@ import cz.cesnet.shongo.controller.api.SecurityToken; /** - * {@link Cache} provided for specified {@link #securityToken}. + * {@link RestCache} provided for specified {@link #securityToken}. * * @author Martin Srom */ public class CacheProvider { /** - * {@link Cache} to be used for retrieving {@link UserInformation}. + * {@link RestCache} to be used for retrieving {@link UserInformation}. */ - private final Cache cache; + private final RestCache cache; /** * {@link SecurityToken} to be used for retrieving {@link UserInformation} by the {@link #cache}. @@ -31,7 +31,7 @@ public class CacheProvider * @param cache sets the {@link #cache} * @param securityToken sets the {@link #securityToken} */ - public CacheProvider(Cache cache, SecurityToken securityToken) + public CacheProvider(RestCache cache, SecurityToken securityToken) { this.cache = cache; this.securityToken = securityToken; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestCache.java similarity index 99% rename from shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java rename to shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestCache.java index 57fa7f794..d285f5b20 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/Cache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestCache.java @@ -53,7 +53,7 @@ @Slf4j @Service @RequiredArgsConstructor -public class Cache +public class RestCache { /** @@ -462,7 +462,7 @@ public ResourceSummary getResourceSummary(SecurityToken securityToken, String re } /** - * Load {@link ReservationRequestSummary}s for given {@code reservationRequestIds} to the {@link Cache}. + * Load {@link ReservationRequestSummary}s for given {@code reservationRequestIds} to the {@link RestCache}. * * @param securityToken * @param reservationRequestIds @@ -492,8 +492,8 @@ public synchronized void fetchReservationRequests(SecurityToken securityToken, S } /** - * Retrieve {@link ReservationRequestSummary} from {@link Cache} or from {@link #reservationService} - * if it doesn't exist in the {@link Cache}. + * Retrieve {@link ReservationRequestSummary} from {@link RestCache} or from {@link #reservationService} + * if it doesn't exist in the {@link RestCache}. * * @param securityToken * @param reservationRequestId diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java index cc27040df..27fc9f34f 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RoomCache.java @@ -32,7 +32,7 @@ public class RoomCache private final ResourceControlService resourceControlService; - private final Cache cache; + private final RestCache cache; /** * {@link RoomExecutable} by roomExecutableId. diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java index 4ddfde6ba..71a8599df 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ParticipantController.java @@ -11,9 +11,9 @@ import cz.cesnet.shongo.controller.api.UsedRoomExecutable; import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.rpc.ExecutableService; -import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.RestApiPath; +import cz.cesnet.shongo.controller.rest.RestCache; import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import cz.cesnet.shongo.controller.rest.models.participant.ParticipantConfigurationModel; import cz.cesnet.shongo.controller.rest.models.participant.ParticipantModel; @@ -50,7 +50,7 @@ public class ParticipantController { - private final Cache cache; + private final RestCache cache; private final ExecutableService executableService; @Operation(summary = "Lists reservation request participants.") diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java index f487de73f..9a880415c 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RecordingController.java @@ -8,8 +8,8 @@ import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.api.rpc.ResourceControlService; -import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.RestApiPath; +import cz.cesnet.shongo.controller.rest.RestCache; import cz.cesnet.shongo.controller.rest.models.recording.RecordingModel; import cz.cesnet.shongo.controller.scheduler.SchedulerReportSet; import io.swagger.v3.oas.annotations.Operation; @@ -38,7 +38,7 @@ public class RecordingController { - private final Cache cache; + private final RestCache cache; private final ExecutableService executableService; private final ResourceControlService resourceControlService; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 6fb21e516..5e97d6baf 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -19,9 +19,9 @@ import cz.cesnet.shongo.controller.api.rpc.ExecutableService; import cz.cesnet.shongo.controller.api.rpc.ReservationService; import cz.cesnet.shongo.controller.api.AuxDataFilter; -import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.RestApiPath; +import cz.cesnet.shongo.controller.rest.RestCache; import cz.cesnet.shongo.controller.rest.models.IdModel; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; import cz.cesnet.shongo.controller.rest.models.reservationrequest.ReservationRequestCreateModel; @@ -69,7 +69,7 @@ public class ReservationRequestController { - private final Cache cache; + private final RestCache cache; private final ReservationService reservationService; private final AuthorizationService authorizationService; private final ExecutableService executableService; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java index ab262d61f..f5d5bcd13 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ResourceController.java @@ -7,8 +7,8 @@ import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.request.ResourceListRequest; import cz.cesnet.shongo.controller.api.rpc.ResourceService; -import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.RestApiPath; +import cz.cesnet.shongo.controller.rest.RestCache; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; import cz.cesnet.shongo.controller.rest.models.resource.ReservationModel; import cz.cesnet.shongo.controller.rest.models.resource.ResourceCapacity; @@ -51,7 +51,7 @@ public class ResourceController { - private final Cache cache; + private final RestCache cache; private final ResourceService resourceService; /** diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java index c9841bd4e..a3c0e7886 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RoomController.java @@ -6,8 +6,8 @@ import cz.cesnet.shongo.controller.api.request.ExecutableListRequest; import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.rpc.ExecutableService; -import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.RestApiPath; +import cz.cesnet.shongo.controller.rest.RestCache; import cz.cesnet.shongo.controller.rest.models.room.RoomAuthorizedData; import cz.cesnet.shongo.controller.rest.models.room.RoomModel; import io.swagger.v3.oas.annotations.Operation; @@ -34,7 +34,7 @@ public class RoomController { - private final Cache cache; + private final RestCache cache; private final ExecutableService executableService; @Operation(summary = "Lists rooms (executables).") diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java index 18b734d74..e6c1f54cc 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/RuntimeController.java @@ -8,9 +8,9 @@ import cz.cesnet.shongo.controller.api.request.ExecutableServiceListRequest; import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.rpc.ExecutableService; -import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.RestApiPath; +import cz.cesnet.shongo.controller.rest.RestCache; import cz.cesnet.shongo.controller.rest.RoomCache; import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import cz.cesnet.shongo.controller.rest.models.runtimemanagement.RuntimeParticipantModel; @@ -49,7 +49,7 @@ public class RuntimeController { - private final Cache cache; + private final RestCache cache; private final RoomCache roomCache; private final ExecutableService executableService; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java index c7684a5f1..1613fef44 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserController.java @@ -9,8 +9,8 @@ import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.request.UserListRequest; import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; -import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.RestApiPath; +import cz.cesnet.shongo.controller.rest.RestCache; import cz.cesnet.shongo.controller.rest.models.users.SettingsModel; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; @@ -39,7 +39,7 @@ public class UserController { private final AuthorizationService authorizationService; - private final Cache cache; + private final RestCache cache; /** * Handle request for list of {@link UserInformation}s which contains given {@code filter} text in any field. diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java index 82a45f7de..7a8b6578c 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/UserRoleController.java @@ -6,9 +6,9 @@ import cz.cesnet.shongo.controller.api.request.AclEntryListRequest; import cz.cesnet.shongo.controller.api.request.ListResponse; import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; -import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; import cz.cesnet.shongo.controller.rest.RestApiPath; +import cz.cesnet.shongo.controller.rest.RestCache; import cz.cesnet.shongo.controller.rest.error.LastOwnerRoleNotDeletableException; import cz.cesnet.shongo.controller.rest.models.roles.UserRoleModel; import io.swagger.v3.oas.annotations.Operation; @@ -42,7 +42,7 @@ public class UserRoleController { private final AuthorizationService authorizationService; - private final Cache cache; + private final RestCache cache; @Operation(summary = "Lists reservation request roles.") @GetMapping diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java index 74a6b5bc9..307d7df20 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestCreateModel.java @@ -47,8 +47,8 @@ import cz.cesnet.shongo.controller.api.request.ReservationRequestListRequest; import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; import cz.cesnet.shongo.controller.api.rpc.ReservationService; -import cz.cesnet.shongo.controller.rest.Cache; import cz.cesnet.shongo.controller.rest.CacheProvider; +import cz.cesnet.shongo.controller.rest.RestCache; import cz.cesnet.shongo.controller.rest.error.UnsupportedApiException; import cz.cesnet.shongo.controller.rest.models.TechnologyModel; import cz.cesnet.shongo.controller.rest.models.TimeInterval; @@ -223,7 +223,7 @@ public ReservationRequestCreateModel(AbstractReservationRequest reservationReque public static List getPermanentRooms( ReservationService reservationService, SecurityToken securityToken, - Cache cache) + RestCache cache) { ReservationRequestListRequest request = new ReservationRequestListRequest(); request.setSecurityToken(securityToken); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java index 5bebb4ea3..78fe8c1f4 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/resource/ResourcesUtilization.java @@ -12,7 +12,7 @@ import cz.cesnet.shongo.controller.api.request.ResourceListRequest; import cz.cesnet.shongo.controller.api.rpc.ReservationService; import cz.cesnet.shongo.controller.api.rpc.ResourceService; -import cz.cesnet.shongo.controller.rest.Cache; +import cz.cesnet.shongo.controller.rest.RestCache; import cz.cesnet.shongo.util.RangeSet; import lombok.extern.slf4j.Slf4j; import org.joda.time.DateTime; @@ -86,7 +86,7 @@ public ResourcesUtilization( SecurityToken securityToken, ReservationService reservations, ResourceService resources, - Cache cache) + RestCache cache) { this.securityToken = securityToken; this.reservationService = reservations; From 40e6b04bb8ed580c2338bb341fdf1bafdaf1d9d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Wed, 20 Mar 2024 23:07:27 +0100 Subject: [PATCH 106/134] add reservation devices configuration --- .../controller/ControllerConfiguration.java | 23 +++++++++++++++ .../ReservationDeviceConfig.java | 28 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java index 4ced27264..a37710e80 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java @@ -2,11 +2,13 @@ import com.google.common.base.Strings; import cz.cesnet.shongo.PersonInformation; +import cz.cesnet.shongo.controller.authorization.ReservationDeviceConfig; import cz.cesnet.shongo.controller.booking.executable.Executable; import cz.cesnet.shongo.controller.settings.UserSessionSettings; import cz.cesnet.shongo.ssl.SSLCommunication; import cz.cesnet.shongo.util.PatternParser; import org.apache.commons.configuration.CombinedConfiguration; +import org.apache.commons.configuration.HierarchicalConfiguration; import org.apache.commons.configuration.tree.NodeCombiner; import org.apache.commons.configuration.tree.UnionCombiner; import org.joda.time.Duration; @@ -223,6 +225,11 @@ public class ControllerConfiguration extends CombinedConfiguration */ public static final String SECURITY_AUTHORIZATION_RESERVATION = "security.authorization.reservation"; + /** + * Configures devices which get access to the system and reservation privilege for a particular resource. + */ + public static final String SECURITY_AUTHORIZATION_RESERVATION_DEVICE = "security.authorization.reservation-devices.device"; + /** * Url where user can change his settings. */ @@ -662,4 +669,20 @@ public String getDeviceTagName() { return getString(DEVICE_TAG); } + + /** + * @return list of reservation devices. + */ + public List getReservationDevices() { + List deviceConfigs = new ArrayList<>(); + + for (HierarchicalConfiguration conf : configurationsAt(SECURITY_AUTHORIZATION_RESERVATION_DEVICE)) { + String accessToken = conf.getString("access-token"); + String resourceId = conf.getString("resource-id"); + ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig(accessToken, resourceId); + deviceConfigs.add(deviceConfig); + } + + return deviceConfigs; + } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java new file mode 100644 index 000000000..56a68a25f --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java @@ -0,0 +1,28 @@ +package cz.cesnet.shongo.controller.authorization; + +import lombok.Getter; +import javax.validation.constraints.NotNull; + +/** + * Configuration for a physical device which can make reservations for a particular resource. + * + * @author Michal Drobňák + */ +@Getter +public final class ReservationDeviceConfig { + private final String accessToken; + private final String resourceId; + + public ReservationDeviceConfig(@NotNull String accessToken, @NotNull String resourceId) { + this.accessToken = accessToken; + this.resourceId = resourceId; + } + + @Override + public String toString() { + return "ReservationDeviceConfig{" + + "accessToken='" + accessToken + '\'' + + ", resourceId='" + resourceId + '\'' + + '}'; + } +} From a5cae2588083615cbfcc4b0cdf035bcea275a966 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 21 Mar 2024 19:58:16 +0100 Subject: [PATCH 107/134] fix listing of periodical physical resource reservations --- .../rest/controllers/ReservationRequestController.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 5e97d6baf..317f50dea 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -154,8 +154,12 @@ ListResponse listRequests( request.setSpecificationResourceIds(resourceId); if (permanentRoomId != null) { - request.setReusedReservationRequestId(permanentRoomId); - reservationTypes.add(ReservationType.ROOM_CAPACITY); + if (reservationTypes.contains(ReservationType.PHYSICAL_RESOURCE)) { + request.setParentReservationRequestId(permanentRoomId); + } else { + request.setReusedReservationRequestId(permanentRoomId); + reservationTypes.add(ReservationType.ROOM_CAPACITY); + } } if (reservationTypes.contains(ReservationType.VIRTUAL_ROOM)) { From e4a03ea8f68bfe8cb5e1968865bdacea77612d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Thu, 21 Mar 2024 22:15:36 +0100 Subject: [PATCH 108/134] set-up user data for reservation devices --- .../authorization/Authorization.java | 52 ++++++++++++++++--- .../ReservationDeviceConfig.java | 18 +++++++ 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java index f5b1d92c2..485e20122 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java @@ -108,6 +108,11 @@ public abstract class Authorization */ private AuthorizationExpression reservationExpression; + /** + * List of devices authorized to make reservations on a particular resource. + */ + private List reservationDevices; + /** * Constructor. * @@ -155,6 +160,8 @@ protected Long getObjectId(PersistentObject object) configuration.getString(ControllerConfiguration.SECURITY_AUTHORIZATION_OPERATOR), this); this.reservationExpression = new AuthorizationExpression( configuration.getString(ControllerConfiguration.SECURITY_AUTHORIZATION_RESERVATION), this); + + this.reservationDevices = configuration.getReservationDevices(); } /** @@ -335,6 +342,16 @@ public final UserData getUserData(String userId) if (userId.equals(ROOT_USER_ID)) { return ROOT_USER_DATA; } + + // Reservation device user + for (ReservationDeviceConfig reservationDevice : reservationDevices) { + UserData userData = reservationDevice.getUserData(); + + if (userId.equals(userData.getUserId())) { + return userData; + } + } + UserData userData; if (cache.hasUserDataByUserId(userId)) { userData = cache.getUserDataByUserId(userId); @@ -402,19 +419,23 @@ public final Collection listUserInformation(Set filterU { logger.debug("Retrieving list of user information..."); List userInformationList = new LinkedList(); - // Remove root id from request, which is static if contains. - if (filterUserIds != null && filterUserIds.contains(ROOT_USER_ID)) { - filterUserIds = new HashSet<>(filterUserIds); - filterUserIds.remove(ROOT_USER_ID); - // Add root user information to result - userInformationList.add(ROOT_USER_DATA.getUserInformation()); + + // Remove root ID from request and add user data if filter contains root ID. + checkForStaticUser(filterUserIds, userInformationList, ROOT_USER_DATA); + + // Remove reservation device ids from request and add their user data if filter contains their ID. + for (ReservationDeviceConfig reservationDevice : reservationDevices) { + UserData deviceUser = reservationDevice.getUserData(); + checkForStaticUser(filterUserIds, userInformationList, deviceUser); } + for (UserData userData : onListUserData(filterUserIds, search)) { userInformationList.add(userData.getUserInformation()); } return userInformationList; } + /** * @param userId * @return true if the user is Shongo admin (should have all permissions), @@ -1086,6 +1107,25 @@ private AclObjectState fetchAclObjectState(AclObjectIdentity aclObjectIdentity) return aclObjectState; } + /** + * Checks if filter contains user ID and if yes then adds the user data to the list and removes the ID from the filter. + * + * @param filterUserIds User ID filter. + * @param userInformationList List of user information. + * @param userData User data to add if filter contains their ID. + */ + private void checkForStaticUser(Set filterUserIds, List userInformationList, UserData userData) { + String userId = userData.getUserId(); + + if (filterUserIds != null && filterUserIds.contains(userId)) { + filterUserIds = new HashSet<>(filterUserIds); + filterUserIds.remove(userId); + + // Add user information to result + userInformationList.add(userData.getUserInformation()); + } + } + /** * Add given {@code aclEntry} to the {@link AuthorizationCache}. * diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java index 56a68a25f..7e2fc1efe 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java @@ -1,5 +1,6 @@ package cz.cesnet.shongo.controller.authorization; +import cz.cesnet.shongo.api.UserInformation; import lombok.Getter; import javax.validation.constraints.NotNull; @@ -12,10 +13,12 @@ public final class ReservationDeviceConfig { private final String accessToken; private final String resourceId; + private final UserData userData; public ReservationDeviceConfig(@NotNull String accessToken, @NotNull String resourceId) { this.accessToken = accessToken; this.resourceId = resourceId; + this.userData = createUserData(); } @Override @@ -25,4 +28,19 @@ public String toString() { ", resourceId='" + resourceId + '\'' + '}'; } + + private UserData createUserData() { + UserData userData = new UserData(); + UserInformation userInformation = userData.getUserInformation(); + UserAuthorizationData userAuthData = new UserAuthorizationData(0); + + String userId = "reservation-device-" + resourceId; + String name = "Reservation Device For " + resourceId; + + userInformation.setUserId(userId); + userInformation.setFullName(name); + userData.setUserAuthorizationData(userAuthData); + + return userData; + } } From d86b58990cb4494600c3dd981f460db882827344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Thu, 21 Mar 2024 22:32:00 +0100 Subject: [PATCH 109/134] validate reservation device token --- .../shongo/controller/authorization/Authorization.java | 10 +++++----- .../controller/authorization/ServerAuthorization.java | 6 ++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java index 485e20122..ad7529b25 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java @@ -72,6 +72,11 @@ public abstract class Authorization */ protected EntityManagerFactory entityManagerFactory; + /** + * List of devices authorized to make reservations on a particular resource. + */ + protected List reservationDevices; + /** * @see cz.cesnet.shongo.controller.acl.AclProvider */ @@ -108,11 +113,6 @@ public abstract class Authorization */ private AuthorizationExpression reservationExpression; - /** - * List of devices authorized to make reservations on a particular resource. - */ - private List reservationDevices; - /** * Constructor. * diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java index dc06aae7b..78fb7e6a2 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java @@ -207,6 +207,12 @@ protected UserData onGetUserDataByAccessToken(String accessToken) return ROOT_USER_DATA; } + for (ReservationDeviceConfig reservationDevice : reservationDevices) { + if (accessToken.equals(reservationDevice.getAccessToken())) { + return reservationDevice.getUserData(); + } + } + Exception errorException = null; String errorReason = null; try { From b95c951b3c2ff577d0a4217e3bd4922db5ab88dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Fri, 22 Mar 2024 22:33:49 +0100 Subject: [PATCH 110/134] modify device id --- .../controller/authorization/ReservationDeviceConfig.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java index 7e2fc1efe..9dfc4ce64 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java @@ -11,6 +11,8 @@ */ @Getter public final class ReservationDeviceConfig { + private static int deviceCount = 1; + private final String accessToken; private final String resourceId; private final UserData userData; @@ -19,6 +21,7 @@ public ReservationDeviceConfig(@NotNull String accessToken, @NotNull String reso this.accessToken = accessToken; this.resourceId = resourceId; this.userData = createUserData(); + deviceCount++; } @Override @@ -34,7 +37,7 @@ private UserData createUserData() { UserInformation userInformation = userData.getUserInformation(); UserAuthorizationData userAuthData = new UserAuthorizationData(0); - String userId = "reservation-device-" + resourceId; + String userId = "shongo:reservation:device:" + deviceCount; String name = "Reservation Device For " + resourceId; userInformation.setUserId(userId); From b1caf97ed2c4e836d5a182c3f867c7235b922c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Sat, 23 Mar 2024 00:39:42 +0100 Subject: [PATCH 111/134] fix reservation request user finding --- .../controller/authorization/Authorization.java | 16 ++++++++++++++-- .../authorization/ReservationDeviceConfig.java | 6 ++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java index ad7529b25..cc8b1e8b8 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java @@ -22,6 +22,8 @@ import javax.persistence.Query; import java.util.*; +import static cz.cesnet.shongo.controller.authorization.ReservationDeviceConfig.DEVICE_ID_PREFIX; + /** * Provides methods for performing authentication, authorization and fetching user data from web service. * @@ -290,6 +292,14 @@ public final UserInformation getUserInformation(SecurityToken securityToken) public final UserInformation getUserInformation(String userId) throws ControllerReportSet.UserNotExistsException { + for (ReservationDeviceConfig reservationDevice : reservationDevices) { + UserData deviceUser = reservationDevice.getUserData(); + + if (userId.equals(deviceUser.getUserId())) { + return deviceUser.getUserInformation(); + } + } + if (UserInformation.isLocal(userId)) { UserData userData = getUserData(userId); return userData.getUserInformation(); @@ -1066,8 +1076,10 @@ private AclUserState fetchAclUserState(String userId) if (userId != null) { aclIdentities.add(aclProvider.getIdentity(AclIdentityType.USER, userId)); } - for (String groupId : listUserGroupIds(userId)) { - aclIdentities.add(aclProvider.getIdentity(AclIdentityType.GROUP, groupId)); + if (!userId.startsWith(DEVICE_ID_PREFIX)) { + for (String groupId : listUserGroupIds(userId)) { + aclIdentities.add(aclProvider.getIdentity(AclIdentityType.GROUP, groupId)); + } } aclIdentities.add(aclProvider.getIdentity(AclIdentityType.GROUP, EVERYONE_GROUP_ID)); EntityManager entityManager = entityManagerFactory.createEntityManager(); diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java index 9dfc4ce64..63a666bcb 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java @@ -11,6 +11,8 @@ */ @Getter public final class ReservationDeviceConfig { + public static final String DEVICE_ID_PREFIX = "shongo:reservation:device:"; + private static int deviceCount = 1; private final String accessToken; @@ -35,9 +37,9 @@ public String toString() { private UserData createUserData() { UserData userData = new UserData(); UserInformation userInformation = userData.getUserInformation(); - UserAuthorizationData userAuthData = new UserAuthorizationData(0); + UserAuthorizationData userAuthData = new UserAuthorizationData(UserAuthorizationData.LOA_NONE); - String userId = "shongo:reservation:device:" + deviceCount; + String userId = DEVICE_ID_PREFIX + deviceCount; String name = "Reservation Device For " + resourceId; userInformation.setUserId(userId); From 0f50cb77731a27dcce51e22de866001d3f9750ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Mon, 25 Mar 2024 15:15:50 +0100 Subject: [PATCH 112/134] allow reading reservation requests of managed resource --- .../controller/ControllerConfiguration.java | 5 +++-- .../api/rpc/ReservationServiceImpl.java | 20 +++++++++++++++---- .../authorization/Authorization.java | 9 +++++---- .../ReservationDeviceConfig.java | 2 +- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java index a37710e80..181fe958a 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java @@ -1,6 +1,7 @@ package cz.cesnet.shongo.controller; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; import cz.cesnet.shongo.PersonInformation; import cz.cesnet.shongo.controller.authorization.ReservationDeviceConfig; import cz.cesnet.shongo.controller.booking.executable.Executable; @@ -673,7 +674,7 @@ public String getDeviceTagName() /** * @return list of reservation devices. */ - public List getReservationDevices() { + public ImmutableList getReservationDevices() { List deviceConfigs = new ArrayList<>(); for (HierarchicalConfiguration conf : configurationsAt(SECURITY_AUTHORIZATION_RESERVATION_DEVICE)) { @@ -683,6 +684,6 @@ public List getReservationDevices() { deviceConfigs.add(deviceConfig); } - return deviceConfigs; + return ImmutableList.copyOf(deviceConfigs); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java index 9d5a0c1b5..d55c02f1e 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java @@ -13,6 +13,7 @@ import cz.cesnet.shongo.controller.api.TagData; import cz.cesnet.shongo.controller.authorization.Authorization; import cz.cesnet.shongo.controller.authorization.AuthorizationManager; +import cz.cesnet.shongo.controller.authorization.ReservationDeviceConfig; import cz.cesnet.shongo.controller.booking.Allocation; import cz.cesnet.shongo.controller.booking.ObjectIdentifier; import cz.cesnet.shongo.controller.booking.datetime.PeriodicDateTime; @@ -1034,10 +1035,21 @@ public ListResponse listReservationRequests(Reservati EntityManager entityManager = entityManagerFactory.createEntityManager(); try { QueryFilter queryFilter = new QueryFilter("reservation_request_summary", true); - - // List only reservation requests which is current user permitted to read - queryFilter.addFilterId("allocation_id", authorization, securityToken, - Allocation.class, ObjectPermission.READ); + + Optional deviceUser = authorization.reservationDevices.stream() + .filter(d -> d.getAccessToken().equals(securityToken.getAccessToken())).findFirst(); + + if (deviceUser.isEmpty()) { + // List only reservation requests which is current user permitted to read + queryFilter.addFilterId("allocation_id", authorization, securityToken, + Allocation.class, ObjectPermission.READ); + } else { + // List only reservation requests of resource which reservation device has access to + String resourceId = deviceUser.get().getResourceId(); + int resourceIdNum = Integer.parseInt(resourceId.substring(resourceId.lastIndexOf(":") + 1)); + queryFilter.addFilter("specification_summary.resource_id = :deviceResourceId"); + queryFilter.addFilterParameter("deviceResourceId", resourceIdNum); + } // List only reservation requests which are requested (but latest versions of them) if (request.getReservationRequestIds().size() > 0) { diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java index cc8b1e8b8..62670beb3 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java @@ -1,5 +1,6 @@ package cz.cesnet.shongo.controller.authorization; +import com.google.common.collect.ImmutableList; import cz.cesnet.shongo.PersistentObject; import cz.cesnet.shongo.TodoImplementException; import cz.cesnet.shongo.api.UserInformation; @@ -70,14 +71,14 @@ public abstract class Authorization } /** - * @see EntityManagerFactory + * List of devices authorized to make reservations on a particular resource. */ - protected EntityManagerFactory entityManagerFactory; + public ImmutableList reservationDevices; /** - * List of devices authorized to make reservations on a particular resource. + * @see EntityManagerFactory */ - protected List reservationDevices; + protected EntityManagerFactory entityManagerFactory; /** * @see cz.cesnet.shongo.controller.acl.AclProvider diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java index 63a666bcb..da9520c60 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java @@ -37,7 +37,7 @@ public String toString() { private UserData createUserData() { UserData userData = new UserData(); UserInformation userInformation = userData.getUserInformation(); - UserAuthorizationData userAuthData = new UserAuthorizationData(UserAuthorizationData.LOA_NONE); + UserAuthorizationData userAuthData = new UserAuthorizationData(UserAuthorizationData.LOA_EXTENDED); String userId = DEVICE_ID_PREFIX + deviceCount; String name = "Reservation Device For " + resourceId; From c464247a825d9f282b76504a374cf1528a3c6a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Mon, 25 Mar 2024 18:00:46 +0100 Subject: [PATCH 113/134] create ACL entries on start-up --- .../authorization/Authorization.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java index 62670beb3..4b22e044e 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java @@ -9,6 +9,7 @@ import cz.cesnet.shongo.controller.acl.AclEntry; import cz.cesnet.shongo.controller.api.*; import cz.cesnet.shongo.controller.booking.Allocation; +import cz.cesnet.shongo.controller.booking.ObjectIdentifier; import cz.cesnet.shongo.controller.booking.ObjectTypeResolver; import cz.cesnet.shongo.controller.booking.person.UserPerson; import cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest; @@ -178,6 +179,7 @@ protected void initialize() this.administratorExpression.evaluate(rootUserInformation, rootUserAuthorizationData); this.operatorExpression.evaluate(rootUserInformation, rootUserAuthorizationData); this.reservationExpression.evaluate(rootUserInformation, rootUserAuthorizationData); + this.createReservationDeviceAclEntries(reservationDevices); } /** @@ -1264,4 +1266,36 @@ public static Authorization getInstance() throws IllegalStateException } return authorization; } + + private void createReservationDeviceAclEntries(List reservationDevices) { + EntityManager entityManager = entityManagerFactory.createEntityManager(); + AuthorizationManager authManager = new AuthorizationManager(entityManager, this); + + reservationDevices.forEach(device -> { + authManager.beginTransaction(); + entityManager.getTransaction().begin(); + + try { + String userId = device.getUserData().getUserId(); + String resourceId = device.getResourceId(); + ObjectIdentifier objectIdentifier = ObjectIdentifier.parse(resourceId); + logger.info("Creating ACL entry for reservation device {} for resource {}", device.getUserData().getUserId(), objectIdentifier); + + SecurityToken securityToken = new SecurityToken(device.getAccessToken()); + securityToken.setUserInformation(device.getUserData().getUserInformation()); + + PersistentObject object = entityManager.find(objectIdentifier.getObjectClass(), + objectIdentifier.getPersistenceId()); + authManager.createAclEntry(AclIdentityType.USER, userId, object, ObjectRole.RESERVATION); + + entityManager.getTransaction().commit(); + authManager.commitTransaction(securityToken); + } catch (Error err) { + entityManager.getTransaction().rollback(); + authManager.rollbackTransaction(); + } + }); + + entityManager.close(); + } } From 3cdaf23133c0d7f22047a25f590de626894d2ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Mon, 25 Mar 2024 18:30:38 +0100 Subject: [PATCH 114/134] configure device ID statically --- .../shongo/controller/ControllerConfiguration.java | 3 ++- .../authorization/ReservationDeviceConfig.java | 12 ++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java index 181fe958a..efae2f697 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java @@ -680,7 +680,8 @@ public ImmutableList getReservationDevices() { for (HierarchicalConfiguration conf : configurationsAt(SECURITY_AUTHORIZATION_RESERVATION_DEVICE)) { String accessToken = conf.getString("access-token"); String resourceId = conf.getString("resource-id"); - ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig(accessToken, resourceId); + String deviceId = conf.getString("device-id"); + ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig(deviceId, accessToken, resourceId); deviceConfigs.add(deviceConfig); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java index da9520c60..d1c2f3174 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java @@ -11,19 +11,16 @@ */ @Getter public final class ReservationDeviceConfig { - public static final String DEVICE_ID_PREFIX = "shongo:reservation:device:"; - - private static int deviceCount = 1; - private final String accessToken; private final String resourceId; + private final String deviceId; private final UserData userData; - public ReservationDeviceConfig(@NotNull String accessToken, @NotNull String resourceId) { + public ReservationDeviceConfig(@NotNull String deviceId, @NotNull String accessToken, @NotNull String resourceId) { this.accessToken = accessToken; this.resourceId = resourceId; + this.deviceId = deviceId; this.userData = createUserData(); - deviceCount++; } @Override @@ -39,10 +36,9 @@ private UserData createUserData() { UserInformation userInformation = userData.getUserInformation(); UserAuthorizationData userAuthData = new UserAuthorizationData(UserAuthorizationData.LOA_EXTENDED); - String userId = DEVICE_ID_PREFIX + deviceCount; String name = "Reservation Device For " + resourceId; - userInformation.setUserId(userId); + userInformation.setUserId(deviceId); userInformation.setFullName(name); userData.setUserAuthorizationData(userAuthData); From b13597767112f75616b49e76a2768bfed368edd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Mon, 25 Mar 2024 18:31:10 +0100 Subject: [PATCH 115/134] make devices private to Authorization --- .../api/rpc/ReservationServiceImpl.java | 3 +- .../authorization/Authorization.java | 38 ++++++++++--------- .../authorization/ServerAuthorization.java | 8 ++-- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java index d55c02f1e..eed84def7 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java @@ -1036,8 +1036,7 @@ public ListResponse listReservationRequests(Reservati try { QueryFilter queryFilter = new QueryFilter("reservation_request_summary", true); - Optional deviceUser = authorization.reservationDevices.stream() - .filter(d -> d.getAccessToken().equals(securityToken.getAccessToken())).findFirst(); + Optional deviceUser = authorization.getReservationDeviceByToken(securityToken.getAccessToken()); if (deviceUser.isEmpty()) { // List only reservation requests which is current user permitted to read diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java index 4b22e044e..1da70a411 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java @@ -24,8 +24,6 @@ import javax.persistence.Query; import java.util.*; -import static cz.cesnet.shongo.controller.authorization.ReservationDeviceConfig.DEVICE_ID_PREFIX; - /** * Provides methods for performing authentication, authorization and fetching user data from web service. * @@ -71,11 +69,6 @@ public abstract class Authorization EVERYONE_GROUP.addAdministrator(ROOT_USER_ID); } - /** - * List of devices authorized to make reservations on a particular resource. - */ - public ImmutableList reservationDevices; - /** * @see EntityManagerFactory */ @@ -117,6 +110,11 @@ public abstract class Authorization */ private AuthorizationExpression reservationExpression; + /** + * List of devices authorized to make reservations on a particular resource. + */ + private final ImmutableList reservationDevices; + /** * Constructor. * @@ -206,6 +204,14 @@ public void clearCache() cache.clear(); } + public final Optional getReservationDeviceById(String id) { + return reservationDevices.stream().filter(device -> device.getDeviceId().equals(id)).findFirst(); + } + + public final Optional getReservationDeviceByToken(String accessToken) { + return reservationDevices.stream().filter(device -> device.getAccessToken().equals(accessToken)).findFirst(); + } + /** * Validate given {@code securityToken}. * @@ -295,12 +301,10 @@ public final UserInformation getUserInformation(SecurityToken securityToken) public final UserInformation getUserInformation(String userId) throws ControllerReportSet.UserNotExistsException { - for (ReservationDeviceConfig reservationDevice : reservationDevices) { - UserData deviceUser = reservationDevice.getUserData(); + Optional reservationDevice = getReservationDeviceById(userId); - if (userId.equals(deviceUser.getUserId())) { - return deviceUser.getUserInformation(); - } + if (reservationDevice.isPresent()) { + return reservationDevice.get().getUserData().getUserInformation(); } if (UserInformation.isLocal(userId)) { @@ -357,12 +361,10 @@ public final UserData getUserData(String userId) } // Reservation device user - for (ReservationDeviceConfig reservationDevice : reservationDevices) { - UserData userData = reservationDevice.getUserData(); + Optional reservationDevice = getReservationDeviceById(userId); - if (userId.equals(userData.getUserId())) { - return userData; - } + if (reservationDevice.isPresent()) { + return reservationDevice.get().getUserData(); } UserData userData; @@ -1079,7 +1081,7 @@ private AclUserState fetchAclUserState(String userId) if (userId != null) { aclIdentities.add(aclProvider.getIdentity(AclIdentityType.USER, userId)); } - if (!userId.startsWith(DEVICE_ID_PREFIX)) { + if (getReservationDeviceById(userId).isEmpty()) { for (String groupId : listUserGroupIds(userId)) { aclIdentities.add(aclProvider.getIdentity(AclIdentityType.GROUP, groupId)); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java index 78fb7e6a2..d58df4ddf 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ServerAuthorization.java @@ -207,10 +207,10 @@ protected UserData onGetUserDataByAccessToken(String accessToken) return ROOT_USER_DATA; } - for (ReservationDeviceConfig reservationDevice : reservationDevices) { - if (accessToken.equals(reservationDevice.getAccessToken())) { - return reservationDevice.getUserData(); - } + Optional reservationDevice = authorization.getReservationDeviceByToken(accessToken); + + if (reservationDevice.isPresent()) { + return reservationDevice.get().getUserData(); } Exception errorException = null; From 8fd500719000df2a98b7df1261618cba33eaca3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Thu, 4 Apr 2024 18:24:13 +0200 Subject: [PATCH 116/134] use ObjectIdentifier class to get persistence id --- .../shongo/controller/api/rpc/ReservationServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java index eed84def7..4fdbe0ab1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImpl.java @@ -1045,7 +1045,7 @@ public ListResponse listReservationRequests(Reservati } else { // List only reservation requests of resource which reservation device has access to String resourceId = deviceUser.get().getResourceId(); - int resourceIdNum = Integer.parseInt(resourceId.substring(resourceId.lastIndexOf(":") + 1)); + long resourceIdNum = ObjectIdentifier.parse(resourceId).getPersistenceId(); queryFilter.addFilter("specification_summary.resource_id = :deviceResourceId"); queryFilter.addFilterParameter("deviceResourceId", resourceIdNum); } From 80aa9d3c6922099f2d8e13c96fc925a1f5e49faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Thu, 4 Apr 2024 18:24:30 +0200 Subject: [PATCH 117/134] make immutable list in Authorization --- .../cz/cesnet/shongo/controller/ControllerConfiguration.java | 4 ++-- .../cesnet/shongo/controller/authorization/Authorization.java | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java index efae2f697..d55506b4d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java @@ -674,7 +674,7 @@ public String getDeviceTagName() /** * @return list of reservation devices. */ - public ImmutableList getReservationDevices() { + public List getReservationDevices() { List deviceConfigs = new ArrayList<>(); for (HierarchicalConfiguration conf : configurationsAt(SECURITY_AUTHORIZATION_RESERVATION_DEVICE)) { @@ -685,6 +685,6 @@ public ImmutableList getReservationDevices() { deviceConfigs.add(deviceConfig); } - return ImmutableList.copyOf(deviceConfigs); + return deviceConfigs; } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java index 1da70a411..95c4e038d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java @@ -163,7 +163,7 @@ protected Long getObjectId(PersistentObject object) this.reservationExpression = new AuthorizationExpression( configuration.getString(ControllerConfiguration.SECURITY_AUTHORIZATION_RESERVATION), this); - this.reservationDevices = configuration.getReservationDevices(); + this.reservationDevices = ImmutableList.copyOf(configuration.getReservationDevices()); } /** @@ -450,7 +450,6 @@ public final Collection listUserInformation(Set filterU return userInformationList; } - /** * @param userId * @return true if the user is Shongo admin (should have all permissions), From 7d79cdbb8711a20ba0a41877bbc75f9dbf1df04f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Fri, 5 Apr 2024 18:42:45 +0200 Subject: [PATCH 118/134] add request to get reservation device data --- .../controller/api/ReservationDevice.java | 46 ++++++++++++++++++ .../api/rpc/AuthorizationService.java | 6 ++- .../controller/ControllerConfiguration.java | 1 - .../api/rpc/AuthorizationServiceImpl.java | 17 ++++++- .../authorization/Authorization.java | 2 - .../shongo/controller/rest/RestApiPath.java | 3 ++ .../ReservationDeviceController.java | 48 +++++++++++++++++++ .../ReservationDeviceModel.java | 25 ++++++++++ 8 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationDevice.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceController.java create mode 100644 shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationdevice/ReservationDeviceModel.java diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationDevice.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationDevice.java new file mode 100644 index 000000000..c49316fdc --- /dev/null +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/ReservationDevice.java @@ -0,0 +1,46 @@ +package cz.cesnet.shongo.controller.api; + +import cz.cesnet.shongo.api.DataMap; +import cz.cesnet.shongo.api.IdentifiedComplexType; +import lombok.Getter; +import lombok.Setter; + +/** + * Reservation device authorized to create/list reservations for a particular resource. + */ +@Getter +@Setter +public class ReservationDevice extends IdentifiedComplexType { + private String accessToken; + private String resourceId; + + private static final String ID = "id"; + private static final String ACCESS_TOKEN = "access_token"; + private static final String RESOURCE_ID = "resource_id"; + + + @Override + public DataMap toData() + { + DataMap dataMap = super.toData(); + dataMap.set(ID, id); + dataMap.set(ACCESS_TOKEN, accessToken); + dataMap.set(RESOURCE_ID, resourceId); + return dataMap; + } + + @Override + public void fromData(DataMap dataMap) + { + super.fromData(dataMap); + id = dataMap.getString(ID); + accessToken = dataMap.getString(ACCESS_TOKEN); + resourceId = dataMap.getString(RESOURCE_ID); + } + + @Override + public String toString() + { + return String.format("ReservationDevice (%s, %s)", id, resourceId); + } +} diff --git a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/rpc/AuthorizationService.java b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/rpc/AuthorizationService.java index 6a7c447ce..c014b5143 100644 --- a/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/rpc/AuthorizationService.java +++ b/shongo-controller-api/src/main/java/cz/cesnet/shongo/controller/api/rpc/AuthorizationService.java @@ -2,15 +2,14 @@ import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.api.rpc.Service; -import cz.cesnet.shongo.controller.ObjectRole; import cz.cesnet.shongo.controller.ObjectPermission; +import cz.cesnet.shongo.controller.api.ReservationDevice; import cz.cesnet.shongo.controller.SystemPermission; import cz.cesnet.shongo.controller.api.*; import cz.cesnet.shongo.controller.api.request.*; import java.util.List; import java.util.Map; -import java.util.Set; /** * Interface defining service for accessing Shongo ACL. @@ -194,4 +193,7 @@ public interface AuthorizationService extends Service */ @API public List listReferencedUsers(SecurityToken securityToken); + + @API + public ReservationDevice getReservationDevice(SecurityToken securityToken); } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java index d55506b4d..24984dcf5 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/ControllerConfiguration.java @@ -1,7 +1,6 @@ package cz.cesnet.shongo.controller; import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; import cz.cesnet.shongo.PersonInformation; import cz.cesnet.shongo.controller.authorization.ReservationDeviceConfig; import cz.cesnet.shongo.controller.booking.executable.Executable; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/AuthorizationServiceImpl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/AuthorizationServiceImpl.java index 5ec432d3d..c88d592ab 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/AuthorizationServiceImpl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/AuthorizationServiceImpl.java @@ -11,9 +11,9 @@ import cz.cesnet.shongo.controller.api.request.*; import cz.cesnet.shongo.controller.authorization.Authorization; import cz.cesnet.shongo.controller.authorization.AuthorizationManager; +import cz.cesnet.shongo.controller.authorization.ReservationDeviceConfig; import cz.cesnet.shongo.controller.authorization.UserIdSet; import cz.cesnet.shongo.controller.booking.ObjectIdentifier; -import cz.cesnet.shongo.controller.booking.person.UserPerson; import cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest; import cz.cesnet.shongo.controller.booking.Allocation; import cz.cesnet.shongo.controller.booking.request.ReservationRequest; @@ -859,6 +859,21 @@ public List listReferencedUsers(SecurityToken securityToken) } } + @Override + public ReservationDevice getReservationDevice(SecurityToken securityToken) { + Optional deviceConfigOpt = authorization.getReservationDeviceByToken(securityToken.getAccessToken()); + + if (deviceConfigOpt.isPresent()) { + ReservationDeviceConfig deviceConfig = deviceConfigOpt.get(); + ReservationDevice device = new ReservationDevice(); + device.setId(deviceConfig.getDeviceId()); + device.setAccessToken(deviceConfig.getAccessToken()); + device.setResourceId(deviceConfig.getResourceId()); + return device; + } + return null; + } + /** * @param objectId of object which should be checked for existence * @param entityManager which can be used diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java index 95c4e038d..0ac0967c3 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java @@ -15,13 +15,11 @@ import cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest; import cz.cesnet.shongo.controller.domains.InterDomainAgent; import cz.cesnet.shongo.controller.settings.UserSessionSettings; -import org.apache.http.MethodNotSupportedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; -import javax.persistence.Query; import java.util.*; /** diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java index df180defb..89e95e5f6 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java @@ -59,4 +59,7 @@ public class RestApiPath public static final String RUNTIME_MANAGEMENT_PARTICIPANTS_SNAPSHOT = "/participants/{participantId:.+}/snapshot"; public static final String RUNTIME_MANAGEMENT_RECORDING_START = "/recording/start"; public static final String RUNTIME_MANAGEMENT_RECORDING_STOP = "/recording/stop"; + + // Reservation device + public static final String RESERVATION_DEVICE = API_PREFIX + "/reservation_device"; } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceController.java new file mode 100644 index 000000000..f6aa75811 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceController.java @@ -0,0 +1,48 @@ +package cz.cesnet.shongo.controller.rest.controllers; + +import cz.cesnet.shongo.controller.api.ReservationDevice; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; +import cz.cesnet.shongo.controller.rest.RestApiPath; +import cz.cesnet.shongo.controller.rest.models.reservationdevice.ReservationDeviceModel; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static cz.cesnet.shongo.controller.rest.config.security.AuthFilter.TOKEN; + +/** + * Rest controller for endpoints related to reservation devices. + * + * @author Michal Drobňák + */ +@RestController +@RequestMapping(RestApiPath.RESERVATION_DEVICE) +@RequiredArgsConstructor +@Slf4j +public class ReservationDeviceController { + + private final AuthorizationService authorizationService; + + @Operation(summary = "Get reservation device associated with Bearer token.") + @GetMapping + ResponseEntity getReservationDevice( + @RequestAttribute(TOKEN) SecurityToken securityToken + ) { + ReservationDevice device = authorizationService.getReservationDevice(securityToken); + + if (device != null) { + ReservationDeviceModel model = new ReservationDeviceModel(device); + log.info("Get reservation device: {}", model); + return ResponseEntity.ok().body(model); + } + log.info("Device not found"); + + return ResponseEntity.notFound().build(); + } +} diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationdevice/ReservationDeviceModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationdevice/ReservationDeviceModel.java new file mode 100644 index 000000000..99b79cbc4 --- /dev/null +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationdevice/ReservationDeviceModel.java @@ -0,0 +1,25 @@ +package cz.cesnet.shongo.controller.rest.models.reservationdevice; + +import cz.cesnet.shongo.controller.api.ReservationDevice; +import lombok.Data; + +/** + * Represents a reservation device with authorization to create/view reservations of a particular resource. + * + * @author Michal Drobňák + */ +@Data +public class ReservationDeviceModel { + private String id; + private String resourceId; + + public ReservationDeviceModel(ReservationDevice reservationDevice) { + this.id = reservationDevice.getId(); + this.resourceId = reservationDevice.getResourceId(); + } + + @Override + public String toString() { + return "ReservationDevice: (id=" + id + ", resourceId=" + resourceId + ")"; + } +} From b81e4c11d6295c6bcfa0f49a7403e2b392dd6d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Wed, 17 Apr 2024 22:34:58 +0200 Subject: [PATCH 119/134] test user data fetching --- data/controller.properties | 4 ++ data/controller.script | 44 +++++++++++++++++++ .../authorization/Authorization.java | 4 +- .../controller/AbstractControllerTest.java | 11 +++++ .../shongo/controller/DummyAuthorization.java | 38 ++++++++++++++++ .../authorization/AuthorizationTest.java | 44 +++++++++++++++---- 6 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 data/controller.properties create mode 100644 data/controller.script diff --git a/data/controller.properties b/data/controller.properties new file mode 100644 index 000000000..1618fd2bd --- /dev/null +++ b/data/controller.properties @@ -0,0 +1,4 @@ +#HSQL Database Engine 2.3.0 +#Wed Mar 20 21:55:45 UTC 2024 +modified=no +version=2.3.0 diff --git a/data/controller.script b/data/controller.script new file mode 100644 index 000000000..1eadfa09e --- /dev/null +++ b/data/controller.script @@ -0,0 +1,44 @@ +SET DATABASE UNIQUE NAME HSQLDB8E5DD0FA30 +SET DATABASE GC 0 +SET DATABASE DEFAULT RESULT MEMORY ROWS 0 +SET DATABASE EVENT LOG LEVEL 0 +SET DATABASE SQL NAMES FALSE +SET DATABASE SQL REFERENCES FALSE +SET DATABASE SQL SIZE TRUE +SET DATABASE SQL TYPES FALSE +SET DATABASE SQL TDC DELETE TRUE +SET DATABASE SQL TDC UPDATE TRUE +SET DATABASE SQL TRANSLATE TTI TYPES TRUE +SET DATABASE SQL CONCAT NULLS TRUE +SET DATABASE SQL UNIQUE NULLS TRUE +SET DATABASE SQL CONVERT TRUNCATE TRUE +SET DATABASE SQL AVG SCALE 0 +SET DATABASE SQL DOUBLE NAN TRUE +SET DATABASE TRANSACTION CONTROL LOCKS +SET DATABASE DEFAULT ISOLATION LEVEL READ COMMITTED +SET DATABASE TRANSACTION ROLLBACK ON CONFLICT TRUE +SET DATABASE TEXT TABLE DEFAULTS '' +SET FILES WRITE DELAY 0 +SET FILES BACKUP INCREMENT TRUE +SET FILES CACHE SIZE 10000 +SET FILES CACHE ROWS 50000 +SET FILES SCALE 32 +SET FILES LOB SCALE 32 +SET FILES DEFRAG 0 +SET FILES NIO TRUE +SET FILES NIO SIZE 256 +SET FILES LOG TRUE +SET FILES LOG SIZE 50 +CREATE USER SA PASSWORD DIGEST 'd41d8cd98f00b204e9800998ecf8427e' +ALTER USER SA SET LOCAL TRUE +CREATE SCHEMA PUBLIC AUTHORIZATION DBA +ALTER SEQUENCE SYSTEM_LOBS.LOB_ID RESTART WITH 1 +SET DATABASE DEFAULT INITIAL SCHEMA PUBLIC +GRANT USAGE ON DOMAIN INFORMATION_SCHEMA.SQL_IDENTIFIER TO PUBLIC +GRANT USAGE ON DOMAIN INFORMATION_SCHEMA.YES_OR_NO TO PUBLIC +GRANT USAGE ON DOMAIN INFORMATION_SCHEMA.TIME_STAMP TO PUBLIC +GRANT USAGE ON DOMAIN INFORMATION_SCHEMA.CARDINAL_NUMBER TO PUBLIC +GRANT USAGE ON DOMAIN INFORMATION_SCHEMA.CHARACTER_DATA TO PUBLIC +GRANT DBA TO SA +SET SCHEMA SYSTEM_LOBS +INSERT INTO BLOCKS VALUES(0,2147483647,0) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java index 0ac0967c3..7b341dec3 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java @@ -202,11 +202,11 @@ public void clearCache() cache.clear(); } - public final Optional getReservationDeviceById(String id) { + public Optional getReservationDeviceById(String id) { return reservationDevices.stream().filter(device -> device.getDeviceId().equals(id)).findFirst(); } - public final Optional getReservationDeviceByToken(String accessToken) { + public Optional getReservationDeviceByToken(String accessToken) { return reservationDevices.stream().filter(device -> device.getAccessToken().equals(accessToken)).findFirst(); } diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java index f97fdd074..e2365379e 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java @@ -9,6 +9,7 @@ import cz.cesnet.shongo.controller.api.request.ReservationRequestListRequest; import cz.cesnet.shongo.controller.api.rpc.*; import cz.cesnet.shongo.controller.authorization.Authorization; +import cz.cesnet.shongo.controller.authorization.ReservationDeviceConfig; import cz.cesnet.shongo.controller.cache.Cache; import cz.cesnet.shongo.controller.domains.InterDomainAgent; import cz.cesnet.shongo.controller.notification.NotificationManager; @@ -67,6 +68,16 @@ public abstract class AbstractControllerTest extends AbstractDatabaseTest protected static final SecurityToken SECURITY_TOKEN_USER3 = new SecurityToken("53a0bbcbb6086add8c232ff5eddf662035a02908"); + /** + * {@link ReservationDeviceConfig} for reservation device #1. + */ + protected static final ReservationDeviceConfig RESERVATION_DEVICE_CONFIG1 = new ReservationDeviceConfig("reservation:device:1", "d48692d7-9fba-4040-8faf-6268963b748a", "shongo:test:res:1"); + + /** + * {@link ReservationDeviceConfig} for reservation device #2. + */ + protected static final ReservationDeviceConfig RESERVATION_DEVICE_CONFIG2 = new ReservationDeviceConfig("reservation:device:2", "2fc6d5b1-2731-423e-9850-da93fa0d79be", "shongo:test:res:2"); + /** * @see #SECURITY_TOKEN_USER1 */ diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/DummyAuthorization.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/DummyAuthorization.java index 87595f13f..d1369a047 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/DummyAuthorization.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/DummyAuthorization.java @@ -5,6 +5,7 @@ import cz.cesnet.shongo.controller.api.SecurityToken; import cz.cesnet.shongo.controller.authorization.AdministrationMode; import cz.cesnet.shongo.controller.authorization.Authorization; +import cz.cesnet.shongo.controller.authorization.ReservationDeviceConfig; import cz.cesnet.shongo.controller.authorization.UserAuthorizationData; import cz.cesnet.shongo.controller.authorization.UserData; import org.slf4j.Logger; @@ -13,6 +14,9 @@ import javax.persistence.EntityManagerFactory; import java.util.*; +import static cz.cesnet.shongo.controller.AbstractControllerTest.RESERVATION_DEVICE_CONFIG1; +import static cz.cesnet.shongo.controller.AbstractControllerTest.RESERVATION_DEVICE_CONFIG2; + /** * Testing {@link Authorization}, * @@ -41,6 +45,12 @@ public class DummyAuthorization extends Authorization */ protected static final UserData USER3_DATA; + + private static final Map reservationDeviceByAccessToken; + + private static final Map reservationDeviceById; + + /** * Known users. */ @@ -98,6 +108,14 @@ public class DummyAuthorization extends Authorization userData.setUserAuthorizationData(new UserAuthorizationData(UserAuthorizationData.LOA_EXTENDED)); userDataById.put(userData.getUserId(), userData); } + + reservationDeviceByAccessToken = new HashMap<>(); + reservationDeviceByAccessToken.put(RESERVATION_DEVICE_CONFIG1.getAccessToken(), RESERVATION_DEVICE_CONFIG1); + reservationDeviceByAccessToken.put(RESERVATION_DEVICE_CONFIG2.getAccessToken(), RESERVATION_DEVICE_CONFIG2); + + reservationDeviceById = new HashMap<>(); + reservationDeviceById.put(RESERVATION_DEVICE_CONFIG1.getDeviceId(), RESERVATION_DEVICE_CONFIG1); + reservationDeviceById.put(RESERVATION_DEVICE_CONFIG2.getDeviceId(), RESERVATION_DEVICE_CONFIG2); } /** @@ -320,6 +338,26 @@ public void onRemoveGroupUser(String groupId, String userId) userIds.remove(userId); } + @Override + public Optional getReservationDeviceById(String id) { + ReservationDeviceConfig device = reservationDeviceById.get(id); + + if (device == null) { + return Optional.empty(); + } + return Optional.of(device); + } + + @Override + public Optional getReservationDeviceByToken(String accessToken) { + ReservationDeviceConfig device = reservationDeviceByAccessToken.get(accessToken); + + if (device == null) { + return Optional.empty(); + } + return Optional.of(device); + } + /** * @param configuration to be used for initialization * @param entityManagerFactory diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/authorization/AuthorizationTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/authorization/AuthorizationTest.java index 686889cb0..77b9b63ef 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/authorization/AuthorizationTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/authorization/AuthorizationTest.java @@ -2,6 +2,7 @@ import cz.cesnet.shongo.AliasType; import cz.cesnet.shongo.Technology; +import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.*; import cz.cesnet.shongo.controller.api.*; import cz.cesnet.shongo.controller.api.request.AclEntryListRequest; @@ -21,6 +22,8 @@ import java.util.Map; import java.util.Set; +import static org.junit.Assert.assertEquals; + /** * Tests for creating, updating and deleting {@link cz.cesnet.shongo.controller.api.AclEntry}s. * @@ -81,12 +84,12 @@ public void testResource() throws Exception String resourceId = createResource(SECURITY_TOKEN, resource); aclEntries.add(new AclEntry(userId, resourceId, ObjectRole.OWNER)); - Assert.assertEquals(aclEntries, getAclEntries()); + assertEquals(aclEntries, getAclEntries()); getResourceService().deleteResource(SECURITY_TOKEN, resourceId); aclEntries.remove(new AclEntry(userId, resourceId, ObjectRole.OWNER)); - Assert.assertEquals(0, aclEntries.size()); + assertEquals(0, aclEntries.size()); } @Test @@ -103,7 +106,7 @@ public void testReservationRequest() throws Exception String resourceId = createResource(SECURITY_TOKEN, resource); aclEntries.add(new AclEntry(userId, resourceId, ObjectRole.OWNER)); - Assert.assertEquals(aclEntries, getAclEntries()); + assertEquals(aclEntries, getAclEntries()); ReservationRequest reservationRequest = new ReservationRequest(); reservationRequest.setSlot("2013-01-01T12:00", "PT2H"); @@ -114,13 +117,13 @@ public void testReservationRequest() throws Exception aclEntries.add(new AclEntry(userId, reservationRequestId, ObjectRole.OWNER)); aclEntries.add(new AclEntry(userId, reservation.getId(), ObjectRole.OWNER)); - Assert.assertEquals(aclEntries, getAclEntries()); + assertEquals(aclEntries, getAclEntries()); deleteAclEntry(userId, reservationRequestId, ObjectRole.OWNER); aclEntries.clear(); aclEntries.add(new AclEntry(userId, resourceId, ObjectRole.OWNER)); - Assert.assertEquals(aclEntries, getAclEntries()); + assertEquals(aclEntries, getAclEntries()); reservationRequest = new ReservationRequest(); reservationRequest.setSlot("2013-01-02T12:00", "PT2H"); @@ -144,13 +147,13 @@ public void testReservationRequest() throws Exception aclEntries.add(new AclEntry(userId, aliasReservation2.getId(), ObjectRole.OWNER)); aclEntries.add(new AclEntry(userId, valueReservation1, ObjectRole.OWNER)); aclEntries.add(new AclEntry(userId, valueReservation2, ObjectRole.OWNER)); - Assert.assertEquals(aclEntries, getAclEntries()); + assertEquals(aclEntries, getAclEntries()); deleteAclEntry(userId, reservationRequestId, ObjectRole.OWNER); aclEntries.clear(); aclEntries.add(new AclEntry(userId, resourceId, ObjectRole.OWNER)); - Assert.assertEquals(aclEntries, getAclEntries()); + assertEquals(aclEntries, getAclEntries()); } @Test @@ -222,8 +225,8 @@ public void testReusedReservationRequest() throws Exception String reservationRequest2Id = allocate(SECURITY_TOKEN_USER1, reservationRequest2); ExistingReservation reservation2 = (ExistingReservation) checkAllocated(reservationRequest2Id); - Assert.assertEquals(aliasReservation.getId(), reservation1.getReservation().getId()); - Assert.assertEquals(aliasReservation.getId(), reservation2.getReservation().getId()); + assertEquals(aliasReservation.getId(), reservation1.getReservation().getId()); + assertEquals(aliasReservation.getId(), reservation2.getReservation().getId()); getAuthorizationService().createAclEntry(SECURITY_TOKEN_USER1, new AclEntry(user2Id, reservationRequest1Id, ObjectRole.OWNER)); @@ -310,6 +313,29 @@ public void multipleAclRequestSpeedTest() throws Exception Assert.assertTrue("List all is not quicker than by one.", listPermissionsTime < listPermissionsByOneTime); } + @Test + public void shouldGetUserDataOfReservationDevice() { + DummyAuthorization authorization = getAuthorization(); + assertEquals(authorization.getUserData(RESERVATION_DEVICE_CONFIG1.getDeviceId()), RESERVATION_DEVICE_CONFIG1.getUserData()); + } + + @Test + public void shouldGetUserInformationOfReservationDeviceById() { + DummyAuthorization authorization = getAuthorization(); + assertEquals(authorization.getUserInformation(RESERVATION_DEVICE_CONFIG1.getDeviceId()), RESERVATION_DEVICE_CONFIG1.getUserData().getUserInformation()); + } + + @Test + public void shouldGetUserInformationOfReservationDeviceByToken() { + DummyAuthorization authorization = getAuthorization(); + UserInformation userInformation = RESERVATION_DEVICE_CONFIG1.getUserData().getUserInformation(); + + SecurityToken securityToken = new SecurityToken(RESERVATION_DEVICE_CONFIG1.getAccessToken()); + securityToken.setUserInformation(userInformation); + + assertEquals(authorization.getUserInformation(securityToken), userInformation); + } + /** * @return collection of all {@link cz.cesnet.shongo.controller.api.AclEntry} for user with {@link #SECURITY_TOKEN} * @throws Exception From fc2953efe1220bc30342f4a00073ce57d229f21e Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 18 Apr 2024 17:59:19 +0200 Subject: [PATCH 120/134] add endpoint for listing reservation requests waiting for confirmation on owned resources --- .../shongo/controller/rest/RestApiPath.java | 1 + .../ReservationRequestController.java | 53 +++++++++++++++++++ .../ReservationRequestModel.java | 25 ++++++--- 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java index df180defb..afd06e6f5 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/RestApiPath.java @@ -32,6 +32,7 @@ public class RestApiPath // Reservation requests public static final String RESERVATION_REQUESTS = API_PREFIX + "/reservation_requests"; + public static final String RESERVATION_REQUESTS_AWAITING_CONFIRMATION = "/awaiting_confirmation"; public static final String RESERVATION_REQUESTS_ACCEPT = ID_SUFFIX + "/accept"; public static final String RESERVATION_REQUESTS_REJECT = ID_SUFFIX + "/reject"; public static final String RESERVATION_REQUESTS_REVERT = ID_SUFFIX + "/revert"; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index 317f50dea..ef056240a 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -50,6 +50,7 @@ import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -401,6 +402,58 @@ void deleteRequests( ids.forEach(id -> reservationService.deleteReservationRequest(securityToken, id)); } + @Operation(summary = "Lists reservation requests waiting for confirmation of resources owned by a user.") + @GetMapping(RestApiPath.RESERVATION_REQUESTS_AWAITING_CONFIRMATION) + public ListResponse listOwnedReservationRequests( + @RequestAttribute(TOKEN) SecurityToken securityToken, + @RequestParam(value = "interval_from", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + DateTime intervalFrom, + @RequestParam(value = "interval_to", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + DateTime intervalTo, + @RequestParam(value = "resource", required = false) Set resourceId, + @RequestParam(value = "start", required = false) Integer start, + @RequestParam(value = "count", required = false) Integer count, + @RequestParam(value = "sort", required = false, defaultValue = "SLOT_START") ReservationRequestListRequest.Sort sort, + @RequestParam(value = "sort_desc", required = false, defaultValue = "true") boolean sortDescending + ) { + if (resourceId == null) { + resourceId = new HashSet<>(); + } + + ReservationRequestListRequest requestListRequest = new ReservationRequestListRequest(securityToken); + requestListRequest.setStart(start); + requestListRequest.setCount(count); + requestListRequest.setSort(sort); + requestListRequest.setSortDescending(sortDescending); + requestListRequest.setAllocationState(AllocationState.CONFIRM_AWAITING); + if (intervalFrom == null) { + intervalFrom = Temporal.DATETIME_INFINITY_START; + } + if (intervalTo == null) { + intervalTo = Temporal.DATETIME_INFINITY_END; + } + Interval interval = new Interval(intervalFrom, intervalTo); + requestListRequest.setInterval(interval); + requestListRequest.setSpecificationResourceIds(resourceId); + + ListResponse response = reservationService.listOwnedResourcesReservationRequests(requestListRequest); + + ListResponse listResponse = new ListResponse<>(); + listResponse.addAll(response.getItems().stream().map(item -> { + String resource = item.getResourceId(); + ResourceSummary resourceSummary = null; + if (resource != null) { + resourceSummary = cache.getResourceSummary(securityToken, resource); + } + return new ReservationRequestModel( + item, item, resourceSummary + ); + }).collect(Collectors.toList())); + listResponse.setStart(response.getStart()); + listResponse.setCount(response.getCount()); + return listResponse; + } + @Operation(summary = "Accepts reservation request.") @PostMapping(RestApiPath.RESERVATION_REQUESTS_ACCEPT) void acceptRequest( diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java index d2e1b60f0..188d81bab 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestModel.java @@ -49,8 +49,6 @@ public class ReservationRequestModel public ReservationRequestModel( ReservationRequestSummary summary, ReservationRequestSummary virtualRoomSummary, - Map> permissionsByReservationRequestId, - UserInformation ownerInformation, ResourceSummary resourceSummary) { this.id = summary.getId(); @@ -58,8 +56,6 @@ public ReservationRequestModel( this.createdAt = summary.getDateTime(); this.parentRequestId = summary.getParentReservationRequestId(); this.state = ReservationRequestState.fromApi(summary); - this.ownerName = ownerInformation.getFullName(); - this.ownerEmail = ownerInformation.getEmail(); this.slot = TimeInterval.fromApi(summary.getEarliestSlot()); this.type = SpecificationType.fromReservationRequestSummary(summary, true); this.virtualRoomData = new VirtualRoomModel(virtualRoomSummary); @@ -69,10 +65,6 @@ public ReservationRequestModel( this.futureSlotCount = summary.getFutureSlotCount(); this.auxData = summary.getAuxData(); - Set objectPermissions = permissionsByReservationRequestId.get(id); - this.isWritable = objectPermissions.contains(ObjectPermission.WRITE); - this.isProvidable = objectPermissions.contains(ObjectPermission.PROVIDE_RESERVATION_REQUEST); - switch (state != null ? state : ReservationRequestState.ALLOCATED) { case ALLOCATED_STARTED: case ALLOCATED_STARTED_AVAILABLE: @@ -83,4 +75,21 @@ public ReservationRequestModel( break; } } + + public ReservationRequestModel( + ReservationRequestSummary summary, + ReservationRequestSummary virtualRoomSummary, + Map> permissionsByReservationRequestId, + UserInformation ownerInformation, + ResourceSummary resourceSummary) + { + this(summary, virtualRoomSummary, resourceSummary); + + this.ownerName = ownerInformation.getFullName(); + this.ownerEmail = ownerInformation.getEmail(); + + Set objectPermissions = permissionsByReservationRequestId.get(id); + this.isWritable = objectPermissions.contains(ObjectPermission.WRITE); + this.isProvidable = objectPermissions.contains(ObjectPermission.PROVIDE_RESERVATION_REQUEST); + } } From 9d1f8957b48b59720ad08d3f9fd624866c3a94d1 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 18 Apr 2024 18:19:25 +0200 Subject: [PATCH 121/134] add rest response handling of SecurityInvalidTokenException --- .../shongo/controller/rest/config/security/AuthFilter.java | 5 +++-- .../controller/rest/error/ControllerExceptionHandler.java | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/AuthFilter.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/AuthFilter.java index f224efa70..5dadb2d63 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/AuthFilter.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/config/security/AuthFilter.java @@ -47,16 +47,17 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha String sanitizedToken = tokenParts[1].strip(); SecurityToken securityToken = new SecurityToken(sanitizedToken); - securityToken.setUserInformation(authorization.getUserInformation(securityToken)); try { + securityToken.setUserInformation(authorization.getUserInformation(securityToken)); authorization.validate(securityToken); - httpRequest.setAttribute(TOKEN, securityToken); } catch (ControllerReportSet.SecurityInvalidTokenException e) { httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Request unauthorized."); return; } + + httpRequest.setAttribute(TOKEN, securityToken); chain.doFilter(httpRequest, httpResponse); } } diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ControllerExceptionHandler.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ControllerExceptionHandler.java index 87717622b..e421a9c2d 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ControllerExceptionHandler.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/error/ControllerExceptionHandler.java @@ -4,6 +4,7 @@ import cz.cesnet.shongo.TodoImplementException; import cz.cesnet.shongo.controller.ControllerReportSet.ReservationRequestDeletedException; import cz.cesnet.shongo.controller.ControllerReportSet.ReservationRequestNotDeletableException; +import cz.cesnet.shongo.controller.ControllerReportSet.SecurityInvalidTokenException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -20,6 +21,12 @@ public class ControllerExceptionHandler { + @ExceptionHandler(SecurityInvalidTokenException.class) + public ResponseEntity handleSecurityInvalidToken(SecurityInvalidTokenException e) + { + return ErrorModel.createResponseFromException(e, HttpStatus.UNAUTHORIZED); + } + @ExceptionHandler(TodoImplementException.class) public ResponseEntity handleTodo(TodoImplementException e) { From 0b0fe7a403fa8ededcd4d247f311ac2c15f5ac65 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Thu, 18 Apr 2024 18:26:28 +0200 Subject: [PATCH 122/134] add guest pin to res req modification --- .../rest/controllers/ReservationRequestController.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index ef056240a..a29ddfe1e 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -371,6 +371,9 @@ IdModel modifyRequest( if (request.getAdminPin() != null) { modifiedRequest.setAdminPin(request.getAdminPin()); } + if (request.getGuestPin() != null) { + modifiedRequest.setGuestPin(request.getGuestPin()); + } if (request.getParticipantCount() != null) { modifiedRequest.setParticipantCount(request.getParticipantCount()); } From 9169897b9753ea998b3f7ea32a48a77bf70ab3fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Mon, 29 Apr 2024 18:07:18 +0200 Subject: [PATCH 123/134] add reservation device controller tests --- shongo-controller/pom.xml | 13 +++++++ .../ReservationDeviceControllerTest.java | 37 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 shongo-controller/src/test/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceControllerTest.java diff --git a/shongo-controller/pom.xml b/shongo-controller/pom.xml index 12e69d154..a306aa348 100644 --- a/shongo-controller/pom.xml +++ b/shongo-controller/pom.xml @@ -244,6 +244,19 @@ lombok provided + + + org.mockito + mockito-core + 5.11.0 + test + + + + net.bytebuddy + byte-buddy + 1.14.12 + diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceControllerTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceControllerTest.java new file mode 100644 index 000000000..26eec58c8 --- /dev/null +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceControllerTest.java @@ -0,0 +1,37 @@ +package cz.cesnet.shongo.controller.rest.controllers; + +import cz.cesnet.shongo.controller.AbstractControllerTest; +import cz.cesnet.shongo.controller.api.ReservationDevice; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; +import cz.cesnet.shongo.controller.rest.models.reservationdevice.ReservationDeviceModel; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.http.ResponseEntity; + +import static org.junit.Assert.assertEquals; + +public class ReservationDeviceControllerTest extends AbstractControllerTest { + private final AuthorizationService authorizationService = Mockito.mock(AuthorizationService.class); + private final ReservationDeviceController controller = new ReservationDeviceController(authorizationService); + + @Test + public void shouldReturnDeviceInfo() { + SecurityToken deviceToken = new SecurityToken(RESERVATION_DEVICE_CONFIG1.getAccessToken()); + ReservationDevice device = new ReservationDevice(); + device.setId(RESERVATION_DEVICE_CONFIG1.getDeviceId()); + device.setAccessToken(RESERVATION_DEVICE_CONFIG1.getAccessToken()); + device.setResourceId(RESERVATION_DEVICE_CONFIG1.getResourceId()); + ReservationDeviceModel model = new ReservationDeviceModel(device); + + Mockito.when(authorizationService.getReservationDevice(deviceToken)).thenReturn(device); + + assertEquals(ResponseEntity.ok(model), controller.getReservationDevice(deviceToken)); + } + + @Test + public void shouldReturnNotFound() { + Mockito.when(authorizationService.getReservationDevice(Mockito.any())).thenReturn(null); + assertEquals(ResponseEntity.notFound().build(), controller.getReservationDevice(new SecurityToken("test"))); + } +} \ No newline at end of file From 43a24e3ccf1673fceb9b8eb68c6c9193385cab7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Mon, 29 Apr 2024 22:39:46 +0200 Subject: [PATCH 124/134] add test for list reservation request with device token --- .../shongo/controller/DummyAuthorization.java | 20 +++++-- .../api/rpc/ReservationServiceImplTest.java | 53 +++++++++++++++++++ 2 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/DummyAuthorization.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/DummyAuthorization.java index d1369a047..5f1db4dfe 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/DummyAuthorization.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/DummyAuthorization.java @@ -110,12 +110,10 @@ public class DummyAuthorization extends Authorization } reservationDeviceByAccessToken = new HashMap<>(); - reservationDeviceByAccessToken.put(RESERVATION_DEVICE_CONFIG1.getAccessToken(), RESERVATION_DEVICE_CONFIG1); - reservationDeviceByAccessToken.put(RESERVATION_DEVICE_CONFIG2.getAccessToken(), RESERVATION_DEVICE_CONFIG2); - reservationDeviceById = new HashMap<>(); - reservationDeviceById.put(RESERVATION_DEVICE_CONFIG1.getDeviceId(), RESERVATION_DEVICE_CONFIG1); - reservationDeviceById.put(RESERVATION_DEVICE_CONFIG2.getDeviceId(), RESERVATION_DEVICE_CONFIG2); + + addReservationDevice(RESERVATION_DEVICE_CONFIG1); + addReservationDevice(RESERVATION_DEVICE_CONFIG2); } /** @@ -371,4 +369,16 @@ public static DummyAuthorization createInstance(ControllerConfiguration configur Authorization.setInstance(authorization); return authorization; } + + public static void addReservationDevice(ReservationDeviceConfig reservationDeviceConfig) { + String deviceId = reservationDeviceConfig.getDeviceId(); + String accessToken = reservationDeviceConfig.getAccessToken(); + UserData userData = reservationDeviceConfig.getUserData(); + + reservationDeviceById.put(deviceId, reservationDeviceConfig); + reservationDeviceByAccessToken.put(accessToken, reservationDeviceConfig); + + userDataById.put(deviceId, userData); + userDataByAccessToken.put(accessToken, userData); + } } diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java new file mode 100644 index 000000000..a0e7b733c --- /dev/null +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java @@ -0,0 +1,53 @@ +package cz.cesnet.shongo.controller.api.rpc; + +import cz.cesnet.shongo.controller.AbstractControllerTest; +import cz.cesnet.shongo.controller.DummyAuthorization; +import cz.cesnet.shongo.controller.ReservationRequestPurpose; +import cz.cesnet.shongo.controller.ReservationRequestReusement; +import cz.cesnet.shongo.controller.api.ReservationRequest; +import cz.cesnet.shongo.controller.api.ReservationRequestSummary; +import cz.cesnet.shongo.controller.api.Resource; +import cz.cesnet.shongo.controller.api.ResourceSpecification; +import cz.cesnet.shongo.controller.api.SecurityToken; +import cz.cesnet.shongo.controller.api.request.ListResponse; +import cz.cesnet.shongo.controller.api.request.ReservationRequestListRequest; +import cz.cesnet.shongo.controller.authorization.ReservationDeviceConfig; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ReservationServiceImplTest extends AbstractControllerTest { + @Test + public void shouldListReservationsForResourceManagedByDevice() throws Exception { + ReservationService reservationService = getReservationService(); + + // Create test resource managed by reservation device. + Resource resource = new Resource(); + resource.setName("resource"); + resource.setAllocatable(true); + String resourceId = createResource(resource); + + // Create test reservation request that device should see. + ReservationRequest reservationRequest = new ReservationRequest(); + reservationRequest.setSlot("2012-01-01T00:00", "P1Y"); + reservationRequest.setSpecification(new ResourceSpecification(resourceId)); + reservationRequest.setReusement(ReservationRequestReusement.OWNED); + reservationRequest.setPurpose(ReservationRequestPurpose.USER); + String reservationRequestId = allocate(reservationRequest); + + // Create reservation device that manages test resource. + ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "asd15asd19e1fe5wf9e51f", resourceId); + DummyAuthorization.addReservationDevice(deviceConfig); + SecurityToken deviceToken = new SecurityToken(deviceConfig.getAccessToken()); + deviceToken.setUserInformation(deviceToken.getUserInformation()); + + // List resources with device's security token. + ReservationRequestListRequest listRequest = new ReservationRequestListRequest(deviceToken); + ListResponse response = reservationService.listReservationRequests(listRequest); + ReservationRequestSummary firstItem = response.getItem(0); + + // Assert that there is only the test reservation request in response. + assertEquals(response.getCount(), 1); + assertEquals(reservationRequestId, firstItem.getId()); + } +} \ No newline at end of file From d32ea79aa4c6d603e1020e8c496103a1212a094e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Mon, 29 Apr 2024 22:55:51 +0200 Subject: [PATCH 125/134] add test for not listing reservation requests without access --- .../controller/AbstractControllerTest.java | 4 +- .../api/rpc/ReservationServiceImplTest.java | 50 ++++++++++++++----- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java index e2365379e..236470795 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java @@ -71,12 +71,12 @@ public abstract class AbstractControllerTest extends AbstractDatabaseTest /** * {@link ReservationDeviceConfig} for reservation device #1. */ - protected static final ReservationDeviceConfig RESERVATION_DEVICE_CONFIG1 = new ReservationDeviceConfig("reservation:device:1", "d48692d7-9fba-4040-8faf-6268963b748a", "shongo:test:res:1"); + protected static final ReservationDeviceConfig RESERVATION_DEVICE_CONFIG1 = new ReservationDeviceConfig("reservation:device:1", "d48692d7-9fba-4040-8faf-6268963b748a", "shongo:cz.cesnet:res:9999"); /** * {@link ReservationDeviceConfig} for reservation device #2. */ - protected static final ReservationDeviceConfig RESERVATION_DEVICE_CONFIG2 = new ReservationDeviceConfig("reservation:device:2", "2fc6d5b1-2731-423e-9850-da93fa0d79be", "shongo:test:res:2"); + protected static final ReservationDeviceConfig RESERVATION_DEVICE_CONFIG2 = new ReservationDeviceConfig("reservation:device:2", "2fc6d5b1-2731-423e-9850-da93fa0d79be", "shongo:cz.cesnet:res:9999"); /** * @see #SECURITY_TOKEN_USER1 diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java index a0e7b733c..6b5e9b0b0 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java @@ -21,19 +21,8 @@ public class ReservationServiceImplTest extends AbstractControllerTest { public void shouldListReservationsForResourceManagedByDevice() throws Exception { ReservationService reservationService = getReservationService(); - // Create test resource managed by reservation device. - Resource resource = new Resource(); - resource.setName("resource"); - resource.setAllocatable(true); - String resourceId = createResource(resource); - - // Create test reservation request that device should see. - ReservationRequest reservationRequest = new ReservationRequest(); - reservationRequest.setSlot("2012-01-01T00:00", "P1Y"); - reservationRequest.setSpecification(new ResourceSpecification(resourceId)); - reservationRequest.setReusement(ReservationRequestReusement.OWNED); - reservationRequest.setPurpose(ReservationRequestPurpose.USER); - String reservationRequestId = allocate(reservationRequest); + String resourceId = createTestResource(); + String reservationRequestId = createTestReservationRequest(resourceId); // Create reservation device that manages test resource. ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "asd15asd19e1fe5wf9e51f", resourceId); @@ -50,4 +39,39 @@ public void shouldListReservationsForResourceManagedByDevice() throws Exception assertEquals(response.getCount(), 1); assertEquals(reservationRequestId, firstItem.getId()); } + + @Test + public void shouldNotListReservationsForResourceNotManagedByDevice() throws Exception { + ReservationService reservationService = getReservationService(); + + String resourceId = createTestResource(); + createTestReservationRequest(resourceId); + + // Create security token for device that doesn't manage the test resource. + SecurityToken deviceToken = new SecurityToken(RESERVATION_DEVICE_CONFIG1.getAccessToken()); + deviceToken.setUserInformation(deviceToken.getUserInformation()); + + // List resources with device's security token. + ReservationRequestListRequest listRequest = new ReservationRequestListRequest(deviceToken); + ListResponse response = reservationService.listReservationRequests(listRequest); + + // Assert that there is only the test reservation request in response. + assertEquals(response.getCount(), 0); + } + + private String createTestResource() { + Resource resource = new Resource(); + resource.setName("resource"); + resource.setAllocatable(true); + return createResource(resource); + } + + private String createTestReservationRequest(String resourceId) throws Exception { + ReservationRequest reservationRequest = new ReservationRequest(); + reservationRequest.setSlot("2012-01-01T00:00", "P1Y"); + reservationRequest.setSpecification(new ResourceSpecification(resourceId)); + reservationRequest.setReusement(ReservationRequestReusement.OWNED); + reservationRequest.setPurpose(ReservationRequestPurpose.USER); + return allocate(reservationRequest); + } } \ No newline at end of file From cbe1ec02e73d025ca8c8ccb101e5ea47293fc3bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Wed, 1 May 2024 17:19:56 +0200 Subject: [PATCH 126/134] refactor reservation device test configuration --- .../authorization/Authorization.java | 16 ++++--- .../controller/AbstractControllerTest.java | 17 +++---- .../shongo/controller/DummyAuthorization.java | 46 ++++--------------- .../api/rpc/ReservationServiceImplTest.java | 18 +++----- .../authorization/AuthorizationTest.java | 17 +++++-- .../ReservationDeviceControllerTest.java | 13 ++++-- 6 files changed, 55 insertions(+), 72 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java index 7b341dec3..5971d16f5 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/Authorization.java @@ -175,7 +175,7 @@ protected void initialize() this.administratorExpression.evaluate(rootUserInformation, rootUserAuthorizationData); this.operatorExpression.evaluate(rootUserInformation, rootUserAuthorizationData); this.reservationExpression.evaluate(rootUserInformation, rootUserAuthorizationData); - this.createReservationDeviceAclEntries(reservationDevices); + this.createReservationDeviceAclEntries(); } /** @@ -203,11 +203,11 @@ public void clearCache() } public Optional getReservationDeviceById(String id) { - return reservationDevices.stream().filter(device -> device.getDeviceId().equals(id)).findFirst(); + return listReservationDevices().stream().filter(device -> device.getDeviceId().equals(id)).findFirst(); } public Optional getReservationDeviceByToken(String accessToken) { - return reservationDevices.stream().filter(device -> device.getAccessToken().equals(accessToken)).findFirst(); + return listReservationDevices().stream().filter(device -> device.getAccessToken().equals(accessToken)).findFirst(); } /** @@ -437,7 +437,7 @@ public final Collection listUserInformation(Set filterU checkForStaticUser(filterUserIds, userInformationList, ROOT_USER_DATA); // Remove reservation device ids from request and add their user data if filter contains their ID. - for (ReservationDeviceConfig reservationDevice : reservationDevices) { + for (ReservationDeviceConfig reservationDevice : listReservationDevices()) { UserData deviceUser = reservationDevice.getUserData(); checkForStaticUser(filterUserIds, userInformationList, deviceUser); } @@ -1065,6 +1065,10 @@ public UserAuthorizationData getUserAuthorizationData(SecurityToken securityToke return userAuthorizationData; } + public Collection listReservationDevices() { + return reservationDevices; + } + /** * Fetch {@link AclUserState} for given {@code userId}. * @@ -1266,11 +1270,11 @@ public static Authorization getInstance() throws IllegalStateException return authorization; } - private void createReservationDeviceAclEntries(List reservationDevices) { + private void createReservationDeviceAclEntries() { EntityManager entityManager = entityManagerFactory.createEntityManager(); AuthorizationManager authManager = new AuthorizationManager(entityManager, this); - reservationDevices.forEach(device -> { + listReservationDevices().forEach(device -> { authManager.beginTransaction(); entityManager.getTransaction().begin(); diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java index 236470795..a7d5facec 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java @@ -68,16 +68,6 @@ public abstract class AbstractControllerTest extends AbstractDatabaseTest protected static final SecurityToken SECURITY_TOKEN_USER3 = new SecurityToken("53a0bbcbb6086add8c232ff5eddf662035a02908"); - /** - * {@link ReservationDeviceConfig} for reservation device #1. - */ - protected static final ReservationDeviceConfig RESERVATION_DEVICE_CONFIG1 = new ReservationDeviceConfig("reservation:device:1", "d48692d7-9fba-4040-8faf-6268963b748a", "shongo:cz.cesnet:res:9999"); - - /** - * {@link ReservationDeviceConfig} for reservation device #2. - */ - protected static final ReservationDeviceConfig RESERVATION_DEVICE_CONFIG2 = new ReservationDeviceConfig("reservation:device:2", "2fc6d5b1-2731-423e-9850-da93fa0d79be", "shongo:cz.cesnet:res:9999"); - /** * @see #SECURITY_TOKEN_USER1 */ @@ -833,4 +823,11 @@ protected void checkSpecificationSummaryConsistency() entityManager.close(); } } + + protected String createTestResource() { + Resource resource = new Resource(); + resource.setName("resource"); + resource.setAllocatable(true); + return createResource(resource); + } } diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/DummyAuthorization.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/DummyAuthorization.java index 5f1db4dfe..8b561c00a 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/DummyAuthorization.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/DummyAuthorization.java @@ -14,9 +14,6 @@ import javax.persistence.EntityManagerFactory; import java.util.*; -import static cz.cesnet.shongo.controller.AbstractControllerTest.RESERVATION_DEVICE_CONFIG1; -import static cz.cesnet.shongo.controller.AbstractControllerTest.RESERVATION_DEVICE_CONFIG2; - /** * Testing {@link Authorization}, * @@ -45,12 +42,6 @@ public class DummyAuthorization extends Authorization */ protected static final UserData USER3_DATA; - - private static final Map reservationDeviceByAccessToken; - - private static final Map reservationDeviceById; - - /** * Known users. */ @@ -71,6 +62,11 @@ public class DummyAuthorization extends Authorization */ private final Map> userIdsInGroup = new HashMap>(); + /** + * List of reservation devices, that would come from the configuration. + */ + private final Collection reservationDevices = new ArrayList<>(); + /** * Static initialization. */ @@ -108,12 +104,6 @@ public class DummyAuthorization extends Authorization userData.setUserAuthorizationData(new UserAuthorizationData(UserAuthorizationData.LOA_EXTENDED)); userDataById.put(userData.getUserId(), userData); } - - reservationDeviceByAccessToken = new HashMap<>(); - reservationDeviceById = new HashMap<>(); - - addReservationDevice(RESERVATION_DEVICE_CONFIG1); - addReservationDevice(RESERVATION_DEVICE_CONFIG2); } /** @@ -337,23 +327,8 @@ public void onRemoveGroupUser(String groupId, String userId) } @Override - public Optional getReservationDeviceById(String id) { - ReservationDeviceConfig device = reservationDeviceById.get(id); - - if (device == null) { - return Optional.empty(); - } - return Optional.of(device); - } - - @Override - public Optional getReservationDeviceByToken(String accessToken) { - ReservationDeviceConfig device = reservationDeviceByAccessToken.get(accessToken); - - if (device == null) { - return Optional.empty(); - } - return Optional.of(device); + public Collection listReservationDevices() { + return reservationDevices; } /** @@ -370,15 +345,14 @@ public static DummyAuthorization createInstance(ControllerConfiguration configur return authorization; } - public static void addReservationDevice(ReservationDeviceConfig reservationDeviceConfig) { + public void addReservationDevice(ReservationDeviceConfig reservationDeviceConfig) { String deviceId = reservationDeviceConfig.getDeviceId(); String accessToken = reservationDeviceConfig.getAccessToken(); UserData userData = reservationDeviceConfig.getUserData(); - reservationDeviceById.put(deviceId, reservationDeviceConfig); - reservationDeviceByAccessToken.put(accessToken, reservationDeviceConfig); - userDataById.put(deviceId, userData); userDataByAccessToken.put(accessToken, userData); + + reservationDevices.add(reservationDeviceConfig); } } diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java index 6b5e9b0b0..a71bfa112 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java @@ -1,12 +1,10 @@ package cz.cesnet.shongo.controller.api.rpc; import cz.cesnet.shongo.controller.AbstractControllerTest; -import cz.cesnet.shongo.controller.DummyAuthorization; import cz.cesnet.shongo.controller.ReservationRequestPurpose; import cz.cesnet.shongo.controller.ReservationRequestReusement; import cz.cesnet.shongo.controller.api.ReservationRequest; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; -import cz.cesnet.shongo.controller.api.Resource; import cz.cesnet.shongo.controller.api.ResourceSpecification; import cz.cesnet.shongo.controller.api.SecurityToken; import cz.cesnet.shongo.controller.api.request.ListResponse; @@ -25,8 +23,8 @@ public void shouldListReservationsForResourceManagedByDevice() throws Exception String reservationRequestId = createTestReservationRequest(resourceId); // Create reservation device that manages test resource. - ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "asd15asd19e1fe5wf9e51f", resourceId); - DummyAuthorization.addReservationDevice(deviceConfig); + ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "test", resourceId); + getAuthorization().addReservationDevice(deviceConfig); SecurityToken deviceToken = new SecurityToken(deviceConfig.getAccessToken()); deviceToken.setUserInformation(deviceToken.getUserInformation()); @@ -48,7 +46,10 @@ public void shouldNotListReservationsForResourceNotManagedByDevice() throws Exce createTestReservationRequest(resourceId); // Create security token for device that doesn't manage the test resource. - SecurityToken deviceToken = new SecurityToken(RESERVATION_DEVICE_CONFIG1.getAccessToken()); + String anotherResourceId = createTestResource(); + ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "test", anotherResourceId); + getAuthorization().addReservationDevice(deviceConfig); + SecurityToken deviceToken = new SecurityToken(deviceConfig.getAccessToken()); deviceToken.setUserInformation(deviceToken.getUserInformation()); // List resources with device's security token. @@ -59,13 +60,6 @@ public void shouldNotListReservationsForResourceNotManagedByDevice() throws Exce assertEquals(response.getCount(), 0); } - private String createTestResource() { - Resource resource = new Resource(); - resource.setName("resource"); - resource.setAllocatable(true); - return createResource(resource); - } - private String createTestReservationRequest(String resourceId) throws Exception { ReservationRequest reservationRequest = new ReservationRequest(); reservationRequest.setSlot("2012-01-01T00:00", "P1Y"); diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/authorization/AuthorizationTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/authorization/AuthorizationTest.java index 77b9b63ef..17c4bc8d4 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/authorization/AuthorizationTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/authorization/AuthorizationTest.java @@ -315,22 +315,31 @@ public void multipleAclRequestSpeedTest() throws Exception @Test public void shouldGetUserDataOfReservationDevice() { + String resourceId = createTestResource(); + ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "test", resourceId); + getAuthorization().addReservationDevice(deviceConfig); DummyAuthorization authorization = getAuthorization(); - assertEquals(authorization.getUserData(RESERVATION_DEVICE_CONFIG1.getDeviceId()), RESERVATION_DEVICE_CONFIG1.getUserData()); + assertEquals(authorization.getUserData(deviceConfig.getDeviceId()), deviceConfig.getUserData()); } @Test public void shouldGetUserInformationOfReservationDeviceById() { + String resourceId = createTestResource(); + ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "test", resourceId); + getAuthorization().addReservationDevice(deviceConfig); DummyAuthorization authorization = getAuthorization(); - assertEquals(authorization.getUserInformation(RESERVATION_DEVICE_CONFIG1.getDeviceId()), RESERVATION_DEVICE_CONFIG1.getUserData().getUserInformation()); + assertEquals(authorization.getUserInformation(deviceConfig.getDeviceId()), deviceConfig.getUserData().getUserInformation()); } @Test public void shouldGetUserInformationOfReservationDeviceByToken() { + String resourceId = createTestResource(); + ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "test", resourceId); + getAuthorization().addReservationDevice(deviceConfig); DummyAuthorization authorization = getAuthorization(); - UserInformation userInformation = RESERVATION_DEVICE_CONFIG1.getUserData().getUserInformation(); + UserInformation userInformation = deviceConfig.getUserData().getUserInformation(); - SecurityToken securityToken = new SecurityToken(RESERVATION_DEVICE_CONFIG1.getAccessToken()); + SecurityToken securityToken = new SecurityToken(deviceConfig.getAccessToken()); securityToken.setUserInformation(userInformation); assertEquals(authorization.getUserInformation(securityToken), userInformation); diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceControllerTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceControllerTest.java index 26eec58c8..b34e0aacb 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceControllerTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceControllerTest.java @@ -4,6 +4,7 @@ import cz.cesnet.shongo.controller.api.ReservationDevice; import cz.cesnet.shongo.controller.api.SecurityToken; import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; +import cz.cesnet.shongo.controller.authorization.ReservationDeviceConfig; import cz.cesnet.shongo.controller.rest.models.reservationdevice.ReservationDeviceModel; import org.junit.Test; import org.mockito.Mockito; @@ -17,11 +18,15 @@ public class ReservationDeviceControllerTest extends AbstractControllerTest { @Test public void shouldReturnDeviceInfo() { - SecurityToken deviceToken = new SecurityToken(RESERVATION_DEVICE_CONFIG1.getAccessToken()); + String resourceId = createTestResource(); + ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "test", resourceId); + getAuthorization().addReservationDevice(deviceConfig); + + SecurityToken deviceToken = new SecurityToken(deviceConfig.getAccessToken()); ReservationDevice device = new ReservationDevice(); - device.setId(RESERVATION_DEVICE_CONFIG1.getDeviceId()); - device.setAccessToken(RESERVATION_DEVICE_CONFIG1.getAccessToken()); - device.setResourceId(RESERVATION_DEVICE_CONFIG1.getResourceId()); + device.setId(deviceConfig.getDeviceId()); + device.setAccessToken(deviceConfig.getAccessToken()); + device.setResourceId(deviceConfig.getResourceId()); ReservationDeviceModel model = new ReservationDeviceModel(device); Mockito.when(authorizationService.getReservationDevice(deviceToken)).thenReturn(device); From a235c872a0d3c2b878d9b40e4ff168890db0b9e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Wed, 1 May 2024 17:44:53 +0200 Subject: [PATCH 127/134] test list user information with reservation devices --- .../authorization/AuthorizationTest.java | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/authorization/AuthorizationTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/authorization/AuthorizationTest.java index 17c4bc8d4..15caa218e 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/authorization/AuthorizationTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/authorization/AuthorizationTest.java @@ -10,19 +10,19 @@ import cz.cesnet.shongo.controller.api.request.ObjectPermissionListRequest; import cz.cesnet.shongo.controller.api.request.ReservationListRequest; import cz.cesnet.shongo.controller.api.rpc.AuthorizationService; -import cz.cesnet.shongo.controller.booking.datetime.*; -import cz.cesnet.shongo.controller.booking.datetime.PeriodicDateTimeSlot; import org.joda.time.DateTime; import org.joda.time.Interval; import org.junit.Assert; import org.junit.Test; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Tests for creating, updating and deleting {@link cz.cesnet.shongo.controller.api.AclEntry}s. @@ -315,28 +315,33 @@ public void multipleAclRequestSpeedTest() throws Exception @Test public void shouldGetUserDataOfReservationDevice() { + DummyAuthorization authorization = getAuthorization(); + String resourceId = createTestResource(); ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "test", resourceId); - getAuthorization().addReservationDevice(deviceConfig); - DummyAuthorization authorization = getAuthorization(); + authorization.addReservationDevice(deviceConfig); + assertEquals(authorization.getUserData(deviceConfig.getDeviceId()), deviceConfig.getUserData()); } @Test public void shouldGetUserInformationOfReservationDeviceById() { + DummyAuthorization authorization = getAuthorization(); + String resourceId = createTestResource(); ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "test", resourceId); - getAuthorization().addReservationDevice(deviceConfig); - DummyAuthorization authorization = getAuthorization(); + authorization.addReservationDevice(deviceConfig); + assertEquals(authorization.getUserInformation(deviceConfig.getDeviceId()), deviceConfig.getUserData().getUserInformation()); } @Test public void shouldGetUserInformationOfReservationDeviceByToken() { + DummyAuthorization authorization = getAuthorization(); + String resourceId = createTestResource(); ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "test", resourceId); - getAuthorization().addReservationDevice(deviceConfig); - DummyAuthorization authorization = getAuthorization(); + authorization.addReservationDevice(deviceConfig); UserInformation userInformation = deviceConfig.getUserData().getUserInformation(); SecurityToken securityToken = new SecurityToken(deviceConfig.getAccessToken()); @@ -345,6 +350,21 @@ public void shouldGetUserInformationOfReservationDeviceByToken() { assertEquals(authorization.getUserInformation(securityToken), userInformation); } + @Test + public void shouldListReservationDeviceUserInformation() { + DummyAuthorization authorization = getAuthorization(); + + String resourceId = createTestResource(); + ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "test", resourceId); + authorization.addReservationDevice(deviceConfig); + + Set reservationDevices = new HashSet<>(); + reservationDevices.add(deviceConfig.getDeviceId()); + + Collection userInformationResult = authorization.listUserInformation(reservationDevices, null); + assertTrue(userInformationResult.contains(deviceConfig.getUserData().getUserInformation())); + } + /** * @return collection of all {@link cz.cesnet.shongo.controller.api.AclEntry} for user with {@link #SECURITY_TOKEN} * @throws Exception From 9e5712424ba1d4a79b6cba2ce8a649203223d278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Wed, 1 May 2024 18:20:17 +0200 Subject: [PATCH 128/134] test create new reservation request --- .../api/rpc/ReservationServiceImplTest.java | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java index a71bfa112..f3ea813b2 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java @@ -13,6 +13,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; public class ReservationServiceImplTest extends AbstractControllerTest { @Test @@ -20,7 +21,7 @@ public void shouldListReservationsForResourceManagedByDevice() throws Exception ReservationService reservationService = getReservationService(); String resourceId = createTestResource(); - String reservationRequestId = createTestReservationRequest(resourceId); + String reservationRequestId = allocateTestReservationRequest(resourceId); // Create reservation device that manages test resource. ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "test", resourceId); @@ -43,7 +44,7 @@ public void shouldNotListReservationsForResourceNotManagedByDevice() throws Exce ReservationService reservationService = getReservationService(); String resourceId = createTestResource(); - createTestReservationRequest(resourceId); + allocateTestReservationRequest(resourceId); // Create security token for device that doesn't manage the test resource. String anotherResourceId = createTestResource(); @@ -60,12 +61,35 @@ public void shouldNotListReservationsForResourceNotManagedByDevice() throws Exce assertEquals(response.getCount(), 0); } - private String createTestReservationRequest(String resourceId) throws Exception { + @Test + public void shouldCreateReservationRequestForManagedResource() { + ReservationService reservationService = getReservationService(); + + String resourceId = createTestResource(); + + // Create reservation device that manages test resource. + ReservationDeviceConfig deviceConfig = new ReservationDeviceConfig("test", "test", resourceId); + getAuthorization().addReservationDevice(deviceConfig); + SecurityToken deviceToken = new SecurityToken(deviceConfig.getAccessToken()); + deviceToken.setUserInformation(deviceToken.getUserInformation()); + + ReservationRequest reservationRequest = createTestReservationRequest(resourceId); + + String reservationRequestId = reservationService.createReservationRequest(deviceToken, reservationRequest); + + assertNotNull(reservationRequestId); + } + + private String allocateTestReservationRequest(String resourceId) throws Exception { + return allocate(createTestReservationRequest(resourceId)); + } + + private ReservationRequest createTestReservationRequest(String resourceId) { ReservationRequest reservationRequest = new ReservationRequest(); reservationRequest.setSlot("2012-01-01T00:00", "P1Y"); reservationRequest.setSpecification(new ResourceSpecification(resourceId)); reservationRequest.setReusement(ReservationRequestReusement.OWNED); reservationRequest.setPurpose(ReservationRequestPurpose.USER); - return allocate(reservationRequest); + return reservationRequest; } } \ No newline at end of file From 153c3254c56f276ac9e3f02efd94ccd784dc1699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Wed, 1 May 2024 18:30:07 +0200 Subject: [PATCH 129/134] use early return in methods --- .../api/rpc/AuthorizationServiceImpl.java | 19 ++++++++++--------- .../ReservationDeviceController.java | 12 ++++++------ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/AuthorizationServiceImpl.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/AuthorizationServiceImpl.java index c88d592ab..35311fbab 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/AuthorizationServiceImpl.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/api/rpc/AuthorizationServiceImpl.java @@ -863,15 +863,16 @@ public List listReferencedUsers(SecurityToken securityToken) public ReservationDevice getReservationDevice(SecurityToken securityToken) { Optional deviceConfigOpt = authorization.getReservationDeviceByToken(securityToken.getAccessToken()); - if (deviceConfigOpt.isPresent()) { - ReservationDeviceConfig deviceConfig = deviceConfigOpt.get(); - ReservationDevice device = new ReservationDevice(); - device.setId(deviceConfig.getDeviceId()); - device.setAccessToken(deviceConfig.getAccessToken()); - device.setResourceId(deviceConfig.getResourceId()); - return device; - } - return null; + if (deviceConfigOpt.isEmpty()) { + return null; + } + + ReservationDeviceConfig deviceConfig = deviceConfigOpt.get(); + ReservationDevice device = new ReservationDevice(); + device.setId(deviceConfig.getDeviceId()); + device.setAccessToken(deviceConfig.getAccessToken()); + device.setResourceId(deviceConfig.getResourceId()); + return device; } /** diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceController.java index f6aa75811..db6009aca 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceController.java @@ -36,13 +36,13 @@ ResponseEntity getReservationDevice( ) { ReservationDevice device = authorizationService.getReservationDevice(securityToken); - if (device != null) { - ReservationDeviceModel model = new ReservationDeviceModel(device); - log.info("Get reservation device: {}", model); - return ResponseEntity.ok().body(model); + if (device == null) { + log.info("Device not found"); + return ResponseEntity.notFound().build(); } - log.info("Device not found"); - return ResponseEntity.notFound().build(); + ReservationDeviceModel model = new ReservationDeviceModel(device); + log.info("Get reservation device: {}", model); + return ResponseEntity.ok().body(model); } } From 280956d7728b2ed2e1997a731db1a98b24f285ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Wed, 1 May 2024 18:30:20 +0200 Subject: [PATCH 130/134] remove toString that is implemented by lombok data --- .../models/reservationdevice/ReservationDeviceModel.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationdevice/ReservationDeviceModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationdevice/ReservationDeviceModel.java index 99b79cbc4..b006689a1 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationdevice/ReservationDeviceModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationdevice/ReservationDeviceModel.java @@ -17,9 +17,4 @@ public ReservationDeviceModel(ReservationDevice reservationDevice) { this.id = reservationDevice.getId(); this.resourceId = reservationDevice.getResourceId(); } - - @Override - public String toString() { - return "ReservationDevice: (id=" + id + ", resourceId=" + resourceId + ")"; - } } From 4e6304c7193ecfa0226d702ba6e319cf57366878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Wed, 1 May 2024 18:33:01 +0200 Subject: [PATCH 131/134] add device user name and last name --- .../controller/authorization/ReservationDeviceConfig.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java index d1c2f3174..a07e423ed 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/authorization/ReservationDeviceConfig.java @@ -39,6 +39,8 @@ private UserData createUserData() { String name = "Reservation Device For " + resourceId; userInformation.setUserId(deviceId); + userInformation.setFirstName("Reservation"); + userInformation.setLastName("Device"); userInformation.setFullName(name); userData.setUserAuthorizationData(userAuthData); From 9d038a77e9d6bca17d636a26aeb078367f854888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Wed, 8 May 2024 18:21:21 +0200 Subject: [PATCH 132/134] fix pull request comments --- data/controller.properties | 4 -- data/controller.script | 44 ------------------- .../controller/AbstractControllerTest.java | 4 -- .../api/rpc/ReservationServiceImplTest.java | 2 +- .../ReservationDeviceControllerTest.java | 2 +- 5 files changed, 2 insertions(+), 54 deletions(-) delete mode 100644 data/controller.properties delete mode 100644 data/controller.script diff --git a/data/controller.properties b/data/controller.properties deleted file mode 100644 index 1618fd2bd..000000000 --- a/data/controller.properties +++ /dev/null @@ -1,4 +0,0 @@ -#HSQL Database Engine 2.3.0 -#Wed Mar 20 21:55:45 UTC 2024 -modified=no -version=2.3.0 diff --git a/data/controller.script b/data/controller.script deleted file mode 100644 index 1eadfa09e..000000000 --- a/data/controller.script +++ /dev/null @@ -1,44 +0,0 @@ -SET DATABASE UNIQUE NAME HSQLDB8E5DD0FA30 -SET DATABASE GC 0 -SET DATABASE DEFAULT RESULT MEMORY ROWS 0 -SET DATABASE EVENT LOG LEVEL 0 -SET DATABASE SQL NAMES FALSE -SET DATABASE SQL REFERENCES FALSE -SET DATABASE SQL SIZE TRUE -SET DATABASE SQL TYPES FALSE -SET DATABASE SQL TDC DELETE TRUE -SET DATABASE SQL TDC UPDATE TRUE -SET DATABASE SQL TRANSLATE TTI TYPES TRUE -SET DATABASE SQL CONCAT NULLS TRUE -SET DATABASE SQL UNIQUE NULLS TRUE -SET DATABASE SQL CONVERT TRUNCATE TRUE -SET DATABASE SQL AVG SCALE 0 -SET DATABASE SQL DOUBLE NAN TRUE -SET DATABASE TRANSACTION CONTROL LOCKS -SET DATABASE DEFAULT ISOLATION LEVEL READ COMMITTED -SET DATABASE TRANSACTION ROLLBACK ON CONFLICT TRUE -SET DATABASE TEXT TABLE DEFAULTS '' -SET FILES WRITE DELAY 0 -SET FILES BACKUP INCREMENT TRUE -SET FILES CACHE SIZE 10000 -SET FILES CACHE ROWS 50000 -SET FILES SCALE 32 -SET FILES LOB SCALE 32 -SET FILES DEFRAG 0 -SET FILES NIO TRUE -SET FILES NIO SIZE 256 -SET FILES LOG TRUE -SET FILES LOG SIZE 50 -CREATE USER SA PASSWORD DIGEST 'd41d8cd98f00b204e9800998ecf8427e' -ALTER USER SA SET LOCAL TRUE -CREATE SCHEMA PUBLIC AUTHORIZATION DBA -ALTER SEQUENCE SYSTEM_LOBS.LOB_ID RESTART WITH 1 -SET DATABASE DEFAULT INITIAL SCHEMA PUBLIC -GRANT USAGE ON DOMAIN INFORMATION_SCHEMA.SQL_IDENTIFIER TO PUBLIC -GRANT USAGE ON DOMAIN INFORMATION_SCHEMA.YES_OR_NO TO PUBLIC -GRANT USAGE ON DOMAIN INFORMATION_SCHEMA.TIME_STAMP TO PUBLIC -GRANT USAGE ON DOMAIN INFORMATION_SCHEMA.CARDINAL_NUMBER TO PUBLIC -GRANT USAGE ON DOMAIN INFORMATION_SCHEMA.CHARACTER_DATA TO PUBLIC -GRANT DBA TO SA -SET SCHEMA SYSTEM_LOBS -INSERT INTO BLOCKS VALUES(0,2147483647,0) diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java index a7d5facec..9d50ed7f8 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/AbstractControllerTest.java @@ -9,13 +9,11 @@ import cz.cesnet.shongo.controller.api.request.ReservationRequestListRequest; import cz.cesnet.shongo.controller.api.rpc.*; import cz.cesnet.shongo.controller.authorization.Authorization; -import cz.cesnet.shongo.controller.authorization.ReservationDeviceConfig; import cz.cesnet.shongo.controller.cache.Cache; import cz.cesnet.shongo.controller.domains.InterDomainAgent; import cz.cesnet.shongo.controller.notification.NotificationManager; import cz.cesnet.shongo.controller.scheduler.Preprocessor; import cz.cesnet.shongo.controller.scheduler.Scheduler; -import cz.cesnet.shongo.controller.util.DatabaseHelper; import cz.cesnet.shongo.controller.util.NativeQuery; import cz.cesnet.shongo.jade.Container; import org.joda.time.DateTime; @@ -25,8 +23,6 @@ import org.slf4j.LoggerFactory; import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.Query; import java.util.*; /** diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java index f3ea813b2..aae4eb2c5 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/api/rpc/ReservationServiceImplTest.java @@ -92,4 +92,4 @@ private ReservationRequest createTestReservationRequest(String resourceId) { reservationRequest.setPurpose(ReservationRequestPurpose.USER); return reservationRequest; } -} \ No newline at end of file +} diff --git a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceControllerTest.java b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceControllerTest.java index b34e0aacb..c111b8c32 100644 --- a/shongo-controller/src/test/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceControllerTest.java +++ b/shongo-controller/src/test/java/cz/cesnet/shongo/controller/rest/controllers/ReservationDeviceControllerTest.java @@ -39,4 +39,4 @@ public void shouldReturnNotFound() { Mockito.when(authorizationService.getReservationDevice(Mockito.any())).thenReturn(null); assertEquals(ResponseEntity.notFound().build(), controller.getReservationDevice(new SecurityToken("test"))); } -} \ No newline at end of file +} From cf89401a960b325479c11dd6756524bee6de1005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Drob=C5=88=C3=A1k?= Date: Wed, 8 May 2024 18:21:58 +0200 Subject: [PATCH 133/134] ignore data folder --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8c01860ae..c6b5a249d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,6 @@ target/ *.fsps *.class .gitattributes +data/ shongo-deployment/**/*.cfg - From 30a7699d7e6a5732a502195714ff6a03ac5b5073 Mon Sep 17 00:00:00 2001 From: Filip Karnis Date: Wed, 24 Apr 2024 19:49:28 +0200 Subject: [PATCH 134/134] add allocationStateReport to ReservationRequestDetailModel --- .../rest/controllers/ReservationRequestController.java | 6 ++++-- .../ReservationRequestDetailModel.java | 9 +++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java index a29ddfe1e..cb2f0cde6 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/controllers/ReservationRequestController.java @@ -331,9 +331,11 @@ ReservationRequestDetailModel getRequest( ); } + AbstractReservationRequest abstractReservationRequest = reservationService.getReservationRequest(securityToken, id); + ReservationRequestDetailModel detailModel = new ReservationRequestDetailModel( - summary, virtualRoomSummary, permissionsByReservationRequestId, ownerInformation, authorizedData, history, - resourceSummary + summary, virtualRoomSummary, permissionsByReservationRequestId, ownerInformation, + abstractReservationRequest, authorizedData, history, resourceSummary ); detailModel.getRoomCapacityData().setIsRecordingActive(isRecordingActive); return detailModel; diff --git a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java index 3a3887823..a7ade477c 100644 --- a/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java +++ b/shongo-controller/src/main/java/cz/cesnet/shongo/controller/rest/models/reservationrequest/ReservationRequestDetailModel.java @@ -2,14 +2,17 @@ import cz.cesnet.shongo.api.UserInformation; import cz.cesnet.shongo.controller.ObjectPermission; +import cz.cesnet.shongo.controller.api.AbstractReservationRequest; import cz.cesnet.shongo.controller.api.AllocationState; import cz.cesnet.shongo.controller.api.ExecutableState; +import cz.cesnet.shongo.controller.api.ReservationRequest; import cz.cesnet.shongo.controller.api.ReservationRequestSummary; import cz.cesnet.shongo.controller.api.ResourceSummary; import cz.cesnet.shongo.controller.rest.models.room.RoomAuthorizedData; import lombok.Data; import lombok.EqualsAndHashCode; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -27,6 +30,7 @@ public class ReservationRequestDetailModel extends ReservationRequestModel { private AllocationState allocationState; + private Collection> allocationStateReport; private ExecutableState executableState; private Boolean notifyParticipants; private RoomAuthorizedData authorizedData; @@ -37,6 +41,7 @@ public ReservationRequestDetailModel( ReservationRequestSummary virtualRoomSummary, Map> permissionsByReservationRequestId, UserInformation ownerInformation, + AbstractReservationRequest abstractReservationRequest, RoomAuthorizedData authorizedData, List history, ResourceSummary resourceSummary) @@ -47,5 +52,9 @@ public ReservationRequestDetailModel( this.executableState = summary.getExecutableState(); this.authorizedData = authorizedData; this.history = history; + if (abstractReservationRequest instanceof ReservationRequest) { + ReservationRequest reservationRequest = (ReservationRequest) abstractReservationRequest; + this.allocationStateReport = reservationRequest.getAllocationStateReport().getReports(); + } } }