Skip to content

Commit

Permalink
feature: implement basic implementation for service to store value
Browse files Browse the repository at this point in the history
  • Loading branch information
mhrynenko committed Dec 19, 2024
1 parent 646b099 commit a32882b
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 17 deletions.
4 changes: 3 additions & 1 deletion internal/cli/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cli

import (
"context"

"github.com/alecthomas/kingpin"
"github.com/rarimo/bio-data-svc/internal/config"
"github.com/rarimo/bio-data-svc/internal/service"
Expand Down Expand Up @@ -39,7 +41,7 @@ func Run(args []string) bool {

switch cmd {
case serviceCmd.FullCommand():
service.Run(cfg)
service.Run(context.Background(), cfg)
case migrateUpCmd.FullCommand():
err = MigrateUp(cfg)
case migrateDownCmd.FullCommand():
Expand Down
58 changes: 58 additions & 0 deletions internal/service/handlers/add_data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package handlers

import (
"encoding/base64"
"net/http"

"github.com/google/uuid"
"github.com/rarimo/bio-data-svc/internal/data"
"github.com/rarimo/bio-data-svc/internal/service/requests"
"github.com/rarimo/bio-data-svc/resources"
"gitlab.com/distributed_lab/ape"
"gitlab.com/distributed_lab/ape/problems"
)

func AddData(w http.ResponseWriter, r *http.Request) {
req, err := requests.AddData(r)
if err != nil {
Log(r).WithError(err).Error("invalid request")
ape.RenderErr(w, problems.BadRequest(err)...)
return
}

value, err := base64.StdEncoding.DecodeString(req.Data.Attributes.Value)
if err != nil {
Log(r).WithError(err).WithField("value", req.Data.Attributes.Value).Error("failed to decode base64 string")
ape.RenderErr(w, problems.InternalError())
return
}

kv := data.KV{
Key: uuid.NewString(),
Value: value,
}

if err = KVQ(r).Insert(kv); err != nil {
Log(r).WithError(err).WithField("kv", kv).Error("failed to insert new key-value")
ape.RenderErr(w, problems.InternalError())
return
}

w.WriteHeader(http.StatusCreated)
ape.Render(w, newKVResponse(kv))
}

func newKVResponse(kv data.KV) resources.ValueResponse {
return resources.ValueResponse{
Data: resources.Value{
Key: resources.Key{
ID: kv.Key,
Type: resources.VALUE,
},
Attributes: resources.ValueAttributes{
Value: base64.StdEncoding.EncodeToString(kv.Value),
Key: kv.Key,
},
},
}
}
12 changes: 12 additions & 0 deletions internal/service/handlers/ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import (
"context"
"net/http"

"github.com/rarimo/bio-data-svc/internal/data"
"gitlab.com/distributed_lab/logan/v3"
)

type ctxKey int

const (
logCtxKey ctxKey = iota
kvCtxKey ctxKey = iota
)

func CtxLog(entry *logan.Entry) func(context.Context) context.Context {
Expand All @@ -22,3 +24,13 @@ func CtxLog(entry *logan.Entry) func(context.Context) context.Context {
func Log(r *http.Request) *logan.Entry {
return r.Context().Value(logCtxKey).(*logan.Entry)
}

func CtxKVQ(stateQ data.KVQ) func(context.Context) context.Context {
return func(ctx context.Context) context.Context {
return context.WithValue(ctx, kvCtxKey, stateQ)
}
}

func KVQ(r *http.Request) data.KVQ {
return r.Context().Value(kvCtxKey).(data.KVQ)
}
51 changes: 51 additions & 0 deletions internal/service/handlers/get_data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package handlers

import (
"encoding/base64"
"net/http"

"github.com/rarimo/bio-data-svc/internal/service/requests"
"gitlab.com/distributed_lab/ape"
"gitlab.com/distributed_lab/ape/problems"
)

func GetData(w http.ResponseWriter, r *http.Request) {
req, err := requests.NewGetDataRequest(r)
if err != nil {
Log(r).WithError(err).Error("invalid request")
ape.RenderErr(w, problems.BadRequest(err)...)
return
}

kvQuery := KVQ(r)

if req.Key != nil {
kvQuery = kvQuery.FilterByKey(*req.Key)
}

if req.Value != nil {
value, err := base64.StdEncoding.DecodeString(*req.Value)
if err != nil {
Log(r).WithError(err).WithField("value", *req.Value).Error("failed to decode base64 string")
ape.RenderErr(w, problems.InternalError())
return
}

kvQuery = kvQuery.FilterByValue(value)
}

kv, err := kvQuery.Get()
if err != nil {
Log(r).WithError(err).Error("failed to get key-value")
ape.RenderErr(w, problems.InternalError())
return
}

if kv == nil {
Log(r).Error("no key-value row found")
ape.RenderErr(w, problems.NotFound())
return
}

ape.Render(w, newKVResponse(*kv))
}
24 changes: 9 additions & 15 deletions internal/service/main.go
Original file line number Diff line number Diff line change
@@ -1,42 +1,36 @@
package service

import (
"context"
"net"
"net/http"

"github.com/rarimo/bio-data-svc/internal/config"
"gitlab.com/distributed_lab/ape"
"gitlab.com/distributed_lab/kit/copus/types"
"gitlab.com/distributed_lab/logan/v3"
"gitlab.com/distributed_lab/logan/v3/errors"
)

type service struct {
log *logan.Entry
copus types.Copus
listener net.Listener
cfg config.Config
}

func (s *service) run() error {
func (s *service) run(ctx context.Context) {
s.log.Info("Service started")
r := s.router()

if err := s.copus.RegisterChi(r); err != nil {
return errors.Wrap(err, "cop failed")
}

return http.Serve(s.listener, r)
ape.Serve(ctx, s.router(), s.cfg, ape.ServeOpts{})
}

func newService(cfg config.Config) *service {
return &service{
log: cfg.Log(),
log: cfg.Log().WithField("service", "api"),
copus: cfg.Copus(),
listener: cfg.Listener(),
cfg: cfg,
}
}

func Run(cfg config.Config) {
if err := newService(cfg).run(); err != nil {
panic(err)
}
func Run(ctx context.Context, cfg config.Config) {
newService(cfg).run(ctx)
}
21 changes: 21 additions & 0 deletions internal/service/requests/add_data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package requests

import (
"encoding/json"
"net/http"

"github.com/go-ozzo/ozzo-validation/is"
val "github.com/go-ozzo/ozzo-validation/v4"
"github.com/rarimo/bio-data-svc/resources"
)

func AddData(r *http.Request) (req resources.AddValueRequest, err error) {
if err = json.NewDecoder(r.Body).Decode(&req); err != nil {
return req, val.Errors{"body": err}
}

return req, val.Errors{
"data/type": val.Validate(req.Data.Type, val.Required, val.In(resources.VALUE)),
"data/attributes/value": val.Validate(req.Data.Attributes.Value, val.Required, is.Base64),
}.Filter()
}
26 changes: 26 additions & 0 deletions internal/service/requests/get_data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package requests

import (
"net/http"

"github.com/go-ozzo/ozzo-validation/is"
val "github.com/go-ozzo/ozzo-validation/v4"
"gitlab.com/distributed_lab/urlval/v4"
)

type GetDataRequest struct {
Key *string `filter:"key"`
Value *string `filter:"value"`
}

func NewGetDataRequest(r *http.Request) (req GetDataRequest, err error) {
if err = urlval.Decode(r.URL.Query(), &req); err != nil {
return req, val.Errors{"query": err}
}

err = val.Errors{
"key": val.Validate(req.Key, val.When(!val.IsEmpty(req.Key), is.UUID)),
"value": val.Validate(req.Value, val.When(!val.IsEmpty(req.Value), is.Base64)),
}.Filter()
return
}
7 changes: 6 additions & 1 deletion internal/service/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package service

import (
"github.com/go-chi/chi"
"github.com/rarimo/bio-data-svc/internal/data/pg"
"github.com/rarimo/bio-data-svc/internal/service/handlers"
"gitlab.com/distributed_lab/ape"
)
Expand All @@ -14,10 +15,14 @@ func (s *service) router() chi.Router {
ape.LoganMiddleware(s.log),
ape.CtxMiddleware(
handlers.CtxLog(s.log),
handlers.CtxKVQ(pg.NewKVQ(s.cfg.DB().Clone())),
),
)
r.Route("/integrations/bio-data-svc", func(r chi.Router) {
// configure endpoints here
r.Route("/value", func(r chi.Router) {
r.Post("/", handlers.AddData)
r.Get("/", handlers.GetData)
})
})

return r
Expand Down

0 comments on commit a32882b

Please sign in to comment.