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

com.amazonaws.codegen.IntermediateModelBuilder Maven / Gradle / Ivy

Go to download

The AWS SDK for Java - Code Generator module holds the classes and templates required to generate the AWS Java SDK clients for AWS services.

There is a newer version: 1.12.772
Show newest version
/*
 * Copyright (c) 2016. 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 com.amazonaws.codegen;

import com.amazonaws.auth.SignerFactory;
import com.amazonaws.codegen.customization.CodegenCustomizationProcessor;
import com.amazonaws.codegen.customization.processors.DefaultCustomizationProcessor;
import com.amazonaws.codegen.internal.TypeUtils;
import com.amazonaws.codegen.internal.Utils;
import com.amazonaws.codegen.model.config.BasicCodeGenConfig;
import com.amazonaws.codegen.model.config.customization.CustomizationConfig;
import com.amazonaws.codegen.model.intermediate.AuthorizerModel;
import com.amazonaws.codegen.model.intermediate.IntermediateModel;
import com.amazonaws.codegen.model.intermediate.MemberModel;
import com.amazonaws.codegen.model.intermediate.OperationModel;
import com.amazonaws.codegen.model.intermediate.Protocol;
import com.amazonaws.codegen.model.intermediate.ServiceExamples;
import com.amazonaws.codegen.model.intermediate.ShapeModel;
import com.amazonaws.codegen.model.intermediate.WaiterDefinitionModel;
import com.amazonaws.codegen.model.service.AuthType;
import com.amazonaws.codegen.model.service.Operation;
import com.amazonaws.codegen.model.service.ServiceModel;
import com.amazonaws.codegen.model.service.Waiters;
import com.amazonaws.codegen.naming.DefaultNamingStrategy;
import com.amazonaws.codegen.naming.NamingStrategy;
import com.amazonaws.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import static com.amazonaws.codegen.AddMetadata.constructMetadata;
import static com.amazonaws.codegen.RemoveUnusedShapes.removeUnusedShapes;

/**
 * Builds an intermediate model to be used by the templates from the service model and
 * customizations.
 */
public class IntermediateModelBuilder {

    private final CustomizationConfig customConfig;
    private final BasicCodeGenConfig codeGenConfig;
    private final ServiceModel service;
    private final ServiceExamples examples;
    private final NamingStrategy namingStrategy;
    private final TypeUtils typeUtils;
    private final List shapeProcessors;
    private final Waiters waiters;
    private final String codeGenBinDirectory;

    public IntermediateModelBuilder(C2jModels models,
                                    String codeGenBinDirectory) {
        this.customConfig = models.customizationConfig();
        this.codeGenConfig = models.codeGenConfig();
        this.service = models.serviceModel();
        this.examples = models.examplesModel();
        this.namingStrategy = new DefaultNamingStrategy(service, customConfig);
        this.typeUtils = new TypeUtils(namingStrategy);
        this.shapeProcessors = createShapeProcessors();
        this.waiters = models.waitersModel();
        this.codeGenBinDirectory = codeGenBinDirectory;
    }



    /**
     * Create default shape processors.
     */
    private List createShapeProcessors() {
        final List processors = new ArrayList<>();
        processors.add(new AddInputShapes(this));
        processors.add(new AddOutputShapes(this));
        processors.add(new AddExceptionShapes(this));
        processors.add(new AddModelShapes(this));
        processors.add(new AddEmptyInputShape(this));
        processors.add(new AddEmptyOutputShape(this));
        return processors;
    }

