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

software.amazon.awssdk.codegen.poet.client.traits.HttpChecksumTrait Maven / Gradle / Ivy

/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.codegen.poet.client.traits;

import com.squareup.javapoet.CodeBlock;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import software.amazon.awssdk.checksums.DefaultChecksumAlgorithm;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
import software.amazon.awssdk.core.client.handler.ClientExecutionParams;
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
import software.amazon.awssdk.core.interceptor.trait.HttpChecksum;
import software.amazon.awssdk.core.protocol.MarshallLocation;
import software.amazon.awssdk.utils.CollectionUtils;
import software.amazon.awssdk.utils.ImmutableMap;

/**
 * The logic for handling the Flexible "httpChecksum" trait within the code generator.
 */
public class HttpChecksumTrait {

    private static final Map CHECKSUM_ALGORITHM_PRIORITY =
        ImmutableMap.builder()
                    .put(DefaultChecksumAlgorithm.CRC32C.algorithmId(), 1)
                    .put(DefaultChecksumAlgorithm.CRC32.algorithmId(), 2)
                    .put(DefaultChecksumAlgorithm.CRC64NVME.algorithmId(), 3)
                    .put(DefaultChecksumAlgorithm.SHA1.algorithmId(), 4)
                    .put(DefaultChecksumAlgorithm.SHA256.algorithmId(), 5)
                    .build();

    private HttpChecksumTrait() {
    }

    /**
     * Generate a ".putExecutionAttribute(...)" code-block for the provided operation model. This should be used within the
     * context of initializing {@link ClientExecutionParams}.
     * If HTTP checksums are not required by the operation, this will return an empty code-block.
     */
    public static CodeBlock create(OperationModel operationModel) {

        software.amazon.awssdk.codegen.checksum.HttpChecksum httpChecksum = operationModel.getHttpChecksum();

        if (httpChecksum == null) {
            return CodeBlock.of("");
        }

        CodeBlock.Builder codeBuilder = CodeBlock.builder();
        codeBuilder.add(CodeBlock.of(".putExecutionAttribute($T.HTTP_CHECKSUM, $T.builder()"
                                     + ".requestChecksumRequired($L).isRequestStreaming($L)",
                                     SdkInternalExecutionAttribute.class, HttpChecksum.class,
                                     httpChecksum.isRequestChecksumRequired(),
                                     operationModel.getInputShape().isHasStreamingMember()));


        String requestAlgorithmMember = httpChecksum.getRequestAlgorithmMember();
        if (requestAlgorithmMember != null) {
            configureRequestChecksumCalculation(operationModel, requestAlgorithmMember, codeBuilder);
        }

        String responseValidationModeMember = httpChecksum.getRequestValidationModeMember();
        if (responseValidationModeMember != null) {
            configureResponseChecksumValidation(operationModel, responseValidationModeMember, codeBuilder, httpChecksum);
        }

        return codeBuilder.add(CodeBlock.of(".build())")).build();
    }

    private static void configureResponseChecksumValidation(OperationModel operationModel,
                                                            String responseValidationModeMember,
                                                            CodeBlock.Builder codeBuilder,
                                                            software.amazon.awssdk.codegen.checksum.HttpChecksum httpChecksum) {
        MemberModel checksumMember =
            operationModel.getInputShape().tryFindMemberModelByC2jName(
                responseValidationModeMember, true);

        if (checksumMember == null) {
            throw new IllegalStateException(responseValidationModeMember
                                            + " is not a member in "
                                            + operationModel.getInputShape().getShapeName());
        }
        codeBuilder.add(".requestValidationMode($N.$N())",
                        operationModel.getInput().getVariableName(),
                        checksumMember.getFluentGetterMethodName());

        List responseAlgorithms = httpChecksum.getResponseAlgorithms();
        if (!CollectionUtils.isNullOrEmpty(responseAlgorithms)) {
            addResponseAlgorithmsCodeBlock(responseAlgorithms, codeBuilder);
        }
    }

    private static void configureRequestChecksumCalculation(OperationModel operationModel,
                                                            String requestAlgorithmMember,
                                                            CodeBlock.Builder codeBuilder) {
        MemberModel requestAlgorithm =
            operationModel.getInputShape().tryFindMemberModelByC2jName(
                requestAlgorithmMember, true);

        if (requestAlgorithm == null) {
            throw new IllegalStateException(requestAlgorithmMember
                                            + " is not a member in "
                                            + operationModel.getInputShape().getShapeName());
        }
        codeBuilder.add(".requestAlgorithm($N.$N())",
                        operationModel.getInput().getVariableName(),
                        requestAlgorithm.getFluentGetterMethodName());

        if (requestAlgorithm.getHttp().getMarshallLocation() != MarshallLocation.HEADER) {
            throw new IllegalStateException("Unsupported request algorithm location for " + requestAlgorithm);
        }

        String headerName = requestAlgorithm.getHttp().getMarshallLocationName();

        if (headerName == null) {
            throw new IllegalStateException("Request algorithm header name is null for " + requestAlgorithm);
        }

        codeBuilder.add(".requestAlgorithmHeader($S)", headerName);
    }

    /**
     * Sort the responseAlgorithms provided by the service by the time it takes to calculate a checksum for a given algorithm
     * with the fastest-to-calculate algorithms first.
     */
    private static void addResponseAlgorithmsCodeBlock(List responseAlgorithms, CodeBlock.Builder codeBuilder) {
        List sortedResponseAlgorithms =
            responseAlgorithms.stream().sorted(Comparator.comparingInt(o -> CHECKSUM_ALGORITHM_PRIORITY.getOrDefault(
            o.toUpperCase(Locale.US), Integer.MAX_VALUE))).collect(Collectors.toList());

        codeBuilder.add(CodeBlock.of(".responseAlgorithmsV2("));
        List responseAlgorithmsCodeBlocks = responseAlgorithmsCodeBlocks(sortedResponseAlgorithms);
        for (int i = 0; i < responseAlgorithmsCodeBlocks.size(); i++) {
            CodeBlock code = responseAlgorithmsCodeBlocks.get(i);
            codeBuilder.add(code);
            if (i != responseAlgorithmsCodeBlocks.size() - 1) {
                codeBuilder.add(",");
            }
        }
        codeBuilder.add(CodeBlock.of(")"));
    }

    private static List responseAlgorithmsCodeBlocks(List responseAlgorithms) {
        List list = new ArrayList<>();
        for (String algo : responseAlgorithms) {
            String algorithmName = algo.toUpperCase(Locale.US);
            if (!CHECKSUM_ALGORITHM_PRIORITY.containsKey(algorithmName)) {
                throw new UnsupportedOperationException("Unsupported algorithm: " + algorithmName);
            }
            CodeBlock codeBlock = CodeBlock.of("$T.$L", DefaultChecksumAlgorithm.class,
                                               algorithmName);
            list.add(codeBlock);
        }
        return list;
    }

    public static boolean hasRequestAlgorithmMember(IntermediateModel model) {
        Predicate requestCalculation =
            httpChecksum -> httpChecksum.getRequestAlgorithmMember() != null || httpChecksum.isRequestChecksumRequired();
        return model.getOperations().values().stream()
                    .filter(operationModel -> operationModel.getHttpChecksum() != null)
                    .anyMatch(opModel -> requestCalculation.test(opModel.getHttpChecksum()));
    }

    public static boolean hasResponseAlgorithms(IntermediateModel model) {
        return model.getOperations().values().stream()
                    .anyMatch(opModel -> opModel.getHttpChecksum() != null
                                         && opModel.getHttpChecksum().getResponseAlgorithms() != null);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy