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

com.slack.api.bolt.service.builtin.AmazonS3InstallationService Maven / Gradle / Ivy

There is a newer version: 1.44.2
Show newest version
package com.slack.api.bolt.service.builtin;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
import com.amazonaws.util.IOUtils;
import com.slack.api.bolt.Initializer;
import com.slack.api.bolt.model.Bot;
import com.slack.api.bolt.model.Installer;
import com.slack.api.bolt.model.builtin.DefaultBot;
import com.slack.api.bolt.model.builtin.DefaultInstaller;
import com.slack.api.bolt.service.InstallationService;
import com.slack.api.bolt.util.JsonOps;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.Optional;

@Slf4j
public class AmazonS3InstallationService implements InstallationService {

    private final String bucketName;
    private boolean historicalDataEnabled;

    public AmazonS3InstallationService(String bucketName) {
        this.bucketName = bucketName;
    }

    @Override
    public Initializer initializer() {
        return (app) -> {
            // The first access to S3 tends to be slow on AWS Lambda.
            AWSCredentials credentials = getCredentials();
            if (credentials == null || credentials.getAWSAccessKeyId() == null) {
                throw new IllegalStateException("AWS credentials not found");
            }
            if (log.isDebugEnabled()) {
                log.debug("AWS credentials loaded (access key id: {})", credentials.getAWSAccessKeyId());
            }
            boolean bucketExists = createS3Client().doesBucketExistV2(bucketName);
            if (!bucketExists) {
                throw new IllegalStateException("Failed to access the Amazon S3 bucket (name: " + bucketName + ")");
            }
        };
    }

    @Override
    public boolean isHistoricalDataEnabled() {
        return historicalDataEnabled;
    }

    @Override
    public void setHistoricalDataEnabled(boolean isHistoricalDataEnabled) {
        this.historicalDataEnabled = isHistoricalDataEnabled;
    }

    @Override
    public void saveInstallerAndBot(Installer i) throws Exception {
        AmazonS3 s3 = this.createS3Client();
        if (isHistoricalDataEnabled()) {
            save(s3, getInstallerKey(i) + "-latest", JsonOps.toJsonString(i), "AWS S3 putObject result of Installer data - {}, {}");
            save(s3, getBotKey(i) + "-latest", JsonOps.toJsonString(i.toBot()), "AWS S3 putObject result of Bot data - {}, {}");
            save(s3, getInstallerKey(i) + "-" + i.getInstalledAt(), JsonOps.toJsonString(i), "AWS S3 putObject result of Installer data - {}, {}");
            save(s3, getBotKey(i) + "-" + i.getInstalledAt(), JsonOps.toJsonString(i.toBot()), "AWS S3 putObject result of Bot data - {}, {}");
        } else {
            save(s3, getInstallerKey(i), JsonOps.toJsonString(i), "AWS S3 putObject result of Installer data - {}, {}");
            save(s3, getBotKey(i), JsonOps.toJsonString(i.toBot()), "AWS S3 putObject result of Bot data - {}, {}");
        }
    }

    @Override
    public void saveBot(Bot bot) throws Exception {
        AmazonS3 s3 = this.createS3Client();
        String keyPrefix = getBotKey(bot.getEnterpriseId(), bot.getTeamId());
        if (isHistoricalDataEnabled()) {
            save(s3, keyPrefix + "-latest", JsonOps.toJsonString(bot), "AWS S3 putObject result of Bot data - {}, {}");
            save(s3, keyPrefix + "-" + bot.getInstalledAt(), JsonOps.toJsonString(bot), "AWS S3 putObject result of Bot data - {}, {}");
        } else {
            save(s3, keyPrefix, JsonOps.toJsonString(bot), "AWS S3 putObject result of Bot data - {}, {}");
        }
    }

    private void save(AmazonS3 s3, String s3Key, String json, String logMessage) {
        PutObjectResult botPutResult = s3.putObject(bucketName, s3Key, json);
        if (log.isDebugEnabled()) {
            log.debug(logMessage, s3Key, JsonOps.toJsonString(botPutResult));
        }
    }

