Skip to content

Commit

Permalink
Issue #2912 Support folder browsing in share srv
Browse files Browse the repository at this point in the history
  • Loading branch information
mzueva committed Nov 9, 2022
1 parent 68553c4 commit b18faa9
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.epam.pipeline.entity.datastorage.DataStorageException;
import com.epam.pipeline.entity.datastorage.DataStorageFile;
import com.epam.pipeline.entity.datastorage.DataStorageItemContent;
import com.epam.pipeline.entity.datastorage.DataStorageItemType;
import com.epam.pipeline.entity.datastorage.DataStorageListing;
import com.epam.pipeline.entity.datastorage.DataStorageStreamingContent;
import com.epam.pipeline.entity.datastorage.DataStorageWithShareMount;
Expand Down Expand Up @@ -369,4 +370,14 @@ public void callOffDataStorageDavMount(final Long id) {
public void updateStorageUsage(final String id) {
eventsService.ifPresent(s -> s.addReindexEvent(id));
}

@PreAuthorize(AclExpressions.STORAGE_ID_READ)
public DataStorageItemType getItemType(final Long id, final String path, final String version) {
return dataStorageManager.getItemType(id, path, version);
}

@PreAuthorize(AclExpressions.STORAGE_ID_OWNER)
public DataStorageItemType getItemTypeOwner(final Long id, final String path, final String version) {
return dataStorageManager.getItemType(id, path, version);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,26 @@ public Result<DataStorageDownloadFileUrl> generateDataStorageItemUrl(
}
}

@RequestMapping(value = "/datastorage/{id}/type", method = RequestMethod.GET)
@ResponseBody
@ApiOperation(
value = "Returns data storage item's type.",
notes = "Returns data storage item's type",
produces = MediaType.APPLICATION_JSON_VALUE)
@ApiResponses(
value = {@ApiResponse(code = HTTP_STATUS_OK, message = API_STATUS_DESCRIPTION)
})
public Result<DataStorageItemType> getStorageItemType(
@PathVariable(value = ID) final Long id,
@RequestParam(value = PATH) final String path,
@RequestParam(value = VERSION, required = false) final String version) {
if (StringUtils.hasText(version)) {
return Result.success(dataStorageApiService.getItemTypeOwner(id, path, version));
} else {
return Result.success(dataStorageApiService.getItemType(id, path, version));
}
}

@GetMapping(value = "/datastorage/{id}/generateUploadUrl")
@ResponseBody
@ApiOperation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,12 @@ public void checkDataStorageObjectExists(final AbstractDataStorage dataStorage,
path, dataStorage.getRoot())));
}

public DataStorageItemType getItemType(final Long id,
final String path,
final String version) {
return storageProviderManager.getItemType(load(id), path, version);
}

public DataStorageItemType getItemType(final AbstractDataStorage dataStorage,
final String path,
final String version) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.epam.pipeline.rest.Result;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;

import java.io.InputStream;

Expand All @@ -15,4 +16,6 @@ public interface CloudPipelineApiExecutor {
byte[] getByteResponse(Call<byte[]> call);

InputStream getResponseStream(Call<ResponseBody> call);

Response<ResponseBody> getResponse(Call<ResponseBody> call);
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,17 @@ public InputStream getResponseStream(final Call<ResponseBody> call) {
}
}

@Override
public Response<ResponseBody> getResponse(final Call<ResponseBody> call) {
try {
final Response<ResponseBody> response = call.execute();
validateResponseStatus(response);
return response;
} catch (IOException e) {
throw new PipelineResponseIOException(e);
}
}

