com.baasbox.commands.DocumentsResource.java Source code

Java tutorial

Introduction

Here is the source code for com.baasbox.commands.DocumentsResource.java

Source

/*
 * Copyright (c) 2014.
 *
 * BaasBox - info@baasbox.com
 *
 * 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.baasbox.commands;

import com.baasbox.commands.exceptions.CommandException;
import com.baasbox.commands.exceptions.CommandExecutionException;
import com.baasbox.commands.exceptions.CommandParsingException;
import com.baasbox.controllers.actions.exceptions.RidNotFoundException;
import com.baasbox.dao.exception.*;
import com.baasbox.enumerations.Permissions;
import com.baasbox.exception.AclNotValidException;
import com.baasbox.exception.RoleNotFoundException;
import com.baasbox.exception.UserNotFoundException;
import com.baasbox.service.scripting.base.JsonCallback;
import com.baasbox.service.scripting.js.Json;
import com.baasbox.service.storage.DocumentService;
import com.baasbox.util.JSONFormats;
import com.baasbox.util.QueryParams;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.BooleanNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.orientechnologies.orient.core.exception.ODatabaseException;
import com.orientechnologies.orient.core.exception.OSecurityException;
import com.orientechnologies.orient.core.record.impl.ODocument;

import org.apache.commons.lang.exception.ExceptionUtils;

import com.baasbox.service.logging.BaasBoxLogger;

import java.io.IOException;
import java.util.List;
import java.util.UUID;

/**
 * {resource: 'documents',
 *  name: <'get'|'list'|'post'|'put'|'delete',
 *  params; {
 *      collection: <collection>,
 *
 *      query: <query>,*
 *
 *      id: <id>*,
 *
 *      grants: {}
 *  }}
 *
 *
 *  {resource: 'documents',
 *   name: 'get',
 *   params: {
 *       collection: <collection>,
 *       id: <uuid>,
 *
 *   }}
 *
 *
 *  {resource: 'documents',
 *   name: 'grant',
 *   params: {
 *       collection: <collection>,
 *       id: <uuid>,
 *       users: {read: [,,,],
 *               update: [,,,,],
 *               delete: [....],
 *               all: [,,,,],
 *               },
 *       roles: {read: [,,,,],
 *               update: [,,,,],
 *               delete: [,,,,,],
 *               all: [,,,,,]
 *   }}
 *
 *
 *
 * Created by Andrea Tortorella on 30/06/14.
 */
class DocumentsResource extends BaseRestResource {
    public static final Resource INSTANCE = new DocumentsResource();

    private static final String RESOURCE_NAME = "documents";

    private static final String COLLECTION = "collection";
    private static final String QUERY = "query";
    private static final String DATA = "data";
    private static final String AUTHOR = "author";

    @Override
    protected ImmutableMap.Builder<String, ScriptCommand> baseCommands() {
        return super.baseCommands().put("grant", new ScriptCommand() {
            @Override
            public JsonNode execute(JsonNode command, JsonCallback callback) throws CommandException {
                return grant(command, true);
            }
        }).put("revoke", new ScriptCommand() {
            @Override
            public JsonNode execute(JsonNode command, JsonCallback callback) throws CommandException {
                return grant(command, false);
            }
        });
    }

    private JsonNode grant(JsonNode command, boolean grant) throws CommandException {
        validateHasParams(command);
        String coll = getCollectionName(command);
        String id = getDocumentId(command);

        try {
            try {
                String rid = DocumentService.getRidByString(id, true);
                alterGrants(command, coll, rid, true, grant);
                alterGrants(command, coll, rid, false, grant);
            } catch (Exception e) {
                BaasBoxLogger.error("error", e);
                throw e;
            }
        } catch (UserNotFoundException e) {

            throw new CommandExecutionException(command, "user not found exception");
        } catch (DocumentNotFoundException e) {
            throw new CommandExecutionException(command, "document not found exception");
        } catch (InvalidCollectionException e) {
            throw new CommandExecutionException(command, "invalid colleciton exception");
        } catch (InvalidModelException e) {
            throw new CommandExecutionException(command, "invalid model exception");
        } catch (RoleNotFoundException e) {
            throw new CommandExecutionException(command, "role not found exception");
        } catch (RidNotFoundException e) {
            throw new CommandExecutionException(command, "document " + id + " not found");
        }
        return BooleanNode.getTrue();
    }

    @Override
    protected JsonNode delete(JsonNode command) throws CommandException {
        validateHasParams(command);
        String coll = getCollectionName(command);
        String id = getDocumentId(command);
        String rid;
        try {
            rid = DocumentService.getRidByString(id, true);
        } catch (RidNotFoundException e) {
            return null;
        }
        try {
            DocumentService.delete(coll, rid);
        } catch (OSecurityException e) {
            throw new CommandExecutionException(command, "you don't have permissions to delete: " + id);
        } catch (ODatabaseException e) {
            return null;
        } catch (Throwable e) {
            throw new CommandExecutionException(command,
                    "error executing delete command on " + id + " message: " + ExceptionUtils.getMessage(e));
        }
        return null;
    }

