All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.seancheatham.graph.akka.http.Router.scala Maven / Gradle / Ivy

package com.seancheatham.graph.akka.http

import akka.http.scaladsl.model.{ContentTypes, StatusCodes}
import akka.http.scaladsl.server.Route
import com.seancheatham.graph.{Edge, Graph, Node}
import play.api.libs.json.{JsValue, Json}

/**
  * Helper object used for determining routes for Graph HTTP Servers
  */
object Router {

  /**
    * Generates a router with basic CRUD functionality for graph operations.
    *
    * @param graph The graph to influence/mutate/read
    * @return a router in the Akka high-level routing DSL
    */
  def apply(graph: Graph): Route = {
    import CustomMarshallers._
    import StatusCodes._
    import akka.http.scaladsl.model.HttpEntity
    import akka.http.scaladsl.server.Directives._
    import de.heikoseeberger.akkahttpplayjson.PlayJsonSupport._

    // Keep a variable graph reference; this will be updated every time a mutating request is made
    var g = graph

    val base =
      pathSingleSlash(
        complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, Documentation.html))
      )

    // Provides routing for reading/updating/deleting a specific node.
    // Also provides routes for accessing the node's edges
    val nodeRoutes =
    path("nodes" / Segment) { id =>
      val maybeNode = g.getNode[Node](id)
      // If the node couldn't be found, 404
      maybeNode.fold[Route](complete(NotFound))(node =>
        // Get a node
        get(complete(Json.toJson(node))) ~
          // Update a node
          put(
            entity(as[Option[Map[String, JsValue]]]) {
              data =>
                val updatedNode = g.updateNode(node)((data getOrElse Map.empty).toSeq: _*)
                g = updatedNode.graph
                complete(Json.toJson(updatedNode))
            }
          ) ~
          // Delete a node
          delete {
            g = g.removeNode(node)
            complete(NoContent)
          }
      )
    } ~
      get {
        // Aggregate all of this node's edges
        path("nodes" / Segment / "edges") { id =>
          val maybeNode = g.getNode[Node](id)
          // If the node couldn't be found, 404
          maybeNode.fold[Route](complete(NotFound))(node =>
            parameter("label".?) { label =>
              entity(as[Option[Map[String, JsValue]]]) {
                data =>
                  val d =
                    data getOrElse Map.empty
                  val edges =
                    g.getEgressEdges[Edge](node, label, d).toIterator ++
                      g.getIngressEdges[Edge](node, label, d)
                  complete(edges.map(Json.toJson(_)))
              }
            }
          )
        } ~
          // Get this node's outgoing/egress edges
          path("nodes" / Segment / "edges" / "egress") { id =>
            val maybeNode = g.getNode[Node](id)
            maybeNode.fold[Route](complete(NotFound))(node =>
              parameter("label".?) { label =>
                entity(as[Option[Map[String, JsValue]]]) {
                  data =>
                    val edges =
                      g.getEgressEdges[Edge](node, label, data getOrElse Map.empty).toIterator
                    complete(edges.map(Json.toJson(_)))
                }
              }
            )
          } ~
          // Get this node's incoming/ingress edges
          path("nodes" / Segment / "edges" / "ingress") { id =>
            val maybeNode = g.getNode[Node](id)
            maybeNode.fold[Route](complete(NotFound))(node =>
              parameter("label".?) { label =>
                entity(as[Option[Map[String, JsValue]]]) {
                  data =>
                    val edges =
                      g.getIngressEdges[Edge](node, label, data getOrElse Map.empty).toIterator
                    complete(edges.map(Json.toJson(_)))
                }
              }
            )
          }
      }

    // Routes for working with collections of nodes
    val nodesRoutes =
      path("nodes")(
        // Get all nodes by label? and data?
        get(
          parameter("label".?)(label =>
            entity(as[Option[Map[String, JsValue]]]) {
              data =>
                val nodes =
                  g.getNodes[Node](label, data getOrElse Map.empty).toIterator
                complete(nodes.map(Json.toJson(_)))
            }
          )
        ) ~
          // Create a new node
          post(
            parameter("label")(label =>
              entity(as[Option[Map[String, JsValue]]]) {
                data =>
                  val node =
                    g.addNode[Node](label, data getOrElse Map.empty)
                  g = node.graph
                  complete(Json.toJson(node))
              }
            )
          )
      )

    // Routes for operating on a specific edge
    val edgeRoutes =
      path("edges" / Segment) { id =>
        val maybeEdge = g.getEdge[Edge](id)
        // If the edge could not be found, 404
        maybeEdge.fold[Route](complete(NotFound))(edge =>
          // Get an edge
          get(complete(Json.toJson(edge))) ~
            // Update an edge
            put(
              entity(as[Option[Map[String, JsValue]]]) {
                data =>
                  val updatedEdge = g.updateEdge(edge)((data getOrElse Map.empty).toSeq: _*)
                  g = updatedEdge.graph
                  complete(Json.toJson(updatedEdge))
              }
            ) ~
            // Delete an edge
            delete {
              g = g.removeEdge(edge)
              complete(NoContent)
            }
        )
      }

    // Routes to operate on collections of edges
    val edgesRoutes =
      path("edges")(
        // Get all edges by label and data
        get(
          parameter("label".?)(label =>
            entity(as[Option[Map[String, JsValue]]]) {
              data =>
                val edges =
                  g.getEdges[Edge](label, data getOrElse Map.empty).toIterator
                complete(edges.map(Json.toJson(_)))
            }
          )
        )
      )

    // Routes to create edges
    val edgeFilterRoutes =
      path("nodes" / Segment / "to" / Segment)((a, b) =>
        post(
          parameter("label")(label =>
            entity(as[Option[Map[String, JsValue]]]) {
              data =>
                val node1 =
                  g.getNode[Node](a)
                val node2 =
                  g.getNode[Node](b)

                if (node1.isEmpty)
                  complete(NotFound)
                else if (node2.isEmpty)
                  complete(NotFound)
                else {
                  val edge = g.addEdge[Edge](label, node1.get, node2.get, data getOrElse Map.empty)
                  g = edge.graph
                  complete(Json.toJson(edge))
                }
            }
          )
        )
      )

    base ~
      nodesRoutes ~
      edgesRoutes ~
      edgeFilterRoutes ~
      nodeRoutes ~
      edgeRoutes

  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy