com.yahoo.elide.parsers.state.RelationshipTerminalState.java Source code

Java tutorial

Introduction

Here is the source code for com.yahoo.elide.parsers.state.RelationshipTerminalState.java

Source

/*
 * Copyright 2015, Yahoo Inc.
 * Licensed under the Apache License, Version 2.0
 * See LICENSE file in project root for terms.
 */
package com.yahoo.elide.parsers.state;

import com.yahoo.elide.core.HttpStatus;
import com.yahoo.elide.core.PersistentResource;
import com.yahoo.elide.core.RelationshipType;
import com.yahoo.elide.core.RequestScope;
import com.yahoo.elide.core.exceptions.ForbiddenAccessException;
import com.yahoo.elide.core.exceptions.InvalidEntityBodyException;
import com.yahoo.elide.jsonapi.document.processors.DocumentProcessor;
import com.yahoo.elide.jsonapi.document.processors.IncludedProcessor;
import com.yahoo.elide.jsonapi.models.Data;
import com.yahoo.elide.jsonapi.models.JsonApiDocument;
import com.yahoo.elide.jsonapi.models.Relationship;
import com.yahoo.elide.jsonapi.models.Resource;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.tuple.Pair;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Supplier;

import javax.ws.rs.core.MultivaluedMap;

/**
 * State to handle relationships.
 */
public class RelationshipTerminalState extends BaseState {
    private final PersistentResource record;
    private final RelationshipType relationshipType;
    private final String relationshipName;

    public RelationshipTerminalState(PersistentResource record, String relationshipName) {
        this.record = record;

        this.relationshipType = record.getRelationshipType(relationshipName);
        this.relationshipName = relationshipName;
    }

    @Override
    public Supplier<Pair<Integer, JsonNode>> handleGet(StateContext state) {
        JsonApiDocument doc = new JsonApiDocument();
        RequestScope requestScope = state.getRequestScope();
        ObjectMapper mapper = requestScope.getMapper().getObjectMapper();
        Optional<MultivaluedMap<String, String>> queryParams = requestScope.getQueryParams();

        Map<String, Relationship> relationships = record.toResource().getRelationships();
        Relationship relationship = null;
        if (relationships != null) {
            relationship = relationships.get(relationshipName);
        }

        // Handle valid relationship
        if (relationship != null) {

            // Set data
            Data<Resource> data = relationship.getData();
            doc.setData(data);

            // Run include processor
            DocumentProcessor includedProcessor = new IncludedProcessor();
            includedProcessor.execute(doc, record, queryParams);

            return () -> Pair.of(HttpStatus.SC_OK, mapper.convertValue(doc, JsonNode.class));
        }

        // Handle no data for relationship
        if (relationshipType.isToMany()) {
            doc.setData(new Data<>(new ArrayList<>()));
        } else if (relationshipType.isToOne()) {
            doc.setData(new Data<>((Resource) null));
        } else {
            throw new IllegalStateException(
                    "Failed to GET a relationship; relationship is neither toMany nor toOne");
        }
        return () -> Pair.of(HttpStatus.SC_OK, mapper.convertValue(doc, JsonNode.class));
    }

    @Override
    public Supplier<Pair<Integer, JsonNode>> handlePatch(StateContext state) {
        return handleRequest(state, this::patch);
    }

    @Override
    public Supplier<Pair<Integer, JsonNode>> handlePost(StateContext state) {
        return handleRequest(state, this::post);
    }

    @Override
    public Supplier<Pair<Integer, JsonNode>> handleDelete(StateContext state) {
        return handleRequest(state, this::delete);
    }

    private Supplier<Pair<Integer, JsonNode>> handleRequest(StateContext state,
            BiFunction<Data<Resource>, RequestScope, Boolean> handler) {
        Data<Resource> data = state.getJsonApiDocument().getData();
        handler.apply(data, state.getRequestScope());
        return () -> Pair.of(HttpStatus.SC_NO_CONTENT, null);
    }

    private boolean patch(Data<Resource> data, RequestScope requestScope) {
        boolean isUpdated;

        if (relationshipType.isToMany() && data == null) {
            throw new InvalidEntityBodyException("Expected data but received null");
        }

        if (relationshipType.isToMany()) {
            Collection<Resource> resources = data.get();
            if (resources == null) {
                return false;
            }
            if (!resources.isEmpty()) {
                isUpdated = record.updateRelation(relationshipName,
                        new Relationship(null, new Data<>(resources)).toPersistentResources(requestScope));
            } else {
                isUpdated = record.clearRelation(relationshipName);
            }
        } else if (relationshipType.isToOne()) {
            if (data != null) {
                Resource resource = data.get().iterator().next();
                Relationship relationship = new Relationship(null, new Data<>(resource));
                isUpdated = record.updateRelation(relationshipName,
                        relationship.toPersistentResources(requestScope));
            } else {
                isUpdated = record.clearRelation(relationshipName);
            }
        } else {
            throw new IllegalStateException("Bad relationship type");
        }

        return isUpdated;
    }

    private boolean post(Data<Resource> data, RequestScope requestScope) {
        if (data == null) {
            throw new InvalidEntityBodyException("Expected data but received null");
        }

        Collection<Resource> resources = data.get();
        if (resources == null) {
            return false;
        }
        resources.stream().forEachOrdered(
                resource -> record.addRelation(relationshipName, resource.toPersistentResource(requestScope)));
        return true;
    }

    private boolean delete(Data<Resource> data, RequestScope requestScope) {
        if (data == null) {
            throw new InvalidEntityBodyException("Expected data but received null");
        }

        Collection<Resource> resources = data.get();
        if (resources == null || resources.isEmpty()) {
            // As per: http://jsonapi.org/format/#crud-updating-relationship-responses-403
            throw new ForbiddenAccessException("Unknown update");
        }
        resources.stream().forEachOrdered(
                resource -> record.removeRelation(relationshipName, resource.toPersistentResource(requestScope)));
        return true;
    }
}