All Downloads are FREE. Search and download functionalities are using the official Maven repository.

no.unit.nva.stubs.FakeS3Client Maven / Gradle / Ivy

The newest version!
package no.unit.nva.stubs;


import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
import nva.commons.core.JacocoGenerated;
import nva.commons.core.ioutils.IoUtils;
import nva.commons.core.paths.UnixPath;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.core.sync.ResponseTransformer;
import software.amazon.awssdk.http.AbortableInputStream;
import software.amazon.awssdk.http.SdkHttpResponse;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.DeleteObjectResponse;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.ListObjectsRequest;
import software.amazon.awssdk.services.s3.model.ListObjectsResponse;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Response;
import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
import software.amazon.awssdk.services.s3.model.S3Object;


@JacocoGenerated
public class FakeS3Client implements S3Client {

    private static final int START_FROM_BEGINNING = 0;
    private final Map filesAndContent;

    public FakeS3Client(String... filesInBucket) {
        this(readResourceFiles(filesInBucket));
    }

    public FakeS3Client(Map filesAndContent) {
        this.filesAndContent = new LinkedHashMap<>(filesAndContent);
    }

    public static FakeS3Client fromContentsMap(Map filesAndContent) {
        var toByteBuffer = filesAndContent.entrySet().stream()
                               .collect(
                                   Collectors.toMap(Entry::getKey, entry -> inputSteamToByteBuffer(entry.getValue())));
        return new FakeS3Client(toByteBuffer);
    }

    //TODO: fix if necessary
    @SuppressWarnings("PMD.CloseResource")
    @Override
    public  ReturnT getObject(GetObjectRequest getObjectRequest,
                                       ResponseTransformer responseTransformer) {
        String filename = getObjectRequest.key();
        var contents = extractContent(filename).array();
        GetObjectResponse response = GetObjectResponse.builder().contentLength((long) contents.length).build();
        return transformResponse(responseTransformer, new ByteArrayInputStream(contents), response);
    }

    /**
     * Lists objects paginated one by one.
     *
     * @param listObjectsRequest the request
     * @return Response containing only one object.
     */
    @Override
    public ListObjectsResponse listObjects(ListObjectsRequest listObjectsRequest) {
        var fileKeys = new ArrayList<>(filesAndContent.keySet());

        var startIndex = calculateStartIndex(fileKeys, listObjectsRequest.marker());
        var excludedEndIndex = calculateEndIndex(fileKeys, listObjectsRequest.marker(), listObjectsRequest.maxKeys());

        var files = fileKeys.subList(startIndex, excludedEndIndex).stream()
                        .filter(filePath -> filePathIsInSpecifiedParentFolder(filePath, listObjectsRequest))
                        .map(filename -> S3Object.builder().key(filename).build())
                        .collect(Collectors.toList());
        var nextStartListingPoint = calculateNestStartListingPoint(fileKeys, excludedEndIndex);

        return ListObjectsResponse.builder().contents(files)
                .nextMarker(nextStartListingPoint)
                .isTruncated(nonNull(nextStartListingPoint)).build();
    }

    @Override
    public ListObjectsV2Response listObjectsV2(ListObjectsV2Request v2Request){
        var oldRequest = ListObjectsRequest.builder()
                .bucket(v2Request.bucket())
                .marker(v2Request.continuationToken())
                .maxKeys(v2Request.maxKeys())
                .prefix(v2Request.prefix())
                .build();
        var oldResponse= listObjects(oldRequest);
        return ListObjectsV2Response
                .builder()
                .contents(oldResponse.contents())
                .isTruncated(oldResponse.isTruncated())
                .continuationToken(v2Request.continuationToken())
                .nextContinuationToken(oldResponse.nextMarker())
                .build();
    }


