Commit 14e3a76e authored by Matthew Stobbs's avatar Matthew Stobbs
Browse files

Users now resolve images, groups and roles

parent 4679cd5e
package postgres
import (
"fmt"
"errors"
"log"
"time"
......@@ -12,32 +12,47 @@ import (
var (
prepareSelectGroup = q.Prepare(
q.Select("id", "name", "owner_id", "created_at", "updated_at"),
q.Select(
`groups.id`,
`groups.name`,
`groups.owner_id`,
`groups.created_at`,
`groups.updated_at`,
),
)
prepareFromGroup = q.Prepare(q.From("groups"))
prepareFromGroup = q.Prepare(q.From(`groups`))
getGroupByIDQuery = q.QueryBuilder(
prepareSelectGroup,
prepareFromGroup,
q.WhereEq("id"),
q.WhereEq(`groups.id`),
)
getGroupByNameQuery = q.QueryBuilder(
prepareSelectGroup,
prepareFromGroup,
q.WhereEq("name"),
q.WhereEq(`groups.name`),
)
getGroupsByOwnerIDQuery = q.QueryBuilder(
prepareSelectGroup,
prepareFromGroup,
q.WhereEq("owner_id"),
q.WhereEq(`groups.owner_id`),
)
getGroupMembersQuery = q.QueryBuilder(
prepareSelectUser,
prepareFromUser,
q.InnerJoin(`groups_users`),
q.On(`groups.id`, `groups_users.group_id`),
q.InnerJoin(`groups`),
q.On(`users.id`, `groups_users.user_id`),
q.WhereEq(`groups_users.group_id`),
)
insertGroupQuery = q.QueryBuilder(
q.Insert(
"groups",
"id",
"name",
"owner_id",
"created_at",
"updated_at",
`groups`,
`id`,
`name`,
`owner_id`,
`created_at`,
`updated_at`,
),
)
)
......@@ -45,59 +60,56 @@ var (
// GetGroupByID is used by the database resolver to find groups
func (d *Db) GetGroupByID(id uuid.UUID) (*models.Group, error) {
g, err := d.GetGroupByQuery(getGroupByIDQuery, id)
if len(g) == 0 {
return nil, &ModelNotFound{}
if len(g) == 0 || err != nil {
return nil, NewModelNotFound(err)
}
if err != nil {
return nil, fmt.Errorf("GetRoleByName: %s", err)
}
return &g[0], err
return &g[0], NewModelFound(err)
}
// GetGroupByName retrieves a group by it's name
func (d *Db) GetGroupByName(name string) (*models.Group, error) {
g, err := d.GetGroupByQuery(getGroupByNameQuery, name)
if len(g) == 0 {
return nil, &ModelNotFound{}
if len(g) == 0 || err != nil {
return nil, NewModelNotFound(err)
}
if err != nil {
return nil, fmt.Errorf("GetRoleByName: %s", err)
}
return &g[0], err
return &g[0], NewModelFound(err)
}
// GetGroupsByOwnerID retrives a list of groups by the owner_id
func (d *Db) GetGroupsByOwnerID(ownerid uuid.UUID) ([]models.Group, error) {
return d.GetGroupByQuery(getGroupsByOwnerIDQuery, ownerid)
}
// GetGroupMembers retrives a list of users belonging to a group
func (d *Db) GetGroupMembers(gid uuid.UUID) ([]models.User, error) {
return d.GetUserByQuery(getGroupMembersQuery, gid)
}
// GetGroupByQuery runs the query and returns a group and error
func (d *Db) GetGroupByQuery(query string, arg ...interface{}) ([]models.Group, error) {
stmt, err := d.Prepare(query)
if err != nil {
return nil, err
return nil, NewUnknownError(err)
}
rows, err := stmt.Query(arg...)
if err != nil {
return nil, err
return nil, NewUnknownError(err)
}
defer rows.Close()
var groups []models.Group
for rows.Next() {
var g models.Group
var ownerID = uuid.Nil
var ctime, utime time.Time
err = rows.Scan(
&g.ID,
&g.Name,
&ownerID,
&g.OwnerID,
&ctime,
&utime,
)
if err != nil {
return nil, err
}
if ownerID != uuid.Nil {
g.Owner, err = d.GetUserByID(ownerID)
if err != nil {
return nil, err
}
return nil, NewUnknownError(err)
}
g.CreatedAt.Set(ctime)
g.UpdatedAt.Set(utime)
......@@ -106,32 +118,40 @@ func (d *Db) GetGroupByQuery(query string, arg ...interface{}) ([]models.Group,
return groups, nil
}
func (d *Db) checkGroupExists(groupname string) error {
_, err := d.GetGroupByName(groupname)
switch err.(type) {
case *ModelNotFound:
return &ModelNotFound{}
case *ModelFound:
return &ModelFound{}
default:
return err
func (d *Db) checkIfGroupExists(groupname string) error {
g, err := d.GetGroupByQuery(getGroupByNameQuery, groupname)
if err != nil {
switch err.(type) {
case *ModelNotFound:
return err
case *ModelFound:
return err
default:
return NewUnknownError(err)
}
}
if len(g) == 0 {
return NewModelNotFound(errors.New(`Group not found`))
}
return NewModelFound(nil)
}
// CreateGroup inserts a group into the database
func (d *Db) CreateGroup(g *models.Group) error {
if err := d.checkGroupExists(g.Name); err != nil {
switch err.(type) {
if err := d.checkIfGroupExists(g.Name); err != nil {
switch err := err.(type) {
case *ModelNotFound:
log.Println("Group not found:", g.Name)
log.Println(err)
case *ModelFound:
return err
default:
return fmt.Errorf("CreateGroup [d.checkGroupExists]: %s", err)
return NewUnknownError(err)
}
}
stmt, err := d.Prepare(insertGroupQuery)
if err != nil {
return fmt.Errorf("CreateGroup [d.Prepare]: %s", err)
return NewUnknownError(err)
}
defer stmt.Close()
g.ID = models.GenUUID()
......@@ -140,12 +160,12 @@ func (d *Db) CreateGroup(g *models.Group) error {
_, err = stmt.Exec(
g.ID,
g.Name,
g.Owner.ID,
g.OwnerID,
g.CreatedAt.GetTime(),
g.UpdatedAt.GetTime(),
)
if err != nil {
return fmt.Errorf("CreateGroup [stmt.Exec]: %s", err)
return NewUnknownError(err)
}
return nil
}
package postgres
import (
"fmt"
"errors"
"log"
"time"
......@@ -14,47 +14,47 @@ import (
var (
selectAllImage = q.Prepare(
q.Select(
"id",
"filename",
"storagepath",
"sha256",
"size",
"height",
"width",
"owner_id",
"created_at",
"updated_at",
`id`,
`filename`,
`storagepath`,
`sha256`,
`size`,
`height`,
`width`,
`owner_id`,
`created_at`,
`updated_at`,
),
)
createImageQuery = q.QueryBuilder(
q.Insert(
"images",
"id",
"filename",
"storagepath",
"sha256",
"size",
"height",
"width",
"owner_id",
"created_at",
"updated_at",
`images`,
`id`,
`filename`,
`storagepath`,
`sha256`,
`size`,
`height`,
`width`,
`owner_id`,
`created_at`,
`updated_at`,
),
)
getImageByIDQuery = q.QueryBuilder(
selectAllImage,
q.From(`images`),
q.WhereEq("id"),
q.WhereEq(`id`),
)
getImageByOwnerIDQuery = q.QueryBuilder(
selectAllImage,
q.From(`images`),
q.WhereEq("owner_id"),
q.WhereEq(`owner_id`),
)
getImageBySha256Query = q.QueryBuilder(
selectAllImage,
q.From(`images`),
q.WhereEq("sha256"),
q.WhereEq(`sha256`),
)
getImageByOwnerIDSha256 = q.QueryBuilder(
selectAllImage,
......@@ -67,10 +67,13 @@ var (
// GetImageByID retrieves an image by it's ID
func (d *Db) GetImageByID(id uuid.UUID) (*models.Image, error) {
i, err := d.GetImageByQuery(getImageByIDQuery, id)
if len(i) == 0 {
return nil, err
if len(i) == 0 && err != nil {
return nil, NewModelNotFound(err)
}
if err != nil {
return nil, NewUnknownError(err)
}
return &i[0], err
return &i[0], nil
}
// GetImagesByOwnerID retrives a list of images based on the given owner_id
......@@ -85,28 +88,30 @@ func (d *Db) GetImagesByOwnerID(ownerid uuid.UUID) ([]models.Image, error) {
// GetImageBySha256 retrieves an image based on it's sha256 sum
func (d *Db) GetImageBySha256(sum string) (*models.Image, error) {
i, err := d.GetImageByQuery(getImageBySha256Query, sum)
if len(i) == 0 {
return nil, err
if len(i) == 0 && err != nil {
return nil, NewModelNotFound(err)
}
if err != nil {
return nil, NewUnknownError(err)
}
return &i[0], err
return &i[0], nil
}
// GetImageByQuery retrieves models using a query
func (d *Db) GetImageByQuery(query string, arg ...interface{}) ([]models.Image, error) {
stmt, err := d.Prepare(query)
if err != nil {
return nil, fmt.Errorf("GetImageByQuery [d.Prepare]: %s", err)
return nil, NewUnknownError(err)
}
defer stmt.Close()
rows, err := stmt.Query(arg...)
if err != nil {
return nil, fmt.Errorf("GetImageByQuery [stmt.Query]: %s", err)
return nil, NewUnknownError(err)
}
defer rows.Close()
var images []models.Image
var ownerid uuid.UUID
var createdat, updatedat time.Time
for rows.Next() {
var i models.Image
......@@ -118,19 +123,13 @@ func (d *Db) GetImageByQuery(query string, arg ...interface{}) ([]models.Image,
&i.Size,
&i.Height,
&i.Width,
&ownerid,
&i.OwnerID,
&createdat,
&updatedat,
)
if err != nil {
return nil, fmt.Errorf("getUserByQuery [rows.scan]: %s", err)
return images, NewModelNotFound(err)
}
i.Owner, err = d.GetUserByID(ownerid)
if err != nil {
log.Println(err)
}
i.CreatedAt.Set(createdat)
i.UpdatedAt.Set(updatedat)
......@@ -143,14 +142,20 @@ func (d *Db) GetImageByQuery(query string, arg ...interface{}) ([]models.Image,
// This verifies it exists in the database only, not in the filesystem
func (d *Db) checkIfImageExistsDB(ownerid uuid.UUID, sha256 string) error {
i, err := d.GetImageByQuery(getImageByOwnerIDSha256, ownerid, sha256)
if len(i) == 0 {
return &ModelNotFound{}
}
if err != nil {
return fmt.Errorf("checkifImageExistsDB: %s", err)
switch err.(type) {
case *ModelNotFound:
return err
case *ModelFound:
return err
default:
return NewUnknownError(err)
}
}
return &ModelFound{}
if len(i) == 0 {
return NewModelNotFound(errors.New(`Image not found`))
}
return NewModelFound(errors.New(`Image found`))
}
// CreateImage generates missing data and inserts an entry in the database
......@@ -161,19 +166,21 @@ func (d *Db) CreateImage(i *models.Image, data []byte) error {
stmt, err := d.Prepare(createImageQuery)
defer stmt.Close()
if err != nil {
return fmt.Errorf("CreateImage [d.Prepare]: %s", err)
return NewUnknownError(err)
}
if err := i.Process(data); err != nil {
return fmt.Errorf("CreateImage [i.Process]: %s", err)
return NewUnknownError(err)
}
if err := d.checkIfImageExistsDB(i.Owner.ID, i.Sha256); err != nil {
switch err.(type) {
if err := d.checkIfImageExistsDB(i.OwnerID, i.Sha256); err != nil {
switch err := err.(type) {
case *ModelNotFound:
log.Println(`Image not found for user:`, i.Owner.Username)
default:
log.Println(err)
case *ModelFound:
return err
default:
return NewUnknownError(err)
}
}
if _, err = stmt.Exec(
......@@ -184,11 +191,11 @@ func (d *Db) CreateImage(i *models.Image, data []byte) error {
i.Size,
i.Height,
i.Width,
i.Owner.ID,
i.OwnerID,
i.CreatedAt.GetTime(),
i.UpdatedAt.GetTime(),
); err != nil {
return fmt.Errorf("CreateImage [d.Prepare:stmt.Exec]: %s", err)
return NewUnknownError(err)
}
return nil
......
......@@ -4,6 +4,7 @@ import (
"database/sql"
"fmt"
"log"
"runtime"
// for wrapping an SQL object
_ "github.com/lib/pq"
......@@ -35,16 +36,69 @@ func ConnString(host string, port int, user, password, dbName string) string {
}
// ModelNotFound error type
type ModelNotFound struct{}
type ModelNotFound struct {
err error
file string
line int
}
// NewModelNotFound generates a meaningful error message when
// a model isn't found.
func NewModelNotFound(err error) *ModelNotFound {
_, file, line, _ := runtime.Caller(1)
return &ModelNotFound{
err: err,
file: file,
line: line,
}
}
// Error fullfills the interface for error
// Error fullfills the interface for errors
func (m *ModelNotFound) Error() string {
return "Model not found"
return fmt.Sprintf(`Model Not Found Error: %s:%d : %s`, m.file, m.line, m.err)
}
// ModelFound error type
type ModelFound struct{}
type ModelFound struct {
err error
file string
line int
}
// NewModelFound generates a meaningful error message when a model is found.
func NewModelFound(err error) *ModelFound {
_, file, line, _ := runtime.Caller(1)
return &ModelFound{
err: err,
file: file,
line: line,
}
}
// Error fullfills the interface for errors
func (m *ModelFound) Error() string {
return "Model found"
return fmt.Sprintf(`Model Found Error: %s:%d : %s`, m.file, m.line, m.err)
}
// UnknownError is to log unknown errors
type UnknownError struct {
err error
file string
line int
}
// NewUnknownError generates a meaningful error message
// when an unknown error occurs
func NewUnknownError(err error) *UnknownError {
_, file, line, _ := runtime.Caller(1)
return &UnknownError{
err: err,
file: file,
line: line,
}
}
// Error fullfills the interface for errors
func (u *UnknownError) Error() string {
return fmt.Sprintf(`Unknown Error: %s:%d : %s`, u.file, u.line, u.err)
}
package postgres
import (
"fmt"
"errors"
"log"
"sync"
"time"
uuid "github.com/satori/go.uuid"
......@@ -50,63 +49,49 @@ func (d *Db) GetProfileByID(id uuid.UUID) (*models.Profile, error) {
// GetProfileByUserID retrieves a profile via it's User ID association
func (d *Db) GetProfileByUserID(userid uuid.UUID) (*models.Profile, error) {
p, err := d.GetProfileByQuery(getProfileByUserIDQuery, userid)
if len(p) == 0 {
return nil, &ModelNotFound{}
}
if err != nil {
return nil, err
switch err.(type) {
case *ModelNotFound:
return nil, err
default:
return nil, NewUnknownError(err)
}
}
return &p[0], err
if len(p) == 0 {
return nil, NewModelNotFound(errors.New(`Profile not found`))
}
return &p[0], nil
}
// GetProfileByQuery returns a list of profiles matching the query and given arguments.
func (d *Db) GetProfileByQuery(query string, args ...interface{}) ([]models.Profile, error) {
var profiles []models.Profile
stmt, err := d.Prepare(query)
if err != nil {
return nil, err
return nil, NewUnknownError(err)
}
defer stmt.Close()
r, err := stmt.Query(args...)
if err != nil {
return nil, err
return nil, NewUnknownError(err)
}
defer r.Close()
var profiles []models.Profile
for r.Next() {
var userid, backgroundid, profileid uuid.UUID
var createdat, updatedat time.Time
var p models.Profile
err = r.Scan(
&p.ID,
&userid,
&backgroundid,
&profileid,
&p.UserID,
&p.BackgroundID,
&p.ProfileID,
&createdat,
&updatedat,
)
if err != nil {
return nil, fmt.Errorf("GetProfileByQuery [r.Scan]: %s", err)
return profiles, NewModelNotFound(err)
}
var wg sync.WaitGroup
go func() {
wg.Add(1)
p.User, err = d.GetUserByID(userid)
wg.Done()
}()
go func() {
wg.Add(1)
p.Background, err = d.GetImageByID(backgroundid)
wg.Done()