/* * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.hawkular.inventory.rest; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static org.hawkular.inventory.rest.Utils.getSegmentTypeFromSimpleName; import java.io.IOException; import java.io.Reader; import java.util.Collection; import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.hawkular.inventory.api.Inventory; import org.hawkular.inventory.api.Relationships; import org.hawkular.inventory.api.ResolvableToSingle; import org.hawkular.inventory.api.Synced; import org.hawkular.inventory.api.model.AbstractElement; import org.hawkular.inventory.api.model.Change; import org.hawkular.inventory.api.model.Relationship; import org.hawkular.inventory.api.model.SyncHash; import org.hawkular.inventory.api.model.Syncable; import org.hawkular.inventory.paths.CanonicalPath; import org.hawkular.inventory.paths.SegmentType; import org.jboss.resteasy.spi.BadRequestException; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; /** * @author Lukas Krejci * @since 0.16.0 */ @Path("/entity") @Produces(APPLICATION_JSON) @Consumes(APPLICATION_JSON) public class RestEntity extends RestBase { public RestEntity() { super("/entity".length()); } @GET @Path("{path:.+}/treeHash") @SuppressWarnings("unchecked") public SyncHash.Tree getTreeHash(@Context UriInfo uriInfo) throws Exception { CanonicalPath path = CanonicalPath.fromPartiallyUntypedString(getPath(uriInfo, "/treeHash".length()), getTenantPath(), AbstractElement.class); Class<?> eltType = Inventory.types().byPath(path).getElementType(); if (!Syncable.class.isAssignableFrom(eltType)) { throw new BadRequestException("Element not syncable - cannot get treeHash for path " + path); } return inventory(uriInfo).inspect(path, Synced.Single.class).treeHash(); } @GET @Path("{path:.+}") @SuppressWarnings("unchecked") public Object get(@Context UriInfo uriInfo) throws Exception { CanonicalPath path = CanonicalPath.fromPartiallyUntypedString(getPath(uriInfo), getTenantPath(), AbstractElement.class); return inventory(uriInfo).inspect(path, ResolvableToSingle.class).entity(); } @GET @Path("{path:.+}/history") @SuppressWarnings("unchecked") public List<Change<?>> getHistory(@Context UriInfo uriInfo) { CanonicalPath path = CanonicalPath.fromPartiallyUntypedString(getPath(uriInfo, "/history".length()), getTenantPath(), AbstractElement.class); return getHistory(uriInfo, path); } @POST @Path("{path:.+}") @SuppressWarnings("unchecked") public Response post(@Context UriInfo uriInfo, Reader input) throws Exception { String pathAndType = getPath(uriInfo); int slashIdx = pathAndType.lastIndexOf('/'); String parent = pathAndType.substring(0, slashIdx); String entityType = pathAndType.substring(slashIdx + 1); SegmentType st = getSegmentTypeFromSimpleName(entityType); if (parent.isEmpty()) { parent = "/"; } CanonicalPath parentPath = CanonicalPath.fromPartiallyUntypedString(parent, getTenantPath(), AbstractElement.class); if (st == SegmentType.rl) { if (!security.canAssociateFrom(parentPath)) { return Response.status(Response.Status.FORBIDDEN).build(); } } else { if (!security.canCreate(Inventory.types().bySegment(st).getElementType()).under(parentPath)) { return Response.status(Response.Status.FORBIDDEN).build(); } } Object toReport = create(parentPath, st, uriInfo, input); if (toReport instanceof Collection) { return ResponseUtil.created((Collection<? extends AbstractElement<?, ?>>) toReport, uriInfo).build(); } else { return ResponseUtil.created((AbstractElement<?, ?>) toReport, uriInfo).build(); } } @PUT @Path("{path:.+}") public Response put(@Context UriInfo uriInfo, Reader input) throws Exception { String path = getPath(uriInfo); CanonicalPath entityPath = CanonicalPath.fromPartiallyUntypedString(path, getTenantPath(), AbstractElement.class); if (!security.canUpdate(entityPath)) { return Response.status(Response.Status.FORBIDDEN).build(); } Class<? extends AbstractElement.Update> updateType = Inventory.types().byPath(entityPath).getUpdateType(); doPut(entityPath, updateType, uriInfo, input); return Response.noContent().build(); } @DELETE @Path("{path:.+}") @ApiOperation("Deletes an inventory entity on the given location.") @ApiResponses({ @ApiResponse(code = 204, message = "Entity deleted."), @ApiResponse(code = 404, message = "No entity found on given traversal URI."), @ApiResponse(code = 500, message = "Internal server error") }) public Response delete(@Context UriInfo uriInfo) { String path = getPath(uriInfo); CanonicalPath entityPath = CanonicalPath.fromPartiallyUntypedString(path, getTenantPath(), AbstractElement.class); Inventory inventory = inventory(uriInfo); if (entityPath.getSegment().getElementType() == SegmentType.rl) { Relationship rl = inventory.inspect(entityPath, Relationships.Single.class).entity(); if (!security.canAssociateFrom(rl.getSource())) { return Response.status(Response.Status.FORBIDDEN).build(); } } else { if (!security.canDelete(entityPath)) { return Response.status(Response.Status.FORBIDDEN).build(); } } inventory.inspect(entityPath, Inventory.types().byPath(entityPath).getSingleAccessorType()).delete(); return Response.noContent().build(); } private <U extends AbstractElement.Update> void doPut(CanonicalPath path, Class<U> updateType, UriInfo uriInfo, Reader data) throws IOException { setupMapper(path); U update = getMapper().reader().forType(updateType).readValue(data); inventory(uriInfo).inspect(path, Inventory.types().byUpdate(updateType).getSingleAccessorType()).update(update); } }