From e62e11b15a53e1fa4386a57f8bc30f442babb87e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Sun, 28 Jan 2024 18:44:12 +0100 Subject: [PATCH] API Change / Migrate harvester Jeeves services to Spring MVC --- .../fao/geonet/services/harvesting/Clear.java | 1 + .../services/harvesting/CreateClone.java | 1 + .../geonet/services/harvesting/Invoke.java | 1 + .../geonet/services/harvesting/Remove.java | 1 + .../fao/geonet/services/harvesting/Run.java | 1 + .../fao/geonet/services/harvesting/Start.java | 1 + .../geonet/api/harvesting/HarvestersApi.java | 193 +++++++++++++++--- .../js/admin/HarvestSettingsController.js | 42 ++-- 8 files changed, 189 insertions(+), 52 deletions(-) diff --git a/harvesters/src/main/java/org/fao/geonet/services/harvesting/Clear.java b/harvesters/src/main/java/org/fao/geonet/services/harvesting/Clear.java index 6a314b215835..805ce1477cd0 100644 --- a/harvesters/src/main/java/org/fao/geonet/services/harvesting/Clear.java +++ b/harvesters/src/main/java/org/fao/geonet/services/harvesting/Clear.java @@ -39,6 +39,7 @@ * * @author delawen */ +@Deprecated public class Clear implements Service { //-------------------------------------------------------------------------- //--- diff --git a/harvesters/src/main/java/org/fao/geonet/services/harvesting/CreateClone.java b/harvesters/src/main/java/org/fao/geonet/services/harvesting/CreateClone.java index 49139c0ec848..1b0dbc61a18b 100644 --- a/harvesters/src/main/java/org/fao/geonet/services/harvesting/CreateClone.java +++ b/harvesters/src/main/java/org/fao/geonet/services/harvesting/CreateClone.java @@ -37,6 +37,7 @@ /** * TODO javadoc. */ +@Deprecated public class CreateClone implements Service { //-------------------------------------------------------------------------- //--- diff --git a/harvesters/src/main/java/org/fao/geonet/services/harvesting/Invoke.java b/harvesters/src/main/java/org/fao/geonet/services/harvesting/Invoke.java index 7fcc052d6bfe..33f5850bd519 100644 --- a/harvesters/src/main/java/org/fao/geonet/services/harvesting/Invoke.java +++ b/harvesters/src/main/java/org/fao/geonet/services/harvesting/Invoke.java @@ -44,6 +44,7 @@ * "Invoke" service will run a harvester directly, i.e. synchronously. The main purpose for this * service is testing, i.e. get results immmediately returned in a test script. */ +@Deprecated public class Invoke implements Service { //-------------------------------------------------------------------------- //--- diff --git a/harvesters/src/main/java/org/fao/geonet/services/harvesting/Remove.java b/harvesters/src/main/java/org/fao/geonet/services/harvesting/Remove.java index e870e7c4f2fd..40189ff746c4 100644 --- a/harvesters/src/main/java/org/fao/geonet/services/harvesting/Remove.java +++ b/harvesters/src/main/java/org/fao/geonet/services/harvesting/Remove.java @@ -35,6 +35,7 @@ //============================================================================= +@Deprecated public class Remove implements Service { //-------------------------------------------------------------------------- //--- diff --git a/harvesters/src/main/java/org/fao/geonet/services/harvesting/Run.java b/harvesters/src/main/java/org/fao/geonet/services/harvesting/Run.java index c50f7a03ed07..9bb2a2843ce4 100644 --- a/harvesters/src/main/java/org/fao/geonet/services/harvesting/Run.java +++ b/harvesters/src/main/java/org/fao/geonet/services/harvesting/Run.java @@ -35,6 +35,7 @@ //============================================================================= +@Deprecated public class Run implements Service { //-------------------------------------------------------------------------- //--- diff --git a/harvesters/src/main/java/org/fao/geonet/services/harvesting/Start.java b/harvesters/src/main/java/org/fao/geonet/services/harvesting/Start.java index e8f4c13c04f3..23b0ef45ff2d 100644 --- a/harvesters/src/main/java/org/fao/geonet/services/harvesting/Start.java +++ b/harvesters/src/main/java/org/fao/geonet/services/harvesting/Start.java @@ -35,6 +35,7 @@ //============================================================================= +@Deprecated public class Start implements Service { //-------------------------------------------------------------------------- //--- diff --git a/services/src/main/java/org/fao/geonet/api/harvesting/HarvestersApi.java b/services/src/main/java/org/fao/geonet/api/harvesting/HarvestersApi.java index ca2b45d30f73..512efdec451f 100644 --- a/services/src/main/java/org/fao/geonet/api/harvesting/HarvestersApi.java +++ b/services/src/main/java/org/fao/geonet/api/harvesting/HarvestersApi.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2016 Food and Agriculture Organization of the + * Copyright (C) 2001-2023 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -43,6 +43,7 @@ import org.fao.geonet.kernel.harvest.harvester.AbstractHarvester; import org.fao.geonet.repository.HarvestHistoryRepository; import org.fao.geonet.repository.SourceRepository; +import org.fao.geonet.services.harvesting.Util; import org.jdom.Element; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; @@ -51,12 +52,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; @@ -86,9 +82,6 @@ public class HarvestersApi { @io.swagger.v3.oas.annotations.Operation( summary = "Assign harvester records to a new source", description = "" -// authorizations = { -// @Authorization(value = "basicAuth") -// }) ) @RequestMapping( value = "/{harvesterUuid}/assign", @@ -108,12 +101,12 @@ public HttpEntity assignHarvestedRecordToSource( description = "The harvester UUID" ) @PathVariable - String harvesterUuid, + String harvesterUuid, @Parameter( description = "The target source UUID" ) @RequestParam - String source) throws Exception { + String source) throws Exception { final long elapsedTime = System.currentTimeMillis(); final AbstractHarvester harvester = harvestManager.getHarvester(harvesterUuid); if (harvester == null) { @@ -132,20 +125,20 @@ public HttpEntity assignHarvestedRecordToSource( final List allHarvestedRecords = metadataRepository.findAllByHarvestInfo_Uuid(harvesterUuid); List records = new ArrayList<>(allHarvestedRecords.size()); - if (allHarvestedRecords.size() < 1) { + if (allHarvestedRecords.isEmpty()) { throw new NoResultsFoundException(String.format( "Harvester with UUID '%s' has no record to assign to source '%s'.", harvesterUuid, source)); } - for (AbstractMetadata record : allHarvestedRecords) { - record.getSourceInfo().setSourceId(source); - record.getHarvestInfo().setHarvested(false) + for (AbstractMetadata metadataRecord : allHarvestedRecords) { + metadataRecord.getSourceInfo().setSourceId(source); + metadataRecord.getHarvestInfo().setHarvested(false) .setUri(null) .setUuid(null); - metadataManager.save(record); - records.add(record.getId() + ""); + metadataManager.save(metadataRecord); + records.add(metadataRecord.getId() + ""); } dataManager.indexMetadata(records); @@ -174,13 +167,9 @@ public HttpEntity assignHarvestedRecordToSource( @io.swagger.v3.oas.annotations.Operation( summary = "Check if a harvester name or host already exist", description = "" - // authorizations = { - // @Authorization(value = "basicAuth") - // }) ) - @RequestMapping( - value = "/properties/{property}", - method = RequestMethod.GET + @GetMapping( + value = "/properties/{property}" ) @ResponseStatus(value = HttpStatus.OK) @PreAuthorize("hasAuthority('UserAdmin')") @@ -194,12 +183,12 @@ public ResponseEntity checkHarvesterPropertyExist( description = "The harvester property to check" ) @PathVariable - String property, + String property, @Parameter( description = "The value to search" ) @RequestParam - String exist, + String exist, HttpServletRequest request) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); final Element list = harvestManager.get(null, context, "site[1]/name[1]"); @@ -212,4 +201,156 @@ public ResponseEntity checkHarvesterPropertyExist( return new ResponseEntity<>(HttpStatus.NOT_FOUND); } + + @io.swagger.v3.oas.annotations.Operation( + summary = "Remove a harvester", + description = "") + @DeleteMapping( + value = "/{harvesterIdentifier}") + @ResponseStatus(HttpStatus.NO_CONTENT) + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "Harvester removed."), + @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_ONLY_USER_ADMIN) + }) + @PreAuthorize("hasAuthority('UserAdmin')") + public ResponseEntity deleteHarvester( + @Parameter( + description = "Harvester identifier", + required = true + ) + @PathVariable + Integer harvesterIdentifier, + HttpServletRequest request + ) throws Exception { + ServiceContext context = ApiUtils.createServiceContext(request); + Element params = new Element("params"); + params.addContent(new Element("id").setText(harvesterIdentifier.toString())); + + Util.exec(params, context, (hm, id) -> hm.remove(id)); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @io.swagger.v3.oas.annotations.Operation( + summary = "Removes the harvester metadata", + description = "") + @PutMapping( + value = "/{harvesterIdentifier}/clear") + @ResponseStatus(HttpStatus.NO_CONTENT) + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "Harvester metadata removed."), + @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_ONLY_USER_ADMIN) + }) + @PreAuthorize("hasAuthority('UserAdmin')") + public ResponseEntity clearHarvester( + @Parameter( + description = "Harvester identifier", + required = true + ) + @PathVariable + Integer harvesterIdentifier, + HttpServletRequest request + ) throws Exception { + ServiceContext context = ApiUtils.createServiceContext(request); + Element params = new Element("params"); + params.addContent(new Element("id").setText(harvesterIdentifier.toString())); + + Util.exec(params, context, (hm, id) -> hm.clearBatch(id)); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @io.swagger.v3.oas.annotations.Operation( + summary = "Activate a harvester", + description = "") + @PutMapping( + value = "/{harvesterIdentifier}/start") + @ResponseStatus(HttpStatus.NO_CONTENT) + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "Harvester activated."), + @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_ONLY_USER_ADMIN) + }) + @PreAuthorize("hasAuthority('UserAdmin')") + public ResponseEntity startHarvester( + @Parameter( + description = "Harvester identifier", + required = true + ) + @PathVariable + Integer harvesterIdentifier, + HttpServletRequest request + ) throws Exception { + ServiceContext context = ApiUtils.createServiceContext(request); + Element params = new Element("params"); + params.addContent(new Element("id").setText(harvesterIdentifier.toString())); + + Util.exec(params, context, (hm, id) -> hm.start(id)); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @io.swagger.v3.oas.annotations.Operation( + summary = "Activate and run a harvester", + description = "") + @PutMapping( + value = "/{harvesterIdentifier}/run") + @ResponseStatus(HttpStatus.NO_CONTENT) + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "Harvester executed."), + @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_ONLY_USER_ADMIN) + }) + @PreAuthorize("hasAuthority('UserAdmin')") + public ResponseEntity runHarvester( + @Parameter( + description = "Harvester identifier", + required = true + ) + @PathVariable + Integer harvesterIdentifier, + HttpServletRequest request + ) throws Exception { + ServiceContext context = ApiUtils.createServiceContext(request); + Element params = new Element("params"); + params.addContent(new Element("id").setText(harvesterIdentifier.toString())); + + Util.exec(params, context, (hm, id) -> hm.run(id)); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @io.swagger.v3.oas.annotations.Operation( + summary = "Create a clone of a harvester", + description = "") + @PutMapping( + value = "/{harvesterIdentifier}/clone") + @ResponseStatus(HttpStatus.CREATED) + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "Harvester cloned."), + @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_ONLY_USER_ADMIN), + @ApiResponse(responseCode = "404", description = ApiParams.API_RESPONSE_RESOURCE_NOT_FOUND) + }) + @PreAuthorize("hasAuthority('UserAdmin')") + @ResponseBody + public ResponseEntity cloneHarvester( + @Parameter( + description = "Harvester identifier", + required = true + ) + @PathVariable + Integer harvesterIdentifier, + HttpServletRequest request + ) throws Exception { + ServiceContext context = ApiUtils.createServiceContext(request); + + String newId = context.getBean(HarvestManager.class).createClone(harvesterIdentifier.toString(), context.getUserSession().getUserId(), context); + + if (newId != null) { + return new ResponseEntity<>(Integer.parseInt(newId), HttpStatus.CREATED); + } else { + //--- we get here only if the 'id' was not present or node was not found + throw new ResourceNotFoundException(String.format( + "Harvester with identifier '%d' not found. Cannot clone the harvester.", + harvesterIdentifier)); + } + } } diff --git a/web-ui/src/main/resources/catalog/js/admin/HarvestSettingsController.js b/web-ui/src/main/resources/catalog/js/admin/HarvestSettingsController.js index dbcd3d5fc9e5..28a1f5b2ed7c 100644 --- a/web-ui/src/main/resources/catalog/js/admin/HarvestSettingsController.js +++ b/web-ui/src/main/resources/catalog/js/admin/HarvestSettingsController.js @@ -300,12 +300,12 @@ }; $scope.cloneHarvester = function (id) { - $http.get("admin.harvester.clone?_content_type=json&id=" + id).then( + $http.put("../api/harvesters/" + id + "/clone").then( function (response) { $scope.$parent.loadHarvesters().then(function () { // Select the clone angular.forEach($scope.$parent.harvesters, function (h) { - if (h["@id"] === response.data[0]) { + if (h["@id"] == response.data) { $scope.selectHarvester(h); } }); @@ -472,32 +472,24 @@ $scope.deleteHarvester = function () { $scope.deleting.push($scope.harvesterSelected["@id"]); - return $http - .get( - "admin.harvester.remove?_content_type=json&id=" + - $scope.harvesterSelected["@id"] - ) - .then( - function (response) { - $scope.harvesterSelected = {}; - $scope.harvesterUpdated = false; - $scope.harvesterNew = false; - $scope.$parent.loadHarvesters(); + return $http.delete("../api/harvesters/" + $scope.harvesterSelected["@id"]).then( + function (response) { + $scope.harvesterSelected = {}; + $scope.harvesterUpdated = false; + $scope.harvesterNew = false; + $scope.$parent.loadHarvesters(); - $scope.deleting.splice($scope.deleting.indexOf(3), 1); - }, - function (response) { - console.log(response.data); - } - ); + $scope.deleting.splice($scope.deleting.indexOf(3), 1); + }, + function (response) { + console.log(response.data); + } + ); }; $scope.deleteHarvesterRecord = function () { return $http - .get( - "admin.harvester.clear?_content_type=json&id=" + - $scope.harvesterSelected["@id"] - ) + .put("../api/harvesters/" + $scope.harvesterSelected["@id"] + "/clear") .then( function (response) { $scope.harvesterSelected = {}; @@ -543,9 +535,7 @@ }; $scope.runHarvester = function () { return $http - .get( - "admin.harvester.run?_content_type=json&id=" + $scope.harvesterSelected["@id"] - ) + .put("../api/harvesters/" + $scope.harvesterSelected["@id"] + "/run") .then(function (response) { $scope.$parent.loadHarvesters().then(function () { refreshSelectedHarvester();