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