diff --git a/Makefile b/Makefile index 69ce32c8fcfad2e917f64c7c40a57d1d5da54130..a79a8b667d7dafad78cc43625437232a70c3d71b 100644 --- a/Makefile +++ b/Makefile @@ -3,10 +3,10 @@ lint: dep ## Lint the files @golint -set_exit_status ./... -test: ## Run unittests +test: dep ## Run unittests @go test -cover -coverprofile=coverage.txt -covermode count ./... -dep: ## Get the dependencies +dep: login ## Get the dependencies @go get -a -v ./... @go get google.golang.org/grpc/internal/transport@v1.37.0 @go get google.golang.org/grpc@v1.37.0 @@ -26,7 +26,7 @@ macos: $(eval export GOOS := darwin) @go build -o builds/main ./cmd/query-service/ -linux: # Build for linux +linux: login # Build for linux $(eval export GOOS := linux) CGO_ENABLED=0 go build -o builds/main ./cmd/query-service/ @@ -34,7 +34,6 @@ run: ./builds/main develop-sivan: - $(eval export RABBIT_USER := haha-test) $(eval export RABBIT_PASSWORD := dikkedraak) $(eval export RABBIT_HOST := 192.168.178.158) @@ -48,7 +47,6 @@ develop-sivan: develop: - $(eval export RABBIT_USER := guest) $(eval export RABBIT_PASSWORD := guest) $(eval export RABBIT_HOST := localhost) @@ -62,14 +60,17 @@ develop: @go run cmd/query-service/main.go -docker: +docker: login make linux @docker build -t query-service:latest . @docker tag query-service:latest datastropheregistry.azurecr.io/query-service:latest @docker push datastropheregistry.azurecr.io/query-service\:latest -staging: +staging: login make linux @docker build -t query-service-staging:latest . @docker tag query-service-staging:latest datastropheregistry.azurecr.io/query-service-staging:latest - @docker push datastropheregistry.azurecr.io/query-service-staging:latest \ No newline at end of file + @docker push datastropheregistry.azurecr.io/query-service-staging:latest + +login: + echo -e "machine git.science.uu.nl\nlogin gitlab-ci-token\npassword ${CI_JOB_TOKEN}" > ~/.netrc \ No newline at end of file diff --git a/cmd/query-service/main.go b/cmd/query-service/main.go index 43ec96b744452cea5784f6c9f1302fd64f1e5b21..987f0196182e30d7feb4e24790bb4849fdbf37fb 100644 --- a/cmd/query-service/main.go +++ b/cmd/query-service/main.go @@ -1,17 +1,16 @@ package main import ( - "query-service/internal/adapters/brokeradapter" - "query-service/internal/drivers/brokerdriver" - "query-service/internal/drivers/keyvaluedriver" "query-service/internal/drivers/rpcdriver" "query-service/internal/usecases/consume" - "query-service/internal/usecases/convertquery" "query-service/internal/usecases/databaseinfo" "query-service/internal/usecases/produce" "query-service/internal/usecases/request" "query-service/pkg/logger" + "git.science.uu.nl/datastrophe/broker" + "git.science.uu.nl/datastrophe/keyvaluestore" + "git.science.uu.nl/datastrophe/query-conversion/aql" "github.com/thijsheijden/alice" ) @@ -20,29 +19,28 @@ func main() { logger.Start() // MARK: Create relevant services - redisService := keyvaluedriver.NewRedisDriver() + keyValueStore := keyvaluestore.NewDriver() // Create new rpc driver rpcDriver := rpcdriver.New() // MARK: Create alice RabbitMQ services - brokerGateway := brokeradapter.CreateGateway() - aliceBroker := brokerdriver.CreateAliceBroker(brokerGateway) + broker := broker.NewDriver() // Instantiate an implementation of the produce UseCase - produceService := produce.NewService(aliceBroker, redisService) + produceService := produce.NewService(broker, keyValueStore) // MARK: Create relevant services for consuming a message - convertQueryService := convertquery.NewService() + convertQueryService := aql.NewService() requestSenderService := request.NewService() databaseInfoService := databaseinfo.NewService(rpcDriver) - consumeService := consume.NewService(aliceBroker, produceService, convertQueryService, requestSenderService, databaseInfoService) + consumeService := consume.NewService(broker, produceService, convertQueryService, requestSenderService, databaseInfoService) // MARK: Start services - redisService.Start() + keyValueStore.Connect() produceService.Start() go consumeService.Start() diff --git a/go.mod b/go.mod index 92a10edd24324270a81bdbb5ae20fd74bfa9f684..8046f082ba2a4ac426d4725c1463fe67283190f1 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,14 @@ module query-service go 1.15 require ( + git.science.uu.nl/datastrophe/broker v0.0.0-20210516094125-abbeaf96fd58 + git.science.uu.nl/datastrophe/keyvaluestore v0.0.0-20210517170603-34902cd5c90d + git.science.uu.nl/datastrophe/query-conversion v0.0.0-20210517164802-5852eee71ec0 github.com/arangodb/go-driver v0.0.0-20210506071742-64f314d85db7 - github.com/boumenot/gocover-cobertura v1.1.0 // indirect - github.com/go-redis/redis/v8 v8.8.2 - github.com/streadway/amqp v1.0.0 github.com/stretchr/testify v1.7.0 github.com/thijsheijden/alice v0.1.18 - golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect - golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 // indirect - golang.org/x/tools v0.1.1 // indirect + golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 // indirect google.golang.org/grpc v1.37.0 google.golang.org/protobuf v1.26.0 ) diff --git a/go.sum b/go.sum index 56750d1c9026646676c3e19110afbf1444c8b453..8685c26d1b1be88c1e1f46e6a0cd6a475bee8f85 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,15 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +git.science.uu.nl/datastrophe/broker v0.0.0-20210516094125-abbeaf96fd58 h1:hnPHRwbH3EwMO+5XSPR1JFusrmxx/dq84mt5wKml1jw= +git.science.uu.nl/datastrophe/broker v0.0.0-20210516094125-abbeaf96fd58/go.mod h1:+ua8t+K6R+rF4zllcXH3QYPzpB+8bsAsw1/h6kflwfM= +git.science.uu.nl/datastrophe/keyvaluestore v0.0.0-20210517170603-34902cd5c90d h1:0F1IcemW3WmwZ0yPsFYIwo+MVlBOLHIcyMM7qKNH3hc= +git.science.uu.nl/datastrophe/keyvaluestore v0.0.0-20210517170603-34902cd5c90d/go.mod h1:8fw3mDyMATpBNUhd3T1Me0FvYRGIYtyRgP6Q4EyBUgE= +git.science.uu.nl/datastrophe/query-conversion v0.0.0-20210517164802-5852eee71ec0 h1:UrYqOFjIFxaHtmzqsoId48g/jPEdGX4MCX5sDjbzBSI= +git.science.uu.nl/datastrophe/query-conversion v0.0.0-20210517164802-5852eee71ec0/go.mod h1:6rvalwekoukmVu3SbWmZkj8wBZEm34wDbA4Ilxcb+jw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/arangodb/go-driver v0.0.0-20210506071742-64f314d85db7 h1:xRFEg4kM17h2fIOt3o4+mws3uRpw5rqWtemPYKfTURg= github.com/arangodb/go-driver v0.0.0-20210506071742-64f314d85db7/go.mod h1:3NUekcRLpgheFIGEwcOvxilEW73MV1queNKW58k7sdc= github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e h1:Xg+hGrY2LcQBbxd0ZFdbGSyRKTYMZCfBbw/pMJFOk1g= github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e/go.mod h1:mq7Shfa/CaixoDxiyAAc5jZ6CVBAyPaNQCGS7mkj4Ho= -github.com/boumenot/gocover-cobertura v1.1.0 h1:nqMsp1zONyd3Bz98gscF2CpMupSCY0BQA4nmLhkcoYQ= -github.com/boumenot/gocover-cobertura v1.1.0/go.mod h1:fz7ly8dslE42VRR5ZWLt2OHGDHjkTiA2oNvKgJEjLT0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -74,15 +78,12 @@ github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/thijsheijden/alice v0.1.18 h1:lHmhzruprpH0FH6XlCg8zDqTZI15Iuuj9VCNvH6a8Wc= github.com/thijsheijden/alice v0.1.18/go.mod h1:lYOP30HKhw/7xJa3lLhs+Xsdc5T7MRo7DOb/npzfg9I= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng= go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg= go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg= @@ -98,13 +99,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -112,11 +107,9 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -127,7 +120,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -140,9 +132,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 h1:yhBbb4IRs2HS9PPlAg6DMC6mUOKexJBNsLf4Z+6En1Q= -golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= @@ -154,12 +145,8 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200526224456-8b020aee10d2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200818005847-188abfa75333/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/adapters/brokeradapter/brokeradapter.go b/internal/adapters/brokeradapter/brokeradapter.go deleted file mode 100644 index 724836ca611da7406f023d35dd82ce81c2ad46cc..0000000000000000000000000000000000000000 --- a/internal/adapters/brokeradapter/brokeradapter.go +++ /dev/null @@ -1,26 +0,0 @@ -package brokeradapter - -import "github.com/streadway/amqp" - -// A Message describes a standard message queue message within the service -type Message struct { - Headers map[string]interface{} - Body []byte -} - -// An Gateway converts AMQP formats to universal formats -type Gateway struct { -} - -// CreateGateway creates a gateway -func CreateGateway() *Gateway { - return &Gateway{} -} - -// TransformMessage transforms an AMQP delivery into a general format -func (a *Gateway) TransformMessage(msg amqp.Delivery) *Message { - return &Message{ - Headers: (map[string]interface{})(msg.Headers), - Body: msg.Body, - } -} diff --git a/internal/adapters/brokeradapter/interface.go b/internal/adapters/brokeradapter/interface.go deleted file mode 100644 index d67d8a9c09e620525725049aae5faaa85b92aac3..0000000000000000000000000000000000000000 --- a/internal/adapters/brokeradapter/interface.go +++ /dev/null @@ -1,8 +0,0 @@ -package brokeradapter - -import "github.com/streadway/amqp" - -// GatewayInterface is an interface describing a GateWay -type GatewayInterface interface { - TransformMessage(msg amqp.Delivery) *Message -} diff --git a/internal/drivers/brokerdriver/broker.go b/internal/drivers/brokerdriver/broker.go deleted file mode 100644 index a37b6fddc2470e5f162424b6db822cabca881551..0000000000000000000000000000000000000000 --- a/internal/drivers/brokerdriver/broker.go +++ /dev/null @@ -1,119 +0,0 @@ -package brokerdriver - -import ( - "log" - "os" - "query-service/internal/adapters/brokeradapter" - "query-service/pkg/errorhandler" - "query-service/pkg/logger" - "strconv" - - "time" - - "github.com/thijsheijden/alice" -) - -// Driver models an Alice RabbitMQ broker -type Driver struct { - broker alice.Broker - gateway brokeradapter.GatewayInterface -} - -// CreateAliceBroker creates an Alice broker -func CreateAliceBroker(gateway brokeradapter.GatewayInterface) *Driver { - - // Create connection config using environment variables - rabbitUser := os.Getenv("RABBIT_USER") - rabbitPassword := os.Getenv("RABBIT_PASSWORD") - rabbitHost := os.Getenv("RABBIT_HOST") - rabbitPort, err := strconv.Atoi(os.Getenv("RABBIT_PORT")) - errorhandler.FailWithError(err, "invalid rabbitmq port given") - - config := alice.CreateConfig(rabbitUser, rabbitPassword, rabbitHost, rabbitPort, true, time.Minute*1, alice.DefaultErrorHandler) - - // Attempt to create a broker, if an error is returned retry the connection every 10 seconds - broker, err := alice.CreateBroker(config) - if err != nil { - errorhandler.FailWithError(err, err.Error()) - errorhandler.LogError(err, "Failed to connect to RabbitMQ") - - // Create 10 second ticker - ticker := time.NewTicker(time.Second * 10) - done := make(chan bool, 1) - - for { - select { - case <-ticker.C: - logger.Log("Retrying RabbitMQ connection") - broker, err = alice.CreateBroker(config) - if err == nil { - done <- true - ticker.Stop() - } - case <-done: - log.Println("Succesfully connected to broker") - return &Driver{ - broker: broker, - gateway: gateway, - } - } - } - } - - // Return the created driver - // This code only gets called if the broker creation works on the first try, which it more often than not does - return &Driver{ - broker: broker, - gateway: gateway, - } -} - -// CreateConsumer creates an AliceConsumer on a certain exchange and queue -func (d *Driver) CreateConsumer() Consumer { - exchangeID := "requests-exchange" - routingKey := "aql-query-request" - - // Declare the exchange we want to bind to - exchange, err := alice.CreateDefaultExchange(exchangeID, alice.Direct) - if err != nil { - errorhandler.FailWithError(err, "failed to create exchange") - } - - // Declare the queue we will consume from - queue := alice.CreateQueue(exchange, "aql-query-queue", true, false, true, false, nil) - - // Create the consumer - c, err := d.broker.CreateConsumer(queue, routingKey, "", alice.DefaultConsumerErrorHandler) - if err != nil { - errorhandler.FailWithError(err, "failed to create consumer") - } - - consumer := &AliceConsumer{ - broker: d, - consumer: c, - } - - return consumer -} - -// CreateProducer creates an AliceProducer on a certain exchange -func (d *Driver) CreateProducer() Producer { - exchangeID := "ui-direct-exchange" - - exchange, err := alice.CreateDefaultExchange(exchangeID, alice.Direct) - if err != nil { - errorhandler.FailWithError(err, "failed to create exchange for producer") - } - - p, err := d.broker.CreateProducer(exchange, alice.DefaultProducerErrorHandler) - if err != nil { - errorhandler.FailWithError(err, "failed to created producer") - } - - producer := &AliceProducer{ - broker: d, - producer: p, - } - - return producer -} diff --git a/internal/drivers/brokerdriver/consumer.go b/internal/drivers/brokerdriver/consumer.go deleted file mode 100644 index 194b2ad217e79868a423f2a6d17b27bfad5805ba..0000000000000000000000000000000000000000 --- a/internal/drivers/brokerdriver/consumer.go +++ /dev/null @@ -1,34 +0,0 @@ -package brokerdriver - -import ( - "query-service/internal/adapters/brokeradapter" - - "github.com/streadway/amqp" - "github.com/thijsheijden/alice" -) - -// AliceConsumer models a RabbitMQ consumer in Alice -type AliceConsumer struct { - broker *Driver - consumer alice.Consumer - messageHandler func(msg *brokeradapter.Message) -} - -// ConsumeMessages starts the consumer using an alice consumer -func (ac *AliceConsumer) ConsumeMessages() { - go ac.consumer.ConsumeMessages(nil, false, ac.handleMessage) -} - -func (ac *AliceConsumer) handleMessage(msg amqp.Delivery) { - // Convert message using the gateway - // Pass message to the message handler - ac.messageHandler(ac.broker.gateway.TransformMessage(msg)) - - // Acknowledge the message was received - msg.Ack(true) -} - -// SetMessageHandler sets the message handler to the supplied function -func (ac *AliceConsumer) SetMessageHandler(handler func(msg *brokeradapter.Message)) { - ac.messageHandler = handler -} diff --git a/internal/drivers/brokerdriver/interface.go b/internal/drivers/brokerdriver/interface.go deleted file mode 100644 index 3dc54da2fc47fbae03fcf60d59c5dedcf8f296f6..0000000000000000000000000000000000000000 --- a/internal/drivers/brokerdriver/interface.go +++ /dev/null @@ -1,24 +0,0 @@ -package brokerdriver - -import ( - "query-service/internal/adapters/brokeradapter" - - "github.com/streadway/amqp" -) - -// Broker models a message broker -type Broker interface { - CreateConsumer() Consumer - CreateProducer() Producer -} - -// A Consumer belongs to a broker and consumes messages from a queue -type Consumer interface { - ConsumeMessages() - SetMessageHandler(handler func(msg *brokeradapter.Message)) -} - -// A Producer belongs to a broker and publishes messages to a queue -type Producer interface { - PublishMessage(body *[]byte, routingKey *string, headers *amqp.Table) -} diff --git a/internal/drivers/brokerdriver/mock/broker.go b/internal/drivers/brokerdriver/mock/broker.go deleted file mode 100644 index 80f1c1c89637212a46c18cd1fd89fc32a2a6c8f7..0000000000000000000000000000000000000000 --- a/internal/drivers/brokerdriver/mock/broker.go +++ /dev/null @@ -1,39 +0,0 @@ -package mockbrokerdriver - -import ( - "query-service/internal/adapters/brokeradapter" - "query-service/internal/drivers/brokerdriver" -) - -// Driver is mock gateway -type Driver struct { - gateway brokeradapter.GatewayInterface - - // Mock messages that are published by producers on this broker - // Key is the routing key - // Value is a slice of messages, in order of being sent 'first -> last' - Messages map[string][]brokeradapter.Message -} - -// CreateBroker creates a broker driver (mock) -func CreateBroker(gateway brokeradapter.GatewayInterface) *Driver { - return &Driver{ - gateway: gateway, - Messages: make(map[string][]brokeradapter.Message), - } -} - -// CreateConsumer creates a consumer (mock) -func (d *Driver) CreateConsumer() brokerdriver.Consumer { - return &Consumer{ - broker: d, - } -} - -// CreateProducer creates a producer (mock) -func (d *Driver) CreateProducer() brokerdriver.Producer { - return &Producer{ - broker: d, - exchange: "ui-direct-exchange", // This is the only exchange this service produces to - } -} diff --git a/internal/drivers/brokerdriver/mock/consumer.go b/internal/drivers/brokerdriver/mock/consumer.go deleted file mode 100644 index 9118043873bffca0bad53758fc7c109361cf409c..0000000000000000000000000000000000000000 --- a/internal/drivers/brokerdriver/mock/consumer.go +++ /dev/null @@ -1,18 +0,0 @@ -package mockbrokerdriver - -import "query-service/internal/adapters/brokeradapter" - -// A Consumer implements the consumer interface (mock) -type Consumer struct { - broker *Driver -} - -// ConsumeMessages consumes messages from the broker (mock) -func (c *Consumer) ConsumeMessages() { - -} - -// SetMessageHandler mocks the setting of a message handler (mock) -func (c *Consumer) SetMessageHandler(handler func(msg *brokeradapter.Message)) { - -} diff --git a/internal/drivers/brokerdriver/mock/producer.go b/internal/drivers/brokerdriver/mock/producer.go deleted file mode 100644 index 1367971d059641fec4a3c0adcec28cf6d4591bd3..0000000000000000000000000000000000000000 --- a/internal/drivers/brokerdriver/mock/producer.go +++ /dev/null @@ -1,27 +0,0 @@ -package mockbrokerdriver - -import ( - "query-service/internal/adapters/brokeradapter" - - "github.com/streadway/amqp" -) - -// A Producer implements the producer interface (mock) -type Producer struct { - broker *Driver - - // The exchange this producer is connected to - exchange string -} - -// PublishMessage publishes a message to the given queue (mock) -func (p *Producer) PublishMessage(body *[]byte, routingKey *string, headers *amqp.Table) { - // Create the message - msg := brokeradapter.Message{ - Headers: *headers, - Body: *body, - } - - // Append the message to the list - p.broker.Messages[*routingKey] = append(p.broker.Messages[*routingKey], msg) -} diff --git a/internal/drivers/brokerdriver/producer.go b/internal/drivers/brokerdriver/producer.go deleted file mode 100644 index 513e43d41b55110590092c0c9dc7479b7dd83402..0000000000000000000000000000000000000000 --- a/internal/drivers/brokerdriver/producer.go +++ /dev/null @@ -1,23 +0,0 @@ -package brokerdriver - -import ( - "fmt" - "query-service/pkg/logger" - - "github.com/streadway/amqp" - "github.com/thijsheijden/alice" -) - -// AliceProducer models a RabbitMQ producer in Alice -type AliceProducer struct { - broker *Driver - producer alice.Producer -} - -// PublishMessage will publish a message to the specified queue id (mock) -func (ap *AliceProducer) PublishMessage(body *[]byte, routingKey *string, headers *amqp.Table) { - sessionID := (*headers)["sessionID"] - logger.Log(fmt.Sprintf("Publishing message to queue %v, for session %v", *routingKey, sessionID)) - - ap.producer.PublishMessage(*body, routingKey, headers) -} diff --git a/internal/drivers/keyvaluedriver/interface.go b/internal/drivers/keyvaluedriver/interface.go deleted file mode 100644 index 9c2c98c3d1e0606e253c9cc381e3ed034e37a06c..0000000000000000000000000000000000000000 --- a/internal/drivers/keyvaluedriver/interface.go +++ /dev/null @@ -1,7 +0,0 @@ -package keyvaluedriver - -// KeyValueStoreInterface is an interface for a key value storage -type KeyValueStoreInterface interface { - Get(key *string) string - Set(key *string, value *string) error -} diff --git a/internal/drivers/keyvaluedriver/keyvaluedriver.go b/internal/drivers/keyvaluedriver/keyvaluedriver.go deleted file mode 100644 index e817e32d0b51dd3a38275877b163966496453d55..0000000000000000000000000000000000000000 --- a/internal/drivers/keyvaluedriver/keyvaluedriver.go +++ /dev/null @@ -1,45 +0,0 @@ -package keyvaluedriver - -import ( - "context" - "fmt" - "os" - "query-service/pkg/logger" - - "github.com/go-redis/redis/v8" -) - -// KeyValueStore models the redis driver -type KeyValueStore struct { - client *redis.Client -} - -// NewRedisDriver creates and returns a redis driver -func NewRedisDriver() *KeyValueStore { - return &KeyValueStore{} -} - -// Start starts the redis driver -func (d *KeyValueStore) Start() { - // Grab the redis host and port from environment vars - redisAddress := os.Getenv("REDIS_ADDRESS") - - // Create redis client - d.client = redis.NewClient(&redis.Options{ - Addr: redisAddress, - }) - - pong := d.client.Ping(context.Background()) - logger.Log(fmt.Sprintf("%v", pong)) -} - -// Get retrieves the value from the redis store that belongs to the given key -func (d *KeyValueStore) Get(key *string) string { - return d.client.Get(context.Background(), *key).Val() -} - -// Set sets the key value pair in the redis store -func (d *KeyValueStore) Set(key *string, value *string) error { - status := d.client.Set(context.Background(), *key, *value, 0) - return status.Err() -} diff --git a/internal/drivers/keyvaluedriver/mock/mockkeyvaluedriver.go b/internal/drivers/keyvaluedriver/mock/mockkeyvaluedriver.go deleted file mode 100644 index 981b7cdacb6d455d966913406dbe9569fed896df..0000000000000000000000000000000000000000 --- a/internal/drivers/keyvaluedriver/mock/mockkeyvaluedriver.go +++ /dev/null @@ -1,24 +0,0 @@ -package mockkeyvaluedriver - -// A KeyValueStore implements methods to set key-value data (mock) -type KeyValueStore struct { - data map[string]string -} - -// CreateKeyValueStore creates a key value store driver (mock) -func CreateKeyValueStore() *KeyValueStore { - return &KeyValueStore{ - data: make(map[string]string), - } -} - -// Set sets a key to a value in the key value store. Expects a non-pointer as value. (mock) -func (kvs *KeyValueStore) Set(key *string, value *string) error { - kvs.data[*key] = *value - return nil -} - -// Get gets the value for the supplied key from the key value store (mock) -func (kvs *KeyValueStore) Get(key *string) string { - return kvs.data[*key] -} diff --git a/internal/entity/querystruct.go b/internal/entity/querystruct.go deleted file mode 100644 index 14ecebe354f5bca3e54998917e697f9419ca9cf2..0000000000000000000000000000000000000000 --- a/internal/entity/querystruct.go +++ /dev/null @@ -1,60 +0,0 @@ -package entity - -// QueryParsedJSON is used for JSON conversion of the incoming byte array -type QueryParsedJSON struct { - DatabaseName string - Return QueryReturnStruct - Entities []QueryEntityStruct - Relations []QueryRelationStruct - // Limit is for limiting the amount of paths AQL will return in a relation let statement - Limit int - Modifiers []QueryModifierStruct -} - -// QueryReturnStruct holds the indices of the entities and relations that need to be returned -type QueryReturnStruct struct { - Entities []int - Relations []int - //Modifiers []int -} - -// QueryEntityStruct encapsulates a single entity with its corresponding constraints -type QueryEntityStruct struct { - Type string - Constraints []QueryConstraintStruct -} - -// QueryRelationStruct encapsulates a single relation with its corresponding constraints -type QueryRelationStruct struct { - Type string - EntityFrom int - EntityTo int - Depth QuerySearchDepthStruct - Constraints []QueryConstraintStruct -} - -// QueryModifierStruct encapsulates a single modifier with its corresponding constraints -type QueryModifierStruct struct { - Type string // SUM COUNT AVG - SelectedType string // node relation - ID int // ID of the enitity or relation - AttributeIndex int // = -1 if its the node or relation, = > -1 if an attribute is selected -} - -// QuerySearchDepthStruct holds the range of traversals for the relation -type QuerySearchDepthStruct struct { - Min int - Max int -} - -// QueryConstraintStruct holds the information of the constraint -// Constraint datatypes -// text MatchTypes: exact/contains/startswith/endswith -// number MatchTypes: GT/LT/EQ -// bool MatchTypes: EQ/NEQ -type QueryConstraintStruct struct { - Attribute string - Value string - DataType string - MatchType string -} diff --git a/internal/usecases/consume/consume.go b/internal/usecases/consume/consume.go index fec726bbf906b0da49e718b1dfccfe99846b7187..dc1974e2f0636f6b182c9cbeae366e0425307ec0 100644 --- a/internal/usecases/consume/consume.go +++ b/internal/usecases/consume/consume.go @@ -1,29 +1,30 @@ package consume import ( - "query-service/internal/drivers/brokerdriver" - "query-service/internal/usecases/convertquery" "query-service/internal/usecases/databaseinfo" "query-service/internal/usecases/produce" "query-service/internal/usecases/request" + + "git.science.uu.nl/datastrophe/broker" + "git.science.uu.nl/datastrophe/query-conversion" ) // Service wraps consumer methods // broker is Alice broker created in brockerdriver driver type Service struct { - broker brokerdriver.Broker + brokerDriver broker.Interface producer produce.UseCase - queryConverter convertquery.UseCase + queryConverter query.Converter requestSender request.UseCase databaseInfoService databaseinfo.UseCase } // NewService creates a new service -func NewService(broker brokerdriver.Broker, produceService produce.UseCase, convertQueryService convertquery.UseCase, requestSenderService request.UseCase, databaseInfoService databaseinfo.UseCase) *Service { +func NewService(broker broker.Interface, produceService produce.UseCase, queryConverter query.Converter, requestSenderService request.UseCase, databaseInfoService databaseinfo.UseCase) *Service { return &Service{ - broker: broker, + brokerDriver: broker, producer: produceService, - queryConverter: convertQueryService, + queryConverter: queryConverter, requestSender: requestSenderService, databaseInfoService: databaseInfoService, } @@ -33,7 +34,7 @@ func NewService(broker brokerdriver.Broker, produceService produce.UseCase, conv func (s *Service) Start() { // Create consumer - consumer := s.broker.CreateConsumer() + consumer := s.brokerDriver.CreateConsumer("requests-exchange", "aql-query-queue", "aql-query-request") consumer.SetMessageHandler(s.HandleMessage) diff --git a/internal/usecases/consume/consume_test.go b/internal/usecases/consume/consume_test.go index 8295bb0548943dc5399ae280b4c33c356db0acfe..0e750c82a906da96e5102adc50f76157998abe6c 100644 --- a/internal/usecases/consume/consume_test.go +++ b/internal/usecases/consume/consume_test.go @@ -1,31 +1,30 @@ package consume import ( + "context" "encoding/json" - "query-service/internal/adapters/brokeradapter" - mockbrokerdriver "query-service/internal/drivers/brokerdriver/mock" - mockkeyvaluedriver "query-service/internal/drivers/keyvaluedriver/mock" - mockconvertquery "query-service/internal/usecases/convertquery/mock" mockdatabaseinfo "query-service/internal/usecases/databaseinfo/mock" "query-service/internal/usecases/produce" mockrequest "query-service/internal/usecases/request/mock" "testing" + "git.science.uu.nl/datastrophe/broker" + "git.science.uu.nl/datastrophe/keyvaluestore" + mockconvertquery "git.science.uu.nl/datastrophe/query-conversion/aql" + "github.com/stretchr/testify/assert" ) func TestHandleCorrectMessage(t *testing.T) { - // Create broker adapter - brokerAdapter := brokeradapter.CreateGateway() // Create a mock broker - mockBroker := mockbrokerdriver.CreateBroker(brokerAdapter) + mockBroker := broker.NewMockDriver().(*broker.MockDriver) // Create mock key value store - keyValueStore := mockkeyvaluedriver.CreateKeyValueStore() + keyValueStore := keyvaluestore.NewMockDriver() // Create new producer service producerService := produce.NewService(mockBroker, keyValueStore) producerService.Start() // Create new convert query service - convertQueryService := mockconvertquery.NewService() + convertQueryService := mockconvertquery.NewMockService() // Create new request sender service requestSenderService := mockrequest.NewService() // Create mock databaseinfo service @@ -39,13 +38,13 @@ func TestHandleCorrectMessage(t *testing.T) { mockQueue := "mock-queue" // Set the test-session sessionID queue to mock-queue in key value store - keyValueStore.Set(&mockSession, &mockQueue) + keyValueStore.Set(context.Background(), mockSession, mockQueue) // Create headers containing a sessionID headers := make(map[string]interface{}) headers["sessionID"] = mockSession headers["clientID"] = mockClient - mockMessage := brokeradapter.Message{ + mockMessage := broker.Message{ Headers: headers, Body: []byte("test message"), } @@ -74,16 +73,14 @@ func TestHandleCorrectMessage(t *testing.T) { // Unit test message received with no session ID func TestHandleMessageNoSessionID(t *testing.T) { - // Create broker adapter - brokerAdapter := brokeradapter.CreateGateway() // Create a mock broker - mockBroker := mockbrokerdriver.CreateBroker(brokerAdapter) + mockBroker := broker.NewMockDriver().(*broker.MockDriver) // Create mock key value store - keyValueStore := mockkeyvaluedriver.CreateKeyValueStore() + keyValueStore := keyvaluestore.NewMockDriver() // Create new producer service producerService := produce.NewService(mockBroker, keyValueStore) // Create new convert query service - convertQueryService := mockconvertquery.NewService() + convertQueryService := mockconvertquery.NewMockService() // Create new request sender service requestSenderService := mockrequest.NewService() // Create mock databaseinfo service @@ -93,7 +90,7 @@ func TestHandleMessageNoSessionID(t *testing.T) { // Create headers containing a sessionID headers := make(map[string]interface{}) - mockMessage := brokeradapter.Message{ + mockMessage := broker.Message{ Headers: headers, Body: []byte("test message"), } @@ -110,17 +107,15 @@ func TestHandleMessageNoSessionID(t *testing.T) { // Unit test receival of message and not being able to parse it func TestFailToConvertQuery(t *testing.T) { - // Create broker adapter - brokerAdapter := brokeradapter.CreateGateway() // Create a mock broker - mockBroker := mockbrokerdriver.CreateBroker(brokerAdapter) + mockBroker := broker.NewMockDriver().(*broker.MockDriver) // Create mock key value store - keyValueStore := mockkeyvaluedriver.CreateKeyValueStore() + keyValueStore := keyvaluestore.NewMockDriver() // Create new producer service producerService := produce.NewService(mockBroker, keyValueStore) producerService.Start() // Create new convert query service - convertQueryService := mockconvertquery.NewService() + convertQueryService := mockconvertquery.NewMockService() // Create new request sender service requestSenderService := mockrequest.NewService() // Create mock databaseinfo service @@ -134,13 +129,13 @@ func TestFailToConvertQuery(t *testing.T) { mockQueue := "mock-queue" // Set the test-session sessionID queue to mock-queue in key value store - keyValueStore.Set(&mockSession, &mockQueue) + keyValueStore.Set(context.Background(), mockSession, mockQueue) // Create headers containing a sessionID headers := make(map[string]interface{}) headers["sessionID"] = mockSession headers["clientID"] = mockClient - mockMessage := brokeradapter.Message{ + mockMessage := broker.Message{ Headers: headers, Body: []byte("test message"), } @@ -162,17 +157,15 @@ func TestFailToConvertQuery(t *testing.T) { // Test AQL querying error handling func TestArangoError(t *testing.T) { - // Create broker adapter - brokerAdapter := brokeradapter.CreateGateway() // Create a mock broker - mockBroker := mockbrokerdriver.CreateBroker(brokerAdapter) + mockBroker := broker.NewMockDriver().(*broker.MockDriver) // Create mock key value store - keyValueStore := mockkeyvaluedriver.CreateKeyValueStore() + keyValueStore := keyvaluestore.NewMockDriver() // Create new producer service producerService := produce.NewService(mockBroker, keyValueStore) producerService.Start() // Create new convert query service - convertQueryService := mockconvertquery.NewService() + convertQueryService := mockconvertquery.NewMockService() // Create new request sender service requestSenderService := mockrequest.NewService() // Create mock databaseinfo service @@ -186,13 +179,13 @@ func TestArangoError(t *testing.T) { mockQueue := "mock-queue" // Set the test-session sessionID queue to mock-queue in key value store - keyValueStore.Set(&mockSession, &mockQueue) + keyValueStore.Set(context.Background(), mockSession, mockQueue) // Create headers containing a sessionID headers := make(map[string]interface{}) headers["sessionID"] = mockSession headers["clientID"] = mockClient - mockMessage := brokeradapter.Message{ + mockMessage := broker.Message{ Headers: headers, Body: []byte("test message"), } diff --git a/internal/usecases/consume/handlemessage.go b/internal/usecases/consume/handlemessage.go index e8160076cdc08f7d8962808252d3a8dcd4625f58..63514093c00da5d2305b06763a498bb3a4481f85 100644 --- a/internal/usecases/consume/handlemessage.go +++ b/internal/usecases/consume/handlemessage.go @@ -2,13 +2,14 @@ package consume import ( "encoding/json" - "query-service/internal/adapters/brokeradapter" "query-service/pkg/errorhandler" "strings" + + "git.science.uu.nl/datastrophe/broker" ) // HandleMessage gets called when a message is received -func (s *Service) HandleMessage(msg *brokeradapter.Message) { +func (s *Service) HandleMessage(msg *broker.Message) { // Grab sessionID and clientID from the headers sessionID, ok := msg.Headers["sessionID"].(string) if !ok { diff --git a/internal/usecases/convertquery/aql.go b/internal/usecases/convertquery/aql.go deleted file mode 100644 index 1e804e9c25f236066dc3e01de563328a27716f25..0000000000000000000000000000000000000000 --- a/internal/usecases/convertquery/aql.go +++ /dev/null @@ -1,321 +0,0 @@ -package convertquery - -import ( - "encoding/json" - "errors" - "fmt" - "query-service/internal/entity" -) - -/* -ConvertQuery converts a json string to an AQL query - -Parameters: jsonMsg is the JSON file directly outputted by the drag and drop query builder in the frontend - -Return: a string containing the corresponding AQL query, a string containing the database name and an error */ -func (s *Service) ConvertQuery(jsonMsg *[]byte) (*string, *string, error) { - - jsonStruct, err := convertJSONToStruct(jsonMsg) - if err != nil { - return nil, nil, err - } - - // Check to make sure all indexes exist - // How many entities are there - numEntities := len(jsonStruct.Entities) - 1 - // How many relations there are - numRelations := len(jsonStruct.Relations) - 1 - - // Make sure no entity should be returned that is outside the range of that list - for _, e := range jsonStruct.Return.Entities { - // If this entity references an entity that is outside the range - if e > numEntities || e < 0 { - return nil, nil, errors.New("non-existing entity referenced in return") - } - } - - // Make sure that no relation mentions a non-existing entity - for _, r := range jsonStruct.Relations { - if r.EntityFrom > numEntities || r.EntityTo > numEntities { - return nil, nil, errors.New("non-exisiting entity referenced in relation") - } - } - - // Make sure no non-existing relation is tried to be returned - for _, r := range jsonStruct.Return.Relations { - if r > numRelations || r < 0 { - return nil, nil, errors.New("non-existing relation referenced in return") - } - } - - result := createQuery(jsonStruct) - return result, &jsonStruct.DatabaseName, nil -} - -/* convertJSONtoStruct reads a JSON file and sorts the data into the appropriate structs -Parameters: jsonMsg is the JSON file directly outputted by the drag and drop query builder in the frontend - -Return: parsedJSON is a struct with the same structure and holding the same data as jsonMsg -*/ -func convertJSONToStruct(jsonMsg *[]byte) (*entity.QueryParsedJSON, error) { - jsonStruct := entity.QueryParsedJSON{} - err := json.Unmarshal(*jsonMsg, &jsonStruct) - - if err != nil { - return nil, err - } - - return &jsonStruct, nil -} - -/* createQuery generates a query based on the json file provided -Parameters: jsonQuery is a parsedJSON struct holding all the data needed to form a query - -Return: a string containing the corresponding AQL query and an error -*/ -func createQuery(jsonQuery *entity.QueryParsedJSON) *string { - // Note: Case #4, where there is an edge only query (without any entity), is not supported by frontend - - // If a modifier is used, disable the limit - if len(jsonQuery.Modifiers) > 0 { - jsonQuery.Limit = -1 - } - - var ( - relationsToReturn []string - nodesToReturn []string - nodeUnion string - relationUnion string - ) - - // Loop over all relations - ret := "" - - for i, relation := range jsonQuery.Relations { - - relationName := fmt.Sprintf("r%v", i) - - if relation.EntityFrom >= 0 { - // if there is a from-node - // create the let for this node - fromName := fmt.Sprintf("n%v", relation.EntityFrom) - - ret += *createNodeLet(&jsonQuery.Entities[relation.EntityFrom], &fromName) - - ret += *createRelationLetWithFromEntity(&relation, relationName, &jsonQuery.Entities, jsonQuery.Limit) - } else if relation.EntityTo >= 0 { - // if there is only a to-node - toName := fmt.Sprintf("n%v", relation.EntityTo) - - ret += *createNodeLet(&jsonQuery.Entities[relation.EntityTo], &toName) - - ret += *createRelationLetWithOnlyToEntity(&relation, relationName, &jsonQuery.Entities, jsonQuery.Limit) - // Add this relation to the list - } else { - fmt.Println("Relation-only queries are currently not supported") - continue - } - - // Add this relation to the list - relationsToReturn = append(relationsToReturn, relationName) - } - - // Add node let statements for nodes that are not yet returned - // Create a set from all the entity-from's and entity-to's, to check if they are returned - nodeSet := make(map[int]bool) - for _, relation := range jsonQuery.Relations { - nodeSet[relation.EntityFrom] = true - nodeSet[relation.EntityTo] = true - } - - // Check if the entities to return are already returned - for _, entityIndex := range jsonQuery.Return.Entities { - if !nodeSet[entityIndex] { - // If not, return this node - name := fmt.Sprintf("n%v", entityIndex) - ret += *createNodeLet(&jsonQuery.Entities[entityIndex], &name) - - // Add this node to the list - nodesToReturn = append(nodesToReturn, name) - } - } - - //If there are modifiers within the query, we run a different set of checks which focus on quantifiable aspects - if len(jsonQuery.Modifiers) > 0 { - modifier := jsonQuery.Modifiers[0] - // There is a distinction between (relations and entities) and (relations or entities) - if len(jsonQuery.Return.Relations) > 0 && len(jsonQuery.Return.Entities) > 0 { - - var pathDistinction string // .vertices or .edges - - // Select the correct addition to the return of r0[**] - if modifier.SelectedType == "entity" { - // ASSUMING THERE IS ONLY 1 RELATION - if jsonQuery.Relations[0].EntityFrom == modifier.ID { - pathDistinction = fmt.Sprintf(".vertices[%v]", jsonQuery.Relations[0].Depth.Min-1) - - } else { - pathDistinction = fmt.Sprintf(".vertices[%v]", jsonQuery.Relations[0].Depth.Max) - - } - } else { - pathDistinction = ".edges[**]" - } - - // Getting the attribute if there is one - if modifier.AttributeIndex != -1 { - if modifier.SelectedType == "entity" { - pathDistinction += fmt.Sprintf(".%v", jsonQuery.Entities[modifier.ID].Constraints[modifier.AttributeIndex].Attribute) - - } else { - pathDistinction += fmt.Sprintf(".%v", jsonQuery.Relations[modifier.ID].Constraints[modifier.AttributeIndex].Attribute) - - } - } - - // If count is used it has to be replaced with Length + unique else use the modifier type - if modifier.Type == "COUNT" { - ret += fmt.Sprintf("RETURN LENGTH (unique(r0[*]%v))", pathDistinction) - - } else { - ret += fmt.Sprintf("RETURN %v (r0[*]%v)", modifier.Type, pathDistinction) - - } - - } else { - // Check if the modifier is on an attribute - if modifier.AttributeIndex == -1 { - ret += fmt.Sprintf("RETURN LENGTH (n%v)", modifier.ID) - } else { - var attribute string - - // Selecting the right attribute from either the entity constraint or relation constraint - if modifier.SelectedType == "entity" { - attribute = jsonQuery.Entities[modifier.ID].Constraints[modifier.AttributeIndex].Attribute - - } else { - attribute = jsonQuery.Relations[modifier.ID].Constraints[modifier.AttributeIndex].Attribute - - } - - // If count is used it has to be replaced with Length + unique else use the modifier type - if modifier.Type == "COUNT" { - ret += fmt.Sprintf("RETURN LENGTH (unique(n%v[*].%v))", modifier.ID, attribute) - - } else { - ret += fmt.Sprintf("RETURN %v (n%v[*].%v)", modifier.Type, modifier.ID, attribute) - - } - } - } - - } else { - - // Create UNION statements that create unique lists of all the nodes and relations - // Thus removing all duplicates - nodeUnion = "\nLET nodes = first(RETURN UNION_DISTINCT(" - for _, relation := range relationsToReturn { - nodeUnion += fmt.Sprintf("flatten(%v[**].vertices), ", relation) - } - - for _, node := range nodesToReturn { - nodeUnion += fmt.Sprintf("%v,", node) - } - nodeUnion += "[],[]))\n" - - relationUnion = "LET edges = first(RETURN UNION_DISTINCT(" - for _, relation := range relationsToReturn { - relationUnion += fmt.Sprintf("flatten(%v[**].edges), ", relation) - } - relationUnion += "[],[]))\n" - - ret += nodeUnion + relationUnion - ret += "RETURN {\"vertices\":nodes, \"edges\":edges }" - - } - - return &ret -} - -/* createNodeLet generates a 'LET' statement for a node related query -Parameters: node is an entityStruct containing the information of a single node, -name is the autogenerated name of the node consisting of "n" + the index of the node - -Return: a string containing a single LET-statement in AQL -*/ -func createNodeLet(node *entity.QueryEntityStruct, name *string) *string { - header := fmt.Sprintf("LET %v = (\n\tFOR x IN %v \n", *name, node.Type) - footer := "\tRETURN x\n)\n" - constraints := *createConstraintStatements(&node.Constraints, "x", false) - - ret := header + constraints + footer - return &ret -} - -/* createRelationLetWithFromEntity generates a 'LET' statement for relations with an 'EntityFrom' property and optionally an 'EntitiyTo' property -Parameters: relation is a relation struct containing the information of a single relation, -name is the autogenerated name of the node consisting of "r" + the index of the relation, -entities is a list of entityStructs that are needed to form the relation LET-statement - -Return: a string containing a single LET-statement in AQL -*/ -func createRelationLetWithFromEntity(relation *entity.QueryRelationStruct, name string, entities *[]entity.QueryEntityStruct, limit int) *string { - header := fmt.Sprintf("LET %v = (\n\tFOR x IN n%v \n", name, relation.EntityFrom) - forStatement := fmt.Sprintf("\tFOR v, e, p IN %v..%v OUTBOUND x %s \n", relation.Depth.Min, relation.Depth.Max, relation.Type) - - // Guarantees that there is no path returned with a duplicate edge - // This way there are no cycle paths possible, TODO: more research about this needed - optionStmtn := "\tOPTIONS { uniqueEdges: \"path\" }\n" - - vFilterStmnt := "" - if relation.EntityTo != -1 { - // If there is a to-node, generate the filter statement - toConstraints := (*entities)[relation.EntityTo].Constraints - vFilterStmnt += *createConstraintStatements(&toConstraints, "v", false) - - // Add a WITH statement if the collection of entityTo is not yet included - if (*entities)[(*relation).EntityFrom].Type != (*entities)[(*relation).EntityTo].Type { - header = fmt.Sprintf("WITH %v\n %v", (*entities)[(*relation).EntityTo].Type, header) - } - } - - relationFilterStmnt := *createConstraintStatements(&relation.Constraints, "p", true) - - // Dont use a limit on quantifing queries - footer := "" - if limit != -1 { - footer += fmt.Sprintf("\tLIMIT %v \n", limit) - } - footer += "RETURN DISTINCT p )\n" - - ret := header + forStatement + optionStmtn + vFilterStmnt + relationFilterStmnt + footer - return &ret -} - -/* createRelationLetWithOnlyToEntity generates a 'LET' statement for relations with only an 'EntityTo' property -Parameters: relation is a relation struct containing the information of a single relation, -name is the autogenerated name of the node consisting of "r" + the index of the relation, -entities is a list of entityStructs that are needed to form the relation LET-statement - -Return: a string containing a single LET-statement in AQL -*/ -func createRelationLetWithOnlyToEntity(relation *entity.QueryRelationStruct, name string, entities *[]entity.QueryEntityStruct, limit int) *string { - header := fmt.Sprintf("LET %v = (\n\tFOR x IN n%v \n", name, relation.EntityTo) - forStatement := fmt.Sprintf("\tFOR v, e, p IN %v..%v INBOUND x %s \n", relation.Depth.Min, relation.Depth.Max, relation.Type) - - // Guarantees that there is no path returned with a duplicate edge - // This way there are no cycle paths possible, TODO: more research about this needed - optionStmtn := "\tOPTIONS { uniqueEdges: \"path\" }\n" - - relationFilterStmnt := *createConstraintStatements(&relation.Constraints, "p", true) - - // Dont use a limit on quantifing queries - footer := "" - if limit != -1 { - footer += fmt.Sprintf("\tLIMIT %v \n", limit) - } - footer += "RETURN DISTINCT p )\n" - - ret := header + forStatement + optionStmtn + relationFilterStmnt + footer - return &ret -} diff --git a/internal/usecases/convertquery/aqlStructs.go b/internal/usecases/convertquery/aqlStructs.go deleted file mode 100644 index ad1be5eeb27d258675af096fa1596c9711470cf5..0000000000000000000000000000000000000000 --- a/internal/usecases/convertquery/aqlStructs.go +++ /dev/null @@ -1,10 +0,0 @@ -package convertquery - -// Service is a model for the convertquery use case -type Service struct { -} - -// NewService creates a new convertquery service -func NewService() *Service { - return &Service{} -} diff --git a/internal/usecases/convertquery/aql_test.go b/internal/usecases/convertquery/aql_test.go deleted file mode 100644 index a8515ebdb2ef8810c8548b0bedd0a7a324e5c12e..0000000000000000000000000000000000000000 --- a/internal/usecases/convertquery/aql_test.go +++ /dev/null @@ -1,833 +0,0 @@ -package convertquery - -import ( - "errors" - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestEmptyQueryConversion(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [], - "relations": [] - }, - "entities": [], - "relations": [], - "limit": 5000 - }`) - - convertedResult, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.NoError(t, err) - - // 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 }` - assert.Equal(t, correctConvertedResult, *convertedResult) -} - -func TestEntityOneAttributeQuery(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [], - "limit": 5000 - }`) - - convertedResult, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET nodes = first(RETURN UNION_DISTINCT(n0,[],[]))LET edges = first(RETURN UNION_DISTINCT([],[]))RETURN {"vertices":nodes, "edges":edges }` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -func TestRelationWithConstraint(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": 0, - "entityTo": -1, - "constraints": [ - { - "attribute": "Day", - "value": "15", - "dataType": "number", - "matchType": "EQ" - } - ] - } - ], - "limit": 5000 - }`) - - convertedResult, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -func TestModifierCountEntity(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [], - "limit": 5000, - "modifiers": [ - { - "type": "COUNT", - "selectedType": "entity", - "id": 0, - "attributeIndex": -1 - } - ] - }`) - - convertedResult, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)RETURN LENGTH (n0)` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} -func TestModifierCountEntityAttribute(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [], - "limit": 5000, - "modifiers": [ - { - "type": "SUM", - "selectedType": "entity", - "id": 0, - "attributeIndex": 0 - } - ] - }`) - - convertedResult, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)RETURN SUM (n0[*].state)` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} -func TestModifierCountRelation(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": 0, - "entityTo": -1, - "constraints": [ - { - "attribute": "Day", - "value": "15", - "dataType": "number", - "matchType": "EQ" - } - ] - } - ], - "limit": 5000, - "modifiers": [ - { - "type": "COUNT", - "selectedType": "relation", - "id": 0, - "attributeIndex": -1 - } - ] - }`) - - convertedResult, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 15 RETURN DISTINCT p )RETURN LENGTH (unique(r0[*].edges[**]))` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} -func TestModifierCountRelationAttribute(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": 0, - "entityTo": -1, - "constraints": [ - { - "attribute": "Day", - "value": "15", - "dataType": "number", - "matchType": "EQ" - } - ] - } - ], - "limit": 5000, - "modifiers": [ - { - "type": "AVG", - "selectedType": "relation", - "id": 0, - "attributeIndex": 0 - } - ] - }`) - - convertedResult, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 15 RETURN DISTINCT p )RETURN AVG (r0[*].edges[**].Day)` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -func TestRelationWithInOutConstraint(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0, - 1 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "text", - "matchType": "exact" - } - ] - }, - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 3 - }, - "entityFrom": 1, - "entityTo": 0, - "constraints": [ - { - "attribute": "Day", - "value": "15", - "dataType": "number", - "matchType": "EQ" - } - ] - } - ], - "limit": 5000 - }`) - - convertedResult, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n1 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n1 FOR v, e, p IN 1..3 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER v.city == "San Francisco" FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -func TestTwoRelations(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0, - 1, - 2 - ], - "relations": [ - 0, - 1 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "New York", - "dataType": "text", - "matchType": "exact" - } - ] - }, - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "text", - "matchType": "exact" - } - ] - }, - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 3 - }, - "entityFrom": 2, - "entityTo": 1, - "constraints": [ - { - "attribute": "Day", - "value": "15", - "dataType": "number", - "matchType": "EQ" - } - ] - }, - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": 0, - "entityTo": -1, - "constraints": [] - } - ], - "limit": 5000 - }`) - - convertedResult, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n2 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n2 FOR v, e, p IN 1..3 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER v.city == "San Francisco" FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET n0 = (FOR x IN airports FILTER x.city == "New York" RETURN x)LET r1 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), flatten(r1[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), flatten(r1[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -func TestRelationWithOnlyToNode(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": -1, - "entityTo": 0, - "constraints": [] - } - ], - "limit": 5000 - }`) - - convertedResult, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.city == "San Francisco" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 INBOUND x flights OPTIONS { uniqueEdges: "path" }LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -func TestTooManyReturnEntities(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0, - 1, - 2 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": -1, - "entityTo": 0, - "constraints": [] - } - ], - "limit": 5000 - }`) - - _, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.Equal(t, errors.New("non-existing entity referenced in return"), err) -} - -func TestTooManyReturnRelations(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [ - 0, - 1, - 2 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": -1, - "entityTo": 0, - "constraints": [] - } - ], - "limit": 5000 - }`) - - _, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.Equal(t, errors.New("non-existing relation referenced in return"), err) -} - -func TestNegativeReturnEntities(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0, - -1 - ], - "relations": [ - 0, - 1, - 2 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": -1, - "entityTo": 0, - "constraints": [] - } - ], - "limit": 5000 - }`) - - _, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.Equal(t, errors.New("non-existing entity referenced in return"), err) -} - -func TestNoRelationsField(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "limit": 5000 - }`) - - convertedResult, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.city == "San Francisco" RETURN x)LET nodes = first(RETURN UNION_DISTINCT(n0,[],[]))LET edges = first(RETURN UNION_DISTINCT([],[]))RETURN {"vertices":nodes, "edges":edges }` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -func TestEntityFromLowerThanNegativeOneInRelation(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": -4, - "entityTo": 0, - "constraints": [] - } - ], - "limit": 5000 - }`) - - _, _, err := service.ConvertQuery(&query) - - // Assert that there is no error - assert.NoError(t, err) -} diff --git a/internal/usecases/convertquery/benchmark.txt b/internal/usecases/convertquery/benchmark.txt deleted file mode 100644 index 1335e5b251b86d891d657eef6585fcb22ade7c3a..0000000000000000000000000000000000000000 --- a/internal/usecases/convertquery/benchmark.txt +++ /dev/null @@ -1,32 +0,0 @@ -time = 2.9 sec - -LET n0 = ( - FOR x IN airports - FILTER x.city == "New York" - RETURN x -) - -LET n1 = ( - FOR x IN airports - FILTER x.state == "OH" AND - x.vip == false - RETURN x -) -FOR a1 IN n0 - FOR node, edges, path IN 1..2 OUTBOUND a1 flights - FILTER node IN n1 - FILTER edges.DayOfWeek == 6 - LIMIT 100 - RETURN { node, edges, path } - - - -time = 1.3 sec - -FOR x IN airports - FILTER x.city == "New York" - FOR node, edges, path IN 1..2 OUTBOUND x flights - FILTER node.state == "OH" AND node.vip == false - FILTER edges.DayOfWeek == 6 - LIMIT 100 - RETURN { node, edges, path } \ No newline at end of file diff --git a/internal/usecases/convertquery/benchmark_test.go b/internal/usecases/convertquery/benchmark_test.go deleted file mode 100644 index 86da73b02be46cef4d15b4f7ec7bd5d75ac74de0..0000000000000000000000000000000000000000 --- a/internal/usecases/convertquery/benchmark_test.go +++ /dev/null @@ -1,152 +0,0 @@ -package convertquery - -import "testing" - -func BenchmarkConvertEmptyQuery(b *testing.B) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [], - "relations": [] - }, - "entities": [], - "relations": [], - "limit": 5000 - }`) - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - service.ConvertQuery(&query) - } -} - -func BenchmarkConvertOneAttributeQuery(b *testing.B) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [], - "limit": 5000 - }`) - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - service.ConvertQuery(&query) - } -} - -func BenchmarkConvertTwoRelationQuery(b *testing.B) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0, - 1, - 2 - ], - "relations": [ - 0, - 1 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "New York", - "dataType": "text", - "matchType": "exact" - } - ] - }, - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "text", - "matchType": "exact" - } - ] - }, - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "text", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 3 - }, - "entityFrom": 2, - "entityTo": 1, - "constraints": [ - { - "attribute": "Day", - "value": "15", - "dataType": "number", - "matchType": "EQ" - } - ] - }, - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": 0, - "entityTo": -1, - "constraints": [] - } - ], - "limit": 5000 - }`) - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - service.ConvertQuery(&query) - } -} diff --git a/internal/usecases/convertquery/createConstraints.go b/internal/usecases/convertquery/createConstraints.go deleted file mode 100644 index 0f2a880c3082063d5784c7dc71469b04eaa130c9..0000000000000000000000000000000000000000 --- a/internal/usecases/convertquery/createConstraints.go +++ /dev/null @@ -1,99 +0,0 @@ -package convertquery - -import ( - "fmt" - "query-service/internal/entity" -) - -/* createConstraintStatements generates the appropriate amount of constraint lines calling createConstraingBoolExpression -Parameters: constraints is a list of constraintStructs that specify the constraints of a node or relation, -name is the id of the corresponding relation/node, -isRelation is a boolean specifying if this constraint comes from a node or relation - -Return: a string containing a FILTER-statement with all the constraints -*/ -func createConstraintStatements(constraints *[]entity.QueryConstraintStruct, name string, isRelation bool) *string { - s := "" - if len(*constraints) == 0 { - return &s - } - - newLineStatement := "\tFILTER" - - for _, v := range *constraints { - s += fmt.Sprintf("%v %v \n", newLineStatement, *createConstraintBoolExpression(&v, name, isRelation)) - newLineStatement = "\tAND" - } - - return &s -} - -/* createConstraintBoolExpression generates a single boolean expression, -e.g. {name}.city == "New York". - -Parameters: constraint is a single constraint of a node or relation, -name is the id of the corresponding relation/node, -isRelation is a boolean specifying if this constraint comes from a node or relation, that changes the structure of the expression - -Return: a string containing an boolean expression of a single constraint -*/ -func createConstraintBoolExpression(constraint *entity.QueryConstraintStruct, name string, isRelation bool) *string { - var ( - match string - value string - line string - ) - - // Constraint datatypes back end - // text MatchTypes: EQ/NEQ/contains/excludes - // number MatchTypes: EQ/NEQ/GT/LT/GET/LET - // bool MatchTypes: EQ/NEQ - - switch constraint.DataType { - case "text": - value = fmt.Sprintf("\"%s\"", constraint.Value) - switch constraint.MatchType { - case "NEQ": - match = "!=" - case "contains": - match = "LIKE" - value = fmt.Sprintf("\"%%%s%%\"", constraint.Value) - case "excludes": - match = "NOT LIKE" - value = fmt.Sprintf("\"%%%s%%\"", constraint.Value) - default: //EQ - match = "==" - } - case "number": - value = constraint.Value - switch constraint.MatchType { - case "NEQ": - match = "!=" - case "GT": - match = ">" - case "LT": - match = "<" - case "GET": - match = ">=" - case "LET": - match = "<=" - default: //EQ - match = "==" - } - default: /*bool*/ - value = constraint.Value - switch constraint.MatchType { - case "NEQ": - match = "!=" - default: //EQ - match = "==" - } - } - - if isRelation { - line = fmt.Sprintf("%s.edges[*].%s ALL %s %s", name, constraint.Attribute, match, value) - } else { - line = fmt.Sprintf("%s.%s %s %s", name, constraint.Attribute, match, value) - } - return &line -} diff --git a/internal/usecases/convertquery/interface.go b/internal/usecases/convertquery/interface.go deleted file mode 100644 index 969693009722758beacdb6fd13e10e888ceb6310..0000000000000000000000000000000000000000 --- a/internal/usecases/convertquery/interface.go +++ /dev/null @@ -1,7 +0,0 @@ -package convertquery - -// UseCase is an interface describing a function for converting a visual query -// to a query of the database language -type UseCase interface { - ConvertQuery(jsonMsg *[]byte) (*string, *string, error) -} diff --git a/internal/usecases/convertquery/mock/mockconvertquery.go b/internal/usecases/convertquery/mock/mockconvertquery.go deleted file mode 100644 index 832ea4288c456842602795bbeb2ad195adf9725e..0000000000000000000000000000000000000000 --- a/internal/usecases/convertquery/mock/mockconvertquery.go +++ /dev/null @@ -1,31 +0,0 @@ -package mockconvertquery - -import "errors" - -// A Service implements the query convert usecase interface (mock) -type Service struct { - throwError bool -} - -// NewService creates a new query convert service (mock) -func NewService() *Service { - return &Service{ - throwError: false, - } -} - -// ConvertQuery returns a hard coded string message (mock) -func (s *Service) ConvertQuery(jsonMsg *[]byte) (*string, *string, error) { - mockQuery := "Query converted" - mockDatabaseName := "test-database" - - if !s.throwError { - return &mockQuery, &mockDatabaseName, nil - } - return nil, &mockDatabaseName, errors.New("Failed to convert query") -} - -// ToggleError decides whether the convert function throws an error -func (s *Service) ToggleError() { - s.throwError = !s.throwError -} diff --git a/internal/usecases/convertquery/queryexamples.txt b/internal/usecases/convertquery/queryexamples.txt deleted file mode 100644 index ac6b44d6540c00319b48d22e6d9d1dff4af240f8..0000000000000000000000000000000000000000 --- a/internal/usecases/convertquery/queryexamples.txt +++ /dev/null @@ -1,189 +0,0 @@ - -#1 -{ - "Return": { - "Entities": [ - 0, - 1 - ], - "Relations": [ - 0 - ] - }, - "Entities": [ - { - "Type": "airports", - "Constraints": [] - }, - { - "Type": "airports", - "Constraints": [] - } - ], - "Relations": [ - { - "Type": "flights", - "Depth": { - "min": 1, - "max": 1 - }, - "EntityFrom": 0, - "EntityTo": 1, - "Constraints": [] - } - ] -} - -#2 - -{ - "Return": { - "Entities": [ - 0 - ], - "Relations": [ - 0 - ] - }, - "Entities": [ - { - "Type": "airports", - "Constraints": [] - } - ], - "Relations": [ - { - "Type": "flights", - "Depth": { - "min": 1, - "max": 1 - }, - "EntityFrom": 0, - "EntityTo": -1, - "Constraints": [] - } - ] -} - -#3 -{ - "Return": { - "Entities": [ - 0 - ], - "Relations": [ - 0 - ] - }, - "Entities": [ - { - "Type": "airports", - "Constraints": [] - } - ], - "Relations": [ - { - "Type": "flights", - "Depth": { - "min": 1, - "max": 1 - }, - "EntityFrom": -1, - "EntityTo": 0, - "Constraints": [] - } - ] -} - -#4 -{ - "Return": { - "Entities": [], - "Relations": [ - 0 - ] - }, - "Entities": [], - "Relations": [ - { - "Type": "flights", - "Depth": { - "min": 1, - "max": 1 - }, - "EntityFrom": -1, - "EntityTo": -1, - "Constraints": [ - { - "Attribute": "Month", - "Value": "1", - "DataType": "number", - "MatchType": "EQ" - }, - { - "Attribute": "Day", - "Value": "15", - "DataType": "number", - "MatchType": "EQ" - } - ] - } - ] -} - -#5 -{ - "Return": { - "Entities": [ - 0 - ], - "Relations": [] - }, - "Entities": [ - { - "Type": "airports", - "Constraints": [ - { - "Attribute": "city", - "Value": "New York", - "DataType": "text", - "MatchType": "exact" - }, - { - "Attribute": "country", - "Value": "USA", - "DataType": "text", - "MatchType": "exact" - } - ] - } - ], - "Relations": [] -} - -LET n0 = (FOR x IN airports FILTER x.city == "New York" RETURN x) -LET nodes = first(RETURN UNION_DISTINCT(n0,[],[])) -LET edges = first(RETURN UNION_DISTINCT([],[])) -RETURN {"vertices":nodes, "edges":edges } - - - -LET n0 = (FOR x IN airports FILTER x.city == "New York" RETURN x) -RETURN LENGTH(n0) - - - - -LET n0 = (FOR x IN airports FILTER x.city == "New York" RETURN x) -LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 8 -LIMIT 5000 RETURN DISTINCT p ) -LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[])) -LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[])) -RETURN {"vertices":nodes, "edges":edges } - - -LET n0 = (FOR x IN airports FILTER x.city == "New York" RETURN x) -LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 8 -RETURN DISTINCT p ) -RETURN COUNT(UNIQUE(r0[**].vertices[0])) - diff --git a/internal/usecases/produce/produce.go b/internal/usecases/produce/produce.go index 85bff2a1c15774a55a05982025aaeb4bc907f508..f9bf6989386144d2abe4839497f9602fbeb0fac0 100644 --- a/internal/usecases/produce/produce.go +++ b/internal/usecases/produce/produce.go @@ -1,19 +1,19 @@ package produce import ( - "query-service/internal/drivers/brokerdriver" - "query-service/internal/drivers/keyvaluedriver" + "git.science.uu.nl/datastrophe/broker" + "git.science.uu.nl/datastrophe/keyvaluestore" ) // Service wraps consumer methods type Service struct { - brokerDriver brokerdriver.Broker - keyValueStore keyvaluedriver.KeyValueStoreInterface - producerDriver brokerdriver.Producer + brokerDriver broker.Interface + keyValueStore keyvaluestore.Interface + producerDriver broker.Producer } // NewService creates a new service -func NewService(broker brokerdriver.Broker, keyValueStore keyvaluedriver.KeyValueStoreInterface) *Service { +func NewService(broker broker.Interface, keyValueStore keyvaluestore.Interface) *Service { return &Service{ brokerDriver: broker, keyValueStore: keyValueStore, @@ -23,7 +23,7 @@ func NewService(broker brokerdriver.Broker, keyValueStore keyvaluedriver.KeyValu // Start starts the producer func (s *Service) Start() { // Create producer - p := s.brokerDriver.CreateProducer() + p := s.brokerDriver.CreateProducer("ui-direct-exchange") s.producerDriver = p } diff --git a/internal/usecases/produce/produce_test.go b/internal/usecases/produce/produce_test.go index ff3f5fefde093ed8b0ba1c39be4c28444ac3b1ae..ff51325b1339444fd94ff09d4d18efca89206dec 100644 --- a/internal/usecases/produce/produce_test.go +++ b/internal/usecases/produce/produce_test.go @@ -1,22 +1,20 @@ package produce import ( - "query-service/internal/adapters/brokeradapter" - mockbrokerdriver "query-service/internal/drivers/brokerdriver/mock" - mockkeyvaluedriver "query-service/internal/drivers/keyvaluedriver/mock" + "context" "testing" + "git.science.uu.nl/datastrophe/broker" + "git.science.uu.nl/datastrophe/keyvaluestore" "github.com/stretchr/testify/assert" ) // Make sure a correct message gets published func TestPublishCorrectMessage(t *testing.T) { - // Create broker adapter - brokerAdapter := brokeradapter.CreateGateway() // Create a mock broker - mockBroker := mockbrokerdriver.CreateBroker(brokerAdapter) + mockBroker := broker.NewMockDriver().(*broker.MockDriver) // Create mock key value store - keyValueStore := mockkeyvaluedriver.CreateKeyValueStore() + keyValueStore := keyvaluestore.NewMockDriver() // Create new service and start it service := NewService(mockBroker, keyValueStore) service.Start() @@ -26,7 +24,7 @@ func TestPublishCorrectMessage(t *testing.T) { mockQueue := "mock-queue" // Set the test-session sessionID queue to mock-queue in key value store - keyValueStore.Set(&mockSession, &mockQueue) + keyValueStore.Set(context.Background(), mockSession, mockQueue) // Create headers containing a sessionID headers := make(map[string]interface{}) @@ -48,12 +46,10 @@ func TestPublishCorrectMessage(t *testing.T) { // Test publishing message without setting routing in key value store func TestPublishMessageNoRouting(t *testing.T) { - // Create broker adapter - brokerAdapter := brokeradapter.CreateGateway() // Create a mock broker - mockBroker := mockbrokerdriver.CreateBroker(brokerAdapter) + mockBroker := broker.NewMockDriver().(*broker.MockDriver) // Create mock key value store - keyValueStore := mockkeyvaluedriver.CreateKeyValueStore() + keyValueStore := keyvaluestore.NewMockDriver() // Create new service and start it service := NewService(mockBroker, keyValueStore) service.Start() diff --git a/internal/usecases/produce/publishmessage.go b/internal/usecases/produce/publishmessage.go index dffd5a8ea4c267a245b21cdeb7a928a6642f3bca..567d45249824f3a66c508d90ef0d58867b78c937 100644 --- a/internal/usecases/produce/publishmessage.go +++ b/internal/usecases/produce/publishmessage.go @@ -1,16 +1,18 @@ package produce import ( + "context" "fmt" "query-service/pkg/logger" - - "github.com/streadway/amqp" ) // PublishMessage will publish the message to the queue retrieved from the key value store, with the given sessionID func (s *Service) PublishMessage(data *[]byte, sessionID *string) { // Use the sessionID to query the key value store to get the queue we need to send this message to - clientQueueID := s.keyValueStore.Get(sessionID) + clientQueueID, ok := s.keyValueStore.Get(context.Background(), *sessionID).(string) + if !ok { + return + } logger.Log(fmt.Sprintf("Found client queue %s for session %s", clientQueueID, *sessionID)) @@ -21,8 +23,8 @@ func (s *Service) PublishMessage(data *[]byte, sessionID *string) { return } - headers := amqp.Table{} + headers := make(map[string]interface{}) headers["sessionID"] = *sessionID headers["type"] = "queryResult" - s.producerDriver.PublishMessage(data, &clientQueueID, &headers) + s.producerDriver.PublishMessage(data, clientQueueID, &headers) }