package org.gcube.gcat.rest; import javax.ws.rs.BadRequestException; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.HttpMethod; import javax.ws.rs.InternalServerErrorException; import javax.ws.rs.NotAllowedException; //import javax.ws.rs.NotAuthorizedException; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.Status; import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode; //import org.gcube.common.authorization.control.annotations.AuthorizationControl; import org.gcube.datacatalogue.metadatadiscovery.DataCalogueMetadataFormatReader; import org.gcube.gcat.api.GCatConstants; //import org.gcube.gcat.api.roles.Role; import org.gcube.gcat.profile.ISProfile; import org.xml.sax.SAXException; import com.webcohesion.enunciate.metadata.rs.ResourceGroup; import com.webcohesion.enunciate.metadata.rs.ResourceLabel; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; /** * A Profile must comply with the defined XSD schema . * * Please find the documentation of profile schema at: * at Metadata Profile * * @author Luca Frosini (ISTI - CNR) */ @Path(Profile.PROFILES) @ResourceGroup("Item Related APIs") @ResourceLabel("Profile APIs") public class Profile extends BaseREST implements org.gcube.gcat.api.interfaces.Profile { public static final String PROFILE_NAME_PARAMETER = "PROFILE_NAME"; public static final String PROFILE_VALIDATION_ERROR; public static final String CANNOT_MANAGE_PROFILE_SCHEMA = "You cannot manage the profile schema"; static { StringBuilder validationError = new StringBuilder(); validationError.append("The Profile is not valid because of the following error at validation time:\n%s\n\n"); validationError.append("The XSD used to validate the profile is available at %s\n\n"); validationError.append( "To check your profile you can use a tool such as Oxygen XML Editor or an online service such as the one available at:\n"); validationError.append("- http://www.utilities-online.info/xsdvalidation/\n"); validationError.append("- https://www.freeformatter.com/xml-validator-xsd.html\n"); PROFILE_VALIDATION_ERROR = validationError.toString(); } /* * Not used as REST method, implemented to respect {@link org.gcube.gcat.api.interfaces.Item} interface */ @Override public int count() { setCalledMethod("GET /" + PROFILES); try { ISProfile isProfile = new ISProfile(); return isProfile.count(); } catch(WebApplicationException e) { throw e; } catch(Exception e) { throw new InternalServerErrorException(e); } } /** * Returns the list of profiles name available for the * context of the request * (i.e. the context where the token has been generated). * * @param count (Default:false) If count=true the API returns total number of profile instead of the list. * @return a JSON Array. * * @pathExample /profiles * @responseExample application/json;charset=UTF-8 ["EmptyProfile","TestProfile",...,"ComplexProfile"] * * @pathExample /profiles?count=true * @responseExample application/json;charset=UTF-8 {"count":5} * */ @GET @Produces(MediaType.APPLICATION_JSON) public String listOrCount(@QueryParam(GCatConstants.COUNT_QUERY_PARAMETER) @DefaultValue("false") Boolean count) { setCalledMethod("GET /" + PROFILES); try { ISProfile isProfile = new ISProfile(); if(count) { return createCountJson(isProfile.count()); }else{ ArrayNode arrayNode = isProfile.list(); return isProfile.getMapper().writeValueAsString(arrayNode); } } catch(WebApplicationException e) { throw e; } catch(Exception e) { throw new InternalServerErrorException(e); } } /* * Not used as REST method, implemented to respect {@link org.gcube.gcat.api.interfaces.Item} interface */ @Override public String list() { try { ISProfile isProfile = new ISProfile(); ArrayNode arrayNode = isProfile.list(); return isProfile.getMapper().writeValueAsString(arrayNode); } catch(WebApplicationException e) { throw e; } catch(Exception e) { throw new InternalServerErrorException(e); } } public static int PRETTY_PRINT_INDENT_FACTOR = 4; /** * This API allow to read a profile definition.
* * This API return by default the content in XML. *

