ai.grakn.engine.controller.ConceptController.java Source code

Java tutorial

Introduction

Here is the source code for ai.grakn.engine.controller.ConceptController.java

Source

/*
 * Grakn - A Distributed Semantic Database
 * Copyright (C) 2016  Grakn Labs Limited
 *
 * Grakn is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Grakn is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Grakn. If not, see <http://www.gnu.org/licenses/gpl.txt>.
 */

package ai.grakn.engine.controller;

import ai.grakn.GraknGraph;
import ai.grakn.concept.Concept;
import ai.grakn.concept.ConceptId;
import ai.grakn.concept.Label;
import ai.grakn.concept.OntologyConcept;
import ai.grakn.engine.factory.EngineGraknGraphFactory;
import ai.grakn.exception.GraknServerException;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.codahale.metrics.Timer.Context;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import mjson.Json;
import org.apache.commons.httpclient.HttpStatus;
import spark.Request;
import spark.Response;
import spark.Service;

import javax.annotation.Nullable;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import static ai.grakn.GraknTxType.READ;
import static ai.grakn.engine.controller.GraqlController.getAcceptType;
import static ai.grakn.engine.controller.util.Requests.mandatoryQueryParameter;
import static ai.grakn.engine.controller.util.Requests.queryParameter;
import static ai.grakn.graql.internal.hal.HALBuilder.renderHALConceptData;
import static ai.grakn.util.REST.Request.Concept.LIMIT_EMBEDDED;
import static ai.grakn.util.REST.Request.Concept.OFFSET_EMBEDDED;
import static ai.grakn.util.REST.Request.ID_PARAMETER;
import static ai.grakn.util.REST.Request.KEYSPACE;
import static ai.grakn.util.REST.Response.ContentType.APPLICATION_ALL;
import static ai.grakn.util.REST.Response.ContentType.APPLICATION_HAL;
import static ai.grakn.util.REST.Response.ContentType.APPLICATION_JSON;
import static ai.grakn.util.REST.Response.Graql.IDENTIFIER;
import static ai.grakn.util.REST.Response.Json.ENTITIES_JSON_FIELD;
import static ai.grakn.util.REST.Response.Json.RELATIONS_JSON_FIELD;
import static ai.grakn.util.REST.Response.Json.RESOURCES_JSON_FIELD;
import static ai.grakn.util.REST.Response.Json.ROLES_JSON_FIELD;
import static ai.grakn.util.REST.WebPath.Concept.CONCEPT;
import static ai.grakn.util.REST.WebPath.Concept.ONTOLOGY;
import static com.codahale.metrics.MetricRegistry.name;
import static java.util.stream.Collectors.toList;

/**
 * <p>
 *     Endpoints used to query the graph by concept type or identifier
 * </p>
 *
 * @author alexandraorth
 */
@Path("/graph")
public class ConceptController {

    private static final int separationDegree = 1;
    private final EngineGraknGraphFactory factory;
    private final Timer conceptIdGetTimer;
    private final Timer ontologyGetTimer;

    public ConceptController(EngineGraknGraphFactory factory, Service spark, MetricRegistry metricRegistry) {
        this.factory = factory;
        this.conceptIdGetTimer = metricRegistry.timer(name(ConceptController.class, "concept-by-identifier"));
        this.ontologyGetTimer = metricRegistry.timer(name(ConceptController.class, "ontology"));

        spark.get(CONCEPT + ID_PARAMETER, this::conceptByIdentifier);
        spark.get(ONTOLOGY, this::ontology);

    }

