2023-08-11 20:12:15 +03:00
|
|
|
package sqlite
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"database/sql"
|
|
|
|
"embed"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/golang-migrate/migrate/v4"
|
|
|
|
"github.com/golang-migrate/migrate/v4/database/sqlite"
|
|
|
|
"github.com/golang-migrate/migrate/v4/source/iofs"
|
|
|
|
"github.com/lib/pq"
|
2023-08-11 21:45:08 +03:00
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
|
|
"go.unistack.org/micro/v4/logger"
|
2023-08-11 20:12:15 +03:00
|
|
|
"go.unistack.org/unistack-org/pkgdash/config"
|
|
|
|
"go.unistack.org/unistack-org/pkgdash/models"
|
2023-08-11 21:45:08 +03:00
|
|
|
pb "go.unistack.org/unistack-org/pkgdash/proto"
|
2023-08-11 20:12:15 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
pathMigration = `migrations/sqlite`
|
|
|
|
)
|
|
|
|
|
|
|
|
type Sqlite struct {
|
|
|
|
db *sql.DB
|
|
|
|
fs embed.FS
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewStorage(db *sql.DB) (interface{}, error) {
|
|
|
|
return &Sqlite{db: db}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewStorageFS(fs embed.FS) func(*sql.DB) (interface{}, error) {
|
|
|
|
return func(db *sql.DB) (interface{}, error) {
|
|
|
|
return &Sqlite{db: db, fs: fs}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Sqlite) MigrateUp() error {
|
|
|
|
driver, err := sqlite.WithInstance(s.db, &sqlite.Config{
|
|
|
|
MigrationsTable: sqlite.DefaultMigrationsTable,
|
|
|
|
DatabaseName: config.ServiceName,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
source, err := iofs.New(s.fs, pathMigration)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: pass own logger
|
|
|
|
m, err := migrate.NewWithInstance("fs", source, config.ServiceName, driver)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = m.Up(); err != nil && !errors.Is(err, migrate.ErrNoChange) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Sqlite) MigrateDown() error {
|
|
|
|
driver, err := sqlite.WithInstance(s.db, &sqlite.Config{
|
|
|
|
MigrationsTable: sqlite.DefaultMigrationsTable,
|
|
|
|
DatabaseName: config.ServiceName,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
source, err := iofs.New(s.fs, pathMigration)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: pass own logger
|
|
|
|
m, err := migrate.NewWithInstance("fs", source, config.ServiceName, driver)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = m.Down(); err != nil && !errors.Is(err, migrate.ErrNoChange) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Sqlite) UpdatePackage(ctx context.Context, req *pb.UpdatePackageReq) error {
|
|
|
|
panic("need implement")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Sqlite) ListPackage(ctx context.Context) (models.ListPackage, error) {
|
|
|
|
rows, err := s.db.QueryContext(ctx, queryListPackage)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if err = rows.Close(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = rows.Err()
|
|
|
|
}()
|
|
|
|
|
|
|
|
result := make([]*models.Package, 0)
|
|
|
|
for rows.Next() {
|
|
|
|
tmp := &models.Package{}
|
|
|
|
if err = rows.Scan(
|
|
|
|
&tmp.ID,
|
|
|
|
&tmp.Name,
|
|
|
|
&tmp.URL,
|
|
|
|
pq.Array(&tmp.Comments),
|
|
|
|
); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
2023-08-12 19:26:50 +03:00
|
|
|
func (s *Sqlite) AddComment(ctx context.Context, req *pb.AddCommentReq) (id uint64, err error) {
|
2023-08-11 20:12:15 +03:00
|
|
|
tx, err := s.db.BeginTx(ctx, nil)
|
|
|
|
if err != nil {
|
2023-08-12 19:26:50 +03:00
|
|
|
return 0, err
|
2023-08-11 20:12:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
|
|
|
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
|
|
|
logger.Errorf(ctx, "AddComment: unable to rollback: %v", rollbackErr)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err = tx.Commit()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2023-08-12 19:26:50 +03:00
|
|
|
if err = tx.QueryRowContext(ctx, queryAddComment, req.Text, req.IdPackage).Scan(&id); err != nil {
|
|
|
|
return id, err
|
2023-08-11 20:12:15 +03:00
|
|
|
}
|
2023-08-12 19:26:50 +03:00
|
|
|
|
|
|
|
return id, err
|
2023-08-11 20:12:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Sqlite) AddPackage(ctx context.Context, req *pb.AddPackageReq) error {
|
|
|
|
tx, err := s.db.BeginTx(ctx, nil)
|
|
|
|
if err != nil {
|
|
|
|
return 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()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2023-08-12 19:26:50 +03:00
|
|
|
res, err := tx.ExecContext(ctx, queryAddPackage, req.Name, req.Url, pq.Array(req.Modules))
|
2023-08-11 20:12:15 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if aff, affErr := res.RowsAffected(); err != nil {
|
|
|
|
err = affErr
|
|
|
|
} else if aff == 0 {
|
|
|
|
err = errors.New("rows affected is 0")
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Sqlite) InsertButchModules(ctx context.Context, req []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(req)
|
|
|
|
|
|
|
|
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 (s *Sqlite) GetModule(ctx context.Context, req *pb.GetModuleReq) (result models.ListModule, err error) {
|
|
|
|
query := ""
|
|
|
|
if len(req.Id) < 1 {
|
|
|
|
query = fmt.Sprintf(queryGetModule, "() or 1=1")
|
|
|
|
} else {
|
|
|
|
query = fmt.Sprintf(queryGetModule, generateArrayIneq(len(req.Id)))
|
|
|
|
}
|
|
|
|
|
|
|
|
rows, err := s.db.QueryContext(ctx, query, convertSliceUInt(req.Id...)...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
if err = rows.Close(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = rows.Err()
|
|
|
|
}()
|
|
|
|
|
|
|
|
for rows.Next() {
|
|
|
|
tmp := &models.Module{}
|
|
|
|
if err = rows.Scan(
|
|
|
|
&tmp.ID,
|
|
|
|
&tmp.Name,
|
|
|
|
&tmp.Version,
|
|
|
|
&tmp.LastVersion,
|
|
|
|
); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
result = append(result, tmp)
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func convertSliceUInt(arg ...uint64) []interface{} {
|
|
|
|
result := make([]interface{}, 0, len(arg))
|
|
|
|
for i := range arg {
|
|
|
|
result = append(result, arg[i])
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
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())
|
|
|
|
}
|
|
|
|
|
|
|
|
func generateArrayIneq(count int) string {
|
|
|
|
return "(?" + strings.Repeat(",?", count-1) + ")"
|
|
|
|
}
|