func main() { app.Init(cfg.EnvProvider{Namespace: Namespace}) // Pull options from the config. var conn *db.DB if _, errHost := cfg.String(cfgWebHost); errHost != nil { xenia.Println("Configuring MongoDB") mongoURI := cfg.MustURL(cfgMongoURI) err := db.RegMasterSession("startup", mongoURI.Path, mongoURI.String(), 0) if err != nil { xenia.Println("Unable to initialize MongoDB") os.Exit(1) } conn, err = db.NewMGO("startup", mongoURI.Path) if err != nil { xenia.Println("Unable to get MongoDB session") os.Exit(1) } defer conn.CloseMGO("startup") } xenia.AddCommand( cmddb.GetCommands(conn), cmdquery.GetCommands(), cmdscript.GetCommands(), cmdregex.GetCommands(), cmdmask.GetCommands(), cmdrelationship.GetCommands(), cmdview.GetCommands(), ) xenia.Execute() }
// loadTestData adds all the test data into the database. func loadTestData(context interface{}, db *db.DB) error { // Make sure the old data is clear. if err := unloadTestData(context, db); err != nil { if !graph.IsQuadNotExist(err) { return err } } // ----------------------------------------------------------- // Load example items, relationships, views, and patterns. items, rels, vs, pats, err := wirefix.Get() if err != nil { return err } if err := wirefix.Add(context, db, items, rels, vs, pats); err != nil { return err } // ----------------------------------------------------------- // Build the example graph. mongoURI := cfg.MustURL("MONGO_URI") if err := cayleyshelf.InitQuadStore(mongoURI.String()); err != nil { return err } var quads []quad.Quad quads = append(quads, quad.Make(wirePrefix+"d1dfa366-d2f7-4a4a-a64f-af89d4c97d82", wirePrefix+"on", wirePrefix+"c1b2bbfe-af9f-4903-8777-bd47c4d5b20a", "")) quads = append(quads, quad.Make(wirePrefix+"6eaaa19f-da7a-4095-bbe3-cee7a7631dd4", wirePrefix+"on", wirePrefix+"c1b2bbfe-af9f-4903-8777-bd47c4d5b20a", "")) quads = append(quads, quad.Make(wirePrefix+"d16790f8-13e9-4cb4-b9ef-d82835589660", wirePrefix+"on", wirePrefix+"c1b2bbfe-af9f-4903-8777-bd47c4d5b20a", "")) quads = append(quads, quad.Make(wirePrefix+"80aa936a-f618-4234-a7be-df59a14cf8de", wirePrefix+"authored", wirePrefix+"d1dfa366-d2f7-4a4a-a64f-af89d4c97d82", "")) quads = append(quads, quad.Make(wirePrefix+"80aa936a-f618-4234-a7be-df59a14cf8de", wirePrefix+"authored", wirePrefix+"6eaaa19f-da7a-4095-bbe3-cee7a7631dd4", "")) quads = append(quads, quad.Make(wirePrefix+"a63af637-58af-472b-98c7-f5c00743bac6", wirePrefix+"authored", wirePrefix+"d16790f8-13e9-4cb4-b9ef-d82835589660", "")) quads = append(quads, quad.Make(wirePrefix+"a63af637-58af-472b-98c7-f5c00743bac6", wirePrefix+"flagged", wirePrefix+"80aa936a-f618-4234-a7be-df59a14cf8de", "")) tx := cayley.NewTransaction() for _, quad := range quads { tx.AddQuad(quad) } if err := db.NewCayley(tests.Context, tests.TestSession); err != nil { return err } store, err := db.GraphHandle(tests.Context) if err != nil { return err } defer store.Close() if err := store.ApplyTransaction(tx); err != nil { return err } return nil }
// teardown deinitializes for each indivdual test. func teardown(t *testing.T, db *db.DB) { relationshipfix.Remove(tests.Context, db, "RTEST_") viewfix.Remove(tests.Context, db, "VTEST_") rfix.Remove(db, "RTEST_") unloadItems(tests.Context, db) unloadTestData(t, db) db.CloseMGO(tests.Context) db.CloseCayley(tests.Context) tests.DisplayLog() }
// Update updates the form gallery in the MongoDB database // collection. func Update(context interface{}, db *db.DB, id string, gallery *Gallery) error { log.Dev(context, "Update", "Started : Gallery[%s]", id) if !bson.IsObjectIdHex(id) { log.Error(context, "Update", ErrInvalidID, "Completed") return ErrInvalidID } if err := gallery.Validate(); err != nil { log.Error(context, "Update", err, "Completed") return err } objectID := bson.ObjectIdHex(id) gallery.DateUpdated = time.Now() f := func(c *mgo.Collection) error { log.Dev(context, "Update", "MGO : db.%s.update(%s, %s)", c.Name, mongo.Query(objectID), mongo.Query(gallery)) return c.UpdateId(objectID, gallery) } if err := db.ExecuteMGO(context, Collection, f); err != nil { log.Error(context, "Update", err, "Completed") return err } log.Dev(context, "Update", "Completed") return nil }
// List retrives the form galleries for a given form from the MongoDB database // collection. func List(context interface{}, db *db.DB, formID string) ([]Gallery, error) { log.Dev(context, "List", "Started") if !bson.IsObjectIdHex(formID) { log.Error(context, "List", ErrInvalidID, "Completed") return nil, ErrInvalidID } formObjectID := bson.ObjectIdHex(formID) var galleries = make([]Gallery, 0) f := func(c *mgo.Collection) error { q := bson.M{ "form_id": formObjectID, } log.Dev(context, "List", "MGO : db.%s.find(%s)", c.Name, mongo.Query(q)) return c.Find(q).All(&galleries) } if err := db.ExecuteMGO(context, Collection, f); err != nil { log.Error(context, "List", err, "Completed") return nil, err } if err := hydrateMany(context, db, galleries); err != nil { log.Error(context, "List", err, "Completed") return nil, err } log.Dev(context, "List", "Completed") return galleries, nil }
// Retrieve retrieves a form gallery from the MongoDB database // collection as well as hydrating the form gallery with form submissions. func Retrieve(context interface{}, db *db.DB, id string) (*Gallery, error) { log.Dev(context, "Retrieve", "Started : Gallery[%s]", id) if !bson.IsObjectIdHex(id) { log.Error(context, "Retrieve", ErrInvalidID, "Completed") return nil, ErrInvalidID } objectID := bson.ObjectIdHex(id) var gallery Gallery f := func(c *mgo.Collection) error { log.Dev(context, "Retrieve", "MGO : db.%s.find(%s)", c.Name, mongo.Query(objectID)) return c.FindId(objectID).One(&gallery) } if err := db.ExecuteMGO(context, Collection, f); err != nil { log.Error(context, "Retrieve", err, "Completed") return nil, err } if err := hydrate(context, db, &gallery); err != nil { log.Error(context, "Retrieve", err, "Completed") return nil, err } log.Dev(context, "Retrieve", "Completed") return &gallery, nil }
// RetrieveMany retrieves a list of Submission's from the MongoDB database collection. func RetrieveMany(context interface{}, db *db.DB, ids []string) ([]Submission, error) { log.Dev(context, "RetrieveMany", "Started") var objectIDs = make([]bson.ObjectId, len(ids)) for i, id := range ids { if !bson.IsObjectIdHex(id) { log.Error(context, "RetrieveMany", ErrInvalidID, "Completed") return nil, ErrInvalidID } objectIDs[i] = bson.ObjectIdHex(id) } var submissions []Submission f := func(c *mgo.Collection) error { q := bson.M{ "_id": bson.M{ "$in": objectIDs, }, } log.Dev(context, "RetrieveMany", "MGO : db.%s.find(%s)", c.Name, mongo.Query(q)) return c.Find(q).All(&submissions) } if err := db.ExecuteMGO(context, Collection, f); err != nil { log.Error(context, "RetrieveMany", err, "Completed") return nil, err } log.Dev(context, "RetrieveMany", "Started") return submissions, nil }
// GetByName retrieves the document for the specified Set. func GetByName(context interface{}, db *db.DB, name string) (*Set, error) { log.Dev(context, "GetByName", "Started : Name[%s]", name) key := "gbn" + name if v, found := cache.Get(key); found { set := v.(Set) log.Dev(context, "GetByName", "Completed : CACHE : Set[%+v]", &set) return &set, nil } var set Set f := func(c *mgo.Collection) error { q := bson.M{"name": name} log.Dev(context, "GetByName", "MGO : db.%s.findOne(%s)", c.Name, mongo.Query(q)) return c.Find(q).One(&set) } if err := db.ExecuteMGO(context, Collection, f); err != nil { if err == mgo.ErrNotFound { err = ErrNotFound } log.Error(context, "GetByName", err, "Completed") return nil, err } // Fix the set so it can be used for processing. set.PrepareForUse() cache.Set(key, set, gc.DefaultExpiration) log.Dev(context, "GetByName", "Completed : Set[%+v]", &set) return &set, nil }
// Count returns the count of current submissions for a given // form id in the Form Submissions MongoDB database collection. func Count(context interface{}, db *db.DB, formID string) (int, error) { log.Dev(context, "Count", "Completed : Form[%s]", formID) if !bson.IsObjectIdHex(formID) { log.Error(context, "Count", ErrInvalidID, "Completed") return 0, ErrInvalidID } formObjectID := bson.ObjectIdHex(formID) var count int f := func(c *mgo.Collection) error { var err error q := bson.M{ "form_id": formObjectID, } log.Dev(context, "Count", "MGO : db.%s.find(%s).count()", c.Name, mongo.Query(q)) count, err = c.Find(q).Count() return err } if err := db.ExecuteMGO(context, Collection, f); err != nil { log.Error(context, "Count", err, "Completed") return 0, err } log.Dev(context, "Count", "Completed") return count, nil }
// GetAll retrieves a list of regexs. func GetAll(context interface{}, db *db.DB, tags []string) ([]Regex, error) { log.Dev(context, "GetAll", "Started : Tags[%v]", tags) key := "grs" + strings.Join(tags, "-") if v, found := cache.Get(key); found { rgxs := v.([]Regex) log.Dev(context, "GetAll", "Completed : CACHE : Rgxs[%d]", len(rgxs)) return rgxs, nil } var rgxs []Regex f := func(c *mgo.Collection) error { log.Dev(context, "GetAll", "MGO : db.%s.find({}).sort([\"name\"])", c.Name) return c.Find(nil).All(&rgxs) } if err := db.ExecuteMGO(context, Collection, f); err != nil { if err == mgo.ErrNotFound { err = ErrNotFound } log.Error(context, "GetAll", err, "Completed") return nil, err } if rgxs == nil { log.Error(context, "GetAll", ErrNotFound, "Completed") return nil, ErrNotFound } cache.Set(key, rgxs, gc.DefaultExpiration) log.Dev(context, "GetAll", "Completed : Rgxs[%d]", len(rgxs)) return rgxs, nil }
// Create adds a new Submission based on a given Form into // the MongoDB database collection. func Create(context interface{}, db *db.DB, formID string, submission *Submission) error { log.Dev(context, "Create", "Started : Form[%s]", formID) if !bson.IsObjectIdHex(formID) { log.Error(context, "Create", ErrInvalidID, "Completed") return ErrInvalidID } if err := submission.Validate(); err != nil { return err } // FIXME: handle Number field maybe with https://docs.mongodb.com/v3.0/tutorial/create-an-auto-incrementing-field/ to resolve race condition count, err := Count(context, db, formID) if err != nil { log.Error(context, "Create", err, "Completed") return err } submission.Number = count + 1 f := func(c *mgo.Collection) error { log.Dev(context, "Create", "MGO : db.%s.insert(%s)", c.Name, mongo.Query(submission)) return c.Insert(submission) } if err := db.ExecuteMGO(context, Collection, f); err != nil { log.Error(context, "Create", err, "Completed") return err } log.Dev(context, "Create", "Completed") return nil }
// UpdateStatus updates a form submissions status inside the MongoDB database // collection. func UpdateStatus(context interface{}, db *db.DB, id, status string) (*Submission, error) { log.Dev(context, "UpdateStatus", "Started : Submission[%s]", id) if !bson.IsObjectIdHex(id) { log.Error(context, "UpdateStatus", ErrInvalidID, "Completed") return nil, ErrInvalidID } objectID := bson.ObjectIdHex(id) f := func(c *mgo.Collection) error { u := bson.M{ "$set": bson.M{ "status": status, "date_updated": time.Now(), }, } log.Dev(context, "UpdateStatus", "MGO : db.%s.update(%s, %s)", c.Name, mongo.Query(objectID), mongo.Query(u)) return c.UpdateId(objectID, u) } if err := db.ExecuteMGO(context, Collection, f); err != nil { log.Error(context, "UpdateStatus", err, "Completed") return nil, err } submission, err := Retrieve(context, db, id) if err != nil { log.Error(context, "UpdateStatus", err, "Completed") return nil, err } log.Dev(context, "UpdateStatus", "Completed") return submission, nil }
// GetByName retrieves the document for the specified query mask. func GetByName(context interface{}, db *db.DB, collection string, field string) (Mask, error) { log.Dev(context, "GetByName", "Started : Collection[%s] Field[%s]", collection, field) key := "gbn" + collection + field if v, found := cache.Get(key); found { mask := v.(Mask) log.Dev(context, "GetByName", "Completed : CACHE : Mask[%+v]", mask) return mask, nil } var mask Mask f := func(c *mgo.Collection) error { q := bson.M{"collection": collection, "field": field} log.Dev(context, "GetByName", "MGO : db.%s.findOne(%s)", c.Name, mongo.Query(q)) return c.Find(q).One(&mask) } if err := db.ExecuteMGO(context, Collection, f); err != nil { if err == mgo.ErrNotFound { err = ErrNotFound } log.Error(context, "GetByName", err, "Completed") return Mask{}, err } cache.Set(key, mask, gc.DefaultExpiration) log.Dev(context, "GetByName", "Completed : Mask[%+v]", mask) return mask, nil }
// GetByIDs retrieves items by ID from Mongo. func GetByIDs(context interface{}, db *db.DB, ids []string) ([]Item, error) { log.Dev(context, "GetByIDs", "Started : IDs%v", ids) // Get the items from Mongo. var items []Item f := func(c *mgo.Collection) error { q := bson.M{"item_id": bson.M{"$in": ids}} log.Dev(context, "Find", "MGO : db.%s.find(%s)", c.Name, mongo.Query(q)) return c.Find(q).All(&items) } if err := db.ExecuteMGO(context, Collection, f); err != nil { if err == mgo.ErrNotFound { err = ErrNotFound } log.Error(context, "GetByIDs", err, "Completed") return items, err } // If we got an unexpected number of items, throw an error. if len(ids) < len(items) { return nil, fmt.Errorf("Expected %d items, got %d: ", len(ids), len(items)) } log.Dev(context, "GetByIDs", "Completed") return items, nil }
// RemoveFlag removes a flag from a given Submission in // the MongoDB database collection. func RemoveFlag(context interface{}, db *db.DB, id, flag string) (*Submission, error) { log.Dev(context, "RemoveFlag", "Started : Submission[%s]", id) if !bson.IsObjectIdHex(id) { log.Error(context, "RemoveFlag", ErrInvalidID, "Completed") return nil, ErrInvalidID } objectID := bson.ObjectIdHex(id) f := func(c *mgo.Collection) error { u := bson.M{ "$pull": bson.M{ "flags": flag, }, } log.Dev(context, "RemoveFlag", "MGO : db.%s.update(%s, %s)", c.Name, mongo.Query(objectID), mongo.Query(u)) return c.UpdateId(objectID, u) } if err := db.ExecuteMGO(context, Collection, f); err != nil { log.Error(context, "RemoveFlag", err, "Completed") return nil, err } submission, err := Retrieve(context, db, id) if err != nil { log.Error(context, "RemoveFlag", err, "Completed") return nil, err } log.Dev(context, "RemoveFlag", "Completed") return submission, nil }
// Remove removes forms in Mongo that match a given pattern. func Remove(context interface{}, db *db.DB, pattern string) error { f := func(c *mgo.Collection) error { q := bson.M{"header.title": bson.RegEx{Pattern: "^" + pattern}} _, err := c.RemoveAll(q) return err } if err := db.ExecuteMGO(context, form.Collection, f); err != nil { return err } if err := db.ExecuteMGO(context, submission.Collection, f); err != nil { return err } return nil }
// Drop drops the temp collection. func Drop(db *db.DB) { col, err := db.CollectionMGO(tests.Context, CollectionExecTest) if err != nil { fmt.Printf("***********> Should be able to get a Mongo session : %v\n", err) return } col.DropCollection() }
func teardownAgg(t *testing.T, db *db.DB) { if err := aggfix.Remove(tests.Context, db, aggPrefix); err != nil { t.Fatalf("%s\tShould be able to remove the forms and submissions : %v", tests.Failed, err) } t.Logf("%s\tShould be able to remove the forms and submissions.", tests.Success) db.CloseMGO(tests.Context) tests.DisplayLog() }
func teardown(t *testing.T, db *db.DB) { if err := galleryfix.Remove(tests.Context, db, prefix); err != nil { t.Fatalf("%s\tShould be able to remove the galleries : %v", tests.Failed, err) } t.Logf("%s\tShould be able to remove the galleries.", tests.Success) db.CloseMGO(tests.Context) tests.DisplayLog() }
// teardown deinitializes for each indivdual test. func teardown(t *testing.T, db *db.DB) { if err := mfix.Remove(db, collection); err != nil { t.Fatalf("%s\tShould be able to remove the query mask : %v", tests.Failed, err) } t.Logf("%s\tShould be able to remove the query mask.", tests.Success) db.CloseMGO(tests.Context) tests.DisplayLog() }
// Remove removes relationships, views, and patterns in Mongo that match a given pattern. func Remove(context interface{}, db *db.DB, prefix string) error { f := func(c *mgo.Collection) error { q := bson.M{"item_id": bson.RegEx{Pattern: prefix}} _, err := c.RemoveAll(q) return err } if err := db.ExecuteMGO(context, item.Collection, f); err != nil { return err } f = func(c *mgo.Collection) error { q := bson.M{"predicate": bson.RegEx{Pattern: prefix}} _, err := c.RemoveAll(q) return err } if err := db.ExecuteMGO(context, relationship.Collection, f); err != nil { return err } f = func(c *mgo.Collection) error { q := bson.M{"name": bson.RegEx{Pattern: prefix}} _, err := c.RemoveAll(q) return err } if err := db.ExecuteMGO(context, view.Collection, f); err != nil { return err } f = func(c *mgo.Collection) error { q := bson.M{"type": bson.RegEx{Pattern: prefix}} _, err := c.RemoveAll(q) return err } if err := db.ExecuteMGO(context, pattern.Collection, f); err != nil { return err } return nil }
// unloadTestData removes all the test data from the database. func unloadTestData(context interface{}, db *db.DB) error { // ------------------------------------------------------------ // Clear items, relationships, and views. wirefix.Remove("context", db, wirePrefix) // ------------------------------------------------------------ // Clear cayley graph. var quads []quad.Quad quads = append(quads, quad.Make(wirePrefix+"d1dfa366-d2f7-4a4a-a64f-af89d4c97d82", wirePrefix+"on", wirePrefix+"c1b2bbfe-af9f-4903-8777-bd47c4d5b20a", "")) quads = append(quads, quad.Make(wirePrefix+"6eaaa19f-da7a-4095-bbe3-cee7a7631dd4", wirePrefix+"on", wirePrefix+"c1b2bbfe-af9f-4903-8777-bd47c4d5b20a", "")) quads = append(quads, quad.Make(wirePrefix+"d16790f8-13e9-4cb4-b9ef-d82835589660", wirePrefix+"on", wirePrefix+"c1b2bbfe-af9f-4903-8777-bd47c4d5b20a", "")) quads = append(quads, quad.Make(wirePrefix+"80aa936a-f618-4234-a7be-df59a14cf8de", wirePrefix+"authored", wirePrefix+"d1dfa366-d2f7-4a4a-a64f-af89d4c97d82", "")) quads = append(quads, quad.Make(wirePrefix+"80aa936a-f618-4234-a7be-df59a14cf8de", wirePrefix+"authored", wirePrefix+"6eaaa19f-da7a-4095-bbe3-cee7a7631dd4", "")) quads = append(quads, quad.Make(wirePrefix+"a63af637-58af-472b-98c7-f5c00743bac6", wirePrefix+"authored", wirePrefix+"d16790f8-13e9-4cb4-b9ef-d82835589660", "")) quads = append(quads, quad.Make(wirePrefix+"a63af637-58af-472b-98c7-f5c00743bac6", wirePrefix+"flagged", wirePrefix+"80aa936a-f618-4234-a7be-df59a14cf8de", "")) tx := cayley.NewTransaction() for _, quad := range quads { tx.RemoveQuad(quad) } if err := db.NewCayley(tests.Context, tests.TestSession); err != nil { return err } store, err := db.GraphHandle(tests.Context) if err != nil { return err } defer store.Close() if err := store.ApplyTransaction(tx); err != nil { return err } return nil }
// cleanupView removed a tempory view collection. func cleanupView(context interface{}, db *db.DB, viewCol string) error { c, err := db.CollectionMGO(context, viewCol) if err != nil { return err } if err := c.DropCollection(); err != nil { return err } return nil }
// viewItems retrieves the items corresponding to the provided list of item IDs. func viewItems(context interface{}, db *db.DB, v *view.View, ids []string, embeds embeddedRels) ([]bson.M, error) { // Form the query. var results []item.Item f := func(c *mgo.Collection) error { return c.Find(bson.M{"item_id": bson.M{"$in": ids}}).All(&results) } // Execute the query. if err := db.ExecuteMGO(context, v.Collection, f); err != nil { if err == mgo.ErrNotFound { err = ErrNotFound } return nil, err } // Group the embedded relationships by item and predicate/tag. embedByItem, err := groupEmbeds(embeds) if err != nil { return nil, err } // Embed any related item IDs in the returned items. var output []bson.M if len(embedByItem) > 0 { for _, result := range results { // Get the respective IDs to embed. itemEmbeds, ok := embedByItem[result.ID] if ok { relMap := make(map[string]interface{}) for k, v := range itemEmbeds { relMap[k] = v } result.Related = relMap } // Convert to bson.M for output. itemBSON := bson.M{ "item_id": result.ID, "type": result.Type, "version": result.Version, "data": result.Data, "created_at": result.CreatedAt, "updated_at": result.UpdatedAt, "related": result.Related, } output = append(output, itemBSON) } } return output, nil }
// Add inserts a mask for testing. func Add(db *db.DB, msk mask.Mask) error { f := func(c *mgo.Collection) error { q := bson.M{"collection": msk.Collection, "field": msk.Field} _, err := c.Upsert(q, msk) return err } if err := db.ExecuteMGO(tests.Context, mask.Collection, f); err != nil { return err } return nil }
// Remove removes gallerys in Mongo that match a given pattern. func Remove(context interface{}, db *db.DB, pattern string) error { f := func(c *mgo.Collection) error { q := bson.M{"description": bson.RegEx{Pattern: "^" + pattern}} _, err := c.RemoveAll(q) return err } if err := db.ExecuteMGO(context, gallery.Collection, f); err != nil { return err } return nil }
// Remove removes patterns in Mongo that match a given prefix. func Remove(context interface{}, db *db.DB, prefix string) error { f := func(c *mgo.Collection) error { q := bson.M{"type": bson.RegEx{Pattern: prefix}} _, err := c.RemoveAll(q) return err } if err := db.ExecuteMGO(context, pattern.Collection, f); err != nil { return err } return nil }
// Add inserts a regex for testing. func Add(db *db.DB, rgx regex.Regex) error { f := func(c *mgo.Collection) error { q := bson.M{"name": rgx.Name} _, err := c.Upsert(q, rgx) return err } if err := db.ExecuteMGO(tests.Context, regex.Collection, f); err != nil { return err } return nil }
// Remove removes relationships in Mongo that match a given pattern. func Remove(context interface{}, db *db.DB, pattern string) error { f := func(c *mgo.Collection) error { q := bson.M{"predicate": bson.RegEx{Pattern: pattern}} _, err := c.RemoveAll(q) return err } if err := db.ExecuteMGO(context, relationship.Collection, f); err != nil { return err } return nil }
// Remove is used to clear out all the test sets from the collection. // All test documents must start with QSTEST in their name. func Remove(db *db.DB, pattern string) error { f := func(c *mgo.Collection) error { q := bson.M{"name": bson.RegEx{Pattern: pattern}} _, err := c.RemoveAll(q) return err } if err := db.ExecuteMGO(tests.Context, query.Collection, f); err != nil { return err } f = func(c *mgo.Collection) error { q := bson.M{"name": bson.RegEx{Pattern: pattern}} _, err := c.RemoveAll(q) return err } if err := db.ExecuteMGO(tests.Context, query.CollectionHistory, f); err != nil { return err } return nil }