    @GET
    @Path("concept/{id}")
    @ApiOperation(value = "Return the HAL representation of a given concept.")
    @ApiImplicitParams({
            @ApiImplicitParam(name = IDENTIFIER, value = "Identifier of the concept", required = true, dataType = "string", paramType = "path"),
            @ApiImplicitParam(name = KEYSPACE, value = "Name of graph to use", required = true, dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = OFFSET_EMBEDDED, value = "Offset to begin at for embedded HAL concepts", required = true, dataType = "boolean", paramType = "query"),
            @ApiImplicitParam(name = LIMIT_EMBEDDED, value = "Limit on the number of embedded HAL concepts", required = true, dataType = "boolean", paramType = "query") })
    private Json conceptByIdentifier(Request request, Response response) {
        validateRequest(request, APPLICATION_ALL, APPLICATION_HAL);

        String keyspace = mandatoryQueryParameter(request, KEYSPACE);
        ConceptId conceptId = ConceptId.of(mandatoryRequestParameter(request, ID_PARAMETER));
        int offset = queryParameter(request, OFFSET_EMBEDDED).map(Integer::parseInt).orElse(0);
        int limit = queryParameter(request, LIMIT_EMBEDDED).map(Integer::parseInt).orElse(-1);
        try (GraknGraph graph = factory.getGraph(keyspace, READ); Context context = conceptIdGetTimer.time()) {
            Concept concept = retrieveExistingConcept(graph, conceptId);

            response.type(APPLICATION_HAL);
            response.status(HttpStatus.SC_OK);

            return Json.read(renderHALConceptData(concept, separationDegree, keyspace, offset, limit));
        }
    }

    @GET
    @Path("/ontology")
    @ApiOperation(value = "Produces a Json object containing meta-ontology types instances.", notes = "The built Json object will contain ontology nodes divided in roles, entities, relations and resources.", response = Json.class)
    @ApiImplicitParam(name = "keyspace", value = "Name of graph to use", dataType = "string", paramType = "query")
    private String ontology(Request request, Response response) {
        String keyspace = mandatoryQueryParameter(request, KEYSPACE);
        validateRequest(request, APPLICATION_ALL, APPLICATION_JSON);
        try (GraknGraph graph = factory.getGraph(keyspace, READ); Context context = ontologyGetTimer.time()) {
            Json responseObj = Json.object();
            responseObj.set(ROLES_JSON_FIELD, subLabels(graph.admin().getMetaRole()));
            responseObj.set(ENTITIES_JSON_FIELD, subLabels(graph.admin().getMetaEntityType()));
            responseObj.set(RELATIONS_JSON_FIELD, subLabels(graph.admin().getMetaRelationType()));
            responseObj.set(RESOURCES_JSON_FIELD, subLabels(graph.admin().getMetaResourceType()));

            response.type(APPLICATION_JSON);
            response.status(HttpStatus.SC_OK);
            return responseObj.toString();
        } catch (Exception e) {
            throw GraknServerException.serverException(500, e);
        }
    }

    static Concept retrieveExistingConcept(GraknGraph graph, ConceptId conceptId) {
        Concept concept = graph.getConcept(conceptId);

        if (notPresent(concept)) {
            throw GraknServerException.noConceptFound(conceptId, graph.getKeyspace());
        }

        return concept;
    }

    static void validateRequest(Request request, String... contentTypes) {
        String acceptType = getAcceptType(request);

        if (!Arrays.asList(contentTypes).contains(acceptType)) {
            throw GraknServerException.unsupportedContentType(acceptType);
        }
    }

    private List<String> subLabels(OntologyConcept ontologyConcept) {
        return ontologyConcept.subs().filter(concept -> !concept.isImplicit()).map(OntologyConcept::getLabel)
                .map(Label::getValue).collect(toList());
    }

    /**
     * Given a {@link Request} object retrieve the value of the {@param parameter} argument. If it is not present
     * in the request URL, return a 404 to the client.
     *
     * @param request information about the HTTP request
     * @param parameter value to retrieve from the HTTP request
     * @return value of the given parameter
     */
    static String mandatoryRequestParameter(Request request, String parameter) {
        return Optional.ofNullable(request.params(parameter))
                .orElseThrow(() -> GraknServerException.requestMissingParameters(parameter));
    }

    /**
     * Check if the concept is a valid concept
     * @param concept the concept to validate
     * @return true if the concept is valid, false otherwise
     */
    private static boolean notPresent(@Nullable Concept concept) {
        return concept == null;
    }

}