diff --git a/api/openapi.json b/api/openapi.json index 8ad571e..e497797 100644 --- a/api/openapi.json +++ b/api/openapi.json @@ -954,7 +954,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/Relationship" + "$ref": "#/components/schemas/dbtypeRelationship" } } } @@ -2629,6 +2629,36 @@ } } }, + "dbtypeRelationship": { + "type": "object", + "properties": { + "Id": { + "type": "integer" + }, + "ElementId": { + "type": "string" + }, + "Type": { + "type": "string", + "nullable": true + }, + "StartId": { + "type": "integer" + }, + "StartElementId":{ + "type": "string" + }, + "EndId": { + "type": "integer" + }, + "EndElementId": { + "type": "string" + }, + "Props": { + "$ref": "#/components/schemas/FamilyRelationship" + } + } + }, "OptimizedPersonNode": { "type": "object", "properties": { diff --git a/apps/app/src/lib/api/api.gen.ts b/apps/app/src/lib/api/api.gen.ts index 327e356..dcbedb6 100644 --- a/apps/app/src/lib/api/api.gen.ts +++ b/apps/app/src/lib/api/api.gen.ts @@ -423,6 +423,7 @@ export interface components { }; Person: { Id?: number; + ElementId?: string; Labels?: string[]; Props?: components["schemas"]["PersonProperties"]; }; @@ -435,12 +436,12 @@ export interface components { to?: string | null; }; Relationship: { - Id?: number; - Label?: string; - Type?: string | null; - Start?: number; - End?: number; - Props?: components["schemas"]["FamilyRelationship"]; + id?: number; + type?: string | null; + label?: string | null; + start?: number; + end?: number; + properties?: components["schemas"]["FamilyRelationship"]; }; OptimizedPersonNode: { id?: number; @@ -482,14 +483,17 @@ export interface components { }; Recipe: { Id?: number; + ElementId?: string; Labels?: string[]; Props?: components["schemas"]["RecipeProperties"]; }; Likes: { Id?: number; - Label?: string; - Start?: string; - End?: string; + Type?: string; + StartId?: number; + StartElementId?: string; + EndId?: number; + EndElementId?: string; Props?: components["schemas"]["LikesProperties"]; }; LikesProperties: { @@ -499,9 +503,11 @@ export interface components { }; Admin: { Id?: number; - Label?: string; - Start?: string; - End?: string; + Type?: string; + StartId?: number; + StartElementId?: string; + EndId?: number; + EndElementId?: string; Props?: { added?: number; }; @@ -578,7 +584,7 @@ export interface operations { content: { "application/json": { person?: components["schemas"]["Person"]; - relationship?: components["schemas"]["Relationship"]; + relationships?: components["schemas"]["Relationship"][]; }; }; }; @@ -611,7 +617,6 @@ export interface operations { query?: never; header: { "X-User-ID": number; - "X-User-Name": string; }; path?: never; cookie?: never; diff --git a/apps/db-adapter/integration-tests/payloads/create_relationship_child.json b/apps/db-adapter/integration-tests/payloads/create_relationship_child.json index c7fb4d3..407034e 100644 --- a/apps/db-adapter/integration-tests/payloads/create_relationship_child.json +++ b/apps/db-adapter/integration-tests/payloads/create_relationship_child.json @@ -1,6 +1,6 @@ { - "id1": 1, - "id2": 3, + "id1": 7, + "id2": 6, "type": "child", "relationship": { "verified": true, diff --git a/apps/db-adapter/integration-tests/payloads/create_relationship_parent.json b/apps/db-adapter/integration-tests/payloads/create_relationship_parent.json new file mode 100644 index 0000000..0d0ac51 --- /dev/null +++ b/apps/db-adapter/integration-tests/payloads/create_relationship_parent.json @@ -0,0 +1,10 @@ +{ + "id1": 6, + "id2": 7, + "type": "parent", + "relationship": { + "verified": false, + "notes": "Test notes asdasdasda", + "from": "2021-01-01" + } +} \ No newline at end of file diff --git a/apps/db-adapter/integration-tests/payloads/create_relationship_sibling.json b/apps/db-adapter/integration-tests/payloads/create_relationship_sibling.json new file mode 100644 index 0000000..498ceec --- /dev/null +++ b/apps/db-adapter/integration-tests/payloads/create_relationship_sibling.json @@ -0,0 +1,10 @@ +{ + "id1": 7, + "id2": 8, + "type": "sibling", + "relationship": { + "verified": true, + "notes": "OwO", + "from": "2024-01-01" + } +} \ No newline at end of file diff --git a/apps/db-adapter/integration-tests/payloads/create_relationship_spouse.json b/apps/db-adapter/integration-tests/payloads/create_relationship_spouse.json new file mode 100644 index 0000000..8ded3f3 --- /dev/null +++ b/apps/db-adapter/integration-tests/payloads/create_relationship_spouse.json @@ -0,0 +1,11 @@ +{ + "id1": 5, + "id2": 8, + "type": "spouse", + "relationship": { + "verified": true, + "notes": "UwU", + "from": "2025-01-01", + "to": "2025-03-01" + } +} \ No newline at end of file diff --git a/apps/db-adapter/integration-tests/payloads/verify_relationship.json b/apps/db-adapter/integration-tests/payloads/verify_relationship.json new file mode 100644 index 0000000..5b06846 --- /dev/null +++ b/apps/db-adapter/integration-tests/payloads/verify_relationship.json @@ -0,0 +1,5 @@ +{ + "relationship": { + "verified": false + } +} \ No newline at end of file diff --git a/apps/db-adapter/integration-tests/person.go b/apps/db-adapter/integration-tests/person.go index c525dd5..48a7cfd 100644 --- a/apps/db-adapter/integration-tests/person.go +++ b/apps/db-adapter/integration-tests/person.go @@ -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/2" + url := dbAdapterUri + "/person/8" 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") + req.Header.Set("X-User-ID", "6") // Send the request resp, err := client.Do(req) @@ -68,7 +68,7 @@ func GetPersonById(dbAdapterUri string, client *http.Client) func(t *testing.T) _, ok := responseBody["Id"] require.True(t, ok) - require.Equal(t, "Johannes", responseBody["Props"].(map[string]any)["first_name"]) + require.Equal(t, "Jhon", responseBody["Props"].(map[string]any)["first_name"]) require.Equal(t, "Doe", responseBody["Props"].(map[string]any)["last_name"]) } } diff --git a/apps/db-adapter/integration-tests/person_family_tree.go b/apps/db-adapter/integration-tests/person_family_tree.go index 0e93ae3..3350f99 100644 --- a/apps/db-adapter/integration-tests/person_family_tree.go +++ b/apps/db-adapter/integration-tests/person_family_tree.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" ) -func GetFamilyTreeByIdTest(dbAdapterUri string, client *http.Client) func(t *testing.T) { +func GetFamilyTreeByIdTest(dbAdapterUri string, client *http.Client) func(t *testing.T) { //nolint:dupl,lll // won't fix this, as it is a test return func(t *testing.T) { url := dbAdapterUri + "/family-tree" @@ -33,19 +33,19 @@ func GetFamilyTreeByIdTest(dbAdapterUri string, client *http.Client) func(t *tes 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)) + require.Len(t, responseBody.People, 4) //nolint:mnd // 4 people in the family tree + require.Len(t, responseBody.Relationships, 4) //nolint:mnd // 4 relationships in the family tree } } -func GetFamilyTreeWithSpousesByIdTest(dbAdapterUri string, client *http.Client) func(t *testing.T) { +func GetFamilyTreeWithSpousesByIdTest(dbAdapterUri string, client *http.Client) func(t *testing.T) { //nolint:dupl,lll // won't fix this, as it is a test 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") + req.Header.Set("X-User-ID", "1") // Send the request resp, err := client.Do(req) @@ -63,7 +63,7 @@ func GetFamilyTreeWithSpousesByIdTest(dbAdapterUri string, client *http.Client) 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)) + require.Len(t, responseBody.People, 5) //nolint:mnd // 5 people in the family tree + require.Len(t, responseBody.Relationships, 5) //nolint:mnd // 5 relationships in the family tree } } diff --git a/apps/db-adapter/integration-tests/person_google.go b/apps/db-adapter/integration-tests/person_google.go index 98dd10c..29dc4c4 100644 --- a/apps/db-adapter/integration-tests/person_google.go +++ b/apps/db-adapter/integration-tests/person_google.go @@ -53,7 +53,7 @@ func getPersonByGoogleIdTest(dbAdapterUri string, client *http.Client) func(t *t //go:embed payloads/create_person_with_invite_code.json var create_person_with_invite_code []byte -func CreatePersonByGoogleIdAndInviteCodeTest(dbAdapterUri string, client *http.Client) func(t *testing.T) { +func CreatePersonByGoogleIdAndInviteCodeTest(dbAdapterUri string, client *http.Client) func(t *testing.T) { //nolint:dupl,lll // won't fix this, as it is a test return func(t *testing.T) { url := dbAdapterUri + "/person/google/test-google-id" @@ -85,7 +85,7 @@ func CreatePersonByGoogleIdAndInviteCodeTest(dbAdapterUri string, client *http.C //go:embed payloads/create_person.json var create_person []byte -func createPersonByGoogleIdTest(dbAdapterUri string, client *http.Client) func(t *testing.T) { +func createPersonByGoogleIdTest(dbAdapterUri string, client *http.Client) func(t *testing.T) { //nolint:dupl,lll // won't fix this, as it is a test return func(t *testing.T) { url := dbAdapterUri + "/person/google/test-google-id" diff --git a/apps/db-adapter/integration-tests/relationship.go b/apps/db-adapter/integration-tests/relationship.go index 5c75acc..4c6ff6a 100644 --- a/apps/db-adapter/integration-tests/relationship.go +++ b/apps/db-adapter/integration-tests/relationship.go @@ -7,27 +7,41 @@ import ( "net/http" "testing" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype" "github.com/stretchr/testify/require" ) //go:embed payloads/create_relationship_child.json var create_relationship_child []byte -func CreateRelationshipsTest(dbAdapterUri string, client *http.Client) func(t *testing.T) { +//go:embed payloads/create_relationship_parent.json +var create_relationship_parent []byte + +//go:embed payloads/create_relationship_sibling.json +var create_relationship_sibling []byte + +//go:embed payloads/create_relationship_spouse.json +var create_relationship_spouse []byte + +func CreateRelationshipsTest(dbAdapterURI string, client *http.Client) func(t *testing.T) { return func(t *testing.T) { - t.Run("CreateChildRelationship", CreateRelationshipTest(dbAdapterUri, &create_relationship_child, client)) + t.Run("CreatePerson6", CreatePersonTest(dbAdapterURI, client)) + t.Run("CreatePerson7", CreatePersonTest(dbAdapterURI, client)) + t.Run("CreatePerson8", CreatePersonTest(dbAdapterURI, client)) + t.Run("CreateChildRelationship", CreateRelationshipTest(dbAdapterURI, &create_relationship_child, client)) + t.Run("CreateParentRelationship", CreateRelationshipTest(dbAdapterURI, &create_relationship_parent, client)) + t.Run("CreateSiblingRelationship", CreateRelationshipTest(dbAdapterURI, &create_relationship_sibling, client)) + t.Run("CreateSpouseRelationship", CreateRelationshipTest(dbAdapterURI, &create_relationship_spouse, client)) } } -func CreateRelationshipTest(dbAdapterUri string, payload *[]byte, client *http.Client) func(t *testing.T) { +func CreateRelationshipTest(dbAdapterURI string, payload *[]byte, client *http.Client) func(t *testing.T) { return func(t *testing.T) { - url := dbAdapterUri + "/relationship" + url := dbAdapterURI + "/relationship" 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", "1") + req.Header.Set("X-User-ID", "0") req.Header.Set("X-User-Name", "application/json") // Send the request @@ -35,7 +49,7 @@ func CreateRelationshipTest(dbAdapterUri string, payload *[]byte, client *http.C require.NoError(t, err) defer resp.Body.Close() - var responseBody []dbtype.Relationship + var responseBody []any err = json.NewDecoder(resp.Body).Decode(&responseBody) require.NoError(t, err) @@ -47,14 +61,14 @@ func CreateRelationshipTest(dbAdapterUri string, payload *[]byte, client *http.C func UpdateRelationship() { } -func GetRelationship(dbAdapterUri string, client *http.Client) func(t *testing.T) { +func GetRelationshipTest(dbAdapterURI string, client *http.Client) func(t *testing.T) { return func(t *testing.T) { - url := dbAdapterUri + "/relationship/2/1" + url := dbAdapterURI + "/relationship/5/8" 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") + req.Header.Set("X-User-ID", "5") // Send the request resp, err := client.Do(req) @@ -68,11 +82,11 @@ func GetRelationship(dbAdapterUri string, client *http.Client) func(t *testing.T // Validate the response require.Equal(t, http.StatusOK, resp.StatusCode) - _, ok := responseBody["id"] + _, ok := responseBody["Id"] require.True(t, ok) - require.Equal(t, 2, responseBody["Props"].(map[string]any)["start"]) - require.Equal(t, 1, responseBody["Props"].(map[string]any)["end"]) + require.Equal(t, "5", responseBody["StartElementId"]) + require.Equal(t, "8", responseBody["EndElementId"]) } } diff --git a/apps/db-adapter/internal/api/auth/read_operations_test.go b/apps/db-adapter/internal/api/auth/read_operations_test.go index f9ff700..f1bb0f0 100644 --- a/apps/db-adapter/internal/api/auth/read_operations_test.go +++ b/apps/db-adapter/internal/api/auth/read_operations_test.go @@ -91,7 +91,7 @@ func TestCouldSeePersonsProfile(t *testing.T) { err := CouldSeePersonsProfile(ctx, mockSession, 1, 3) require.Error(t, err) - require.EqualError(t, err, "could not convert people to []api.PersonProperties") + require.EqualError(t, err, "could not convert people to []map[string]any: unexpected type: string") mockSession.AssertExpectations(t) }) } diff --git a/apps/db-adapter/internal/api/flatten_family_tree.go b/apps/db-adapter/internal/api/flatten_family_tree.go index af46aee..0f0badd 100644 --- a/apps/db-adapter/internal/api/flatten_family_tree.go +++ b/apps/db-adapter/internal/api/flatten_family_tree.go @@ -15,7 +15,6 @@ 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 diff --git a/apps/db-adapter/internal/api/person_and_relationship.go b/apps/db-adapter/internal/api/person_and_relationship.go index cb9cefc..af23425 100644 --- a/apps/db-adapter/internal/api/person_and_relationship.go +++ b/apps/db-adapter/internal/api/person_and_relationship.go @@ -13,7 +13,9 @@ import ( "go.uber.org/zap" ) -func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params api.CreatePersonAndRelationshipParams) { +func (srv *server) CreatePersonAndRelationship( //nolint:gocyclo,funlen // this is a complex function, but it is not too long + c *gin.Context, id int, params api.CreatePersonAndRelationshipParams, +) { var requestBody api.CreatePersonAndRelationshipJSONBody if err := c.ShouldBindJSON(&requestBody); err != nil { c.JSON(http.StatusBadRequest, gin.H{"msg": err.Error()}) @@ -87,6 +89,7 @@ func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params ap convertedRelationship := memgraph.StructToMap(requestBody.Relationship) var relationShipResultRaw neo4j.ResultWithContext + relationships := []any{} var relationshipError error switch *requestBody.Type { case api.CreatePersonAndRelationshipJSONBodyTypeChild: @@ -96,6 +99,19 @@ func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params ap "childRelationship": convertedRelationship, "parentRelationship": convertedRelationship, }) + sres, err := trs.Run(relCtx, memgraph.CreateSiblingRelationshipsBasedOnParentCypherQuery, map[string]any{ //nolint:govet,lll // this is intentional + "childId": personID, + "parentId": id, + }) + if err == nil { + if srec, err := sres.Single(relCtx); err == nil { + if relationshipsRaw, ok := srec.Get("relationships"); ok { //nolint:govet // this is intentional + if rrelationships, ok := relationshipsRaw.([]any); ok { + relationships = append(relationships, rrelationships...) + } + } + } + } case api.CreatePersonAndRelationshipJSONBodyTypeParent: relationShipResultRaw, relationshipError = trs.Run(relCtx, memgraph.CreateChildParentRelationshipCypherQuery, map[string]any{ "childId": id, @@ -133,13 +149,22 @@ func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params ap return } - relationships, ok := relationshipsSingle.Get("relationships") + rrelationships, ok := relationshipsSingle.Get("relationships") if !ok { - c.JSON(http.StatusInternalServerError, gin.H{"msg": "no relationship was created"}) + c.JSON(http.StatusInternalServerError, gin.H{"msg": "no relationships were found"}) return } + rrrelationships, ok := rrelationships.([]any) + if !ok { + c.JSON(http.StatusInternalServerError, gin.H{"msg": "unknown relationships type were returned by database"}) + + return + } + + relationships = append(relationships, rrrelationships...) + 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()}) diff --git a/apps/db-adapter/internal/api/person_google_test.go b/apps/db-adapter/internal/api/person_google_test.go index ac7adb8..654185a 100644 --- a/apps/db-adapter/internal/api/person_google_test.go +++ b/apps/db-adapter/internal/api/person_google_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" memgraphMock "github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/internal/memgraph/mock" @@ -76,7 +77,10 @@ func TestCreatePersonByGoogleIdAndInviteCode(t *testing.T) { mockSession := new(memgraphMock.SessionWithContext) mockDriver := new(memgraphMock.DriverWithContext) mockDriver.On("NewSession", mock.Anything, mock.Anything).Return(mockSession) - mockSession.On("ExecuteWrite", mock.Anything, mock.Anything, mock.Anything).Return(map[string]any{"result": "success"}, nil) + mockSession.On("ExecuteWrite", mock.Anything, mock.Anything, mock.Anything).Return( + map[string]any{"person": dbtype.Node{Id: 3}}, + nil, + ) mockSession.On("Close", mock.Anything).Return(nil) srv := &server{ @@ -95,7 +99,7 @@ func TestCreatePersonByGoogleIdAndInviteCode(t *testing.T) { srv.CreatePersonByGoogleIdAndInviteCode(c, "test-google-id") assert.Equal(t, http.StatusOK, w.Code) - assert.Contains(t, w.Body.String(), "success") + assert.Contains(t, w.Body.String(), "Id") }) t.Run("Bad request case", func(t *testing.T) { @@ -124,7 +128,7 @@ func TestCreatePersonByGoogleId(t *testing.T) { mockDriver := new(memgraphMock.DriverWithContext) mockDriver.On("NewSession", mock.Anything, mock.Anything).Return(mockSession) mockSession.On("ExecuteWrite", mock.Anything, mock.Anything, mock.Anything).Return( - map[string]any{"result": "success"}, nil, + map[string]any{"person": dbtype.Node{Id: 3}}, nil, ) mockSession.On("Close", mock.Anything).Return(nil) @@ -144,7 +148,7 @@ func TestCreatePersonByGoogleId(t *testing.T) { srv.CreatePersonByGoogleId(c, "test-google-id") assert.Equal(t, http.StatusOK, w.Code) - assert.Contains(t, w.Body.String(), "success") + assert.Contains(t, w.Body.String(), "Id") }) t.Run("Bad request case", func(t *testing.T) { diff --git a/apps/db-adapter/internal/api/person_test.go b/apps/db-adapter/internal/api/person_test.go index d07b2fd..578e4c3 100644 --- a/apps/db-adapter/internal/api/person_test.go +++ b/apps/db-adapter/internal/api/person_test.go @@ -12,6 +12,7 @@ import ( "github.com/gin-gonic/gin" "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/internal/memgraph" @@ -27,8 +28,9 @@ func TestCreatePerson(t *testing.T) { mockTransaction := new(memgraphMock.ExplicitTransaction) mockResult := new(memgraphMock.Result) mockResult.On("Single", mock.Anything).Return(&neo4j.Record{ - Values: []any{1}, - Keys: []string{"id"}, + Values: []any{dbtype.Node{ + Id: 1}}, + Keys: []string{"person"}, }, nil) mockDriver := new(memgraphMock.DriverWithContext) mockDriver.On("NewSession", mock.Anything, mock.Anything).Return(mockSession) @@ -57,7 +59,7 @@ func TestCreatePerson(t *testing.T) { srv.CreatePerson(c, params) assert.Equal(t, http.StatusOK, w.Code) - assert.Contains(t, w.Body.String(), "id") + assert.Contains(t, w.Body.String(), "Id") }) t.Run("Bad request case", func(t *testing.T) { diff --git a/apps/db-adapter/internal/api/relationship.go b/apps/db-adapter/internal/api/relationship.go index c1de9cf..ea1020d 100644 --- a/apps/db-adapter/internal/api/relationship.go +++ b/apps/db-adapter/internal/api/relationship.go @@ -27,7 +27,7 @@ func (srv *server) CreateRelationship(c *gin.Context, params api.CreateRelations defer aCancel() if err := auth.CouldManagePersonUnknownAdmin(actx, session, *relationship.Id1, params.XUserID); err != nil { - if err := auth.CouldManagePersonUnknownAdmin(actx, session, *relationship.Id1, params.XUserID); err != nil { + if err := auth.CouldManagePersonUnknownAdmin(actx, session, *relationship.Id2, params.XUserID); err != nil { c.JSON(http.StatusUnauthorized, gin.H{"msg": fmt.Sprint("User does not have access to this person", err.Error())}) return @@ -43,7 +43,7 @@ func (srv *server) CreateRelationship(c *gin.Context, params api.CreateRelations case api.CreateRelationshipJSONBodyTypeChild: relationShipResultRaw, relationshipError = session.ExecuteWrite( qctx, memgraph.CreateChildParentRelationship( - qctx, *relationship.Id1, *relationship.Id2, *relationship.Relationship, + qctx, *relationship.Id2, *relationship.Id1, *relationship.Relationship, ), ) case api.CreateRelationshipJSONBodyTypeParent: @@ -91,7 +91,7 @@ func (srv *server) UpdateRelationship(c *gin.Context, id1, id2 int, params api.U defer aCancel() if err := auth.CouldManagePersonUnknownAdmin(actx, session, *relationship.Id1, params.XUserID); err != nil { - if err := auth.CouldManagePersonUnknownAdmin(actx, session, *relationship.Id1, params.XUserID); err != nil { + if err := auth.CouldManagePersonUnknownAdmin(actx, session, *relationship.Id2, params.XUserID); err != nil { c.JSON(http.StatusUnauthorized, gin.H{"msg": fmt.Sprint("User does not have access to this person", err.Error())}) return @@ -117,8 +117,8 @@ func (srv *server) GetRelationship(c *gin.Context, id1, id2 int, params api.GetR actx, aCancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout) defer aCancel() - if err := auth.CouldManagePersonUnknownAdmin(actx, session, id1, params.XUserID); err != nil { - if err := auth.CouldManagePersonUnknownAdmin(actx, session, id2, params.XUserID); err != nil { + if err := auth.CouldSeePersonsProfile(actx, session, id1, params.XUserID); err != nil { + if err := auth.CouldSeePersonsProfile(actx, session, id2, params.XUserID); err != nil { c.JSON(http.StatusUnauthorized, gin.H{"msg": fmt.Sprint("User does not have access to this person", err.Error())}) return diff --git a/apps/db-adapter/internal/memgraph/person_test.go b/apps/db-adapter/internal/memgraph/person_test.go index e50a806..89b7e63 100644 --- a/apps/db-adapter/internal/memgraph/person_test.go +++ b/apps/db-adapter/internal/memgraph/person_test.go @@ -29,7 +29,7 @@ func TestCreatePerson(t *testing.T) { } mockResult.On("Single", context.Background()).Return(mockRecord, nil) mockTx.On("Run", context.Background(), CreatePersonCypherQuery, map[string]any{ - "Person": api.PersonProperties{}, + "Person": map[string]any{}, }).Return(mockResult, nil) return mockTx }, @@ -41,7 +41,7 @@ func TestCreatePerson(t *testing.T) { mockTxSetup: func() *mock.Transaction { mockTx := new(mock.Transaction) mockTx.On("Run", context.Background(), CreatePersonCypherQuery, map[string]any{ - "Person": api.PersonProperties{}, + "Person": map[string]any{}, }).Return(nil, errors.New("run error")) return mockTx }, @@ -55,7 +55,7 @@ func TestCreatePerson(t *testing.T) { mockResult := new(mock.Result) mockResult.On("Single", context.Background()).Return(nil, errors.New("single error")) mockTx.On("Run", context.Background(), CreatePersonCypherQuery, map[string]any{ - "Person": api.PersonProperties{}, + "Person": map[string]any{}, }).Return(mockResult, nil) return mockTx }, @@ -160,13 +160,13 @@ func TestUpdatePerson(t *testing.T) { mockTx := new(mock.Transaction) mockResult := new(mock.Result) mockRecord := &neo4j.Record{ - Values: []any{"updatedValue"}, - Keys: []string{"updatedKey"}, + Values: []any{map[string]any{"updatedKey": "updatedValue"}}, + Keys: []string{"person"}, } mockResult.On("Single", context.Background()).Return(mockRecord, nil) mockTx.On("Run", context.Background(), UpdatePersonCypherQuery, map[string]any{ "id": 123, - "props": api.PersonProperties{}, + "props": map[string]any{}, }).Return(mockResult, nil) return mockTx }, @@ -179,7 +179,7 @@ func TestUpdatePerson(t *testing.T) { mockTx := new(mock.Transaction) mockTx.On("Run", context.Background(), UpdatePersonCypherQuery, map[string]any{ "id": 123, - "props": api.PersonProperties{}, + "props": map[string]any{}, }).Return(nil, errors.New("run error")) return mockTx }, @@ -194,7 +194,7 @@ func TestUpdatePerson(t *testing.T) { mockResult.On("Single", context.Background()).Return(nil, errors.New("single error")) mockTx.On("Run", context.Background(), UpdatePersonCypherQuery, map[string]any{ "id": 123, - "props": api.PersonProperties{}, + "props": map[string]any{}, }).Return(mockResult, nil) return mockTx }, diff --git a/apps/db-adapter/internal/memgraph/queries/create_child_parent_relationships.cypher b/apps/db-adapter/internal/memgraph/queries/create_child_parent_relationships.cypher index 4958c4a..98ddfcd 100644 --- a/apps/db-adapter/internal/memgraph/queries/create_child_parent_relationships.cypher +++ b/apps/db-adapter/internal/memgraph/queries/create_child_parent_relationships.cypher @@ -1,6 +1,6 @@ MATCH (a:Person), (b:Person) WHERE id(a) = $childId AND id(b) = $parentId AND $parentId != $childId -MERGE (a)-[r1:Child]->(b)-[r2:Parent]->(a) +MERGE (a)-[r1:Parent]->(b)-[r2:Child]->(a) ON CREATE SET r1 = $childRelationship ON CREATE SET r2 = $parentRelationship RETURN collect(r1)+collect(r2) as relationships; \ No newline at end of file diff --git a/apps/db-adapter/internal/memgraph/relationship.go b/apps/db-adapter/internal/memgraph/relationship.go index 2d4c3ab..451cd49 100644 --- a/apps/db-adapter/internal/memgraph/relationship.go +++ b/apps/db-adapter/internal/memgraph/relationship.go @@ -5,7 +5,6 @@ import ( "fmt" "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" ) @@ -108,16 +107,16 @@ func CreateChildParentRelationship( return nil, err } - relationships, rok := relationshipsRaw.([]dbtype.Relationship) + relationships, rok := relationshipsRaw.([]any) if !rok { return nil, err } siblingRecord, err := siblingResult.Single(ctx) - if err != nil { + if err == nil { siblingRelationship, ok := siblingRecord.Get("relationships") if ok { - siblings, ok := siblingRelationship.([]dbtype.Relationship) + siblings, ok := siblingRelationship.([]any) if ok { relationships = append(relationships, siblings...) } @@ -128,7 +127,7 @@ func CreateChildParentRelationship( } } -func CreateSiblingRelationship( +func CreateSiblingRelationship( //nolint:dupl // this is not worth fixing ctx context.Context, siblingId1, siblingId2 int, relationship api.FamilyRelationship, ) neo4j.ManagedTransactionWork { convertedRelationship := StructToMap(relationship) @@ -157,7 +156,7 @@ func CreateSiblingRelationship( } } -func CreateSpouseRelationship( +func CreateSpouseRelationship( //nolint:dupl // this is not worth fixing ctx context.Context, spouseId1, spouseId2 int, relationship api.FamilyRelationship, ) neo4j.ManagedTransactionWork { convertedRelationship := StructToMap(relationship) diff --git a/apps/db-adapter/internal/memgraph/struct_to_map.go b/apps/db-adapter/internal/memgraph/struct_to_map.go index e6febc0..fc838de 100644 --- a/apps/db-adapter/internal/memgraph/struct_to_map.go +++ b/apps/db-adapter/internal/memgraph/struct_to_map.go @@ -10,7 +10,7 @@ import ( // StructToMap recursively converts a struct to a map using JSON tags. // Nil pointers and unexported fields are excluded. -func StructToMap(input any) map[string]any { +func StructToMap(input any) map[string]any { //nolint:cyclop,gocyclo // this is a known issue with the neo4j-go-driver result := make(map[string]any) value := reflect.ValueOf(input) diff --git a/apps/db-adapter/main_test.go b/apps/db-adapter/main_test.go index 47dbba5..c55a233 100644 --- a/apps/db-adapter/main_test.go +++ b/apps/db-adapter/main_test.go @@ -111,10 +111,8 @@ func IntegrationTestFlow(dbAdapterURI string) func(t *testing.T) { t.Run("UpdatePerson", integration_tests.UpdatePersonTest(dbAdapterURI, client)) t.Run("AddInviteCodeToPerson", integration_tests.UpdatePersonWithInviteCodeTest(dbAdapterURI, client)) t.Run("CreateFamilyTest", integration_tests.CreateAFamilyTest(dbAdapterURI, client)) - t.Run("CreatePerson6", integration_tests.CreatePersonTest(dbAdapterURI, client)) - t.Run("CreatePerson7", integration_tests.CreatePersonTest(dbAdapterURI, client)) - t.Run("CreatePerson8", integration_tests.CreatePersonTest(dbAdapterURI, client)) t.Run("CreateRelationships", integration_tests.CreateRelationshipsTest(dbAdapterURI, client)) + t.Run("GetRelationship", integration_tests.GetRelationshipTest(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)) diff --git a/apps/db-adapter/manual-testing/create_relationship_child.bru b/apps/db-adapter/manual-testing/create_relationship_child.bru new file mode 100644 index 0000000..ec00e08 --- /dev/null +++ b/apps/db-adapter/manual-testing/create_relationship_child.bru @@ -0,0 +1,29 @@ +meta { + name: create_relationship_child + type: http + seq: 16 +} + +post { + url: http://localhost:8080/relationship + body: json + auth: inherit +} + +headers { + Content-Type: application/json + X-User-Id: 2 +} + +body:json { + { + "id1": 7, + "id2": 6, + "type": "child", + "relationship": { + "verified": true, + "notes": "Test notes", + "from": "2022-01-01" + } + } +} diff --git a/apps/db-adapter/manual-testing/create_relationship_parent.bru b/apps/db-adapter/manual-testing/create_relationship_parent.bru new file mode 100644 index 0000000..4a310fd --- /dev/null +++ b/apps/db-adapter/manual-testing/create_relationship_parent.bru @@ -0,0 +1,29 @@ +meta { + name: create_relationship_parent + type: http + seq: 17 +} + +post { + url: http://localhost:8080/relationship + body: json + auth: inherit +} + +headers { + Content-Type: application/json + X-User-Id: 2 +} + +body:json { + { + "id1": 6, + "id2": 7, + "type": "parent", + "relationship": { + "verified": false, + "notes": "Test notes asdasdasda", + "from": "2021-01-01" + } + } +} diff --git a/apps/db-adapter/manual-testing/create_relationship_sibling.bru b/apps/db-adapter/manual-testing/create_relationship_sibling.bru new file mode 100644 index 0000000..24be191 --- /dev/null +++ b/apps/db-adapter/manual-testing/create_relationship_sibling.bru @@ -0,0 +1,29 @@ +meta { + name: create_relationship_sibling + type: http + seq: 18 +} + +post { + url: http://localhost:8080/relationship + body: json + auth: inherit +} + +headers { + Content-Type: application/json + X-User-Id: 2 +} + +body:json { + { + "id1": 7, + "id2": 8, + "type": "sibling", + "relationship": { + "verified": true, + "notes": "OwO", + "from": "2024-01-01" + } + } +} diff --git a/apps/db-adapter/manual-testing/create_relationship_spouse.bru b/apps/db-adapter/manual-testing/create_relationship_spouse.bru new file mode 100644 index 0000000..a77eb80 --- /dev/null +++ b/apps/db-adapter/manual-testing/create_relationship_spouse.bru @@ -0,0 +1,29 @@ +meta { + name: create_relationship_spouse + type: http + seq: 19 +} + +post { + url: http://localhost:8080/relationship + body: json + auth: inherit +} + +headers { + Content-Type: application/json + X-User-Id: 2 +} + +body:json { + { + "id1": 7, + "id2": 8, + "type": "sibling", + "relationship": { + "verified": true, + "notes": "OwO", + "from": "2024-01-01" + } + } +} diff --git a/apps/db-adapter/pkg/api/api.gen.go b/apps/db-adapter/pkg/api/api.gen.go index 40353ca..5a72e43 100644 --- a/apps/db-adapter/pkg/api/api.gen.go +++ b/apps/db-adapter/pkg/api/api.gen.go @@ -240,6 +240,18 @@ type Relationship struct { Type *string `json:"type"` } +// DbtypeRelationship defines model for dbtypeRelationship. +type DbtypeRelationship struct { + ElementId *string `json:"ElementId,omitempty"` + EndElementId *string `json:"EndElementId,omitempty"` + EndId *int `json:"EndId,omitempty"` + Id *int `json:"Id,omitempty"` + Props *FamilyRelationship `json:"Props,omitempty"` + StartElementId *string `json:"StartElementId,omitempty"` + StartId *int `json:"StartId,omitempty"` + Type *string `json:"Type"` +} + // GetProfileAdminsParams defines parameters for GetProfileAdmins. type GetProfileAdminsParams struct { XUserID int `json:"X-User-ID"` diff --git a/apps/db-adapter/pkg/api/flatten_list.go b/apps/db-adapter/pkg/api/flatten_list.go index 0a3f868..a3132f5 100644 --- a/apps/db-adapter/pkg/api/flatten_list.go +++ b/apps/db-adapter/pkg/api/flatten_list.go @@ -16,7 +16,7 @@ func Flatten(input any, uniqueIds *[]int64, result *[]any) error { switch val.Kind() { case reflect.Slice, reflect.Array: - for i := 0; i < val.Len(); i++ { + for i := range val.Len() { if err := Flatten(val.Index(i).Interface(), uniqueIds, result); err != nil { return err } @@ -35,15 +35,15 @@ func Flatten(input any, uniqueIds *[]int64, result *[]any) error { switch input.(type) { case dbtype.Node: node := val.Interface().(dbtype.Node) - if !slices.Contains(*uniqueIds, node.Id) { + if !slices.Contains(*uniqueIds, node.Id) { //nolint:staticcheck // this is a known issue with the neo4j-go-driver *result = append(*result, node) - *uniqueIds = append(*uniqueIds, node.Id) + *uniqueIds = append(*uniqueIds, node.Id) //nolint:staticcheck // this is a known issue with the neo4j-go-driver } case dbtype.Relationship: relationship := val.Interface().(dbtype.Relationship) - if !slices.Contains(*uniqueIds, relationship.Id) { + if !slices.Contains(*uniqueIds, relationship.Id) { //nolint:staticcheck // this is a known issue with the neo4j-go-driver *result = append(*result, relationship) - *uniqueIds = append(*uniqueIds, relationship.Id) + *uniqueIds = append(*uniqueIds, relationship.Id) //nolint:staticcheck // this is a known issue with the neo4j-go-driver } } default: