mirror of
https://github.com/vcscsvcscs/GenerationsHeritage.git
synced 2025-08-14 14:59:07 +02:00
implement admin syste,
This commit is contained in:
130
apps/db-adapter/internal/api/admin.go
Normal file
130
apps/db-adapter/internal/api/admin.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/internal/api/auth"
|
||||
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/internal/memgraph"
|
||||
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/pkg/api"
|
||||
)
|
||||
|
||||
func (srv *server) CreateAdminRelationship(c *gin.Context, id1 int, id2 int, params api.CreateAdminRelationshipParams) {
|
||||
session := srv.db.NewSession(c.Request.Context(), neo4j.SessionConfig{})
|
||||
defer closeSession(c.Request.Context(), session, srv.dbOpTimeout)
|
||||
|
||||
actx, aCancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
|
||||
defer aCancel()
|
||||
if err := auth.CouldManagePerson(actx, session, id1, id2, params.XUserID); err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"msg": fmt.Sprint("User does not have permissions to manage this person with error:", err.Error())})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
qctx, qCancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
|
||||
defer qCancel()
|
||||
|
||||
res, err := session.ExecuteWrite(qctx, memgraph.CreateAdminRelationship(qctx, id1, id2))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, res)
|
||||
}
|
||||
|
||||
func (srv *server) DeleteAdminRelationship(c *gin.Context, id1 int, id2 int, params api.DeleteAdminRelationshipParams) {
|
||||
session := srv.db.NewSession(c.Request.Context(), neo4j.SessionConfig{})
|
||||
defer closeSession(c.Request.Context(), session, srv.dbOpTimeout)
|
||||
|
||||
actx, aCancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
|
||||
defer aCancel()
|
||||
if err := auth.CouldManagePerson(actx, session, id1, id2, params.XUserID); err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"msg": fmt.Sprint("User does not have permissions to manage this person with error:", err.Error())})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
qctx, qCancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
|
||||
defer qCancel()
|
||||
|
||||
_, err := session.ExecuteWrite(qctx, memgraph.DeleteAdminRelationship(qctx, id1, id2))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"msg": "admin relationship was deleted"})
|
||||
}
|
||||
|
||||
func (srv *server) GetAdminRelationship(c *gin.Context, id1 int, id2 int, params api.GetAdminRelationshipParams) {
|
||||
session := srv.db.NewSession(c.Request.Context(), neo4j.SessionConfig{})
|
||||
defer closeSession(c.Request.Context(), session, srv.dbOpTimeout)
|
||||
|
||||
actx, aCancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
|
||||
defer aCancel()
|
||||
if err := auth.CouldSeePersonsProfile(actx, session, id1, params.XUserID); err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"msg": fmt.Sprint("User does not have permissions to see this person", err.Error())})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
qctx, qCancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
|
||||
defer qCancel()
|
||||
|
||||
res, err := session.ExecuteRead(qctx, memgraph.GetAdminRelationship(qctx, id1, id2))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, res)
|
||||
}
|
||||
|
||||
func (srv *server) GetProfileAdmins(c *gin.Context, id int, params api.GetProfileAdminsParams) {
|
||||
session := srv.db.NewSession(c.Request.Context(), neo4j.SessionConfig{})
|
||||
defer closeSession(c.Request.Context(), session, srv.dbOpTimeout)
|
||||
|
||||
actx, aCancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
|
||||
defer aCancel()
|
||||
if err := auth.CouldSeePersonsProfile(actx, session, id, params.XUserID); err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"msg": fmt.Sprint("User does not have permissions to see this person", err.Error())})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
qctx, qCancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
|
||||
defer qCancel()
|
||||
|
||||
res, err := session.ExecuteRead(qctx, memgraph.GetProfileAdmins(qctx, id))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, res)
|
||||
}
|
||||
|
||||
func (srv *server) GetManagedProfiles(c *gin.Context, params api.GetManagedProfilesParams) {
|
||||
session := srv.db.NewSession(c.Request.Context(), neo4j.SessionConfig{})
|
||||
defer closeSession(c.Request.Context(), session, srv.dbOpTimeout)
|
||||
|
||||
qctx, qCancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
|
||||
defer qCancel()
|
||||
|
||||
res, err := session.ExecuteRead(qctx, memgraph.GetManagedProfiles(qctx, id))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, res)
|
||||
}
|
27
apps/db-adapter/internal/api/auth/admin_operations.go
Normal file
27
apps/db-adapter/internal/api/auth/admin_operations.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/internal/memgraph"
|
||||
)
|
||||
|
||||
// This function checks if the user has permission to manage another user's profile, it returns an error if the user does not have permission.
|
||||
func CouldManagePerson(ctx context.Context, session neo4j.SessionWithContext, userId, adminId, XUserID int) error {
|
||||
if adminId == XUserID {
|
||||
return nil
|
||||
}
|
||||
|
||||
return CouldManagePersonUnknownAdmin(ctx, session, userId, XUserID)
|
||||
}
|
||||
|
||||
// This function checks if the user has permission to manage another user's profile, it returns an error if the user does not have permission.
|
||||
func CouldManagePersonUnknownAdmin(ctx context.Context, session neo4j.SessionWithContext, userId, XUserID int) error {
|
||||
if userId == XUserID {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := session.ExecuteRead(ctx, memgraph.GetAdminRelationship(ctx, userId, XUserID), nil)
|
||||
return err
|
||||
}
|
14
apps/db-adapter/internal/api/auth/read_operations.go
Normal file
14
apps/db-adapter/internal/api/auth/read_operations.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||
)
|
||||
|
||||
func CouldSeePersonsProfile(ctx context.Context, session neo4j.SessionWithContext, userId, XUserID int) error {
|
||||
if CouldManagePersonUnknownAdmin(ctx, session, userId, XUserID) == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
@@ -1,45 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/internal/memgraph"
|
||||
)
|
||||
|
||||
type accessMode int
|
||||
|
||||
const (
|
||||
accessModeNone accessMode = iota
|
||||
accessModeRead
|
||||
accessModeWrite
|
||||
)
|
||||
|
||||
func userWithIdHasAccessToGivenPerson(ctx context.Context, session neo4j.SessionWithContext, userId, personId int) accessMode {
|
||||
resPerson, err := session.ExecuteRead(ctx, memgraph.GetPersonById(ctx, userId))
|
||||
if err != nil {
|
||||
return accessModeNone
|
||||
}
|
||||
|
||||
resPersonMap, ok := resPerson.(map[string]any)
|
||||
if !ok {
|
||||
return accessModeNone
|
||||
}
|
||||
|
||||
if resPersonMap["id"] == userId {
|
||||
return accessModeWrite
|
||||
}
|
||||
|
||||
AllowAdminAccess, ok := resPersonMap["allow_admin_access"].([]map[string]any)
|
||||
if !ok {
|
||||
return accessModeNone
|
||||
}
|
||||
|
||||
for _, admin := range AllowAdminAccess {
|
||||
if admin["id"].(int) == userId {
|
||||
return accessModeWrite
|
||||
}
|
||||
}
|
||||
|
||||
return accessModeNone
|
||||
}
|
@@ -2,10 +2,12 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/internal/api/auth"
|
||||
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/internal/memgraph"
|
||||
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/pkg/api"
|
||||
)
|
||||
@@ -18,29 +20,57 @@ func (srv *server) CreatePerson(c *gin.Context, params api.CreatePersonParams) {
|
||||
return
|
||||
}
|
||||
|
||||
adminList := []struct {
|
||||
Id *int "json:\"id,omitempty\""
|
||||
Name *string "json:\"name,omitempty\""
|
||||
}{
|
||||
{Id: ¶ms.XUserID, Name: ¶ms.XUserName},
|
||||
}
|
||||
person.AllowAdminAccess = &adminList
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), srv.dbOpTimeout)
|
||||
defer cancel()
|
||||
session := srv.db.NewSession(ctx, neo4j.SessionConfig{})
|
||||
session := srv.db.NewSession(c.Request.Context(), neo4j.SessionConfig{})
|
||||
defer closeSession(c.Request.Context(), session, srv.dbOpTimeout)
|
||||
|
||||
trs, err := session.BeginTransaction(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
trs.Commit(c.Request.Context())
|
||||
trs.Close(c.Request.Context())
|
||||
}()
|
||||
|
||||
qctx, qCancel := context.WithTimeout(context.Background(), srv.dbOpTimeout)
|
||||
defer qCancel()
|
||||
res, err := session.ExecuteRead(qctx, memgraph.CreatePerson(qctx, person))
|
||||
res, err := trs.Run(qctx, memgraph.CreatePersonCypherQuery, map[string]any{
|
||||
"Person": *person,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, res)
|
||||
singleRes, err := res.Single(qctx)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
personId, ok := singleRes.Get("id")
|
||||
if !ok {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"msg": "Person ID not found in response"})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
actx, acancel := context.WithTimeout(context.Background(), srv.dbOpTimeout)
|
||||
defer acancel()
|
||||
_, aErr := trs.Run(actx, memgraph.CreateAdminRelationshipCypherQuery, map[string]any{
|
||||
"id2": personId.(int),
|
||||
"id1": params.XUserID,
|
||||
})
|
||||
if aErr != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"msg": aErr.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, singleRes.AsMap())
|
||||
}
|
||||
|
||||
func (srv *server) GetPersonById(c *gin.Context, id int, params api.GetPersonByIdParams) {
|
||||
@@ -51,8 +81,8 @@ func (srv *server) GetPersonById(c *gin.Context, id int, params api.GetPersonByI
|
||||
|
||||
actx, acancel := context.WithTimeout(context.Background(), srv.dbOpTimeout)
|
||||
defer acancel()
|
||||
if userWithIdHasAccessToGivenPerson(actx, session, params.XUserID, id) == accessModeNone {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"msg": "User does not have access to this person"})
|
||||
if err := auth.CouldSeePersonsProfile(actx, session, id, params.XUserID); err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"msg": fmt.Sprint("User does not have access to this person", err.Error())})
|
||||
|
||||
return
|
||||
}
|
||||
@@ -77,8 +107,8 @@ func (srv *server) SoftDeletePerson(c *gin.Context, id int, params api.SoftDelet
|
||||
|
||||
actx, acancel := context.WithTimeout(context.Background(), srv.dbOpTimeout)
|
||||
defer acancel()
|
||||
if userWithIdHasAccessToGivenPerson(actx, session, params.XUserID, id) != accessModeWrite {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"msg": "User does not have access to this person"})
|
||||
if err := auth.CouldManagePersonUnknownAdmin(actx, session, id, params.XUserID); err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"msg": fmt.Sprint("User does not have access to this person", err.Error())})
|
||||
|
||||
return
|
||||
}
|
||||
@@ -108,8 +138,8 @@ func (srv *server) UpdatePerson(c *gin.Context, id int, params api.UpdatePersonP
|
||||
session := srv.db.NewSession(actx, neo4j.SessionConfig{})
|
||||
defer closeSession(c.Request.Context(), session, srv.dbOpTimeout)
|
||||
|
||||
if userWithIdHasAccessToGivenPerson(actx, session, params.XUserID, id) != accessModeWrite {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"msg": "User does not have access to this person"})
|
||||
if err := auth.CouldManagePersonUnknownAdmin(actx, session, id, params.XUserID); err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"msg": fmt.Sprint("User does not have access to this person", err.Error())})
|
||||
|
||||
return
|
||||
}
|
||||
@@ -134,8 +164,8 @@ func (srv *server) HardDeletePerson(c *gin.Context, id int, params api.HardDelet
|
||||
|
||||
actx, acancel := context.WithTimeout(context.Background(), srv.dbOpTimeout)
|
||||
defer acancel()
|
||||
if userWithIdHasAccessToGivenPerson(actx, session, params.XUserID, id) != accessModeWrite {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"msg": "User does not have access to this person"})
|
||||
if err := auth.CouldManagePersonUnknownAdmin(actx, session, id, params.XUserID); err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"msg": fmt.Sprint("User does not have access to this person", err.Error())})
|
||||
|
||||
return
|
||||
}
|
||||
|
@@ -27,19 +27,13 @@ func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params ap
|
||||
qctx, qCancel := context.WithTimeout(ctx, srv.dbOpTimeout)
|
||||
defer qCancel()
|
||||
|
||||
res, err := session.ExecuteRead(qctx, memgraph.CreatePerson(qctx, &api.PersonProperties{
|
||||
res, err := session.ExecuteWrite(qctx, memgraph.CreatePerson(qctx, &api.PersonProperties{
|
||||
FirstName: &requestBody.Person.FirstName,
|
||||
LastName: &requestBody.Person.LastName,
|
||||
Born: &requestBody.Person.Born,
|
||||
MothersFirstName: &requestBody.Person.MothersFirstName,
|
||||
MothersLastName: &requestBody.Person.MothersLastName,
|
||||
Limit: &requestBody.Person.Limit,
|
||||
AllowAdminAccess: &[]struct {
|
||||
Id *int "json:\"id,omitempty\""
|
||||
Name *string "json:\"name,omitempty\""
|
||||
}{
|
||||
{Id: ¶ms.XUserID},
|
||||
},
|
||||
}))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
|
||||
|
@@ -47,7 +47,7 @@ func (srv *server) CreatePersonByGoogleIdAndInviteCode(c *gin.Context, googleId
|
||||
|
||||
qctx, qCancel := context.WithTimeout(context.Background(), srv.dbOpTimeout)
|
||||
defer qCancel()
|
||||
res, err := session.ExecuteRead(qctx, memgraph.UpdatePersonByInviteCode(qctx, person.InviteCode, person.Props))
|
||||
res, err := session.ExecuteWrite(qctx, memgraph.UpdatePersonByInviteCode(qctx, person.InviteCode, person.Props))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
|
||||
|
||||
@@ -72,7 +72,7 @@ func (srv *server) CreatePersonByGoogleId(c *gin.Context, googleId string) {
|
||||
|
||||
qctx, qCancel := context.WithTimeout(context.Background(), srv.dbOpTimeout)
|
||||
defer qCancel()
|
||||
res, err := session.ExecuteRead(qctx, memgraph.CreatePerson(qctx, person))
|
||||
res, err := session.ExecuteWrite(qctx, memgraph.CreatePerson(qctx, person))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
|
||||
|
||||
|
@@ -1 +1,77 @@
|
||||
package memgraph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||
)
|
||||
|
||||
func CreateAdminRelationship(ctx context.Context, userId int, adminId int) neo4j.ManagedTransactionWork {
|
||||
return func(tx neo4j.ManagedTransaction) (any, error) {
|
||||
result, err := tx.Run(ctx, CreateAdminRelationshipCypherQuery, map[string]any{
|
||||
"id1": adminId,
|
||||
"id2": userId,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record, err := result.Single(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return record.AsMap(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteAdminRelationship(ctx context.Context, userId int, adminId int) neo4j.ManagedTransactionWork {
|
||||
return func(tx neo4j.ManagedTransaction) (any, error) {
|
||||
result, err := tx.Run(ctx, DeleteAdminRelationshipCypherQuery, map[string]any{
|
||||
"id1": adminId,
|
||||
"id2": userId,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.Peek(ctx) {
|
||||
return nil, fmt.Errorf("there was a returned value, when deleting admin but there should be none")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetAdminRelationship(ctx context.Context, userId int, adminId int) neo4j.ManagedTransactionWork {
|
||||
return func(tx neo4j.ManagedTransaction) (any, error) {
|
||||
result, err := tx.Run(ctx, GetAdminRelationshipCypherQuery, map[string]any{
|
||||
"id1": adminId,
|
||||
"id2": userId,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record, err := result.Single(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return record.AsMap(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetProfileAdmins(ctx context.Context, userId int) neo4j.ManagedTransactionWork {
|
||||
return func(tx neo4j.ManagedTransaction) (any, error) {
|
||||
result, err := tx.Run(ctx, GetProfileAdminsCypherQuery, map[string]any{
|
||||
"id": userId,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result.Collect(ctx)
|
||||
}
|
||||
}
|
||||
|
@@ -86,3 +86,23 @@ var DeleteRelationshipCypherQuery string
|
||||
//
|
||||
//go:embed queries/get_family_tree_by_id.cypher
|
||||
var GetFamilyTreeByIdCypherQuery string
|
||||
|
||||
// Requires id1, id2 parameter.
|
||||
//
|
||||
//go:embed queries/create_admin_relationship.cypher
|
||||
var CreateAdminRelationshipCypherQuery string
|
||||
|
||||
// Requires id1, id2 parameter.
|
||||
//
|
||||
//go:embed queries/delete_admin_relationship.cypher
|
||||
var DeleteAdminRelationshipCypherQuery string
|
||||
|
||||
// Requires id1, id2 parameter.
|
||||
//
|
||||
//go:embed queries/get_admin_relationship.cypher
|
||||
var GetAdminRelationshipCypherQuery string
|
||||
|
||||
// Requires id parameter.
|
||||
//
|
||||
//go:embed queries/get_profile_admins.cypher
|
||||
var GetProfileAdminsCypherQuery string
|
||||
|
@@ -0,0 +1,5 @@
|
||||
MATCH (a:Person), (b:Person)
|
||||
WHERE id(a) = $id1 AND id(b) = $id2 AND $id1 != $id2
|
||||
MERGE (a)-[r1:Admin]->(b)
|
||||
ON CREATE SET r1 = {added : timestamp()}
|
||||
RETURN r1 as relationship;
|
@@ -0,0 +1,3 @@
|
||||
MATCH (a)-[r1:Admin]->(b)
|
||||
WHERE id(a) = $id1 AND id(b) = $id2 AND $id1 != $id2
|
||||
DELETE r1;
|
@@ -0,0 +1,3 @@
|
||||
MATCH (a)-[r1:Admin]->(b)
|
||||
WHERE id(a) = $id1 AND id(b) = $id2 AND $id1 != $id2
|
||||
RETURN r1 as relationship;
|
@@ -0,0 +1,3 @@
|
||||
MATCH (a)-[r1:Admin]->(b)
|
||||
WHERE id(b) = $id
|
||||
RETURN collect(r1) as adminRelationship, collect({id: id(a), first_name: a.first_name, last_name: a.last_name}) as admins;
|
Reference in New Issue
Block a user