    public IntermediateModel build() throws IOException{
        // Note: This needs to come before any pre/post processing of the
        // models, as the transformer must have access to the original shapes,
        // before any customizations have been applied (which modifies them).
        System.out.println("Applying customizations to examples...");
        new ExamplesCustomizer(service, customConfig).applyCustomizationsToExamples(examples);
        System.out.println("Examples customized.");

        CodegenCustomizationProcessor customization = DefaultCustomizationProcessor
                .getProcessorFor(customConfig);

        customization.preprocess(service);

        final Map operations = new TreeMap<>();
        final Map shapes = new HashMap<>();
        final Map waiters = new HashMap<>();
        final Map authorizers = new HashMap<>();

        operations.putAll(new AddOperations(this).constructOperations());
        waiters.putAll(new AddWaiters(this.waiters, operations, codeGenBinDirectory).constructWaiters());
        authorizers.putAll(new AddCustomAuthorizers(this.service, getNamingStrategy()).constructAuthorizers());

        OperationModel endpointOperation = null;
        boolean endpointCacheRequired = false;

        for (OperationModel o : operations.values()) {
            if (o.isEndpointOperation()) {
                endpointOperation = o;
            }

            if (o.getEndpointDiscovery() != null && o.getEndpointDiscovery().isRequired()) {
                endpointCacheRequired = true;
            }
        }

        if (endpointOperation != null) {
            endpointOperation.setEndpointCacheRequired(endpointCacheRequired);
        }

        for (IntermediateModelShapeProcessor processor : shapeProcessors) {
            shapes.putAll(processor.process(Collections.unmodifiableMap(operations),
                                            Collections.unmodifiableMap(shapes)));
        }

        System.out.println(shapes.size() + " shapes found in total.");

        IntermediateModel fullModel = new IntermediateModel(
                constructMetadata(service, codeGenConfig, customConfig), operations, shapes,
                customConfig, examples, endpointOperation, waiters, authorizers);

        customization.postprocess(fullModel);

        System.out.println(fullModel.getShapes().size() + " shapes remained after " +
                           "applying customizations.");

        Map trimmedShapes = removeUnusedShapes(fullModel);

        System.out.println(trimmedShapes.size() + " shapes remained after removing unused shapes.");

        IntermediateModel trimmedModel = new IntermediateModel(fullModel.getMetadata(),
                                                               fullModel.getOperations(),
                                                               trimmedShapes,
                                                               fullModel.getCustomizationConfig(),
                                                               fullModel.getExamples(),
                                                               fullModel.getEndpointOperation(),
                                                               fullModel.getWaiters(),
                                                               fullModel.getCustomAuthorizers());

        linkMembersToShapes(trimmedModel);
        linkOperationsToInputOutputShapes(trimmedModel);
        linkCustomAuthorizationToRequestShapes(trimmedModel);
        setupPackageName(trimmedShapes, trimmedModel);
        setupFullyQualifiedNameOfShapes(trimmedShapes, trimmedModel);

        return trimmedModel;
    }

    /**
     * Link the member to it's corresponding shape (if it exists).
     *
     * @param model Final IntermediateModel
     */
    private void linkMembersToShapes(IntermediateModel model) {
        for (Map.Entry entry : model.getShapes().entrySet()) {
            if (entry.getValue().getMembers() != null) {
                for (MemberModel member : entry.getValue().getMembers()) {
                    member.setShape(
                            Utils.findShapeModelByC2jNameIfExists(model, member.getC2jShape()));
                }
            }
        }
    }

    private void setupFullyQualifiedNameOfShapes(Map shapes, IntermediateModel model) {
        shapes.keySet()
              .forEach(shapeName -> {
                  ShapeModel inputShape = shapes.get(shapeName);
                  inputShape.setFullyQualifiedName(concatPackageName(inputShape, model)+ "." + inputShape.getShapeName());
             });
    }

    private void setupPackageName(Map shapes, IntermediateModel model) {
        shapes.keySet()
              .forEach(shapeName -> {
                  ShapeModel inputShape = shapes.get(shapeName);
                  inputShape.setPackageName(concatPackageName(inputShape, model));
              });
    }


    private String concatPackageName(ShapeModel inputShape, IntermediateModel model) {
        return model.getMetadata().getPackageName() + "." + inputShape.getType().toLowerCase();
    }

