001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2016 ForgeRock AS.
015 */
016
017package org.forgerock.api.models;
018
019import static org.forgerock.api.util.ValidationUtil.containsWhitespace;
020import static org.forgerock.api.util.ValidationUtil.isEmpty;
021
022import java.lang.reflect.Method;
023import java.util.Objects;
024
025import com.fasterxml.jackson.annotation.JsonInclude;
026import com.fasterxml.jackson.annotation.JsonProperty;
027import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
028import org.wrensecurity.guava.common.base.Strings;
029
030import org.forgerock.api.ApiValidationException;
031
032/**
033 * Class that represents the Action operation type in API descriptor.
034 */
035@JsonDeserialize(builder = Action.Builder.class)
036@JsonInclude(JsonInclude.Include.NON_NULL)
037public final class Action extends Operation implements Comparable<Action> {
038
039    private final String name;
040    private final Schema request;
041    private final Schema response;
042
043    /**
044     * Protected contstructor of the Action.
045     *
046     * @param builder Action Builder
047     */
048    private Action(Builder builder) {
049        super(builder);
050        this.name = builder.name;
051        this.request = builder.request;
052        this.response = builder.response;
053
054        if (isEmpty(name)) {
055            throw new ApiValidationException("name is required");
056        }
057        if (containsWhitespace(name)) {
058            throw new ApiValidationException("name contains whitespace");
059        }
060    }
061
062    /**
063     * Getter of the ID.
064     *
065     * @return Id
066     */
067    public String getName() {
068        return name;
069    }
070
071    /**
072     * Getter of the request.
073     *
074     * @return Request
075     */
076    public Schema getRequest() {
077        return request;
078    }
079
080    /**
081     * Getter of the response.
082     *
083     * @return Response
084     */
085    public Schema getResponse() {
086        return response;
087    }
088
089    @Override
090    public boolean equals(Object o) {
091        if (this == o) {
092            return true;
093        }
094        if (o == null || getClass() != o.getClass()) {
095            return false;
096        }
097        if (!super.equals(o)) {
098            return false;
099        }
100        Action action = (Action) o;
101        return super.equals(o)
102                && Objects.equals(name, action.name)
103                && Objects.equals(request, action.request)
104                && Objects.equals(response, action.response);
105    }
106
107    @Override
108    public int hashCode() {
109        return Objects.hash(super.hashCode(), name, request, response);
110    }
111
112    /**
113     * Creates a new builder for Action.
114     *
115     * @return New builder instance
116     */
117    public static final Builder action() {
118        return new Builder();
119    }
120
121    /**
122     * Allocates the Action operation type to the given Resource Builder.
123     *
124     * @param resourceBuilder - Resource Builder to add the operation
125     */
126    @Override
127    protected void allocateToResource(Resource.Builder resourceBuilder) {
128        resourceBuilder.action(this);
129    }
130
131    /**
132     * Builds an Action object using the data in the annotation.
133     * @param action The annotation that holds the data for the built object.
134     * @param annotated The action method.
135     * @param descriptor The root descriptor to add definitions to.
136     * @param relativeType The type relative to which schema resources should be resolved.
137     * @return Action instance.
138     */
139    public static Action fromAnnotation(org.forgerock.api.annotations.Action action, Method annotated,
140            ApiDescription descriptor, Class<?> relativeType) {
141        Builder builder = action();
142        String specifiedName = action.name();
143        if (Strings.isNullOrEmpty(specifiedName)) {
144            if (annotated == null) {
145                throw new IllegalArgumentException("Action does not have a name: " + action);
146            }
147            specifiedName = annotated.getName();
148        }
149        return builder.name(specifiedName)
150                .request(Schema.fromAnnotation(action.request(), descriptor, relativeType))
151                .response(Schema.fromAnnotation(action.response(), descriptor, relativeType))
152                .detailsFromAnnotation(action.operationDescription(), descriptor, relativeType)
153                .build();
154    }
155
156    /**
157     * Compares two strings lexicographically.
158     * @param action Action to compare to
159     * @return  the value {@code 0} if the argument string is equal to
160     *          this string; a value less than {@code 0} if this string
161     *          is lexicographically less than the string argument; and a
162     *          value greater than {@code 0} if this string is
163     *          lexicographically greater than the string argument.
164     */
165    @Override
166    public int compareTo(Action action) {
167        return this.name.compareTo(action.getName());
168    }
169
170    /**
171     * Builder class for creating the Action.
172     */
173    public static final class Builder extends Operation.Builder<Builder> {
174
175        private String name;
176        private Schema request;
177        private Schema response;
178
179        @Override
180        protected Builder self() {
181            return this;
182        }
183
184        /**
185         * Set the Id.
186         *
187         * @param name Action name
188         * @return Builder
189         */
190        @JsonProperty("name")
191        public Builder name(String name) {
192            this.name = name;
193            return this;
194        }
195
196        /**
197         * Set the request.
198         *
199         * @param request Action request
200         * @return Builder
201         */
202        @JsonProperty("request")
203        public Builder request(Schema request) {
204            this.request = request;
205            return this;
206        }
207
208        /**
209         * Set the response.
210         *
211         * @param response Action resopnse
212         * @return Builder
213         */
214        @JsonProperty("response")
215        public Builder response(Schema response) {
216            this.response = response;
217            return this;
218        }
219
220        /**
221         * Builds the Action instance.
222         *
223         * @return Action instance
224         */
225        public Action build() {
226            return new Action(this);
227        }
228    }
229
230}