    @Override
    public void deleteBot(Bot bot) throws Exception {
        AmazonS3 s3 = this.createS3Client();
        String key = getBotKey(bot.getEnterpriseId(), bot.getTeamId());
        if (isHistoricalDataEnabled()) {
            key = key + "-latest";
        }
        s3.deleteObject(bucketName, key);
    }

    @Override
    public void deleteInstaller(Installer installer) throws Exception {
        AmazonS3 s3 = this.createS3Client();
        String key = getInstallerKey(installer);
        if (isHistoricalDataEnabled()) {
            key = key + "-latest";
        }
        s3.deleteObject(bucketName, key);
    }

    @Override
    public Bot findBot(String enterpriseId, String teamId) {
        AmazonS3 s3 = this.createS3Client();
        if (enterpriseId != null) {
            // try finding org-level bot token first - teamId is intentionally null here
            String fullKey = getBotKey(enterpriseId, null);
            if (isHistoricalDataEnabled()) {
                fullKey = fullKey + "-latest";
            }
            if (getObjectMetadata(s3, fullKey) != null) {
                S3Object s3Object = getObject(s3, fullKey);
                try {
                    return toBot(s3Object);
                } catch (IOException e) {
                    log.error("Failed to load org-level Bot installation for enterprise_id: {}", enterpriseId);
                }
            }
            // not found - going to find workspace level installation
        }
        String fullKey = getBotKey(enterpriseId, teamId);
        if (isHistoricalDataEnabled()) {
            fullKey = fullKey + "-latest";
        }
        if (getObjectMetadata(s3, fullKey) == null && enterpriseId != null) {
            String nonGridKey = getBotKey(null, teamId);
            if (isHistoricalDataEnabled()) {
                nonGridKey = nonGridKey + "-latest";
            }
            S3Object nonGridObject = getObject(s3, nonGridKey);
            if (nonGridObject != null) {
                try {
                    Bot bot = toBot(nonGridObject);
                    bot.setEnterpriseId(enterpriseId); // the workspace seems to be in a Grid org now
                    save(s3, fullKey, JsonOps.toJsonString(bot), "AWS S3 putObject result of Bot data - {}");
                    return bot;
                } catch (Exception e) {
                    log.error("Failed to save a new Bot data for enterprise_id: {}, team_id: {}", enterpriseId, teamId);
                }
            }
        }
        S3Object s3Object = getObject(s3, fullKey);
        try {
            return toBot(s3Object);
        } catch (IOException e) {
            log.error("Failed to load Bot data for enterprise_id: {}, team_id: {}", enterpriseId, teamId);
            return null;
        }
    }

    @Override
    public Installer findInstaller(String enterpriseId, String teamId, String userId) {
        AmazonS3 s3 = this.createS3Client();
        if (enterpriseId != null) {
            // try finding org-level user token first - teamId is intentionally null here
            String fullKey = getInstallerKey(enterpriseId, null, userId);
            if (isHistoricalDataEnabled()) {
                fullKey = fullKey + "-latest";
            }
            if (getObjectMetadata(s3, fullKey) != null) {
                S3Object s3Object = getObject(s3, fullKey);
                try {
                    return toInstaller(s3Object);
                } catch (IOException e) {
                    log.error("Failed to load org-level installation for enterprise_id: {}, user_id: {}", enterpriseId, userId);
                }
            }
            // not found - going to find workspace level installation
        }
        String fullKey = getInstallerKey(enterpriseId, teamId, userId);
        if (isHistoricalDataEnabled()) {
            fullKey = fullKey + "-latest";
        }
        if (getObjectMetadata(s3, fullKey) == null && enterpriseId != null) {
            String nonGridKey = getInstallerKey(null, teamId, userId);
            if (isHistoricalDataEnabled()) {
                nonGridKey = nonGridKey + "-latest";
            }
            S3Object nonGridObject = getObject(s3, nonGridKey);
            if (nonGridObject != null) {
                try {
                    Installer installer = toInstaller(nonGridObject);
                    installer.setEnterpriseId(enterpriseId); // the workspace seems to be in a Grid org now
                    saveInstallerAndBot(installer);
                    return installer;
                } catch (Exception e) {
                    log.error("Failed to save a new Installer data for enterprise_id: {}, team_id: {}, user_id: {}",
                            enterpriseId, teamId, userId);
                }
            }
        }
        S3Object s3Object = getObject(s3, fullKey);
        try {
            return toInstaller(s3Object);
        } catch (Exception e) {
            log.error("Failed to save a new Installer data for enterprise_id: {}, team_id: {}, user_id: {}",
                    enterpriseId, teamId, userId);
            return null;
        }
    }

