Пример #1
0
// Marks a bid as placed. This is purely informational for the user.
func PlaceBid(c appengine.Context, bidId string) error {
	var key *datastore.Key
	if k, err := datastore.DecodeKey(bidId); err != nil {
		return err
	} else {
		key = k
	}

	f := func(c appengine.Context) error {
		var bid Bid
		if err := datastore.Get(c, key, bidCodec{&bid}); err != nil {
			return err
		}

		if bid.State != InQueue {
			c.Infof("Not placing bid %v : State=%v", key, bid.State)
			return nil
		}

		bid.State = Placed

		if _, err := datastore.Put(c, key, bidCodec{&bid}); err != nil {
			return err
		}
		return nil
	}

	return datastore.RunInTransaction(c, f, nil)
}
Пример #2
0
func ParseFeed(c appengine.Context, contentType, origUrl, fetchUrl string, body []byte) (*Feed, []*Story, error) {
	cr := defaultCharsetReader
	if !bytes.EqualFold(body[:len(xml.Header)], []byte(xml.Header)) {
		enc, err := encodingReader(body, contentType)
		if err != nil {
			return nil, nil, err
		}
		if enc != encoding.Nop {
			cr = nilCharsetReader
			body, err = ioutil.ReadAll(transform.NewReader(bytes.NewReader(body), enc.NewDecoder()))
			if err != nil {
				return nil, nil, err
			}
		}
	}
	var feed *Feed
	var stories []*Story
	var atomerr, rsserr, rdferr error
	feed, stories, atomerr = parseAtom(c, body, cr)
	if feed == nil {
		feed, stories, rsserr = parseRSS(c, body, cr)
	}
	if feed == nil {
		feed, stories, rdferr = parseRDF(c, body, cr)
	}
	if feed == nil {
		c.Warningf("atom parse error: %s", atomerr.Error())
		c.Warningf("xml parse error: %s", rsserr.Error())
		c.Warningf("rdf parse error: %s", rdferr.Error())
		return nil, nil, fmt.Errorf("Could not parse feed data")
	}
	feed.Url = origUrl
	return parseFix(c, feed, stories, fetchUrl)
}
Пример #3
0
func getTickets(c appengine.Context, status string, newStatus string, limit int) ([]Ticket, []*datastore.Key, error) {

	c.Debugf("getTickets: ", status)
	statusKey, err := getStatusByType(c, status)
	if err != nil {
		return nil, nil, err
	}

	c.Debugf("statusKey: ", statusKey)

	q := datastore.NewQuery(DATASTORE_TICKET).
		Filter("Status =", statusKey).
		//Filter("SellerUrl >", "").
		Order("Modified").
		Limit(limit)

	var tickets []Ticket
	ticket_keys, err := q.GetAll(c, &tickets)
	if err != nil {
		return nil, nil, err
	}

	if len(tickets) < 1 {
		return nil, nil, fmt.Errorf("getTickets: No new tickets found with status: %s", status)
	}

	if newStatus != "" {
		err = setTicketsStatus(c, tickets, ticket_keys, newStatus)
		if err != nil {
			return nil, nil, err
		}
	}

	return tickets, ticket_keys, nil
}
Пример #4
0
func (e Event) GetHTMLView(c appengine.Context) string {
	buffer := new(bytes.Buffer)
	var tmpltxt = `<label>Event Title: </label><a href="https://orgreminders.appspot.com/editevent?id={{.Key}}">{{.Title}}</a>
	<br>
	<label>When Due: </label>{{.DueFormatted}}
	<br>
	<label>Organization(s): </label>{{range .Orgs}}{{.}},{{end}}
	<br>
	<label>Email enabled: </label>{{.Email}}
	<br>
	<label>Text Enabled: </label>{{.Text}}
	<br>
	<label>Email Message: </label><br><div class="msgbody">{{.EmailMessage}}</div>
	<br>
	<label>Text Message: </label><br><div class="msgbody"><pre>{{.TextMessage}}</pre></div>
	<br>`

	template, terr := template.New("foo").Parse(tmpltxt)

	if terr != nil {
		c.Infof("error parsing event html template: %v", terr)
		return ""
	}

	template.Execute(buffer, e)
	return buffer.String()
}
Пример #5
0
func searchUser(instanceId string, c appengine.Context) (key *datastore.Key, user *User, err error) {
	var v []User
	// Initial variables
	key = nil
	user = nil
	err = nil

	// Query
	f := datastore.NewQuery(UserKind)
	f = f.Filter("InstanceId=", instanceId)
	k, err := f.GetAll(c, &v)
	if err != nil {
		c.Errorf("%s in getting data from datastore\n", err)
		err = errors.New("Datastore is temporary unavailable")
		return
	}

	if k == nil || len(k) == 0 {
		return
	}

	key = k[0]
	user = &v[0]
	return
}
Пример #6
0
// Handler to find all questions with specific tags
func tagHandler(w http.ResponseWriter, r *http.Request, c appengine.Context, user stackongo.User) {
	// Collect query
	tag := r.FormValue("tagSearch")
	// Create and fill in a new webData struct
	tempData := newWebData()

	data.CacheLock.Lock()
	// range through the question caches golang stackongoand add if the question contains the tag
	for cacheType, cache := range data.Caches {
		for _, question := range cache {
			if contains(question.Tags, tag) {
				tempData.Caches[cacheType] = append(tempData.Caches[cacheType], question)
			}
		}
	}
	tempData.Qns = data.Qns
	data.CacheLock.Unlock()

	page := template.Must(template.ParseFiles("public/template.html"))
	var tagQuery = []string{
		"tag",
		tag,
	}
	if err := page.Execute(w, writeResponse(user, tempData, c, tagQuery)); err != nil {
		c.Criticalf("%v", err.Error())
	}
}
Пример #7
0
//This is the main tags page
//Should display a list of tags that are logged in the database
//User can either click on a tag to view any questions containing that tag or search by a specific tag
func viewTagsHandler(w http.ResponseWriter, r *http.Request, c appengine.Context, user stackongo.User) {
	//Read all tags and their counts from the db, and execute the page
	query := readTagsFromDb()
	//Format array of tags into another array, to be easier formatted on the page into a table
	//An array of tagData arrays of size 4
	var tagArray [][]tagData
	var tempTagArray []tagData
	i := 0
	for _, t := range query {
		tempTagArray = append(tempTagArray, t)
		i++
		if i == 4 {
			tagArray = append(tagArray, tempTagArray)
			i = 0
			//clear the temp array.
			tempTagArray = nil
		}
	}
	tagArray = append(tagArray, tempTagArray)
	page := template.Must(template.ParseFiles("public/viewTags.html"))
	if err := page.Execute(w, queryReply{user, tagArray}); err != nil {
		c.Criticalf("%v", err.Error())
	}

}
Пример #8
0
Файл: app.go Проект: 0x7cc/rsc
func read(c appengine.Context, name string) (fi *FileInfo, data []byte, err error) {
	name, _, _ = mangle(c, name)
	fi1, err := stat(c, name)
	if err != nil {
		return nil, nil, err
	}
	if fi1.IsDir {
		dt, err := readdir(c, name)
		if err != nil {
			return nil, nil, err
		}
		fi = fi1
		data = dt
		return fi, data, nil
	}

	root := datastore.NewKey(c, "RootKey", "v2:", 0, nil)
	dkey := datastore.NewKey(c, "FileData", "", fi1.Qid, root)
	var fd FileData
	c.Infof("DATASTORE Read %q", name)
	if err := datastore.Get(c, dkey, &fd); err != nil {
		return nil, nil, err
	}
	fi = fi1
	data = fd.Data
	return fi, data, nil
}
Пример #9
0
func createPostKeyID(postID string, c appengine.Context) string {
	if postID == "" {
		c.Errorf("Creating a post entity key with no postID!")
	}

	return "post:" + postID
}
Пример #10
0
func HandleEvent(tags *event.EventHeaderTags, ev event.Event, ctx appengine.Context, sender EventSendService) error {
	res, err := handleRecvEvent(tags, ev, ctx)
	if nil != err {
		ctx.Errorf("Failed to handle event[%d:%d] for reason:%v", ev.GetType(), ev.GetVersion(), err)
		return err
	}
	if nil == res {
		var empty bytes.Buffer
		sender.Send(&empty)
		return nil
	}
	res.SetHash(ev.GetHash())
	compressType := Cfg.CompressType
	if httpres, ok := res.(*event.HTTPResponseEvent); ok {
		v := httpres.GetHeader("Content-Type")
		if len(v) > 0 && Cfg.CompressType != event.COMPRESSOR_NONE {
			if isContentTypeInCompressFilter(v) {
				compressType = event.COMPRESSOR_NONE
			}
		}
	}
	x := new(event.CompressEvent)
	x.SetHash(ev.GetHash())
	x.CompressType = compressType
	x.Ev = res
	y := new(event.EncryptEvent)
	y.SetHash(ev.GetHash())
	y.EncryptType = Cfg.EncryptType
	y.Ev = x
	var buf bytes.Buffer
	tags.Encode(&buf)
	event.EncodeEvent(&buf, y)
	sender.Send(&buf)
	return nil
}
Пример #11
0
func RetrieveActDetails(c appengine.Context, actId int) (res *ActDetail, err os.Error) {
	var d DSActDetail
	if itm, err := memcache.Get(c, "actId__"+strconv.Itoa(actId)); err != nil && err != memcache.ErrCacheMiss {
		return nil, err
	} else if err == nil {
		// Cache hit
		buf := bytes.NewBuffer(itm.Value)
		dec := gob.NewDecoder(buf)
		dec.Decode(&d)
	} else {
		// Cache miss
		key := datastore.NewKey(c, "DSActDetail", "", int64(actId), nil)
		if err := datastore.Get(c, key, &d); err == datastore.ErrNoSuchEntity {
			return &ActDetail{ActId: actId}, nil
		} else if err != nil {
			return nil, err
		}
		buf := bytes.NewBufferString("")
		enc := gob.NewEncoder(buf)
		enc.Encode(d)

		itm := &memcache.Item{
			Key:   "actId__" + strconv.Itoa(actId),
			Value: buf.Bytes(),
		}

		err = memcache.Set(c, itm)
		c.Debugf("Request cache to memcache")
	}
	return d.fromDS(), nil
}
Пример #12
0
// AllocateIDs returns a range of n integer IDs with the given kind and parent
// combination. kind cannot be empty; parent may be nil. The IDs in the range
// returned will not be used by the datastore's automatic ID sequence generator
// and may be used with NewKey without conflict.
//
// The range is inclusive at the low end and exclusive at the high end. In
// other words, valid intIDs x satisfy low <= x && x < high.
//
// If no error is returned, low + n == high.
func AllocateIDs(c appengine.Context, kind string, parent *Key, n int) (low, high int64, err error) {
	if kind == "" {
		return 0, 0, errors.New("datastore: AllocateIDs given an empty kind")
	}
	if n < 0 {
		return 0, 0, fmt.Errorf("datastore: AllocateIDs given a negative count: %d", n)
	}
	if n == 0 {
		return 0, 0, nil
	}
	req := &pb.AllocateIdsRequest{
		ModelKey: keyToProto("", NewIncompleteKey(c, kind, parent)),
		Size:     proto.Int64(int64(n)),
	}
	res := &pb.AllocateIdsResponse{}
	if err := c.Call("datastore_v3", "AllocateIds", req, res, nil); err != nil {
		return 0, 0, err
	}
	// The protobuf is inclusive at both ends. Idiomatic Go (e.g. slices, for loops)
	// is inclusive at the low end and exclusive at the high end, so we add 1.
	low = res.GetStart()
	high = res.GetEnd() + 1
	if low+int64(n) != high {
		return 0, 0, fmt.Errorf("datastore: internal error: could not allocate %d IDs", n)
	}
	return low, high, nil
}
Пример #13
0
// Purge removes all tasks from a queue.
func Purge(c appengine.Context, queueName string) error {
	req := &taskqueue_proto.TaskQueuePurgeQueueRequest{
		QueueName: []byte(queueName),
	}
	res := &taskqueue_proto.TaskQueuePurgeQueueResponse{}
	return c.Call("taskqueue", "PurgeQueue", req, res, nil)
}
Пример #14
0
func createUserKeyID(userID string, c appengine.Context) string {
	if userID == "" {
		c.Errorf("Creating an appUser entity key with no userID!")
	}

	return "user:" + userID
}
Пример #15
0
func count_uv_pv(c appengine.Context, mins int) (uv int, pv int) {
	count := 0
	uniq := make(map[string]bool)
	query := &log.Query{
		AppLogs:   true,
		StartTime: time.Now().Add(time.Duration(-1*mins) * time.Minute),
		Versions:  []string{"1"},
	}
	for results := query.Run(c); ; {
		record, err := results.Next()
		if err == log.Done {
			break
		}
		if err != nil {
			c.Errorf("Failed to retrieve next log: %v", err)
		}

		if len(record.AppLogs) > 0 && strings.Index(record.Combined, "GET "+config.URL_BEACON) > 0 {
			zcookie := record.AppLogs[0].Message
			if zcookie != "" {
				count++
				uniq[zcookie] = true
			}
		}
	}
	uv = len(uniq)
	pv = count
	return
}
Пример #16
0
// DeleteMulti is a batch version of Delete.
// If any keys cannot be found, an appengine.MultiError is returned.
// Each key must be at most 250 bytes in length.
func DeleteMulti(c appengine.Context, key []string) error {
	req := &pb.MemcacheDeleteRequest{
		Item: make([]*pb.MemcacheDeleteRequest_Item, len(key)),
	}
	for i, k := range key {
		req.Item[i] = &pb.MemcacheDeleteRequest_Item{Key: []byte(k)}
	}
	res := &pb.MemcacheDeleteResponse{}
	if err := c.Call("memcache", "Delete", req, res, nil); err != nil {
		return err
	}
	if len(res.DeleteStatus) != len(key) {
		return ErrServerError
	}
	me, any := make(appengine.MultiError, len(key)), false
	for i, s := range res.DeleteStatus {
		switch s {
		case pb.MemcacheDeleteResponse_DELETED:
			// OK
		case pb.MemcacheDeleteResponse_NOT_FOUND:
			me[i] = ErrCacheMiss
			any = true
		default:
			me[i] = ErrServerError
			any = true
		}
	}
	if any {
		return me
	}
	return nil
}
Пример #17
0
// Sends an email to the author of part with a link to continue.
func sendMail(c appengine.Context, story Story) {
	if story.Complete {
		return
	}
	var subject, text string
	part := story.LastPart()
	url := fmt.Sprintf(serverUrl, story.Id, story.NextId)
	if part != nil {
		subject = "Please write the next part of this story."
		text = fmt.Sprintf("%s, %s wrote:\n> %s\n\nPlease visit %s to write the next part.",
			capital(fuzzyTime(part.Written)), getFullEmail(c, part.Author), part.Visible, url)
	} else {
		subject = "Please write the first part of this story."
		text = fmt.Sprintf("%s, %s initiated a new story.\n\nPlease visit %s to write the beginning.",
			capital(fuzzyTime(story.Created)), getFullEmail(c, story.Creator), url)
	}

	msg := &mail.Message{
		Sender:  sender,
		To:      []string{story.NextAuthor},
		Subject: subject,
		Body:    text,
	}
	if err := mail.Send(c, msg); err != nil {
		c.Errorf("Couldn't send email: %v", err)
		panic(err)
	}
}
Пример #18
0
func load(c appengine.Context, id string) (*Group, error) {
	group := &Group{}
	_, err := memcache.JSON.Get(c, id, group)
	if err == nil {
		return group, nil
	}
	if err != memcache.ErrCacheMiss {
		c.Errorf("memcache get %q: %v", id, err)
	}

	group, err = fetch(c, id)
	if err != nil {
		return nil, err
	}

	item := &memcache.Item{
		Key:        id,
		Object:     group,
		Expiration: time.Hour,
	}
	err = memcache.JSON.Set(c, item)
	if err != nil {
		c.Errorf("memcache set %q: %v", id, err)
	}
	return group, nil
}
Пример #19
0
// Handler to find all questions answered/being answered by the user in URL
func userHandler(w http.ResponseWriter, r *http.Request, c appengine.Context, user stackongo.User) {
	userID, _ := strconv.Atoi(r.FormValue("id"))
	query := userData{}

	// Create and fill in a new webData struct
	tempData := newWebData()

	data.CacheLock.Lock()
	// range through the question caches golang stackongo and add if the question contains the tag
	tempData.Caches["unanswered"] = data.Caches["unanswered"]
	if userQuery, ok := data.Users[userID]; ok {
		query = userQuery
		for cacheType, cache := range data.Users[userID].Caches {
			if cacheType != "unanswered" {
				tempData.Caches[cacheType] = cache
			}
		}
		tempData.Qns = data.Qns
	}
	data.CacheLock.Unlock()
	page := template.Must(template.ParseFiles("public/template.html"))

	var userQuery = []string{
		"user",
		query.User_info.Display_name,
	}
	if err := page.Execute(w, writeResponse(user, tempData, c, userQuery)); err != nil {
		c.Criticalf("%v", err.Error())
	}
}
Пример #20
0
// DeleteMulti is a batch version of Delete.
// The returned slice will have the same length as the input slice.
// If a given key cannot be found, its corresponding value in the
// returned error slice is set to ErrCacheMiss.
// Each key must be at most 250 bytes in length.
func DeleteMulti(c appengine.Context, key []string) []error {
	req := &pb.MemcacheDeleteRequest{
		Item: make([]*pb.MemcacheDeleteRequest_Item, len(key)),
	}
	for i, k := range key {
		req.Item[i] = &pb.MemcacheDeleteRequest_Item{Key: []byte(k)}
	}
	res := &pb.MemcacheDeleteResponse{}
	e := make([]error, len(key))
	err := c.Call("memcache", "Delete", req, res, nil)
	if err == nil && len(e) != len(res.DeleteStatus) {
		err = ErrServerError
	}
	if err != nil {
		for i := range e {
			e[i] = err
		}
		return e
	}
	for i, s := range res.DeleteStatus {
		switch s {
		case pb.MemcacheDeleteResponse_DELETED:
			e[i] = nil
		case pb.MemcacheDeleteResponse_NOT_FOUND:
			e[i] = ErrCacheMiss
		default:
			e[i] = ErrServerError
		}
	}
	return e
}
Пример #21
0
func userPageHandler(w http.ResponseWriter, r *http.Request, c appengine.Context, user stackongo.User) {
	page := template.Must(template.ParseFiles("public/userPage.html"))
	usr, _ := strconv.Atoi(r.FormValue("userId"))
	currentUser := data.Users[usr]
	query := userData{User_info: currentUser.User_info}

	var n int
	query.Caches = make(map[string][]stackongo.Question)

	n = Min(3, len(currentUser.Caches["unanswered"]))
	if n > 0 {
		query.Caches["answered"] = currentUser.Caches["answered"][0:n]
	}
	n = Min(3, len(currentUser.Caches["pending"]))
	if n > 0 {
		query.Caches["pending"] = currentUser.Caches["pending"][0:n]
	}

	n = Min(3, len(currentUser.Caches["updating"]))
	if n > 0 {
		query.Caches["updating"] = currentUser.Caches["updating"][0:n]
	}
	if err := page.Execute(w, queryReply{user, query}); err != nil {
		c.Criticalf("%v", err.Error())
	}
}
Пример #22
0
func writeEncoded(c appengine.Context, w http.ResponseWriter, data interface{}) {
	newEncoder := bencode.NewEncoder(w)
	if err := newEncoder.Encode(data); err != nil {
		c.Errorf("Failed to encode data: %s", err)
		writeInternalError(w)
	}
}
Пример #23
0
func searchGroup(name string, c appengine.Context) (key *datastore.Key, group *Group, err error) {
	var v []Group
	// Initial variables
	key = nil
	group = nil
	err = nil

	// Query
	f := datastore.NewQuery(GroupKind)
	f = f.Filter("Name=", name)
	k, err := f.GetAll(c, &v)
	if err != nil {
		c.Errorf("%s in getting data from datastore\n", err)
		err = errors.New("Datastore is temporary unavailable")
		return
	}

	if k == nil || len(k) == 0 {
		return
	}

	key = k[0]
	group = &v[0]
	return
}
Пример #24
0
func GetAllAccounts(c appengine.Context) ([]string, error) {

	accounts := []string{}
	_, err := memcache.Gob.Get(c, "AccountsArray", &accounts)
	if err == nil {
		return accounts, nil
	}

	var accountsDS []AccountDS
	query := datastore.NewQuery("Account")
	_, err = query.GetAll(c, &accountsDS)
	if err != nil {
		return []string{}, err
	}

	for _, account := range accountsDS {
		accounts = append(accounts, account.UserID)
	}

	cacheItem := &memcache.Item{
		Key:    "AccountsArray",
		Object: accounts,
	}

	err = memcache.Gob.Set(c, cacheItem)
	if err != nil {
		c.Infof("Accounts array not cached (%v)", err)
	}

	return accounts, err
}
Пример #25
0
// Send sends a message.
// If any failures occur with specific recipients, the error will be an appengine.MultiError.
func (m *Message) Send(c appengine.Context) error {
	req := &pb.XmppMessageRequest{
		Jid:    m.To,
		Body:   &m.Body,
		RawXml: &m.RawXML,
	}
	if m.Type != "" && m.Type != "chat" {
		req.Type = &m.Type
	}
	if m.Sender != "" {
		req.FromJid = &m.Sender
	}
	res := &pb.XmppMessageResponse{}
	if err := c.Call("xmpp", "SendMessage", req, res, nil); err != nil {
		return err
	}

	if len(res.Status) != len(req.Jid) {
		return fmt.Errorf("xmpp: sent message to %d JIDs, but only got %d statuses back", len(req.Jid), len(res.Status))
	}
	me, any := make(appengine.MultiError, len(req.Jid)), false
	for i, st := range res.Status {
		if st != pb.XmppMessageResponse_NO_ERROR {
			me[i] = errors.New(st.String())
			any = true
		}
	}
	if any {
		return me
	}
	return nil
}
Пример #26
0
// extracts the URL from an Feed. If the feed has publication date, we only
// add the URL since the last feed update
func getLinksFromFeed(dsFeed *data.Feed, r *http.Request, c appengine.Context) ([]string, error) {
	c.Infof("DS Feed @ importer: %v", dsFeed)
	_, body, err := extract.GetURL(dsFeed.URL, r)
	if err != nil {
		return nil, err
	}

	rss, err := parseFeedContent(body)
	if err != nil {
		return nil, err
	}

	var urls []string
	stopURL := dsFeed.LastURL
	for index, i := range rss.ItemList {
		if index == 0 {
			dsFeed.LastURL = i.Link
		}
		if i.Link == stopURL {
			break
		}
		urls = append(urls, i.Link)
	}

	return urls, nil
}
Пример #27
0
func addLogo(profilePtr *image.Image, logo string, context appengine.Context) []byte {
	profileImage := *profilePtr
	destImage := image.NewRGBA(profileImage.Bounds())
	draw.Draw(destImage, destImage.Bounds(), profileImage, image.ZP, draw.Src)

	if logoImages, ok := THELOGOIMAGES[logo]; ok {
		randi := rand.Intn(len(logoImages))
		logoImage := logoImages[randi]

		offset := image.Pt(5, 5)
		if strings.HasPrefix(logo, "NLD-") {
			offset = image.Pt(0, 0)
		}

		start := profileImage.Bounds().Size()
		start = start.Sub(offset)
		start = start.Sub(logoImage.Bounds().Size())

		bounds := image.Rectangle{start, start.Add(logoImage.Bounds().Size())}
		draw.Draw(destImage, bounds, logoImage, image.ZP, draw.Over)

	} else {
		context.Errorf("Cannot load logoimage for %s", logo)
	}

	buffer := new(bytes.Buffer)
	err := png.Encode(buffer, destImage)
	check(err, context)

	return buffer.Bytes()
}
Пример #28
0
func dumpMessage(c appengine.Context, msg *mail.Message) {
	iw := InfoWriter{c}

	c.Infof("--BEGIN MESSAGE")
	io.Copy(iw, msg.Body)
	c.Infof("END MESSAGE--")
}
Пример #29
0
// NewPage returns a new Page initialized embedding the template with the
// given name and data, the current user for the given context, and the
// latest announcement.
func NewPage(ctx appengine.Context, name string, data interface{}) (*Page, error) {
	p := &Page{
		Content: name,
		Data:    data,
		Topics:  topicList,
		Cities:  cityList,
	}

	a, err := conf.LatestAnnouncement(ctx)
	if err != nil {
		ctx.Errorf("latest announcement: %v", err)
	}
	if a != nil {
		p.Announcement = a.Message
	}

	if u := user.Current(ctx); u != nil {
		p.User = u
		p.LogoutURL, err = user.LogoutURL(ctx, "/")
	} else {
		p.LoginURL, err = user.LoginURL(ctx, "/")
	}

	return p, err
}
Пример #30
0
// This will reimburse the bid's price and fee to the buyer.
func RetireBid(c appengine.Context, key *datastore.Key) error {
	f := func(c appengine.Context) error {
		now := time.Now()
		dao := NewGaeAccountingDao(c, true)
		var bid Bid
		if err := datastore.Get(c, key, bidCodec{&bid}); err != nil {
			return err
		}

		if bid.State == Matched {
			c.Infof("Not retiring matched bid %v", key)
			return nil
		}

		if err := bid.Retire(dao, key.Encode(), now); err != nil {
			return err
		}

		if _, err := datastore.Put(c, key, bidCodec{&bid}); err != nil {
			return err
		}

		return dao.Flush()
	}

	if err := datastore.RunInTransaction(c, f, &datastore.TransactionOptions{XG: true}); err != nil {
		return err
	}

	return nil
}