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

com.limemojito.test.s3.S3Support Maven / Gradle / Ivy

Go to download

Test utilities for various development work. Json, reflection, getter/setter testing, DTO, Canonical form, etc. AWS support for Dynamo DB, SQS, SNS, S3. Prometheus metrics reader and asserter. Synthetic S3 Event generation.

There is a newer version: 14.3.1
Show newest version
/*
 * Copyright 2011-2024 Lime Mojito Pty Ltd
 *
 *    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.limemojito.test.s3;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.core.sync.ResponseTransformer;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.S3Uri;
import software.amazon.awssdk.services.s3.model.BucketAlreadyOwnedByYouException;
import software.amazon.awssdk.services.s3.model.ListObjectsResponse;
import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
import software.amazon.awssdk.services.s3.model.S3Object;
import software.amazon.awssdk.utils.IoUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;

/**
 * The S3Support class provides methods for interacting with Amazon S3 bucket.
 *
 * @since 1.0
 */
@Slf4j
@Service
public class S3Support {

    private final S3Client s3;

    /**
     * Initializes a new instance of the S3Support class with the specified S3 client.
     *
     * @param s3 The S3 client to be used for interacting with Amazon S3.
     */
    public S3Support(S3Client s3) {
        this.s3 = s3;
    }

    /**
     * Wipes all objects from the specified bucket.
     *
     * @param bucketName the name of the bucket to be wiped
     */
    public void wipeBucket(String bucketName) {
        log.info("Wiping bucket {}", bucketName);
        ListObjectsResponse objectListing;
        int deleted = 0;
        do {
            objectListing = s3.listObjects(r -> r.bucket(bucketName));
            for (S3Object objectSummary : objectListing.contents()) {
                s3.deleteObject(r -> r.bucket(bucketName).key(objectSummary.key()));
                deleted++;
            }
            log.debug("Deleted {} from {}", deleted, bucketName);
        } while (objectListing.isTruncated());
        log.info("Wipe of {} completed.  {} objects deleted.", bucketName, deleted);
    }

    /**
     * Uploads data to a specified location in a bucket.
     *
     * @param bucketName the name of the bucket where the data will be uploaded.
     * @param key the key or path of the file in the bucket.
     * @param input the input stream containing the data to be uploaded.
     * @param inputLength the length of the input stream in bytes.
     * @param contentType the content type of the data to be uploaded.
     */
    public void putData(String bucketName, String key, InputStream input, long inputLength, String contentType) {
        log.info("Uploading {}->{} of type {}", bucketName, key, contentType);
        s3.putObject(r -> r.bucket(bucketName)
                           .key(key)
                           .contentType(contentType)
                           .contentLength(inputLength), RequestBody.fromInputStream(input, inputLength));
        log.info("Upload {}->{} completed.", bucketName, key);
    }

    /**
     * Saves the specified data to the specified URI using the specified content type.
     *
     * @param uri         the URI where the data will be saved
     * @param contentType the content type of the data
     * @param data        the data to be saved
     */
    @SneakyThrows
    public void putData(URI uri, String contentType, byte[] data) {
        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(data)) {
            final S3Uri s3Uri = s3.utilities().parseUri(uri);
            final String bucketName = s3Uri.bucket().orElseThrow();
            final String key = s3Uri.key().orElseThrow();
            putData(bucketName, key, inputStream, data.length, contentType);
        }
    }

    /**
     * Puts a classpath binary resource into the specified S3 bucket with the given key.
     * The classpath resource is identified by its resource path.
     * This method calls the putClasspathResourceAs method internally with the content type set to "application/octet-stream".
     *
     * @param bucketName            the name of the S3 bucket where the classpath binary resource should be stored
     * @param key                   the key under which the classpath binary resource will be stored
     * @param classpathResourcePath the resource path of the classpath binary resource to be stored,
     *                              relative to the classpath root
     */
    public void putClasspathBinary(String bucketName, String key, String classpathResourcePath) {
        putClasspathResourceAs(bucketName, key, classpathResourcePath, "application/octet-stream");
    }

    /**
     * Uploads a classpath resource to the specified S3 bucket with the given key and MIME type.
     *
     * @param bucketName            the name of the S3 bucket to upload the resource to
     * @param key                   the key (or path) under which the resource will be stored in the S3 bucket
     * @param classpathResourcePath the path of the classpath resource to upload
     * @param mimeType              the MIME type of the resource
     */
    public void putClasspathResourceAs(String bucketName, String key, String classpathResourcePath, String mimeType) {
        try (InputStream inputStream = getClass().getResourceAsStream(classpathResourcePath)) {
            if (inputStream == null) {
                throw new IllegalArgumentException("Could not load " + classpathResourcePath + " from classpath");
            }
            final byte[] dataBytes = IoUtils.toByteArray(inputStream);
            try (ByteArrayInputStream byteData = new ByteArrayInputStream(dataBytes)) {
                putData(bucketName, key, byteData, dataBytes.length, mimeType);
            }
        } catch (IOException | NullPointerException e) {
            throw new RuntimeException("Could not load " + classpathResourcePath + " from classpath");
        }
    }

    /**
     * Checks if a key exists in a given bucket.
     *
     * @param bucketName the name of the bucket
     * @param key        the key to be checked
     * @return {@code true} if the key exists in the bucket, {@code false} otherwise
     */
    public boolean keyExists(String bucketName, String key) {
        try {
            s3.headObject(r -> r.bucket(bucketName).key(key));
            return true;
        } catch (NoSuchKeyException e) {
            return false;
        }
    }

    /**
     * Retrieves data from the specified bucket and key.
     *
     * @param bucketName the name of the bucket
     * @param key        the key of the object to retrieve
     * @return an InputStream of the retrieved data.  Should be closed by caller.
     */
    public InputStream getData(String bucketName, String key) {
        log.info("Retrieving {}->{}", bucketName, key);
        return s3.getObject(r -> r.bucket(bucketName).key(key), ResponseTransformer.toInputStream());
    }

    /**
     * Converts the given S3 bucket and key to an S3 URI.
     *
     * @param bucket The name of the S3 bucket.
     * @param key    The key of the object in the S3 bucket.
     * @return The S3 URI in the format "s3://bucket/key".
     */
    public URI toS3Uri(String bucket, String key) {
        return URI.create("s3://%s/%s".formatted(bucket, key));
    }

    /**
     * Creates a bucket with the given bucket name.  If the bucket already exists this method returns cleanly.
     *
     * @param uri s3 uri representing the bucket.
     */
    public void createBucket(URI uri) {
        createBucket(s3.utilities()
                       .parseUri(uri)
                       .bucket()
                       .orElseThrow());
    }

    /**
     * Creates a bucket with the given bucket name.  If the bucket already exists this method returns cleanly.
     *
     * @param bucketName the name of the bucket to be created
     */
    public void createBucket(String bucketName) {
        try {
            s3.createBucket(r -> r.bucket(bucketName));
            log.info("Created bucket s3://{}", bucketName);
        } catch (BucketAlreadyOwnedByYouException e) {
            log.info("Bucket already created.");
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy