package project import ( "context" "fmt" "time" "geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/db" "github.com/pkg/errors" "go.opencensus.io/trace" mgo "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" ) const projectsCollection = "projects" var ( // ErrNotFound abstracts the mgo not found error. ErrNotFound = errors.New("Entity not found") // ErrInvalidID occurs when an ID is not in a valid form. ErrInvalidID = errors.New("ID is not in its proper form") ) // List retrieves a list of existing projects from the database. func List(ctx context.Context, dbConn *db.DB) ([]Project, error) { ctx, span := trace.StartSpan(ctx, "internal.project.List") defer span.End() p := []Project{} f := func(collection *mgo.Collection) error { return collection.Find(nil).All(&p) } if err := dbConn.Execute(ctx, projectsCollection, f); err != nil { return nil, errors.Wrap(err, "db.projects.find()") } return p, nil } // Retrieve gets the specified project from the database. func Retrieve(ctx context.Context, dbConn *db.DB, id string) (*Project, error) { ctx, span := trace.StartSpan(ctx, "internal.project.Retrieve") defer span.End() if !bson.IsObjectIdHex(id) { return nil, ErrInvalidID } q := bson.M{"_id": bson.ObjectIdHex(id)} var p *Project f := func(collection *mgo.Collection) error { return collection.Find(q).One(&p) } if err := dbConn.Execute(ctx, projectsCollection, f); err != nil { if err == mgo.ErrNotFound { return nil, ErrNotFound } return nil, errors.Wrap(err, fmt.Sprintf("db.projects.find(%s)", db.Query(q))) } return p, nil } // Create inserts a new project into the database. func Create(ctx context.Context, dbConn *db.DB, cp *NewProject, now time.Time) (*Project, error) { ctx, span := trace.StartSpan(ctx, "internal.project.Create") defer span.End() // Mongo truncates times to milliseconds when storing. We and do the same // here so the value we return is consistent with what we store. now = now.Truncate(time.Millisecond) p := Project{ ID: bson.NewObjectId(), Name: cp.Name, Cost: cp.Cost, Quantity: cp.Quantity, DateCreated: now, DateModified: now, } f := func(collection *mgo.Collection) error { return collection.Insert(&p) } if err := dbConn.Execute(ctx, projectsCollection, f); err != nil { return nil, errors.Wrap(err, fmt.Sprintf("db.projects.insert(%s)", db.Query(&p))) } return &p, nil } // Update replaces a project document in the database. func Update(ctx context.Context, dbConn *db.DB, id string, upd UpdateProject, now time.Time) error { ctx, span := trace.StartSpan(ctx, "internal.project.Update") defer span.End() if !bson.IsObjectIdHex(id) { return ErrInvalidID } fields := make(bson.M) if upd.Name != nil { fields["name"] = *upd.Name } if upd.Cost != nil { fields["cost"] = *upd.Cost } if upd.Quantity != nil { fields["quantity"] = *upd.Quantity } // If there's nothing to update we can quit early. if len(fields) == 0 { return nil } fields["date_modified"] = now m := bson.M{"$set": fields} q := bson.M{"_id": bson.ObjectIdHex(id)} f := func(collection *mgo.Collection) error { return collection.Update(q, m) } if err := dbConn.Execute(ctx, projectsCollection, f); err != nil { if err == mgo.ErrNotFound { return ErrNotFound } return errors.Wrap(err, fmt.Sprintf("db.customers.update(%s, %s)", db.Query(q), db.Query(m))) } return nil } // Delete removes a project from the database. func Delete(ctx context.Context, dbConn *db.DB, id string) error { ctx, span := trace.StartSpan(ctx, "internal.project.Delete") defer span.End() if !bson.IsObjectIdHex(id) { return ErrInvalidID } q := bson.M{"_id": bson.ObjectIdHex(id)} f := func(collection *mgo.Collection) error { return collection.Remove(q) } if err := dbConn.Execute(ctx, projectsCollection, f); err != nil { if err == mgo.ErrNotFound { return ErrNotFound } return errors.Wrap(err, fmt.Sprintf("db.projects.remove(%v)", q)) } return nil }