implement further integration tests on person and relationship creation

This commit is contained in:
2025-04-22 22:47:37 +02:00
parent 23da2c2186
commit 92c1d29ace
26 changed files with 523 additions and 111 deletions

View File

@@ -116,8 +116,11 @@
"person": {
"$ref": "#/components/schemas/Person"
},
"relationship": {
"$ref": "#/components/schemas/Relationship"
"relationships": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Relationship"
}
}
}
}
@@ -169,14 +172,6 @@
"schema": {
"type": "integer"
}
},
{
"name": "X-User-Name",
"in": "header",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
@@ -2560,6 +2555,9 @@
"Id": {
"type": "integer"
},
"ElementId": {
"type": "string"
},
"Labels": {
"type": "array",
"items": {
@@ -2597,23 +2595,24 @@
"Relationship": {
"type": "object",
"properties": {
"Id": {
"id": {
"type": "integer"
},
"Label": {
"type": "string"
},
"Type": {
"type": {
"type": "string",
"nullable": true
},
"Start": {
"label":{
"type": "string",
"nullable": true
},
"start": {
"type": "integer"
},
"End": {
"end": {
"type": "integer"
},
"Props": {
"properties": {
"$ref": "#/components/schemas/FamilyRelationship"
}
}
@@ -2766,6 +2765,9 @@
"Id": {
"type": "integer"
},
"ElementId":{
"type": "string"
},
"Labels": {
"type": "array",
"items": {
@@ -2783,13 +2785,19 @@
"Id": {
"type": "integer"
},
"Label": {
"Type": {
"type": "string"
},
"Start": {
"StartId": {
"type": "integer"
},
"StartElementId": {
"type": "string"
},
"End": {
"EndId": {
"type": "integer"
},
"EndElementId": {
"type": "string"
},
"Props": {
@@ -2820,13 +2828,19 @@
"Id": {
"type": "integer"
},
"Label": {
"Type": {
"type": "string"
},
"Start": {
"StartId": {
"type": "integer"
},
"StartElementId": {
"type": "string"
},
"End": {
"EndId": {
"type": "integer"
},
"EndElementId": {
"type": "string"
},
"Props": {

View File

@@ -0,0 +1,10 @@
{
"id1": 2,
"id2": 3,
"type": "child",
"relationship": {
"verified": true,
"notes": "Test notes",
"from": "2022-01-01"
}
}

View File

@@ -46,12 +46,12 @@ func CreatePersonTest(dbAdapterUri string, client *http.Client) func(t *testing.
func GetPersonById(dbAdapterUri string, client *http.Client) func(t *testing.T) {
return func(t *testing.T) {
url := dbAdapterUri + "/person/0"
url := dbAdapterUri + "/person/2"
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, url, http.NoBody)
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", "0")
req.Header.Set("X-User-ID", "3")
// Send the request
resp, err := client.Do(req)
@@ -68,8 +68,8 @@ func GetPersonById(dbAdapterUri string, client *http.Client) func(t *testing.T)
_, ok := responseBody["Id"]
require.True(t, ok)
require.Equal(t, "Alice", responseBody["Props"].(map[string]any)["first_name"])
require.Equal(t, "Wonderland", responseBody["Props"].(map[string]any)["last_name"])
require.Equal(t, "Johannes", responseBody["Props"].(map[string]any)["first_name"])
require.Equal(t, "Doe", responseBody["Props"].(map[string]any)["last_name"])
}
}

View File

@@ -7,6 +7,7 @@ import (
"net/http"
"testing"
"github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype"
"github.com/stretchr/testify/require"
)
@@ -35,29 +36,28 @@ func CreatePersonAndRelationshipTest(dbAdapterUri string, payload *[]byte, clien
return func(t *testing.T) {
url := dbAdapterUri + "/person_and_relationship/1"
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, url, bytes.NewBuffer(create_other_person))
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, url, bytes.NewBuffer(*payload))
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", "0")
req.Header.Set("X-User-Name", "application/json")
// Send the request
resp, err := client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()
var responseBody map[string]any
var responseBody struct {
Person dbtype.Node `json:"person"`
Relationships []dbtype.Relationship `json:"relationships"`
}
err = json.NewDecoder(resp.Body).Decode(&responseBody)
require.NoError(t, err)
// Validate the response
require.Equal(t, http.StatusOK, resp.StatusCode)
_, ok := responseBody["Id"]
require.True(t, ok)
_, ok = responseBody["person"]
require.True(t, ok)
_, ok = responseBody["relationship"]
require.True(t, ok)
require.NotEmpty(t, responseBody.Person)
require.NotEmpty(t, responseBody.Relationships)
}
}

View File

@@ -1,7 +1,69 @@
package integration_tests
func GetFamilyTreeById() {
import (
"encoding/json"
"net/http"
"testing"
"github.com/stretchr/testify/require"
)
func GetFamilyTreeByIdTest(dbAdapterUri string, client *http.Client) func(t *testing.T) {
return func(t *testing.T) {
url := dbAdapterUri + "/family-tree"
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, url, http.NoBody)
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", "1")
// Send the request
resp, err := client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()
// Validate the response
require.Equal(t, http.StatusOK, resp.StatusCode)
var responseBody struct {
People []any `json:"people"`
Relationships []any `json:"relationships"`
}
err = json.NewDecoder(resp.Body).Decode(&responseBody)
require.NoError(t, err)
require.Equal(t, 4, len(responseBody.People))
require.Equal(t, 4, len(responseBody.Relationships))
}
}
func GetFamilyTreeWithSpousesById() {
func GetFamilyTreeWithSpousesByIdTest(dbAdapterUri string, client *http.Client) func(t *testing.T) {
return func(t *testing.T) {
url := dbAdapterUri + "/family-tree-with-spouses"
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, url, http.NoBody)
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", "3")
// Send the request
resp, err := client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()
// Validate the response
require.Equal(t, http.StatusOK, resp.StatusCode)
var responseBody struct {
People []any `json:"people"`
Relationships []any `json:"relationships"`
}
err = json.NewDecoder(resp.Body).Decode(&responseBody)
require.NoError(t, err)
require.Equal(t, 5, len(responseBody.People))
require.Equal(t, 6, len(responseBody.Relationships))
}
}

View File

@@ -71,7 +71,7 @@ func CreatePersonByGoogleIdAndInviteCodeTest(dbAdapterUri string, client *http.C
require.NoError(t, err)
// Validate the response
t.Log("Response Status Code: ", responseBody)
// t.Log("Response Status Code: ", responseBody)
require.Equal(t, http.StatusOK, resp.StatusCode)
_, ok := responseBody["Id"]

View File

@@ -1,6 +1,6 @@
package integration_tests
func CreateRelationship() {
func CreateRelationshipTest() {
}
func UpdateRelationship() {

View File

@@ -24,13 +24,19 @@ func CouldSeePersonsProfile(ctx context.Context, session neo4j.SessionWithContex
return fmt.Errorf("could not convert result to map[string]any")
}
people, ok := resMap["people"].([]api.OptimizedPersonNode)
if !ok {
return fmt.Errorf("could not convert people to []api.PersonProperties")
var uniqueIds []int64
var flattenedPeople []any
if err := api.Flatten(resMap["people"], &uniqueIds, &flattenedPeople); err != nil {
return fmt.Errorf("could not convert people to []map[string]any: %w", err)
}
for _, person := range people {
if *person.Id == userId {
for _, person := range flattenedPeople {
person, ok := person.(map[string]any)
if !ok {
return fmt.Errorf("could not convert person to map[string]any")
}
if person["id"].(int64) == int64(userId) {
return nil
}
}

View File

@@ -0,0 +1,29 @@
package api
import (
"fmt"
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/pkg/api"
)
type FamilyTree struct {
People []any `json:"people"`
Relationships []any `json:"relationships"`
}
func FlattenFamilyTree(input any, result *FamilyTree) error {
root, ok := input.(map[string]any)
if !ok {
return fmt.Errorf("could not convert result to map[string]any")
}
var uniqueIds []int64
err := api.Flatten(root["people"], &uniqueIds, &result.People)
if err != nil {
return err
}
uniqueIds = []int64{}
return api.Flatten(root["relationships"], &uniqueIds, &result.Relationships)
}

View File

@@ -126,13 +126,6 @@ func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params ap
return
}
if err := trs.Commit(c.Request.Context()); err != nil {
srv.logger.Error("failed to commit transaction", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
return
}
relationshipsSingle, err := relationShipResultRaw.Single(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"msg": "no relationship was created" + err.Error()})
@@ -147,8 +140,15 @@ func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params ap
return
}
if err := trs.Commit(c.Request.Context()); err != nil {
srv.logger.Error("failed to commit transaction", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
return
}
c.JSON(http.StatusOK, struct {
Person any `json:"person"`
Rel any `json:"relationship"`
}{Person: singleRes, Rel: relationships})
Rel any `json:"relationships"`
}{Person: createdPerson, Rel: relationships})
}

View File

@@ -20,7 +20,17 @@ func (srv *server) GetFamilyTreeById(c *gin.Context, params api.GetFamilyTreeByI
return
}
c.JSON(http.StatusOK, res)
results := FamilyTree{
People: []any{},
Relationships: []any{},
}
err = FlattenFamilyTree(res, &results)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
return
}
c.JSON(http.StatusOK, results)
}
func (srv *server) GetFamilyTreeWithSpousesById(
@@ -36,5 +46,15 @@ func (srv *server) GetFamilyTreeWithSpousesById(
return
}
c.JSON(http.StatusOK, res)
results := FamilyTree{
People: []any{},
Relationships: []any{},
}
err = FlattenFamilyTree(res, &results)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
return
}
c.JSON(http.StatusOK, results)
}

View File

@@ -52,7 +52,7 @@ func TestCreatePerson(t *testing.T) {
c.Request = httptest.NewRequestWithContext(
t.Context(), http.MethodPost, "/person", io.NopCloser(strings.NewReader(body)),
)
params := api.CreatePersonParams{XUserID: 1, XUserName: "test"}
params := api.CreatePersonParams{XUserID: 1}
srv.CreatePerson(c, params)

View File

@@ -69,6 +69,13 @@ var GetRelationshipCypherQuery string
//go:embed queries/create_child_parent_relationships.cypher
var CreateChildParentRelationshipCypherQuery string
// Requires childId, parentId parameters.
//
// returns relationships
//
//go:embed queries/create_sibling_relationships_based_on_parent.cypher
var CreateSiblingRelationshipsBasedOnParentCypherQuery string
// Requires id1, id2, Relationship1, Relationship1 parameters.
//
// return relationships

View File

@@ -4,14 +4,14 @@ OPTIONAL MATCH (n)-[p:Parent*..]->(family:Person)
OPTIONAL MATCH (family)-[c:Child*1..4]->(children:Person)
OPTIONAL MATCH (family)-[s:Sibling]->(siblings:Person)
OPTIONAL MATCH (n)-[ds:Sibling]->(direct_siblings:Person)
WITH collections.to_set(collect(n)+collect(family)+collect(children)+collect(direct_siblings)) as people,
WITH collections.to_set(collect(n)+collect(family)+collect(children)+collect(direct_siblings)+collect(siblings)) as people,
collections.to_set(collect(c) + collect(p) + collect(s) + collect(ds)) as relationships
UNWIND people as ppl
RETURN collect({
id: id(ppl),
first_name: ppl.first_name,
middle_name: ppl.middle_name,
last_name: ppl.last_name,
last_name: ppl.last_name,
born: ppl.born,
died: ppl.died,
profile_picture: ppl.profile_picture

View File

@@ -7,7 +7,7 @@ OPTIONAL MATCH (n)-[ds:Sibling]->(direct_siblings:Person)
OPTIONAL MATCH (family)-[fsp:Spouse]->(fspouse:Person)
OPTIONAL MATCH (children)-[csp:Spouse]->(cspouse:Person)
OPTIONAL MATCH (n)-[sp:Spouse]->(spouse:Person)
WITH collections.to_set(collect(n) + collect(family) + collect(children) + collect(direct_siblings) + collect(fspouse) + collect(cspouse) + collect(spouse)) as people,
WITH collections.to_set(collect(n) + collect(family) + collect(children) + collect(direct_siblings) + collect(fspouse) + collect(cspouse) + collect(spouse) + collect(siblings)) as people,
collections.to_set(collect(c) + collect(p) + collect(s) + collect(ds) + collect(fsp) + collect(csp) + collect(sp)) as relationships
UNWIND people as ppl
RETURN collect({

View File

@@ -4,6 +4,7 @@ import (
"context"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype"
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/pkg/api"
)
@@ -78,7 +79,40 @@ func CreateChildParentRelationship(
return nil, err
}
return result.Single(ctx)
siblingResult, err := tx.Run(ctx, CreateSiblingRelationshipsBasedOnParentCypherQuery, map[string]any{
"childId": childId,
"parentId": parentId,
})
if err != nil {
return nil, err
}
relationship, err := result.Single(ctx)
if err != nil {
return nil, err
}
relationshipsRaw, ok := relationship.Get("relationships")
if !ok {
return nil, err
}
relationships, rok := relationshipsRaw.([]dbtype.Relationship)
if !rok {
return nil, err
}
siblingRecord, err := siblingResult.Single(ctx)
if err != nil {
siblingRelationship, ok := siblingRecord.Get("relationships")
siblings, ok := siblingRelationship.([]dbtype.Relationship)
if ok {
relationships = append(relationships, siblings...)
}
}
return relationships, nil
}
}

View File

@@ -5,6 +5,7 @@ import (
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype"
openapi_types "github.com/oapi-codegen/runtime/types"
)
// StructToMap recursively converts a struct to a map using JSON tags.
@@ -70,7 +71,12 @@ func StructToMap(input any) map[string]any {
// Recurse into nested structs
switch val.Kind() {
case reflect.Struct:
result[jsonKey] = StructToMap(val.Interface())
switch val.Interface().(type) {
case openapi_types.Date:
result[jsonKey] = val.Interface().(openapi_types.Date).String()
default:
result[jsonKey] = StructToMap(val.Interface())
}
case reflect.Slice, reflect.Array:
result[jsonKey] = processSlice(val)
default:
@@ -114,7 +120,12 @@ func processSlice(val reflect.Value) []any {
if isPreservedType(item) {
slice[i] = item
} else if reflect.ValueOf(item).Kind() == reflect.Struct {
slice[i] = StructToMap(item)
switch item.(type) {
case openapi_types.Date:
slice[i] = item.(openapi_types.Date).String()
default:
slice[i] = StructToMap(item)
}
} else {
slice[i] = item
}

View File

@@ -110,9 +110,11 @@ func IntegrationTestFlow(dbAdapterURI string) func(t *testing.T) {
t.Run("CreatePerson", integration_tests.CreatePersonTest(dbAdapterURI, client))
t.Run("UpdatePerson", integration_tests.UpdatePersonTest(dbAdapterURI, client))
t.Run("AddInviteCodeToPerson", integration_tests.UpdatePersonWithInviteCodeTest(dbAdapterURI, client))
t.Run("GetPersonById", integration_tests.GetPersonById(dbAdapterURI, client))
t.Run("CreateFamilyTest", integration_tests.CreateAFamilyTest(dbAdapterURI, client))
t.Run("SoftDeletePerson", integration_tests.SoftDeletePersonTest(dbAdapterURI, client))
t.Run("HardDeletePerson", integration_tests.HardDeletePersonTest(dbAdapterURI, client))
t.Run("GetPersonById", integration_tests.GetPersonById(dbAdapterURI, client))
t.Run("GetFamilyTreeByIdTest", integration_tests.GetFamilyTreeByIdTest(dbAdapterURI, client))
t.Run("GetFamilyTreeWithSpousesByIdTest", integration_tests.GetFamilyTreeWithSpousesByIdTest(dbAdapterURI, client))
}
}

View File

@@ -0,0 +1,40 @@
meta {
name: create_person_and_relationship_child
type: http
seq: 10
}
post {
url: http://localhost:8080/person_and_relationship/{{id}}
body: json
auth: inherit
}
headers {
Content-Type: application/json
X-User-Id: 2
}
body:json {
{
"person": {
"first_name": "Johannes",
"last_name": "Doe",
"born": "2000-07-01",
"limit": 1000,
"mothers_first_name": "Frances",
"mothers_last_name": "Soft",
"email": "dj@example.com"
},
"type": "child",
"relationship": {
"verified": true,
"notes": "Test notes",
"from": "2023-01-01"
}
}
}
vars:pre-request {
id: 2
}

View File

@@ -0,0 +1,38 @@
meta {
name: create_person_and_relationship_parent
type: http
seq: 11
}
post {
url: http://localhost:8080/person_and_relationship/{{id}}
body: json
auth: inherit
}
headers {
Content-Type: application/json
X-User-Id: 2
}
body:json {
{
"person": {
"first_name": "Ferdinand",
"last_name": "Fritz",
"born": "1940-06-01",
"limit": 1000,
"mothers_first_name": "Feras",
"mothers_last_name": "Frea",
"email": "FFd@example.com"
},
"type": "parent",
"relationship": {
"verified": true
}
}
}
vars:pre-request {
id: 2
}

View File

@@ -0,0 +1,39 @@
meta {
name: create_person_and_relationship_sibling
type: http
seq: 12
}
post {
url: http://localhost:8080/person_and_relationship/{{id}}
body: json
auth: inherit
}
headers {
Content-Type: application/json
X-User-Id: 2
}
body:json {
{
"person": {
"first_name": "Sandra",
"last_name": "Doe",
"born": "1987-07-01",
"limit": 1000,
"mothers_first_name": "Mary",
"mothers_last_name": "Smith",
"email": "SD@example.com"
},
"type": "sibling",
"relationship": {
"verified": true,
"notes": "Good siblings"
}
}
}
vars:pre-request {
id: 2
}

View File

@@ -0,0 +1,19 @@
meta {
name: get_person
type: http
seq: 14
}
get {
url: http://localhost:8080/person/{{id}}
body: none
auth: inherit
}
headers {
X-User-Id: 9
}
vars:pre-request {
id: 2
}

View File

@@ -0,0 +1,29 @@
meta {
name: person_family_tree
type: http
seq: 13
}
post {
url: http://localhost:8080/person
body: json
auth: inherit
}
headers {
X-User-Id: 2
X-User-Name: Alice Wonderland
Content-Type: application/json
}
body:json {
{
"first_name": "Jhon",
"last_name": "Doe",
"born": "1985-07-01",
"limit": 1000,
"mothers_first_name": "Mary",
"mothers_last_name": "Smith",
"email": "jd@example.com"
}
}

View File

@@ -0,0 +1,15 @@
meta {
name: person_family_tree_with_spouses
type: http
seq: 15
}
get {
url: http://localhost:8080/family-tree
body: none
auth: inherit
}
headers {
X-User-Id: 2
}

View File

@@ -30,13 +30,15 @@ const (
// Admin defines model for Admin.
type Admin struct {
End *string `json:"End,omitempty"`
Id *int `json:"Id,omitempty"`
Label *string `json:"Label,omitempty"`
Props *struct {
EndElementId *string `json:"EndElementId,omitempty"`
EndId *int `json:"EndId,omitempty"`
Id *int `json:"Id,omitempty"`
Props *struct {
Added *int `json:"added,omitempty"`
} `json:"Props,omitempty"`
Start *string `json:"Start,omitempty"`
StartElementId *string `json:"StartElementId,omitempty"`
StartId *int `json:"StartId,omitempty"`
Type *string `json:"Type,omitempty"`
}
// FamilyRelationship defines model for FamilyRelationship.
@@ -55,11 +57,13 @@ type FamilyTree struct {
// Likes defines model for Likes.
type Likes struct {
End *string `json:"End,omitempty"`
Id *int `json:"Id,omitempty"`
Label *string `json:"Label,omitempty"`
Props *LikesProperties `json:"Props,omitempty"`
Start *string `json:"Start,omitempty"`
EndElementId *string `json:"EndElementId,omitempty"`
EndId *int `json:"EndId,omitempty"`
Id *int `json:"Id,omitempty"`
Props *LikesProperties `json:"Props,omitempty"`
StartElementId *string `json:"StartElementId,omitempty"`
StartId *int `json:"StartId,omitempty"`
Type *string `json:"Type,omitempty"`
}
// LikesProperties defines model for LikesProperties.
@@ -84,9 +88,10 @@ type OptimizedPersonNode struct {
// Person defines model for Person.
type Person struct {
Id *int `json:"Id,omitempty"`
Labels *[]string `json:"Labels,omitempty"`
Props *PersonProperties `json:"Props,omitempty"`
ElementId *string `json:"ElementId,omitempty"`
Id *int `json:"Id,omitempty"`
Labels *[]string `json:"Labels,omitempty"`
Props *PersonProperties `json:"Props,omitempty"`
}
// PersonProperties defines model for PersonProperties.
@@ -197,9 +202,10 @@ type PersonRegistration struct {
// Recipe defines model for Recipe.
type Recipe struct {
Id *int `json:"Id,omitempty"`
Labels *[]string `json:"Labels,omitempty"`
Props *RecipeProperties `json:"Props,omitempty"`
ElementId *string `json:"ElementId,omitempty"`
Id *int `json:"Id,omitempty"`
Labels *[]string `json:"Labels,omitempty"`
Props *RecipeProperties `json:"Props,omitempty"`
}
// RecipeProperties defines model for RecipeProperties.
@@ -226,12 +232,12 @@ type RecipeProperties struct {
// Relationship defines model for Relationship.
type Relationship struct {
End *int `json:"End,omitempty"`
Id *int `json:"Id,omitempty"`
Label *string `json:"Label,omitempty"`
Props *FamilyRelationship `json:"Props,omitempty"`
Start *int `json:"Start,omitempty"`
Type *string `json:"Type"`
End *int `json:"end,omitempty"`
Id *int `json:"id,omitempty"`
Label *string `json:"label"`
Properties *FamilyRelationship `json:"properties,omitempty"`
Start *int `json:"start,omitempty"`
Type *string `json:"type"`
}
// GetProfileAdminsParams defines parameters for GetProfileAdmins.
@@ -271,8 +277,7 @@ type GetManagedProfilesParams struct {
// CreatePersonParams defines parameters for CreatePerson.
type CreatePersonParams struct {
XUserID int `json:"X-User-ID"`
XUserName string `json:"X-User-Name"`
XUserID int `json:"X-User-ID"`
}
// CreatePersonByGoogleIdAndInviteCodeJSONBody defines parameters for CreatePersonByGoogleIdAndInviteCode.
@@ -916,28 +921,6 @@ func (siw *ServerInterfaceWrapper) CreatePerson(c *gin.Context) {
return
}
// ------------- Required header parameter "X-User-Name" -------------
if valueList, found := headers[http.CanonicalHeaderKey("X-User-Name")]; found {
var XUserName string
n := len(valueList)
if n != 1 {
siw.ErrorHandler(c, fmt.Errorf("Expected one value for X-User-Name, got %d", n), http.StatusBadRequest)
return
}
err = runtime.BindStyledParameterWithOptions("simple", "X-User-Name", valueList[0], &XUserName, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true})
if err != nil {
siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter X-User-Name: %w", err), http.StatusBadRequest)
return
}
params.XUserName = XUserName
} else {
siw.ErrorHandler(c, fmt.Errorf("Header parameter X-User-Name is required, but not found"), http.StatusBadRequest)
return
}
for _, middleware := range siw.HandlerMiddlewares {
middleware(c)
if c.IsAborted() {

View File

@@ -0,0 +1,54 @@
package api
import (
"fmt"
"reflect"
"slices"
"github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype"
)
func Flatten(input any, uniqueIds *[]int64, result *[]any) error {
val := reflect.ValueOf(input)
if uniqueIds == nil {
uniqueIds = &[]int64{}
}
switch val.Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < val.Len(); i++ {
if err := Flatten(val.Index(i).Interface(), uniqueIds, result); err != nil {
return err
}
}
case reflect.Map:
id, ok := input.(map[string]any)["id"].(int64)
if !ok {
return fmt.Errorf("could not convert id to int: %v", input.(map[string]any)["id"])
}
if !slices.Contains(*uniqueIds, id) {
*result = append(*result, input.(map[string]any))
*uniqueIds = append(*uniqueIds, id)
}
case reflect.Struct:
switch input.(type) {
case dbtype.Node:
node := val.Interface().(dbtype.Node)
if !slices.Contains(*uniqueIds, node.Id) {
*result = append(*result, node)
*uniqueIds = append(*uniqueIds, node.Id)
}
case dbtype.Relationship:
relationship := val.Interface().(dbtype.Relationship)
if !slices.Contains(*uniqueIds, relationship.Id) {
*result = append(*result, relationship)
*uniqueIds = append(*uniqueIds, relationship.Id)
}
}
default:
return fmt.Errorf("unexpected type: %T", input)
}
return nil
}