io.syndesis.rest.v1.handler.connection.ConnectionActionHandler.java Source code

Java tutorial

Introduction

Here is the source code for io.syndesis.rest.v1.handler.connection.ConnectionActionHandler.java

Source

/**
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License 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 io.syndesis.rest.v1.handler.connection;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;

import javax.persistence.EntityNotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.syndesis.model.connection.Action;
import io.syndesis.model.connection.ActionDefinition;
import io.syndesis.model.connection.ConfigurationProperty;
import io.syndesis.model.connection.Connection;
import io.syndesis.model.connection.Connector;
import io.syndesis.model.connection.DataShape;
import io.syndesis.model.connection.DynamicActionMetadata;
import io.syndesis.model.connection.DynamicActionMetadata.ActionPropertySuggestion;
import io.syndesis.verifier.VerificationConfigurationProperties;

@Api(value = "actions")
public class ConnectionActionHandler {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    private final List<Action> actions;

    private final VerificationConfigurationProperties config;

    private final Connection connection;

    private final Connector connector;

    public ConnectionActionHandler(final Connection connection, final VerificationConfigurationProperties config) {
        this.connection = connection;
        this.config = config;

        final Optional<Connector> maybeConnector = connection.getConnector();
        connector = maybeConnector.orElseThrow(() -> new EntityNotFoundException(
                "Connection with id `" + connection.getId() + "` does not have a Connector defined"));

        actions = connector.getActions();
    }

    @POST
    @Path(value = "/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    @ApiOperation("Retrieves enriched action definition, that is an action definition that has input/output data shapes and property enums defined with respect to the given action properties")
    @ApiResponses(@ApiResponse(code = 200, response = ActionDefinition.class, message = "A map of zero or more action property suggestions keyed by the property name"))
    public ActionDefinition enrichWithMetadata(
            @PathParam("id") @ApiParam(required = true, example = "io.syndesis:salesforce-create-or-update:latest") final String id,
            final Map<String, String> properties) {

        final Action action = actions.stream().filter(a -> a.idEquals(id)).findAny()
                .orElseThrow(() -> new EntityNotFoundException("Action with id: " + id));

        final ActionDefinition defaultDefinition = action.getDefinition();

        if (!action.getTags().contains("dynamic")) {
            return defaultDefinition;
        }

        final String connectorId = connector.getId().get();

        final Map<String, String> parameters = new HashMap<>(
                Optional.ofNullable(properties).orElseGet(HashMap::new));
        // put all action parameters with `null` values
        defaultDefinition.getPropertyDefinitionSteps()
                .forEach(step -> step.getProperties().forEach((k, v) -> parameters.putIfAbsent(k, null)));

        // lastly put all connection properties
        parameters.putAll(connection.getConfiguredProperties());

        final Client client = createClient();
        final WebTarget target = client.target(
                String.format("http://%s/api/v1/connectors/%s/actions/%s", config.getService(), connectorId, id));

        final ActionDefinition.Builder enriched = new ActionDefinition.Builder().createFrom(defaultDefinition);
        final DynamicActionMetadata dynamicActionMetadata = target.request(MediaType.APPLICATION_JSON)
                .post(Entity.entity(parameters, MediaType.APPLICATION_JSON), DynamicActionMetadata.class);

        final Map<String, List<DynamicActionMetadata.ActionPropertySuggestion>> actionPropertySuggestions = dynamicActionMetadata
                .properties();
        actionPropertySuggestions.forEach((k, vals) -> enriched.replaceConfigurationProperty(k, b -> b.addAllEnum(
                vals.stream().map(s -> ConfigurationProperty.PropertyValue.Builder.from(s))::iterator)));

        //Setting the defaultValue as suggested by the metadata
        for (Entry<String, List<ActionPropertySuggestion>> suggestions : actionPropertySuggestions.entrySet()) {
            if (suggestions.getValue().size() == 1) {
                for (DynamicActionMetadata.ActionPropertySuggestion suggestion : suggestions.getValue()) {
                    enriched.replaceConfigurationProperty(suggestion.displayValue(),
                            v -> v.defaultValue(suggestion.value()));
                }
            }
        }

        final Object input = dynamicActionMetadata.inputSchema();
        if (shouldEnrichDataShape(defaultDefinition.getInputDataShape(), input)) {
            enriched.inputDataShape(new DataShape.Builder().type(typeFrom(input)).kind("json-schema")
                    .specification(specificationFrom(input)).build());
        }

        final Object output = dynamicActionMetadata.outputSchema();
        if (shouldEnrichDataShape(defaultDefinition.getOutputDataShape(), output)) {
            enriched.outputDataShape(new DataShape.Builder().type(typeFrom(output)).kind("json-schema")
                    .specification(specificationFrom(output)).build());
        }

        return enriched.build();
    }

    /* default */ Client createClient() {
        return ClientBuilder.newClient();
    }

    /* default */ static boolean shouldEnrichDataShape(final Optional<DataShape> maybeExistingDataShape,
            final Object received) {
        if (maybeExistingDataShape.isPresent() && received != null) {
            final DataShape existingDataShape = maybeExistingDataShape.get();

            return "json-schema".equals(existingDataShape.getKind())
                    && existingDataShape.getSpecification() == null;
        }

        return false;
    }

    /* default */ static String specificationFrom(final Object obj) {
        try {
            return MAPPER.writeValueAsString(obj);
        } catch (final JsonProcessingException e) {
            throw new IllegalArgumentException("Unable to serialize JSON", e);
        }
    }

    /* default */ static String typeFrom(final Object obj) {
        final JsonNode node = (JsonNode) obj;

        return node.get("title").asText();
    }

}