    private String getCollectionName(JsonNode command) throws CommandParsingException {
        JsonNode params = command.get(ScriptCommand.PARAMS);
        JsonNode collNode = params.get(COLLECTION);
        if (collNode == null || !collNode.isTextual()) {
            throw new CommandParsingException(command,
                    "invalid collection param: " + (collNode == null ? "null" : collNode.toString()));
        }
        return collNode.asText();
    }

    @Override
    protected JsonNode put(JsonNode command) throws CommandException {
        validateHasParams(command);
        String coll = getCollectionName(command);
        JsonNode data = getData(command);
        String id = getDocumentId(command);
        try {
            String rid = null;
            try {
                rid = DocumentService.getRidByString(id, true);
            } catch (RidNotFoundException e) {
                //swallow
            }
            ODocument doc = null;
            if (rid != null) //update
                doc = DocumentService.update(coll, rid, (ObjectNode) data);
            else // save
                doc = DocumentService.create(coll, (ObjectNode) data);
            String json = JSONFormats.prepareDocToJson(doc, JSONFormats.Formats.DOCUMENT_PUBLIC);
            ObjectNode node = (ObjectNode) Json.mapper().readTree(json);
            node.remove(TO_REMOVE);
            //node.remove("@rid");
            return node;
        } catch (RidNotFoundException e) {
            throw new CommandExecutionException(command, "document: " + id + " does not exists");
        } catch (UpdateOldVersionException e) {
            throw new CommandExecutionException(command, "document: " + id + " has a more recent version");
        } catch (DocumentNotFoundException e) {
            throw new CommandExecutionException(command, "document: " + id + " does not exists");
        } catch (InvalidCollectionException e) {
            throw new CommandExecutionException(command, "invalid collection: " + coll);
        } catch (InvalidModelException e) {
            throw new CommandExecutionException(command,
                    "error updating document (is the provided ID belonging to the provided collection?): " + id
                            + " message: " + ExceptionUtils.getMessage(e));
        } catch (JsonProcessingException e) {
            throw new CommandExecutionException(command,
                    "data do not represents a valid document, message: " + ExceptionUtils.getMessage(e));
        } catch (IOException e) {
            throw new CommandExecutionException(command,
                    "error updating document: " + id + " message:" + ExceptionUtils.getMessage(e));
        } catch (AclNotValidException e) {
            throw new CommandExecutionException(command, "error updating document (check the ACL fields): " + id
                    + " message:" + ExceptionUtils.getMessage(e));
        } catch (Throwable e) {
            BaasBoxLogger.error("error updating document: " + id, e);
            throw new CommandExecutionException(command,
                    " error updating document: " + id + " message:" + ExceptionUtils.getMessage(e));
        }
    }

    @Override
    protected JsonNode post(JsonNode command) throws CommandException {
        validateHasParams(command);
        String collection = getCollectionName(command);
        JsonNode data = getData(command);
        String authorOverride = getAuthorOverride(command);

        try {
            ODocument doc = DocumentService.create(collection, (ObjectNode) data);
            //                    DocumentService.createOnBehalf(collection,authorOverride,data);
            if (doc == null) {
                return null;
            }
            String fmt = JSONFormats.prepareDocToJson(doc, JSONFormats.Formats.DOCUMENT_PUBLIC);
            JsonNode node = Json.mapper().readTree(fmt);
            ObjectNode n = (ObjectNode) node;
            n.remove(TO_REMOVE);
            //            n.remove("@rid");
            return n;
        } catch (InvalidCollectionException throwable) {
            throw new CommandExecutionException(command, "invalid collection: " + collection);
        } catch (InvalidModelException e) {
            throw new CommandExecutionException(command,
                    "error creating document: " + ExceptionUtils.getMessage(e));
        } catch (Throwable e) {
            throw new CommandExecutionException(command,
                    "error creating document: " + ExceptionUtils.getMessage(e));
        }
    }

    private String getAuthorOverride(JsonNode command) throws CommandParsingException {
        JsonNode node = command.get(ScriptCommand.PARAMS).get(AUTHOR);
        if (node != null && !node.isTextual()) {
            throw new CommandParsingException(command, "author must be a string");

        } else if (node != null) {
            return node.asText();
        }
        return null;
    }

    private JsonNode getData(JsonNode command) throws CommandParsingException {
        JsonNode node = command.get(ScriptCommand.PARAMS).get(DATA);
        if (node == null || (!node.isObject())) {
            throw new CommandParsingException(command, "missing required data parameter");
        }
        return node;
    }

    @Override
    protected JsonNode list(JsonNode command) throws CommandException {
        String collection = getCollectionName(command);
        QueryParams params = QueryParams.getParamsFromJson(command.get(ScriptCommand.PARAMS).get(QUERY));

        try {
            List<ODocument> docs = DocumentService.getDocuments(collection, params);

            String s = JSONFormats.prepareDocToJson(docs, JSONFormats.Formats.DOCUMENT_PUBLIC);
            ArrayNode lst = (ArrayNode) Json.mapper().readTree(s);
            lst.forEach((j) -> ((ObjectNode) j).remove(TO_REMOVE));//.remove("@rid"));
            return lst;
        } catch (SqlInjectionException | IOException e) {
            throw new CommandExecutionException(command, "error executing command: " + ExceptionUtils.getMessage(e),
                    e);
        } catch (InvalidCollectionException e) {
            throw new CommandExecutionException(command, "invalid collection: " + collection, e);
        }
    }

    @Override
    protected JsonNode get(JsonNode command) throws CommandException {
        String collection = getCollectionName(command);
        String id = getDocumentId(command);
        try {
            String rid = DocumentService.getRidByString(id, true);
            ODocument document = DocumentService.get(collection, rid);
            if (document == null) {
                return null;
            } else {
                String s = JSONFormats.prepareDocToJson(document, JSONFormats.Formats.DOCUMENT_PUBLIC);
                ObjectNode node = (ObjectNode) Json.mapper().readTree(s);
                //necessary to create relations between documents
                node.remove(TO_REMOVE);//.remove("@rid");
                return node;
            }
        } catch (RidNotFoundException e) {
            return null;
        } catch (DocumentNotFoundException e) {
            return null;
        } catch (InvalidCollectionException e) {
            throw new CommandExecutionException(command, "invalid collection: " + collection);
        } catch (InvalidModelException e) {
            throw new CommandExecutionException(command,
                    "error executing command: " + ExceptionUtils.getMessage(e));
        } catch (JsonProcessingException e) {
            throw new CommandExecutionException(command,
                    "error executing command: " + ExceptionUtils.getMessage(e));
        } catch (IOException e) {
            throw new CommandExecutionException(command,
                    "error executing command: " + ExceptionUtils.getMessage(e));
        }
    }

    private void alterGrants(JsonNode command, String collection, String docId, boolean users, boolean grant)
            throws CommandParsingException, UserNotFoundException, DocumentNotFoundException,
            InvalidCollectionException, InvalidModelException, RoleNotFoundException {
        JsonNode params = command.get(ScriptCommand.PARAMS);
        JsonNode node = users ? params.get("users") : params.get("roles");
        if (node != null) {
            JsonNode read = node.get("read");
            if (read != null) {
                alterGrantsTo(command, read, collection, docId, grant, users, Permissions.ALLOW_READ);
            }

            //DEPRECATED
            JsonNode write = node.get("write");
            if (write != null) {
                alterGrantsTo(command, write, collection, docId, grant, users, Permissions.ALLOW_UPDATE);
            }

            //issue 682 - field to grant/revoke update permission is "write" instead of "update" into the plugin engine
            //we now have to maintain both due retro-compatibility 
            JsonNode update = node.get("update");
            if (update != null) {
                alterGrantsTo(command, update, collection, docId, grant, users, Permissions.ALLOW_UPDATE);
            }
            //------

            JsonNode delete = node.get("delete");
            if (delete != null) {
                alterGrantsTo(command, delete, collection, docId, grant, users, Permissions.ALLOW_DELETE);
            }
            JsonNode all = node.get("all");
            if (all != null) {
                alterGrantsTo(command, all, collection, docId, grant, users, Permissions.ALLOW);
            }

        }
    }

    private void alterGrantsTo(JsonNode command, JsonNode to, String collection, String docId, boolean isGrant,
            boolean users, Permissions permission) throws CommandParsingException, UserNotFoundException,
            DocumentNotFoundException, InvalidCollectionException, InvalidModelException, RoleNotFoundException {
        if (!to.isArray())
            throw new CommandParsingException(command, "targets of permissions must be an array");
        if (isGrant) {
            if (users) {
                for (JsonNode u : to) {
                    if (!u.isTextual())
                        throw new CommandParsingException(command, "invalid user name specified: " + u);
                    DocumentService.grantPermissionToUser(collection, docId, permission, u.asText());
                }
            } else {
                for (JsonNode r : to) {
                    if (!r.isTextual())
                        throw new CommandParsingException(command, "invalid role name specified: " + r);
                    DocumentService.grantPermissionToRole(collection, docId, permission, r.asText());
                }
            }
        } else {
            if (users) {
                for (JsonNode u : to) {
                    if (!u.isTextual())
                        throw new CommandParsingException(command, "invalid user name specified: " + u);
                    DocumentService.revokePermissionToUser(collection, docId, permission, u.asText());
                }
            } else {
                for (JsonNode r : to) {
                    if (!r.isTextual())
                        throw new CommandParsingException(command, "invalid role name specified: " + r);
                    DocumentService.revokePermissionToRole(collection, docId, permission, r.asText());
                }
            }
        }
    }

    private String getDocumentId(JsonNode command) throws CommandException {
        JsonNode params = command.get(ScriptCommand.PARAMS);
        JsonNode id = params.get("id");
        if (id == null || !id.isTextual()) {
            throw new CommandParsingException(command, "missing document id");
        }
        String idString = id.asText();
        return idString;
    }

    @Override
    public String name() {
        return RESOURCE_NAME;
    }
}