* It is possible reading a profile in JSON by specifing the HTTP Header:
* Accept: application/json *

* * @param name the name of the profile * @return the profile definition * * @pathExample /profiles/EmptyProfile * @responseExample application/xml classpath:/api-docs-examples/profile/read-profile-response.xml * */ @GET @Path("/{" + PROFILE_NAME_PARAMETER + "}") @Produces({MediaType.APPLICATION_XML}) @StatusCodes ({ @ResponseCode ( code = 200, condition = "The pprofile exists.") }) @Override public String read(@PathParam(PROFILE_NAME_PARAMETER) String name) { setCalledMethod("GET /" + PROFILES + "/{" + PROFILE_NAME_PARAMETER + "}"); try { // If the name is SCHEMA if(name.compareToIgnoreCase(SCHEMA) == 0) { return DataCalogueMetadataFormatReader.getProfileSchemaString(); } String accept = httpHeaders.getHeaderString("Accept"); ISProfile isProfile = new ISProfile(); boolean xml = true; if(accept.startsWith(MediaType.APPLICATION_JSON)) { xml = false; } return isProfile.read(name, xml); } catch(WebApplicationException e) { throw e; } catch(Exception e) { throw new InternalServerErrorException(e); } } @PUT @Path("/{" + PROFILE_NAME_PARAMETER + "}") @Consumes(MediaType.APPLICATION_XML) @Produces(MediaType.APPLICATION_XML) // @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class) public Response createOrUpdate(@PathParam(PROFILE_NAME_PARAMETER) String name, String xml) { setCalledMethod("PUT /" + PROFILES + "/{" + PROFILE_NAME_PARAMETER + "}"); try { if(name.compareToIgnoreCase(SCHEMA) == 0) { throw new NotAllowedException(CANNOT_MANAGE_PROFILE_SCHEMA, new Throwable(CANNOT_MANAGE_PROFILE_SCHEMA), HttpMethod.GET.toString(), HttpMethod.HEAD.toString()); } ISProfile isProfile = new ISProfile(); boolean created = isProfile.createOrUpdate(name, xml); ResponseBuilder responseBuilder = null; if(created) { responseBuilder = Response.status(Status.CREATED); responseBuilder.header(LOCATION_HEADER, uriInfo.getAbsolutePath()); } else { responseBuilder = Response.status(Status.OK); } responseBuilder.entity(xml); return responseBuilder.type(MediaType.APPLICATION_XML).build(); } catch(WebApplicationException e) { throw e; } catch(SAXException e) { String schemaURL = uriInfo.getRequestUri().toString().replace(name, SCHEMA); throw new BadRequestException(String.format(PROFILE_VALIDATION_ERROR, e.getMessage(), schemaURL)); } catch(Exception e) { throw new InternalServerErrorException(e); } } @DELETE @Path("/{" + PROFILE_NAME_PARAMETER + "}") // @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class) public Response delete(@PathParam(PROFILE_NAME_PARAMETER) String name) { setCalledMethod("DELETE /" + PROFILES + "/{" + PROFILE_NAME_PARAMETER + "}"); try { if(name.compareToIgnoreCase(SCHEMA) == 0) { throw new NotAllowedException(CANNOT_MANAGE_PROFILE_SCHEMA, new Throwable(CANNOT_MANAGE_PROFILE_SCHEMA), HttpMethod.GET.toString(), HttpMethod.HEAD.toString()); } ISProfile isProfile = new ISProfile(); isProfile.delete(name); return Response.status(Status.NO_CONTENT).build(); } catch(WebApplicationException e) { throw e; } catch(Exception e) { throw new InternalServerErrorException(e); } } @Override public Response create(String name, String xml) { return createOrUpdate(name, xml); } @Override public String update(String name, String xml) { return createOrUpdate(name, xml).getEntity().toString(); } }