Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 2912: Support folder browsing for static resources #2917

Merged
5 changes: 4 additions & 1 deletion api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ task copyLocalDtsStartupScripts(type: Copy) {
}

task generateComponentsVersions(type: Exec) {
commandLine "bash",
commandLine "bash",
"$project.rootDir/scripts/pipeline-scripts/generate_components_versions.sh",
"$project.rootDir",
"$rootDir/api/src/main/resources/static/components_versions.json"
Expand Down Expand Up @@ -249,6 +249,9 @@ dependencies {
compile('io.reflectoring.diffparser:diffparser:1.4')

compile 'dnsjava:dnsjava:3.4.3'

//Template engine
compile group: 'org.apache.velocity', name: 'velocity', version: '1.7'
}

// >>>>> processes profiles
Expand Down
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 @@ -830,8 +830,9 @@ public final class MessageConstants {
"error.ngs.preprocessing.sampleid.is.not.found";

// Static resource
public static final String ERROR_STATIC_RESOURCES_INVALID_PATH =
"error.static.resources.invalid.path";
public static final String ERROR_STATIC_RESOURCES_INVALID_PATH = "error.static.resources.invalid.path";
public static final String ERROR_STATIC_RESOURCES_FOLDER_PATH = "error.static.resources.folder.path";


private MessageConstants() {
// no-op
Expand Down
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 @@ -19,12 +19,15 @@
import com.epam.pipeline.acl.resource.StaticResourceApiService;
import com.epam.pipeline.controller.AbstractRestController;
import com.epam.pipeline.entity.datastorage.DataStorageStreamingContent;
import com.epam.pipeline.exception.InvalidPathException;
import com.epam.pipeline.manager.datastorage.providers.ProviderUtils;
import com.epam.pipeline.manager.preference.PreferenceManager;
import com.epam.pipeline.manager.preference.SystemPreferences;
import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -45,18 +48,25 @@ public class StaticResourcesController extends AbstractRestController {

private static final FileNameMap FILE_NAME_MAP = URLConnection.getFileNameMap();
private static final String STATIC_RESOURCES = "/static-resources/";

private final StaticResourceApiService resourcesService;
private final PreferenceManager preferenceManager;

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

writeStreamToResponse(response, content.getContent(), fileName, mediaType,
!MediaType.APPLICATION_OCTET_STREAM.equals(mediaType), getCustomHeaders(fileName));
} catch (InvalidPathException e) {
response.setHeader("Location", request.getRequestURI() + ProviderUtils.DELIMITER);
response.setStatus(HttpStatus.FOUND.value());
}
}

private Map<String, String> getCustomHeaders(final String fileName) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2022 EPAM Systems, Inc. (https://www.epam.com/)
*
* 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 com.epam.pipeline.exception;

public class InvalidPathException extends RuntimeException {

public InvalidPathException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,18 @@ 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) {
return storageProviderManager.getItemType(dataStorage, path, version);
}

private void assertToolsToMount(final DataStorageVO dataStorageVO) {
if (!CollectionUtils.isEmpty(dataStorageVO.getToolsToMount())) {
for (ToolFingerprint tool : dataStorageVO.getToolsToMount()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.epam.pipeline.entity.datastorage.DataStorageFile;
import com.epam.pipeline.entity.datastorage.DataStorageFolder;
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.DataStorageType;
Expand Down Expand Up @@ -249,4 +250,10 @@ public String verifyOrDefaultRestoreMode(final AbstractDataStorage dataStorage,
final StorageRestoreActionRequest restoreActionRequest) {
return getStorageProvider(dataStorage).verifyOrDefaultRestoreMode(restoreActionRequest);
}

public DataStorageItemType getItemType(final AbstractDataStorage dataStorage,
final String path,
final String version) {
return getStorageProvider(dataStorage).getItemType(dataStorage, path, version);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import com.epam.pipeline.entity.datastorage.DataStorageFile;
import com.epam.pipeline.entity.datastorage.DataStorageFolder;
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.DataStorageType;
Expand Down Expand Up @@ -138,4 +139,6 @@ default StoragePolicy buildPolicy(VersioningAwareRegion region, StoragePolicy st
void verifyRestoreActionSupported();

String verifyOrDefaultRestoreMode(StorageRestoreActionRequest restoreMode);

DataStorageItemType getItemType(T dataStorage, String path, String version);
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
import com.epam.pipeline.entity.datastorage.StoragePolicy;
import com.epam.pipeline.entity.datastorage.aws.S3bucketDataStorage;
import com.epam.pipeline.entity.region.AwsRegion;
import com.epam.pipeline.exception.ObjectNotFoundException;
import com.epam.pipeline.manager.datastorage.providers.ProviderUtils;
import com.epam.pipeline.utils.FileContentUtils;
import com.google.common.primitives.SignedBytes;
Expand Down Expand Up @@ -1113,7 +1114,7 @@ private DataStorageItemType getNonVersionType(AmazonS3 client, String bucket, St
} else if (!listing.getObjectSummaries().isEmpty()) {
return DataStorageItemType.File;
} else {
throw new IllegalArgumentException(messageHelper
throw new ObjectNotFoundException(messageHelper
.getMessage(MessageConstants.ERROR_DATASTORAGE_PATH_NOT_FOUND, path, bucket));
}
}
Expand All @@ -1130,7 +1131,7 @@ private DataStorageItemType getVersionType(AmazonS3 client, String bucket, Strin
} else if (!listing.getVersionSummaries().isEmpty()) {
return DataStorageItemType.File;
} else {
throw new IllegalArgumentException(messageHelper
throw new ObjectNotFoundException(messageHelper
.getMessage(MessageConstants.ERROR_DATASTORAGE_PATH_NOT_FOUND, path, bucket));
}
}
Expand Down Expand Up @@ -1217,6 +1218,12 @@ public static void validateFolderPathMatchingMasks(final S3bucketDataStorage dat
}
}

public DataStorageItemType getItemType(final String bucket,
final String path,
final String version) {
return checkItemType(getDefaultS3Client(), bucket, path, StringUtils.hasValue(version));
}

private static void validatePathMatchingMasks(final S3bucketDataStorage dataStorage, final String path) {
final Set<String> linkingMasks = dataStorage.getLinkingMasks();
if (CollectionUtils.isNotEmpty(linkingMasks)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import com.epam.pipeline.entity.datastorage.DataStorageFile;
import com.epam.pipeline.entity.datastorage.DataStorageFolder;
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.DataStorageType;
Expand Down Expand Up @@ -442,6 +443,14 @@ public S3Helper getS3Helper(final TemporaryCredentials credentials, final AwsReg
return new TemporaryCredentialsS3Helper(credentials, messageHelper, region);
}

@Override
public DataStorageItemType getItemType(final S3bucketDataStorage dataStorage,
final String path,
final String version) {
return getS3Helper(dataStorage).getItemType(dataStorage.getRoot(),
ProviderUtils.buildPath(dataStorage, path), version);
}

private AwsRegion getAwsRegion(S3bucketDataStorage dataStorage) {
return cloudRegionManager.getAwsRegion(dataStorage);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.epam.pipeline.entity.datastorage.DataStorageFile;
import com.epam.pipeline.entity.datastorage.DataStorageFolder;
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.DataStorageType;
Expand Down Expand Up @@ -297,6 +298,13 @@ public String verifyOrDefaultRestoreMode(final StorageRestoreActionRequest actio
throw new UnsupportedOperationException("Restore mechanism isn't supported for this provider.");
}

@Override
public DataStorageItemType getItemType(final AzureBlobStorage dataStorage,
final String path,
final String version) {
throw new UnsupportedOperationException();
}

private AzureStorageHelper getAzureStorageHelper(final AzureBlobStorage storage) {
final AzureRegion region = cloudRegionManager.getAzureRegion(storage);
final AzureRegionCredentials credentials = cloudRegionManager.loadCredentials(region);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.epam.pipeline.entity.datastorage.DataStorageFile;
import com.epam.pipeline.entity.datastorage.DataStorageFolder;
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.DataStorageType;
Expand Down Expand Up @@ -253,6 +254,13 @@ public String verifyOrDefaultRestoreMode(final StorageRestoreActionRequest actio
throw new UnsupportedOperationException("Restore mechanism isn't supported for this provider.");
}

@Override
public DataStorageItemType getItemType(final GSBucketStorage dataStorage,
final String path,
final String version) {
throw new UnsupportedOperationException();
}

private GSBucketStorageHelper getHelper(final GSBucketStorage storage) {
final GCPRegion gcpRegion = cloudRegionManager.getGCPRegion(storage);
return new GSBucketStorageHelper(messageHelper, gcpRegion, gcpClient);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.epam.pipeline.entity.datastorage.DataStorageFile;
import com.epam.pipeline.entity.datastorage.DataStorageFolder;
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.DataStorageType;
Expand Down Expand Up @@ -535,6 +536,13 @@ public String verifyOrDefaultRestoreMode(final StorageRestoreActionRequest actio
throw new UnsupportedOperationException("Restore mechanism isn't supported for this provider.");
}

@Override
public DataStorageItemType getItemType(final NFSDataStorage dataStorage,
final String path,
final String version) {
throw new UnsupportedOperationException();
}

private String encodeUrl(final String path) {
try {
return URLEncoder.encode(path, StandardCharsets.UTF_8.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,9 @@ public class SystemPreferences {
new ObjectPreference<>("data.sharing.static.resource.headers", null,
new TypeReference<Map<String, Map<String, String>>>() {}, DATA_SHARING_GROUP,
isNullOrValidJson(new TypeReference<Map<String, Map<String, String>>>() {}));
public static final StringPreference STATIC_RESOURCES_FOLDER_TEMPLATE_PATH =
new StringPreference("data.sharing.static.resource.template.path", "classpath:views/folder.vm",
DATA_SHARING_GROUP, pass);

// SYSTEM_GROUP
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2022 EPAM Systems, Inc. (https://www.epam.com/)
*
* 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 com.epam.pipeline.manager.resource;

import com.epam.pipeline.entity.datastorage.DataStorageItemType;
import lombok.Builder;
import lombok.Getter;

@Builder
@Getter
public class HtmlStorageItem {
private String name;
private String path;
private String size;
private String changed;
private DataStorageItemType type;
}
Loading