/******************************************************************************* | |
* Copyright (c) 2020 Contributors to the Eclipse Foundation | |
* | |
* See the NOTICE file(s) distributed with this work for additional | |
* information regarding copyright ownership. | |
* | |
* This program and the accompanying materials are made available under the | |
* terms of the Eclipse Public License v. 2.0 which is available at | |
* http://www.eclipse.org/legal/epl-2.0. | |
* | |
* SPDX-License-Identifier: EPL-2.0 | |
*******************************************************************************/ | |
package org.eclipse.mdm.businessobjects.boundary; | |
import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ATTRIBUTENAME; | |
import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_CONTEXTCOMPONENTNAME; | |
import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_CONTEXTTYPE; | |
import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID; | |
import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_REMOTEPATH; | |
import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME; | |
import static org.eclipse.mdm.businessobjects.service.EntityService.V; | |
import java.io.InputStream; | |
import javax.ejb.EJB; | |
import javax.ws.rs.Consumes; | |
import javax.ws.rs.DELETE; | |
import javax.ws.rs.GET; | |
import javax.ws.rs.POST; | |
import javax.ws.rs.Path; | |
import javax.ws.rs.PathParam; | |
import javax.ws.rs.Produces; | |
import javax.ws.rs.core.MediaType; | |
import javax.ws.rs.core.Response; | |
import javax.ws.rs.core.Response.Status; | |
import org.eclipse.mdm.api.base.model.ContextDescribable; | |
import org.eclipse.mdm.api.base.model.ContextType; | |
import org.eclipse.mdm.api.base.model.Environment; | |
import org.eclipse.mdm.api.base.model.FileLink; | |
import org.eclipse.mdm.api.base.model.MimeType; | |
import org.eclipse.mdm.api.base.model.TestStep; | |
import org.eclipse.mdm.businessobjects.control.FileLinkActivity; | |
import org.eclipse.mdm.businessobjects.entity.FileSize; | |
import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse; | |
import org.eclipse.mdm.businessobjects.entity.MDMFileLink; | |
import org.eclipse.mdm.businessobjects.service.EntityService; | |
import org.eclipse.mdm.businessobjects.utils.Serializer; | |
import org.eclipse.mdm.businessobjects.utils.ServiceUtils; | |
import org.glassfish.jersey.media.multipart.FormDataContentDisposition; | |
import org.glassfish.jersey.media.multipart.FormDataParam; | |
import io.swagger.v3.oas.annotations.Operation; | |
import io.swagger.v3.oas.annotations.Parameter; | |
import io.swagger.v3.oas.annotations.media.Content; | |
import io.swagger.v3.oas.annotations.media.Schema; | |
import io.swagger.v3.oas.annotations.responses.ApiResponse; | |
import io.vavr.Tuple; | |
/** | |
* context files subresource | |
* | |
* @author Johannes Stamm, peak-Solution GmbH | |
* | |
*/ | |
public class ContextFilesSubresource { | |
@EJB | |
private FileLinkActivity fileLinkActivity; | |
@EJB | |
private EntityService entityService; | |
private Class<? extends ContextDescribable> contextDescribableClass; | |
/** | |
* Set the Entity class of the parent resource. This method has to be called | |
* right after the resource was initialized by the container. The entity class | |
* is used to distinguish the type of the parent resource. | |
* | |
* This method is package-private to workaround an issue in WELD complaining | |
* about: Parameter 1 of type ... is not resolvable to a concrete type. | |
* | |
* @param contextDescribableClass the Entity class of the parent resource | |
*/ | |
void setEntityClass(Class<? extends ContextDescribable> contextDescribableClass) { | |
this.contextDescribableClass = contextDescribableClass; | |
} | |
/** | |
* Creates new {@link FileLink} for {@link TestStep}. | |
* | |
* @param sourceName name of the source (MDM {@link Environment} name) | |
* @param body The {@link FileLink} to create. | |
* @return | |
*/ | |
@POST | |
@Operation(summary = "Creates a new file on a context attribute.", responses = { | |
@ApiResponse(description = "The stored file link.", content = @Content(schema = @Schema(implementation = MDMFileLink.class))), | |
@ApiResponse(responseCode = "400", description = "Error") }) | |
@Produces(MediaType.APPLICATION_JSON) | |
@Consumes(MediaType.MULTIPART_FORM_DATA) | |
public Response createFile( | |
@Parameter(description = "Name of the MDM datasource", required = true) @PathParam(REQUESTPARAM_SOURCENAME) String sourceName, | |
@Parameter(description = "ID of the parent ContextDescribable", required = true) @PathParam(REQUESTPARAM_ID) String contextDescribableId, | |
@Parameter(description = "ContextType of the Component holding the attribute with the FileLink", required = true) @PathParam(REQUESTPARAM_CONTEXTTYPE) ContextType contextType, | |
@Parameter(description = "Name of the ContextComponent holding the attribute with the FileLink", required = true) @PathParam(REQUESTPARAM_CONTEXTCOMPONENTNAME) String contextComponentName, | |
@Parameter(description = "Name of the Attribute holding the FileLink", required = true) @PathParam(REQUESTPARAM_ATTRIBUTENAME) String attributeName, | |
@Parameter(description = "InputStream containing file data", required = true) @FormDataParam("file") InputStream fileInputStream, | |
@Parameter(description = "Meta data describing file", required = true) @FormDataParam("file") FormDataContentDisposition cdh, | |
@Parameter(description = "File description", required = true) @FormDataParam("description") String description, | |
@Parameter(description = "Mimetype of the file", required = true) @FormDataParam("mimeType") MimeType mimeType) { | |
return entityService.find(V(sourceName), contextDescribableClass, V(contextDescribableId)) | |
.map(contextDescribable -> fileLinkActivity.createFile(sourceName, contextDescribable, | |
cdh.getFileName(), fileInputStream, description, mimeType, contextType, contextComponentName, | |
attributeName)) | |
.map(fileLink -> ServiceUtils.toResponse(Serializer.serializeFileLink(fileLink), Status.OK)) | |
.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER).getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE); | |
} | |
@GET | |
@Operation(summary = "Stream the contents of a file link", responses = { | |
@ApiResponse(description = "The content of the file link as a stream. MimeType is set to that of the file link.", content = @Content(schema = @Schema(implementation = MDMEntityResponse.class))), | |
@ApiResponse(responseCode = "400", description = "Invalid ID or remote path supplied") }) | |
@Path("/{" + REQUESTPARAM_REMOTEPATH + "}") | |
public Response streamFileLink( | |
@Parameter(description = "Name of the MDM datasource", required = true) @PathParam(REQUESTPARAM_SOURCENAME) String sourceName, | |
@Parameter(description = "ID of the parent ContextDescribable", required = true) @PathParam(REQUESTPARAM_ID) String contextDescribableId, | |
@Parameter(description = "ContextType of the Component holding the attribute with the FileLink", required = true) @PathParam(REQUESTPARAM_CONTEXTTYPE) ContextType contextType, | |
@Parameter(description = "Name of the ContextComponent holding the attribute with the FileLink", required = true) @PathParam(REQUESTPARAM_CONTEXTCOMPONENTNAME) String contextComponentName, | |
@Parameter(description = "Name of the Attribute holding the FileLink", required = true) @PathParam(REQUESTPARAM_ATTRIBUTENAME) String attributeName, | |
@Parameter(description = "The remote path of the file link whose content is to be retrieved", required = true) @PathParam(REQUESTPARAM_REMOTEPATH) String remotePath) { | |
return entityService.find(V(sourceName), contextDescribableClass, V(contextDescribableId)) | |
.map(contextDescribable -> Tuple.of(contextDescribable, | |
fileLinkActivity.findFileLinkInContext(remotePath, sourceName, contextDescribable, contextType, | |
contextComponentName, attributeName))) | |
.map(tuple -> Tuple.of(fileLinkActivity.toStreamingOutput(sourceName, tuple._1, tuple._2), | |
tuple._2.getMimeType().toString())) | |
.map(tuple -> Response.ok(tuple._1, tuple._2).build()).recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) | |
.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE); | |
} | |
@GET | |
@Operation(summary = "Returns the size of a file in bytes.", responses = { | |
@ApiResponse(description = "The file size of the requested remote path.", content = @Content(schema = @Schema(implementation = FileSize.class))), | |
@ApiResponse(responseCode = "400", description = "Error") }) | |
@Produces(MediaType.APPLICATION_JSON) | |
@Path("/size/{" + REQUESTPARAM_REMOTEPATH + "}") | |
public Response loadFileSize( | |
@Parameter(description = "Name of the MDM datasource", required = true) @PathParam(REQUESTPARAM_SOURCENAME) String sourceName, | |
@Parameter(description = "ID of the parent ContextDescribable", required = true) @PathParam(REQUESTPARAM_ID) String contextDescribableId, | |
@Parameter(description = "The remote path of the file link whose content is to be retrieved", required = true) @PathParam(REQUESTPARAM_REMOTEPATH) String remotePath) { | |
return entityService.find(V(sourceName), contextDescribableClass, V(contextDescribableId)) | |
.map(contextDescribabale -> fileLinkActivity.loadFileSize(sourceName, contextDescribabale, | |
FileLink.newRemote(remotePath, null, null))) | |
.map(fileSize -> Response.ok(fileSize).build()).recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) | |
.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE); | |
} | |
/** | |
* Deletes attached file from {@link ContextDescirbable}. | |
* | |
* @param sourceName name of the source (MDM {@link Environment} name) | |
* @param id The identifier of the {@link ContextDescirbable} which | |
* contains the {@link FileLink} | |
* @param remotePath The remote path of the {@link FileLink} to delete. | |
* @return | |
*/ | |
@DELETE | |
@Operation(summary = "Deletes an attached file from a context attribute.", responses = { | |
@ApiResponse(description = "The deleted file link", content = @Content(schema = @Schema(implementation = MDMFileLink.class))), | |
@ApiResponse(responseCode = "400", description = "Error") }) | |
@Produces(MediaType.APPLICATION_JSON) | |
@Path("/{" + REQUESTPARAM_REMOTEPATH + "}") | |
public Response deleteFile( | |
@Parameter(description = "Name of the MDM datasource", required = true) @PathParam(REQUESTPARAM_SOURCENAME) String sourceName, | |
@Parameter(description = "ID of the parent ContextDescribable", required = true) @PathParam(REQUESTPARAM_ID) String contextDescribableId, | |
@Parameter(description = "ContextType of the Component holding the attribute with the FileLink", required = true) @PathParam(REQUESTPARAM_CONTEXTTYPE) ContextType contextType, | |
@Parameter(description = "Name of the ContextComponent holding the attribute with the FileLink", required = true) @PathParam(REQUESTPARAM_CONTEXTCOMPONENTNAME) String contextComponentName, | |
@Parameter(description = "Name of the Attribute holding the FileLink", required = true) @PathParam(REQUESTPARAM_ATTRIBUTENAME) String attributeName, | |
@Parameter(description = "The remote path of the file link whose content is to be retrieved", required = true) @PathParam(REQUESTPARAM_REMOTEPATH) String remotePath) { | |
return entityService.find(V(sourceName), contextDescribableClass, V(contextDescribableId)) | |
.map(contextDescribable -> fileLinkActivity.deleteFileLink(sourceName, contextDescribable, contextType, | |
contextComponentName, attributeName, remotePath)) | |
.map(fileLink -> ServiceUtils.toResponse(Serializer.serializeFileLink(fileLink), Status.OK)) | |
.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER).getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE); | |
} | |
} |