S3AutoConfiguration.kt

package io.github.lishangbu.avalon.s3.autoconfiguration

import io.github.lishangbu.avalon.s3.client.AvalonS3ClientFactory
import io.github.lishangbu.avalon.s3.client.AvalonS3ClientRegistry
import io.github.lishangbu.avalon.s3.customizer.S3AsyncClientBuilderCustomizer
import io.github.lishangbu.avalon.s3.customizer.S3ClientBuilderCustomizer
import io.github.lishangbu.avalon.s3.customizer.S3ControlAsyncClientBuilderCustomizer
import io.github.lishangbu.avalon.s3.customizer.S3ControlClientBuilderCustomizer
import io.github.lishangbu.avalon.s3.customizer.S3PresignerBuilderCustomizer
import io.github.lishangbu.avalon.s3.customizer.S3TransferManagerBuilderCustomizer
import io.github.lishangbu.avalon.s3.facade.BucketOperations
import io.github.lishangbu.avalon.s3.facade.MultipartOperations
import io.github.lishangbu.avalon.s3.facade.ObjectOperations
import io.github.lishangbu.avalon.s3.facade.PresignOperations
import io.github.lishangbu.avalon.s3.facade.S3Facade
import io.github.lishangbu.avalon.s3.facade.TransferOperations
import io.github.lishangbu.avalon.s3.properties.AvalonS3Properties
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import software.amazon.awssdk.services.s3.S3AsyncClient
import software.amazon.awssdk.services.s3.S3Client
import software.amazon.awssdk.services.s3.presigner.S3Presigner
import software.amazon.awssdk.services.s3.waiters.S3Waiter
import software.amazon.awssdk.services.s3control.S3ControlAsyncClient
import software.amazon.awssdk.services.s3control.S3ControlClient
import software.amazon.awssdk.transfer.s3.S3TransferManager

@AutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(S3Client::class)
@ConditionalOnProperty(prefix = AvalonS3Properties.PREFIX, name = ["enabled"], havingValue = "true")
@EnableConfigurationProperties(AvalonS3Properties::class)
class S3AutoConfiguration {
    /** 创建 registry。 */
    @Bean(destroyMethod = "close")
    @ConditionalOnMissingBean(AvalonS3ClientRegistry::class)
    fun avalonS3ClientRegistry(
        properties: AvalonS3Properties,
        s3ClientBuilderCustomizers: List<S3ClientBuilderCustomizer>,
        s3AsyncClientBuilderCustomizers: List<S3AsyncClientBuilderCustomizer>,
        s3PresignerBuilderCustomizers: List<S3PresignerBuilderCustomizer>,
        s3ControlClientBuilderCustomizers: List<S3ControlClientBuilderCustomizer>,
        s3ControlAsyncClientBuilderCustomizers: List<S3ControlAsyncClientBuilderCustomizer>,
        s3TransferManagerBuilderCustomizers: List<S3TransferManagerBuilderCustomizer>,
    ): AvalonS3ClientRegistry {
        val factory =
            AvalonS3ClientFactory(
                s3ClientBuilderCustomizers = s3ClientBuilderCustomizers,
                s3AsyncClientBuilderCustomizers = s3AsyncClientBuilderCustomizers,
                s3PresignerBuilderCustomizers = s3PresignerBuilderCustomizers,
                s3ControlClientBuilderCustomizers = s3ControlClientBuilderCustomizers,
                s3ControlAsyncClientBuilderCustomizers = s3ControlAsyncClientBuilderCustomizers,
                s3TransferManagerBuilderCustomizers = s3TransferManagerBuilderCustomizers,
            )

        val enabledClients =
            properties.clients
                .filterValues { it.enabled }
                .mapValues { (clientName, clientProperties) ->
                    factory.create(clientName, clientProperties)
                }

        return AvalonS3ClientRegistry(properties.defaultClientName, enabledClients)
    }

    /** 默认 facade。 */
    @Bean
    @ConditionalOnMissingBean
    fun s3Facade(registry: AvalonS3ClientRegistry): S3Facade = registry.defaultFacade()

    /** 默认同步 client。 */
    @Bean(destroyMethod = "")
    @ConditionalOnMissingBean(S3Client::class)
    fun s3Client(s3Facade: S3Facade): S3Client = s3Facade.s3Client

    /** 默认异步 client。 */
    @Bean(destroyMethod = "")
    @ConditionalOnMissingBean(S3AsyncClient::class)
    fun s3AsyncClient(s3Facade: S3Facade): S3AsyncClient = s3Facade.s3AsyncClient

    /** 默认 waiter。 */
    @Bean(destroyMethod = "")
    @ConditionalOnMissingBean(S3Waiter::class)
    fun s3Waiter(s3Facade: S3Facade): S3Waiter = s3Facade.s3Waiter

    /** 默认 presigner。 */
    @Bean(destroyMethod = "")
    @ConditionalOnMissingBean(S3Presigner::class)
    fun s3Presigner(s3Facade: S3Facade): S3Presigner = s3Facade.s3Presigner

    /** 默认同步 S3 Control client。 */
    @Bean(destroyMethod = "")
    @ConditionalOnMissingBean(S3ControlClient::class)
    fun s3ControlClient(s3Facade: S3Facade): S3ControlClient = s3Facade.s3ControlClient

    /** 默认异步 S3 Control client。 */
    @Bean(destroyMethod = "")
    @ConditionalOnMissingBean(S3ControlAsyncClient::class)
    fun s3ControlAsyncClient(s3Facade: S3Facade): S3ControlAsyncClient = s3Facade.s3ControlAsyncClient

    /** 默认 Transfer Manager。 */
    @Bean(destroyMethod = "")
    @ConditionalOnMissingBean(S3TransferManager::class)
    fun s3TransferManager(s3Facade: S3Facade): S3TransferManager = s3Facade.s3TransferManager

    /** 默认桶操作 facade。 */
    @Bean
    @ConditionalOnMissingBean
    fun bucketOperations(s3Facade: S3Facade): BucketOperations = s3Facade.buckets

    /** 默认对象操作 facade。 */
    @Bean
    @ConditionalOnMissingBean
    fun objectOperations(s3Facade: S3Facade): ObjectOperations = s3Facade.objects

    /** 默认 multipart facade。 */
    @Bean
    @ConditionalOnMissingBean
    fun multipartOperations(s3Facade: S3Facade): MultipartOperations = s3Facade.multipart

    /** 默认 presign facade。 */
    @Bean
    @ConditionalOnMissingBean
    fun presignOperations(s3Facade: S3Facade): PresignOperations = s3Facade.presign

    /** 默认 transfer facade。 */
    @Bean
    @ConditionalOnMissingBean
    fun transferOperations(s3Facade: S3Facade): TransferOperations = s3Facade.transfer
}