initial commit
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.json
|
||||
*.zst
|
||||
badger/
|
||||
9
assets/category.fbs
Normal file
9
assets/category.fbs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace internal.encoding.fbs;
|
||||
|
||||
table Category {
|
||||
id:long;
|
||||
name:string;
|
||||
}
|
||||
|
||||
root_type Category;
|
||||
|
||||
23
assets/gooditem.fbs
Normal file
23
assets/gooditem.fbs
Normal file
@ -0,0 +1,23 @@
|
||||
namespace internal.encoding.fbs;
|
||||
|
||||
table GoodItem {
|
||||
sku:string;
|
||||
photo:string;
|
||||
name:string;
|
||||
description:string;
|
||||
category:string;
|
||||
type:string;
|
||||
producer:string;
|
||||
pack:short;
|
||||
step:short;
|
||||
price:float;
|
||||
tariff:float;
|
||||
cart:long;
|
||||
stock:short;
|
||||
}
|
||||
|
||||
table GoodItems {
|
||||
items:[GoodItem];
|
||||
}
|
||||
|
||||
root_type GoodItems;
|
||||
444
cmd/converter/main.go
Normal file
444
cmd/converter/main.go
Normal file
@ -0,0 +1,444 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"git.loyso.art/frx/eway/internal/config"
|
||||
"git.loyso.art/frx/eway/internal/entity"
|
||||
"git.loyso.art/frx/eway/internal/storage"
|
||||
xbadger "git.loyso.art/frx/eway/internal/storage/badger"
|
||||
|
||||
badger "github.com/dgraph-io/badger/v4"
|
||||
"github.com/dgraph-io/badger/v4/pb"
|
||||
"github.com/rodaine/table"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type appSettings struct {
|
||||
Badger config.Badger
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
defer func() {
|
||||
cancel()
|
||||
}()
|
||||
|
||||
var corewg sync.WaitGroup
|
||||
err := runcli(ctx, &corewg)
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unable to handle app: %v", err)
|
||||
|
||||
corewg.Wait()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
corewg.Wait()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func runcli(ctx context.Context, wg *sync.WaitGroup) (err error) {
|
||||
tsSet := func(wr *zerolog.ConsoleWriter) {
|
||||
wr.TimeFormat = time.RFC3339
|
||||
}
|
||||
log := zerolog.New(zerolog.NewConsoleWriter(tsSet)).Level(zerolog.DebugLevel).With().Timestamp().Str("app", "converter").Logger()
|
||||
|
||||
defer func() {
|
||||
log.Info().Err(err).Msg("app finished")
|
||||
}()
|
||||
|
||||
ctx = log.WithContext(ctx)
|
||||
|
||||
log.Info().Msg("making badger")
|
||||
|
||||
db, err := xbadger.Open(ctx, "badger/", log)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening badger: %w", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
var item atomic.Uint64
|
||||
err = db.Subscribe(
|
||||
ctx,
|
||||
func(kvlist *badger.KVList) error {
|
||||
kvs := kvlist.GetKv()
|
||||
for _, kv := range kvs {
|
||||
count := item.Add(1)
|
||||
log.Debug().Bytes("key", kv.GetKey()).Uint64("count", count).Msg("inspecting")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
[]pb.Match{
|
||||
{
|
||||
Prefix: []byte("!!category!!"),
|
||||
},
|
||||
{
|
||||
Prefix: []byte("!!goodsitem!!"),
|
||||
},
|
||||
},
|
||||
)
|
||||
log.Err(err).Msg("subscribing")
|
||||
|
||||
}()
|
||||
|
||||
maxBatch := db.MaxBatchCount()
|
||||
log.Info().Int("max_batch", int(maxBatch)).Msg("max batch settings")
|
||||
client, err := xbadger.NewClient(db)
|
||||
if err != nil {
|
||||
return fmt.Errorf("making new client: %w", err)
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
defer func() {
|
||||
defer wg.Done()
|
||||
|
||||
println("closing client")
|
||||
errClose := client.Close()
|
||||
if errClose != nil {
|
||||
log.Warn().Err(errClose).Msg("unable to close client")
|
||||
}
|
||||
|
||||
println("flushing db")
|
||||
errSync := db.Sync()
|
||||
if errSync != nil {
|
||||
log.Warn().Err(errSync).Msg("unable to sync db")
|
||||
}
|
||||
|
||||
// time.Sleep(time.Second * 5)
|
||||
|
||||
println("closing db")
|
||||
errClose = db.Close()
|
||||
if errClose != nil {
|
||||
log.Warn().Err(errClose).Msg("unable to close db")
|
||||
}
|
||||
}()
|
||||
|
||||
app := setupCLI(ctx, client, maxBatch)
|
||||
|
||||
return app.Run(os.Args)
|
||||
}
|
||||
|
||||
func setupCLI(ctx context.Context, r storage.Repository, maxBatch int64) *cli.App {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Commands = cli.Commands{
|
||||
newImportCmd(ctx, r, maxBatch),
|
||||
newViewCmd(ctx, r),
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func newImportCmd(ctx context.Context, r storage.Repository, maxBatch int64) cli.Command {
|
||||
return cli.Command{
|
||||
Name: "import",
|
||||
Usage: "category for importing data from sources",
|
||||
Flags: []cli.Flag{
|
||||
// &cli.StringFlag{
|
||||
// Name: "config",
|
||||
// Usage: "path to config",
|
||||
// Value: "config.json",
|
||||
// TakesFile: true,
|
||||
// },
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Usage: "set logger to debug mode",
|
||||
},
|
||||
},
|
||||
Before: func(c *cli.Context) error {
|
||||
if c.Bool("verbose") {
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Subcommands: cli.Commands{
|
||||
newImportFromFileCmd(ctx, r, maxBatch),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newViewCmd(ctx context.Context, r storage.Repository) cli.Command {
|
||||
return cli.Command{
|
||||
Name: "view",
|
||||
Usage: "Set of commands to view the data inside db",
|
||||
Subcommands: []cli.Command{
|
||||
newViewCategoriesCmd(ctx, r),
|
||||
newViewItemsCmd(ctx, r),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newViewCategoriesCmd(ctx context.Context, r storage.Repository) cli.Command {
|
||||
return cli.Command{
|
||||
Name: "categories",
|
||||
Usage: "Set of commands to work with categories",
|
||||
Subcommands: []cli.Command{
|
||||
newViewCategoriesListCmd(ctx, r),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newViewItemsCmd(ctx context.Context, r storage.Repository) cli.Command {
|
||||
return cli.Command{
|
||||
Name: "items",
|
||||
Usage: "Set of command to work with items",
|
||||
Subcommands: cli.Commands{
|
||||
newViewItemsCountCmd(ctx, r),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newViewItemsCountCmd(ctx context.Context, r storage.Repository) cli.Command {
|
||||
return cli.Command{
|
||||
Name: "count",
|
||||
Usage: "iterates over collection and counts number of items",
|
||||
Action: viewItemsCount(ctx, r),
|
||||
}
|
||||
}
|
||||
|
||||
func newViewCategoriesListCmd(ctx context.Context, r storage.Repository) cli.Command {
|
||||
return cli.Command{
|
||||
Name: "list",
|
||||
Usage: "lists stored categories stored in database",
|
||||
Flags: []cli.Flag{
|
||||
&cli.IntFlag{
|
||||
Name: "limit",
|
||||
Usage: "limits output to selected items",
|
||||
Value: 20,
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "page",
|
||||
Usage: "in case of limit, selects page",
|
||||
Value: 0,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "with-total",
|
||||
Usage: "prints total count of categories",
|
||||
},
|
||||
},
|
||||
Action: viewCategoriesListAction(ctx, r),
|
||||
}
|
||||
}
|
||||
|
||||
func viewItemsCount(ctx context.Context, r storage.Repository) cli.ActionFunc {
|
||||
f := func(c *cli.Context) error {
|
||||
// itemChan, err := r.GoodsItem().ListIter(ctx, 10)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("getting list iter: %w", err)
|
||||
// }
|
||||
//
|
||||
var count int
|
||||
// for range itemChan {
|
||||
// count++
|
||||
// }
|
||||
//
|
||||
items, err := r.GoodsItem().List(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting list: %w", err)
|
||||
}
|
||||
|
||||
zerolog.Ctx(ctx).Info().Int("count", count).Int("list_count", len(items)).Msg("read all items")
|
||||
return nil
|
||||
}
|
||||
|
||||
return wrapActionFunc(ctx, f)
|
||||
}
|
||||
|
||||
func viewCategoriesListAction(ctx context.Context, r storage.Repository) cli.ActionFunc {
|
||||
f := func(c *cli.Context) error {
|
||||
categories, err := r.Category().List(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listing categories: %w", err)
|
||||
}
|
||||
|
||||
limit := c.Int("limit")
|
||||
page := c.Int("page")
|
||||
total := len(categories)
|
||||
|
||||
if page == 0 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
offset := (page - 1) * limit
|
||||
if offset > len(categories) {
|
||||
offset = len(categories) - 1
|
||||
}
|
||||
|
||||
limit = offset + limit
|
||||
if limit > len(categories) {
|
||||
limit = len(categories)
|
||||
}
|
||||
|
||||
categories = categories[offset:limit]
|
||||
}
|
||||
|
||||
tbl := table.New("ID", "Name")
|
||||
for _, category := range categories {
|
||||
if category.ID == 0 && category.Name == "" {
|
||||
continue
|
||||
}
|
||||
tbl.AddRow(category.ID, category.Name)
|
||||
}
|
||||
|
||||
tbl.Print()
|
||||
|
||||
if c.Bool("with-total") {
|
||||
zerolog.Ctx(ctx).Info().Int("count", total).Msg("total categories stats")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return wrapActionFunc(ctx, f)
|
||||
}
|
||||
|
||||
func newImportFromFileCmd(ctx context.Context, r storage.Repository, maxBatch int64) cli.Command {
|
||||
return cli.Command{
|
||||
Name: "fromfile",
|
||||
Usage: "imports from file into db",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "src",
|
||||
Value: "",
|
||||
Usage: "source of the data.",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Action: handleConvert(ctx, r, maxBatch),
|
||||
}
|
||||
}
|
||||
|
||||
func handleConvert(ctx context.Context, r storage.Repository, maxBatch int64) cli.ActionFunc {
|
||||
f := func(c *cli.Context) error {
|
||||
filesrc := c.String("src")
|
||||
log := zerolog.Ctx(ctx)
|
||||
|
||||
log.Debug().Str("filepath", filesrc).Msg("importing data from file")
|
||||
|
||||
productsFile, err := os.Open(filesrc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening file: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
errClose := productsFile.Close()
|
||||
if errClose != nil {
|
||||
log.Warn().Err(errClose).Msg("unable to close file")
|
||||
}
|
||||
}()
|
||||
|
||||
var (
|
||||
goodsItem entity.GoodsItem
|
||||
// goodsItems []entity.GoodsItem
|
||||
failedToInsert int
|
||||
)
|
||||
|
||||
seenCategories := make(map[string]struct{})
|
||||
categories, err := r.Category().List(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listing categories: %w", err)
|
||||
}
|
||||
|
||||
for _, category := range categories {
|
||||
seenCategories[category.Name] = struct{}{}
|
||||
}
|
||||
|
||||
bfile := bufio.NewReader(productsFile)
|
||||
for {
|
||||
line, _, err := bfile.ReadLine()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
|
||||
}
|
||||
return fmt.Errorf("reading line: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(line, &goodsItem)
|
||||
if err != nil {
|
||||
// log.Warn().Err(err).Str("line", string(line)).Msg("unable to unmarshal line into item")
|
||||
failedToInsert++
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = r.GoodsItem().UpsertMany(ctx, goodsItem)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to upsert new item: %w", err)
|
||||
}
|
||||
|
||||
// goodsItems = append(goodsItems, goodsItem)
|
||||
|
||||
if _, ok := seenCategories[goodsItem.Type]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = r.Category().Create(ctx, goodsItem.Type)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create new category: %w", err)
|
||||
}
|
||||
log.Debug().Any("category", goodsItem.Type).Msg("inserted new category")
|
||||
seenCategories[goodsItem.Type] = struct{}{}
|
||||
}
|
||||
|
||||
// log.Debug().Int("count", len(goodsItems)).Int("failed", failedToInsert).Msg("preparing to upload")
|
||||
//
|
||||
// start := time.Now()
|
||||
// batchSize := int(maxBatch)
|
||||
// for i := 0; i < len(goodsItems); i += batchSize {
|
||||
// to := i + batchSize
|
||||
// if to > len(goodsItems) {
|
||||
// to = len(goodsItems)
|
||||
// }
|
||||
//
|
||||
// _, err = r.GoodsItem().UpsertMany(ctx, goodsItems[i:to]...)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("upserting items: %w", err)
|
||||
// }
|
||||
// log.Debug().Int("count", to-i).Msg("inserted batch")
|
||||
// time.Sleep(time.Second)
|
||||
// }
|
||||
// log.Debug().Dur("elapsed", time.Since(start)).Msg("upload finished")
|
||||
//
|
||||
|
||||
time.Sleep(time.Second * 30)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
time.Sleep(time.Second * 10)
|
||||
|
||||
return wrapActionFunc(ctx, f)
|
||||
}
|
||||
|
||||
func wrapActionFunc(ctx context.Context, next cli.ActionFunc) cli.ActionFunc {
|
||||
return func(c *cli.Context) error {
|
||||
var data [3]byte
|
||||
_, _ = rand.Read(data[:])
|
||||
reqid := hex.EncodeToString(data[:])
|
||||
|
||||
log := zerolog.Ctx(ctx).With().Str("reqid", reqid).Logger()
|
||||
ctx = log.WithContext(ctx)
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
log.Info().Dur("elapsed", time.Since(start)).Msg("command completed")
|
||||
}()
|
||||
|
||||
log.Info().Msg("command execution started")
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
34
go.mod
Normal file
34
go.mod
Normal file
@ -0,0 +1,34 @@
|
||||
module git.loyso.art/frx/eway
|
||||
|
||||
go 1.21.4
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/dgraph-io/badger/v4 v4.2.0 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.10.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/glog v1.0.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.3 // indirect
|
||||
github.com/google/flatbuffers v23.5.26+incompatible // indirect
|
||||
github.com/karlseguin/ccache/v3 v3.0.5 // indirect
|
||||
github.com/kataras/tablewriter v0.0.0-20180708051242-e063d29b7c23 // indirect
|
||||
github.com/klauspost/compress v1.12.3 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rodaine/table v1.1.1 // indirect
|
||||
github.com/rs/zerolog v1.31.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/urfave/cli v1.22.14 // indirect
|
||||
go.opencensus.io v0.22.5 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
)
|
||||
177
go.sum
Normal file
177
go.sum
Normal file
@ -0,0 +1,177 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger/v4 v4.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8BzuWsEs=
|
||||
github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak=
|
||||
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/go-resty/resty/v2 v2.10.0 h1:Qla4W/+TMmv0fOeeRqzEpXPLfTUnR5HZ1+lGs+CkiCo=
|
||||
github.com/go-resty/resty/v2 v2.10.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/flatbuffers v23.5.26+incompatible h1:M9dgRyhJemaM4Sw8+66GHBu8ioaQmyPLg1b8VwK5WJg=
|
||||
github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/karlseguin/ccache/v3 v3.0.5 h1:hFX25+fxzNjsRlREYsoGNa2LoVEw5mPF8wkWq/UnevQ=
|
||||
github.com/karlseguin/ccache/v3 v3.0.5/go.mod h1:qxC372+Qn+IBj8Pe3KvGjHPj0sWwEF7AeZVhsNPZ6uY=
|
||||
github.com/kataras/tablewriter v0.0.0-20180708051242-e063d29b7c23 h1:M8exrBzuhWcU6aoHJlHWPe4qFjVKzkMGRal78f5jRRU=
|
||||
github.com/kataras/tablewriter v0.0.0-20180708051242-e063d29b7c23/go.mod h1:kBSna6b0/RzsOcOZf515vAXwSsXYusl2U7SA0XP09yI=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU=
|
||||
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
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/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.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rodaine/table v1.1.1 h1:zBliy3b4Oj6JRmncse2Z85WmoQvDrXOYuy0JXCt8Qz8=
|
||||
github.com/rodaine/table v1.1.1/go.mod h1:iqTRptjn+EVcrVBYtNMlJ2wrJZa3MpULUmcXFpfcziA=
|
||||
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/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
|
||||
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
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/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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20220722155257-8c9f86f7a55f/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-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.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/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
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=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
5
internal/config/badger.go
Normal file
5
internal/config/badger.go
Normal file
@ -0,0 +1,5 @@
|
||||
package config
|
||||
|
||||
type Badger struct {
|
||||
Path string `json:"path"`
|
||||
}
|
||||
64
internal/core/base.go
Normal file
64
internal/core/base.go
Normal file
@ -0,0 +1,64 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type BaseAction interface{}
|
||||
|
||||
type Action[Q, R any] interface {
|
||||
BaseAction
|
||||
|
||||
Do(context.Context, Q) (R, error)
|
||||
}
|
||||
|
||||
type ActionDecorator[Q, R any, A Action[Q, R]] interface {
|
||||
Action[Q, R]
|
||||
}
|
||||
|
||||
type baseAction struct {
|
||||
env *Env
|
||||
}
|
||||
|
||||
func newBaseAction(env *Env) baseAction {
|
||||
return baseAction{
|
||||
env: env,
|
||||
}
|
||||
}
|
||||
|
||||
func applyDecorators[Q, R any, A Action[Q, R]](action A) ActionDecorator[Q, R, A] {
|
||||
return logActionDecorator[Q, R, A]{
|
||||
action: action,
|
||||
}
|
||||
}
|
||||
|
||||
type logActionDecorator[Q, R any, A Action[Q, R]] struct {
|
||||
action Action[Q, R]
|
||||
}
|
||||
|
||||
func (d logActionDecorator[Q, R, A]) Do(ctx context.Context, params Q) (result R, err error) {
|
||||
actionName := getTypeName[A]()
|
||||
start := time.Now()
|
||||
log := zerolog.Ctx(ctx).With().Str("action_name", actionName).Logger()
|
||||
ctx = log.WithContext(ctx)
|
||||
|
||||
result, err = d.action.Do(ctx, params)
|
||||
elapsed := time.Since(start)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Dur("elapsed", elapsed).Msg("action failed")
|
||||
}
|
||||
|
||||
log.Info().Dur("elapsed", elapsed).Msg("action successed")
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func getTypeName[T any]() string {
|
||||
var t T
|
||||
out := fmt.Sprintf("%T", t)
|
||||
return out
|
||||
}
|
||||
45
internal/core/creategoodsitem.go
Normal file
45
internal/core/creategoodsitem.go
Normal file
@ -0,0 +1,45 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.loyso.art/frx/eway/internal/entity"
|
||||
)
|
||||
|
||||
type CreateGoodsItemParams struct {
|
||||
GoodsItems []entity.GoodsItem
|
||||
}
|
||||
type CreateGoodsItemResult struct {
|
||||
GoodsItems []entity.GoodsItem
|
||||
}
|
||||
|
||||
type createGoodsItemAction struct {
|
||||
baseAction
|
||||
}
|
||||
|
||||
type CreateGoodsItemAction Action[CreateGoodsItemParams, CreateGoodsItemResult]
|
||||
|
||||
func NewCreateGoodsItemAction(
|
||||
env *Env,
|
||||
) ActionDecorator[CreateGoodsItemParams, CreateGoodsItemResult, CreateGoodsItemAction] {
|
||||
ba := newBaseAction(env)
|
||||
|
||||
action := &createGoodsItemAction{
|
||||
baseAction: ba,
|
||||
}
|
||||
|
||||
return applyDecorators(action)
|
||||
}
|
||||
|
||||
func (a *createGoodsItemAction) Do(
|
||||
ctx context.Context,
|
||||
params CreateGoodsItemParams,
|
||||
) (result CreateGoodsItemResult, err error) {
|
||||
result.GoodsItems, err = a.env.repository.GoodsItem().UpsertMany(ctx, params.GoodsItems...)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("upserting items: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
11
internal/core/env.go
Normal file
11
internal/core/env.go
Normal file
@ -0,0 +1,11 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"git.loyso.art/frx/eway/internal/entity"
|
||||
"git.loyso.art/frx/eway/internal/storage"
|
||||
)
|
||||
|
||||
type Env struct {
|
||||
repository storage.Repository
|
||||
mapper entity.Mapper
|
||||
}
|
||||
49
internal/core/getcategory.go
Normal file
49
internal/core/getcategory.go
Normal file
@ -0,0 +1,49 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.loyso.art/frx/eway/internal/entity"
|
||||
)
|
||||
|
||||
type GetCategoryParams struct {
|
||||
ID int64
|
||||
Name string
|
||||
}
|
||||
type GetCategoryResult struct {
|
||||
Category entity.Category
|
||||
}
|
||||
|
||||
type GetCategoryAction Action[GetCategoryParams, GetCategoryResult]
|
||||
|
||||
type getCategoryAction struct {
|
||||
baseAction
|
||||
}
|
||||
|
||||
func NewGetCategoryAction(env *Env) ActionDecorator[GetCategoryParams, GetCategoryResult, GetCategoryAction] {
|
||||
ba := newBaseAction(env)
|
||||
|
||||
action := &getCategoryAction{
|
||||
baseAction: ba,
|
||||
}
|
||||
|
||||
return applyDecorators(action)
|
||||
}
|
||||
|
||||
func (a *getCategoryAction) Do(ctx context.Context, params GetCategoryParams) (result GetCategoryResult, err error) {
|
||||
id := params.ID
|
||||
if params.Name != "" {
|
||||
id, err = a.env.mapper.CategoryNameToID(ctx, params.Name)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("resolving category id: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
result.Category, err = a.env.repository.Category().Get(ctx, id)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("getting category: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
38
internal/core/listcategories.go
Normal file
38
internal/core/listcategories.go
Normal file
@ -0,0 +1,38 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.loyso.art/frx/eway/internal/entity"
|
||||
)
|
||||
|
||||
type ListCategoriesParams struct{}
|
||||
type ListCategoriesResult struct {
|
||||
Categories []entity.Category
|
||||
}
|
||||
|
||||
type listCategoriesAction struct {
|
||||
baseAction
|
||||
}
|
||||
|
||||
type ListCategoriesAction Action[ListCategoriesParams, ListCategoriesResult]
|
||||
|
||||
func NewListCategoriesAction(env *Env) ActionDecorator[ListCategoriesParams, ListCategoriesResult, ListCategoriesAction] {
|
||||
ba := newBaseAction(env)
|
||||
|
||||
action := &listCategoriesAction{
|
||||
baseAction: ba,
|
||||
}
|
||||
|
||||
return applyDecorators(action)
|
||||
}
|
||||
|
||||
func (l *listCategoriesAction) Do(ctx context.Context, params ListCategoriesParams) (result ListCategoriesResult, err error) {
|
||||
result.Categories, err = l.env.repository.Category().List(ctx)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("listing: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
75
internal/encoding/fbs/Category.go
Normal file
75
internal/encoding/fbs/Category.go
Normal file
@ -0,0 +1,75 @@
|
||||
// Code generated by the FlatBuffers compiler. DO NOT EDIT.
|
||||
|
||||
package fbs
|
||||
|
||||
import (
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
)
|
||||
|
||||
type Category struct {
|
||||
_tab flatbuffers.Table
|
||||
}
|
||||
|
||||
func GetRootAsCategory(buf []byte, offset flatbuffers.UOffsetT) *Category {
|
||||
n := flatbuffers.GetUOffsetT(buf[offset:])
|
||||
x := &Category{}
|
||||
x.Init(buf, n+offset)
|
||||
return x
|
||||
}
|
||||
|
||||
func FinishCategoryBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) {
|
||||
builder.Finish(offset)
|
||||
}
|
||||
|
||||
func GetSizePrefixedRootAsCategory(buf []byte, offset flatbuffers.UOffsetT) *Category {
|
||||
n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
|
||||
x := &Category{}
|
||||
x.Init(buf, n+offset+flatbuffers.SizeUint32)
|
||||
return x
|
||||
}
|
||||
|
||||
func FinishSizePrefixedCategoryBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) {
|
||||
builder.FinishSizePrefixed(offset)
|
||||
}
|
||||
|
||||
func (rcv *Category) Init(buf []byte, i flatbuffers.UOffsetT) {
|
||||
rcv._tab.Bytes = buf
|
||||
rcv._tab.Pos = i
|
||||
}
|
||||
|
||||
func (rcv *Category) Table() flatbuffers.Table {
|
||||
return rcv._tab
|
||||
}
|
||||
|
||||
func (rcv *Category) Id() int64 {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
|
||||
if o != 0 {
|
||||
return rcv._tab.GetInt64(o + rcv._tab.Pos)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (rcv *Category) MutateId(n int64) bool {
|
||||
return rcv._tab.MutateInt64Slot(4, n)
|
||||
}
|
||||
|
||||
func (rcv *Category) Name() []byte {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(6))
|
||||
if o != 0 {
|
||||
return rcv._tab.ByteVector(o + rcv._tab.Pos)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CategoryStart(builder *flatbuffers.Builder) {
|
||||
builder.StartObject(2)
|
||||
}
|
||||
func CategoryAddId(builder *flatbuffers.Builder, id int64) {
|
||||
builder.PrependInt64Slot(0, id, 0)
|
||||
}
|
||||
func CategoryAddName(builder *flatbuffers.Builder, name flatbuffers.UOffsetT) {
|
||||
builder.PrependUOffsetTSlot(1, flatbuffers.UOffsetT(name), 0)
|
||||
}
|
||||
func CategoryEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
return builder.EndObject()
|
||||
}
|
||||
216
internal/encoding/fbs/GoodItem.go
Normal file
216
internal/encoding/fbs/GoodItem.go
Normal file
@ -0,0 +1,216 @@
|
||||
// Code generated by the FlatBuffers compiler. DO NOT EDIT.
|
||||
|
||||
package fbs
|
||||
|
||||
import (
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
)
|
||||
|
||||
type GoodItem struct {
|
||||
_tab flatbuffers.Table
|
||||
}
|
||||
|
||||
func GetRootAsGoodItem(buf []byte, offset flatbuffers.UOffsetT) *GoodItem {
|
||||
n := flatbuffers.GetUOffsetT(buf[offset:])
|
||||
x := &GoodItem{}
|
||||
x.Init(buf, n+offset)
|
||||
return x
|
||||
}
|
||||
|
||||
func FinishGoodItemBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) {
|
||||
builder.Finish(offset)
|
||||
}
|
||||
|
||||
func GetSizePrefixedRootAsGoodItem(buf []byte, offset flatbuffers.UOffsetT) *GoodItem {
|
||||
n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
|
||||
x := &GoodItem{}
|
||||
x.Init(buf, n+offset+flatbuffers.SizeUint32)
|
||||
return x
|
||||
}
|
||||
|
||||
func FinishSizePrefixedGoodItemBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) {
|
||||
builder.FinishSizePrefixed(offset)
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Init(buf []byte, i flatbuffers.UOffsetT) {
|
||||
rcv._tab.Bytes = buf
|
||||
rcv._tab.Pos = i
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Table() flatbuffers.Table {
|
||||
return rcv._tab
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Sku() []byte {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
|
||||
if o != 0 {
|
||||
return rcv._tab.ByteVector(o + rcv._tab.Pos)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Photo() []byte {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(6))
|
||||
if o != 0 {
|
||||
return rcv._tab.ByteVector(o + rcv._tab.Pos)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Name() []byte {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(8))
|
||||
if o != 0 {
|
||||
return rcv._tab.ByteVector(o + rcv._tab.Pos)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Description() []byte {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(10))
|
||||
if o != 0 {
|
||||
return rcv._tab.ByteVector(o + rcv._tab.Pos)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Category() []byte {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(12))
|
||||
if o != 0 {
|
||||
return rcv._tab.ByteVector(o + rcv._tab.Pos)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Type() []byte {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(14))
|
||||
if o != 0 {
|
||||
return rcv._tab.ByteVector(o + rcv._tab.Pos)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Producer() []byte {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(16))
|
||||
if o != 0 {
|
||||
return rcv._tab.ByteVector(o + rcv._tab.Pos)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Pack() int16 {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(18))
|
||||
if o != 0 {
|
||||
return rcv._tab.GetInt16(o + rcv._tab.Pos)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) MutatePack(n int16) bool {
|
||||
return rcv._tab.MutateInt16Slot(18, n)
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Step() int16 {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(20))
|
||||
if o != 0 {
|
||||
return rcv._tab.GetInt16(o + rcv._tab.Pos)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) MutateStep(n int16) bool {
|
||||
return rcv._tab.MutateInt16Slot(20, n)
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Price() float32 {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(22))
|
||||
if o != 0 {
|
||||
return rcv._tab.GetFloat32(o + rcv._tab.Pos)
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) MutatePrice(n float32) bool {
|
||||
return rcv._tab.MutateFloat32Slot(22, n)
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Tariff() float32 {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(24))
|
||||
if o != 0 {
|
||||
return rcv._tab.GetFloat32(o + rcv._tab.Pos)
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) MutateTariff(n float32) bool {
|
||||
return rcv._tab.MutateFloat32Slot(24, n)
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Cart() int64 {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(26))
|
||||
if o != 0 {
|
||||
return rcv._tab.GetInt64(o + rcv._tab.Pos)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) MutateCart(n int64) bool {
|
||||
return rcv._tab.MutateInt64Slot(26, n)
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) Stock() int16 {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(28))
|
||||
if o != 0 {
|
||||
return rcv._tab.GetInt16(o + rcv._tab.Pos)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (rcv *GoodItem) MutateStock(n int16) bool {
|
||||
return rcv._tab.MutateInt16Slot(28, n)
|
||||
}
|
||||
|
||||
func GoodItemStart(builder *flatbuffers.Builder) {
|
||||
builder.StartObject(13)
|
||||
}
|
||||
func GoodItemAddSku(builder *flatbuffers.Builder, sku flatbuffers.UOffsetT) {
|
||||
builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(sku), 0)
|
||||
}
|
||||
func GoodItemAddPhoto(builder *flatbuffers.Builder, photo flatbuffers.UOffsetT) {
|
||||
builder.PrependUOffsetTSlot(1, flatbuffers.UOffsetT(photo), 0)
|
||||
}
|
||||
func GoodItemAddName(builder *flatbuffers.Builder, name flatbuffers.UOffsetT) {
|
||||
builder.PrependUOffsetTSlot(2, flatbuffers.UOffsetT(name), 0)
|
||||
}
|
||||
func GoodItemAddDescription(builder *flatbuffers.Builder, description flatbuffers.UOffsetT) {
|
||||
builder.PrependUOffsetTSlot(3, flatbuffers.UOffsetT(description), 0)
|
||||
}
|
||||
func GoodItemAddCategory(builder *flatbuffers.Builder, category flatbuffers.UOffsetT) {
|
||||
builder.PrependUOffsetTSlot(4, flatbuffers.UOffsetT(category), 0)
|
||||
}
|
||||
func GoodItemAddType(builder *flatbuffers.Builder, type_ flatbuffers.UOffsetT) {
|
||||
builder.PrependUOffsetTSlot(5, flatbuffers.UOffsetT(type_), 0)
|
||||
}
|
||||
func GoodItemAddProducer(builder *flatbuffers.Builder, producer flatbuffers.UOffsetT) {
|
||||
builder.PrependUOffsetTSlot(6, flatbuffers.UOffsetT(producer), 0)
|
||||
}
|
||||
func GoodItemAddPack(builder *flatbuffers.Builder, pack int16) {
|
||||
builder.PrependInt16Slot(7, pack, 0)
|
||||
}
|
||||
func GoodItemAddStep(builder *flatbuffers.Builder, step int16) {
|
||||
builder.PrependInt16Slot(8, step, 0)
|
||||
}
|
||||
func GoodItemAddPrice(builder *flatbuffers.Builder, price float32) {
|
||||
builder.PrependFloat32Slot(9, price, 0.0)
|
||||
}
|
||||
func GoodItemAddTariff(builder *flatbuffers.Builder, tariff float32) {
|
||||
builder.PrependFloat32Slot(10, tariff, 0.0)
|
||||
}
|
||||
func GoodItemAddCart(builder *flatbuffers.Builder, cart int64) {
|
||||
builder.PrependInt64Slot(11, cart, 0)
|
||||
}
|
||||
func GoodItemAddStock(builder *flatbuffers.Builder, stock int16) {
|
||||
builder.PrependInt16Slot(12, stock, 0)
|
||||
}
|
||||
func GoodItemEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
return builder.EndObject()
|
||||
}
|
||||
75
internal/encoding/fbs/GoodsItem.go
Normal file
75
internal/encoding/fbs/GoodsItem.go
Normal file
@ -0,0 +1,75 @@
|
||||
// Code generated by the FlatBuffers compiler. DO NOT EDIT.
|
||||
|
||||
package fbs
|
||||
|
||||
import (
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
)
|
||||
|
||||
type GoodItems struct {
|
||||
_tab flatbuffers.Table
|
||||
}
|
||||
|
||||
func GetRootAsGoodItems(buf []byte, offset flatbuffers.UOffsetT) *GoodItems {
|
||||
n := flatbuffers.GetUOffsetT(buf[offset:])
|
||||
x := &GoodItems{}
|
||||
x.Init(buf, n+offset)
|
||||
return x
|
||||
}
|
||||
|
||||
func FinishGoodItemsBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) {
|
||||
builder.Finish(offset)
|
||||
}
|
||||
|
||||
func GetSizePrefixedRootAsGoodItems(buf []byte, offset flatbuffers.UOffsetT) *GoodItems {
|
||||
n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
|
||||
x := &GoodItems{}
|
||||
x.Init(buf, n+offset+flatbuffers.SizeUint32)
|
||||
return x
|
||||
}
|
||||
|
||||
func FinishSizePrefixedGoodItemsBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) {
|
||||
builder.FinishSizePrefixed(offset)
|
||||
}
|
||||
|
||||
func (rcv *GoodItems) Init(buf []byte, i flatbuffers.UOffsetT) {
|
||||
rcv._tab.Bytes = buf
|
||||
rcv._tab.Pos = i
|
||||
}
|
||||
|
||||
func (rcv *GoodItems) Table() flatbuffers.Table {
|
||||
return rcv._tab
|
||||
}
|
||||
|
||||
func (rcv *GoodItems) Items(obj *GoodItem, j int) bool {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
|
||||
if o != 0 {
|
||||
x := rcv._tab.Vector(o)
|
||||
x += flatbuffers.UOffsetT(j) * 4
|
||||
x = rcv._tab.Indirect(x)
|
||||
obj.Init(rcv._tab.Bytes, x)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (rcv *GoodItems) ItemsLength() int {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
|
||||
if o != 0 {
|
||||
return rcv._tab.VectorLen(o)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func GoodItemsStart(builder *flatbuffers.Builder) {
|
||||
builder.StartObject(1)
|
||||
}
|
||||
func GoodItemsAddItems(builder *flatbuffers.Builder, items flatbuffers.UOffsetT) {
|
||||
builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(items), 0)
|
||||
}
|
||||
func GoodItemsStartItemsVector(builder *flatbuffers.Builder, numElems int) flatbuffers.UOffsetT {
|
||||
return builder.StartVector(4, numElems, 4)
|
||||
}
|
||||
func GoodItemsEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
return builder.EndObject()
|
||||
}
|
||||
140
internal/encoding/fbs/helpers.go
Normal file
140
internal/encoding/fbs/helpers.go
Normal file
@ -0,0 +1,140 @@
|
||||
package fbs
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"git.loyso.art/frx/eway/internal/entity"
|
||||
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
)
|
||||
|
||||
var builderPool = sync.Pool{
|
||||
New: func() any {
|
||||
builder := flatbuffers.NewBuilder(1024)
|
||||
return builder
|
||||
},
|
||||
}
|
||||
|
||||
func getBuilder() *flatbuffers.Builder {
|
||||
builder := builderPool.Get().(*flatbuffers.Builder)
|
||||
return builder
|
||||
}
|
||||
|
||||
func putBuilder(builder *flatbuffers.Builder) {
|
||||
builder.Reset()
|
||||
builderPool.Put(builder)
|
||||
}
|
||||
|
||||
func MakeDomainGoodItems(in ...entity.GoodsItem) []byte {
|
||||
builder := getBuilder()
|
||||
defer putBuilder(builder)
|
||||
|
||||
offsets := make([]flatbuffers.UOffsetT, 0, len(in))
|
||||
for _, item := range in {
|
||||
inFB := makeDomainGoodItem(builder, item)
|
||||
offsets = append(offsets, inFB)
|
||||
}
|
||||
|
||||
GoodItemsStartItemsVector(builder, len(offsets))
|
||||
for i := len(offsets) - 1; i >= 0; i-- {
|
||||
builder.PrependUOffsetT(offsets[i])
|
||||
}
|
||||
goodItemsVec := builder.EndVector(len(offsets))
|
||||
|
||||
GoodItemsStart(builder)
|
||||
GoodItemsAddItems(builder, goodItemsVec)
|
||||
out := GoodItemsEnd(builder)
|
||||
builder.Finish(out)
|
||||
return builder.FinishedBytes()
|
||||
}
|
||||
|
||||
func MakeDomainGoodItemFinished(in entity.GoodsItem) []byte {
|
||||
builder := getBuilder()
|
||||
defer putBuilder(builder)
|
||||
|
||||
item := makeDomainGoodItem(builder, in)
|
||||
builder.Finish(item)
|
||||
|
||||
return builder.FinishedBytes()
|
||||
}
|
||||
|
||||
func makeDomainGoodItem(builder *flatbuffers.Builder, in entity.GoodsItem) flatbuffers.UOffsetT {
|
||||
sku := builder.CreateString(in.Articul)
|
||||
photo := builder.CreateString(in.Photo)
|
||||
name := builder.CreateString(in.Name)
|
||||
desc := builder.CreateString(in.Description)
|
||||
var cat flatbuffers.UOffsetT
|
||||
if in.Category != "" {
|
||||
cat = builder.CreateString(in.Category)
|
||||
}
|
||||
t := builder.CreateString(in.Type)
|
||||
producer := builder.CreateString(in.Producer)
|
||||
|
||||
GoodItemStart(builder)
|
||||
GoodItemAddSku(builder, sku)
|
||||
GoodItemAddPhoto(builder, photo)
|
||||
GoodItemAddName(builder, name)
|
||||
GoodItemAddDescription(builder, desc)
|
||||
if in.Category != "" {
|
||||
GoodItemAddCategory(builder, cat)
|
||||
}
|
||||
GoodItemAddType(builder, t)
|
||||
GoodItemAddProducer(builder, producer)
|
||||
GoodItemAddPack(builder, int16(in.Pack))
|
||||
GoodItemAddStep(builder, int16(in.Step))
|
||||
GoodItemAddPrice(builder, float32(in.Price))
|
||||
GoodItemAddTariff(builder, float32(in.TariffPrice))
|
||||
GoodItemAddCart(builder, int64(in.Cart))
|
||||
GoodItemAddStock(builder, int16(in.Stock))
|
||||
|
||||
return GoodItemEnd(builder)
|
||||
}
|
||||
|
||||
func ParseGoodsItem(data []byte) (item entity.GoodsItem) {
|
||||
itemFBS := GetRootAsGoodItem(data, 0)
|
||||
item.Articul = string(itemFBS.Sku())
|
||||
item.Photo = string(itemFBS.Photo())
|
||||
item.Name = string(itemFBS.Name())
|
||||
item.Description = string(itemFBS.Description())
|
||||
if value := itemFBS.Category(); value != nil {
|
||||
item.Category = string(value)
|
||||
}
|
||||
item.Type = string(itemFBS.Type())
|
||||
item.Producer = string(itemFBS.Producer())
|
||||
item.Pack = int(itemFBS.Pack())
|
||||
item.Step = int(itemFBS.Step())
|
||||
item.Price = float64(itemFBS.Price())
|
||||
item.TariffPrice = float64(itemFBS.Tariff())
|
||||
item.Cart = itemFBS.Cart()
|
||||
item.Stock = int(itemFBS.Stock())
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
func ParseCategory(data []byte) (category entity.Category) {
|
||||
categoryFBS := GetRootAsCategory(data, 0)
|
||||
category.ID = categoryFBS.Id()
|
||||
category.Name = string(categoryFBS.Name())
|
||||
|
||||
return category
|
||||
}
|
||||
|
||||
func MakeCategoryFinished(category entity.Category) []byte {
|
||||
builder := getBuilder()
|
||||
defer putBuilder(builder)
|
||||
|
||||
offset := makeCategory(builder, category)
|
||||
builder.Finish(offset)
|
||||
|
||||
return builder.FinishedBytes()
|
||||
}
|
||||
|
||||
func makeCategory(builder *flatbuffers.Builder, category entity.Category) flatbuffers.UOffsetT {
|
||||
name := builder.CreateString(category.Name)
|
||||
|
||||
CategoryStart(builder)
|
||||
CategoryAddId(builder, category.ID)
|
||||
CategoryAddName(builder, name)
|
||||
|
||||
return CategoryEnd(builder)
|
||||
}
|
||||
6
internal/entity/category.go
Normal file
6
internal/entity/category.go
Normal file
@ -0,0 +1,6 @@
|
||||
package entity
|
||||
|
||||
type Category struct {
|
||||
ID int64
|
||||
Name string
|
||||
}
|
||||
11
internal/entity/error.go
Normal file
11
internal/entity/error.go
Normal file
@ -0,0 +1,11 @@
|
||||
package entity
|
||||
|
||||
type SimpleError string
|
||||
|
||||
func (err SimpleError) Error() string {
|
||||
return string(err)
|
||||
}
|
||||
|
||||
const (
|
||||
ErrNotFound SimpleError = "not found"
|
||||
)
|
||||
122
internal/entity/gooditem.go
Normal file
122
internal/entity/gooditem.go
Normal file
@ -0,0 +1,122 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type GoodsItem struct {
|
||||
Articul string `json:"sku"`
|
||||
Photo string `json:"photo"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category"`
|
||||
Type string `json:"type"`
|
||||
Producer string `json:"producer"`
|
||||
Pack int `json:"pack"`
|
||||
Step int `json:"step"`
|
||||
Price float64 `json:"price"`
|
||||
TariffPrice float64 `json:"tariff_price"`
|
||||
Cart int64 `json:"cart"`
|
||||
Stock int `json:"stock"`
|
||||
}
|
||||
|
||||
type GoodsItemRaw struct {
|
||||
SKU string // or articul
|
||||
Photo string
|
||||
Certificate string
|
||||
Configurator []string
|
||||
Name []string // first element is name, second is description
|
||||
Category string
|
||||
Type string
|
||||
Producer string
|
||||
Storage string // ?
|
||||
Pack string // like "20 шт."
|
||||
Step string
|
||||
Price string // float actually
|
||||
TariffPrice string // float
|
||||
SpecialOffers []any
|
||||
Cart float64
|
||||
Other string
|
||||
}
|
||||
|
||||
type MappedGoodsRemnants map[int]GoodsRemnant
|
||||
type GoodsRemnant [4]int32
|
||||
|
||||
func ExtractProductIDs(items []GoodsItem) (out []int) {
|
||||
out = make([]int, 0, len(items))
|
||||
for _, item := range items {
|
||||
out = append(out, int(item.Cart))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func MakeGoodsItem(
|
||||
gi GoodsItemRaw,
|
||||
remnants MappedGoodsRemnants,
|
||||
) (out GoodsItem, err error) {
|
||||
var name, desc string
|
||||
var pack, step int
|
||||
var price, tariffPrice float64
|
||||
|
||||
if len(gi.Name) >= 2 {
|
||||
name = gi.Name[0]
|
||||
desc = gi.Name[1]
|
||||
}
|
||||
|
||||
fixSpace := func(in string) string {
|
||||
return strings.ReplaceAll(in, " ", "")
|
||||
}
|
||||
|
||||
price, err = strconv.ParseFloat(fixSpace(gi.Price), 64)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("parsing price: %w", err)
|
||||
}
|
||||
tariffPrice, err = strconv.ParseFloat(fixSpace(gi.TariffPrice), 64)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("parsing tariff_price: %w", err)
|
||||
}
|
||||
|
||||
getDigits := func(in string) string {
|
||||
var sb strings.Builder
|
||||
sb.Grow(len(in))
|
||||
for _, c := range in {
|
||||
if unicode.IsDigit(c) {
|
||||
sb.WriteRune(c)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
const countTemplate = "%d"
|
||||
_, err = fmt.Sscanf(getDigits(gi.Pack), countTemplate, &pack)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("getting pack count (%s): %w", gi.Pack, err)
|
||||
}
|
||||
_, err = fmt.Sscanf(getDigits(gi.Step), countTemplate, &step)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("getting step count (%s): %w", gi.Step, err)
|
||||
}
|
||||
|
||||
return GoodsItem{
|
||||
Articul: gi.SKU,
|
||||
Photo: gi.Photo,
|
||||
Name: name,
|
||||
Description: desc,
|
||||
Category: gi.Category,
|
||||
Type: gi.Type,
|
||||
Producer: gi.Producer,
|
||||
Pack: pack,
|
||||
Step: step,
|
||||
Price: price,
|
||||
TariffPrice: tariffPrice,
|
||||
Cart: int64(gi.Cart),
|
||||
Stock: int(remnants[int(gi.Cart)][0]),
|
||||
}, nil
|
||||
}
|
||||
23
internal/entity/repository.go
Normal file
23
internal/entity/repository.go
Normal file
@ -0,0 +1,23 @@
|
||||
package entity
|
||||
|
||||
import "context"
|
||||
|
||||
type GoodsItemRepository interface {
|
||||
ListIter(context.Context, int) (<-chan GoodsItem, error)
|
||||
List(context.Context) ([]GoodsItem, error)
|
||||
Get(context.Context, string) (GoodsItem, error)
|
||||
GetByCart(context.Context, int64) (GoodsItem, error)
|
||||
|
||||
UpsertMany(context.Context, ...GoodsItem) ([]GoodsItem, error)
|
||||
}
|
||||
|
||||
type CategoryRepository interface {
|
||||
List(context.Context) ([]Category, error)
|
||||
Get(context.Context, int64) (Category, error)
|
||||
|
||||
Create(ctx context.Context, name string) (Category, error)
|
||||
}
|
||||
|
||||
type Mapper interface {
|
||||
CategoryNameToID(context.Context, string) (int64, error)
|
||||
}
|
||||
228
internal/interconnect/eway/client.go
Normal file
228
internal/interconnect/eway/client.go
Normal file
@ -0,0 +1,228 @@
|
||||
package eway
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.loyso.art/frx/eway/internal/entity"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
http *resty.Client
|
||||
log zerolog.Logger
|
||||
}
|
||||
|
||||
func NewClientWithSession(sessionid, sessionuser string, log zerolog.Logger) client {
|
||||
cookies := []*http.Cookie{
|
||||
{
|
||||
Name: "session_id",
|
||||
Value: sessionid,
|
||||
Domain: "eway.elevel.ru",
|
||||
HttpOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "session_user",
|
||||
Value: sessionuser,
|
||||
Domain: "eway.elevel.ru",
|
||||
HttpOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "contract",
|
||||
Value: "6101",
|
||||
Domain: "eway.elevel.ru",
|
||||
HttpOnly: true,
|
||||
},
|
||||
}
|
||||
|
||||
httpclient := resty.New().
|
||||
SetDebug(false).
|
||||
SetCookies(cookies).
|
||||
SetBaseURL("https://eway.elevel.ru/api")
|
||||
|
||||
return client{
|
||||
http: httpclient,
|
||||
log: log.With().Str("client", "eway").Logger(),
|
||||
}
|
||||
}
|
||||
|
||||
type getGoodsNewOrder struct {
|
||||
Column int
|
||||
Dir string
|
||||
}
|
||||
|
||||
type GetGoodsNewParams struct {
|
||||
Draw int
|
||||
Order getGoodsNewOrder
|
||||
Start int
|
||||
// 100 is max
|
||||
Length int
|
||||
SearchInStocks bool
|
||||
RemmantsAtleast int
|
||||
}
|
||||
|
||||
type getGoodsNewResponse struct {
|
||||
Draw string `json:"draw"`
|
||||
RecordsFiltered int `json:"recordsFiltered"`
|
||||
RecordsTotal int `json:"recordsTotal"`
|
||||
Data [][]any `json:"data"`
|
||||
Replacement bool `json:"replacement"`
|
||||
}
|
||||
|
||||
type goodRemnant [4]int
|
||||
|
||||
func parseGoodItem(items []any) (out entity.GoodsItemRaw) {
|
||||
valueOf := reflect.ValueOf(&out).Elem()
|
||||
typeOf := valueOf.Type()
|
||||
numField := valueOf.NumField()
|
||||
|
||||
for i := 0; i < numField; i++ {
|
||||
field := valueOf.Field(i)
|
||||
fieldType := typeOf.Field(i)
|
||||
if fieldType.Type.Kind() == reflect.Slice &&
|
||||
field.Type().Elem().Kind() != reflect.String {
|
||||
continue
|
||||
}
|
||||
|
||||
itemValue := reflect.ValueOf(items[i])
|
||||
if items[i] == nil ||
|
||||
(itemValue.CanAddr() && itemValue.IsNil()) ||
|
||||
itemValue.IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
if field.Type().Kind() != itemValue.Type().Kind() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Dirty hack that accepts only strings.
|
||||
if field.Type().Kind() == reflect.Slice {
|
||||
values := items[i].([]any)
|
||||
elemSlice := reflect.MakeSlice(typeOf.Field(i).Type, 0, field.Len())
|
||||
for _, value := range values {
|
||||
valueStr, ok := value.(string)
|
||||
if ok {
|
||||
elemSlice = reflect.Append(elemSlice, reflect.ValueOf(valueStr))
|
||||
}
|
||||
}
|
||||
|
||||
field.Set(elemSlice)
|
||||
continue
|
||||
}
|
||||
|
||||
field.Set(itemValue)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func mapResponseByOrder(response getGoodsNewResponse) (items []entity.GoodsItemRaw) {
|
||||
for _, columns := range response.Data {
|
||||
gi := parseGoodItem(columns)
|
||||
items = append(items, gi)
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
func (c client) GetGoodsRemnants(
|
||||
ctx context.Context,
|
||||
productIDs []int,
|
||||
) (out entity.MappedGoodsRemnants, err error) {
|
||||
if len(productIDs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
productsStr := make([]string, 0, len(productIDs))
|
||||
for _, sku := range productIDs {
|
||||
productsStr = append(productsStr, strconv.Itoa(sku))
|
||||
}
|
||||
|
||||
resp, err := c.http.R().
|
||||
SetFormData(map[string]string{
|
||||
"products": strings.Join(productsStr, ","),
|
||||
}).
|
||||
SetDoNotParseResponse(true).
|
||||
Post("/goods_remnants")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting goods new: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
err = resp.RawBody().Close()
|
||||
if err != nil {
|
||||
c.log.Error().Err(err).Msg("unable to close body")
|
||||
}
|
||||
}()
|
||||
|
||||
if resp.IsError() {
|
||||
return nil, errors.New("request was not successful")
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp.RawBody())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading raw body: %w", err)
|
||||
}
|
||||
|
||||
c.log.Debug().RawJSON("response", data).Msg("body prepared")
|
||||
|
||||
out = make(entity.MappedGoodsRemnants, len(productIDs))
|
||||
err = json.NewDecoder(bytes.NewReader(data)).Decode(&out)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decoding body: %w", err)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c client) GetGoodsNew(
|
||||
ctx context.Context,
|
||||
params GetGoodsNewParams,
|
||||
) (items []entity.GoodsItemRaw, total int, err error) {
|
||||
var response getGoodsNewResponse
|
||||
resp, err := c.http.R().
|
||||
SetFormData(map[string]string{
|
||||
"draw": strconv.Itoa(params.Draw),
|
||||
"start": strconv.Itoa(params.Start),
|
||||
"length": strconv.Itoa(params.Length),
|
||||
"order[0][column]": "14",
|
||||
"order[0][dir]": "desc",
|
||||
"search[value]": "",
|
||||
"search[regex]": "false",
|
||||
"search_in_stocks": "on",
|
||||
"remnants_atleast": "5",
|
||||
}).
|
||||
SetQueryParam("category_id", "0").
|
||||
SetQueryParam("own", "26476"). // user id?
|
||||
SetDoNotParseResponse(true).
|
||||
Post("/goods_new")
|
||||
if err != nil {
|
||||
return nil, -1, fmt.Errorf("getting goods new: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
err = resp.RawBody().Close()
|
||||
if err != nil {
|
||||
c.log.Error().Err(err).Msg("unable to close body")
|
||||
}
|
||||
}()
|
||||
|
||||
if resp.IsError() {
|
||||
return nil, -1, errors.New("request was not successful")
|
||||
}
|
||||
|
||||
err = json.NewDecoder(resp.RawBody()).Decode(&response)
|
||||
if err != nil {
|
||||
return nil, -1, fmt.Errorf("decoding body: %w", err)
|
||||
}
|
||||
|
||||
return mapResponseByOrder(response), response.RecordsTotal, nil
|
||||
}
|
||||
40
internal/storage/inmemory/mapper.go
Normal file
40
internal/storage/inmemory/mapper.go
Normal file
@ -0,0 +1,40 @@
|
||||
package inmemory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.loyso.art/frx/eway/internal/entity"
|
||||
"git.loyso.art/frx/eway/internal/storage"
|
||||
)
|
||||
|
||||
type mapper struct {
|
||||
categoryIDByName map[string]int64
|
||||
base storage.Repository
|
||||
}
|
||||
|
||||
func NewMapper(ctx context.Context, r storage.Repository) (*mapper, error) {
|
||||
categories, err := r.Category().List(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing categories: %w", err)
|
||||
}
|
||||
|
||||
catsByName := make(map[string]int64, len(categories))
|
||||
for _, category := range categories {
|
||||
catsByName[category.Name] = category.ID
|
||||
}
|
||||
|
||||
return &mapper{
|
||||
categoryIDByName: catsByName,
|
||||
base: r,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mapper) CategoryNameToID(ctx context.Context, name string) (int64, error) {
|
||||
out, ok := m.categoryIDByName[name]
|
||||
if !ok {
|
||||
return 0, entity.ErrNotFound
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
8
internal/storage/storage.go
Normal file
8
internal/storage/storage.go
Normal file
@ -0,0 +1,8 @@
|
||||
package storage
|
||||
|
||||
import "git.loyso.art/frx/eway/internal/entity"
|
||||
|
||||
type Repository interface {
|
||||
Category() entity.CategoryRepository
|
||||
GoodsItem() entity.GoodsItemRepository
|
||||
}
|
||||
Reference in New Issue
Block a user