package mgoapi import ( "reflect" "strconv" "strings" "gitbase.de/gopackage/mgocrud" "github.com/gin-gonic/gin" mgo "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" ) func (api *API) collectionGetOneHandler(m mgocrud.ModelInterface) gin.HandlerFunc { return func(c *gin.Context) { session := api.DBSession.Copy() defer session.Close() db := session.DB(api.DBName) var selector bson.M selectorStr := c.Query("select") if selectorStr != "" { err := bson.UnmarshalJSON([]byte(selectorStr), &selector) if err != nil { c.JSON(400, gin.H{ "error": "select: " + err.Error(), }) return } } validSelect, err := validateSelect(&Context{ API: api, Context: c, DB: db, }, m, selector) if err != nil { c.JSON(500, gin.H{ "error": "select: " + err.Error(), }) return } newM, err := getDocument(c, db, m, validSelect) if err != nil { status := 500 if err == mgo.ErrNotFound { status = 404 } c.JSON(status, gin.H{ "error": err.Error(), }) return } err = processResults(&Context{ API: api, Context: c, DB: db, }, m, newM) if err != nil { c.JSON(500, gin.H{ "error": err.Error(), }) return } c.JSON(200, newM) } } func (api *API) collectionGetHandler(m mgocrud.ModelInterface) gin.HandlerFunc { return func(c *gin.Context) { // we need to make a slice of the correct type, so that custom MarhalJSON functions of slice elements work // []ModelInterface type would not work => bson.M not compatible (results of mgo query) // see: https://stackoverflow.com/questions/25384640/why-golang-reflect-makeslice-returns-un-addressable-value slice := reflect.MakeSlice( reflect.SliceOf( modelTypeOf(m), ), 0, 0) x := reflect.New(slice.Type()) x.Elem().Set(slice) results := x.Interface() session := api.DBSession.Copy() defer session.Close() db := session.DB(api.DBName) var filter bson.M filterStr := c.Query("filter") if filterStr != "" { err := bson.UnmarshalJSON([]byte(filterStr), &filter) if err != nil { c.JSON(400, gin.H{ "error": "filter: " + err.Error(), }) return } } validFilter, err := validateFilter(&Context{ API: api, Context: c, DB: db, }, m, filter) if err != nil { c.JSON(500, gin.H{ "error": "filter: " + err.Error(), }) return } var selector bson.M setIdent := false if c.Query("ident") != "" { selector = m.GetIdentSelector() setIdent = true } else { selectorStr := c.Query("select") if selectorStr != "" { err := bson.UnmarshalJSON([]byte(selectorStr), &selector) if err != nil { c.JSON(400, gin.H{ "error": "select: " + err.Error(), }) return } } } validSelect, err := validateSelect(&Context{ API: api, Context: c, DB: db, }, m, selector) if err != nil { c.JSON(500, gin.H{ "error": "select: " + err.Error(), }) return } limit, _ := strconv.Atoi(c.Query("limit")) offset, _ := strconv.Atoi(c.Query("offset")) sort := c.QueryArray("sort") if len(sort) <= 0 { sort = c.QueryArray("sort[]") } clearedSort := []string{} for _, s := range sort { if s != "" { clearedSort = append(clearedSort, s) } } queryCount := c.Query("count") if queryCount == "1" || strings.ToLower(queryCount) == "true" { count, err := mgocrud.ReadCollectionCount(db, m, validFilter) if err != nil { c.JSON(500, gin.H{ "error": err.Error(), }) return } c.Header("X-Results-Count", strconv.Itoa(count)) } var pipelineModFunc mgocrud.PipelineModifierFunction if i, ok := m.(interface { PipelineModifier(*Context) (mgocrud.PipelineModifierFunction, error) }); ok { pipelineModFunc, err = i.PipelineModifier(&Context{ API: api, Context: c, DB: db, }) if err != nil { c.JSON(500, gin.H{ "error": err.Error(), }) return } } err = mgocrud.ReadCollection(db, results, validFilter, validSelect, offset, limit, clearedSort, pipelineModFunc) if err != nil { c.JSON(500, gin.H{ "error": err.Error(), }) return } err = processResults(&Context{ API: api, Context: c, DB: db, }, m, results) if err != nil { c.JSON(500, gin.H{ "error": err.Error(), }) return } if setIdent { xE := x.Elem() // results slice (of pointer to slice) for i := 0; i < xE.Len(); i++ { resSliceEl := xE.Index(i).Addr().Interface().(mgocrud.ModelInterface) resSliceEl.SetIdent() } } c.JSON(200, results) } } func (api *API) collectionPostHandler(m mgocrud.ModelInterface) gin.HandlerFunc { return func(c *gin.Context) { newM := newModelOf(m).(mgocrud.ModelInterface) if err := c.Bind(newM); err != nil { c.JSON(400, gin.H{ "error": err.Error(), }) return } session := api.DBSession.Copy() defer session.Close() db := session.DB(api.DBName) if err := validateObject(&Context{ API: api, Context: c, DB: db, }, newM, nil); err != nil { c.JSON(400, gin.H{ "error": err.Error(), "apiContext": "validateObject", }) return } err := mgocrud.CreateDocument(db, newM) if err != nil { c.JSON(500, gin.H{ "error": err.Error(), "apiContext": "createDocument", }) return } readM := newModelOf(newM).(mgocrud.ModelInterface) readM.SetID(newM.GetID()) err = mgocrud.ReadDocument(db, readM, nil) if err != nil { c.JSON(500, gin.H{ "error": err.Error(), "apiContext": "readDocument", }) return } if err := savedObject(&Context{ API: api, Context: c, DB: db, }, readM, nil); err != nil { c.JSON(400, gin.H{ "error": err.Error(), "apiContext": "savedObject", }) return } err = processResults(&Context{ API: api, Context: c, DB: db, }, m, readM) if err != nil { c.JSON(500, gin.H{ "error": err.Error(), }) return } c.JSON(200, readM) } } func (api *API) collectionPutHandler(m mgocrud.ModelInterface) gin.HandlerFunc { return func(c *gin.Context) { session := api.DBSession.Copy() defer session.Close() db := session.DB(api.DBName) orgM, err := getDocument(c, db, m, nil) if err != nil { status := 500 if err == mgo.ErrNotFound { status = 404 } c.JSON(status, gin.H{ "error": err.Error(), }) return } newM := newModelOf(m).(mgocrud.ModelInterface) if err := c.Bind(newM); err != nil { c.JSON(400, gin.H{ "error": err.Error(), }) return } // dont allow id in PUT if newM.GetID() != nil { c.JSON(400, gin.H{ "error": "id not allowed in update", }) return } /* mapDecoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ ErrorUnused: true, ZeroFields: true, Result: orgM, TagName: "json", }) if err != nil { c.JSON(500, gin.H{ "error": err.Error(), }) return } err = mapDecoder.Decode(change) if err != nil { c.JSON(400, gin.H{ "error": err.Error(), }) return } */ // apply newM on orgM and record changes for db update, only first level for now changes := bson.M{} newMStruct := reflect.Indirect(reflect.ValueOf(newM)) orgMStruct := reflect.Indirect(reflect.ValueOf(orgM)) for i := 0; i < newMStruct.NumField(); i++ { field := newMStruct.Field(i) fieldT := newMStruct.Type().Field(i) if fieldT.Anonymous && fieldT.Name == "Model" { // is embedded mgocrud.Model, so not in changes continue } nilAble := (field.Kind() == reflect.Ptr || field.Kind() == reflect.Map || field.Kind() == reflect.Slice) if (nilAble && !field.IsNil()) || !nilAble { // apply naming conventions of mgo for changeset tag := fieldT.Tag.Get("bson") if tag == "" && !strings.Contains(string(fieldT.Tag), ":") { tag = string(fieldT.Tag) } if tag == "-" { continue } tagFields := strings.Split(tag, ",") tag = tagFields[0] fieldName := tag if fieldName == "" { fieldName = strings.ToLower(fieldT.Name) } changes[fieldName] = field.Interface() orgMStruct.Field(i).Set(field) } } //spew.Dump(changes) //spew.Dump(orgM) if err := validateObject(&Context{ API: api, Context: c, DB: db, }, orgM, changes); err != nil { c.JSON(400, gin.H{ "error": err.Error(), }) return } err = mgocrud.UpdateDocument(db, orgM, changes) if err != nil { c.JSON(500, gin.H{ "error": err.Error(), }) return } //spew.Dump(orgM) // reread from db newM, err = getDocument(c, db, orgM, nil) if err != nil { c.JSON(500, gin.H{ "error": err.Error(), }) return } if err := savedObject(&Context{ API: api, Context: c, DB: db, }, newM, changes); err != nil { c.JSON(400, gin.H{ "error": err.Error(), }) return } err = processResults(&Context{ API: api, Context: c, DB: db, }, m, newM) if err != nil { c.JSON(500, gin.H{ "error": err.Error(), }) return } c.JSON(200, newM) } } func (api *API) collectionDeleteHandler(m mgocrud.ModelInterface) gin.HandlerFunc { return func(c *gin.Context) { session := api.DBSession.Copy() defer session.Close() db := session.DB(api.DBName) orgM, err := getDocument(c, db, m, bson.M{"_id": 1}) if err != nil { status := 500 if err == mgo.ErrNotFound { status = 404 } c.JSON(status, gin.H{ "error": err.Error(), }) return } err = mgocrud.DeleteDocument(db, orgM) if err != nil { c.JSON(500, gin.H{ "error": err.Error(), }) } c.JSON(200, gin.H{"message": "ok"}) } } func (api *API) collectionGetMetaHandler(m mgocrud.ModelInterface) gin.HandlerFunc { return func(c *gin.Context) { c.JSON(200, getModelMeta(m)) } }