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

software.amazon.smithy.model.traits.EndpointTrait Maven / Gradle / Ivy

/*
 * Copyright 2019 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.smithy.model.traits;

import static java.lang.String.format;
import static software.amazon.smithy.model.pattern.SmithyPattern.Segment;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.StringTokenizer;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.pattern.InvalidPatternException;
import software.amazon.smithy.model.pattern.SmithyPattern;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.utils.ToSmithyBuilder;

/**
 * Defines custom endpoint bindings of an operation.
 */
public final class EndpointTrait extends AbstractTrait implements ToSmithyBuilder {
    public static final ShapeId ID = ShapeId.from("smithy.api#endpoint");

    private final SmithyPattern hostPrefix;

    private EndpointTrait(Builder builder) {
        super(ID, builder.sourceLocation);
        String hostPrefix = Objects.requireNonNull(builder.hostPrefix, "hostPrefix not set");

        StringTokenizer tokenizer = new StringTokenizer(hostPrefix, "{}", true);
        List segments = new ArrayList<>();
        int position = 0;
        while (tokenizer.hasMoreTokens()) {
            String token = tokenizer.nextToken();
            if (token.equals("{")) {
                if (!segments.isEmpty() && segments.get(segments.size() - 1).isLabel()) {
                    throw new InvalidPatternException(
                            format("Host labels must not be adjacent in a pattern. Found %s", hostPrefix));
                }
                // If we found a label, grab the content and the close brace
                for (int i = 0; i < 2; i++) {
                    if (tokenizer.hasMoreTokens()) {
                        token += tokenizer.nextToken();
                    } else {
                        throw new InvalidPatternException("Unclosed label found in pattern at: ." + position);
                    }
                }
            } else if (token.equals("}")) {
                throw new InvalidPatternException("Literal segments must not contain `}`. Found at: " + position);
            }
            segments.add(Segment.parse(token, position));
            position += token.length();
        }

        this.hostPrefix = SmithyPattern.builder()
                .allowsGreedyLabels(false)
                .segments(segments)
                .pattern(hostPrefix)
                .build();
    }

    public static final class Provider extends AbstractTrait.Provider {
        public Provider() {
            super(ID);
        }

        @Override
        public Trait createTrait(ShapeId target, Node value) {
            EndpointTrait.Builder builder = builder().sourceLocation(value);
            value.expectObjectNode().expectStringMember("hostPrefix", builder::hostPrefix);
            EndpointTrait result = builder.build();
            result.setNodeCache(value);
            return result;
        }
    }

    public SmithyPattern getHostPrefix() {
        return hostPrefix;
    }

    /**
     * @return Returns a builder used to create an Endpoint trait.
     */
    public static Builder builder() {
        return new Builder();
    }

    @Override
    public Builder toBuilder() {
        return new Builder().sourceLocation(getSourceLocation()).hostPrefix(hostPrefix.toString());
    }

    @Override
    protected Node createNode() {
        return new ObjectNode(Collections.emptyMap(), getSourceLocation())
                .withMember("hostPrefix", Node.from(hostPrefix.toString()));
    }

    public static final class Builder extends AbstractTraitBuilder {
        private String hostPrefix;

        private Builder() {}

        public Builder hostPrefix(String hostPrefix) {
            this.hostPrefix = hostPrefix;
            return this;
        }

        @Override
        public EndpointTrait build() {
            return new EndpointTrait(this);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy