Implementing service

This commit is contained in:
Luca Frosini 2023-05-29 14:44:10 +02:00
parent e659628dc7
commit 7f3c34acb9
7 changed files with 415 additions and 58 deletions

20
pom.xml
View File

@ -83,16 +83,36 @@
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.portlets.user</groupId>
<artifactId>uri-resolver-manager</artifactId>
<version>[1.0.0, 2.0.0-SNAPSHOT)</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.32</version>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>storagehub-model</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.data-publishing</groupId>
<artifactId>storagehub-application-persistence</artifactId>
<version>[3.0.0,4.0.0-SNAPSHOT)</version>
</dependency>
<!-- Added to support Java 11 JDK -->
<dependency>

View File

@ -1,52 +1,69 @@
package org.gcube.grsf.publisher.record;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.UriInfo;
import org.gcube.com.fasterxml.jackson.core.JsonProcessingException;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
import org.gcube.common.storagehub.client.dsl.FileContainer;
import org.gcube.common.storagehub.model.Metadata;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.gcat.persistence.ckan.CKANPackage;
import org.gcube.gcat.persistence.ckan.CKANUserCache;
import org.gcube.grsf.publisher.freemarker.FreeMarker;
import org.gcube.storagehub.StorageHubManagement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public abstract class Record {
public abstract class Record extends CKANPackage {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public static final String GRSF_UUID_PROPERTY = "grsf_uuid";
public static final String RESOURCES_PROPERTY = "resources";
public static final String RESOURCE_ELEMENT_NAME_PROPERTY = "name";
public static final String RESOURCE_ELEMENT_URL_PROPERTY = "url";
public static final String TIMESERIES_PROPERTY = "timeseries";
public static final String TIMESERIES_ELEMENT_PROPERTY_PROPERTY = "property";
public static final String TIMESERIES_ELEMENT_FILENAME_PROPERTY = "filename";
protected UriInfo uriInfo;
protected String id;
protected String grsfUUID;
protected ObjectMapper objectMapper;
protected JsonNode jsonNode;
protected Set<String> groups;
protected Set<String> tags;
protected Set<String> resources;
protected Set<FileContainer> wsUploadedFiles;
public Record() {
this.objectMapper = new ObjectMapper();
this.groups = new HashSet<>();
this.tags = new HashSet<>();
this.resources = new HashSet<>();
}
public abstract String getType();
@ -59,26 +76,24 @@ public abstract class Record {
this.tags.add(tag);
}
public void setId(String id) {
this.id = id;
public void setGRSFUUID(String grsfUUID) {
this.grsfUUID = grsfUUID;
}
public String getId() {
return this.id;
public String getGRSFUUID() {
return this.grsfUUID;
}
public void setUriInfo(UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
public void elaborate() throws Exception {
public JsonNode elaborate(JsonNode node) throws Exception {
FreeMarker freeMarker = new FreeMarker();
Template tsTemplate = freeMarker.getTemplate("timeseries.ftl");
Template template = freeMarker.getTemplate(getType() + ".ftl");
Map<String, Object> map = getMapFromSourceJson();
Map<String, Object> map = getMapFromSourceJson(node);
grsfUUID = map.get(Record.GRSF_UUID_PROPERTY).toString();
File transformedJson = getTransformedJsonFile(template,map);
List<File> timeseriesFiles = elaborateTimeSeries(tsTemplate,map,transformedJson);
// TODO save files on workspace using storagehub-application-persistence
jsonNode = mapper.readTree(transformedJson);
jsonNode = elaborateTimeSeries(tsTemplate,map);
return jsonNode;
}
public String list(int limit, int offset) {
@ -86,7 +101,24 @@ public abstract class Record {
return null;
}
protected Map<String, Object> getMapFromSourceJson() throws JsonProcessingException {
/**
* Save the file in the workspace using storagehub-application-persistence
* @param file the file to persist in the workspace
* @return
* @throws Exception
*/
protected FileContainer persistFile(File file) throws Exception{
StorageHubManagement shm = new StorageHubManagement();
FileInputStream fis = new FileInputStream(file);
Map<String,Object> map = new HashMap<>();
map.put(Record.GRSF_UUID_PROPERTY, grsfUUID);
Metadata metadata = new Metadata(map);
shm.persistFile(fis, file.getName(), "text/csv", metadata);
FileContainer fileContainer = shm.getPersistedFile();
return fileContainer;
}
protected Map<String, Object> getMapFromSourceJson(JsonNode jsonNode) throws JsonProcessingException {
@SuppressWarnings("unchecked")
Map<String, Object> map = objectMapper.treeToValue(jsonNode, Map.class);
@ -99,7 +131,7 @@ public abstract class Record {
}
protected File getTransformedJsonFile(Template template, Map<String, Object> map) throws Exception {
File out = new File(map.get("grsf_uuid").toString()+".json");
File out = new File(grsfUUID+".json");
out.delete();
Writer writer = new FileWriter(out);
@ -110,47 +142,115 @@ public abstract class Record {
return out;
}
protected List<File> elaborateTimeSeries(Template tsTemplate,Map<String, Object> map, File transformedJson) throws IOException, TemplateException {
protected JsonNode elaborateTimeSeries(Template tsTemplate, Map<String, Object> map) throws Exception {
ArrayNode resources = (ArrayNode) jsonNode.get(Record.RESOURCES_PROPERTY);
this.wsUploadedFiles = new HashSet<>();
ArrayNode timeseries = (ArrayNode) jsonNode.get(Record.TIMESERIES_PROPERTY);
List<File> timeseriesFiles = new ArrayList<>();
JsonNode node = objectMapper.readTree(transformedJson);
ArrayNode timeseries = (ArrayNode) node.get("timeseries");
for(JsonNode n : timeseries) {
String key = n.get("property").asText();
String fileName = n.get("filename").asText();
logger.trace("Elaborating {} timeseries {} from file {}", getType(), key, transformedJson.getName());
map.put("timeseries", map.get(key));
File tsOut = new File(transformedJson.getName().replace(".json","")+"-"+fileName+".csv");
tsOut.delete();
Writer tsWriter = new FileWriter(tsOut);
tsTemplate.process(map, tsWriter);
tsWriter.flush();
tsWriter.close();
timeseriesFiles.add(tsOut);
map.remove("timeseries");
}
return timeseriesFiles;
try {
for(JsonNode n : timeseries) {
String key = n.get(Record.TIMESERIES_ELEMENT_PROPERTY_PROPERTY).asText();
try {
String fileName = n.get(Record.TIMESERIES_ELEMENT_FILENAME_PROPERTY).asText();
logger.trace("Elaborating {} timeseries from property {} of Record with GRSF UUID {}", getType(), key, grsfUUID);
/*
* Adding to map the name of the property containing the timeseries
* It is used by the FreeMarker template to get json property to be used
* to generate the timeseries
*/
map.put(Record.TIMESERIES_PROPERTY, map.get(key));
File tsOut = new File(grsfUUID + ".json-"+fileName+".csv");
tsOut.delete();
Writer tsWriter = new FileWriter(tsOut);
tsTemplate.process(map, tsWriter);
tsWriter.flush();
tsWriter.close();
timeseriesFiles.add(tsOut);
/*
* Removing the property form the map keep clean the map
* generated using the input json
*/
map.remove(Record.TIMESERIES_PROPERTY);
FileContainer fc = persistFile(tsOut);
this.wsUploadedFiles.add(fc);
URL url = fc.getPublicLink();
ObjectNode resourceNode = objectMapper.createObjectNode();
resourceNode.put(RESOURCE_ELEMENT_NAME_PROPERTY, fileName);
resourceNode.put(RESOURCE_ELEMENT_URL_PROPERTY, url.toString());
resources.add(resourceNode);
((ObjectNode)jsonNode).replace(RESOURCES_PROPERTY, resources);
timeseriesFiles.remove(tsOut);
tsOut.delete();
}catch (Exception e) {
logger.error("Something went wrong generating {} timeseries from property {} of Record with GRSF UUID {}", getType(), key, grsfUUID);
throw e;
}
}
((ObjectNode)jsonNode).remove(Record.TIMESERIES_PROPERTY);
} finally {
/*
* Remove the local files of timeseries
* if an error occurs to keep the environment clean
*/
for(File f : timeseriesFiles) {
f.delete();
}
}
return jsonNode;
}
@Override
public String create(String json) {
try {
this.jsonNode = objectMapper.readTree(json);
elaborate();
// TODO
} catch (Exception e) {
JsonNode jsonNode = objectMapper.readTree(json);
elaborate(jsonNode);
sendPostRequest(CREATE, json);
parseResult();
String userFullName = CKANUserCache.getCurrrentCKANUser().getNameSurname();
sendSocialPost(userFullName);
return getAsCleanedString(result);
} catch(WebApplicationException e) {
deleteUploadedFiles();
throw e;
} catch(Exception e) {
deleteUploadedFiles();
throw new InternalServerErrorException(e);
}
return null;
}
private void deleteUploadedFiles() {
for(FileContainer fileContainer : wsUploadedFiles) {
try {
fileContainer.delete();
} catch (StorageHubException e) {
logger.warn("Unable to delete from StorageHub the file with ID:{}", fileContainer.getId());
}
}
}
@Override
public String update(String json) {
try {
this.jsonNode = objectMapper.readTree(json);
elaborate();
JsonNode jsonNode = objectMapper.readTree(json);
elaborate(jsonNode);
// TODO
} catch (Exception e) {
throw new InternalServerErrorException(e);
@ -158,10 +258,11 @@ public abstract class Record {
return null;
}
@Override
public String patch(String json) {
try {
this.jsonNode = objectMapper.readTree(json);
elaborate();
JsonNode jsonNode = objectMapper.readTree(json);
elaborate(jsonNode);
// TODO
} catch (Exception e) {
throw new InternalServerErrorException(e);
@ -173,11 +274,13 @@ public abstract class Record {
// TODO
}
@Override
public String read() {
// TODO
return null;
}
@Override
public int count() {
// TODO
return 0;

View File

@ -115,28 +115,28 @@ public class BaseRESTAPIs<R extends Record> {
String ret = record.create(json);
ResponseBuilder responseBuilder = Response.status(Status.CREATED).entity(ret);
responseBuilder = addLocation(responseBuilder, record.getId());
responseBuilder = addLocation(responseBuilder, record.getGRSFUUID());
return responseBuilder.type(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8).build();
}
public String read(String id) {
setCalledMethod("GET /" + COLLECTION_PARAMETER + "/{" + ID_PARAMETER + "}");
Record record = getInstance();
record.setId(id);
record.setGRSFUUID(id);
return record.read();
}
public String update(String id, String json) {
setCalledMethod("PUT /" + COLLECTION_PARAMETER + "/{" + ID_PARAMETER + "}");
Record record = getInstance();
record.setId(id);
record.setGRSFUUID(id);
return record.update(json);
}
public String patch(String id, String json) {
setCalledMethod("PATCH /" + COLLECTION_PARAMETER + "/{" + ID_PARAMETER + "}");
Record record = getInstance();
record.setId(id);
record.setGRSFUUID(id);
return record.patch(json);
}
@ -151,7 +151,7 @@ public class BaseRESTAPIs<R extends Record> {
setCalledMethod("DELETE /" + COLLECTION_PARAMETER + "/{" + ID_PARAMETER + "}");
}
Record record = getInstance();
record.setId(id);
record.setGRSFUUID(id);
record.delete(purge);
return Response.status(Status.NO_CONTENT).build();
}

View File

@ -0,0 +1,122 @@
/**
*
*/
package org.gcube.grsf.publisher;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.gcube.common.authorization.utils.manager.SecretManager;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.common.authorization.utils.secret.JWTSecret;
import org.gcube.common.authorization.utils.secret.Secret;
import org.gcube.common.authorization.utils.secret.SecretUtility;
import org.gcube.common.keycloak.KeycloakClientFactory;
import org.gcube.common.keycloak.model.TokenResponse;
import org.gcube.common.scope.api.ScopeProvider;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class ContextTest {
private static final Logger logger = LoggerFactory.getLogger(ContextTest.class);
protected static final String CONFIG_INI_FILENAME = "config.ini";
public static final String PARENT_DEFAULT_TEST_SCOPE;
public static final String DEFAULT_TEST_SCOPE;
public static final String ALTERNATIVE_TEST_SCOPE;
public static final String GCUBE;
public static final String DEVNEXT;
public static final String NEXTNEXT;
public static final String DEVSEC;
public static final String DEVVRE;
protected static final Properties properties;
protected static final String CLIENT_ID_PROPERTY_KEY = "client_id";
protected static final String CLIENT_SECRET_PROPERTY_KEY = "client_secret";
protected static final String clientID;
protected static final String clientSecret;
static {
GCUBE = "/gcube";
DEVNEXT = GCUBE + "/devNext";
NEXTNEXT = DEVNEXT + "/NextNext";
DEVSEC = GCUBE + "/devsec";
DEVVRE = DEVSEC + "/devVRE";
PARENT_DEFAULT_TEST_SCOPE = "/gcube";
DEFAULT_TEST_SCOPE = DEVSEC;
ALTERNATIVE_TEST_SCOPE = DEVVRE;
properties = new Properties();
InputStream input = ContextTest.class.getClassLoader().getResourceAsStream(CONFIG_INI_FILENAME);
try {
// load the properties file
properties.load(input);
clientID = properties.getProperty(CLIENT_ID_PROPERTY_KEY);
clientSecret = properties.getProperty(CLIENT_SECRET_PROPERTY_KEY);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void set(Secret secret) throws Exception {
SecretManagerProvider.instance.reset();
SecretManager secretManager = new SecretManager();
secretManager.addSecret(secret);
SecretManagerProvider.instance.set(secretManager);
SecretManagerProvider.instance.get().set();
}
public static void setContextByName(String fullContextName) throws Exception {
Secret secret = getSecretByContextName(fullContextName);
set(secret);
}
private static TokenResponse getJWTAccessToken(String context) throws Exception {
ScopeProvider.instance.set(context);
TokenResponse tr = KeycloakClientFactory.newInstance().queryUMAToken(clientID, clientSecret, context, null);
return tr;
}
public static Secret getSecretByContextName(String context) throws Exception {
TokenResponse tr = getJWTAccessToken(context);
Secret secret = new JWTSecret(tr.getAccessToken());
return secret;
}
public static void setContext(String token) throws Exception {
Secret secret = getSecret(token);
set(secret);
}
private static Secret getSecret(String token) throws Exception {
Secret secret = SecretUtility.getSecretByTokenString(token);
return secret;
}
@BeforeClass
public static void beforeClass() throws Exception {
setContextByName(DEFAULT_TEST_SCOPE);
}
@AfterClass
public static void afterClass() throws Exception {
SecretManagerProvider.instance.reset();
}
}

View File

@ -85,7 +85,7 @@ public class FreeMarkerTest {
for(File jsonFile : jsonFiles) {
ObjectMapper mapper = new ObjectMapper();
JsonNode test = mapper.readTree(jsonFile);
mapper.readTree(jsonFile);
@SuppressWarnings("unchecked")
Map<String, Object> map = mapper.readValue(jsonFile, Map.class);

View File

@ -0,0 +1,110 @@
package org.gcube.grsf.publisher.record;
import java.io.File;
import java.io.FilenameFilter;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
import org.gcube.grsf.publisher.ContextTest;
import org.gcube.grsf.publisher.freemarker.FreeMarker;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RecordTest extends ContextTest {
private static Logger logger = LoggerFactory.getLogger(RecordTest.class);
public File getExampleDirectory(String type) throws Exception {
File resourcesDirectory = FreeMarker.getResourcesDirectory();
return new File(resourcesDirectory, "examples/" + type);
}
@Test
public void testRecords() throws Exception {
// String[] types = new String[] {"Stock", "Fishery", "TraceabilityUnit"};
String[] types = new String[] { "Stock" };
// String[] types = new String[] {"Fishery"};
// String[] types = new String[] {"TraceabilityUnit"};
Calendar start = Calendar.getInstance();
int countRecord = 0;
int countTimeSeries = 0;
File outputDir = new File(FreeMarker.getResourcesDirectory(), "output");
outputDir.mkdir();
FilenameFilter dirnameFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
File f = new File(dir, name);
return f.isDirectory();
}
};
FilenameFilter filenameFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".json");
}
};
for (String type : types) {
File examplesDir = getExampleDirectory(type);
File outputTypeDir = new File(outputDir, type);
outputTypeDir.mkdir();
// File[] sourceFiles = examplesDir.listFiles(dirnameFilter);
File[] sourceFiles = new File[] { new File(examplesDir, "grsf") };
for (File source : sourceFiles) {
String sourceString = source.getName();
File outputSourceDir = new File(outputTypeDir, sourceString);
outputSourceDir.mkdir();
// File[] jsonFiles = source.listFiles(filenameFilter);
File[] jsonFiles = new File[] {new File(source,"88818c3f-7120-322b-9637-7c7d2e9fc1e5.json")};
for (File jsonFile : jsonFiles) {
ObjectMapper mapper = new ObjectMapper();
ObjectNode input = (ObjectNode) mapper.readTree(jsonFile);
input.put("source", sourceString);
Record record;
switch (type) {
case "Fishery":
record = new Fishery();
break;
case "Stock":
record = new Stock();
break;
case "TraceabilityUnit":
record = new TraceabilityUnit();
break;
default:
return;
}
record.create(mapper.writeValueAsString(input));
}
}
}
Calendar end = Calendar.getInstance();
long diff = end.getTimeInMillis() - start.getTimeInMillis();
logger.info("Generated {} records and {} CSV files from TimeSeries in {} milliseconds (~{} seconds)",
countRecord, countTimeSeries, diff, TimeUnit.MILLISECONDS.toSeconds(diff));
}
}

View File

@ -1,3 +1,5 @@
/FARM.gcubekey
/token.properties
/config.properties
/*.gcubekey
/config.ini