implemented critical features in Restful Golang API which'll handle Neo4j interactions.

This commit is contained in:
2025-05-10 22:34:12 -06:00
parent bd59f428ba
commit 228cf1137b
9 changed files with 797 additions and 17 deletions
+214 -6
View File
@@ -2,26 +2,108 @@ package main
import (
"context"
// "encoding/json"
"encoding/json"
// "fmt"
//"io"
// "log"
"log"
"net/http"
// "os"
// "os/signal"
"strconv"
//"strings"
// "sync"
// "time"
// "github.com/gorilla/mux"
"github.com/gorilla/mux"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
)
// TODO: Need to test.
func create_new_space(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:2025")
// Must be a user to create a space.
user_id, ok := user_id_from_context(r.Context())
if !ok {
log.Println("Invalid user ID")
http.Error(w, "Invalid user ID", http.StatusBadRequest)
return
}
err := r.ParseMultipartForm(10 << 20) // Limit to 10MB
if err != nil {
log.Println("Unable to parse form:", err)
http.Error(w, "Unable to parse form", http.StatusBadRequest)
return
}
var space map[string]any
err = json.NewDecoder(r.Body).Decode(&space)
if space["id"] != -1 {
log.Println("Attempt to create a space with a defined ID")
http.Error(w, "Space ID cannot be provided.", http.StatusBadRequest)
return
}
stmt := `
MATCH (owner:User)
WHERE id(owner) = $user_id
CREATE (s:Space {
created_at: datetime(),
name: $name,
description: $description
})
CREATE (owner)-[:SPACE_MEMBER {role: "Owner"}]->(s)
UNWIND $members as member
MATCH (m:User)
WHERE id(m) = member.id
CREATE (m)-[:SPACE_MEMBER {role: member.role}]->(s)
RETURN id(s) AS id,
s.name AS name
`
stmt_args := map[string]any{
"user_id": user_id,
"name": space["name"],
"description": space["description"],
"members": space["members"],
}
result, err := neo4j.ExecuteQuery(driver_ctx, driver, stmt, stmt_args, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("neo4j"))
if err != nil || len(result.Records) != 2 {
log.Println("Error creating Space:", err)
http.Error(w, "Failed to create Space", http.StatusInternalServerError)
return
}
record := result.Records[0]
space_id, _ := record.Get("id")
space_name, _ := record.Get("name")
record_obj := map[string]any{
"space_id": space_id,
"space_name": space_name,
}
obj, err := json.Marshal(record_obj)
if err != nil {
http.Error(w, "Failed to marshal JSON.", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
w.Write(obj)
return
}
}
// TODO: Need to implement for ADMIN ONLY
func retrieve_all_spaces(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
return
@@ -34,30 +116,156 @@ func retrieve_space(driver neo4j.DriverWithContext, driver_ctx context.Context)
}
}
func auth_user_in_space(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
return
// TODO: Implement access for Public and Open Spaces
func auth_user_in_space(driver neo4j.DriverWithContext, driver_ctx context.Context, user_id int, space_id int) bool {
stmt := `
MATCH (u:User), (s:Space)
WHERE id(u) = $user_id AND id(s) = $space_id
RETURN EXISTS((u)-[:SPACE_MEMBER]->(s))
`
stmt_args := map[string]any{
"user_id": user_id,
"space_id": space_id,
}
result, err := neo4j.ExecuteQuery(driver_ctx, driver, stmt, stmt_args, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("neo4j"))
if err != nil {
log.Println("Something went wrong authorizing the user to a space.")
return false
}
if len(result.Records) > 0 {
return result.Records[0].Values[0].(bool)
} else {
return false
}
}
// TODO: Implement
func update_space(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
return
}
}
// TODO: Implement
func delete_space(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
return
}
}
// TODO: Need to handle multiple topics.
func retrieve_spaces_posts(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:2025")
// Must be a user to create a space.
user_id, ok := user_id_from_context(r.Context())
if !ok {
log.Println("Invalid user ID")
http.Error(w, "Invalid user ID", http.StatusBadRequest)
return
}
space_id, _ := strconv.Atoi(mux.Vars(r)["space_id"])
if !auth_user_in_space(driver, driver_ctx, user_id, space_id) {
log.Println("User", user_id, "attempted access to space", space_id, ", which is not allowed.")
http.Error(w, "You are not a member of that space.", http.StatusUnauthorized)
return
}
stmt := `
MATCH (p:Post)-[:IN_SPACE]->(s:Space)
MATCH (s)-[:SPACE_TYPE]->(st:SpaceType)
MATCH (p)-[:POST_TYPE]->(pt:PostType)
MATCH (p)-[:STATUS]->(ps:PostStatus)
MATCH (p)-[:TAGGED_WITH]->(t:Topic)
MATCH (a:User)-[:CREATED]->(p)
WHERE
id(s) = $space_id AND
ps.name = "Published"
RETURN
id(a) AS author_id,
a.username AS author_username,
a.profile_picture AS author_picture,
p.title AS title,
p.excerpt AS excerpt,
p.location AS location,
p.content AS content,
pt.name AS post_type,
id(t) AS topic_id,
t.name AS topic_name,
s.name AS space_name,
`
stmt_args := map[string]any{
"space_id": space_id,
}
result, err := neo4j.ExecuteQuery(driver_ctx, driver, stmt, stmt_args, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("neo4j"))
if !(len(result.Records) >= 2) {
log.Println("Failed to retrieve Posts for Space", space_id, ". Error:", err)
http.Error(w, "Error retrieving Posts from Space.", http.StatusInternalServerError)
return
}
posts_obj := map[int]any{}
for index, record := range result.Records {
author_id, _ := record.Get("author_id")
author_username, _ := record.Get("author_username")
author_picture, _ := record.Get("author_picture")
title, _ := record.Get("title")
excerpt, _ := record.Get("excerpt")
location, _ := record.Get("location")
content, _ := record.Get("content")
post_type, _ := record.Get("post_type")
topic_id, _ := record.Get("topic_id")
topic_name, _ := record.Get("topic_name")
space_name, _ := record.Get("space_name")
record_obj := map[string]any{
"author_id": author_id,
"author_username": author_username,
"author_picture": author_picture,
"title": title,
"excerpt": excerpt,
"location": location,
"content": content,
"post_type": post_type,
"topic_id": topic_id,
"topic_name": topic_name,
"space_name": space_name,
}
posts_obj[index] = record_obj
}
obj, err := json.Marshal(posts_obj)
if err != nil {
http.Error(w, "Failed to parse into JSON", http.StatusInternalServerError)
log.Println("error marshalling json")
return
}
w.WriteHeader(http.StatusOK)
w.Write(obj)
return
}
}
// TODO: Implement for Space Member role == Administrator OR role == Owner
func retrieve_spaces_users(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
return