Package com.streamreduce.rest.resource.gateway

Source Code of com.streamreduce.rest.resource.gateway.GatewayResource

/*
* Copyright 2012 Nodeable 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 com.streamreduce.rest.resource.gateway;

import com.google.common.collect.ImmutableSet;
import com.streamreduce.ConnectionNotFoundException;
import com.streamreduce.OutboundStorageException;
import com.streamreduce.core.event.EventId;
import com.streamreduce.core.model.Account;
import com.streamreduce.core.model.Connection;
import com.streamreduce.core.model.Event;
import com.streamreduce.core.model.InventoryItem;
import com.streamreduce.core.model.SobaObject;
import com.streamreduce.core.service.EventService;
import com.streamreduce.core.service.InventoryService;
import com.streamreduce.core.service.MessageService;
import com.streamreduce.core.service.OutboundStorageService;
import com.streamreduce.core.service.exception.InvalidCredentialsException;
import com.streamreduce.core.service.exception.InventoryItemNotFoundException;
import com.streamreduce.rest.resource.AbstractResource;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import net.sf.json.JSONArray;
import net.sf.json.JSONNull;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Component
@Path("gateway") //Exposed on /gateway
public class GatewayResource extends AbstractResource {

    @Autowired
    protected MessageService messageService;
    @Autowired
    protected OutboundStorageService outboundService;
    @Autowired
    protected InventoryService inventoryService;
    @Autowired
    protected EventService eventService;


    final Set<String> validMetricTypes = ImmutableSet.of(
            "ABSOLUTE",
            "DELTA"
    );


    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response createGatewayMessage(JSONObject json) {

        //GateProviderType type = GateProviderType.valueOf(getJSON(json, "providerId"));
        // TODO: switch on type?
        return createCustomConnectionMessage(json);

    }

    /**
     * Processes an IMG message that can be a message and/or metrics for a connection or inventory item.  Your message
     * payload must contain either message and/or metrics attributes.  If your message has the inventoryItemId
     * attribute, the message and/or metrics will be associated with the inventory item.  If the inventory item for the
     * provided inventoryItemId does not exist, one will be created for you.
     *
     * @param json the JSON representing the payload
     * @resource.representation.201 Returned when the create is successful
     * @resource.representation.403 Returned when the account the connection is in does not allow IMG messages
     * @resource.representation.405 Returned whenever the payload is invalid
     * @deprecated @see #createCustomConnectionMessage(JSONObject)
     */
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("generic")
    public Response createGenericConnectionMessage(JSONObject json) {
        return createCustomConnectionMessage(json);
    }

    /**
     * Processes an IMG message that can be a message and/or metrics for a connection or inventory item.  Your message
     * payload must contain either message and/or metrics attributes.  If your message has the inventoryItemId
     * attribute, the message and/or metrics will be associated with the inventory item.  If the inventory item for the
     * provided inventoryItemId does not exist, one will be created for you.
     *
     * @param json the JSON representing the payload
     * @resource.representation.201 Returned when the create is successful
     * @resource.representation.403 Returned when the account the connection is in does not allow IMG messages
     * @resource.representation.405 Returned whenever the payload is invalid
     */
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("custom")
    public Response createCustomConnectionMessage(JSONObject json) {

        Connection connection = securityService.getCurrentGatewayConnection();

        // Make sure this account has IMG support enabled
        if (connection.getAccount().getConfigValue(Account.ConfigKey.DISABLE_INBOUND_API)) {
            return error("This account is not provisioned for inbound payloads, please contact support@nodeable.com.",
                    Response.status(Response.Status.FORBIDDEN));
        }

        JSONArray entries = new JSONArray();

        if (json.containsKey("data")) {
            Object raw = json.get("data");

            if (!(raw instanceof JSONArray)) {
                return error("'data' must be an object array.", Response.status(Response.Status.BAD_REQUEST));
            }

            JSONArray rawEntries = (JSONArray) raw;

            for (Object rawEntry : rawEntries) {
                if (!(rawEntry instanceof JSONObject)) {
                    return error("Every object in the 'data' array should be an object.",
                            Response.status(Response.Status.BAD_REQUEST));
                }
                entries.add(rawEntry);
            }
        } else {
            // The request is for a single "entry"
            entries.add(json);
        }

        for (Object rawEntry : entries) {
            if (!(rawEntry instanceof JSONObject)) {
                return error("'data' entries must be JSON objects.", Response.status(Response.Status.BAD_REQUEST));
            }

            JSONObject entry = (JSONObject) rawEntry;
            Date dateGenerated = new Date();

            // Make sure that at least a message is being created or metrics are being gathered or both
            if (getJSON(entry, "message") == null && getJSON(entry, "metrics") == null) {
                return error("You must supply at least a 'message' or 'metrics' attribute in the payload.",
                        Response.status(Response.Status.BAD_REQUEST));
            }

            // Make sure hashtags is an array if specified (Type validation occurs later)
            if (getJSON(entry, "hashtags") != null && !(entry.get("hashtags") instanceof JSONArray)) {
                return error("'hashtags' must be a string array.", Response.status(Response.Status.BAD_REQUEST));
            }

            // Make sure metrics is an array if specified (Type validation occurs later)
            if (getJSON(entry, "metrics") != null && !(entry.get("metrics") instanceof JSONArray)) {
                return error("'metrics' must be an object array.", Response.status(Response.Status.BAD_REQUEST));
            }

            // Use the "generatedDate" attribute if applicable
            if (entry.containsKey("dateGenerated")) {
                try {
                    dateGenerated = new Date(Long.valueOf(entry.get("dateGenerated").toString()));
                } catch (NumberFormatException nfe) {
                    return error("'dateGenerated' must be an number.", Response.status(Response.Status.BAD_REQUEST));
                }
            }

            // See if the user specified an inventory item to associate the message and/or metrics to
            String inventoryItemId = getJSON(entry, "inventoryItemId");
            String message = getJSON(entry, "message");
            InventoryItem inventoryItem = null;

            if (inventoryItemId != null) {
                try {
                    inventoryItem = inventoryService.getInventoryItemForExternalId(connection, inventoryItemId);
                } catch (InventoryItemNotFoundException e) {
                    // This is handled below
                }

                if (inventoryItem == null) {
                    try {
                        // Create the inventory item referenced by the inventoryItemId if one does not exist
                        inventoryItem = inventoryService.createInventoryItem(connection, entry);
                    } catch (ConnectionNotFoundException e) {
                        // Should never happen
                        return error("No connection could be found based on the API key used.",
                                Response.status(Response.Status.BAD_REQUEST));
                    } catch (InvalidCredentialsException e) {
                        // Should never happen
                        return error("The credentials for the connection are invalid.",
                                Response.status(Response.Status.BAD_REQUEST));
                    } catch (IOException e) {
                        // Should never happen
                        return error("Unexpected IO exception.", Response.status(Response.Status.BAD_REQUEST));
                    }
                }
            }

            // Add tags to the Connection/InventoryItem for message creation only, do not persist (Validation occurs)
            JSONArray hashtags = entry.has("hashtags") ? entry.getJSONArray("hashtags") : null;
            SobaObject target = inventoryItemId == null ? connection : inventoryItem;
            // TODO: add Custom TAG hack, remove this and do it right
            target.addHashtag("custom");

            if (hashtags != null) {
                // Pull in hashtags from the actual IMG message
                for (Object rawHashtag : hashtags) {
                    if (rawHashtag instanceof String) {
                        target.addHashtag((String) rawHashtag);
                    } else {
                        return error("All hashtags specified in the 'hashtags' attribute must be strings.",
                                Response.status(Response.Status.BAD_REQUEST));
                    }
                }
            }

            // Validate the metrics
            JSONArray metrics = entry.has("metrics") ? entry.getJSONArray("metrics") : null;

            if (metrics != null) {
                for (Object rawMetric : metrics) {
                    if (rawMetric instanceof JSONObject) {
                        JSONObject metric = (JSONObject) rawMetric;
                        String name = getJSON(metric, "name");
                        String type = getJSON(metric, "type");
                        Object rawValue = metric.get("value");
                        String errorMessage = null;

                        if (!StringUtils.hasText(name)) {
                            errorMessage = "'name' is required for each metric in the 'metrics' attribute.";
                        } else if (!StringUtils.hasText("type") || !validMetricTypes.contains(type)) {
                            errorMessage = "'type' is required for each metric in the 'metrics' attribute and must " +
                                    "be either 'ABSOLUTE' or 'DELTA'.";
                        } else if (rawValue instanceof JSONNull || rawValue == null) {
                            errorMessage = "'value' is required for each metric in the 'metrics' attribute and " +
                                    "must be a valid numerical value.";
                        } else {
                            try {
                                Double.valueOf(rawValue.toString());
                            } catch (NumberFormatException e) {
                                errorMessage = "'value' for each metric in the 'metrics' attribute must be a " +
                                        "numerical value.";
                            }
                        }

                        if (errorMessage != null) {
                            return error(errorMessage, Response.status(Response.Status.BAD_REQUEST));
                        }
                    } else {
                        return error("All metrics specified in the 'metrics' attribute must be objects.",
                                Response.status(Response.Status.BAD_REQUEST));
                    }
                }
            }

            // optional config for user
            try {
                outboundService.sendRawMessage(entry, connection);
            } catch (OutboundStorageException e) {
                logger.error("Unable to send RawMessage outbound to Connection.", e);
            }

            // Create the event stream entry
            Map<String, Object> eventContext = new HashMap<>();
            // Persist the full payload
            eventContext.put("message", message);
            eventContext.put("dateGenerated", dateGenerated);
            eventContext.put("payload", entry);
            Event event = eventService.createEvent(EventId.ACTIVITY, target, eventContext);

            if (message != null) {
                if (inventoryItem != null) {
                    messageService.sendGatewayMessage(event, inventoryItem, dateGenerated.getTime());
                } else {
                    messageService.sendGatewayMessage(event, connection, dateGenerated.getTime());
                }
            }
        }

        return Response
                .status(Response.Status.CREATED)
                .build();
    }
}
TOP

Related Classes of com.streamreduce.rest.resource.gateway.GatewayResource

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.