From 6192ca3fcc151d31c475a0e2cabfc948a74c401c Mon Sep 17 00:00:00 2001 From: Jeroen Peters Date: Mon, 11 Mar 2024 19:49:52 +0100 Subject: [PATCH] feat: update FindLocations API handler --- cmd/app/main.go | 10 +++---- go.mod | 8 +++--- go.sum | 16 +++++++---- internal/api/handler-location.go | 31 +++++---------------- internal/api/middlewares.go | 12 ++++---- internal/api/pagination.go | 4 +-- internal/api/server.go | 8 +++--- internal/configs/logging.go | 18 ++++++++---- internal/store/mongo/client.go | 47 ++++++++++++++++++++------------ internal/store/mongo/config.go | 4 +-- internal/store/store.go | 3 +- 11 files changed, 83 insertions(+), 78 deletions(-) diff --git a/cmd/app/main.go b/cmd/app/main.go index 7c84aa0..3a8ce4d 100644 --- a/cmd/app/main.go +++ b/cmd/app/main.go @@ -10,19 +10,19 @@ import ( "github.com/pejeio/blood-donate-locator-api/internal/auth" "github.com/pejeio/blood-donate-locator-api/internal/configs" "github.com/pejeio/blood-donate-locator-api/internal/store/mongo" - log "github.com/sirupsen/logrus" + "github.com/rs/zerolog/log" ) func main() { // Set up logging configs.SetUpLogging() - log.Info("🛫 Starting the app") + log.Info().Msg("🛫 Starting the app") // Load config cfg, err := configs.LoadConfig() if err != nil { - log.Fatal("🧐 Could not load environment variables", err) + log.Fatal().Msgf("🧐 Could not load environment variables %v", err) } // Set up context with signal handling @@ -32,13 +32,13 @@ func main() { // Initialize database client dbClient, err := mongo.Init(ctx, &cfg) if err != nil { - log.Error("❌ Failed to connect to the database", err) + log.Error().Msgf("❌ Failed to connect to the database: %v", err) } // Initialize authentication client authClient := auth.NewClient(cfg.KCBaseURL, cfg.KCClientID, cfg.KCClientSecret, cfg.KCRealm) if err != nil { - log.Error("❌ Failed to set up authentication client", err) + log.Error().Msgf("❌ Failed to set up authentication client: %v", err) } // Create Fiber app diff --git a/go.mod b/go.mod index 3f7b119..c6199ed 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,9 @@ require ( github.com/Nerzal/gocloak/v13 v13.8.0 github.com/go-playground/validator/v10 v10.11.1 github.com/gofiber/fiber/v2 v2.40.0 - github.com/sirupsen/logrus v1.9.0 + github.com/rs/zerolog v1.31.0 github.com/spf13/viper v1.14.0 + github.com/valyala/fasthttp v1.41.0 go.mongodb.org/mongo-driver v1.11.1 golang.org/x/sync v0.1.0 ) @@ -25,7 +26,7 @@ require ( github.com/leodido/go-urn v1.2.1 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/montanaflynn/stats v0.6.6 // indirect @@ -41,7 +42,6 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.41.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect @@ -49,7 +49,7 @@ require ( github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect golang.org/x/crypto v0.6.0 // indirect golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect + golang.org/x/sys v0.12.0 // indirect golang.org/x/text v0.7.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index af2b408..abc9c20 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -76,6 +77,7 @@ github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJ github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofiber/fiber/v2 v2.40.0 h1:fdU7w5hT6PLL7jiWIhtQ+S/k5WEFYoUZidptlPu8GBo= github.com/gofiber/fiber/v2 v2.40.0/go.mod h1:Gko04sLksnHbzLSRBFWPFdzM9Ws9pRxvvIaohJK1dsk= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= @@ -166,8 +168,9 @@ github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamh github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -194,10 +197,11 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= @@ -398,13 +402,13 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/api/handler-location.go b/internal/api/handler-location.go index c4c86c4..b766de8 100644 --- a/internal/api/handler-location.go +++ b/internal/api/handler-location.go @@ -5,17 +5,17 @@ import ( "github.com/gofiber/fiber/v2" "github.com/pejeio/blood-donate-locator-api/internal/types" - log "github.com/sirupsen/logrus" + "github.com/rs/zerolog/log" "golang.org/x/sync/errgroup" ) func (s *Server) CreateLocation(c *fiber.Ctx) error { - log.Println("Creating location") + log.Info().Msg("Creating location") // Parse the request body body := new(types.CreateLocationRequest) if err := c.BodyParser(body); err != nil { - log.Errorln(err) + log.Error().Err(err) return c.Status(fiber.StatusUnprocessableEntity).JSON( JSONErrorResponse{Message: err.Error()}, ) @@ -43,12 +43,6 @@ func (s *Server) CreateLocation(c *fiber.Ctx) error { // FindLocations retrieves a list of locations based on the provided query parameters. func (s *Server) FindLocations(c *fiber.Ctx) error { - var ( - g errgroup.Group - locations []types.Location - locationsCount int64 - ) - pagQParams, err := GetPaginationQueryParams(c) if err != nil { return err @@ -61,19 +55,8 @@ func (s *Server) FindLocations(c *fiber.Ctx) error { Offset: pagQParams.Offset, } - g.Go(func() error { - locs, err := s.Store.GetLocations(s.Ctx, query) - locations = locs - return err - }) - - g.Go(func() error { - count, err := s.Store.CountLocations(s.Ctx, query) - locationsCount = count - return err - }) - - if err := g.Wait(); err != nil { + locations, totalCount, err := s.Store.GetLocations(s.Ctx, query) + if err != nil { return c.Status(fiber.StatusInternalServerError).JSON( JSONErrorResponse{Message: err.Error()}, ) @@ -83,7 +66,7 @@ func (s *Server) FindLocations(c *fiber.Ctx) error { Response: types.Response{ Data: locations, }, - Meta: types.ResponseMeta{Count: locationsCount}, + Meta: types.ResponseMeta{Count: totalCount}, }) } @@ -156,7 +139,7 @@ func (s *Server) FindLocation(c *fiber.Ctx) error { // DeleteLocation deletes a location. func (s *Server) DeleteLocation(c *fiber.Ctx) error { - log.Println("Deleting location") + log.Info().Msg("Deleting location") id := c.Params("id") delCount, err := s.Store.DeleteLocation(s.Ctx, id) if err != nil { diff --git a/internal/api/middlewares.go b/internal/api/middlewares.go index 0d41d23..7d0ab05 100644 --- a/internal/api/middlewares.go +++ b/internal/api/middlewares.go @@ -4,7 +4,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" "github.com/pejeio/blood-donate-locator-api/internal/auth" - log "github.com/sirupsen/logrus" + "github.com/rs/zerolog/log" ) func CorsHandler() fiber.Handler { @@ -46,25 +46,25 @@ func Protect(authClient *auth.Client) fiber.Handler { return func(c *fiber.Ctx) error { accessToken := GetBearerTokenFromHeaders(c) - log.Println("Retrospecting token...") + log.Debug().Msg(("Retrospecting token...")) rptResult, err := authClient.GC.RetrospectToken(c.Context(), accessToken, authClient.ClientID, authClient.ClientSecret, authClient.Realm) if err != nil { - log.Println("Token is invalid.") + log.Debug().Msg("Token is invalid.") return UnauthorizedJSONErrorResponse(c) } isTokenValid := *rptResult.Active if !isTokenValid { - log.Println("Token is invalid.") + log.Debug().Msg("Token is invalid.") return c.Status(fiber.StatusUnauthorized).JSON(JSONErrorResponse{ Message: "Unauthorized", }) } - log.Println("Decoding access token...") + log.Trace().Msg("Decoding access token...") _, claims, err := authClient.GC.DecodeAccessToken(c.Context(), accessToken, authClient.Realm) if err != nil { - log.Println("Token is invalid.") + log.Debug().Msg("Token is invalid.") return UnauthorizedJSONErrorResponse(c) } diff --git a/internal/api/pagination.go b/internal/api/pagination.go index d4ce3e6..9278f51 100644 --- a/internal/api/pagination.go +++ b/internal/api/pagination.go @@ -5,7 +5,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/pejeio/blood-donate-locator-api/internal/types" - log "github.com/sirupsen/logrus" + "github.com/rs/zerolog/log" ) type PageLimitOffset struct { @@ -23,7 +23,7 @@ func GetPaginationQueryParams(c *fiber.Ctx) (*PageLimitOffset, error) { // Parse query parameters into q if err := c.QueryParser(q); err != nil { - log.Println(err) + log.Error().Err(err) return nil, err } diff --git a/internal/api/server.go b/internal/api/server.go index 9fbbb13..3d6aa06 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -7,7 +7,7 @@ import ( "github.com/pejeio/blood-donate-locator-api/internal/auth" "github.com/pejeio/blood-donate-locator-api/internal/configs" "github.com/pejeio/blood-donate-locator-api/internal/store" - log "github.com/sirupsen/logrus" + "github.com/rs/zerolog/log" ) type Server struct { @@ -35,13 +35,13 @@ func (s *Server) Start() { // Create location indexes if err := s.Store.CreateLocationIndexes(s.Ctx); err != nil { - log.Fatal(err) + log.Fatal().Err(err) } // Start the server serverAddr := ":" + s.Config.ServerPort - log.Printf("👂 Listening and serving HTTP on %s\n", serverAddr) - log.Fatal(s.App.Listen(serverAddr)) + log.Info().Msgf("👂 Listening and serving HTTP on %s", serverAddr) + log.Fatal().Err(s.App.Listen(serverAddr)) } func (s *Server) Routes() { diff --git a/internal/configs/logging.go b/internal/configs/logging.go index 620a8c4..b3a7082 100644 --- a/internal/configs/logging.go +++ b/internal/configs/logging.go @@ -1,11 +1,17 @@ package configs -import "github.com/sirupsen/logrus" +import ( + "os" -// Set custom config options for logrus + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +// Set custom config options for zerolog func SetUpLogging() { - customFormatter := new(logrus.TextFormatter) - customFormatter.DisableLevelTruncation = true - customFormatter.DisableTimestamp = true - logrus.SetFormatter(customFormatter) + zerolog.SetGlobalLevel(zerolog.DebugLevel) + log.Logger = log.Output(zerolog.ConsoleWriter{ + Out: os.Stderr, + PartsExclude: []string{zerolog.TimestampFieldName}, + }) } diff --git a/internal/store/mongo/client.go b/internal/store/mongo/client.go index dfac241..a9a8d2a 100644 --- a/internal/store/mongo/client.go +++ b/internal/store/mongo/client.go @@ -9,6 +9,7 @@ import ( "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" + "golang.org/x/sync/errgroup" ) type Client struct { @@ -63,7 +64,9 @@ func (c *Client) ReverseGeoCodeLocations(ctx context.Context, query types.Lookup return locations, nil } -func (c *Client) GetLocations(ctx context.Context, query types.FindLocationsRequest) ([]types.Location, error) { +func (c *Client) GetLocations(ctx context.Context, query types.FindLocationsRequest) ([]types.Location, int64, error) { + var g errgroup.Group + opts := options.Find() opts.SetSort(bson.D{{Key: "created_at", Value: -1}}) opts.SetLimit(int64(query.Limit)) @@ -72,28 +75,38 @@ func (c *Client) GetLocations(ctx context.Context, query types.FindLocationsRequ filter := createFindLocationsFilter(query) locations := make([]types.Location, 0) + var locationCount int64 - cursor, err := c.LocationsCollection().Find(ctx, filter, opts) - if err != nil { - return nil, err - } - - for cursor.Next(ctx) { - var location types.Location - err := cursor.Decode(&location) + g.Go(func() error { + cursor, err := c.LocationsCollection().Find(ctx, filter, opts) if err != nil { - return nil, err + return err } - locations = append(locations, location) - } - return locations, nil -} + for cursor.Next(ctx) { + var location types.Location + if err := cursor.Decode(&location); err != nil { + return err + } + locations = append(locations, location) + } + return nil + }) -func (c *Client) CountLocations(ctx context.Context, query types.FindLocationsRequest) (int64, error) { - filter := createFindLocationsFilter(query) + g.Go(func() error { + count, err := c.LocationsCollection().CountDocuments(ctx, filter) + if err != nil { + return err + } + locationCount = count + return nil + }) + + if err := g.Wait(); err != nil { + return nil, 0, err + } - return c.LocationsCollection().CountDocuments(ctx, filter) + return locations, locationCount, nil } func (c *Client) GetLocationByID(ctx context.Context, id string) (types.Location, error) { diff --git a/internal/store/mongo/config.go b/internal/store/mongo/config.go index 30baf69..b812d11 100644 --- a/internal/store/mongo/config.go +++ b/internal/store/mongo/config.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - log "github.com/sirupsen/logrus" + "github.com/rs/zerolog/log" "github.com/pejeio/blood-donate-locator-api/internal/configs" "github.com/pejeio/blood-donate-locator-api/internal/store" @@ -25,7 +25,7 @@ func Init(ctx context.Context, c *configs.Config) (store.Store, error) { return nil, err } - log.Println("🚀 Connected successfully to the database") + log.Info().Msg("🚀 Connected successfully to the database") client := &Client{ Database: mClient.Database(c.DBName), } diff --git a/internal/store/store.go b/internal/store/store.go index 2738a5e..cb7b240 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -10,8 +10,7 @@ type Store interface { CreateLocationIndexes(ctx context.Context) error CreateLocation(ctx context.Context, loc types.CreateLocationRequest) (*types.Location, error) GetLocationByID(ctx context.Context, id string) (types.Location, error) - GetLocations(ctx context.Context, query types.FindLocationsRequest) ([]types.Location, error) - CountLocations(ctx context.Context, query types.FindLocationsRequest) (int64, error) + GetLocations(ctx context.Context, query types.FindLocationsRequest) ([]types.Location, int64, error) DeleteLocation(ctx context.Context, id string) (int, error) ReverseGeoCodeLocations(ctx context.Context, query types.LookupLocationRequest) ([]types.Location, error) }