    //TODO: fix if necessary
    @SuppressWarnings("PMD.CloseResource")
    @Override
    public PutObjectResponse putObject(PutObjectRequest putObjectRequest, RequestBody requestBody) {
        var path = putObjectRequest.key();
        var inputStream = requestBody.contentStreamProvider().newStream();
        this.filesAndContent.put(path, inputSteamToByteBuffer(inputStream));
        var sdkHttpResponse = SdkHttpResponse.builder().statusCode(200).build();
        return (PutObjectResponse) PutObjectResponse.builder()
                                       .sdkHttpResponse(sdkHttpResponse)
                                       .build();
    }

    @Override
    public DeleteObjectResponse deleteObject(DeleteObjectRequest deleteObjectRequest) {
        var path = deleteObjectRequest.key();
        this.filesAndContent.remove(path);
        return DeleteObjectResponse.builder().build();
    }

    @Override
    public String serviceName() {
        return "FakeS3Client";
    }

    @Override
    public void close() {

    }

    private String calculateNestStartListingPoint(List fileKeys,
      int excludedEndIndex) {
        return excludedEndIndex >= fileKeys.size()
          ? null
          : fileKeys.get(excludedEndIndex-1);
    }

    private static Map readResourceFiles(String... filesInBucket) {
        List suppliedFilenames = Arrays.asList(filesInBucket);
        return suppliedFilenames.stream()
                   .map(filename -> new SimpleEntry<>(filename, readFileFromResources(filename)))
                   .collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue));
    }

    private static ByteBuffer readFileFromResources(String filename) {
        final var inputStream = IoUtils.inputStreamFromResources(filename);
        return inputSteamToByteBuffer(inputStream);
    }

    private static ByteBuffer inputSteamToByteBuffer(InputStream inputStream) {
        return ByteBuffer.wrap(readAllBytes(inputStream));
    }

    private static byte[] readAllBytes(InputStream inputStream) {
        try {
            return inputStream.readAllBytes();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }



    private boolean filePathIsInSpecifiedParentFolder(String filePathString, ListObjectsRequest listObjectsRequest) {
        var filePath = UnixPath.of(filePathString).removeRoot();
        var parentFolder = Optional.of(listObjectsRequest)
                               .map(ListObjectsRequest::prefix)
                               .map(UnixPath::of)
                               .map(UnixPath::removeRoot)
                               .orElse(UnixPath.EMPTY_PATH);

        return parentFolder.isEmptyPath()
               || parentFolder.isRoot()
               || filePath.toString().startsWith(parentFolder.toString());
    }

    private int calculateEndIndex(List fileKeys, String marker, Integer pageSize) {
        int startIndex = calculateStartIndex(fileKeys, marker);
        return Math.min(startIndex + pageSize, fileKeys.size());
    }

    private int calculateStartIndex(List fileKeys, String marker) {
        if (isNull(marker)) {
            return START_FROM_BEGINNING;
        } else {
            var calculatedStartIndex = indexOfLastReadFile(fileKeys, marker) + 1;
            if (calculatedStartIndex < fileKeys.size()) {
                return calculatedStartIndex;
            }
        }
        throw new IllegalStateException("Start index is out of bounds in FakeS3Client");
    }

    private static int indexOfLastReadFile(List fileKeys, String marker) {
        int indexOfLastFileRead = fileKeys.indexOf(marker);
        if(indexOfLastFileRead<0){
            throw new IllegalArgumentException("Marker/ContinuationToken is not valid");
        }
        return indexOfLastFileRead;
    }

    private ByteBuffer extractContent(String filename) {
        if (filesAndContent.containsKey(filename)) {
            return filesAndContent.get(filename);
        } else {
            throw NoSuchKeyException.builder().message("File does not exist:" + filename).build();
        }
    }

    private  ReturnT transformResponse(ResponseTransformer responseTransformer,
                                                InputStream inputStream, GetObjectResponse response) {
        try {
            return responseTransformer.transform(response, AbortableInputStream.create(inputStream));
        } catch (Exception exception) {
            throw new RuntimeException(exception);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy