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.rest;
018
019import java.util.HashMap;
020import java.util.Map;
021
022import org.apache.camel.Component;
023import org.apache.camel.ComponentVerifier;
024import org.apache.camel.VerifiableComponent;
025import org.apache.camel.impl.verifier.CatalogVerifierCustomizer;
026import org.apache.camel.impl.verifier.DefaultComponentVerifier;
027import org.apache.camel.impl.verifier.ResultBuilder;
028import org.apache.camel.impl.verifier.ResultErrorBuilder;
029import org.apache.camel.runtimecatalog.JSonSchemaHelper;
030import org.apache.camel.runtimecatalog.RuntimeCamelCatalog;
031import org.apache.camel.spi.RestConsumerFactory;
032import org.apache.camel.spi.RestProducerFactory;
033import org.apache.camel.util.ObjectHelper;
034import org.apache.camel.util.function.Suppliers;
035
036public class RestComponentVerifier extends DefaultComponentVerifier {
037    private static final CatalogVerifierCustomizer CUSTOMIZER = new CatalogVerifierCustomizer().excludeUnknown();
038    private final RestComponent component;
039
040    RestComponentVerifier(RestComponent component) {
041        super("rest", component.getCamelContext());
042
043        this.component = component;
044    }
045
046    // *********************************
047    // Parameters validation
048    // *********************************
049
050    @Override
051    protected Result verifyParameters(Map<String, Object> parameters) {
052        ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.PARAMETERS);
053
054        // Validate using the catalog but do not report unknown options as error
055        // as the may be used to customize the underlying component
056        super.verifyParametersAgainstCatalog(builder, parameters, CUSTOMIZER);
057
058        verifyUnderlyingComponent(Scope.PARAMETERS, builder, parameters);
059
060        return builder.build();
061    }
062
063    // *********************************
064    // Connectivity validation
065    // *********************************
066
067    @Override
068    protected Result verifyConnectivity(Map<String, Object> parameters) {
069        ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.CONNECTIVITY);
070
071        verifyUnderlyingComponent(Scope.CONNECTIVITY, builder, parameters);
072
073        return builder.build();
074    }
075
076    // *********************************
077    // Helpers
078    // *********************************
079
080    protected void verifyUnderlyingComponent(Scope scope, ResultBuilder builder, Map<String, Object> parameters) {
081        // componentName is required for validation even at runtime camel might
082        // be able to find a suitable component at runtime.
083        String componentName = (String)parameters.get("componentName");
084        if (ObjectHelper.isNotEmpty(componentName)) {
085            try {
086                Component component = getTransportComponent(componentName);
087                if (component instanceof VerifiableComponent) {
088                    final RuntimeCamelCatalog catalog = getCamelContext().getRuntimeCamelCatalog();
089                    final String json = catalog.componentJSonSchema("rest");
090                    final Map<String, Object> restParameters = new HashMap<>(parameters);
091
092                    for (Map<String, String> m : JSonSchemaHelper.parseJsonSchema("componentProperties", json, true)) {
093                        String name = m.get("name");
094                        Object val = restParameters.remove(name);
095                        if (val != null) {
096                            // Add rest prefix to properties belonging to the rest
097                            // component so the underlying component know we want
098                            // to validate rest-related stuffs.
099                            restParameters.put("rest." + name, parameters.get(name));
100                        }
101                    }
102                    for (Map<String, String> m : JSonSchemaHelper.parseJsonSchema("properties", json, true)) {
103                        String name = m.get("name");
104                        Object val = restParameters.remove(name);
105                        if (val != null) {
106                            // Add rest prefix to properties belonging to the rest
107                            // component so the underlying component know we want
108                            // to validate rest-related stuffs.
109                            restParameters.put("rest." + name, parameters.get(name));
110                        }
111                    }
112
113                    ComponentVerifier verifier = ((VerifiableComponent)component).getVerifier();
114
115                    // restParameters now should contains rest-component related
116                    // properties with "rest." prefix and all the remaining can
117                    // be used to customize the underlying component (i.e. http
118                    // proxies, auth, etc)
119                    Result result = verifier.verify(scope, restParameters);
120
121                    // Combine errors and add an information about the component
122                    // they comes from
123                    for (VerificationError error : result.getErrors()) {
124                        builder.error(
125                            ResultErrorBuilder.fromError(error)
126                                .detail("component", componentName)
127                                .build()
128                        );
129                    }
130                } else {
131                    builder.error(
132                        ResultErrorBuilder.withUnsupportedComponent(componentName).build()
133                    );
134                }
135            } catch (Exception e) {
136                builder.error(
137                    ResultErrorBuilder.withException(e).build()
138                );
139            }
140        } else {
141            builder.error(ResultErrorBuilder.withMissingOption("componentName").build());
142        }
143    }
144
145    private Component getTransportComponent(String componentName) throws Exception {
146        return Suppliers.firstMatching(
147            comp -> comp != null && (comp instanceof RestConsumerFactory || comp instanceof RestProducerFactory),
148            () -> getCamelContext().getRegistry().lookupByNameAndType(componentName, Component.class),
149            () -> getCamelContext().getComponent(componentName, true, false)
150        ).orElse(null);
151    }
152}