package nl.knaw.huygens.alexandria.endpoint.annotation; /* * #%L * alexandria-main * ======= * Copyright (C) 2015 - 2017 Huygens ING (KNAW) * ======= * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import java.util.UUID; import java.util.function.Supplier; import javax.inject.Inject; import javax.validation.constraints.NotNull; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import nl.knaw.huygens.alexandria.api.EndpointPaths; import nl.knaw.huygens.alexandria.api.model.AlexandriaState; import nl.knaw.huygens.alexandria.api.model.StatePrototype; import nl.knaw.huygens.alexandria.endpoint.JSONEndpoint; import nl.knaw.huygens.alexandria.endpoint.UUIDParam; import nl.knaw.huygens.alexandria.exception.ConflictException; import nl.knaw.huygens.alexandria.exception.NotFoundException; import nl.knaw.huygens.alexandria.exception.TentativeObjectException; import nl.knaw.huygens.alexandria.model.AlexandriaAnnotation; import nl.knaw.huygens.alexandria.service.AlexandriaService; @Path(EndpointPaths.ANNOTATIONS) @Api("annotations") public class AnnotationsEndpoint extends JSONEndpoint { private final AlexandriaService service; private final AnnotationEntityBuilder entityBuilder; private final AnnotationDeprecationRequestBuilder requestBuilder; @Inject public AnnotationsEndpoint(AlexandriaService service, // AnnotationEntityBuilder entityBuilder, // AnnotationDeprecationRequestBuilder requestBuilder) { this.service = service; this.entityBuilder = entityBuilder; this.requestBuilder = requestBuilder; } static Supplier<NotFoundException> annotationNotFoundForId(Object id) { return () -> new NotFoundException(NoAnnotationFoundWithId(id)); } private static String NoAnnotationFoundWithId(Object id) { return "No annotation found with id " + id; } private static Supplier<NotFoundException> annotationNotFoundForIdAndRevision(Object id, Integer revision) { return () -> new NotFoundException(NoAnnotationFoundWithId(id) + ", revision " + revision); } static WebApplicationException annotationIsTentative(UUID uuid) { return new TentativeObjectException("annotation " + uuid + " is tentative, please confirm it first"); } @GET @Path("{uuid}") @ApiOperation(value = "get the annotation", response = AnnotationEntity.class) public Response readAnnotation(@PathParam("uuid") UUIDParam uuidParam) { return ok(readExistingAnnotation(uuidParam)); } // Sub-resource delegation @GET @Path("{uuid}/" + EndpointPaths.REV + "/{revision}") @ApiOperation(value = "get the given revision of the annotation", response = AnnotationEntity.class) public Response readVersionedAnnotation(@PathParam("uuid") UUIDParam uuidParam, // @PathParam("revision") Integer revision) { return ok(readExistingAnnotationWithIdAndRevision(uuidParam, revision)); } @PUT @Path("{uuid}") @Consumes(MediaType.APPLICATION_JSON) @ApiOperation(value // = "make a new annotation from the payload and use it to deprecate the annotation with the given uuid") public Response deprecateAnnotation(@PathParam("uuid") UUIDParam uuidParam, // @NotNull @OmittedOrMatchingType AnnotationPrototype prototype) { AlexandriaAnnotation annotation = readExistingAnnotation(uuidParam); prototype.setState(AlexandriaState.CONFIRMED); AnnotationDeprecationRequest request = requestBuilder.ofAnnotation(annotation).build(prototype); request.execute(service); return noContent(); } @DELETE @Path("{uuid}") public Response deleteAnnotation(@PathParam("uuid") final UUIDParam uuidParam) { AlexandriaAnnotation annotation = readExistingAnnotation(uuidParam); if (!annotation.getAnnotations().isEmpty()) { throw new ConflictException("annotation " + annotation.getId() + " still has annotations"); } service.deleteAnnotation(annotation); return noContent(); } @PUT @Path("{uuid}/state") @Consumes(MediaType.APPLICATION_JSON) @ApiOperation(value = "update the state of the annotation (only state=CONFIRMED accepted)") public Response setAnnotationState(@PathParam("uuid") final UUIDParam uuidParam, @NotNull StatePrototype protoType) { // Log.trace("protoType=[{}]", protoType); AlexandriaAnnotation annotation = readExistingAnnotation(uuidParam); if (protoType.isConfirmed()) { if (!annotation.isActive()) { throw new ConflictException(annotation.getState() + " annotations cannot be set to CONFIRMED"); } service.confirmAnnotation(annotation.getId()); return noContent(); } throw new ConflictException("Annotations can only be CONFIRMED via their /state endpoint"); } @Path("{uuid}/annotations") public Class<AnnotationAnnotationsEndpoint> getAnnotations() { return AnnotationAnnotationsEndpoint.class; // no instantiation of our own; let Jersey handle the lifecycle } @Path("{uuid}/provenance") public Class<AnnotationProvenanceEndpoint> getProvenance() { return AnnotationProvenanceEndpoint.class; // no instantiation of our own; let Jersey handle the lifecycle } private Response ok(AlexandriaAnnotation annotation) { return ok(entityBuilder.build(annotation)); } private AlexandriaAnnotation readExistingAnnotation(UUIDParam uuidParam) { return service.readAnnotation(uuidParam.getValue()) // .orElseThrow(annotationNotFoundForId(uuidParam)); } private AlexandriaAnnotation readExistingAnnotationWithIdAndRevision(UUIDParam uuidParam, int revision) { return service.readAnnotation(uuidParam.getValue(), revision) // .orElseThrow(annotationNotFoundForIdAndRevision(uuidParam, revision)); } }