Add external medias package
This commit is contained in:
parent
a6f84f1622
commit
a2adc4ccf5
62
src/internal/external_medias/external_medias.go
Normal file
62
src/internal/external_medias/external_medias.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package extmedias
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/sqly"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
addExternalMediaQuery = `INSERT INTO external_medias (type, source, category, ids) VALUES ($1, $2, $3, $4) RETURNING id;`
|
||||||
|
updateExternalMediaQuery = `UPDATE external_medias SET type=:type, source=:source, category=:category, ids=:ids WHERE id=:id RETURNING *;`
|
||||||
|
|
||||||
|
deleteExternalMediaQuery = `DELETE FROM external_medias WHERE id=:id;`
|
||||||
|
getExternalMediaQuery = `SELECT * FROM external_medias WHERE type=$1 AND source=$2 AND category=$3 LIMIT 1;`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Media represents an external media
|
||||||
|
type Media struct {
|
||||||
|
sqly.BaseModel
|
||||||
|
Type string `db:"type"`
|
||||||
|
Source string `db:"source"`
|
||||||
|
Category string `db:"category"`
|
||||||
|
IDs sqly.StringSlice `db:"ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds the Media in the database
|
||||||
|
func (m *Media) Add(q sqlx.Queryer) error {
|
||||||
|
var id string
|
||||||
|
err := q.QueryRowx(addExternalMediaQuery, m.Type, m.Source, m.Category, m.IDs).Scan(&id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.ID = id
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update ids only updates the IDs of the media
|
||||||
|
func (m *Media) Update(ex *sqlx.DB) error {
|
||||||
|
rows, err := ex.NamedQuery(updateExternalMediaQuery, m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for rows.Next() {
|
||||||
|
rows.StructScan(m)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the media from database or raise an error
|
||||||
|
func (m *Media) Delete(ex *sqlx.DB) error {
|
||||||
|
_, err := ex.NamedExec(deleteExternalMediaQuery, m)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets a media
|
||||||
|
func Get(q sqlx.Queryer, mtype, msrc, mcat string) (*Media, error) {
|
||||||
|
m := &Media{}
|
||||||
|
if err := q.QueryRowx(getExternalMediaQuery, mtype, msrc, mcat).StructScan(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
75
src/internal/external_medias/external_medias_test.go
Normal file
75
src/internal/external_medias/external_medias_test.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package extmedias
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
_ "github.com/mattes/migrate/driver/postgres"
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/sqly"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var db *sqlx.DB
|
||||||
|
var pgdsn string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
pgdsn = os.Getenv("POSTGRES_DSN")
|
||||||
|
db, err = sqlx.Connect("postgres", pgdsn)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Unavailable PG tests:\n %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddExternalMedias(t *testing.T) {
|
||||||
|
sqly.RunWithLastestMigration(db, pgdsn, t, func(db *sqlx.DB, t *testing.T) {
|
||||||
|
media := &Media{
|
||||||
|
Type: "movie",
|
||||||
|
Source: "trakttv",
|
||||||
|
Category: "trending",
|
||||||
|
IDs: []string{"1", "2", "3"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add it
|
||||||
|
if err := media.Add(db); err != nil {
|
||||||
|
t.Fatalf("failed to add external media: %q", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the IDs
|
||||||
|
media.IDs = []string{"1", "2", "3", "4"}
|
||||||
|
if err := media.Update(db); err != nil {
|
||||||
|
t.Fatalf("failed to update the external media: %q", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find it
|
||||||
|
m, err := Get(db, media.Type, media.Source, media.Category)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed get the external media: %q", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Small, almost useless check
|
||||||
|
if len(m.IDs) != 4 {
|
||||||
|
t.Fatalf("the media should have 4 ids, only %d found", len(m.IDs))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete it
|
||||||
|
if err := media.Delete(db); err != nil {
|
||||||
|
t.Fatalf("failed to delete the external media: %q", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search it and expect that it's not found
|
||||||
|
m, err = Get(db, media.Type, media.Source, media.Category)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("there should be an error, was the external media deleted ?")
|
||||||
|
}
|
||||||
|
if err != sql.ErrNoRows {
|
||||||
|
t.Fatalf("unexpected error: %q", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
58
src/internal/sqly/string_slice.go
Normal file
58
src/internal/sqly/string_slice.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package sqly
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/csv"
|
||||||
|
"errors"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This mainly comes from https://gist.github.com/adharris/4163702
|
||||||
|
|
||||||
|
// StringSlice represents an array of string. The custom type is needed because
|
||||||
|
// pq does not support slices yet..
|
||||||
|
type StringSlice []string
|
||||||
|
|
||||||
|
// for replacing escaped quotes except if it is preceded by a literal backslash
|
||||||
|
// eg "\\" should translate to a quoted element whose value is \
|
||||||
|
|
||||||
|
var quoteEscapeRegex = regexp.MustCompile(`([^\\]([\\]{2})*)\\"`)
|
||||||
|
|
||||||
|
// Scan convert to a slice of strings
|
||||||
|
// http://www.postgresql.org/docs/9.1/static/arrays.html#ARRAYS-IO
|
||||||
|
func (s *StringSlice) Scan(src interface{}) error {
|
||||||
|
asBytes, ok := src.([]byte)
|
||||||
|
if !ok {
|
||||||
|
return error(errors.New("Scan source was not []bytes"))
|
||||||
|
}
|
||||||
|
str := string(asBytes)
|
||||||
|
|
||||||
|
// change quote escapes for csv parser
|
||||||
|
str = quoteEscapeRegex.ReplaceAllString(str, `$1""`)
|
||||||
|
str = strings.Replace(str, `\\`, `\`, -1)
|
||||||
|
// remove braces
|
||||||
|
str = str[1 : len(str)-1]
|
||||||
|
csvReader := csv.NewReader(strings.NewReader(str))
|
||||||
|
|
||||||
|
slice, err := csvReader.Read()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
(*s) = StringSlice(slice)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the Valuer interface
|
||||||
|
func (s StringSlice) Value() (driver.Value, error) {
|
||||||
|
// string escapes.
|
||||||
|
// \ => \\\
|
||||||
|
// " => \"
|
||||||
|
for i, elem := range s {
|
||||||
|
s[i] = `"` + strings.Replace(strings.Replace(elem, `\`, `\\\`, -1), `"`, `\"`, -1) + `"`
|
||||||
|
}
|
||||||
|
return "{" + strings.Join(s, ",") + "}", nil
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user