    private void linkOperationsToInputOutputShapes(IntermediateModel model) {
        for (Map.Entry entry : model.getOperations().entrySet()) {
            Operation operation = service.getOperations().get(entry.getKey());
            if (entry.getValue().getInput() != null) {
                entry.getValue().setInputShape(model.getShapes().get(entry.getValue().getInput().getSimpleType()));
            }
            if (operation.getOutput() != null) {
                String outputShapeName = operation.getOutput().getShape();
                // TODO need to figure this out for wrapper outputs.
                // See [JAVA-1556]

                // Only link when output shape is not a result wrapper. When it is a result wrapper
                // we only preserve the single member the wrapper has in the intermediate model
                // so this lookup will fail.
                if (StringUtils.isNullOrEmpty(operation.getOutput().getResultWrapper())) {
                    entry.getValue().setOutputShape(model.getShapeByC2jName(outputShapeName));
                }
            }

        }
    }

    private void linkCustomAuthorizationToRequestShapes(final IntermediateModel model) {
        model.getOperations().values().stream()
                .filter(OperationModel::isAuthenticated)
                .forEach(operation -> {
                    Operation c2jOperation = service.getOperation(operation.getOperationName());

                    ShapeModel inputShape = operation.getInputShape();
                    if (inputShape == null) {
                        throw new IllegalStateException(String.format("Operation %s has unknown input shape", operation.getOperationName()));
                    }

                    if (model.getMetadata().getProtocol() == Protocol.API_GATEWAY) {
                        linkCustomAuthorizationToRequestShapeForApiGatewayProtocol(model, c2jOperation, inputShape);
                    } else {
                        linkCustomAuthorizationToRequestShapeForAwsProtocol(c2jOperation.getAuthType(), inputShape);
                    }
                });
    }

    private void linkCustomAuthorizationToRequestShapeForApiGatewayProtocol(IntermediateModel model, Operation c2jOperation, ShapeModel inputShape) {
        if(AuthType.CUSTOM.equals(c2jOperation.getAuthType())) {
            AuthorizerModel auth = model.getCustomAuthorizers().get(c2jOperation.getAuthorizer());
            if (auth == null) {
                throw new IllegalStateException(String.format("Required custom auth not defined: %s", c2jOperation.getAuthorizer()));
            }
            inputShape.setRequestSignerClassFqcn(model.getMetadata().getPackageName() + ".auth." + auth.getInterfaceName());
        } else if (AuthType.IAM.equals(c2jOperation.getAuthType())) {
            model.getMetadata().setRequiresIamSigners(true);
            inputShape.setRequestSignerClassFqcn("com.amazonaws.opensdk.protect.auth.IamRequestSigner");
        }
    }

    private void linkCustomAuthorizationToRequestShapeForAwsProtocol(AuthType authType, ShapeModel inputShape) {
        switch (authType) {
            case V4:
                inputShape.setSignerType(SignerFactory.VERSION_FOUR_SIGNER);
                break;
            case V4_UNSIGNED_BODY:
                inputShape.setSignerType(SignerFactory.VERSION_FOUR_UNSIGNED_PAYLOAD_SIGNER);
                break;
            case NONE:
            case IAM:
                // just ignore this, this is the default value but only applicable to APIG generated clients
                break;
            default:
                throw new IllegalArgumentException("Unsupported authtype for AWS Request: " + authType);
        }
    }


    public CustomizationConfig getCustomConfig() {
        return customConfig;
    }

    public BasicCodeGenConfig getCodeGenConfig() {
        return codeGenConfig;
    }

    public ServiceModel getService() {
        return service;
    }

    public ServiceExamples getExamples() {
        return examples;
    }

    public NamingStrategy getNamingStrategy() {
        return namingStrategy;
    }

    public TypeUtils getTypeUtils() {
        return typeUtils;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy