package org.gcube.gcat.persistence.ckan; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.ws.rs.BadRequestException; import javax.ws.rs.InternalServerErrorException; import javax.ws.rs.NotAuthorizedException; import javax.ws.rs.NotFoundException; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import org.gcube.datacatalogue.ckanutillibrary.server.DataCatalogue; import org.gcube.datacatalogue.ckanutillibrary.server.DataCatalogueFactory; import org.gcube.gcat.utils.ContextUtility; import org.gcube.gcat.utils.HTTPCall; import org.gcube.gcat.utils.HTTPCall.HTTPMETHOD; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.NullNode; import com.fasterxml.jackson.databind.node.ObjectNode; /** * @author Luca Frosini (ISTI - CNR) */ public abstract class CKAN { private static final Logger logger = LoggerFactory.getLogger(CKAN.class); protected static final String ID_KEY = "id"; protected static final String NAME_KEY = "name"; protected static final String ERROR_KEY = "error"; protected static final String ERROR_TYPE_KEY = "__type"; protected static final String MESSAGE_KEY = "message"; protected static final String OWNER_ORG_KEY = "owner_org"; protected static final String RESULT_KEY = "result"; protected static final String SUCCESS_KEY = "success"; protected static final String NOT_FOUND_ERROR = "Not Found Error"; protected static final String AUTHORIZATION_ERROR = "Authorization Error"; protected static final String VALIDATION_ERROR = "Validation Error"; // api rest path CKAN public final static String CKAN_API_PATH = "/api/3/action/"; // ckan header authorization public final static String AUTH_CKAN_HEADER = "Authorization"; protected String LIST; protected String CREATE; protected String READ; protected String UPDATE; protected String PATCH; protected String DELETE; protected String PURGE; protected final ObjectMapper mapper; protected final DataCatalogue dataCatalogue; protected String name; protected String apiKey; protected JsonNode result; public String getApiKey() { if(apiKey==null) { try { return CKANUtility.getApiKey(); }catch (Exception e) { throw new InternalServerErrorException(e); } } return apiKey; } public void setApiKey(String apiKey) { this.apiKey = apiKey; } public String getName() { return name; } public void setName(String name) { this.name = name; } public ObjectMapper getMapper() { return mapper; } public JsonNode getJsonNodeResult() { return result; } protected CKAN() { try { this.mapper = new ObjectMapper(); this.dataCatalogue = getCatalogue(); }catch (Exception e) { throw new InternalServerErrorException(e); } } protected JsonNode getAsJsonNode(String json) { try { return mapper.readTree(json); } catch(IOException e) { throw new BadRequestException(e); } } /** * Retrieve an instance of the library for the current scope * @return * @throws Exception */ public static DataCatalogue getCatalogue() throws Exception { String context = ContextUtility.getCurrentContext(); logger.debug("Discovering ckan instance in context {}", context); return DataCatalogueFactory.getFactory().getUtilsPerScope(context); } /** * Validate the CKAN response and return the * @param json * @return */ protected JsonNode validateCKANResponse(String json) { JsonNode jsonNode = getAsJsonNode(json); if(jsonNode.get(SUCCESS_KEY).asBoolean()) { return jsonNode.get(RESULT_KEY); } else { try { JsonNode error = jsonNode.get(ERROR_KEY); String errorType = error.get(ERROR_TYPE_KEY).asText(); if(errorType.compareTo(VALIDATION_ERROR) == 0) { throw new BadRequestException(getAsString(error)); } String message = error.get(MESSAGE_KEY).asText(); if(errorType.compareTo(NOT_FOUND_ERROR) == 0) { throw new NotFoundException(message); } if(errorType.compareTo(AUTHORIZATION_ERROR) == 0) { throw new NotAuthorizedException(message); } // TODO parse more cases } catch(WebApplicationException e) { throw e; } catch(Exception e) { throw new BadRequestException(json); } throw new BadRequestException(json); } } protected String getAsString(JsonNode node) { try { String json = mapper.writeValueAsString(node); return json; } catch(JsonProcessingException e) { throw new InternalServerErrorException(e); } } protected JsonNode checkName(JsonNode jsonNode) { try { String gotName = jsonNode.get(NAME_KEY).asText(); if(name==null) { name = gotName; } if(gotName != null && gotName.compareTo(name) != 0) { String error = String.format( "The name (%s) does not match with the '%s' contained in the provided content (%s).", name, NAME_KEY, gotName); throw new BadRequestException(error); } return jsonNode; } catch(BadRequestException e) { throw e; } catch(Exception e) { throw new BadRequestException("Unable to obtain a correct 'name' from the provided content"); } } protected JsonNode checkName(String json) { JsonNode jsonNode = getAsJsonNode(json); checkName(jsonNode); return jsonNode; } protected JsonNode createJsonNodeWithID(String id) { ObjectNode objectNode = mapper.createObjectNode(); objectNode.put(ID_KEY, id); return objectNode; } protected JsonNode createJsonNodeWithNameAsID() { return createJsonNodeWithID(name); } protected Map getMapWithNameAsID() { return getMapWithID(name); } protected Map getMapWithID(String id) { Map map = new HashMap<>(); map.put(ID_KEY, id); return map; } public JsonNode sendRequest(HTTPMETHOD httpMethod, String path, Map parameters, String body) { String catalogueURL = dataCatalogue.getCatalogueUrl().endsWith("/") ? dataCatalogue.getCatalogueUrl() : dataCatalogue.getCatalogueUrl() + "/"; HTTPCall httpCall = new HTTPCall(catalogueURL); httpCall.setgCubeTargetService(false); httpCall.addHeader(AUTH_CKAN_HEADER, getApiKey()); String ret = httpCall.call(path, httpMethod, parameters, body, MediaType.APPLICATION_JSON); JsonNode result = validateCKANResponse(ret); if(result instanceof NullNode) { result = mapper.createObjectNode(); } return result; } public String sendGetRequest(String path, Map parameters) { result = sendRequest(HTTPMETHOD.GET, path, parameters, null); return getAsString(result); } public String sendPostRequest(String path, String body) { result = sendRequest(HTTPMETHOD.POST, path, null, body); return getAsString(result); } public String sendPostRequest(String path, JsonNode jsonNode) { return sendPostRequest(path, getAsString(jsonNode)); } public String list() { return sendGetRequest(LIST, null); } public String create(String json) { return sendPostRequest(CREATE, json); } public String read() { return sendGetRequest(READ, getMapWithNameAsID()); } public String update(String json) { checkName(json); return sendPostRequest(UPDATE, json); } public String patch(String json) { JsonNode jsonNode = checkName(json); ObjectNode objectNode = ((ObjectNode) jsonNode); objectNode.put(ID_KEY, name); objectNode.remove(NAME_KEY); return sendPostRequest(PATCH, objectNode); } protected void delete() { sendPostRequest(DELETE, createJsonNodeWithNameAsID()); } public void delete(boolean purge) { if(purge) { purge(); } else { delete(); } } protected void purge() { sendPostRequest(PURGE, createJsonNodeWithNameAsID()); } }