    @Override
    public void deleteAll(String enterpriseId, String teamId) {
        AmazonS3 s3 = this.createS3Client();
        deleteAllObjectsMatchingPrefix(s3, "installer/"
                + Optional.ofNullable(enterpriseId).orElse("none")
                + "-"
                + Optional.ofNullable(teamId).orElse("none"));
        deleteAllObjectsMatchingPrefix(s3, "bot/"
                + Optional.ofNullable(enterpriseId).orElse("none")
                + "-"
                + Optional.ofNullable(teamId).orElse("none"));
    }

    private void deleteAllObjectsMatchingPrefix(AmazonS3 s3, String prefix) {
        for (S3ObjectSummary obj : s3.listObjects(bucketName, prefix).getObjectSummaries()) {
            s3.deleteObject(bucketName, obj.getKey());
        }
    }

    private ObjectMetadata getObjectMetadata(AmazonS3 s3, String fullKey) {
        try {
            return s3.getObjectMetadata(bucketName, fullKey);
        } catch (AmazonS3Exception e) {
            if (log.isDebugEnabled()) {
                log.debug("Amazon S3 object metadata not found (key: {}, AmazonS3Exception: {})", fullKey, e.toString());
            }
            return null;
        }
    }

    private S3Object getObject(AmazonS3 s3, String fullKey) {
        try {
            return s3.getObject(bucketName, fullKey);
        } catch (AmazonS3Exception e) {
            if (log.isDebugEnabled()) {
                log.debug("Amazon S3 object metadata not found (key: {}, AmazonS3Exception: {})", fullKey, e.toString());
            }
            return null;
        }
    }

    private Bot toBot(S3Object s3Object) throws IOException {
        if (s3Object == null) {
            return null;
        }
        String json = IOUtils.toString(s3Object.getObjectContent());
        return JsonOps.fromJson(json, DefaultBot.class);
    }

    private Installer toInstaller(S3Object s3Object) throws IOException {
        if (s3Object == null) {
            return null;
        }
        String json = IOUtils.toString(s3Object.getObjectContent());
        return JsonOps.fromJson(json, DefaultInstaller.class);
    }

    protected AWSCredentials getCredentials() {
        return DefaultAWSCredentialsProviderChain.getInstance().getCredentials();
    }

    protected AmazonS3 createS3Client() {
        return AmazonS3ClientBuilder.defaultClient();
    }

    private String getInstallerKey(Installer i) {
        return getInstallerKey(i.getEnterpriseId(), i.getTeamId(), i.getInstallerUserId());
    }

    private String getInstallerKey(String enterpriseId, String teamId, String userId) {
        return "installer/"
                + Optional.ofNullable(enterpriseId).orElse("none")
                + "-"
                + Optional.ofNullable(teamId).orElse("none")
                + "-"
                + userId;
    }

    private String getBotKey(Installer i) {
        return getBotKey(i.getEnterpriseId(), i.getTeamId());
    }

    private String getBotKey(String enterpriseId, String teamId) {
        return "bot/"
                + Optional.ofNullable(enterpriseId).orElse("none")
                + "-"
                + Optional.ofNullable(teamId).orElse("none");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy