001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.component.extension;
018
019import java.io.Serializable;
020import java.util.List;
021import java.util.Map;
022import java.util.Set;
023
024import org.apache.camel.Component;
025import org.apache.camel.component.extension.ComponentVerifierExtensionHelper.ErrorAttribute;
026import org.apache.camel.component.extension.ComponentVerifierExtensionHelper.ErrorCode;
027import org.apache.camel.component.extension.ComponentVerifierExtensionHelper.ExceptionErrorAttribute;
028import org.apache.camel.component.extension.ComponentVerifierExtensionHelper.GroupErrorAttribute;
029import org.apache.camel.component.extension.ComponentVerifierExtensionHelper.HttpErrorAttribute;
030import org.apache.camel.component.extension.ComponentVerifierExtensionHelper.StandardErrorCode;
031
032/**
033 * Defines the interface used for validating component/endpoint parameters. The central method of this
034 * interface is {@link #verify(ComponentVerifierExtension.Scope, Map)} which takes a scope and a set of parameters which should be verified.
035 * <p/>
036 * The return value is a {@link ComponentVerifierExtension.Result} of the verification
037 *
038 */
039public interface ComponentVerifierExtension extends ComponentExtension {
040
041    /**
042     * Verify the given parameters against a provided scope.
043     *
044     * <p>
045     * The supported scopes are:
046     * <ul>
047     *   <li><strong>{@link ComponentVerifierExtension.Scope#PARAMETERS}</strong>: to validate that all the mandatory options are provided and syntactically correct.</li>
048     *   <li><strong>{@link ComponentVerifierExtension.Scope#CONNECTIVITY}</strong>: to validate that the given options (i.e. credentials, addresses) are correct. Verifying with this
049     *       scope typically implies reaching out to the backend via some sort of network connection.</li>
050     * </ul>
051     *
052     * @param scope the scope of the verification
053     * @param parameters the parameters to verify which are interpreted individually by each component verifier
054     * @return the verification result
055     */
056    Result verify(Scope scope, Map<String, Object> parameters);
057
058    /**
059     * The result of a verification
060     */
061    interface Result extends Serializable {
062
063        /**
064         * Status of the verification
065         */
066        enum Status {
067            /**
068             * Verification succeeded
069             */
070            OK,
071            /**
072             * Error occurred during the verification
073             */
074            ERROR,
075            /**
076             * Verification is not supported. This can depend on the given scope.
077             */
078            UNSUPPORTED
079        }
080
081        /**
082         * Scope of the verification. This is the scope given to the call to {@link #verify(Scope, Map)}  and
083         * can be used for correlation.
084         *
085         * @return the scope against which the parameters have been validated.
086         */
087        Scope getScope();
088
089        /**
090         * Result of the validation as status. This should be the first datum to check after a verification
091         * happened.
092         *
093         * @return the status
094         */
095        Status getStatus();
096
097        /**
098         * Collection of errors happened for the verification. This list is empty (but non null) if the verification
099         * succeeded.
100         *
101         * @return a list of errors. Can be empty when verification was successful
102         */
103        List<VerificationError> getErrors();
104    }
105
106    /**
107     * The scope defines how the parameters should be verified.
108     */
109    enum Scope {
110        /**
111         * Only validate the parameters for their <em>syntactic</em> soundness. Verifications in this scope should
112         * be as fast as possible
113         */
114        PARAMETERS,
115
116        /**
117         * Reach out to the backend and verify that a connection can be established. This means, if the verification
118         * in this scope succeeds, then it can safely be assumed that the component can be used.
119         */
120        CONNECTIVITY;
121
122        /**
123         * Get an instance of this scope from a string representation
124         *
125         * @param scope the scope as string, which can be in any case
126         * @return the scope enum represented by this string
127         */
128        public static Scope fromString(String scope) {
129            return Scope.valueOf(scope != null ? scope.toUpperCase() : null);
130        }
131    }
132
133    // =============================================================================================
134
135    /**
136     * This interface represents a detailed error in case when the verification fails.
137     */
138    interface VerificationError extends Serializable {
139
140        /**
141         * The overall error code, which can be either a {@link StandardCode} or a custom code. It is
142         * recommended to stick to the predefined standard codes
143         *
144         * @return the general error code.
145         */
146        Code getCode();
147
148        /**
149         * A human readable description of the error in plain english
150         *
151         * @return the error description (if available)
152         */
153        String getDescription();
154
155        /**
156         * A set of input parameter names which fails the verification. These are keys to the parameter provided
157         * to {@link #verify(ComponentVerifierExtension.Scope, Map)}.
158         *
159         * @return the parameter names which are malformed and caused the failure of the validation
160         */
161        Set<String> getParameterKeys();
162
163        /**
164         * Details about the failed verification. The keys can be either predefined values
165         * ({@link ExceptionAttribute}, {@link HttpAttribute}, {@link GroupAttribute}) or it can be free-form
166         * custom keys specific to a component. The standard attributes are defined as enums in all uppercase (with
167         * underscore as separator), custom attributes are supposed to be in all lower case (also with underscores
168         * as separators)
169         *
170         * @return a number of key/value pair with additional information related to the verification.
171         */
172        Map<Attribute, Object> getDetails();
173
174        /**
175         * Get a single detail for a given attribute
176         *
177         * @param attribute the attribute to lookup
178         * @return the detail value or null if no such attribute exists
179         */
180        default Object getDetail(Attribute attribute) {
181            Map<Attribute, Object> details = getDetails();
182            if (details != null) {
183                return details.get(attribute);
184            }
185            return null;
186        }
187
188        /**
189         * Get a single detail for a given attribute
190         *
191         * @param attribute the attribute to lookup
192         * @return the detail value or null if no such attribute exists
193         */
194        default Object getDetail(String attribute) {
195            return getDetail(asAttribute(attribute));
196        }
197
198        /**
199         * Convert a string to an {@link Code}
200         *
201         * @param code the code to convert. It should be in all lower case (with
202         *             underscore as a separator) to avoid overlap with {@link StandardCode}
203         * @return error code
204         */
205        static Code asCode(String code) {
206            return new ErrorCode(code);
207        }
208
209        /**
210         * Convert a string to an {@link Attribute}
211         *
212         * @param attribute the string representation of an attribute to convert. It should be in all lower case (with
213         *                  underscore as a separator) to avoid overlap with standard attributes like {@link ExceptionAttribute},
214         *                  {@link HttpAttribute} or {@link GroupAttribute}
215         * @return generated attribute
216         */
217        static Attribute asAttribute(String attribute) {
218            return new ErrorAttribute(attribute);
219        }
220
221        /**
222         * Interface defining an error code. This is implemented by the {@link StandardCode} but also
223         * own code can be generated by implementing this interface. This is best done via {@link #asCode(String)}
224         * If possible, the standard codes should be reused
225         */
226        interface Code extends Serializable {
227            /**
228             * Name of the code. All uppercase for standard codes, all lower case for custom codes.
229             * Separator between two words is an underscore.
230             *
231             * @return code name
232             */
233            String name();
234
235            /**
236             * Bean style accessor to name.
237             * This is required for framework like Jackson using bean convention for object serialization.
238             *
239             * @return code name
240             */
241            default String getName() {
242                return name();
243            }
244        }
245
246        /**
247         * Standard set of error codes
248         */
249        interface StandardCode extends Code {
250            /**
251             * Authentication failed
252             */
253            StandardCode AUTHENTICATION = new StandardErrorCode("AUTHENTICATION");
254            /**
255             * An exception occurred
256             */
257            StandardCode EXCEPTION = new StandardErrorCode("EXCEPTION");
258            /**
259             * Internal error while performing the verification
260             */
261            StandardCode INTERNAL = new StandardErrorCode("INTERNAL");
262            /**
263             * A mandatory parameter is missing
264             */
265            StandardCode MISSING_PARAMETER = new StandardErrorCode("MISSING_PARAMETER");
266            /**
267             * A given parameter is not known to the component
268             */
269            StandardCode UNKNOWN_PARAMETER = new StandardErrorCode("UNKNOWN_PARAMETER");
270            /**
271             * A given parameter is illegal
272             */
273            StandardCode ILLEGAL_PARAMETER = new StandardErrorCode("ILLEGAL_PARAMETER");
274            /**
275             * A combination of parameters is illegal. See {@link VerificationError#getParameterKeys()} for the set
276             * of affected parameters
277             */
278            StandardCode ILLEGAL_PARAMETER_GROUP_COMBINATION = new StandardErrorCode("ILLEGAL_PARAMETER_GROUP_COMBINATION");
279            /**
280             * A parameter <em>value</em> is not valid
281             */
282            StandardCode ILLEGAL_PARAMETER_VALUE = new StandardErrorCode("ILLEGAL_PARAMETER_VALUE");
283            /**
284             * A group of parameters is not complete in order to be valid
285             */
286            StandardCode INCOMPLETE_PARAMETER_GROUP = new StandardErrorCode("INCOMPLETE_PARAMETER_GROUP");
287            /**
288             * The verification is not supported
289             */
290            StandardCode UNSUPPORTED = new StandardErrorCode("UNSUPPORTED");
291            /**
292             * The requested {@link Scope} is not supported
293             */
294            StandardCode UNSUPPORTED_SCOPE = new StandardErrorCode("UNSUPPORTED_SCOPE");
295            /**
296             * The requested {@link Component} is not supported
297             */
298            StandardCode UNSUPPORTED_COMPONENT = new StandardErrorCode("UNSUPPORTED_COMPONENT");
299            /**
300             * Generic error which is explained in more details with {@link VerificationError#getDetails()}
301             */
302            StandardCode GENERIC = new StandardErrorCode("GENERIC");
303        }
304
305        /**
306         * Interface defining an attribute which is a key for the detailed error messages. This is implemented by several
307         * standard enums like {@link ExceptionAttribute}, {@link HttpAttribute} or {@link GroupAttribute} but can also
308         * implemented for component specific details. This is best done via {@link #asAttribute(String)}
309         * or using one of the other builder method in this error builder (like
310         * {@link org.apache.camel.component.extension.verifier.ResultErrorBuilder#detail(String, Object)}
311         * <p>
312         * With respecting to name, the same rules as for {@link Code} apply: Standard attributes are all upper case with _
313         * as separators, whereas custom attributes are lower case with underscore separators.
314         */
315        interface Attribute extends Serializable {
316            /**
317             * Name of the attribute. All uppercase for standard attributes and all lower case for custom attributes.
318             * Separator between words is an underscore.
319             *
320             * @return attribute name
321             */
322            String name();
323
324            /**
325             * Bean style accessor to name;
326             * This is required for framework like Jackson using bean convention for object serialization.
327             *
328             * @return attribute name
329             */
330            default String getName() {
331                return name();
332            }
333        }
334
335        /**
336         * Attributes for details about an exception that was raised
337         */
338        interface ExceptionAttribute extends Attribute {
339            /**
340             * The exception object that has been thrown. Note that this can be a complex
341             * object and can cause large content when e.g. serialized as JSON
342             */
343            ExceptionAttribute EXCEPTION_INSTANCE = new ExceptionErrorAttribute("EXCEPTION_INSTANCE");
344            /**
345             * The exception class
346             */
347            ExceptionAttribute EXCEPTION_CLASS = new ExceptionErrorAttribute("EXCEPTION_CLASS");
348        }
349
350        /**
351         * HTTP related error details
352         */
353        interface HttpAttribute extends Attribute {
354            /**
355             * The erroneous HTTP code that occurred
356             */
357            HttpAttribute HTTP_CODE = new HttpErrorAttribute("HTTP_CODE");
358            /**
359             * HTTP response's body
360             */
361            HttpAttribute HTTP_TEXT = new HttpErrorAttribute("HTTP_TEXT");
362            /**
363             * If given as details, specifies that a redirect happened and the
364             * content of this detail is the redirect URL
365             */
366            HttpAttribute HTTP_REDIRECT = new HttpErrorAttribute("HTTP_REDIRECT");
367        }
368
369        /**
370         * Group related details
371         */
372        interface GroupAttribute extends Attribute {
373            /**
374             * Group name
375             */
376            GroupAttribute GROUP_NAME = new GroupErrorAttribute("GROUP_NAME");
377            /**
378             * Options for the group
379             */
380            GroupAttribute GROUP_OPTIONS = new GroupErrorAttribute("GROUP_OPTIONS");
381        }
382    }
383}