270 lines
6.2 KiB
Go
270 lines
6.2 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"local/dndex/storage/entity"
|
|
"local/dndex/storage/operator"
|
|
"net/http"
|
|
"path"
|
|
"strings"
|
|
|
|
"github.com/gomarkdown/markdown"
|
|
"github.com/gomarkdown/markdown/html"
|
|
"github.com/gomarkdown/markdown/parser"
|
|
"github.com/google/uuid"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
"gopkg.in/mgo.v2/bson"
|
|
)
|
|
|
|
type shortEntity struct {
|
|
Name string
|
|
ID string
|
|
}
|
|
|
|
func (rest *REST) entities(w http.ResponseWriter, r *http.Request) {
|
|
scope, err := rest.entityScope(r)
|
|
if err != nil {
|
|
rest.respNotFound(w)
|
|
return
|
|
}
|
|
if r.Method == http.MethodGet {
|
|
} else if r.Method == http.MethodPost && len(scope) != 0 {
|
|
rest.respNotFound(w)
|
|
return
|
|
} else if len(scope) > 1 {
|
|
if r.Method == http.MethodDelete {
|
|
q := r.URL.Query()
|
|
q.Set("delete", "")
|
|
r.URL.RawQuery = q.Encode()
|
|
}
|
|
r.Method = http.MethodPatch
|
|
}
|
|
if r.Method == http.MethodPatch && len(scope) == 1 {
|
|
r.Method = http.MethodPut
|
|
}
|
|
if r.Method == http.MethodPatch && len(scope) == 0 {
|
|
rest.respNotFound(w)
|
|
return
|
|
}
|
|
switch r.Method {
|
|
case http.MethodPut:
|
|
rest.entitiesReplace(w, r)
|
|
case http.MethodPatch:
|
|
rest.entitiesUpdate(w, r)
|
|
case http.MethodPost:
|
|
rest.entitiesCreate(w, r)
|
|
case http.MethodGet:
|
|
rest.entitiesGet(w, r)
|
|
case http.MethodDelete:
|
|
rest.entitiesDelete(w, r)
|
|
default:
|
|
rest.respNotFound(w)
|
|
}
|
|
}
|
|
|
|
func (rest *REST) entitiesCreate(w http.ResponseWriter, r *http.Request) {
|
|
scope := rest.scope(r)
|
|
one, err := rest.entityParse(r.Body)
|
|
if err != nil {
|
|
rest.respBadRequest(w, err.Error())
|
|
return
|
|
}
|
|
one.ID = uuid.New().String()
|
|
if err := rest.g.Insert(r.Context(), scope.Namespace, one); err != nil {
|
|
rest.respError(w, err)
|
|
return
|
|
}
|
|
r.URL.Path += "/" + one.ID
|
|
rest.entitiesGet(w, r)
|
|
}
|
|
|
|
func (rest *REST) entitiesDelete(w http.ResponseWriter, r *http.Request) {
|
|
scope := rest.scope(r)
|
|
if err := rest.g.Delete(r.Context(), scope.Namespace, bson.M{entity.ID: scope.EntityID}); err != nil {
|
|
rest.respError(w, err)
|
|
return
|
|
}
|
|
rest.respOK(w)
|
|
}
|
|
|
|
func (rest *REST) entitiesGet(w http.ResponseWriter, r *http.Request) {
|
|
entityScope, _ := rest.entityScope(r)
|
|
switch len(entityScope) {
|
|
case 0:
|
|
rest.entitiesGetN(w, r)
|
|
case 1:
|
|
rest.entitiesGetOne(w, r)
|
|
default:
|
|
rest.entitiesGetOneSub(w, r)
|
|
}
|
|
}
|
|
|
|
func (rest *REST) entitiesGetOne(w http.ResponseWriter, r *http.Request) {
|
|
scope := rest.scope(r)
|
|
entityScope, _ := rest.entityScope(r)
|
|
one, err := rest.g.Get(r.Context(), scope.Namespace, entityScope[0])
|
|
if err != nil {
|
|
rest.respNotFound(w)
|
|
return
|
|
}
|
|
resp := one.Generic()
|
|
if _, ok := r.URL.Query()["light"]; !ok {
|
|
m := bson.M{}
|
|
for id := range one.Connections {
|
|
one2, err := rest.g.Get(r.Context(), scope.Namespace, id)
|
|
if err == nil {
|
|
m2 := one2.Generic()
|
|
m2[entity.Relationship] = one.Connections[id].Relationship
|
|
m[id] = m2
|
|
}
|
|
}
|
|
resp[entity.Connections] = m
|
|
}
|
|
_, md := r.URL.Query()["md"]
|
|
if md {
|
|
renderer := html.NewRenderer(html.RendererOptions{Flags: html.CommonFlags | html.TOC})
|
|
parser := parser.NewWithExtensions(parser.CommonExtensions | parser.HeadingIDs | parser.AutoHeadingIDs | parser.Titleblock)
|
|
resp["md"] = markdownHead + string(markdown.ToHTML([]byte(one.Text), parser, renderer)) + markdownTail
|
|
}
|
|
rest.respMap(w, entityScope[0], resp)
|
|
}
|
|
|
|
func (rest *REST) entitiesGetOneSub(w http.ResponseWriter, r *http.Request) {
|
|
entityScope, _ := rest.entityScope(r)
|
|
scope := rest.scope(r)
|
|
one, err := rest.g.Get(r.Context(), scope.Namespace, scope.EntityID)
|
|
if err != nil {
|
|
rest.respNotFound(w)
|
|
return
|
|
}
|
|
b, err := bson.Marshal(one)
|
|
if err != nil {
|
|
rest.respError(w, err)
|
|
return
|
|
}
|
|
var m bson.M
|
|
err = bson.Unmarshal(b, &m)
|
|
if err != nil {
|
|
rest.respError(w, err)
|
|
return
|
|
}
|
|
entityScope = entityScope[1:]
|
|
for len(entityScope) > 1 {
|
|
k := entityScope[0]
|
|
entityScope = entityScope[1:]
|
|
subm, ok := m[k]
|
|
if !ok {
|
|
m = nil
|
|
break
|
|
}
|
|
mm, ok := subm.(bson.M)
|
|
if !ok {
|
|
m = nil
|
|
break
|
|
}
|
|
m = mm
|
|
}
|
|
rest.respMap(w, scope.EntityID, m[entityScope[0]])
|
|
}
|
|
|
|
func (rest *REST) entitiesGetN(w http.ResponseWriter, r *http.Request) {
|
|
scope := rest.scope(r)
|
|
entities, err := rest.g.List(r.Context(), scope.Namespace)
|
|
if err != nil {
|
|
rest.respError(w, err)
|
|
return
|
|
}
|
|
short := make([]shortEntity, len(entities))
|
|
for i := range entities {
|
|
short[i] = shortEntity{Name: entities[i].Name, ID: entities[i].ID}
|
|
}
|
|
rest.respMap(w, scope.Namespace, short)
|
|
}
|
|
|
|
func (rest *REST) entitiesReplace(w http.ResponseWriter, r *http.Request) {
|
|
scope := rest.scope(r)
|
|
one, err := rest.entityParse(r.Body)
|
|
if err != nil {
|
|
rest.respBadRequest(w, err.Error())
|
|
return
|
|
}
|
|
if one.ID != scope.EntityID && one.ID != "" {
|
|
rest.respBadRequest(w, "cannot change primary key")
|
|
return
|
|
}
|
|
one.ID = scope.EntityID
|
|
err = rest.g.Update(r.Context(), scope.Namespace, bson.M{entity.ID: scope.EntityID}, operator.SetMany{Value: one})
|
|
if err != nil {
|
|
rest.respError(w, err)
|
|
return
|
|
}
|
|
rest.entitiesGet(w, r)
|
|
}
|
|
|
|
func (rest *REST) entitiesUpdate(w http.ResponseWriter, r *http.Request) {
|
|
entityScope, _ := rest.entityScope(r)
|
|
scope := rest.scope(r)
|
|
|
|
_, del := r.URL.Query()["delete"]
|
|
|
|
var m interface{}
|
|
if !del {
|
|
err := json.NewDecoder(r.Body).Decode(&m)
|
|
if err != nil {
|
|
rest.respBadRequest(w, err.Error())
|
|
return
|
|
}
|
|
if mm, ok := m.(primitive.M); ok {
|
|
delete(mm, entity.ID)
|
|
m = mm
|
|
}
|
|
}
|
|
|
|
key := strings.Join(entityScope[1:], ".")
|
|
var operation interface{}
|
|
operation = operator.Set{Key: key, Value: m}
|
|
if del {
|
|
operation = operator.Unset(key)
|
|
}
|
|
|
|
err := rest.g.Update(
|
|
r.Context(),
|
|
scope.Namespace,
|
|
bson.M{entity.ID: scope.EntityID},
|
|
operation,
|
|
)
|
|
if err != nil {
|
|
rest.respError(w, err)
|
|
return
|
|
}
|
|
|
|
r.URL.Path = path.Join("/", scope.EntityID)
|
|
rest.entitiesGet(w, r)
|
|
}
|
|
|
|
func (rest *REST) entityParse(r io.Reader) (entity.One, error) {
|
|
var one entity.One
|
|
err := json.NewDecoder(r).Decode(&one)
|
|
return one, err
|
|
}
|
|
|
|
func (rest *REST) entityScope(r *http.Request) ([]string, error) {
|
|
p := r.URL.Path
|
|
if path.Dir(p) == path.Base(p) {
|
|
if r.Method == http.MethodGet || r.Method == http.MethodPost {
|
|
return []string{}, nil
|
|
}
|
|
return nil, errors.New("nothing specified")
|
|
}
|
|
ps := strings.Split(p, "/")
|
|
ps2 := []string{}
|
|
for i := range ps {
|
|
if ps[i] != "" {
|
|
ps2 = append(ps2, ps[i])
|
|
}
|
|
}
|
|
return ps2, nil
|
|
}
|