2016-10-08 20:46:17 +02:00
package org.gcube.data_catalogue.grsf_publish_ws.services ;
2016-11-04 16:26:19 +01:00
import java.net.URLEncoder ;
2016-10-08 23:10:25 +02:00
import java.util.ArrayList ;
2016-12-01 19:19:22 +01:00
import java.util.HashSet ;
2016-10-08 23:10:25 +02:00
import java.util.List ;
2016-10-10 18:50:11 +02:00
import java.util.Map ;
2016-12-01 19:19:22 +01:00
import java.util.Set ;
2016-10-08 23:10:25 +02:00
2016-10-10 18:50:11 +02:00
import javax.servlet.ServletContext ;
2016-10-14 18:44:34 +02:00
import javax.validation.Valid ;
2016-10-15 22:34:57 +02:00
import javax.validation.ValidationException ;
import javax.validation.constraints.NotNull ;
2016-10-08 20:46:17 +02:00
import javax.ws.rs.Consumes ;
2016-10-14 18:44:34 +02:00
import javax.ws.rs.DELETE ;
2016-10-10 18:50:11 +02:00
import javax.ws.rs.GET ;
2016-10-08 20:46:17 +02:00
import javax.ws.rs.POST ;
import javax.ws.rs.Path ;
2016-12-01 19:19:22 +01:00
import javax.ws.rs.PathParam ;
2016-10-08 20:46:17 +02:00
import javax.ws.rs.Produces ;
2016-10-10 18:50:11 +02:00
import javax.ws.rs.core.Context ;
2016-10-08 20:46:17 +02:00
import javax.ws.rs.core.MediaType ;
import javax.ws.rs.core.Response ;
2016-10-08 23:10:25 +02:00
import javax.ws.rs.core.Response.Status ;
2016-10-08 20:46:17 +02:00
2016-10-08 23:10:25 +02:00
import org.gcube.common.authorization.library.provider.AuthorizationProvider ;
2016-10-10 18:50:11 +02:00
import org.gcube.common.authorization.library.provider.SecurityTokenProvider ;
2016-10-08 23:10:25 +02:00
import org.gcube.common.authorization.library.utils.Caller ;
import org.gcube.common.scope.api.ScopeProvider ;
2016-12-01 22:20:46 +01:00
import org.gcube.data_catalogue.grsf_publish_ws.json.input.Common ;
2016-10-14 18:44:34 +02:00
import org.gcube.data_catalogue.grsf_publish_ws.json.input.DeleteProductBean ;
2016-12-03 11:50:38 +01:00
import org.gcube.data_catalogue.grsf_publish_ws.json.input.RefersToBean ;
2016-10-08 20:46:17 +02:00
import org.gcube.data_catalogue.grsf_publish_ws.json.input.StockRecord ;
2016-10-08 23:10:25 +02:00
import org.gcube.data_catalogue.grsf_publish_ws.json.output.ResponseCreationBean ;
import org.gcube.data_catalogue.grsf_publish_ws.utils.HelperMethods ;
2016-12-03 11:50:38 +01:00
import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Product_Type ;
2016-12-01 19:19:22 +01:00
import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Record_Type ;
import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Sources ;
2016-11-24 17:53:50 +01:00
import org.gcube.data_catalogue.grsf_publish_ws.utils.threads.AssociationToGroupThread ;
import org.gcube.data_catalogue.grsf_publish_ws.utils.threads.ManageTimeSeriesThread ;
2016-10-08 23:10:25 +02:00
import org.gcube.datacatalogue.ckanutillibrary.DataCatalogue ;
2016-10-13 14:24:39 +02:00
import org.gcube.datacatalogue.ckanutillibrary.models.ResourceBean ;
2016-10-10 18:50:11 +02:00
import org.gcube.datacatalogue.ckanutillibrary.models.RolesCkanGroupOrOrg ;
2016-10-08 20:46:17 +02:00
import org.slf4j.LoggerFactory ;
2016-10-14 18:44:34 +02:00
import eu.trentorise.opendata.jackan.model.CkanDataset ;
2016-10-08 20:46:17 +02:00
/ * *
* Stock web service methods
* @author Costantino Perciante at ISTI - CNR
* /
2016-12-01 19:19:22 +01:00
@Path ( " {source}/stock/ " )
2016-10-08 20:46:17 +02:00
public class GrsfPublisherStockService {
2016-10-08 23:10:25 +02:00
2016-10-11 11:39:25 +02:00
private static final String DEFAULT_STOCK_LICENSE = " CC-BY-SA-4.0 " ;
2016-10-10 18:50:11 +02:00
// the context
2016-12-01 19:19:22 +01:00
@Context ServletContext contextServlet ;
2016-10-10 18:50:11 +02:00
2016-10-08 20:46:17 +02:00
// Logger
private static final org . slf4j . Logger logger = LoggerFactory . getLogger ( GrsfPublisherStockService . class ) ;
2016-10-11 11:39:25 +02:00
2016-10-10 18:50:11 +02:00
@GET
@Path ( " hello " )
@Produces ( MediaType . TEXT_PLAIN )
public Response hello ( ) {
return Response . ok ( " Hello.. Stock service is here " ) . build ( ) ;
}
2016-10-08 23:10:25 +02:00
2016-10-11 11:39:25 +02:00
@GET
@Path ( " get-licenses " )
@Produces ( MediaType . APPLICATION_JSON )
public Response getLicenses ( ) {
2016-12-01 22:20:46 +01:00
Status status = Status . OK ;
2016-12-03 11:50:38 +01:00
Map < String , String > licenses = CommonServiceUtils . getLicenses ( ) ;
2016-12-01 22:20:46 +01:00
if ( licenses = = null )
2016-10-11 11:39:25 +02:00
status = Status . INTERNAL_SERVER_ERROR ;
return Response . status ( status ) . entity ( licenses ) . build ( ) ;
}
2016-10-08 20:46:17 +02:00
@POST
@Path ( " publish-product " )
@Consumes ( MediaType . APPLICATION_JSON )
@Produces ( MediaType . APPLICATION_JSON )
2016-10-15 22:34:57 +02:00
public Response publishStock (
2016-12-03 11:50:38 +01:00
@NotNull ( message = " record cannot be null " ) @Valid StockRecord record ,
@PathParam ( " source " ) String source ) throws ValidationException {
2016-10-08 23:10:25 +02:00
// retrieve context and username
Caller caller = AuthorizationProvider . instance . get ( ) ;
String username = caller . getClient ( ) . getId ( ) ;
String context = ScopeProvider . instance . get ( ) ;
2016-11-04 16:26:19 +01:00
String token = SecurityTokenProvider . instance . get ( ) ;
2016-10-08 23:10:25 +02:00
2016-10-10 18:50:11 +02:00
logger . info ( " Incoming request for creating a stock record = " + record ) ;
logger . info ( " Request coming from user " + username + " in context " + context ) ;
2016-10-08 23:10:25 +02:00
ResponseCreationBean responseBean = new ResponseCreationBean ( ) ;
2016-10-10 18:50:11 +02:00
Status status = Status . INTERNAL_SERVER_ERROR ;
2016-10-13 14:24:39 +02:00
2016-10-08 23:10:25 +02:00
try {
2016-10-10 18:50:11 +02:00
2016-12-01 19:19:22 +01:00
// Cast the source to the accepted ones
2016-12-01 19:25:30 +01:00
Sources sourceInPath = Sources . onDeserialize ( source ) ;
2016-10-10 18:50:11 +02:00
2016-12-01 19:19:22 +01:00
if ( sourceInPath = = null )
2016-12-03 11:50:38 +01:00
throw new Exception ( " The specified source in the path is unrecogized. Values accepted are [rams, firms, fishsource, grsf] " ) ;
2016-10-10 18:50:11 +02:00
2016-12-01 19:19:22 +01:00
logger . info ( " The request is to create a stock object of source " + sourceInPath ) ;
DataCatalogue catalogue = HelperMethods . getDataCatalogueRunningInstance ( context ) ;
if ( catalogue = = null ) {
status = Status . INTERNAL_SERVER_ERROR ;
throw new Exception ( " There was a problem while serving your request " ) ;
2016-10-10 18:50:11 +02:00
} else {
2016-12-01 19:19:22 +01:00
// check the user has editor/admin role into the org
String organization = HelperMethods . retrieveOrgNameFromScope ( context ) ;
String role = catalogue . getRoleOfUserInOrganization ( username , organization , catalogue . getApiKeyFromUsername ( username ) ) ;
logger . info ( " Role of the user " + username + " is " + role ) ;
if ( ! role . equalsIgnoreCase ( RolesCkanGroupOrOrg . ADMIN . toString ( ) ) ) {
2016-10-10 18:50:11 +02:00
2016-12-01 19:19:22 +01:00
status = Status . FORBIDDEN ;
throw new Exception ( " You are not authorized to create a product. Please check you have the Catalogue-Administrator role! " ) ;
2016-10-10 18:50:11 +02:00
2016-12-01 19:19:22 +01:00
}
2016-10-10 18:50:11 +02:00
2016-12-01 19:19:22 +01:00
// check the record has a name, at least
String futureName = record . getUuid ( ) ;
String futureTitle = record . getStockName ( ) ;
if ( ! HelperMethods . isNameValid ( futureName ) ) {
2016-10-10 18:50:11 +02:00
2016-12-01 19:19:22 +01:00
status = Status . BAD_REQUEST ;
throw new Exception ( " The name requested for the product is not correct! It should contain only alphanumeric characters, and symbols like '.' or '_', '-' " ) ;
} else {
logger . debug ( " Checking if such name [ " + futureName + " ] doesn't exist yet... " ) ;
boolean alreadyExist = catalogue . existProductWithNameOrId ( futureName ) ;
2016-10-10 18:50:11 +02:00
2016-12-01 19:19:22 +01:00
if ( alreadyExist ) {
2016-10-10 18:50:11 +02:00
2016-12-01 19:19:22 +01:00
logger . debug ( " A product with name " + futureName + " already exists " ) ;
status = Status . CONFLICT ;
throw new Exception ( " Sorry but a product with such name already exists! " ) ;
2016-10-10 18:50:11 +02:00
} else {
2016-12-03 11:50:38 +01:00
// validate the record if it is a GRSF one and set the record type
2016-12-01 22:20:46 +01:00
if ( sourceInPath . equals ( Sources . GRSF ) ) {
2016-12-03 11:50:38 +01:00
record . setRecordType ( Record_Type . AGGREGATED ) ;
CommonServiceUtils . validateAggregatedRecord ( record ) ;
} else
record . setRecordType ( Record_Type . ORIGINAL ) ;
2016-10-10 18:50:11 +02:00
2016-12-01 19:19:22 +01:00
// set the type
2016-12-03 11:50:38 +01:00
record . setProductType ( Product_Type . STOCK . getOrigName ( ) ) ;
2016-12-01 19:19:22 +01:00
2016-12-03 11:50:38 +01:00
// evaluate the custom fields/tags, resources and groups
2016-12-01 19:19:22 +01:00
Map < String , List < String > > customFields = record . getExtrasFields ( ) ;
2016-12-03 11:50:38 +01:00
Set < String > tags = new HashSet < String > ( ) ;
Set < String > groups = new HashSet < String > ( ) ;
List < ResourceBean > resources = record . getExtrasResources ( ) ;
boolean skipTags = ! sourceInPath . equals ( Sources . GRSF ) ; // no tags for the Original records
2016-12-04 12:24:37 +01:00
CommonServiceUtils . getTagsGroupsResourcesExtrasByRecord ( tags , skipTags , groups , resources , customFields , record , username , sourceInPath . getOrigName ( ) . toLowerCase ( ) ) ;
2016-12-01 19:19:22 +01:00
2016-12-03 11:50:38 +01:00
// manage the refers to
if ( sourceInPath . equals ( Sources . GRSF ) ) {
List < RefersToBean > refersTo = record . getRefersTo ( ) ;
for ( RefersToBean refersToBean : refersTo ) {
resources . add ( new ResourceBean ( refersToBean . getUrl ( ) , " Source of product " + futureTitle + " in the catalogue has id: "
+ refersToBean . getId ( ) , " Information of a source of the product " + futureTitle , null , username , null , null ) ) ;
}
}
2016-12-01 19:19:22 +01:00
// retrieve the user's email and fullname
String authorMail = HelperMethods . getUserEmail ( context , token ) ;
String authorFullname = HelperMethods . getUserFullname ( context , token ) ;
2016-10-10 18:50:11 +02:00
2016-12-01 19:19:22 +01:00
if ( authorMail = = null | | authorFullname = = null ) {
2016-10-10 18:50:11 +02:00
2016-12-01 19:19:22 +01:00
logger . debug ( " Author fullname or mail missing, cannot continue " ) ;
responseBean . setId ( null ) ;
status = Status . INTERNAL_SERVER_ERROR ;
throw new Exception ( " Sorry but was not possible to retrieve your fullname/email! " ) ;
2016-10-10 18:50:11 +02:00
} else {
2016-12-01 19:19:22 +01:00
// check the license id
String license = null ;
if ( record . getLicense ( ) = = null | | record . getLicense ( ) . isEmpty ( ) )
license = DEFAULT_STOCK_LICENSE ;
else
if ( HelperMethods . existsLicenseId ( record . getLicense ( ) , catalogue ) )
license = record . getLicense ( ) ;
else throw new Exception ( " Please check the license id! " ) ;
long version = record . getVersion ( ) = = null ? 1 : record . getVersion ( ) ;
2016-12-01 22:20:46 +01:00
2016-12-01 19:27:40 +01:00
logger . info ( " Invoking creation method.. " ) ;
2016-12-01 19:19:22 +01:00
// create the product
String id = catalogue . createCKanDatasetMultipleCustomFields (
catalogue . getApiKeyFromUsername ( username ) ,
futureTitle ,
futureName ,
2016-12-04 15:40:58 +01:00
organization , //"grsf", //TO DELETE TODO
2016-12-01 19:19:22 +01:00
authorFullname ,
authorMail ,
record . getMaintainer ( ) ,
record . getMaintainerContact ( ) ,
version ,
HelperMethods . removeHTML ( record . getDescription ( ) ) ,
license ,
new ArrayList < String > ( tags ) ,
customFields ,
resources ,
2016-12-04 12:24:37 +01:00
true ) ;
2016-12-01 19:19:22 +01:00
if ( id ! = null ) {
logger . info ( " Product created! Id is " + id ) ;
responseBean . setId ( id ) ;
status = Status . CREATED ;
responseBean . setProductUrl ( catalogue . getPortletUrl ( ) + " ? " + URLEncoder . encode ( " path=/dataset/ " + futureName , " UTF-8 " ) ) ;
responseBean . setKbUuid ( record . getUuid ( ) ) ;
if ( ! groups . isEmpty ( ) ) {
logger . info ( " Launching thread for association to the list of groups " + groups ) ;
AssociationToGroupThread threadGroups = new AssociationToGroupThread ( new ArrayList < String > ( groups ) , id , organization , username , catalogue ) ;
threadGroups . start ( ) ;
logger . info ( " Waiting association thread to die.. " ) ;
threadGroups . join ( ) ;
logger . debug ( " Ok, it died " ) ;
}
// manage time series
logger . info ( " Launching thread for time series handling " ) ;
new ManageTimeSeriesThread ( record , futureName , username , catalogue , context , token ) . start ( ) ;
} else
throw new Exception ( " There was an error during the product generation, sorry " ) ;
2016-10-10 18:50:11 +02:00
}
}
}
}
2016-12-01 19:19:22 +01:00
// }
2016-10-08 23:10:25 +02:00
} catch ( Exception e ) {
logger . error ( " Failed to create stock record " , e ) ;
2016-10-10 18:50:11 +02:00
responseBean . setError ( e . getMessage ( ) ) ;
2016-10-08 23:10:25 +02:00
}
2016-10-10 18:50:11 +02:00
2016-10-08 23:10:25 +02:00
return Response . status ( status ) . entity ( responseBean ) . build ( ) ;
2016-10-08 20:46:17 +02:00
}
2016-10-14 18:44:34 +02:00
@DELETE
@Path ( " delete-product " )
@Consumes ( MediaType . APPLICATION_JSON )
@Produces ( MediaType . APPLICATION_JSON )
2016-11-06 21:21:11 +01:00
public Response deleteStock (
@NotNull ( message = " missing input value " )
2016-12-03 11:50:38 +01:00
@Valid DeleteProductBean recordToDelete ,
@PathParam ( " source " ) String source ) throws ValidationException {
2016-10-14 18:44:34 +02:00
// retrieve context and username
Caller caller = AuthorizationProvider . instance . get ( ) ;
String username = caller . getClient ( ) . getId ( ) ;
String context = ScopeProvider . instance . get ( ) ;
ResponseCreationBean responseBean = new ResponseCreationBean ( ) ;
Status status = Status . INTERNAL_SERVER_ERROR ;
// check it is a stock ...
logger . info ( " Received call to delete product with id " + recordToDelete . getId ( ) + " , checking if it is a stock " ) ;
try {
DataCatalogue catalogue = HelperMethods . getDataCatalogueRunningInstance ( context ) ;
if ( catalogue = = null ) {
status = Status . INTERNAL_SERVER_ERROR ;
throw new Exception ( " There was a problem while serving your request " ) ;
}
2016-12-03 11:50:38 +01:00
// Cast the source to the accepted ones
Sources sourceInPath = Sources . onDeserialize ( source ) ;
if ( sourceInPath = = null )
2016-12-04 12:24:37 +01:00
throw new Exception ( " The specified source in the path is unrecognized. Values accepted are [rams, firms, fishsource, grsf] " ) ;
2016-12-03 11:50:38 +01:00
logger . info ( " The request is to create a stock object of source " + sourceInPath ) ;
2016-10-14 18:44:34 +02:00
// retrieve the catalogue instance
CkanDataset stockInCkan = catalogue . getDataset ( recordToDelete . getId ( ) , catalogue . getApiKeyFromUsername ( username ) ) ;
if ( stockInCkan = = null ) {
status = Status . NOT_FOUND ;
throw new Exception ( " There was a problem while serving your request. This product was not found " ) ;
}
2016-11-27 12:37:44 +01:00
// get extras and check there is the product type
2016-12-03 11:50:38 +01:00
if ( stockInCkan . getExtrasAsHashMap ( ) . get ( Common . PRODUCT_TYPE_KEY ) . equals ( Product_Type . STOCK . getOrigName ( ) ) ) {
2016-10-14 18:44:34 +02:00
logger . warn ( " Ok, this is a stock, removing it " ) ;
boolean deleted = catalogue . deleteProduct ( stockInCkan . getId ( ) , catalogue . getApiKeyFromUsername ( username ) , true ) ;
if ( deleted ) {
logger . info ( " Stock DELETED AND PURGED! " ) ;
status = Status . OK ;
responseBean . setId ( stockInCkan . getId ( ) ) ;
}
else {
status = Status . INTERNAL_SERVER_ERROR ;
throw new Exception ( " Request failed, sorry " ) ;
}
} else {
status = Status . BAD_REQUEST ;
throw new Exception ( " The id you are using doesn't belong to a Stock product! " ) ;
}
} catch ( Exception e ) {
logger . error ( " Failed to delete this " , e ) ;
status = Status . INTERNAL_SERVER_ERROR ;
responseBean . setError ( e . getMessage ( ) ) ;
}
return Response . status ( status ) . entity ( responseBean ) . build ( ) ;
}
2016-10-08 20:46:17 +02:00
}