From a4049969e06fc80a534beea4cb94cb4dae4d7bda Mon Sep 17 00:00:00 2001 From: "Geurtjens,D. (Douwe Geurtjens)" <d.geurtjens@students.uu.nl> Date: Mon, 29 Nov 2021 08:48:43 +0000 Subject: [PATCH] Better error handling and beter seperation for clean code architecture --- aql/convertQuery.go | 17 +- aql/convertQuery_test.go | 332 +++++++++++++++++++++++++++++++-- aql/hierarchy_test.go | 2 +- {aql => entity}/fixIndices.go | 11 +- entity/queryStructValidator.go | 20 ++ 5 files changed, 354 insertions(+), 28 deletions(-) rename {aql => entity}/fixIndices.go (66%) diff --git a/aql/convertQuery.go b/aql/convertQuery.go index b9b8129..e02227a 100644 --- a/aql/convertQuery.go +++ b/aql/convertQuery.go @@ -25,19 +25,18 @@ ConvertQuery converts an IncomingQueryJSON object into AQL func (s *Service) ConvertQuery(JSONQuery *entity.IncomingQueryJSON) (*string, error) { // TODO: MICHAEL WANT A SINGLE ENTITY TO RETURN A SUMMARY OF ATTRIBUTE VALUES (HISTOGRAM THINGIES) // Check to make sure all indexes exist - // The largest possible id for an entity - entityCount := len(JSONQuery.Entities) - 1 - // The largest possible id for a relation - relationCount := len(JSONQuery.Relations) - 1 + // The count of entities + entityCount := len(JSONQuery.Entities) + // The count of relations + relationCount := len(JSONQuery.Relations) // There are no entities or relations, our query is empty - if entityCount < 0 && relationCount < 0 { + if entityCount <= 0 && relationCount <= 0 { fmt.Println("Empty query sent, returning default response") return defaultReturn() } // There are no enities or there are no relations - if entityCount < 0 || relationCount < 0 { - fmt.Println("No relations or entities sent, returning default response") - return defaultReturn() + if entityCount <= 0 || relationCount <= 0 { + return nil, errors.New("no relations or entities sent") } potentialErrors := entity.ValidateStruct(*JSONQuery) @@ -49,7 +48,7 @@ func (s *Service) ConvertQuery(JSONQuery *entity.IncomingQueryJSON) (*string, er return nil, errors.New("JSONQuery invalid") } - entityMap, relationMap, _ := fixIndices(JSONQuery) + entityMap, relationMap, _ := entity.FixIndices(JSONQuery) var tree []entity.Tree var topNode entity.QueryEntityStruct diff --git a/aql/convertQuery_test.go b/aql/convertQuery_test.go index 5effaf7..4546451 100644 --- a/aql/convertQuery_test.go +++ b/aql/convertQuery_test.go @@ -1531,19 +1531,9 @@ func TestNoRelationsField(t *testing.T) { var JSONQuery entity.IncomingQueryJSON json.Unmarshal(query, &JSONQuery) - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) + _, err := service.ConvertQuery(&JSONQuery) - // Assert that the result and the expected result are the same - correctConvertedResult := `LET nodes = first(RETURN UNION_DISTINCT([],[])) - LET edges = first(RETURN UNION_DISTINCT([],[])) - RETURN {"vertices":nodes, "edges":edges }` - regExCleaner := regexp.MustCompile(`\s+`) - correctCleanedResult := regExCleaner.ReplaceAllString(string(correctConvertedResult), " ") - convertedCleanedResult := regExCleaner.ReplaceAllString(*convertedResult, " ") - assert.Equal(t, correctCleanedResult, convertedCleanedResult) + assert.Equal(t, errors.New("no relations or entities sent"), err) } /* @@ -1607,3 +1597,321 @@ func TestIncorrectRelationFrom(t *testing.T) { // Assert that there is an error assert.Equal(t, errors.New("JSONQuery invalid"), err) } + +/* +Tests two separated chains consisting of 1 relation each + t: *testing.T, makes go recognise this as a test +*/ +func TestSeparatedChainSingleRelationPerChain(t *testing.T) { + // Setup for test + // Create query conversion service + service := NewService() + + query := []byte(`{ + "databaseName": "TweedeKamer", + "return": { + "entities": [ + 0, + 1, + 2, + 3 + ], + "relations": [ + 0 + ] + }, + "entities": [ + { + "name": "parliament", + "ID": 0, + "constraints": [] + }, + { + "name": "parties", + "ID": 1, + "constraints": [] + }, + { + "name": "parliament", + "ID": 2, + "constraints": [] + }, + { + "name": "parties", + "ID": 3, + "constraints": [] + } + ], + "relations": [ + { + "ID": 0, + "name": "member_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 0, + "toType": "entity", + "toID": 1, + "constraints":[] + }, + { + "ID": 1, + "name": "member_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 2, + "toType": "entity", + "toID": 3, + "constraints":[] + } + ], + "groupBys": [], + "limit": 5000, + "modifiers": [] + }`) + + // Unmarshall the incoming message into an IncomingJSONQuery object + var JSONQuery entity.IncomingQueryJSON + json.Unmarshal(query, &JSONQuery) + + _, err := service.ConvertQuery(&JSONQuery) + + // Assert that there is an error + assert.Equal(t, errors.New("JSONQuery invalid"), err) +} + +/* +Tests two separated chains consisting of 1 relation each + t: *testing.T, makes go recognise this as a test +*/ +func TestSeparatedChainDoubleRelationPerChain(t *testing.T) { + // Setup for test + // Create query conversion service + service := NewService() + + query := []byte(`{ + "databaseName": "TweedeKamer", + "return": { + "entities": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "relations": [ + 0 + ] + }, + "entities": [ + { + "name": "parliament", + "ID": 0, + "constraints": [] + }, + { + "name": "parties", + "ID": 1, + "constraints": [] + }, + { + "name": "parliament", + "ID": 2, + "constraints": [] + }, + { + "name": "parties", + "ID": 3, + "constraints": [] + }, + { + "name": "resolutions", + "ID": 4, + "constraints": [] + }, + { + "name": "resolutions", + "ID": 5, + "constraints": [] + } + ], + "relations": [ + { + "ID": 0, + "name": "member_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 0, + "toType": "entity", + "toID": 1, + "constraints":[] + }, + { + "ID": 1, + "name": "member_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 2, + "toType": "entity", + "toID": 3, + "constraints":[] + }, + { + "ID": 2, + "name": "submits", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 0, + "toType": "entity", + "toID": 4, + "constraints":[] + }, + { + "ID": 3, + "name": "submits", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 2, + "toType": "entity", + "toID": 5, + "constraints":[] + } + ], + "groupBys": [], + "limit": 5000, + "modifiers": [] + }`) + + // Unmarshall the incoming message into an IncomingJSONQuery object + var JSONQuery entity.IncomingQueryJSON + json.Unmarshal(query, &JSONQuery) + + _, err := service.ConvertQuery(&JSONQuery) + + // Assert that there is an error + assert.Equal(t, errors.New("JSONQuery invalid"), err) +} + +/* +Tests two separated chains with unequal relations per chain + t: *testing.T, makes go recognise this as a test +*/ +func TestSeparatedChainUnequalRelationPerChain(t *testing.T) { + // Setup for test + // Create query conversion service + service := NewService() + + query := []byte(`{ + "databaseName": "TweedeKamer", + "return": { + "entities": [ + 0, + 1, + 2, + 3, + 4 + ], + "relations": [ + 0 + ] + }, + "entities": [ + { + "name": "parliament", + "ID": 0, + "constraints": [] + }, + { + "name": "parties", + "ID": 1, + "constraints": [] + }, + { + "name": "parliament", + "ID": 2, + "constraints": [] + }, + { + "name": "parties", + "ID": 3, + "constraints": [] + }, + { + "name": "resolutions", + "ID": 4, + "constraints": [] + } + ], + "relations": [ + { + "ID": 0, + "name": "member_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 0, + "toType": "entity", + "toID": 1, + "constraints":[] + }, + { + "ID": 1, + "name": "member_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 2, + "toType": "entity", + "toID": 3, + "constraints":[] + }, + { + "ID": 2, + "name": "submits", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 2, + "toType": "entity", + "toID": 4, + "constraints":[] + } + ], + "groupBys": [], + "limit": 5000, + "modifiers": [] + }`) + + // Unmarshall the incoming message into an IncomingJSONQuery object + var JSONQuery entity.IncomingQueryJSON + json.Unmarshal(query, &JSONQuery) + + _, err := service.ConvertQuery(&JSONQuery) + + // Assert that there is an error + assert.Equal(t, errors.New("JSONQuery invalid"), err) +} diff --git a/aql/hierarchy_test.go b/aql/hierarchy_test.go index 0738254..8f8a507 100644 --- a/aql/hierarchy_test.go +++ b/aql/hierarchy_test.go @@ -140,7 +140,7 @@ func TestHierarchyBasic(t *testing.T) { var JSONQuery entity.IncomingQueryJSON json.Unmarshal(query, &JSONQuery) // Get the hierarchy and turn it into JSON so we can turn it into strings later - entityMap, relationMap, _ := fixIndices(&JSONQuery) + entityMap, relationMap, _ := entity.FixIndices(&JSONQuery) treeList, topNode := createHierarchy(&JSONQuery, entityMap, relationMap) jsonTopNode, err := json.Marshal(topNode) if err != nil { diff --git a/aql/fixIndices.go b/entity/fixIndices.go similarity index 66% rename from aql/fixIndices.go rename to entity/fixIndices.go index 1056cf4..dc97192 100644 --- a/aql/fixIndices.go +++ b/entity/fixIndices.go @@ -1,10 +1,9 @@ -package aql +package entity -import ( - "git.science.uu.nl/graphpolaris/query-conversion/entity" -) - -func fixIndices(JSONQuery *entity.IncomingQueryJSON) (map[int]int, map[int]int, map[int]int) { +/* +A function that maps pill IDs to a range of 0..x +*/ +func FixIndices(JSONQuery *IncomingQueryJSON) (map[int]int, map[int]int, map[int]int) { entityMap := make(map[int]int) for i, e := range JSONQuery.Entities { entityMap[e.ID] = i diff --git a/entity/queryStructValidator.go b/entity/queryStructValidator.go index 2d44ce8..d25f2d8 100644 --- a/entity/queryStructValidator.go +++ b/entity/queryStructValidator.go @@ -22,6 +22,10 @@ func ValidateStruct(JSONQuery IncomingQueryJSON) []error { if len(JSONQuery.GroupBys) != 0 { ret = append(ret, getIllegalToFromInGroupBy(JSONQuery, minEntityID, maxEntityID, minRelationID, maxRelationID)...) } + if len(ret) == 0 { + ret = append(ret, separatedChainExists(JSONQuery)...) + } + return ret } @@ -145,3 +149,19 @@ func getMinAndMaxGroupByID(groupBys []QueryGroupByStruct) (int, int) { return min, max } + +// Get chains that are not connected to the main chain +func separatedChainExists(JSONQuery IncomingQueryJSON) []error { + ret := make([]error, 0) + entityCount := len(JSONQuery.Entities) + relationCount := len(JSONQuery.Relations) + // A relation always has 2 entities if it's on its own + // If there is a chain (e-r-e-r-e) then there will be one more entity than relation + // If there is two seperate chains you will always end up adding at least 2 entities more than relations + // (e-r-e-r-e) and (e-r-e) has 3 relations but 5 entities, thus we know they are separate + if relationCount != entityCount-1 { + err := errors.New("separated chain found") + ret = append(ret, err) + } + return ret +} -- GitLab