diff --git a/geonetwork-api/src/main/java/au/org/aodn/geonetwork_api/openapi/api/helper/LogosHelper.java b/geonetwork-api/src/main/java/au/org/aodn/geonetwork_api/openapi/api/helper/LogosHelper.java index 14c0bf6..9495801 100644 --- a/geonetwork-api/src/main/java/au/org/aodn/geonetwork_api/openapi/api/helper/LogosHelper.java +++ b/geonetwork-api/src/main/java/au/org/aodn/geonetwork_api/openapi/api/helper/LogosHelper.java @@ -6,16 +6,18 @@ import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; import org.springframework.http.*; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestClientException; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -25,10 +27,12 @@ public class LogosHelper { protected static final String IMAGE = "image"; protected Logger logger = LogManager.getLogger(LogosHelper.class); + protected ResourceLoader resourceLoader; protected LogosApiExt api; - public LogosHelper(LogosApiExt api) { + public LogosHelper(LogosApiExt api, ResourceLoader resourceLoader) { this.api = api; + this.resourceLoader = resourceLoader; } public LogosApiExt getApi() { @@ -52,10 +56,11 @@ public List createLogos(List config) { parsed = parser.parseLogosConfig(v); try { + logger.info("Processing logo config -> {}", v); // Read the link and download the file - URL url = new URL(parsed.getJsonObject().getString("link")); + Resource resource = resourceLoader.getResource(parsed.getJsonObject().getString("link")); - try (InputStream is = url.openStream()) { + try (InputStream is = resource.getInputStream()) { // Store in temp folder File file = File.createTempFile("img", "img"); file.deleteOnExit(); @@ -63,7 +68,12 @@ public List createLogos(List config) { FileUtils.copyInputStreamToFile(is, file); // Delete before add - getApi().deleteLogoWithHttpInfo(parsed.getJsonObject().getString(IMAGE)); + try { + getApi().deleteLogoWithHttpInfo(parsed.getJsonObject().getString(IMAGE)); + } + catch(Exception e) { + logger.warn("Ignore error because delete file do not exist"); + } ResponseEntity response = getApi().addLogoWithHttpInfo( file, @@ -83,19 +93,19 @@ public List createLogos(List config) { } return status; } - catch(HttpServerErrorException.InternalServerError | HttpClientErrorException.BadRequest i){ + catch(HttpServerErrorException.InternalServerError | HttpClientErrorException.BadRequest i) { status.setStatus(i.getStatusCode()); status.setMessage("File already exist in folder? " + i.getMessage()); logger.error(status.getMessage()); return status; } - catch (MalformedURLException e) { - status.setStatus(HttpStatus.BAD_REQUEST); - status.setMessage("Invalid URL in the config : " + parsed.getJsonObject().getString("link")); - logger.error(status.getMessage()); - return status; + catch(RestClientException restClientException) { + // This error indicate file already exist so it is fine + logger.info("Ignore error {} for {}", restClientException.getMessage(), v); + return null; } }) + .filter(Objects::nonNull) .collect(Collectors.toList()); } diff --git a/geonetwork-api/src/test/java/au/org/aodn/geonetwork_api/openapi/api/helper/LogoHelperTest.java b/geonetwork-api/src/test/java/au/org/aodn/geonetwork_api/openapi/api/helper/LogoHelperTest.java new file mode 100644 index 0000000..3cb916c --- /dev/null +++ b/geonetwork-api/src/test/java/au/org/aodn/geonetwork_api/openapi/api/helper/LogoHelperTest.java @@ -0,0 +1,51 @@ +package au.org.aodn.geonetwork_api.openapi.api.helper; + +import au.org.aodn.geonetwork_api.openapi.api.LogosApiExt; +import org.apache.commons.io.FileUtils; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.ResourceUtils; +import org.springframework.web.server.ResponseStatusException; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +public class LogoHelperTest { + /** + * The create logo function will try to delete logo before add, this test is used to verify even + * the logo do not exist and not found exception throw from server, the function still works. + */ + @Test + public void verifyAddNewLogoWorks() throws IOException { + LogosApiExt api = Mockito.mock(LogosApiExt.class); + LogosHelper helper = new LogosHelper(api, new DefaultResourceLoader()); + + when(api.deleteLogoWithHttpInfo(anyString())) + .thenThrow(new ResponseStatusException(HttpStatus.NOT_FOUND, "entity not found")); + + when(api.addLogoWithHttpInfo(any(File.class), anyString(), eq(Boolean.TRUE))) + .thenReturn(ResponseEntity.ok(null)); + + String json1 = FileUtils.readFileToString( + ResourceUtils.getFile("classpath:aad_logo.json"), + StandardCharsets.UTF_8); + + // The logo gif do not exist in the test resources, so this is a negative test case + String json2 = FileUtils.readFileToString( + ResourceUtils.getFile("classpath:not_exist_logo.json"), + StandardCharsets.UTF_8); + + helper.createLogos(List.of(json1, json2)); + + // Only called once because if the logo file not exist then it won't call addLogo + verify(api, times(1)).addLogoWithHttpInfo(any(File.class), anyString(), eq(Boolean.TRUE)); + } +} diff --git a/geonetwork-api/src/test/resources/AAD_logo.png b/geonetwork-api/src/test/resources/AAD_logo.png new file mode 100644 index 0000000..f5c7036 Binary files /dev/null and b/geonetwork-api/src/test/resources/AAD_logo.png differ diff --git a/geonetwork-api/src/test/resources/aad_logo.json b/geonetwork-api/src/test/resources/aad_logo.json new file mode 100644 index 0000000..ab56b83 --- /dev/null +++ b/geonetwork-api/src/test/resources/aad_logo.json @@ -0,0 +1,5 @@ +{ + "id": "aad_logo", + "image": "AAD_logo.gif", + "link": "classpath:AAD_logo.png" +} diff --git a/geonetwork-api/src/test/resources/not_exist_logo.json b/geonetwork-api/src/test/resources/not_exist_logo.json new file mode 100644 index 0000000..59cd0d6 --- /dev/null +++ b/geonetwork-api/src/test/resources/not_exist_logo.json @@ -0,0 +1,5 @@ +{ + "id": "not_exist_logo", + "image": "not_exist_logo.gif", + "link": "file:not_exist_logo.gif" +} diff --git a/geonetwork-config/config.json b/geonetwork-config/config.json index e2bd4b3..0be8d6b 100644 --- a/geonetwork-config/config.json +++ b/geonetwork-config/config.json @@ -49,7 +49,7 @@ }, { "type": "logos", - "jsonFileName": "imas_colour_logo.json" + "jsonFileName": "imos_colour_logo.json" }, { "type": "logos", diff --git a/geonetwork-config/logos/imas_colour_logo.json b/geonetwork-config/logos/imas_colour_logo.json index c0140f9..b3119b4 100644 --- a/geonetwork-config/logos/imas_colour_logo.json +++ b/geonetwork-config/logos/imas_colour_logo.json @@ -1,5 +1,5 @@ { "id": "imas_colour_logo", - "image": "IMAS_colour_logo.gif", - "link": "https://content.aodn.org.au/Documents/Images/Logos/AODN_Partner/IMAS_colour_logo.gif" + "image": "IMAS_colour_logo.png", + "link": "https://content.aodn.org.au/Documents/Images/Logos/AODN_Partner/IMAS_colour_logo.png" } diff --git a/geonetwork-config/logos/imos_colour_logo.json b/geonetwork-config/logos/imos_colour_logo.json index dc1e06c..7de36c0 100644 --- a/geonetwork-config/logos/imos_colour_logo.json +++ b/geonetwork-config/logos/imos_colour_logo.json @@ -1,5 +1,5 @@ { "id": "imos_colour_logo", - "image": "IMOS_logo.gif", - "link": "https://content.aodn.org.au/Documents/Images/Logos/IMOS/IMOS_colour_logo.gif" + "image": "IMOS_colour_logo.gif", + "link": "https://content.aodn.org.au/Documents/Images/Logos/AODN_Partner/IMOS_colour_logo.gif" } diff --git a/geonetwork/src/main/java/au/org/aodn/geonetwork4/Config.java b/geonetwork/src/main/java/au/org/aodn/geonetwork4/Config.java index 2278dd7..35755af 100644 --- a/geonetwork/src/main/java/au/org/aodn/geonetwork4/Config.java +++ b/geonetwork/src/main/java/au/org/aodn/geonetwork4/Config.java @@ -27,6 +27,7 @@ import org.fao.geonet.ApplicationContextHolder; +import org.springframework.core.io.ResourceLoader; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.http.client.support.BasicAuthenticationInterceptor; @@ -40,7 +41,6 @@ import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Map; -import java.util.Objects; @Aspect @Configuration @@ -105,12 +105,11 @@ public class Config { * Use aspectJ to intercept all call that ends with WithHttpInfo, we must always use geonetwork api call * ends with WithHttpInfo because the way geonetworks works is you must present an X-XSRF-TOKEN and session * in the call with username password, otherwise it will fail. - * * You need to make an init call to get the X-XSRF-TOKEN, the call will have status forbidden * and comes back with the token in cookie, then you need to set the token before next call. */ @Pointcut("execution(public * au.org.aodn.geonetwork_api.openapi.api..*.*WithHttpInfo(..))") - public void interceptWithHttpInfo() {}; + public void interceptWithHttpInfo() {} @Around("interceptWithHttpInfo()") public ResponseEntity aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable { @@ -139,14 +138,14 @@ public ResponseEntity aroundAdvice(ProceedingJoinPoint joinPoint) throws Thro public void init() throws NoSuchAlgorithmException, KeyManagementException { logger.info("AODN - Done set logger info"); - /** + /* * No need to do host verfication, this should apply to dev env only */ if(environment == Environment.DEV) { HttpsTrustManager.allowAllSSL(); } - /** + /* * The key here is to use the application context of a child JeevesApplicationContext where its parent * is ApplicationContext. */ @@ -154,10 +153,11 @@ public void init() throws NoSuchAlgorithmException, KeyManagementException { jeevesContext.getBeanFactory().registerSingleton("genericEntityListener", genericEntityListener); } /** - * The reason we need is to setup the WEB_ROOT context to be use by Actuator. In springboot application it is + * The reason we need is to set the WEB_ROOT context to be used by Actuator. In springboot application it is * set on start, but this geonetwork is a different species that it isn't a springboot app so this setting * is missing - * @param sc + * + * @param sc servlet context that pass around */ @Autowired public void setRootContext(ServletContext sc, ConfigurableApplicationContext context) { @@ -177,9 +177,9 @@ public GenericEntityListener createGenericEntityListener() { * Must use prototype scope as there is a XSRF-TOKEN header for each api, that cannot share * with the same api. * - * @param username - * @param password - * @return + * @param username geonetwork admin user name + * @param password geonetwork admin password + * @return The api client connects geonetwork */ @Bean("apiClient") @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) @@ -261,7 +261,8 @@ RegistriesApiExt getRegistriesApi(@Qualifier("apiClient") ApiClient client) { } @Bean - public Setup getSetup(MeApi meApi, + public Setup getSetup(ResourceLoader resourceLoader, + MeApi meApi, LogosApiExt logosApi, GroupsApi groupsApi, TagsApi tagsApi, @@ -271,7 +272,7 @@ public Setup getSetup(MeApi meApi, @Qualifier("harvestersApiLegacy") HarvestersApiLegacy harvestersApiLegacy, @Qualifier("harvestersApi") HarvestersApi harvestersApi) { - return new Setup(meApi, logosApi, groupsApi, tagsApi, registriesApi, siteApi, usersApi, harvestersApiLegacy, harvestersApi); + return new Setup(resourceLoader, meApi, logosApi, groupsApi, tagsApi, registriesApi, siteApi, usersApi, harvestersApiLegacy, harvestersApi); } /** * By default it use the main branch, however when you do your development, you can use a different branch diff --git a/geonetwork/src/main/java/au/org/aodn/geonetwork4/Setup.java b/geonetwork/src/main/java/au/org/aodn/geonetwork4/Setup.java index 05118c7..defa995 100644 --- a/geonetwork/src/main/java/au/org/aodn/geonetwork4/Setup.java +++ b/geonetwork/src/main/java/au/org/aodn/geonetwork4/Setup.java @@ -12,11 +12,13 @@ import org.json.JSONArray; import org.json.JSONObject; +import org.springframework.core.io.ResourceLoader; import org.springframework.http.ResponseEntity; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; /** @@ -36,7 +38,8 @@ public class Setup { protected HarvestersApiLegacy harvestersApiLegacy; protected GroupsHelper groupsHelper; - public Setup(MeApi meApi, + public Setup(ResourceLoader resourceLoader, + MeApi meApi, LogosApiExt logosApi, GroupsApi groupsApi, TagsApi tagsApi, @@ -47,7 +50,7 @@ public Setup(MeApi meApi, HarvestersApi harvestersApi) { this.meApi = meApi; - this.logosHelper = new LogosHelper(logosApi); + this.logosHelper = new LogosHelper(logosApi, resourceLoader); this.groupsHelper = new GroupsHelper(groupsApi); this.tagsHelper = new TagsHelper(tagsApi); this.vocabulariesHelper = new VocabulariesHelper(registriesApi); @@ -63,8 +66,8 @@ public void getMe() { public ResponseEntity> getSystemInfo() { return ResponseEntity.ok(Map.of( - "systemInfo", siteHelper.getApi().getSystemInfoWithHttpInfo().getBody(), - "siteInfo", siteHelper.getApi().getInformationWithHttpInfo().getBody() + "systemInfo", Objects.requireNonNull(siteHelper.getApi().getSystemInfoWithHttpInfo().getBody()), + "siteInfo", Objects.requireNonNull(siteHelper.getApi().getInformationWithHttpInfo().getBody()) )); } @@ -76,7 +79,6 @@ public void deleteAllHarvesters() { /** * The getAllHarvesters is used to get the json format of harvester setting of geonetwork4. It will add * extra fields so works better across different instance of geonetwork4. - * * However, before we store it into repo, we need to break it down so that each harvester json contains 1 harvester * setting only. * diff --git a/geonetwork/src/main/resources/log4j-imos-index.xml b/geonetwork/src/main/resources/log4j-imos-index.xml index fd151d1..2635668 100644 --- a/geonetwork/src/main/resources/log4j-imos-index.xml +++ b/geonetwork/src/main/resources/log4j-imos-index.xml @@ -142,6 +142,11 @@ + + + + + diff --git a/geonetwork/src/main/resources/log4j-imos.xml b/geonetwork/src/main/resources/log4j-imos.xml index 7952acf..3b89e04 100644 --- a/geonetwork/src/main/resources/log4j-imos.xml +++ b/geonetwork/src/main/resources/log4j-imos.xml @@ -143,6 +143,11 @@ + + + + + diff --git a/geonetwork/src/test/java/au/org/aodn/geonetwork4/handler/SetupTest.java b/geonetwork/src/test/java/au/org/aodn/geonetwork4/handler/SetupTest.java index 3351a7e..f4ea07d 100644 --- a/geonetwork/src/test/java/au/org/aodn/geonetwork4/handler/SetupTest.java +++ b/geonetwork/src/test/java/au/org/aodn/geonetwork4/handler/SetupTest.java @@ -17,7 +17,6 @@ import java.nio.charset.StandardCharsets; import java.util.List; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; @@ -50,6 +49,7 @@ public void verifyGetAllHarvesters() throws IOException { .thenReturn(ResponseEntity.ok(groups)); Setup setup = new Setup( + null, null, null, groupsApi,