private byte[] internalGetByteResponse(final Call<byte[]> call) throws IOException {
try {
final Response<byte[]> response = call.execute();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,27 @@
package com.epam.pipeline.external.datastorage.controller.resource;

import com.epam.pipeline.external.datastorage.controller.AbstractRestController;
import com.epam.pipeline.external.datastorage.exception.InvalidPathException;
import com.epam.pipeline.external.datastorage.manager.preference.PreferenceService;
import com.epam.pipeline.external.datastorage.manager.resource.StaticResourcesService;
import com.fasterxml.jackson.core.type.TypeReference;
import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import okhttp3.Headers;
import okhttp3.ResponseBody;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import retrofit2.Response;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

@RestController
Expand All @@ -46,23 +46,35 @@
@RequiredArgsConstructor
public class StaticResourcesController extends AbstractRestController {

private static final FileNameMap FILE_NAME_MAP = URLConnection.getFileNameMap();
private static final String STATIC_RESOURCES = "/static-resources/";
private static final String UI_STORAGE_STATIC_PREVIEW_MASK = "ui.storage.static.preview.mask";
private static final String DATA_SHARING_STATIC_RESOURCE_HEADERS = "data.sharing.static.resource.headers";
private static final String CONTENT_DISPOSITION = "Content-Disposition";
private static final String CONTENT_TYPE = "Content-Type";

private final StaticResourcesService resourcesService;
private final PreferenceService preferenceService;

@GetMapping(value = "/**")
public void getStaticFile(final HttpServletRequest request, final HttpServletResponse response)
throws IOException {
final String path = request.getPathInfo().replaceFirst(STATIC_RESOURCES, "");
final String fileName = FilenameUtils.getName(path);
final MediaType mediaType = getMediaType(fileName);
final InputStream content = resourcesService.getContent(path);
writeStreamToResponse(response, content, fileName, mediaType,
!MediaType.APPLICATION_OCTET_STREAM.equals(mediaType), getCustomHeaders(fileName));
try {
final String path = request.getPathInfo().replaceFirst(STATIC_RESOURCES, "");
final Response<ResponseBody> content = resourcesService.getContent(path);
final String fileName = FilenameUtils.getName(path);

final Headers responseHeaders = content.headers();
final String contentDisposition = responseHeaders.get(CONTENT_DISPOSITION);
final MediaType mediaType = MediaType.parseMediaType(responseHeaders.get(CONTENT_TYPE));
final Map<String, String> headers = getCustomHeaders(fileName);

headers.put(CONTENT_DISPOSITION, contentDisposition);
headers.put(CONTENT_TYPE, mediaType.getType());
writeStreamToResponse(response, content.body().byteStream(), fileName, mediaType,
!MediaType.APPLICATION_OCTET_STREAM.equals(mediaType), getCustomHeaders(fileName));
} catch (InvalidPathException e) {
response.setHeader("Location", request.getRequestURI() + "/");
response.setStatus(HttpStatus.FOUND.value());
}
}

private Map<String, String> getCustomHeaders(final String fileName) {
Expand All @@ -71,25 +83,8 @@ private Map<String, String> getCustomHeaders(final String fileName) {
new TypeReference<Map<String, Map<String, String>>>() {});
final String extension = FilenameUtils.getExtension(fileName);
if (MapUtils.isEmpty(headers) || !headers.containsKey(extension)) {
return Collections.emptyMap();
return new HashMap<>();
}
return headers.get(extension);
}

private MediaType getMediaType(final String fileName) {
final String preference = preferenceService.getValue(UI_STORAGE_STATIC_PREVIEW_MASK);
if (StringUtils.isBlank(preference)) {
return MediaType.APPLICATION_OCTET_STREAM;
}
final String[] supportedExtensions =preference.split(",");
final String extension = FilenameUtils.getExtension(fileName);
return Arrays.stream(supportedExtensions)
.filter(ext -> ext.trim().equalsIgnoreCase(extension))
.findFirst()
.map(ext -> {
final String mimeType = FILE_NAME_MAP.getContentTypeFor(fileName);
return MediaType.parseMediaType(mimeType);
})
.orElse(MediaType.APPLICATION_OCTET_STREAM);
return new HashMap<>(headers.get(extension));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.epam.pipeline.entity.datastorage.DataStorageDownloadFileUrl;
import com.epam.pipeline.entity.datastorage.DataStorageFile;
import com.epam.pipeline.entity.datastorage.DataStorageItemContent;
import com.epam.pipeline.entity.datastorage.DataStorageItemType;
import com.epam.pipeline.entity.datastorage.DataStorageListing;
import com.epam.pipeline.entity.datastorage.TemporaryCredentials;
import com.epam.pipeline.rest.Result;
Expand All @@ -50,6 +51,9 @@ public interface PipelineDataStorageClient {
String SHOW_VERSION = "showVersion";
String AUTHORIZATION = "Authorization";

@GET("restapi/datastorage/findByPath")
Call<Result<AbstractDataStorage>> getStorage(@Query(ID) String id, @Header(AUTHORIZATION) String token);

@GET("restapi/datastorage/{id}/load")
Call<Result<AbstractDataStorage>> getStorage(@Path(ID) long id, @Header(AUTHORIZATION) String token);

Expand Down Expand Up @@ -140,4 +144,9 @@ Call<ResponseBody> downloadFile(@Path(ID) long id,
@Query(PATH) String path,
@Query(VERSION) String version,
@Header(AUTHORIZATION) String token);

@GET("restapi/datastorage/{id}/type")
Call<Result<DataStorageItemType>> getItemType(@Path(ID) Long storageId,
@Query(PATH) String path,
@Header(AUTHORIZATION) String token);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,61 @@
package com.epam.pipeline.external.datastorage.manager.resource;

import com.epam.pipeline.client.pipeline.CloudPipelineApiExecutor;
import com.epam.pipeline.entity.datastorage.AbstractDataStorage;
import com.epam.pipeline.entity.datastorage.DataStorageItemType;
import com.epam.pipeline.exception.ObjectNotFoundException;
import com.epam.pipeline.external.datastorage.exception.InvalidPathException;
import com.epam.pipeline.external.datastorage.exception.ResourceNotFoundException;
import com.epam.pipeline.external.datastorage.manager.CloudPipelineApiBuilder;
import com.epam.pipeline.external.datastorage.manager.auth.PipelineAuthManager;
import com.epam.pipeline.external.datastorage.manager.datastorage.PipelineDataStorageClient;
import lombok.SneakyThrows;
import okhttp3.ResponseBody;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriUtils;
import retrofit2.Response;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;

@Service
public class StaticResourcesService {

public static final String DELIMITER = "/";
private final PipelineAuthManager authManager;
private final StaticResourcesClient client;
private final PipelineDataStorageClient storageClient;
private final CloudPipelineApiExecutor apiExecutor;

public StaticResourcesService(final PipelineAuthManager authManager,
final CloudPipelineApiBuilder builder,
final CloudPipelineApiExecutor apiExecutor) {
this.authManager = authManager;
this.client = builder.getClient(StaticResourcesClient.class);
this.storageClient = builder.getClient(PipelineDataStorageClient.class);
this.apiExecutor = apiExecutor;
}

@SneakyThrows
public InputStream getContent(final String path) {
public Response<ResponseBody> getContent(final String path) {
try {
return apiExecutor.getResponseStream(client.getContent(
UriUtils.encodePath(path, StandardCharsets.UTF_8.displayName()), authManager.getHeader()));
final String header = authManager.getHeader();
if (!path.endsWith(DELIMITER)) {
validateStorageItem(path, header);
}
return apiExecutor.getResponse(client.getContent(
UriUtils.encodePath(path, StandardCharsets.UTF_8.displayName()), header));
} catch (ObjectNotFoundException e) {
throw new ResourceNotFoundException(String.format("Storage path '%s' does not exist.", path));
}
}

private void validateStorageItem(final String requestPath, final String header) {
final AbstractDataStorage storage = apiExecutor.execute(storageClient.getStorage(requestPath, header));
final String[] split = requestPath.split(DELIMITER, 2);
final String path = split.length == 1 ? "" : split[1];
final DataStorageItemType type = apiExecutor.execute(storageClient.getItemType(storage.getId(), path, header));
if (type == DataStorageItemType.Folder && !requestPath.endsWith(DELIMITER)) {
throw new InvalidPathException("Folder path shall end with slash.");
}
}
}

0 comments on commit b18faa9

Please sign in to comment.