ResourceLambdaHandler.kt

/**
 * This file is part of the pl.wrzasq.cform.
 *
 * @license http://mit-license.org/ The MIT license
 * @copyright 2021, 2024 © by Rafał Wrzeszcz - Wrzasq.pl.
 */

package pl.wrzasq.cform.commons

import pl.wrzasq.cform.commons.action.ActionHandler
import pl.wrzasq.cform.commons.config.Configuration
import software.amazon.awssdk.regions.PartitionMetadata
import software.amazon.awssdk.regions.Region
import software.amazon.cloudformation.Action
import software.amazon.cloudformation.LambdaWrapper
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy
import software.amazon.cloudformation.proxy.HandlerRequest
import software.amazon.cloudformation.proxy.ProgressEvent
import software.amazon.cloudformation.proxy.ResourceHandlerRequest
import software.amazon.cloudformation.proxy.StdCallbackContext

/**
 * Generic handler for Lambda invocations.
 *
 * @param configuration Handler configuration.
 * @param handlers Mapping of action handlers.
 */
abstract class ResourceLambdaHandler<ResourceType, ConfigurationType>(
    private val configuration: Configuration,
    private val handlers: Map<Action, ActionHandler<ResourceType>>,
) : LambdaWrapper<ResourceType?, StdCallbackContext, ConfigurationType>() {
    override fun provideResourceSchemaJSONObject() = configuration.resourceSchema

    override fun provideResourceDefinedTags(resourceModel: ResourceType?): Map<String, String>? = null

    override fun transform(
        request: HandlerRequest<ResourceType?, StdCallbackContext, ConfigurationType>,
    ): ResourceHandlerRequest<ResourceType?> {
        val requestData = request.requestData
        return ResourceHandlerRequest.builder<ResourceType?>()
            .clientRequestToken(request.bearerToken)
            .desiredResourceState(requestData.resourceProperties)
            .previousResourceState(requestData.previousResourceProperties)
            .desiredResourceTags(getDesiredResourceTags(request))
            .systemTags(request.requestData.systemTags)
            .awsAccountId(request.awsAccountId)
            .logicalResourceIdentifier(request.requestData.logicalResourceId)
            .nextToken(request.nextToken)
            .region(request.region)
            .awsPartition(PartitionMetadata.of(Region.of(request.region)).id())
            .build()
    }

    override fun invokeHandler(
        proxy: AmazonWebServicesClientProxy?,
        request: ResourceHandlerRequest<ResourceType?>,
        action: Action?,
        callbackContext: StdCallbackContext?,
        typeConfiguration: ConfigurationType,
    ): ProgressEvent<ResourceType?, StdCallbackContext> {
        requireNotNull(proxy)
        val actionName = action.toString()
        val handler = handlers[action] ?: throw RuntimeException("Unknown action $actionName")
        loggerProxy.log("[$actionName] invoking handler…")

        return handler.handleRequest(
            proxy,
            request,
            callbackContext ?: StdCallbackContext(),
            loggerProxy,
        ).also {
            loggerProxy.log("[$actionName] handler invoked")
        }
    }
}