func makeMutation(mmap map[string][]*cassandra.Mutation, cf, name string, value []byte, now time.Time) { var m *cassandra.Mutation = cassandra.NewMutation() var col *cassandra.Column = cassandra.NewColumn() col.Name = []byte(name) col.Value = value col.Timestamp = proto.Int64(now.UnixNano()) m.ColumnOrSupercolumn = cassandra.NewColumnOrSuperColumn() m.ColumnOrSupercolumn.Column = col mmap[cf] = append(mmap[cf], m) }
func (self *ProductEditAPI) ServeHTTP(w http.ResponseWriter, req *http.Request) { var buf *bytes.Buffer = new(bytes.Buffer) var specid string = req.PostFormValue("id") var uuid UUID var codes *Barcodes = new(Barcodes) var mmap map[string]map[string][]*cassandra.Mutation var tsprefix []byte var prod Product var mutations []*cassandra.Mutation var mutation *cassandra.Mutation var col *cassandra.Column var ire *cassandra.InvalidRequestException var ue *cassandra.UnavailableException var te *cassandra.TimedOutException var barcode string var now time.Time = time.Now() var err error var match bool numRequests.Add(1) numAPIRequests.Add(1) // Check the user is in the reqeuested scope. if !self.authenticator.IsAuthenticatedScope(req, self.scope) { numDisallowedScope.Add(1) http.Error(w, "You are not in the right group to access this resource", http.StatusForbidden) return } // Check if the product name has been specified. prod.Name = req.PostFormValue("prodname") if len(prod.Name) <= 0 { log.Print("Parsing product name: ", err) http.Error(w, "Product name empty", http.StatusNotAcceptable) return } prod.Price, err = strconv.ParseFloat(req.PostFormValue("price"), 64) if err != nil { log.Print("Parsing price: ", err) http.Error(w, "price: "+err.Error(), http.StatusNotAcceptable) return } prod.Stock, err = strconv.ParseUint(req.PostFormValue("stock"), 10, 64) if err != nil { log.Print("Parsing stock: ", err) http.Error(w, "stock: "+err.Error(), http.StatusNotAcceptable) return } // Check if the barcode has been given. If it was, it needs to be // numeric (EAN-13). If we find different types of barcodes we can // always revise this. for _, barcode = range req.PostForm["barcode"] { barcode = strings.Replace(barcode, " ", "", -1) if len(barcode) > 0 { match, err = regexp.MatchString("^[0-9]+$", barcode) if err != nil { productEditErrors.Add(err.Error(), 1) http.Error(w, err.Error(), http.StatusInternalServerError) return } if match { codes.Barcode = append(codes.Barcode, barcode) } else { productEditErrors.Add("barcode-format-error", 1) http.Error(w, "Barcode should only contain numbers", http.StatusNotAcceptable) return } } } // Create column data for the product row. col = cassandra.NewColumn() col.Name = []byte("name") col.Value = []byte(prod.Name) col.Timestamp = now.Unix() mutation = cassandra.NewMutation() mutation.ColumnOrSupercolumn = cassandra.NewColumnOrSuperColumn() mutation.ColumnOrSupercolumn.Column = col mutations = append(mutations, mutation) err = binary.Write(buf, binary.BigEndian, prod.Price) if err != nil { productEditErrors.Add(err.Error(), 1) http.Error(w, err.Error(), http.StatusInternalServerError) return } col = cassandra.NewColumn() col.Name = []byte("price") col.Value = buf.Bytes() col.Timestamp = now.Unix() mutation = cassandra.NewMutation() mutation.ColumnOrSupercolumn = cassandra.NewColumnOrSuperColumn() mutation.ColumnOrSupercolumn.Column = col mutations = append(mutations, mutation) buf = new(bytes.Buffer) err = binary.Write(buf, binary.BigEndian, prod.Stock) if err != nil { productEditErrors.Add(err.Error(), 1) http.Error(w, err.Error(), http.StatusInternalServerError) return } col = cassandra.NewColumn() col.Name = []byte("stock") col.Value = buf.Bytes() col.Timestamp = now.Unix() mutation = cassandra.NewMutation() mutation.ColumnOrSupercolumn = cassandra.NewColumnOrSuperColumn() mutation.ColumnOrSupercolumn.Column = col mutations = append(mutations, mutation) col = cassandra.NewColumn() col.Name = []byte("barcodes") col.Value, err = proto.Marshal(codes) if err != nil { productEditErrors.Add(err.Error(), 1) http.Error(w, err.Error(), http.StatusInternalServerError) return } col.Timestamp = now.Unix() mutation = cassandra.NewMutation() mutation.ColumnOrSupercolumn = cassandra.NewColumnOrSuperColumn() mutation.ColumnOrSupercolumn.Column = col mutations = append(mutations, mutation) // If we're editing an existing product, re-use that UUID. Otherwise, // generate one. if len(specid) > 0 { uuid, err = ParseUUID(specid) } else { uuid, err = GenTimeUUID(&now) } if err != nil { productEditErrors.Add(err.Error(), 1) http.Error(w, err.Error(), http.StatusInternalServerError) return } mmap = make(map[string]map[string][]*cassandra.Mutation) mmap[string(uuid)] = make(map[string][]*cassandra.Mutation) mmap[string(uuid)]["products"] = mutations // Create the entry in the products_byname index. mutations = make([]*cassandra.Mutation, 0) col = cassandra.NewColumn() col.Name = []byte("product") col.Value = uuid col.Timestamp = now.Unix() mutation = cassandra.NewMutation() mutation.ColumnOrSupercolumn = cassandra.NewColumnOrSuperColumn() mutation.ColumnOrSupercolumn.Column = col mutations = append(mutations, mutation) mmap[prod.Name] = make(map[string][]*cassandra.Mutation) mmap[prod.Name]["products_byname"] = mutations // If a barcode has been given, specify it in the products_bybarcode // column. if len(barcode) > 0 { mmap[barcode] = make(map[string][]*cassandra.Mutation) mmap[barcode]["products_bybarcode"] = mutations } // Log a timeseries entry stating that the product has been present // at the time. mutations = make([]*cassandra.Mutation, 0) col = cassandra.NewColumn() col.Name = []byte("product-count") col.Value = buf.Bytes() // still the product count from above. col.Timestamp = now.Unix() mutation = cassandra.NewMutation() mutation.ColumnOrSupercolumn = cassandra.NewColumnOrSuperColumn() mutation.ColumnOrSupercolumn.Column = col mutations = append(mutations, mutation) // Create the TS prefix: first the UUID, then the timestamp. buf = new(bytes.Buffer) err = binary.Write(buf, binary.BigEndian, now.UnixNano()) if err != nil { productEditErrors.Add(err.Error(), 1) http.Error(w, err.Error(), http.StatusInternalServerError) return } tsprefix = make([]byte, len(uuid)+buf.Len()+1) copy(tsprefix, uuid) tsprefix[len(uuid)] = ':' for i, v := range buf.Bytes() { tsprefix[len(uuid)+i+1] = v } mmap[string(tsprefix)] = make(map[string][]*cassandra.Mutation) mmap[string(tsprefix)]["product_tsdata"] = mutations // Now, write the mutations to the database. ire, ue, te, err = self.client.AtomicBatchMutate(mmap, cassandra.ConsistencyLevel_QUORUM) if ire != nil { log.Println("Invalid request: ", ire.Why) productEditErrors.Add(ire.Why, 1) return } if ue != nil { log.Println("Unavailable") productEditErrors.Add("unavailable", 1) return } if te != nil { log.Println("Request to database backend timed out") productEditErrors.Add("timeout", 1) return } if err != nil { log.Println("Generic error: ", err) productEditErrors.Add(err.Error(), 1) return } }