add handlers, storage(Postgres, sqlite) #3
@ -2,16 +2,16 @@ package handler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
cmsstorage "go.unistack.org/cms-service/storage"
|
cmsstorage "go.unistack.org/cms-service/storage"
|
||||||
"go.unistack.org/micro/v3"
|
"go.unistack.org/micro/v3"
|
||||||
"go.unistack.org/micro/v3/errors"
|
"go.unistack.org/micro/v3/errors"
|
||||||
"go.unistack.org/unistack-org/pkgdash/config"
|
"go.unistack.org/unistack-org/pkgdash/config"
|
||||||
pb "go.unistack.org/unistack-org/pkgdash/proto/go_generate"
|
pb "go.unistack.org/unistack-org/pkgdash/proto/go_generate"
|
||||||
|
"go.unistack.org/unistack-org/pkgdash/service/client_git"
|
||||||
"go.unistack.org/unistack-org/pkgdash/storage"
|
"go.unistack.org/unistack-org/pkgdash/storage"
|
||||||
"google.golang.org/protobuf/encoding/protojson"
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
@ -21,6 +21,9 @@ type Handler struct {
|
|||||||
writer writer
|
writer writer
|
||||||
protojson.MarshalOptions
|
protojson.MarshalOptions
|
||||||
protojson.UnmarshalOptions
|
protojson.UnmarshalOptions
|
||||||
|
|
||||||
|
git client_git.Client
|
||||||
|
chanUrl chan *pb.AddPackageRsp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) ListPackage(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) ListPackage(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -42,12 +45,39 @@ func (h *Handler) ListPackage(w http.ResponseWriter, r *http.Request) {
|
|||||||
h.writer.Response(ctx, w, rsp)
|
h.writer.Response(ctx, w, rsp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) UpdateInfo(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) UpdatePackage(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
logger := h.svc.Logger()
|
logger := h.svc.Logger()
|
||||||
logger.Debug(ctx, "Start UpdateInfo")
|
logger.Debug(ctx, "Start UpdatePackage")
|
||||||
|
|
||||||
// TODO
|
defer r.Body.Close()
|
||||||
|
all, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(ctx, err)
|
||||||
|
h.writer.Response(ctx, w, NewInternalError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rsp := new(pb.UpdatePackageRsp)
|
||||||
|
if err = h.Unmarshal(all, rsp); err != nil {
|
||||||
|
logger.Error(ctx, err)
|
||||||
|
h.writer.Response(ctx, w, NewUnmarshalError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = rsp.Validate(); err != nil {
|
||||||
|
logger.Error(ctx, err)
|
||||||
|
h.writer.Response(ctx, w, NewValidationError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = h.store.UpdatePackage(ctx, rsp); err != nil {
|
||||||
|
logger.Error(ctx, err)
|
||||||
|
h.writer.Response(ctx, w, NewInternalError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug(ctx, "Success finish UpdatePackage")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) AddComment(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) AddComment(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -111,21 +141,24 @@ func (h *Handler) AddPackage(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO:
|
if h.git.IsClose() {
|
||||||
// need logic for add module from go.mod
|
logger.Error(ctx, "chan is closed")
|
||||||
// err := setModules(req)
|
} else {
|
||||||
|
h.chanUrl <- req
|
||||||
if err = h.store.AddPackage(ctx, req); err != nil {
|
|
||||||
logger.Error(ctx, err)
|
|
||||||
h.writer.Response(ctx, w, NewInternalError(err))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug(ctx, "Success finish addPackage")
|
logger.Debug(ctx, "Success finish addPackage")
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandler(svc micro.Service, w writer) *Handler {
|
func NewHandler(svc micro.Service, w writer, client client_git.Client) *Handler {
|
||||||
return &Handler{svc: svc, writer: w}
|
h := &Handler{
|
||||||
|
svc: svc,
|
||||||
|
writer: w,
|
||||||
|
git: client,
|
||||||
|
}
|
||||||
|
h.EmitUnpopulated = true
|
||||||
|
h.UseProtoNames = false
|
||||||
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) Init(ctx context.Context) error {
|
func (h *Handler) Init(ctx context.Context) error {
|
||||||
@ -138,6 +171,8 @@ func (h *Handler) Init(ctx context.Context) error {
|
|||||||
return errors.New(config.ServiceName, "error init storage", 1)
|
return errors.New(config.ServiceName, "error init storage", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h.chanUrl = h.git.Run(ctx, st)
|
||||||
|
|
||||||
h.store = st
|
h.store = st
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
2
main.go
2
main.go
@ -65,7 +65,7 @@ func main() {
|
|||||||
cloneOpts.Depth = 1
|
cloneOpts.Depth = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cloneOpts.Validate(); err != nil {
|
if err = cloneOpts.Validate(); err != nil {
|
||||||
logger.Fatal(ctx, err)
|
logger.Fatal(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ type Package struct {
|
|||||||
ID int64 `db:"id" json:"id"` // package id
|
ID int64 `db:"id" json:"id"` // package id
|
||||||
Name string `db:"name" json:"name"` // service name, last component path
|
Name string `db:"name" json:"name"` // service name, last component path
|
||||||
URL string `db:"url" json:"url"` // scm url
|
URL string `db:"url" json:"url"` // scm url
|
||||||
Modules []int64 `db:"modules" json:"modules"` // parsed go.mod modules
|
Modules []uint64 `db:"modules" json:"modules"` // parsed go.mod modules
|
||||||
Issues []int64 `db:"issues" json:"issues,omitempty"` // issues list
|
Issues []int64 `db:"issues" json:"issues,omitempty"` // issues list
|
||||||
Comments []int64 `db:"comments" json:"comments,omitempty"`
|
Comments []int64 `db:"comments" json:"comments,omitempty"`
|
||||||
}
|
}
|
||||||
@ -20,6 +20,7 @@ type Module struct {
|
|||||||
Name string `db:"name"` // module name
|
Name string `db:"name"` // module name
|
||||||
Version string `db:"version"` // module
|
Version string `db:"version"` // module
|
||||||
Package int64 `db:"package"`
|
Package int64 `db:"package"`
|
||||||
|
LastVersion string `db:"last_version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Issue struct {
|
type Issue struct {
|
||||||
|
218
service/client_git/client.go
Normal file
218
service/client_git/client.go
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
package client_git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"go.unistack.org/unistack-org/pkgdash/internal"
|
||||||
|
"go.unistack.org/unistack-org/pkgdash/models"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-git/go-git/v5"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/filemode"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/object"
|
||||||
|
"github.com/go-git/go-git/v5/storage/memory"
|
||||||
|
"go.unistack.org/micro/v3/logger"
|
||||||
|
pb "go.unistack.org/unistack-org/pkgdash/proto/go_generate"
|
||||||
|
"go.unistack.org/unistack-org/pkgdash/storage"
|
||||||
|
"golang.org/x/mod/modfile"
|
||||||
|
"golang.org/x/mod/module"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client interface {
|
||||||
|
Run(ctx context.Context, st storage.Storage) chan *pb.AddPackageRsp
|
||||||
|
IsClose() bool
|
||||||
|
Done() <-chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
worker chan *pb.AddPackageRsp
|
||||||
|
closed bool
|
||||||
|
lock chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(cap uint) Client {
|
||||||
|
return &client{
|
||||||
|
make(chan *pb.AddPackageRsp, cap),
|
||||||
|
false,
|
||||||
|
make(chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) Run(ctx context.Context, st storage.Storage) chan *pb.AddPackageRsp {
|
||||||
|
go func() {
|
||||||
|
defer close(c.worker)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case data := <-c.worker:
|
||||||
|
go func() {
|
||||||
|
runner(ctx, st, data)
|
||||||
|
}()
|
||||||
|
case <-ctx.Done():
|
||||||
|
logger.Info(ctx, ctx.Err())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return c.worker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) IsClose() bool {
|
||||||
|
return c.closed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done for locked goroutine
|
||||||
|
func (c *client) Done() <-chan struct{} {
|
||||||
|
return c.lock
|
||||||
|
}
|
||||||
|
|
||||||
|
func runner(ctx context.Context, st storage.Storage, rsp *pb.AddPackageRsp) {
|
||||||
|
modules, err := getGoModule(ctx, rsp.Url.Value)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Infof(ctx, "success get list mod", modules)
|
||||||
|
|
||||||
|
if rsp.Modules, err = st.InsertButchModules(ctx, modules); err != nil {
|
||||||
|
logger.Error(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = st.AddPackage(ctx, rsp); err != nil {
|
||||||
|
logger.Error(ctx, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGoModule(ctx context.Context, gitUrl string) ([]models.Module, error) {
|
||||||
|
u, err := url.Parse(gitUrl)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var rev string
|
||||||
|
if idx := strings.Index(u.Path, "@"); idx > 0 {
|
||||||
|
rev = u.Path[idx+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
cloneOpts := &git.CloneOptions{
|
||||||
|
URL: gitUrl,
|
||||||
|
Progress: os.Stdout,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rev) == 0 {
|
||||||
|
cloneOpts.SingleBranch = true
|
||||||
|
cloneOpts.Depth = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = cloneOpts.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, err := git.CloneContext(ctx, memory.NewStorage(), nil, cloneOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ref, err := repo.Head()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get head: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
commit, err := repo.CommitObject(ref.Hash())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get commit: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tree, err := commit.Tree()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
unique := make(map[string]models.Module)
|
||||||
|
var mvs []module.Version
|
||||||
|
err = tree.Files().ForEach(func(file *object.File) error {
|
||||||
|
if file == nil {
|
||||||
|
err = errors.New("file pointer is nil")
|
||||||
|
logger.Error(ctx, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch file.Mode {
|
||||||
|
case filemode.Regular:
|
||||||
|
if strings.HasSuffix(file.Name, "go.mod") {
|
||||||
|
if mvs, err = Direct(file); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := range mvs {
|
||||||
|
unique[mvs[i].Path] = models.Module{
|
||||||
|
Name: mvs[i].Path,
|
||||||
|
Version: mvs[i].Version,
|
||||||
|
LastVersion: mvs[i].Version,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal.Updates(internal.UpdateOptions{
|
||||||
|
Pre: false,
|
||||||
|
Major: false,
|
||||||
|
Cached: false,
|
||||||
|
Modules: mvs,
|
||||||
|
OnUpdate: func(u internal.Update) {
|
||||||
|
if u.Err != nil {
|
||||||
|
logger.Errorf(ctx, "%s: failed: %v\n", u.Module.Path, u.Err)
|
||||||
|
} else {
|
||||||
|
val := unique[u.Module.Path]
|
||||||
|
val.LastVersion = u.Version
|
||||||
|
unique[u.Module.Path] = val
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
result := make([]models.Module, 0, len(unique))
|
||||||
|
for _, v := range unique {
|
||||||
|
result = append(result, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(result, func(i, j int) bool {
|
||||||
|
return result[i].Name < result[j].Name
|
||||||
|
})
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Direct(file *object.File) ([]module.Version, error) {
|
||||||
|
r, err := file.Reader()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
data, err := io.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
modfile, err := modfile.ParseLax("go.mod", data, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var mods []module.Version
|
||||||
|
for _, req := range modfile.Require {
|
||||||
|
// if !req.Indirect {
|
||||||
|
mods = append(mods, req.Mod)
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
sort.Slice(mods, func(i, j int) bool {
|
||||||
|
return mods[i].Path < mods[j].Path
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
return mods, nil
|
||||||
|
}
|
49
service/client_git/client_test.go
Normal file
49
service/client_git/client_test.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package client_git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
pb "go.unistack.org/unistack-org/pkgdash/proto/go_generate"
|
||||||
|
"go.unistack.org/unistack-org/pkgdash/storage"
|
||||||
|
"go.unistack.org/unistack-org/pkgdash/storage/postgres"
|
||||||
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClient(t *testing.T) {
|
||||||
|
dsn := fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=disable", "test", "123", "localhost", "5432", "postgres")
|
||||||
|
conn, err := sql.Open("postgres", dsn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
if err = conn.Ping(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
st, err := postgres.NewStorage(conn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s, ok := st.(storage.Storage)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("typecast error")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
_ = cancel
|
||||||
|
cli := NewClient(1)
|
||||||
|
|
||||||
|
ch := cli.Run(ctx, s)
|
||||||
|
|
||||||
|
data := &pb.AddPackageRsp{
|
||||||
|
Name: wrapperspb.String("test"),
|
||||||
|
Url: wrapperspb.String("https://github.com/dantedenis/service_history.git"),
|
||||||
|
}
|
||||||
|
|
||||||
|
ch <- data
|
||||||
|
|
||||||
|
<-cli.Done()
|
||||||
|
}
|
@ -6,6 +6,7 @@ import (
|
|||||||
intcfg "go.unistack.org/unistack-org/pkgdash/config"
|
intcfg "go.unistack.org/unistack-org/pkgdash/config"
|
||||||
"go.unistack.org/unistack-org/pkgdash/handler"
|
"go.unistack.org/unistack-org/pkgdash/handler"
|
||||||
"go.unistack.org/unistack-org/pkgdash/handler/encoders"
|
"go.unistack.org/unistack-org/pkgdash/handler/encoders"
|
||||||
|
"go.unistack.org/unistack-org/pkgdash/service/client_git"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
//pbmicro "go.unistack.org/unistack-org/pkgdash/proto/micro"
|
//pbmicro "go.unistack.org/unistack-org/pkgdash/proto/micro"
|
||||||
@ -53,7 +54,7 @@ func NewService(ctx context.Context) (micro.Service, error) {
|
|||||||
logger.Fatalf(ctx, "failed init writer: %v", err)
|
logger.Fatalf(ctx, "failed init writer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
h := handler.NewHandler(svc, writer)
|
h := handler.NewHandler(svc, writer, client_git.NewClient(5))
|
||||||
|
|
||||||
if err := svc.Init(
|
if err := svc.Init(
|
||||||
micro.AfterStart(func(_ context.Context) error {
|
micro.AfterStart(func(_ context.Context) error {
|
||||||
@ -128,7 +129,7 @@ func NewService(ctx context.Context) (micro.Service, error) {
|
|||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
mux.HandleFunc("/listPackage", handler.Methods(http.MethodGet, h.ListPackage))
|
mux.HandleFunc("/listPackage", handler.Methods(http.MethodGet, h.ListPackage))
|
||||||
mux.HandleFunc("/updateInfo", handler.Methods(http.MethodPost, h.UpdateInfo))
|
mux.HandleFunc("/updateInfo", handler.Methods(http.MethodPost, h.UpdatePackage))
|
||||||
mux.HandleFunc("/addComment", handler.Methods(http.MethodPut, h.AddComment))
|
mux.HandleFunc("/addComment", handler.Methods(http.MethodPut, h.AddComment))
|
||||||
mux.HandleFunc("/addPackage", handler.Methods(http.MethodPost, h.AddPackage))
|
mux.HandleFunc("/addPackage", handler.Methods(http.MethodPost, h.AddPackage))
|
||||||
|
|
||||||
|
@ -14,7 +14,8 @@ create table if not exists comment (
|
|||||||
create table if not exists module (
|
create table if not exists module (
|
||||||
id serial not null unique primary key ,
|
id serial not null unique primary key ,
|
||||||
name varchar not null ,
|
name varchar not null ,
|
||||||
version varchar not null
|
version varchar not null ,
|
||||||
|
last_version varchar not null
|
||||||
);
|
);
|
||||||
|
|
||||||
create table if not exists issue (
|
create table if not exists issue (
|
||||||
@ -34,4 +35,5 @@ create table if not exists package (
|
|||||||
comments integer[] default '{}'::integer[]
|
comments integer[] default '{}'::integer[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create unique index module_info on module(name, version);
|
||||||
|
|
||||||
|
@ -19,5 +19,10 @@ update package set comments = array_append(comments, (select * from insert_comm)
|
|||||||
`
|
`
|
||||||
queryAddPackage = `
|
queryAddPackage = `
|
||||||
insert into package(name, url, modules) values ($1, $2, $3);
|
insert into package(name, url, modules) values ($1, $2, $3);
|
||||||
|
`
|
||||||
|
queryInsMsgGetIDs = `
|
||||||
|
insert into module(name, version, last_version) values
|
||||||
|
%s
|
||||||
|
returning id;
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
@ -5,7 +5,9 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"embed"
|
"embed"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
pb "go.unistack.org/unistack-org/pkgdash/proto/go_generate"
|
pb "go.unistack.org/unistack-org/pkgdash/proto/go_generate"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/golang-migrate/migrate/v4"
|
"github.com/golang-migrate/migrate/v4"
|
||||||
mpgx "github.com/golang-migrate/migrate/v4/database/pgx"
|
mpgx "github.com/golang-migrate/migrate/v4/database/pgx"
|
||||||
@ -87,6 +89,10 @@ func (s *Postgres) MigrateDown() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Postgres) UpdatePackage(ctx context.Context, rsp *pb.UpdatePackageRsp) error {
|
||||||
|
panic("need implement")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Postgres) ListPackage(ctx context.Context) (models.ListPackage, error) {
|
func (s *Postgres) ListPackage(ctx context.Context) (models.ListPackage, error) {
|
||||||
rows, err := s.db.QueryContext(ctx, queryListPackage)
|
rows, err := s.db.QueryContext(ctx, queryListPackage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -175,3 +181,57 @@ func (s *Postgres) AddPackage(ctx context.Context, rsp *pb.AddPackageRsp) error
|
|||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Postgres) InsertButchModules(ctx context.Context, rsp []models.Module) ([]uint64, error) {
|
||||||
|
tx, err := s.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||||
|
logger.Errorf(ctx, "AddPackage: unable to rollback: %v", rollbackErr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = tx.Commit()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
query := generateQuery(rsp)
|
||||||
|
|
||||||
|
rows, err := tx.QueryContext(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err = rows.Close(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = rows.Err()
|
||||||
|
}()
|
||||||
|
|
||||||
|
result := make([]uint64, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
tmp := uint64(0)
|
||||||
|
if err = rows.Scan(&tmp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateQuery(rsp []models.Module) string {
|
||||||
|
const pattern = `%c('%s', '%s', '%s')`
|
||||||
|
build := strings.Builder{}
|
||||||
|
comma := ' '
|
||||||
|
for i := range rsp {
|
||||||
|
str := fmt.Sprintf(pattern, comma, rsp[i].Name, rsp[i].Version, rsp[i].LastVersion)
|
||||||
|
build.WriteString(str)
|
||||||
|
comma = ','
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(queryInsMsgGetIDs, build.String())
|
||||||
|
}
|
||||||
|
25
storage/postgres/storage_test.go
Normal file
25
storage/postgres/storage_test.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go.unistack.org/unistack-org/pkgdash/models"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGenerate(t *testing.T) {
|
||||||
|
m := []models.Module{
|
||||||
|
{
|
||||||
|
1, "test", "1.2.3", 2, "23.31",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
1, "321test", "1.3", 4, "2111.31",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
1, "testabcd", "1.2.3", 2, "153453.31",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
str := generateQuery(m)
|
||||||
|
|
||||||
|
fmt.Println(str)
|
||||||
|
}
|
@ -26,8 +26,10 @@ type Storage interface {
|
|||||||
cmsstorage.Migrator
|
cmsstorage.Migrator
|
||||||
|
|
||||||
ListPackage(ctx context.Context) (models.ListPackage, error)
|
ListPackage(ctx context.Context) (models.ListPackage, error)
|
||||||
|
UpdatePackage(ctx context.Context, rsp *pb.UpdatePackageRsp) error
|
||||||
AddComment(ctx context.Context, rsp *pb.AddCommentRsp) error
|
AddComment(ctx context.Context, rsp *pb.AddCommentRsp) error
|
||||||
AddPackage(ctx context.Context, rsp *pb.AddPackageRsp) error
|
AddPackage(ctx context.Context, rsp *pb.AddPackageRsp) error
|
||||||
|
InsertButchModules(ctx context.Context, rsp []models.Module) ([]uint64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStorage(name string, db *sql.DB) (interface{}, error) {
|
func NewStorage(name string, db *sql.DB) (interface{}, error) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user