From 05c2e545aea57408824cc9cff7d940cabdc8b304 Mon Sep 17 00:00:00 2001 From: Paulo Gustavo Veiga Date: Wed, 7 Feb 2024 18:44:33 -0800 Subject: [PATCH] Add map metadata. --- wise-api/pom.xml | 3 +- .../java/com/wisemapping/Application.java | 1 - .../config/mvc/InterceptorsConfig.java | 43 -------- .../wisemapping/config/mvc/MvcAppConfig.java | 49 --------- .../config/mvc/MvcSecurityConfig.java | 100 ------------------ .../config/rest/RestAppConfig.java | 2 +- .../wisemapping/rest/MindmapController.java | 39 ++++--- .../rest/model/RestMindmapMetadata.java | 51 +++++++-- .../com/wisemapping/view/MindMapBean.java | 4 - .../src/main/resources/public/favicon.ico | Bin 1150 -> 0 bytes .../src/main/resources/public/favicon.png | Bin 34875 -> 0 bytes wise-api/src/main/resources/public/index.html | 6 -- .../test/rest/RestMindmapControllerTest.java | 86 +++++++++------ 13 files changed, 124 insertions(+), 260 deletions(-) delete mode 100644 wise-api/src/main/java/com/wisemapping/config/mvc/InterceptorsConfig.java delete mode 100644 wise-api/src/main/java/com/wisemapping/config/mvc/MvcAppConfig.java delete mode 100644 wise-api/src/main/java/com/wisemapping/config/mvc/MvcSecurityConfig.java delete mode 100644 wise-api/src/main/resources/public/favicon.ico delete mode 100644 wise-api/src/main/resources/public/favicon.png delete mode 100644 wise-api/src/main/resources/public/index.html diff --git a/wise-api/pom.xml b/wise-api/pom.xml index aecf4ca6..30c6510b 100644 --- a/wise-api/pom.xml +++ b/wise-api/pom.xml @@ -4,7 +4,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.1 + 3.2.2 org.wisemapping @@ -199,7 +199,6 @@ - org.apache.maven.plugins diff --git a/wise-api/src/main/java/com/wisemapping/Application.java b/wise-api/src/main/java/com/wisemapping/Application.java index 5ac5f722..5283b2f5 100644 --- a/wise-api/src/main/java/com/wisemapping/Application.java +++ b/wise-api/src/main/java/com/wisemapping/Application.java @@ -1,7 +1,6 @@ package com.wisemapping; import com.wisemapping.config.common.CommonConfig; -import com.wisemapping.config.mvc.MvcAppConfig; import com.wisemapping.config.rest.RestAppConfig; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; diff --git a/wise-api/src/main/java/com/wisemapping/config/mvc/InterceptorsConfig.java b/wise-api/src/main/java/com/wisemapping/config/mvc/InterceptorsConfig.java deleted file mode 100644 index 400a68e5..00000000 --- a/wise-api/src/main/java/com/wisemapping/config/mvc/InterceptorsConfig.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright [2022] [wisemapping] - * - * Licensed under WiseMapping Public License, Version 1.0 (the "License"). - * It is basically the Apache License, Version 2.0 (the "License") plus the - * "powered by wisemapping" text requirement on every single page; - * you may not use this file except in compliance with the License. - * You may obtain a copy of the license at - * - * http://www.wisemapping.org/license - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.wisemapping.config.mvc; - -import com.wisemapping.filter.RequestPropertiesInterceptor; -import com.wisemapping.filter.UserLocaleInterceptor; -import org.jetbrains.annotations.NotNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -//@Configuration -//@ComponentScan(basePackageClasses = UserLocaleInterceptor.class) -public class InterceptorsConfig implements WebMvcConfigurer { - @Autowired - private UserLocaleInterceptor userLocaleInterceptor; - - @Autowired - private RequestPropertiesInterceptor requestPropertiesInterceptor; - - @Override - public void addInterceptors(@NotNull final InterceptorRegistry registry) { - registry.addInterceptor(userLocaleInterceptor); - registry.addInterceptor(requestPropertiesInterceptor); - } -} \ No newline at end of file diff --git a/wise-api/src/main/java/com/wisemapping/config/mvc/MvcAppConfig.java b/wise-api/src/main/java/com/wisemapping/config/mvc/MvcAppConfig.java deleted file mode 100644 index c17f0b51..00000000 --- a/wise-api/src/main/java/com/wisemapping/config/mvc/MvcAppConfig.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.wisemapping.config.mvc; - -import com.wisemapping.webmvc.MvcMindmapController; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; -import org.springframework.web.servlet.HandlerExceptionResolver; -import org.springframework.web.servlet.ViewResolver; -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.handler.SimpleMappingExceptionResolver; -import org.springframework.web.servlet.view.InternalResourceViewResolver; -import org.springframework.web.servlet.view.JstlView; - - -//@SpringBootApplication -//@Import({MvcMindmapController.class, MvcSecurityConfig.class}) -//@EnableWebMvc -public class MvcAppConfig implements WebMvcConfigurer { - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry - .addResourceHandler("/**") - .addResourceLocations("classpath:/public/"); - } - - @Bean - public ViewResolver viewResolver() { - InternalResourceViewResolver resolver = new InternalResourceViewResolver(); - resolver.setPrefix("/WEB-INF/jsp/"); - resolver.setSuffix(".jsp"); - resolver.setViewClass(JstlView.class); - return resolver; - } - - @Bean - HandlerExceptionResolver errorHandler() { - final SimpleMappingExceptionResolver result = new SimpleMappingExceptionResolver(); - - //mapping status code with view response. - result.addStatusCode("reactInclude", 403); - - //setting default error view - result.setDefaultErrorView("reactInclude"); - result.setDefaultStatusCode(500); - return result; - } -} \ No newline at end of file diff --git a/wise-api/src/main/java/com/wisemapping/config/mvc/MvcSecurityConfig.java b/wise-api/src/main/java/com/wisemapping/config/mvc/MvcSecurityConfig.java deleted file mode 100644 index cb236793..00000000 --- a/wise-api/src/main/java/com/wisemapping/config/mvc/MvcSecurityConfig.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.wisemapping.config.mvc; - -import org.jetbrains.annotations.NotNull; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.Order; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; -import org.springframework.web.servlet.handler.HandlerMappingIntrospector; - -@Configuration -@EnableWebSecurity -public class MvcSecurityConfig { - @Bean - @Order(1) - public SecurityFilterChain embeddedDisabledXOrigin(@NotNull final HttpSecurity http, @NotNull final MvcRequestMatcher.Builder mvc) throws Exception { - http - .securityMatchers((matchers) -> - matchers.requestMatchers(mvc.pattern("/c/maps/*/embed"))) - .authorizeHttpRequests( - (auth) -> auth.requestMatchers(mvc.pattern(("/c/maps/*/embed"))).permitAll()) - .headers((header -> header.frameOptions() - .disable() - )) - .csrf(AbstractHttpConfigurer::disable); - - return http.build(); - } - - @Bean - MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) { - return new MvcRequestMatcher.Builder(introspector); - } - - @Bean - @Order(2) - public SecurityFilterChain mvcFilterChain(@NotNull final HttpSecurity http, @NotNull final MvcRequestMatcher.Builder mvc) throws Exception { - http - .securityMatchers((matchers) -> - matchers.requestMatchers(mvc.pattern("/c/**"))) - .authorizeHttpRequests( - (auth) -> - auth - .requestMatchers(mvc.pattern("/c/login")).permitAll() - .requestMatchers(mvc.pattern("/c/logout")).permitAll() - .requestMatchers(mvc.pattern("/c/registration")).permitAll() - .requestMatchers(mvc.pattern("/c/registration-success")).permitAll() - .requestMatchers(mvc.pattern("/c/registration-google")).permitAll() - - .requestMatchers(mvc.pattern("/c/forgot-password")).permitAll() - .requestMatchers(mvc.pattern("/c/forgot-password-success")).permitAll() - .requestMatchers(mvc.pattern("/c/maps/*/try")).permitAll() - .requestMatchers(mvc.pattern("/c/maps/*/public")).permitAll() - .requestMatchers(mvc.pattern("/c/**")).hasAnyRole("USER", "ADMIN") - .anyRequest().authenticated()) - .formLogin((loginForm) -> - loginForm.loginPage("/c/login") - .loginProcessingUrl("/c/perform-login") - .defaultSuccessUrl("/c/maps/") - .failureUrl("/c/login?login_error=2")) - .logout((logout) -> - logout - .logoutUrl("/c/logout") - .logoutSuccessUrl("/c/login") - .invalidateHttpSession(true) - .deleteCookies("JSESSIONID") - .permitAll() - ).rememberMe(remember -> - remember - .tokenValiditySeconds(2419200) - .rememberMeParameter("remember-me" - ) - ).headers((header -> header.frameOptions() - .disable() - )) - .csrf((csrf) -> - csrf.ignoringRequestMatchers(mvc.pattern("/c/logout"))); - - return http.build(); - } - - @Bean - @Order(3) - public SecurityFilterChain shareResourcesFilterChain(@NotNull final HttpSecurity http, @NotNull final MvcRequestMatcher.Builder mvc) throws Exception { - return http.authorizeHttpRequests( - (auth) -> - auth.requestMatchers(mvc.pattern("/static/**")).permitAll(). - requestMatchers(mvc.pattern("/css/**")).permitAll(). - requestMatchers(mvc.pattern("/js/**")).permitAll(). - // @todo: Why this is required ... - requestMatchers(mvc.pattern("/WEB-INF/jsp/*.jsp")).permitAll(). - requestMatchers(mvc.pattern("/images/**")).permitAll(). - requestMatchers(mvc.pattern("/*")).permitAll() - - ).build(); - } -} diff --git a/wise-api/src/main/java/com/wisemapping/config/rest/RestAppConfig.java b/wise-api/src/main/java/com/wisemapping/config/rest/RestAppConfig.java index 7ee683a9..65546bd1 100644 --- a/wise-api/src/main/java/com/wisemapping/config/rest/RestAppConfig.java +++ b/wise-api/src/main/java/com/wisemapping/config/rest/RestAppConfig.java @@ -54,7 +54,7 @@ public class RestAppConfig { })) .csrf(AbstractHttpConfigurer::disable) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) -// .httpBasic(withDefaults()) + .httpBasic(withDefaults()) .build(); } } diff --git a/wise-api/src/main/java/com/wisemapping/rest/MindmapController.java b/wise-api/src/main/java/com/wisemapping/rest/MindmapController.java index 526c5fcb..b1d1df7d 100644 --- a/wise-api/src/main/java/com/wisemapping/rest/MindmapController.java +++ b/wise-api/src/main/java/com/wisemapping/rest/MindmapController.java @@ -24,6 +24,7 @@ import com.wisemapping.rest.model.*; import com.wisemapping.security.Utils; import com.wisemapping.service.*; import com.wisemapping.validator.MapInfoValidator; +import com.wisemapping.view.MindMapBean; import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.validator.routines.EmailValidator; import org.apache.logging.log4j.LogManager; @@ -32,6 +33,7 @@ import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -72,7 +74,7 @@ public class MindmapController extends BaseController { @RequestMapping(method = RequestMethod.GET, value = "/{id}", produces = {"application/json"}) @ResponseBody public RestMindmap retrieve(@PathVariable int id) throws WiseMappingException { - final User user = Utils.getUser(); + final User user = Utils.getUser(true); final Mindmap mindMap = findMindmapById(id); return new RestMindmap(mindMap, user); } @@ -80,17 +82,28 @@ public class MindmapController extends BaseController { @PreAuthorize("isAuthenticated() and hasRole('ROLE_USER')") @RequestMapping(method = RequestMethod.GET, value = "/{id}/metadata", produces = {"application/json"}) @ResponseBody - public RestMindmap retrieveMetadata(@PathVariable int id) throws WiseMappingException { - final User user = Utils.getUser(); - - final Mindmap mindMap = findMindmapById(id); - return new RestMindmap(mindMap, user); + public RestMindmapMetadata retrieveMetadata(@PathVariable int id) throws WiseMappingException { + final User user = Utils.getUser(true); + final Mindmap mindmap = findMindmapById(id); + final MindMapBean mindMapBean = new MindMapBean(mindmap, user); + + // Is the mindmap locked ?. + boolean isLocked = false; + final LockManager lockManager = this.mindmapService.getLockManager(); + String lockFullName = null; + if (lockManager.isLocked(mindmap) && !lockManager.isLockedBy(mindmap, user)) { + final LockInfo lockInfo = lockManager.getLockInfo(mindmap); + isLocked = true; + lockFullName = lockInfo.getUser().getFullName(); + } + + return new RestMindmapMetadata(mindmap.getTitle(), mindMapBean.getProperties(), isLocked, lockFullName); } @PreAuthorize("isAuthenticated() and hasRole('ROLE_USER')") @RequestMapping(method = RequestMethod.GET, value = "/", produces = {"application/json"}) public RestMindmapList retrieveList(@RequestParam(required = false) String q) { - final User user = Utils.getUser(); + final User user = Utils.getUser(true); final MindmapFilter filter = MindmapFilter.parse(q); List mindmaps = mindmapService.findMindmapsByUser(user); @@ -119,7 +132,7 @@ public class MindmapController extends BaseController { public void updateDocument(@RequestBody RestMindmap restMindmap, @PathVariable int id, @RequestParam(required = false) boolean minor) throws WiseMappingException, IOException { final Mindmap mindmap = findMindmapById(id); - final User user = Utils.getUser(); + final User user = Utils.getUser(true); // Validate arguments ... final String properties = restMindmap.getProperties(); @@ -148,7 +161,7 @@ public class MindmapController extends BaseController { @ResponseStatus(value = HttpStatus.NO_CONTENT) public void updateRevertMindmap(@PathVariable int id, @PathVariable String hid) throws WiseMappingException, IOException { final Mindmap mindmap = findMindmapById(id); - final User user = Utils.getUser(); + final User user = Utils.getUser(true); if (LATEST_HISTORY_REVISION.equals(hid)) { // Revert to the latest stored version ... @@ -178,7 +191,7 @@ public class MindmapController extends BaseController { @ResponseBody public void updateDocument(@PathVariable int id, @RequestBody String xmlDoc) throws WiseMappingException { final Mindmap mindmap = findMindmapById(id); - final User user = Utils.getUser(); + final User user = Utils.getUser(true); mindmap.setXmlStr(xmlDoc); saveMindmapDocument(false, mindmap, user); @@ -203,7 +216,7 @@ public class MindmapController extends BaseController { public void updateProperties(@RequestBody RestMindmap restMindmap, @PathVariable int id, @RequestParam(required = false) boolean minor) throws IOException, WiseMappingException { final Mindmap mindmap = findMindmapById(id); - final User user = Utils.getUser(); + final User user = Utils.getUser(true); final String xml = restMindmap.getXml(); if (xml != null && !xml.isEmpty()) { @@ -239,7 +252,7 @@ public class MindmapController extends BaseController { @NotNull private Mindmap findMindmapById(int id) throws MapCouldNotFoundException, AccessDeniedSecurityException { // Has enough permissions ? - final User user = Utils.getUser(); + final User user = Utils.getUser(true); if (!mindmapService.hasPermissions(user, id, CollaborationRole.VIEWER)) { throw new AccessDeniedSecurityException(id, user); } @@ -258,7 +271,7 @@ public class MindmapController extends BaseController { public void updateTitle(@RequestBody String title, @PathVariable int id) throws WiseMappingException { final Mindmap mindMap = findMindmapById(id); - final User user = Utils.getUser(); + final User user = Utils.getUser(true); // Is there a map with the same name ? if (mindmapService.getMindmapByTitle(title, user) != null) { diff --git a/wise-api/src/main/java/com/wisemapping/rest/model/RestMindmapMetadata.java b/wise-api/src/main/java/com/wisemapping/rest/model/RestMindmapMetadata.java index 63158ce3..050cf876 100644 --- a/wise-api/src/main/java/com/wisemapping/rest/model/RestMindmapMetadata.java +++ b/wise-api/src/main/java/com/wisemapping/rest/model/RestMindmapMetadata.java @@ -20,18 +20,10 @@ package com.wisemapping.rest.model; import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.wisemapping.exceptions.InvalidMindmapException; -import com.wisemapping.exceptions.WiseMappingException; -import com.wisemapping.model.*; -import com.wisemapping.util.TimeUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.IOException; -import java.util.Calendar; - @JsonAutoDetect( fieldVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, @@ -40,8 +32,49 @@ import java.util.Calendar; ) @JsonIgnoreProperties(ignoreUnknown = true) public class RestMindmapMetadata { + private String jsonProps; + private boolean locked; + private String title; - public RestMindmapMetadata() throws WiseMappingException { + public String getJsonProps() { + return jsonProps; + } + + public void setJsonProps(String jsonProps) { + this.jsonProps = jsonProps; + } + + public boolean isLocked() { + return locked; + } + + public void setLocked(boolean locked) { + this.locked = locked; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getLockFullName() { + return lockFullName; + } + + public void setLockFullName(String lockFullName) { + this.lockFullName = lockFullName; + } + + private String lockFullName; + + public RestMindmapMetadata(@NotNull String title, @NotNull String jsonProps, boolean locked, @Nullable String lockFullName) { + this.jsonProps = jsonProps; + this.title = title; + this.locked = locked; + this.lockFullName = lockFullName; } } diff --git a/wise-api/src/main/java/com/wisemapping/view/MindMapBean.java b/wise-api/src/main/java/com/wisemapping/view/MindMapBean.java index 4b806f3a..b62da707 100644 --- a/wise-api/src/main/java/com/wisemapping/view/MindMapBean.java +++ b/wise-api/src/main/java/com/wisemapping/view/MindMapBean.java @@ -127,10 +127,6 @@ public class MindMapBean { mindmap.setDescription(d); } - public String getXmlAsJsLiteral() throws IOException { - return this.mindmap.getXmlAsJsLiteral(); - } - public String getProperties() throws WiseMappingException { String result = null; diff --git a/wise-api/src/main/resources/public/favicon.ico b/wise-api/src/main/resources/public/favicon.ico deleted file mode 100644 index 955c5116c3c50060440fc49510674f5e6e564ac3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcmcIjT}V@57=Cm%olBdQGn-3|qz0A6D5Q)s?H94NmBdCZ%FJ}IH6aYM)Q!a#)36cTlNd(YVoVNIwa?YXtQ- zhLF0joCTzRAtJ#%X#rLj+(mu!B>KJ+dM5~$E8mczZeo5m;^TiOBSntpG}jKJ<0GLz z%%BKfOc9zM6LuS)At_l4^@>=HEWVUP*8&<0Sgo|K+(alfGmg@l14c2Xw{h-}Vd}VkN5HHr_Akp!ck>zK zjQNlx?1j<(1>QlzlQDk(Q9}EBMlkAgtnm>cztF?=Dn_cvV-sC*NJ}q+p=tn)4@2+` z5qid%8`Y_A9*4H*7T2mFO`7Gz?x|^lp9Q7r1U3}6prqV~Z8|SBS|?=k63)qD34ip2 pK@(<{|Ju1*JY5K1h+r}s7+e5!Co&{}BktF}LT;36W|$*ze*j~O6XO5? diff --git a/wise-api/src/main/resources/public/favicon.png b/wise-api/src/main/resources/public/favicon.png deleted file mode 100644 index bb62808d994add45030bb41a6891c31eda51e96a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34875 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⋙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 MG&LyDyZZ3|1Le=gy#N3J diff --git a/wise-api/src/main/resources/public/index.html b/wise-api/src/main/resources/public/index.html deleted file mode 100644 index 02dcf806..00000000 --- a/wise-api/src/main/resources/public/index.html +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/wise-api/src/test/java/com/wisemapping/test/rest/RestMindmapControllerTest.java b/wise-api/src/test/java/com/wisemapping/test/rest/RestMindmapControllerTest.java index 078e2928..6e01a87a 100644 --- a/wise-api/src/test/java/com/wisemapping/test/rest/RestMindmapControllerTest.java +++ b/wise-api/src/test/java/com/wisemapping/test/rest/RestMindmapControllerTest.java @@ -22,6 +22,7 @@ import org.springframework.web.util.DefaultUriBuilderFactory; import java.io.IOException; import java.net.URI; +import java.net.URISyntaxException; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -35,7 +36,7 @@ public class RestMindmapControllerTest { private RestUser user; - @Autowired + @Autowired private TestRestTemplate restTemplate; @BeforeEach @@ -52,7 +53,7 @@ public class RestMindmapControllerTest { } @Test - public void listMaps() { + public void listMaps() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -81,7 +82,7 @@ public class RestMindmapControllerTest { } @Test - public void deleteMap() { + public void deleteMap() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -103,7 +104,7 @@ public class RestMindmapControllerTest { } @Test - public void changeMapTitle() { + public void changeMapTitle() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -117,7 +118,7 @@ public class RestMindmapControllerTest { } @Test - public void validateMapsCreation() { // Configure media types ... + public void validateMapsCreation() throws URISyntaxException { // Configure media types ... final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); requestHeaders.set(HttpHeaders.ACCEPT_LANGUAGE, "en"); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -135,7 +136,7 @@ public class RestMindmapControllerTest { @Test - public void changeMapDescription() { + public void changeMapDescription() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -155,7 +156,7 @@ public class RestMindmapControllerTest { @Test - public void updateMapXml() throws IOException { // Configure media types ... + public void updateMapXml() throws IOException, URISyntaxException { // Configure media types ... final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -183,7 +184,7 @@ public class RestMindmapControllerTest { @Test - public void cloneMap() throws IOException { + public void cloneMap() throws IOException, URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -211,7 +212,7 @@ public class RestMindmapControllerTest { @Test - public void updateStarred() { // Configure media types ... + public void updateStarred() throws URISyntaxException { // Configure media types ... final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -238,7 +239,7 @@ public class RestMindmapControllerTest { @Test - public void verifyMapOwnership() { + public void verifyMapOwnership() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate firstUser = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -266,7 +267,7 @@ public class RestMindmapControllerTest { } @Test - public void updateMap() throws IOException, WiseMappingException { + public void updateMap() throws IOException, WiseMappingException, URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -303,7 +304,7 @@ public class RestMindmapControllerTest { @Test - public void addCollabs() { + public void addCollabs() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -324,7 +325,7 @@ public class RestMindmapControllerTest { } @Test - public void updateCollabType() { + public void updateCollabType() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -361,7 +362,7 @@ public class RestMindmapControllerTest { @Test - public void deleteCollabs() { + public void deleteCollabs() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -400,7 +401,7 @@ public class RestMindmapControllerTest { @Test - public void deleteCollabsWithInvalidEmail() { + public void deleteCollabsWithInvalidEmail() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -418,7 +419,7 @@ public class RestMindmapControllerTest { } @Test - public void deleteCollabsWithoutOwnerPermission() { + public void deleteCollabsWithoutOwnerPermission() throws URISyntaxException { final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); final URI resourceUri = addNewMap(restTemplate, "deleteWithoutOwnerPermission"); @@ -439,7 +440,7 @@ public class RestMindmapControllerTest { } @Test - public void deleteOwnerCollab() { + public void deleteOwnerCollab() throws URISyntaxException { final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); // Create a sample map ... @@ -458,7 +459,7 @@ public class RestMindmapControllerTest { } @Test - public void addCollabsInvalidOwner() { + public void addCollabsInvalidOwner() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -479,7 +480,7 @@ public class RestMindmapControllerTest { } @Test - public void removeLabelFromMindmap() { // Configure media types ... + public void removeLabelFromMindmap() throws URISyntaxException { // Configure media types ... final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -524,7 +525,7 @@ public class RestMindmapControllerTest { } @Test - public void addLabelToMindmap() { // Configure media types ... + public void addLabelToMindmap() throws URISyntaxException { // Configure media types ... final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -548,7 +549,23 @@ public class RestMindmapControllerTest { } @Test - public void updateCollabs() { + public void fetchMapMetadata() throws URISyntaxException { + final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); + final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); + + // Create a sample map ... + final String mapTitle = "Maps 1 !"; + final URI mindmapUri = addNewMap(restTemplate, mapTitle); + final String mapId = mindmapUri.getPath().replace("/api/restful/maps/", ""); + + final ResponseEntity exchange = restTemplate.exchange(mindmapUri + "/metadata", HttpMethod.GET, null, RestMindmapMetadata.class); + assertTrue(exchange.getStatusCode().is2xxSuccessful()); + assertEquals(mapTitle, exchange.getBody().getTitle()); + + } + + @Test + public void updateCollabs() throws URISyntaxException { // Create a sample map ... final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); @@ -592,7 +609,7 @@ public class RestMindmapControllerTest { @Test - public void updateProperties() throws IOException, WiseMappingException { + public void updateProperties() throws IOException, WiseMappingException, URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -623,7 +640,7 @@ public class RestMindmapControllerTest { @Test - public void batchDelete() { + public void batchDelete() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -646,7 +663,7 @@ public class RestMindmapControllerTest { @Test - public void updatePublishState() { + public void updatePublishState() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -663,7 +680,7 @@ public class RestMindmapControllerTest { } @Test - public void fetchMapHistory() { + public void fetchMapHistory() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -681,7 +698,7 @@ public class RestMindmapControllerTest { @Test - public void updateRevertMindmap() throws IOException { + public void updateRevertMindmap() throws IOException, URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -709,7 +726,7 @@ public class RestMindmapControllerTest { @Test - public void addCollabWhitoutOwnerPermission() { + public void addCollabWhitoutOwnerPermission() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -736,7 +753,7 @@ public class RestMindmapControllerTest { } @Test - public void addCollabWhitOwnerRole() { + public void addCollabWhitOwnerRole() throws URISyntaxException { final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final TestRestTemplate restTemplate = this.restTemplate.withBasicAuth(user.getEmail(), user.getPassword()); @@ -800,14 +817,19 @@ public class RestMindmapControllerTest { } // - private URI addNewMap(@NotNull TestRestTemplate template, @NotNull String title, @Nullable String xml) { + private URI addNewMap(@NotNull TestRestTemplate template, @NotNull String title, @Nullable String xml) throws URISyntaxException { // Create a new map ... final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_XML); - HttpEntity createUserEntity = new HttpEntity<>(xml, requestHeaders); - return template.postForLocation("/api/restful/maps?title=" + title, createUserEntity); + final HttpEntity createUserEntity = new HttpEntity<>(xml, requestHeaders); + + final ResponseEntity exchange = template.exchange("/api/restful/maps?title=" + title, HttpMethod.POST, createUserEntity, String.class); + assertTrue(exchange.getStatusCode().is2xxSuccessful()); + + final List locations = exchange.getHeaders().get(HttpHeaders.LOCATION); + return new URI(locations.stream().findFirst().get()); } - private URI addNewMap(@NotNull TestRestTemplate template, @NotNull String title) { + private URI addNewMap(@NotNull TestRestTemplate template, @NotNull String title) throws URISyntaxException { return addNewMap(template, title, null); }