<commit_before>package telebot

import (
	"strconv"
	"time"
)

// Message object represents a message.
type Message struct {
	ID int `json:"message_id"`

	InlineID string `json:"-"`

	// For message sent to channels, Sender will be nil
	Sender *User `json:"from"`

	// Unixtime, use Message.Time() to get time.Time
	Unixtime int64 `json:"date"`

	// Conversation the message belongs to.
	Chat *Chat `json:"chat"`

	// For forwarded messages, sender of the original message.
	OriginalSender *User `json:"forward_from"`

	// For forwarded messages, chat of the original message when
	// forwarded from a channel.
	OriginalChat *Chat `json:"forward_from_chat"`

	// For forwarded messages, unixtime of the original message.
	OriginalUnixtime int `json:"forward_date"`

	// For replies, ReplyTo represents the original message.
	//
	// Note that the Message object in this field will not
	// contain further ReplyTo fields even if it
	// itself is a reply.
	ReplyTo *Message `json:"reply_to_message"`

	// (Optional) Time of last edit in Unix
	LastEdit int64 `json:"edit_date"`

	// AlbumID is the unique identifier of a media message group
	// this message belongs to.
	AlbumID string `json:"media_group_id"`

	// Author signature (in channels).
	Signature string `json:"author_signature"`

	// For a text message, the actual UTF-8 text of the message.
	Text string `json:"text"`

	// For registered commands, will contain the string payload:
	//
	// Ex: `/command <payload>` or `/command@botname <payload>`
	Payload string `json:"-"`

	// For text messages, special entities like usernames, URLs, bot commands,
	// etc. that appear in the text.
	Entities []MessageEntity `json:"entities,omitempty"`

	// Some messages containing media, may as well have a caption.
	Caption string `json:"caption,omitempty"`

	// For messages with a caption, special entities like usernames, URLs,
	// bot commands, etc. that appear in the caption.
	CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`

	// For an audio recording, information about it.
	Audio *Audio `json:"audio"`

	// For a gneral file, information about it.
	Document *Document `json:"document"`

	// For a photo, all available sizes (thumbnails).
	Photo *Photo `json:"photo"`

	// For a sticker, information about it.
	Sticker *Sticker `json:"sticker"`

	// For a voice message, information about it.
	Voice *Voice `json:"voice"`

	// For a video note, information about it.
	VideoNote *VideoNote `json:"video_note"`

	// For a video, information about it.
	Video *Video `json:"video"`

	// For a animation, information about it.
	Animation *Animation `json:"animation"`

	// For a contact, contact information itself.
	Contact *Contact `json:"contact"`

	// For a location, its longitude and latitude.
	Location *Location `json:"location"`

	// For a venue, information about it.
	Venue *Venue `json:"venue"`

	// For a poll, information the native poll.
	Poll *Poll `json:"poll"`

	// For a dice, information about it.
	Dice *Dice `json:"dice"`

	// For a service message, represents a user,
	// that just got added to chat, this message came from.
	//
	// Sender leads to User, capable of invite.
	//
	// UserJoined might be the Bot itself.
	UserJoined *User `json:"new_chat_member"`

	// For a service message, represents a user,
	// that just left chat, this message came from.
	//
	// If user was kicked, Sender leads to a User,
	// capable of this kick.
	//
	// UserLeft might be the Bot itself.
	UserLeft *User `json:"left_chat_member"`

	// For a service message, represents a new title
	// for chat this message came from.
	//
	// Sender would lead to a User, capable of change.
	NewGroupTitle string `json:"new_chat_title"`

	// For a service message, represents all available
	// thumbnails of the new chat photo.
	//
	// Sender would lead to a User, capable of change.
	NewGroupPhoto *Photo `json:"new_chat_photo"`

	// For a service message, new members that were added to
	// the group or supergroup and information about them
	// (the bot itself may be one of these members).
	UsersJoined []User `json:"new_chat_members"`

	// For a service message, true if chat photo just
	// got removed.
	//
	// Sender would lead to a User, capable of change.
	GroupPhotoDeleted bool `json:"delete_chat_photo"`

	// For a service message, true if group has been created.
	//
	// You would receive such a message if you are one of
	// initial group chat members.
	//
	// Sender would lead to creator of the chat.
	GroupCreated bool `json:"group_chat_created"`

	// For a service message, true if super group has been created.
	//
	// You would receive such a message if you are one of
	// initial group chat members.
	//
	// Sender would lead to creator of the chat.
	SuperGroupCreated bool `json:"supergroup_chat_created"`

	// For a service message, true if channel has been created.
	//
	// You would receive such a message if you are one of
	// initial channel administrators.
	//
	// Sender would lead to creator of the chat.
	ChannelCreated bool `json:"channel_chat_created"`

	// For a service message, the destination (super group) you
	// migrated to.
	//
	// You would receive such a message when your chat has migrated
	// to a super group.
	//
	// Sender would lead to creator of the migration.
	MigrateTo int64 `json:"migrate_to_chat_id"`

	// For a service message, the Origin (normal group) you migrated
	// from.
	//
	// You would receive such a message when your chat has migrated
	// to a super group.
	//
	// Sender would lead to creator of the migration.
	MigrateFrom int64 `json:"migrate_from_chat_id"`

	// Specified message was pinned. Note that the Message object
	// in this field will not contain further ReplyTo fields even
	// if it is itself a reply.
	PinnedMessage *Message `json:"pinned_message"`

	// Inline keyboard attached to the message.
	ReplyMarkup InlineKeyboardMarkup `json:"reply_markup"`
}

// MessageEntity object represents "special" parts of text messages,
// including hashtags, usernames, URLs, etc.
type MessageEntity struct {
	// Specifies entity type.
	Type EntityType `json:"type"`

	// Offset in UTF-16 code units to the start of the entity.
	Offset int `json:"offset"`

	// Length of the entity in UTF-16 code units.
	Length int `json:"length"`

	// (Optional) For EntityTextLink entity type only.
	//
	// URL will be opened after user taps on the text.
	URL string `json:"url,omitempty"`

	// (Optional) For EntityTMention entity type only.
	User *User `json:"user,omitempty"`
}

// MessageSig satisfies Editable interface (see Editable.)
func (m *Message) MessageSig() (string, int64) {
	if m.InlineID != "" {
		return m.InlineID, 0
	}
	return strconv.Itoa(m.ID), m.Chat.ID
}

// Time returns the moment of message creation in local time.
func (m *Message) Time() time.Time {
	return time.Unix(m.Unixtime, 0)
}

// LastEdited returns time.Time of last edit.
func (m *Message) LastEdited() time.Time {
	return time.Unix(m.LastEdit, 0)
}

// IsForwarded says whether message is forwarded copy of another
// message or not.
func (m *Message) IsForwarded() bool {
	return m.OriginalSender != nil || m.OriginalChat != nil
}

// IsReply says whether message is a reply to another message.
func (m *Message) IsReply() bool {
	return m.ReplyTo != nil
}

// Private returns true, if it's a personal message.
func (m *Message) Private() bool {
	return m.Chat.Type == ChatPrivate
}

// FromGroup returns true, if message came from a group OR
// a super group.
func (m *Message) FromGroup() bool {
	return m.Chat.Type == ChatGroup || m.Chat.Type == ChatSuperGroup
}

// FromChannel returns true, if message came from a channel.
func (m *Message) FromChannel() bool {
	return m.Chat.Type == ChatChannel
}

// IsService returns true, if message is a service message,
// returns false otherwise.
//
// Service messages are automatically sent messages, which
// typically occur on some global action. For instance, when
// anyone leaves the chat or chat title changes.
func (m *Message) IsService() bool {
	fact := false

	fact = fact || m.UserJoined != nil
	fact = fact || len(m.UsersJoined) > 0
	fact = fact || m.UserLeft != nil
	fact = fact || m.NewGroupTitle != ""
	fact = fact || m.NewGroupPhoto != nil
	fact = fact || m.GroupPhotoDeleted
	fact = fact || m.GroupCreated || m.SuperGroupCreated
	fact = fact || (m.MigrateTo != m.MigrateFrom)

	return fact
}
<commit_msg>message: update message types<commit_after>package telebot

import (
	"strconv"
	"time"
)

// Message object represents a message.
type Message struct {
	ID int `json:"message_id"`

	InlineID string `json:"-"`

	// For message sent to channels, Sender will be nil
	Sender *User `json:"from"`

	// Unixtime, use Message.Time() to get time.Time
	Unixtime int64 `json:"date"`

	// Conversation the message belongs to.
	Chat *Chat `json:"chat"`

	// For forwarded messages, sender of the original message.
	OriginalSender *User `json:"forward_from"`

	// For forwarded messages, chat of the original message when
	// forwarded from a channel.
	OriginalChat *Chat `json:"forward_from_chat"`

	// For forwarded messages, unixtime of the original message.
	OriginalUnixtime int `json:"forward_date"`

	// For replies, ReplyTo represents the original message.
	//
	// Note that the Message object in this field will not
	// contain further ReplyTo fields even if it
	// itself is a reply.
	ReplyTo *Message `json:"reply_to_message"`

	// (Optional) Time of last edit in Unix
	LastEdit int64 `json:"edit_date"`

	// AlbumID is the unique identifier of a media message group
	// this message belongs to.
	AlbumID string `json:"media_group_id"`

	// Author signature (in channels).
	Signature string `json:"author_signature"`

	// For a text message, the actual UTF-8 text of the message.
	Text string `json:"text"`

	// For registered commands, will contain the string payload:
	//
	// Ex: `/command <payload>` or `/command@botname <payload>`
	Payload string `json:"-"`

	// For text messages, special entities like usernames, URLs, bot commands,
	// etc. that appear in the text.
	Entities []MessageEntity `json:"entities,omitempty"`

	// Some messages containing media, may as well have a caption.
	Caption string `json:"caption,omitempty"`

	// For messages with a caption, special entities like usernames, URLs,
	// bot commands, etc. that appear in the caption.
	CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`

	// For an audio recording, information about it.
	Audio *Audio `json:"audio"`

	// For a gneral file, information about it.
	Document *Document `json:"document"`

	// For a photo, all available sizes (thumbnails).
	Photo *Photo `json:"photo"`

	// For a sticker, information about it.
	Sticker *Sticker `json:"sticker"`

	// For a voice message, information about it.
	Voice *Voice `json:"voice"`

	// For a video note, information about it.
	VideoNote *VideoNote `json:"video_note"`

	// For a video, information about it.
	Video *Video `json:"video"`

	// For a animation, information about it.
	Animation *Animation `json:"animation"`

	// For a contact, contact information itself.
	Contact *Contact `json:"contact"`

	// For a location, its longitude and latitude.
	Location *Location `json:"location"`

	// For a venue, information about it.
	Venue *Venue `json:"venue"`

	// For a poll, information the native poll.
	Poll *Poll `json:"poll"`

	// For a dice, information about it.
	Dice *Dice `json:"dice"`

	// For a service message, represents a user,
	// that just got added to chat, this message came from.
	//
	// Sender leads to User, capable of invite.
	//
	// UserJoined might be the Bot itself.
	UserJoined *User `json:"new_chat_member"`

	// For a service message, represents a user,
	// that just left chat, this message came from.
	//
	// If user was kicked, Sender leads to a User,
	// capable of this kick.
	//
	// UserLeft might be the Bot itself.
	UserLeft *User `json:"left_chat_member"`

	// For a service message, represents a new title
	// for chat this message came from.
	//
	// Sender would lead to a User, capable of change.
	NewGroupTitle string `json:"new_chat_title"`

	// For a service message, represents all available
	// thumbnails of the new chat photo.
	//
	// Sender would lead to a User, capable of change.
	NewGroupPhoto *Photo `json:"new_chat_photo"`

	// For a service message, new members that were added to
	// the group or supergroup and information about them
	// (the bot itself may be one of these members).
	UsersJoined []User `json:"new_chat_members"`

	// For a service message, true if chat photo just
	// got removed.
	//
	// Sender would lead to a User, capable of change.
	GroupPhotoDeleted bool `json:"delete_chat_photo"`

	// For a service message, true if group has been created.
	//
	// You would receive such a message if you are one of
	// initial group chat members.
	//
	// Sender would lead to creator of the chat.
	GroupCreated bool `json:"group_chat_created"`

	// For a service message, true if super group has been created.
	//
	// You would receive such a message if you are one of
	// initial group chat members.
	//
	// Sender would lead to creator of the chat.
	SuperGroupCreated bool `json:"supergroup_chat_created"`

	// For a service message, true if channel has been created.
	//
	// You would receive such a message if you are one of
	// initial channel administrators.
	//
	// Sender would lead to creator of the chat.
	ChannelCreated bool `json:"channel_chat_created"`

	// For a service message, the destination (super group) you
	// migrated to.
	//
	// You would receive such a message when your chat has migrated
	// to a super group.
	//
	// Sender would lead to creator of the migration.
	MigrateTo int64 `json:"migrate_to_chat_id"`

	// For a service message, the Origin (normal group) you migrated
	// from.
	//
	// You would receive such a message when your chat has migrated
	// to a super group.
	//
	// Sender would lead to creator of the migration.
	MigrateFrom int64 `json:"migrate_from_chat_id"`

	// Specified message was pinned. Note that the Message object
	// in this field will not contain further ReplyTo fields even
	// if it is itself a reply.
	PinnedMessage *Message `json:"pinned_message"`

	// The domain name of the website on which the user has logged in.
	ConnectedWebsite string `json:"connected_website,omitempty"`

	// Inline keyboard attached to the message.
	ReplyMarkup InlineKeyboardMarkup `json:"reply_markup"`
}

// MessageEntity object represents "special" parts of text messages,
// including hashtags, usernames, URLs, etc.
type MessageEntity struct {
	// Specifies entity type.
	Type EntityType `json:"type"`

	// Offset in UTF-16 code units to the start of the entity.
	Offset int `json:"offset"`

	// Length of the entity in UTF-16 code units.
	Length int `json:"length"`

	// (Optional) For EntityTextLink entity type only.
	//
	// URL will be opened after user taps on the text.
	URL string `json:"url,omitempty"`

	// (Optional) For EntityTMention entity type only.
	User *User `json:"user,omitempty"`

	// (Optional) For EntityCodeBlock entity type only.
	Language string `json:"language,omitempty"`
}

// MessageSig satisfies Editable interface (see Editable.)
func (m *Message) MessageSig() (string, int64) {
	if m.InlineID != "" {
		return m.InlineID, 0
	}
	return strconv.Itoa(m.ID), m.Chat.ID
}

// Time returns the moment of message creation in local time.
func (m *Message) Time() time.Time {
	return time.Unix(m.Unixtime, 0)
}

// LastEdited returns time.Time of last edit.
func (m *Message) LastEdited() time.Time {
	return time.Unix(m.LastEdit, 0)
}

// IsForwarded says whether message is forwarded copy of another
// message or not.
func (m *Message) IsForwarded() bool {
	return m.OriginalSender != nil || m.OriginalChat != nil
}

// IsReply says whether message is a reply to another message.
func (m *Message) IsReply() bool {
	return m.ReplyTo != nil
}

// Private returns true, if it's a personal message.
func (m *Message) Private() bool {
	return m.Chat.Type == ChatPrivate
}

// FromGroup returns true, if message came from a group OR
// a super group.
func (m *Message) FromGroup() bool {
	return m.Chat.Type == ChatGroup || m.Chat.Type == ChatSuperGroup
}

// FromChannel returns true, if message came from a channel.
func (m *Message) FromChannel() bool {
	return m.Chat.Type == ChatChannel
}

// IsService returns true, if message is a service message,
// returns false otherwise.
//
// Service messages are automatically sent messages, which
// typically occur on some global action. For instance, when
// anyone leaves the chat or chat title changes.
func (m *Message) IsService() bool {
	fact := false

	fact = fact || m.UserJoined != nil
	fact = fact || len(m.UsersJoined) > 0
	fact = fact || m.UserLeft != nil
	fact = fact || m.NewGroupTitle != ""
	fact = fact || m.NewGroupPhoto != nil
	fact = fact || m.GroupPhotoDeleted
	fact = fact || m.GroupCreated || m.SuperGroupCreated
	fact = fact || (m.MigrateTo != m.MigrateFrom)

	return fact
}
<|endoftext|>


<commit_before>package main

import (
    "log"
    "encoding/json"
    "errors"
    "time"
)

type Message struct {
    Event string
    Body map[string]interface{}
    Time int64
}

func (this *Message) FromSocket(sock *Socket) {
    log.Printf("Handling message fo type %s\n", this.Event)

    if this.Event == "MessageUser" {
        msg, err := this.formatBody()
        if err != nil {
            return
        }

        UID, ok := this.Body["UID"].(string)
        if !ok {
            return
        }

        rec, err := sock.Server.Store.Client(UID)
        if err != nil {
            return
        }

        rec.buff <- msg
    }

    if this.Event == "MessageAll" {
        msg_str, _ := json.Marshal(this)

        sock.Server.Store.redis.Publish("Message", string(msg_str))
    }
}

func (this *Message) FromRedis(server *Server) {
    log.Printf("Handling message fo type %s\n", this.Event)

    switch this.Event {

    case "MessageUser":
        msg, err := this.formatBody()
        if err != nil {
            return
        }

        UID, ok := this.Body["UID"].(string)
        if !ok {
            return
        }

        rec, err := server.Store.Client(UID)
        if err != nil {
            return
        }

        rec.buff <- msg
        return

    case "MessageAll":
        msg, err := this.formatBody()
        if err != nil {
            return
        }

        clients := server.Store.Clients()

        for _, sock := range clients {
            sock.buff <- msg
        }

        return
    }
}

func (this *Message) formatBody() (*Message, error) {
    event, e_ok := this.Body["Event"].(string)
    body,  b_ok := this.Body["Message"].(map[string]interface{})

    if !b_ok || ! e_ok {
        return nil, errors.New("Could not format message body")
    }

    msg := &Message{event, body, time.Now().UTC().Unix()};

    return msg, nil
}
<commit_msg>use switch statment for messages<commit_after>package main

import (
    "log"
    "encoding/json"
    "errors"
    "time"
)

type Message struct {
    Event string
    Body map[string]interface{}
    Time int64
}

func (this *Message) FromSocket(sock *Socket) {
    log.Printf("Handling message fo type %s\n", this.Event)

    switch this.Event {
    case "MessageUser":
        msg, err := this.formatBody()
        if err != nil {
            return
        }

        UID, ok := this.Body["UID"].(string)
        if !ok {
            return
        }

        rec, err := sock.Server.Store.Client(UID)
        if err != nil {
            return
        }

        rec.buff <- msg

    case "MessageAll":
        msg_str, _ := json.Marshal(this)

        sock.Server.Store.redis.Publish("Message", string(msg_str))
    }
}

func (this *Message) FromRedis(server *Server) {
    log.Printf("Handling message fo type %s\n", this.Event)

    switch this.Event {

    case "MessageUser":
        msg, err := this.formatBody()
        if err != nil {
            return
        }

        UID, ok := this.Body["UID"].(string)
        if !ok {
            return
        }

        rec, err := server.Store.Client(UID)
        if err != nil {
            return
        }

        rec.buff <- msg
        return

    case "MessageAll":
        msg, err := this.formatBody()
        if err != nil {
            return
        }

        clients := server.Store.Clients()

        for _, sock := range clients {
            sock.buff <- msg
        }

        return
    }
}

func (this *Message) formatBody() (*Message, error) {
    event, e_ok := this.Body["Event"].(string)
    body,  b_ok := this.Body["Message"].(map[string]interface{})

    if !b_ok || ! e_ok {
        return nil, errors.New("Could not format message body")
    }

    msg := &Message{event, body, time.Now().UTC().Unix()};

    return msg, nil
}
<|endoftext|>


<commit_before>package mackerel

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
)

// MetricValue metric value
type MetricValue struct {
	Name  string      `json:"name,omitempty"`
	Time  int64       `json:"time,omitempty"`
	Value interface{} `json:"value,omitempty"`
}

// HostMetricValue host metric value
type HostMetricValue struct {
	HostID string `json:"hostId,omitempty"`
	*MetricValue
}

// LatestMetricValues latest metric value
type LatestMetricValues map[string]map[string]*MetricValue

// PostHostMetricValues post host metrics
func (c *Client) PostHostMetricValues(metricValues [](*HostMetricValue)) error {
	resp, err := c.PostJSON("/api/v0/tsdb", metricValues)
	defer closeResponse(resp)
	return err
}

// PostHostMetricValuesByHostID post host metrics
func (c *Client) PostHostMetricValuesByHostID(hostID string, metricValues [](*MetricValue)) error {
	var hostMetricValues []*HostMetricValue
	for _, metricValue := range metricValues {
		hostMetricValues = append(hostMetricValues, &HostMetricValue{
			HostID:      hostID,
			MetricValue: metricValue,
		})
	}
	return c.PostHostMetricValues(hostMetricValues)
}

// PostServiceMetricValues post service metrics
func (c *Client) PostServiceMetricValues(serviceName string, metricValues [](*MetricValue)) error {
	resp, err := c.PostJSON(fmt.Sprintf("/api/v0/services/%s/tsdb", serviceName), metricValues)
	defer closeResponse(resp)
	return err
}

// FetchLatestMetricValues fetch latest metrics
func (c *Client) FetchLatestMetricValues(hostIDs []string, metricNames []string) (LatestMetricValues, error) {
	v := url.Values{}
	for _, hostID := range hostIDs {
		v.Add("hostId", hostID)
	}
	for _, metricName := range metricNames {
		v.Add("name", metricName)
	}

	req, err := http.NewRequest("GET", fmt.Sprintf("%s?%s", c.urlFor("/api/v0/tsdb/latest").String(), v.Encode()), nil)
	if err != nil {
		return nil, err
	}
	resp, err := c.Request(req)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data struct {
		LatestMetricValues *LatestMetricValues `json:"tsdbLatest"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}

	return *(data.LatestMetricValues), err
}

// FetchHostMetricValues retrieves the metric values for a Host
func (c *Client) FetchHostMetricValues(hostID string, metricName string, from int64, to int64) ([]MetricValue, error) {
	return c.fetchMetricValues(&hostID, nil, metricName, from, to)
}

// FetchServiceMetricValues retrieves the metric values for a Service
func (c *Client) FetchServiceMetricValues(serviceName string, metricName string, from int64, to int64) ([]MetricValue, error) {
	return c.fetchMetricValues(nil, &serviceName, metricName, from, to)
}

func (c *Client) fetchMetricValues(hostID *string, serviceName *string, metricName string, from int64, to int64) ([]MetricValue, error) {
	v := url.Values{}
	v.Add("name", metricName)
	v.Add("from", strconv.FormatInt(from, 10))
	v.Add("to", strconv.FormatInt(to, 10))

	url := ""
	if hostID != nil {
		url = "/api/v0/hosts/" + *hostID + "/metrics"
	} else if serviceName != nil {
		url = "/api/v0/services/" + *serviceName + "/metrics"
	} else {
		return nil, errors.New("specify either host or service")
	}
	req, err := http.NewRequest("GET", fmt.Sprintf("%s?%s", c.urlFor(url).String(), v.Encode()), nil)
	if err != nil {
		return nil, err
	}
	resp, err := c.Request(req)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data struct {
		MetricValues *[]MetricValue `json:"metrics"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return *(data.MetricValues), err
}
<commit_msg>use less pointers for parsing response body<commit_after>package mackerel

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
)

// MetricValue metric value
type MetricValue struct {
	Name  string      `json:"name,omitempty"`
	Time  int64       `json:"time,omitempty"`
	Value interface{} `json:"value,omitempty"`
}

// HostMetricValue host metric value
type HostMetricValue struct {
	HostID string `json:"hostId,omitempty"`
	*MetricValue
}

// LatestMetricValues latest metric value
type LatestMetricValues map[string]map[string]*MetricValue

// PostHostMetricValues post host metrics
func (c *Client) PostHostMetricValues(metricValues [](*HostMetricValue)) error {
	resp, err := c.PostJSON("/api/v0/tsdb", metricValues)
	defer closeResponse(resp)
	return err
}

// PostHostMetricValuesByHostID post host metrics
func (c *Client) PostHostMetricValuesByHostID(hostID string, metricValues [](*MetricValue)) error {
	var hostMetricValues []*HostMetricValue
	for _, metricValue := range metricValues {
		hostMetricValues = append(hostMetricValues, &HostMetricValue{
			HostID:      hostID,
			MetricValue: metricValue,
		})
	}
	return c.PostHostMetricValues(hostMetricValues)
}

// PostServiceMetricValues post service metrics
func (c *Client) PostServiceMetricValues(serviceName string, metricValues [](*MetricValue)) error {
	resp, err := c.PostJSON(fmt.Sprintf("/api/v0/services/%s/tsdb", serviceName), metricValues)
	defer closeResponse(resp)
	return err
}

// FetchLatestMetricValues fetch latest metrics
func (c *Client) FetchLatestMetricValues(hostIDs []string, metricNames []string) (LatestMetricValues, error) {
	v := url.Values{}
	for _, hostID := range hostIDs {
		v.Add("hostId", hostID)
	}
	for _, metricName := range metricNames {
		v.Add("name", metricName)
	}

	req, err := http.NewRequest("GET", fmt.Sprintf("%s?%s", c.urlFor("/api/v0/tsdb/latest").String(), v.Encode()), nil)
	if err != nil {
		return nil, err
	}
	resp, err := c.Request(req)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data struct {
		LatestMetricValues LatestMetricValues `json:"tsdbLatest"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}

	return data.LatestMetricValues, err
}

// FetchHostMetricValues retrieves the metric values for a Host
func (c *Client) FetchHostMetricValues(hostID string, metricName string, from int64, to int64) ([]MetricValue, error) {
	return c.fetchMetricValues(&hostID, nil, metricName, from, to)
}

// FetchServiceMetricValues retrieves the metric values for a Service
func (c *Client) FetchServiceMetricValues(serviceName string, metricName string, from int64, to int64) ([]MetricValue, error) {
	return c.fetchMetricValues(nil, &serviceName, metricName, from, to)
}

func (c *Client) fetchMetricValues(hostID *string, serviceName *string, metricName string, from int64, to int64) ([]MetricValue, error) {
	v := url.Values{}
	v.Add("name", metricName)
	v.Add("from", strconv.FormatInt(from, 10))
	v.Add("to", strconv.FormatInt(to, 10))

	url := ""
	if hostID != nil {
		url = "/api/v0/hosts/" + *hostID + "/metrics"
	} else if serviceName != nil {
		url = "/api/v0/services/" + *serviceName + "/metrics"
	} else {
		return nil, errors.New("specify either host or service")
	}
	req, err := http.NewRequest("GET", fmt.Sprintf("%s?%s", c.urlFor(url).String(), v.Encode()), nil)
	if err != nil {
		return nil, err
	}
	resp, err := c.Request(req)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data struct {
		MetricValues []MetricValue `json:"metrics"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return data.MetricValues, err
}
<|endoftext|>


<commit_before>package metrics

import (
	"fmt"
	metrics "github.com/rcrowley/go-metrics"
	"net/http"
	"os"
	"time"
)

const hostnameLabelKey = "hostname"

func (r *reporter) ensureMetric(name string, t string) error {
	fullMetricName := NameInDomain(name)
	metric := &Metric{
		Name: fullMetricName,
		Type: t,
		Labels: map[string]string{
			NameInDomain(hostnameLabelKey): "Hostname of machine sending metric.",
		},
	}
	fmt.Println("creating metric:", metric.Name)
	err := CreateMetric(r.client, r.project, metric)
	if err != nil {
		fmt.Println("error creating metric:", err.Error())
	}
	return nil
}

func (r *reporter) reportMetric(metricName string, metricType string, val interface{}) {
	r.ensureMetric(metricName, metricType)
	hostname, err := os.Hostname()
	if err != nil {
		fmt.Println("error retrieving hostname, can't report:", err.Error())
		return
	}
	labels := make(map[string]string, 1)
	labels[hostnameLabelKey] = hostname

	timeseries := &Timeseries{
		MetricName: NameInDomain(metricName),
		Now:        time.Now(),
		Labels:     labels,
	}

	if metricType == Int {
		timeseries.Int64Value = val.(int64)
	} else if metricType == Double {
		timeseries.DoubleValue = val.(float64)
	}

	err = WriteTimeseries(r.client, r.project, []*Timeseries{timeseries})
	if err != nil {
		fmt.Println("error writing timeseries:", err.Error())
	}
}

func (r *reporter) reportMeter(name string, val metrics.Meter) {
	r.reportMetric(fmt.Sprintf("%s.count", name), Int, val.Count())
}

type reporter struct {
	client  *http.Client
	project string
}

func newReporter(client *http.Client, project string) *reporter {
	return &reporter{client, project}
}

func (r *reporter) report(name string, val interface{}) {
	switch metric := val.(type) {
	case metrics.Meter:
		r.reportMeter(name, metric)
	}
}

func GoogleCloudMonitoring(r metrics.Registry, d time.Duration, client *http.Client, project string) {
	reporter := newReporter(client, project)
	for _ = range time.Tick(d) {
		r.Each(reporter.report)
	}
}
<commit_msg>track which metrics have already been created<commit_after>package metrics

import (
	"fmt"
	metrics "github.com/rcrowley/go-metrics"
	"net/http"
	"os"
	"time"
)

const hostnameLabelKey = "hostname"

func (r *reporter) ensureMetric(name string, t string) error {
	_, tracked := r.trackedMetrics[name]
	if tracked {
		return nil
	}

	fullMetricName := NameInDomain(name)
	metric := &Metric{
		Name: fullMetricName,
		Type: t,
		Labels: map[string]string{
			NameInDomain(hostnameLabelKey): "Hostname of machine sending metric.",
		},
	}
	fmt.Println("creating metric:", metric.Name)
	err := CreateMetric(r.client, r.project, metric)
	if err != nil {
		fmt.Println("error creating metric:", err.Error())
	}
	r.trackedMetrics[name] = true

	return nil
}

func (r *reporter) reportMetric(metricName string, metricType string, val interface{}) {
	r.ensureMetric(metricName, metricType)
	hostname, err := os.Hostname()
	if err != nil {
		fmt.Println("error retrieving hostname, can't report:", err.Error())
		return
	}
	labels := make(map[string]string, 1)
	labels[hostnameLabelKey] = hostname

	timeseries := &Timeseries{
		MetricName: NameInDomain(metricName),
		Now:        time.Now(),
		Labels:     labels,
	}

	if metricType == Int {
		timeseries.Int64Value = val.(int64)
	} else if metricType == Double {
		timeseries.DoubleValue = val.(float64)
	}

	err = WriteTimeseries(r.client, r.project, []*Timeseries{timeseries})
	if err != nil {
		fmt.Println("error writing timeseries:", err.Error())
	}
}

func (r *reporter) reportMeter(name string, val metrics.Meter) {
	r.reportMetric(fmt.Sprintf("%s.count", name), Int, val.Count())
	r.reportMetric(fmt.Sprintf("%s.one-minute", name), Double, val.Rate1())
	r.reportMetric(fmt.Sprintf("%s.five-minute", name), Double, val.Rate5())
	r.reportMetric(fmt.Sprintf("%s.fifteen-minute", name), Double, val.Rate15())
	r.reportMetric(fmt.Sprintf("%s.mean", name), Double, val.RateMean())
}

type reporter struct {
	client         *http.Client
	project        string
	trackedMetrics map[string]bool
}

func newReporter(client *http.Client, project string) *reporter {
	return &reporter{client, project, make(map[string]bool)}
}

func (r *reporter) report(name string, val interface{}) {
	switch metric := val.(type) {
	case metrics.Meter:
		r.reportMeter(name, metric)
	}
}

func GoogleCloudMonitoring(r metrics.Registry, d time.Duration, client *http.Client, project string) {
	reporter := newReporter(client, project)
	for _ = range time.Tick(d) {
		r.Each(reporter.report)
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 bee authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package main

import (
	"database/sql"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"time"
)

var cmdMigrate = &Command{
	UsageLine: "migrate [Command]",
	Short:     "run database migrations",
	Long: `
bee migrate
    run all outstanding migrations
    -driver: [mysql | postgresql | sqlite], the default is mysql
    -conn:   the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test

bee migrate rollback
    rollback the last migration operation
    -driver: [mysql | postgresql | sqlite], the default is mysql
    -conn:   the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test

bee migrate reset
    rollback all migrations
    -driver: [mysql | postgresql | sqlite], the default is mysql
    -conn:   the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test

bee migrate refresh
    rollback all migrations and run them all again
    -driver: [mysql | postgresql | sqlite], the default is mysql
    -conn:   the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
`,
}

var mDriver docValue
var mConn docValue

func init() {
	cmdMigrate.Run = runMigration
	cmdMigrate.Flag.Var(&mDriver, "driver", "database driver: mysql, postgresql, etc.")
	cmdMigrate.Flag.Var(&mConn, "conn", "connection string used by the driver to connect to a database instance")
}

// runMigration is the entry point for starting a migration
func runMigration(cmd *Command, args []string) {
	crupath, _ := os.Getwd()

	gopath := os.Getenv("GOPATH")
	Debugf("gopath:%s", gopath)
	if gopath == "" {
		ColorLog("[ERRO] $GOPATH not found\n")
		ColorLog("[HINT] Set $GOPATH in your environment vairables\n")
		os.Exit(2)
	}
	// load config
	err := loadConfig()
	if err != nil {
		ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err)
	}
	// getting command line arguments
	if len(args) != 0 {
		cmd.Flag.Parse(args[1:])
	}
	if mDriver == "" {
		mDriver = docValue(conf.Database.Driver)
		if mDriver == "" {
			mDriver = "mysql"
		}
	}
	if mConn == "" {
		mConn = docValue(conf.Database.Conn)
		if mConn == "" {
			mConn = "root:@tcp(127.0.0.1:3306)/test"
		}
	}
	ColorLog("[INFO] Using '%s' as 'driver'\n", mDriver)
	ColorLog("[INFO] Using '%s' as 'conn'\n", mConn)
	driverStr, connStr := string(mDriver), string(mConn)
	if len(args) == 0 {
		// run all outstanding migrations
		ColorLog("[INFO] Running all outstanding migrations\n")
		migrateUpdate(crupath, driverStr, connStr)
	} else {
		mcmd := args[0]
		switch mcmd {
		case "rollback":
			ColorLog("[INFO] Rolling back the last migration operation\n")
			migrateRollback(crupath, driverStr, connStr)
		case "reset":
			ColorLog("[INFO] Reseting all migrations\n")
			migrateReset(crupath, driverStr, connStr)
		case "refresh":
			ColorLog("[INFO] Refreshing all migrations\n")
			migrateReset(crupath, driverStr, connStr)
		default:
			ColorLog("[ERRO] Command is missing\n")
			os.Exit(2)
		}
	}
	ColorLog("[SUCC] Migration successful!\n")
}

// migrateUpdate does the schema update
func migrateUpdate(crupath, driver, connStr string) {
	migrate("upgrade", crupath, driver, connStr)
}

// migrateRollback rolls back the latest migration
func migrateRollback(crupath, driver, connStr string) {
	migrate("rollback", crupath, driver, connStr)
}

// migrateReset rolls back all migrations
func migrateReset(crupath, driver, connStr string) {
	migrate("reset", crupath, driver, connStr)
}

// migrationRefresh rolls back all migrations and start over again
func migrateRefresh(crupath, driver, connStr string) {
	migrate("refresh", crupath, driver, connStr)
}

// migrate generates source code, build it, and invoke the binary who does the actual migration
func migrate(goal, crupath, driver, connStr string) {
	dir := path.Join(crupath, "database", "migrations")
	binary := "m"
	source := binary + ".go"
	// connect to database
	db, err := sql.Open(driver, connStr)
	if err != nil {
		ColorLog("[ERRO] Could not connect to %s: %s\n", driver, connStr)
		os.Exit(2)
	}
	defer db.Close()
	checkForSchemaUpdateTable(db)
	latestName, latestTime := getLatestMigration(db)
	writeMigrationSourceFile(dir, source, driver, connStr, latestTime, latestName, goal)
	buildMigrationBinary(dir, binary)
	runMigrationBinary(dir, binary)
	removeTempFile(dir, source)
	removeTempFile(dir, binary)
}

// checkForSchemaUpdateTable checks the existence of migrations table.
// It checks for the proper table structures and creates the table using MYSQL_MIGRATION_DDL if it does not exist.
func checkForSchemaUpdateTable(db *sql.DB) {
	if rows, err := db.Query("SHOW TABLES LIKE 'migrations'"); err != nil {
		ColorLog("[ERRO] Could not show migrations table: %s\n", err)
		os.Exit(2)
	} else if !rows.Next() {
		// no migrations table, create anew
		ColorLog("[INFO] Creating 'migrations' table...\n")
		if _, err := db.Query(MYSQL_MIGRATION_DDL); err != nil {
			ColorLog("[ERRO] Could not create migrations table: %s\n", err)
			os.Exit(2)
		}
	}
	// checking that migrations table schema are expected
	if rows, err := db.Query("DESC migrations"); err != nil {
		ColorLog("[ERRO] Could not show columns of migrations table: %s\n", err)
		os.Exit(2)
	} else {
		for rows.Next() {
			var fieldBytes, typeBytes, nullBytes, keyBytes, defaultBytes, extraBytes []byte
			if err := rows.Scan(&fieldBytes, &typeBytes, &nullBytes, &keyBytes, &defaultBytes, &extraBytes); err != nil {
				ColorLog("[ERRO] Could not read column information: %s\n", err)
				os.Exit(2)
			}
			fieldStr, typeStr, nullStr, keyStr, defaultStr, extraStr :=
				string(fieldBytes), string(typeBytes), string(nullBytes), string(keyBytes), string(defaultBytes), string(extraBytes)
			if fieldStr == "id_migration" {
				if keyStr != "PRI" || extraStr != "auto_increment" {
					ColorLog("[ERRO] Column migration.id_migration type mismatch: KEY: %s, EXTRA: %s\n", keyStr, extraStr)
					ColorLog("[HINT] Expecting KEY: PRI, EXTRA: auto_increment\n")
					os.Exit(2)
				}
			} else if fieldStr == "name" {
				if !strings.HasPrefix(typeStr, "varchar") || nullStr != "YES" {
					ColorLog("[ERRO] Column migration.name type mismatch: TYPE: %s, NULL: %s\n", typeStr, nullStr)
					ColorLog("[HINT] Expecting TYPE: varchar, NULL: YES\n")
					os.Exit(2)
				}

			} else if fieldStr == "created_at" {
				if typeStr != "timestamp" || defaultStr != "CURRENT_TIMESTAMP" {
					ColorLog("[ERRO] Column migration.timestamp type mismatch: TYPE: %s, DEFAULT: %s\n", typeStr, defaultStr)
					ColorLog("[HINT] Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP\n")
					os.Exit(2)
				}
			}
		}
	}
}

// getLatestMigration retrives latest migration with status 'update'
func getLatestMigration(db *sql.DB) (file string, createdAt int64) {
	sql := "SELECT name, created_at FROM migrations where status = 'update' ORDER BY id_migration DESC LIMIT 1"
	if rows, err := db.Query(sql); err != nil {
		ColorLog("[ERRO] Could not retrieve migrations: %s\n", err)
		os.Exit(2)
	} else {
		var createdAtStr string
		if rows.Next() {
			if err := rows.Scan(&file, &createdAtStr); err != nil {
				ColorLog("[ERRO] Could not read migrations in database: %s\n", err)
				os.Exit(2)
			}
			if t, err := time.Parse("2006-01-02 15:04:05", createdAtStr); err != nil {
				ColorLog("[ERRO] Could not parse time: %s\n", err)
				os.Exit(2)
			} else {
				createdAt = t.Unix()
			}
		} else {
			file, createdAt = "", 0
		}
	}
	return
}

// writeMigrationSourceFile create the source file based on MIGRATION_MAIN_TPL
func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime int64, latestName string, task string) {
	os.Chdir(dir)
	if f, err := os.OpenFile(source, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err != nil {
		ColorLog("[ERRO] Could not create file: %s\n", err)
		os.Exit(2)
	} else {
		content := strings.Replace(MIGRATION_MAIN_TPL, "{{DBDriver}}", driver, -1)
		content = strings.Replace(content, "{{ConnStr}}", connStr, -1)
		content = strings.Replace(content, "{{LatestTime}}", strconv.FormatInt(latestTime, 10), -1)
		content = strings.Replace(content, "{{LatestName}}", latestName, -1)
		content = strings.Replace(content, "{{Task}}", task, -1)
		if _, err := f.WriteString(content); err != nil {
			ColorLog("[ERRO] Could not write to file: %s\n", err)
			os.Exit(2)
		}
		f.Close()
	}
}

// buildMigrationBinary changes directory to database/migrations folder and go-build the source
func buildMigrationBinary(dir, binary string) {
	os.Chdir(dir)
	cmd := exec.Command("go", "build", "-o", binary)
	if out, err := cmd.CombinedOutput(); err != nil {
		ColorLog("[ERRO] Could not build migration binary: %s\n", err)
		formatShellErrOutput(string(out))
		os.Exit(2)
	}
}

// runMigrationBinary runs the migration program who does the actual work
func runMigrationBinary(dir, binary string) {
	os.Chdir(dir)
	cmd := exec.Command("./" + binary)
	if out, err := cmd.CombinedOutput(); err != nil {
		formatShellOutput(string(out))
		ColorLog("[ERRO] Could not run migration binary: %s\n", err)
		os.Exit(2)
	} else {
		formatShellOutput(string(out))
	}
}

// removeTempFile removes a file in dir
func removeTempFile(dir, file string) {
	os.Chdir(dir)
	if err := os.Remove(file); err != nil {
		ColorLog("[ERRO] Could not remove temporary migration files: %s\n", err)
		os.Exit(2)
	}
}

// formatShellErrOutput formats the error shell output
func formatShellErrOutput(o string) {
	for _, line := range strings.Split(o, "\n") {
		if line != "" {
			ColorLog("[ERRO] -| %s\n", line)
		}
	}
}

// formatShellOutput formats the normal shell output
func formatShellOutput(o string) {
	for _, line := range strings.Split(o, "\n") {
		if line != "" {
			ColorLog("[INFO] -| %s\n", line)
		}
	}
}

const (
	MIGRATION_MAIN_TPL = `package main

import(
	"os"

	"github.com/astaxie/beego/orm"
	"github.com/astaxie/beego/migration"

	_ "github.com/go-sql-driver/mysql"
)

func init(){
	orm.RegisterDataBase("default", "{{DBDriver}}","{{ConnStr}}")
}

func main(){
	task := "{{Task}}"
	switch task {
	case "upgrade":
		if err := migration.Upgrade({{LatestTime}}); err != nil {
			os.Exit(2)
		}
	case "rollback":
		if err := migration.Rollback("{{LatestName}}"); err != nil {
			os.Exit(2)
		}
	case "reset":
		if err := migration.Reset(); err != nil {
			os.Exit(2)
		}
	case "refresh":
		if err := migration.Refresh(); err != nil {
			os.Exit(2)
		}
	}
}

`
	MYSQL_MIGRATION_DDL = `
CREATE TABLE migrations (
	id_migration int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'surrogate key',
	name varchar(255) DEFAULT NULL COMMENT 'migration name, unique',
	created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'date migrated or rolled back',
	statements longtext COMMENT 'SQL statements for this migration',
	rollback_statements longtext COMMENT 'SQL statment for rolling back migration',
	status ENUM('update', 'rollback') COMMENT 'update indicates it is a normal migration while rollback means this migration is rolled back',
	PRIMARY KEY (id_migration)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
`
)
<commit_msg>auto delete temp file while error<commit_after>// Copyright 2013 bee authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package main

import (
	"database/sql"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"time"
)

var cmdMigrate = &Command{
	UsageLine: "migrate [Command]",
	Short:     "run database migrations",
	Long: `
bee migrate
    run all outstanding migrations
    -driver: [mysql | postgresql | sqlite], the default is mysql
    -conn:   the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test

bee migrate rollback
    rollback the last migration operation
    -driver: [mysql | postgresql | sqlite], the default is mysql
    -conn:   the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test

bee migrate reset
    rollback all migrations
    -driver: [mysql | postgresql | sqlite], the default is mysql
    -conn:   the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test

bee migrate refresh
    rollback all migrations and run them all again
    -driver: [mysql | postgresql | sqlite], the default is mysql
    -conn:   the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
`,
}

var mDriver docValue
var mConn docValue

func init() {
	cmdMigrate.Run = runMigration
	cmdMigrate.Flag.Var(&mDriver, "driver", "database driver: mysql, postgresql, etc.")
	cmdMigrate.Flag.Var(&mConn, "conn", "connection string used by the driver to connect to a database instance")
}

// runMigration is the entry point for starting a migration
func runMigration(cmd *Command, args []string) {
	crupath, _ := os.Getwd()

	gopath := os.Getenv("GOPATH")
	Debugf("gopath:%s", gopath)
	if gopath == "" {
		ColorLog("[ERRO] $GOPATH not found\n")
		ColorLog("[HINT] Set $GOPATH in your environment vairables\n")
		os.Exit(2)
	}
	// load config
	err := loadConfig()
	if err != nil {
		ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err)
	}
	// getting command line arguments
	if len(args) != 0 {
		cmd.Flag.Parse(args[1:])
	}
	if mDriver == "" {
		mDriver = docValue(conf.Database.Driver)
		if mDriver == "" {
			mDriver = "mysql"
		}
	}
	if mConn == "" {
		mConn = docValue(conf.Database.Conn)
		if mConn == "" {
			mConn = "root:@tcp(127.0.0.1:3306)/test"
		}
	}
	ColorLog("[INFO] Using '%s' as 'driver'\n", mDriver)
	ColorLog("[INFO] Using '%s' as 'conn'\n", mConn)
	driverStr, connStr := string(mDriver), string(mConn)
	if len(args) == 0 {
		// run all outstanding migrations
		ColorLog("[INFO] Running all outstanding migrations\n")
		migrateUpdate(crupath, driverStr, connStr)
	} else {
		mcmd := args[0]
		switch mcmd {
		case "rollback":
			ColorLog("[INFO] Rolling back the last migration operation\n")
			migrateRollback(crupath, driverStr, connStr)
		case "reset":
			ColorLog("[INFO] Reseting all migrations\n")
			migrateReset(crupath, driverStr, connStr)
		case "refresh":
			ColorLog("[INFO] Refreshing all migrations\n")
			migrateReset(crupath, driverStr, connStr)
		default:
			ColorLog("[ERRO] Command is missing\n")
			os.Exit(2)
		}
	}
	ColorLog("[SUCC] Migration successful!\n")
}

// migrateUpdate does the schema update
func migrateUpdate(crupath, driver, connStr string) {
	migrate("upgrade", crupath, driver, connStr)
}

// migrateRollback rolls back the latest migration
func migrateRollback(crupath, driver, connStr string) {
	migrate("rollback", crupath, driver, connStr)
}

// migrateReset rolls back all migrations
func migrateReset(crupath, driver, connStr string) {
	migrate("reset", crupath, driver, connStr)
}

// migrationRefresh rolls back all migrations and start over again
func migrateRefresh(crupath, driver, connStr string) {
	migrate("refresh", crupath, driver, connStr)
}

// migrate generates source code, build it, and invoke the binary who does the actual migration
func migrate(goal, crupath, driver, connStr string) {
	dir := path.Join(crupath, "database", "migrations")
	binary := "m"
	source := binary + ".go"
	// connect to database
	db, err := sql.Open(driver, connStr)
	if err != nil {
		ColorLog("[ERRO] Could not connect to %s: %s\n", driver, connStr)
		os.Exit(2)
	}
	defer db.Close()
	checkForSchemaUpdateTable(db)
	latestName, latestTime := getLatestMigration(db)
	writeMigrationSourceFile(dir, source, driver, connStr, latestTime, latestName, goal)
	buildMigrationBinary(dir, binary)
	runMigrationBinary(dir, binary)
	removeTempFile(dir, source)
	removeTempFile(dir, binary)
}

// checkForSchemaUpdateTable checks the existence of migrations table.
// It checks for the proper table structures and creates the table using MYSQL_MIGRATION_DDL if it does not exist.
func checkForSchemaUpdateTable(db *sql.DB) {
	if rows, err := db.Query("SHOW TABLES LIKE 'migrations'"); err != nil {
		ColorLog("[ERRO] Could not show migrations table: %s\n", err)
		os.Exit(2)
	} else if !rows.Next() {
		// no migrations table, create anew
		ColorLog("[INFO] Creating 'migrations' table...\n")
		if _, err := db.Query(MYSQL_MIGRATION_DDL); err != nil {
			ColorLog("[ERRO] Could not create migrations table: %s\n", err)
			os.Exit(2)
		}
	}
	// checking that migrations table schema are expected
	if rows, err := db.Query("DESC migrations"); err != nil {
		ColorLog("[ERRO] Could not show columns of migrations table: %s\n", err)
		os.Exit(2)
	} else {
		for rows.Next() {
			var fieldBytes, typeBytes, nullBytes, keyBytes, defaultBytes, extraBytes []byte
			if err := rows.Scan(&fieldBytes, &typeBytes, &nullBytes, &keyBytes, &defaultBytes, &extraBytes); err != nil {
				ColorLog("[ERRO] Could not read column information: %s\n", err)
				os.Exit(2)
			}
			fieldStr, typeStr, nullStr, keyStr, defaultStr, extraStr :=
				string(fieldBytes), string(typeBytes), string(nullBytes), string(keyBytes), string(defaultBytes), string(extraBytes)
			if fieldStr == "id_migration" {
				if keyStr != "PRI" || extraStr != "auto_increment" {
					ColorLog("[ERRO] Column migration.id_migration type mismatch: KEY: %s, EXTRA: %s\n", keyStr, extraStr)
					ColorLog("[HINT] Expecting KEY: PRI, EXTRA: auto_increment\n")
					os.Exit(2)
				}
			} else if fieldStr == "name" {
				if !strings.HasPrefix(typeStr, "varchar") || nullStr != "YES" {
					ColorLog("[ERRO] Column migration.name type mismatch: TYPE: %s, NULL: %s\n", typeStr, nullStr)
					ColorLog("[HINT] Expecting TYPE: varchar, NULL: YES\n")
					os.Exit(2)
				}

			} else if fieldStr == "created_at" {
				if typeStr != "timestamp" || defaultStr != "CURRENT_TIMESTAMP" {
					ColorLog("[ERRO] Column migration.timestamp type mismatch: TYPE: %s, DEFAULT: %s\n", typeStr, defaultStr)
					ColorLog("[HINT] Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP\n")
					os.Exit(2)
				}
			}
		}
	}
}

// getLatestMigration retrives latest migration with status 'update'
func getLatestMigration(db *sql.DB) (file string, createdAt int64) {
	sql := "SELECT name, created_at FROM migrations where status = 'update' ORDER BY id_migration DESC LIMIT 1"
	if rows, err := db.Query(sql); err != nil {
		ColorLog("[ERRO] Could not retrieve migrations: %s\n", err)
		os.Exit(2)
	} else {
		var createdAtStr string
		if rows.Next() {
			if err := rows.Scan(&file, &createdAtStr); err != nil {
				ColorLog("[ERRO] Could not read migrations in database: %s\n", err)
				os.Exit(2)
			}
			if t, err := time.Parse("2006-01-02 15:04:05", createdAtStr); err != nil {
				ColorLog("[ERRO] Could not parse time: %s\n", err)
				os.Exit(2)
			} else {
				createdAt = t.Unix()
			}
		} else {
			file, createdAt = "", 0
		}
	}
	return
}

// writeMigrationSourceFile create the source file based on MIGRATION_MAIN_TPL
func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime int64, latestName string, task string) {
	os.Chdir(dir)
	if f, err := os.OpenFile(source, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err != nil {
		ColorLog("[ERRO] Could not create file: %s\n", err)
		os.Exit(2)
	} else {
		content := strings.Replace(MIGRATION_MAIN_TPL, "{{DBDriver}}", driver, -1)
		content = strings.Replace(content, "{{ConnStr}}", connStr, -1)
		content = strings.Replace(content, "{{LatestTime}}", strconv.FormatInt(latestTime, 10), -1)
		content = strings.Replace(content, "{{LatestName}}", latestName, -1)
		content = strings.Replace(content, "{{Task}}", task, -1)
		if _, err := f.WriteString(content); err != nil {
			ColorLog("[ERRO] Could not write to file: %s\n", err)
			os.Exit(2)
		}
		f.Close()
	}
}

// buildMigrationBinary changes directory to database/migrations folder and go-build the source
func buildMigrationBinary(dir, binary string) {
	os.Chdir(dir)
	cmd := exec.Command("go", "build", "-o", binary)
	if out, err := cmd.CombinedOutput(); err != nil {
		ColorLog("[ERRO] Could not build migration binary: %s\n", err)
		formatShellErrOutput(string(out))
		removeTempFile(dir, binary)
		removeTempFile(dir, binary+".go")
		os.Exit(2)
	}
}

// runMigrationBinary runs the migration program who does the actual work
func runMigrationBinary(dir, binary string) {
	os.Chdir(dir)
	cmd := exec.Command("./" + binary)
	if out, err := cmd.CombinedOutput(); err != nil {
		formatShellOutput(string(out))
		ColorLog("[ERRO] Could not run migration binary: %s\n", err)
		removeTempFile(dir, binary)
		removeTempFile(dir, binary+".go")
		os.Exit(2)
	} else {
		formatShellOutput(string(out))
	}
}

// removeTempFile removes a file in dir
func removeTempFile(dir, file string) {
	os.Chdir(dir)
	if err := os.Remove(file); err != nil {
		ColorLog("[ERRO] Could not remove temporary migration files: %s\n", err)
		os.Exit(2)
	}
}

// formatShellErrOutput formats the error shell output
func formatShellErrOutput(o string) {
	for _, line := range strings.Split(o, "\n") {
		if line != "" {
			ColorLog("[ERRO] -| %s\n", line)
		}
	}
}

// formatShellOutput formats the normal shell output
func formatShellOutput(o string) {
	for _, line := range strings.Split(o, "\n") {
		if line != "" {
			ColorLog("[INFO] -| %s\n", line)
		}
	}
}

const (
	MIGRATION_MAIN_TPL = `package main

import(
	"os"

	"github.com/astaxie/beego/orm"
	"github.com/astaxie/beego/migration"

	_ "github.com/go-sql-driver/mysql"
)

func init(){
	orm.RegisterDataBase("default", "{{DBDriver}}","{{ConnStr}}")
}

func main(){
	task := "{{Task}}"
	switch task {
	case "upgrade":
		if err := migration.Upgrade({{LatestTime}}); err != nil {
			os.Exit(2)
		}
	case "rollback":
		if err := migration.Rollback("{{LatestName}}"); err != nil {
			os.Exit(2)
		}
	case "reset":
		if err := migration.Reset(); err != nil {
			os.Exit(2)
		}
	case "refresh":
		if err := migration.Refresh(); err != nil {
			os.Exit(2)
		}
	}
}

`
	MYSQL_MIGRATION_DDL = `
CREATE TABLE migrations (
	id_migration int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'surrogate key',
	name varchar(255) DEFAULT NULL COMMENT 'migration name, unique',
	created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'date migrated or rolled back',
	statements longtext COMMENT 'SQL statements for this migration',
	rollback_statements longtext COMMENT 'SQL statment for rolling back migration',
	status ENUM('update', 'rollback') COMMENT 'update indicates it is a normal migration while rollback means this migration is rolled back',
	PRIMARY KEY (id_migration)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
`
)
<|endoftext|>


<commit_before>// Package debug allows to debug services
package debug

import (
	"fmt"
	"time"

	"github.com/micro/cli"
	"github.com/micro/go-micro"
	"github.com/micro/go-micro/debug/log"
	dbg "github.com/micro/go-micro/debug/service"
)

const (
	// LogsUsage message for logs command
	LogsUsage = "Required usage: micro logs --name example"
)

var (
	// Name of the service
	Name = "go.micro.debug"
	// Address of the service
	Address = ":8089"
)

func getLogs(ctx *cli.Context, srvOpts ...micro.Option) {
	log.Name("debug")

	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	// get the args
	name := ctx.String("name")
	since := ctx.String("since")
	count := ctx.Int("count")

	// must specify service name
	if len(name) == 0 {
		log.Fatal(LogsUsage)
	}

	service := dbg.NewDebug(name)

	var options []log.ReadOption

	// TODO: Since should be time.Duration
	readSince, err := time.Parse(time.RFC3339, since)
	if err == nil {
		options = append(options, log.Since(readSince))
	}

	if count > 0 {
		options = append(options, log.Count(count))
	}

	logs, err := service.Logs(options...)
	if err != nil {
		log.Fatal(err)
	}

	for record := range logs {
		fmt.Printf("%v\n", record)
	}
}

func getStats(ctx *cli.Context, srvOpts ...micro.Option) {
	log.Name("debug")

	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	// TODO: implement this cruft
}

func run(ctx *cli.Context, srvOpts ...micro.Option) {
	log.Name("debug")

	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	if len(ctx.String("address")) > 0 {
		Address = ctx.String("address")
	}

	if len(ctx.GlobalString("server_name")) > 0 {
		Name = ctx.GlobalString("server_name")
	}

	if len(Address) > 0 {
		srvOpts = append(srvOpts, micro.Address(Address))
	}

	// append name
	srvOpts = append(srvOpts, micro.Name(Name))

	// new service
	service := micro.NewService(srvOpts...)

	// TODO: figure out this shit; DefaultHandler is registered
	//pb.RegisterDebugHandler(service.Server(),
	//	handler.DefaultHandler,
	//)

	// start debug service
	if err := service.Run(); err != nil {
		log.Errorf("error running service: %v", err)
	}

	log.Infof("successfully stopped")
}

// Flags is shared flags so we don't have to continually re-add
func Flags() []cli.Flag {
	return []cli.Flag{
		cli.StringFlag{
			Name:  "name",
			Usage: "Set the name of the service to debug",
		},
		cli.StringFlag{
			Name:  "version",
			Usage: "Set the version of the service to debug",
			Value: "latest",
		},
		cli.BoolFlag{
			Name:  "stream",
			Usage: "Set to stream logs continuously",
		},
		cli.StringFlag{
			Name:  "since",
			Usage: "Set to the relative time from which to show the logs for",
		},
		cli.IntFlag{
			Name:  "count",
			Usage: "Set to query the last number of log events",
		},
	}
}

func Commands(options ...micro.Option) []cli.Command {
	command := []cli.Command{
		{
			Name:  "debug",
			Usage: "Run the micro debug service",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:   "address",
					Usage:  "Set the registry http address e.g 0.0.0.0:8089",
					EnvVar: "MICRO_SERVER_ADDRESS",
				},
			},
			Action: func(ctx *cli.Context) {
				run(ctx, options...)
			},
		},
		{
			Name:  "logs",
			Usage: "Get logs for a service",
			Flags: Flags(),
			Action: func(ctx *cli.Context) {
				getLogs(ctx, options...)
			},
		},
		{
			Name:  "stats",
			Usage: "Get stats for a service",
			Flags: Flags(),
			Action: func(ctx *cli.Context) {
				getStats(ctx, options...)
			},
		},
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command[0].Subcommands = append(command[0].Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command[0].Flags = append(command[0].Flags, flags...)
		}
	}

	return command
}
<commit_msg>Specify duration to --since flag.<commit_after>// Package debug allows to debug services
package debug

import (
	"fmt"
	"time"

	"github.com/micro/cli"
	"github.com/micro/go-micro"
	"github.com/micro/go-micro/debug/log"
	dbg "github.com/micro/go-micro/debug/service"
)

const (
	// LogsUsage message for logs command
	LogsUsage = "Required usage: micro logs --name example"
)

var (
	// Name of the service
	Name = "go.micro.debug"
	// Address of the service
	Address = ":8089"
)

func getLogs(ctx *cli.Context, srvOpts ...micro.Option) {
	log.Name("debug")

	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	// get the args
	name := ctx.String("name")
	since := ctx.String("since")
	count := ctx.Int("count")

	// must specify service name
	if len(name) == 0 {
		log.Fatal(LogsUsage)
	}

	service := dbg.NewDebug(name)

	var options []log.ReadOption

	d, err := time.ParseDuration(since)
	if err == nil {
		readSince := time.Now().Add(-d)
		options = append(options, log.Since(readSince))
	}

	if count > 0 {
		options = append(options, log.Count(count))
	}

	logs, err := service.Logs(options...)
	if err != nil {
		log.Fatal(err)
	}

	for record := range logs {
		fmt.Printf("%v\n", record)
	}
}

func getStats(ctx *cli.Context, srvOpts ...micro.Option) {
	log.Name("debug")

	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	// TODO: implement this cruft
}

func run(ctx *cli.Context, srvOpts ...micro.Option) {
	log.Name("debug")

	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	if len(ctx.String("address")) > 0 {
		Address = ctx.String("address")
	}

	if len(ctx.GlobalString("server_name")) > 0 {
		Name = ctx.GlobalString("server_name")
	}

	if len(Address) > 0 {
		srvOpts = append(srvOpts, micro.Address(Address))
	}

	// append name
	srvOpts = append(srvOpts, micro.Name(Name))

	// new service
	service := micro.NewService(srvOpts...)

	// TODO: figure out this shit;

	// start debug service
	if err := service.Run(); err != nil {
		log.Errorf("error running service: %v", err)
	}

	log.Infof("successfully stopped")
}

// Flags is shared flags so we don't have to continually re-add
func Flags() []cli.Flag {
	return []cli.Flag{
		cli.StringFlag{
			Name:  "name",
			Usage: "Set the name of the service to debug",
		},
		cli.StringFlag{
			Name:  "version",
			Usage: "Set the version of the service to debug",
			Value: "latest",
		},
		cli.BoolFlag{
			Name:  "stream",
			Usage: "Set to stream logs continuously",
		},
		cli.StringFlag{
			Name:  "since",
			Usage: "Set to the relative time from which to show the logs for e.g. 1h",
		},
		cli.IntFlag{
			Name:  "count",
			Usage: "Set to query the last number of log events",
		},
	}
}

func Commands(options ...micro.Option) []cli.Command {
	command := []cli.Command{
		{
			Name:  "debug",
			Usage: "Run the micro debug service",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:   "address",
					Usage:  "Set the registry http address e.g 0.0.0.0:8089",
					EnvVar: "MICRO_SERVER_ADDRESS",
				},
			},
			Action: func(ctx *cli.Context) {
				run(ctx, options...)
			},
		},
		{
			Name:  "logs",
			Usage: "Get logs for a service",
			Flags: Flags(),
			Action: func(ctx *cli.Context) {
				getLogs(ctx, options...)
			},
		},
		{
			Name:  "stats",
			Usage: "Get stats for a service",
			Flags: Flags(),
			Action: func(ctx *cli.Context) {
				getStats(ctx, options...)
			},
		},
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command[0].Subcommands = append(command[0].Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command[0].Flags = append(command[0].Flags, flags...)
		}
	}

	return command
}
<|endoftext|>


<commit_before>package cloth

import (
	"testing"

	"bytes"
	"encoding/binary"

	"github.com/osamingo/boolconv"
	"google.golang.org/cloud/bigtable"
)

func TestReadItems(t *testing.T) {

	err := ReadItems(nil, nil)
	if err != nil {
		t.Error("error should be nil")
	}

	s := struct {
		TNonTag  string
		TString  string  `bigtable:"tstr"`
		TBool    bool    `bigtable:"tbool"`
		TInt     int     `bigtable:"tint"`
		TInt8    int8    `bigtable:"tint8"`
		TInt16   int16   `bigtable:"tint16"`
		TInt32   int32   `bigtable:"tint32"`
		TInt64   int64   `bigtable:"tint64"`
		TUint    uint    `bigtable:"tuint"`
		TUint8   uint8   `bigtable:"tuint8"`
		TUint16  uint16  `bigtable:"tuint16"`
		TUint32  uint32  `bigtable:"tuint32"`
		TUint64  uint64  `bigtable:"tuint64"`
		TFloat32 float32 `bigtable:"tfloat32"`
		TFloat64 float64 `bigtable:"tfloat64"`
	}{}

	str := "hoge"
	bl := true

	ris := []*bigtable.ReadItem{
		&bigtable.ReadItem{
			Column: "tstr",
			Value:  []byte(str),
		},
		&bigtable.ReadItem{
			Column: "tbool",
			Value:  boolconv.NewBool(bl).Bytes(),
		},
	}

	err = ReadItems(ris, struct{}{})
	if err != nil {
		t.Error("error should be nil")
	}

	num := 123
	buf := &bytes.Buffer{}

	binary.Write(buf, binary.BigEndian, int64(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tint",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, int8(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tint8",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, int16(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tint16",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, int32(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tint32",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, int64(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tint64",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, uint64(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tuint",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, uint8(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tuint8",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, uint16(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tuint16",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, uint32(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tuint32",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, uint64(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tuint64",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, float32(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tfloat32",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, float64(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tfloat64",
		Value:  buf.Bytes(),
	})

	err = ReadItems(ris, &s)
	if err != nil {
		t.Error("error should not be nil")
	}

	if s.TString != str {
		t.Errorf("expected %s got %s", str, s.TString)
	}

	if !s.TBool {
		t.Errorf("expected %v got %v", bl, s.TBool)
	}

	if s.TInt != int(num) {
		t.Errorf("expected %d got %d", num, s.TInt)
	}

	if s.TInt8 != int8(num) {
		t.Errorf("expected %d got %d", num, s.TInt8)
	}

	if s.TInt16 != int16(num) {
		t.Errorf("expected %d got %d", num, s.TInt16)
	}

	if s.TInt32 != int32(num) {
		t.Errorf("expected %d got %d", num, s.TInt32)
	}

	if s.TInt64 != int64(num) {
		t.Errorf("expected %d got %d", num, s.TInt64)
	}

	if s.TUint != uint(num) {
		t.Errorf("expected %d got %d", num, s.TUint)
	}

	if s.TUint8 != uint8(num) {
		t.Errorf("expected %d got %d", num, s.TUint8)
	}

	if s.TUint16 != uint16(num) {
		t.Errorf("expected %d got %d", num, s.TUint16)
	}

	if s.TUint32 != uint32(num) {
		t.Errorf("expected %d got %d", num, s.TUint32)
	}

	if s.TUint64 != uint64(num) {
		t.Errorf("expected %d got %d", num, s.TUint64)
	}

	if s.TFloat32 != float32(num) {
		t.Errorf("expected %d got %v", num, s.TFloat32)
	}

	if s.TFloat64 != float64(num) {
		t.Errorf("expected %d got %v", num, s.TFloat64)
	}

}
<commit_msg>Add testcase<commit_after>package cloth

import (
	"testing"

	"bytes"
	"encoding/binary"

	"github.com/osamingo/boolconv"
	"google.golang.org/cloud/bigtable"
)

func TestReadItemsErrorCase(t *testing.T) {

	s := struct {
		T int `bigtable:"test"`
	}{}

	err := ReadItems(nil, nil)
	if err != nil {
		t.Error("error should be nil")
	}

	ris := []*bigtable.ReadItem{
		&bigtable.ReadItem{
			Column: "test",
			Value:  []byte("test"),
		},
	}

	err = ReadItems(ris, struct{}{})
	if err != nil {
		t.Error("error should be nil")
	}

	err = ReadItems(ris, &s)
	if err == nil {
		t.Error("error is occurred")
	}

}

func TestReadItems(t *testing.T) {

	s := struct {
		TNonTag  string
		TString  string  `bigtable:"tstr"`
		TBool    bool    `bigtable:"tbool"`
		TInt     int     `bigtable:"tint"`
		TInt8    int8    `bigtable:"tint8"`
		TInt16   int16   `bigtable:"tint16"`
		TInt32   int32   `bigtable:"tint32"`
		TInt64   int64   `bigtable:"tint64"`
		TUint    uint    `bigtable:"tuint"`
		TUint8   uint8   `bigtable:"tuint8"`
		TUint16  uint16  `bigtable:"tuint16"`
		TUint32  uint32  `bigtable:"tuint32"`
		TUint64  uint64  `bigtable:"tuint64"`
		TFloat32 float32 `bigtable:"tfloat32"`
		TFloat64 float64 `bigtable:"tfloat64"`
	}{}

	str := "hoge"
	bl := true
	num := 123
	buf := &bytes.Buffer{}

	ris := []*bigtable.ReadItem{
		&bigtable.ReadItem{
			Column: "tstr",
			Value:  []byte(str),
		},
		&bigtable.ReadItem{
			Column: "tbool",
			Value:  boolconv.NewBool(bl).Bytes(),
		},
	}

	binary.Write(buf, binary.BigEndian, int64(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tint",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, int8(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tint8",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, int16(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tint16",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, int32(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tint32",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, int64(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tint64",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, uint64(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tuint",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, uint8(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tuint8",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, uint16(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tuint16",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, uint32(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tuint32",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, uint64(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tuint64",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, float32(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tfloat32",
		Value:  buf.Bytes(),
	})

	buf = &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, float64(num))
	ris = append(ris, &bigtable.ReadItem{
		Column: "tfloat64",
		Value:  buf.Bytes(),
	})

	err := ReadItems(ris, &s)
	if err != nil {
		t.Error("error should not be nil")
	}

	if s.TString != str {
		t.Errorf("expected %s got %s", str, s.TString)
	}

	if !s.TBool {
		t.Errorf("expected %v got %v", bl, s.TBool)
	}

	if s.TInt != int(num) {
		t.Errorf("expected %d got %d", num, s.TInt)
	}

	if s.TInt8 != int8(num) {
		t.Errorf("expected %d got %d", num, s.TInt8)
	}

	if s.TInt16 != int16(num) {
		t.Errorf("expected %d got %d", num, s.TInt16)
	}

	if s.TInt32 != int32(num) {
		t.Errorf("expected %d got %d", num, s.TInt32)
	}

	if s.TInt64 != int64(num) {
		t.Errorf("expected %d got %d", num, s.TInt64)
	}

	if s.TUint != uint(num) {
		t.Errorf("expected %d got %d", num, s.TUint)
	}

	if s.TUint8 != uint8(num) {
		t.Errorf("expected %d got %d", num, s.TUint8)
	}

	if s.TUint16 != uint16(num) {
		t.Errorf("expected %d got %d", num, s.TUint16)
	}

	if s.TUint32 != uint32(num) {
		t.Errorf("expected %d got %d", num, s.TUint32)
	}

	if s.TUint64 != uint64(num) {
		t.Errorf("expected %d got %d", num, s.TUint64)
	}

	if s.TFloat32 != float32(num) {
		t.Errorf("expected %d got %v", num, s.TFloat32)
	}

	if s.TFloat64 != float64(num) {
		t.Errorf("expected %d got %v", num, s.TFloat64)
	}

}
<|endoftext|>


<commit_before>package yaml

import (
	"testing"

	"github.com/zclconf/go-cty/cty"
)

func TestUnmarshal(t *testing.T) {
	tests := map[string]struct {
		converter *Converter
		src       string
		ty        cty.Type
		want      cty.Value
		wantErr   string
	}{
		"single string doublequote": {
			Standard,
			`"hello"`,
			cty.String,
			cty.StringVal("hello"),
			``,
		},
		"single string singlequote": {
			Standard,
			`'hello'`,
			cty.String,
			cty.StringVal("hello"),
			``,
		},
		"single string literal": {
			Standard,
			"|\n  hello\n  world",
			cty.String,
			cty.StringVal("hello\nworld"),
			``,
		},
		"single string folded": {
			Standard,
			">\n  hello\n  world",
			cty.String,
			cty.StringVal("hello world"),
			``,
		},
		"single string implied": {
			Standard,
			`hello`,
			cty.String,
			cty.StringVal("hello"),
			``,
		},
		"single string implied not merge": {
			Standard,
			`<<`,
			cty.String,
			cty.StringVal("<<"),
			``,
		},
		"single string short tag": {
			Standard,
			`!!str true`,
			cty.String,
			cty.StringVal("true"),
			``,
		},
		"single string long tag": {
			Standard,
			`!<tag:yaml.org,2002:str> true`,
			cty.String,
			cty.StringVal("true"),
			``,
		},
		"single bool implied true": {
			Standard,
			`true`,
			cty.Bool,
			cty.True,
			``,
		},
		"single bool implied converted to string": {
			Standard,
			`yes`,                 // YAML defines this as being a boolean true...
			cty.String,            // but we want a string result...
			cty.StringVal("true"), // so the boolean is converted to string using cty's rules
			``,
		},
		"single bool implied false": {
			Standard,
			`false`,
			cty.Bool,
			cty.False,
			``,
		},
		"single bool short tag": {
			Standard,
			`!!bool true`,
			cty.Bool,
			cty.True,
			``,
		},
		"single bool long tag": {
			Standard,
			`!<tag:yaml.org,2002:bool> true`,
			cty.Bool,
			cty.True,
			``,
		},
		"single bool short tag invalid": {
			Standard,
			`!!bool bananas`,
			cty.Bool,
			cty.NilVal,
			`cannot parse "bananas" as tag:yaml.org,2002:bool`,
		},
		"single float implied by prefix": {
			Standard,
			`.5`,
			cty.Number,
			cty.NumberFloatVal(0.5),
			``,
		},
		"single float implied by parsability": {
			Standard,
			`1.5`,
			cty.Number,
			cty.NumberFloatVal(1.5),
			``,
		},
		"single float short tag": {
			Standard,
			`!!float 1.5`,
			cty.Number,
			cty.NumberFloatVal(1.5),
			``,
		},
		"single int implied by parsability": {
			Standard,
			`12`,
			cty.Number,
			cty.NumberIntVal(12),
			``,
		},
		"single int negative implied by parsability": {
			Standard,
			`-12`,
			cty.Number,
			cty.NumberIntVal(-12),
			``,
		},
		"single int short tag": {
			Standard,
			`!!int 1`,
			cty.Number,
			cty.NumberIntVal(1),
			``,
		},
		"single positive infinity implied": {
			Standard,
			`+Inf`,
			cty.Number,
			cty.PositiveInfinity,
			``,
		},
		"single negative infinity implied": {
			Standard,
			`-Inf`,
			cty.Number,
			cty.NegativeInfinity,
			``,
		},
		"single NaN implied": {
			Standard,
			`.NaN`,
			cty.Number,
			cty.NilVal,
			`floating point NaN is not supported`,
		},
		"single timestamp implied": {
			Standard,
			`2006-1-2`,
			cty.String,
			cty.StringVal("2006-01-02T00:00:00Z"),
			``,
		},
		"single timestamp short tag": {
			Standard,
			`!!timestamp 2006-1-2`,
			cty.String,
			cty.StringVal("2006-01-02T00:00:00Z"),
			``,
		},
		"single binary short tag": {
			Standard,
			`!!binary 'aGVsbG8='`,
			cty.String,
			cty.StringVal("aGVsbG8="),
			``,
		},
		"single binary short tag invalid base64": {
			Standard,
			`!!binary '>>>>>>>>>'`,
			cty.String,
			cty.NilVal,
			`cannot parse ">>>>>>>>>" as tag:yaml.org,2002:binary: not valid base64`,
		},
		"single null implied": {
			Standard,
			`null`,
			cty.String,
			cty.NullVal(cty.String),
			``,
		},
		"single scalar invalid tag": {
			Standard,
			`!!nope foo`,
			cty.String,
			cty.NilVal,
			`unsupported tag "tag:yaml.org,2002:nope"`,
		},

		"mapping empty flow mode": {
			Standard,
			`{}`,
			cty.Map(cty.String),
			cty.MapValEmpty(cty.String),
			``,
		},
		"mapping flow mode": {
			Standard,
			`{a: 1, b: true}`,
			cty.Object(map[string]cty.Type{
				"a": cty.Number,
				"b": cty.Bool,
			}),
			cty.ObjectVal(map[string]cty.Value{
				"a": cty.NumberIntVal(1),
				"b": cty.True,
			}),
			``,
		},
		"mapping multi-line mode": {
			Standard,
			`
a: 1
b: true
`,
			cty.Object(map[string]cty.Type{
				"a": cty.Number,
				"b": cty.Bool,
			}),
			cty.ObjectVal(map[string]cty.Value{
				"a": cty.NumberIntVal(1),
				"b": cty.True,
			}),
			``,
		},

		"mapping with sequence multi-line mode": {
			Standard,
			`
a: 1
b:
  - foo
  - <<
  - baz
`,
			cty.Object(map[string]cty.Type{
				"a": cty.Number,
				"b": cty.List(cty.String),
			}),
			cty.ObjectVal(map[string]cty.Value{
				"a": cty.NumberIntVal(1),
				"b": cty.ListVal([]cty.Value{
					cty.StringVal("foo"),
					cty.StringVal("<<"),
					cty.StringVal("baz"),
				}),
			}),
			``,
		},
		"sequence empty flow mode": {
			Standard,
			`[]`,
			cty.Set(cty.String),
			cty.SetValEmpty(cty.String),
			``,
		},
		"sequence flow mode": {
			Standard,
			`[a, b, true]`,
			cty.Tuple([]cty.Type{
				cty.String,
				cty.String,
				cty.Bool,
			}),
			cty.TupleVal([]cty.Value{
				cty.StringVal("a"),
				cty.StringVal("b"),
				cty.True,
			}),
			``,
		},
		"sequence multi-line mode": {
			Standard,
			`
- a
- <<
- true
`,
			cty.Tuple([]cty.Type{
				cty.String,
				cty.String,
				cty.Bool,
			}),
			cty.TupleVal([]cty.Value{
				cty.StringVal("a"),
				cty.StringVal("<<"),
				cty.True,
			}),
			``,
		},

		"alias": {
			Standard,
			`
foo: &bar
  - x
bar: *bar
`,
			cty.Map(cty.List(cty.String)),
			cty.MapVal(map[string]cty.Value{
				"foo": cty.ListVal([]cty.Value{
					cty.StringVal("x"),
				}),
				"bar": cty.ListVal([]cty.Value{
					cty.StringVal("x"),
				}),
			}),
			``,
		},
		"alias cyclic": {
			Standard,
			`
foo: &bar
  - x
  - *bar
`,
			cty.DynamicPseudoType,
			cty.NilVal,
			`on line 3, column 5: cannot refer to anchor "bar" from inside its own definition`,
		},
		"alias merge": {
			Standard,
			`
foo: &bar
  a: b
bar:
  <<: *bar
  c: d
`,
			cty.Map(cty.Map(cty.String)),
			cty.MapVal(map[string]cty.Value{
				"foo": cty.MapVal(map[string]cty.Value{
					"a": cty.StringVal("b"),
				}),
				"bar": cty.MapVal(map[string]cty.Value{
					"a": cty.StringVal("b"),
					"c": cty.StringVal("d"),
				}),
			}),
			``,
		},
	}

	for name, test := range tests {
		t.Run(name, func(t *testing.T) {
			got, gotErr := test.converter.Unmarshal([]byte(test.src), test.ty)

			if gotErr != nil {
				if test.wantErr == "" {
					t.Fatalf("wrong error\ngot:  %s\nwant: (no error)", gotErr.Error())
				}
				if got, want := gotErr.Error(), test.wantErr; got != want {
					t.Fatalf("wrong error\ngot:  %s\nwant: %s", got, want)
				}
				return
			}

			if test.wantErr != "" {
				t.Fatalf("wrong error\ngot:  (no error)\nwant: %s", test.wantErr)
			}
			if !test.want.RawEquals(got) {
				t.Fatalf("wrong result\ngot:  %#v\nwant: %#v", got, test.want)
			}
		})
	}
}
<commit_msg>A test for decoding scalar aliases<commit_after>package yaml

import (
	"testing"

	"github.com/zclconf/go-cty/cty"
)

func TestUnmarshal(t *testing.T) {
	tests := map[string]struct {
		converter *Converter
		src       string
		ty        cty.Type
		want      cty.Value
		wantErr   string
	}{
		"single string doublequote": {
			Standard,
			`"hello"`,
			cty.String,
			cty.StringVal("hello"),
			``,
		},
		"single string singlequote": {
			Standard,
			`'hello'`,
			cty.String,
			cty.StringVal("hello"),
			``,
		},
		"single string literal": {
			Standard,
			"|\n  hello\n  world",
			cty.String,
			cty.StringVal("hello\nworld"),
			``,
		},
		"single string folded": {
			Standard,
			">\n  hello\n  world",
			cty.String,
			cty.StringVal("hello world"),
			``,
		},
		"single string implied": {
			Standard,
			`hello`,
			cty.String,
			cty.StringVal("hello"),
			``,
		},
		"single string implied not merge": {
			Standard,
			`<<`,
			cty.String,
			cty.StringVal("<<"),
			``,
		},
		"single string short tag": {
			Standard,
			`!!str true`,
			cty.String,
			cty.StringVal("true"),
			``,
		},
		"single string long tag": {
			Standard,
			`!<tag:yaml.org,2002:str> true`,
			cty.String,
			cty.StringVal("true"),
			``,
		},
		"single bool implied true": {
			Standard,
			`true`,
			cty.Bool,
			cty.True,
			``,
		},
		"single bool implied converted to string": {
			Standard,
			`yes`,                 // YAML defines this as being a boolean true...
			cty.String,            // but we want a string result...
			cty.StringVal("true"), // so the boolean is converted to string using cty's rules
			``,
		},
		"single bool implied false": {
			Standard,
			`false`,
			cty.Bool,
			cty.False,
			``,
		},
		"single bool short tag": {
			Standard,
			`!!bool true`,
			cty.Bool,
			cty.True,
			``,
		},
		"single bool long tag": {
			Standard,
			`!<tag:yaml.org,2002:bool> true`,
			cty.Bool,
			cty.True,
			``,
		},
		"single bool short tag invalid": {
			Standard,
			`!!bool bananas`,
			cty.Bool,
			cty.NilVal,
			`cannot parse "bananas" as tag:yaml.org,2002:bool`,
		},
		"single float implied by prefix": {
			Standard,
			`.5`,
			cty.Number,
			cty.NumberFloatVal(0.5),
			``,
		},
		"single float implied by parsability": {
			Standard,
			`1.5`,
			cty.Number,
			cty.NumberFloatVal(1.5),
			``,
		},
		"single float short tag": {
			Standard,
			`!!float 1.5`,
			cty.Number,
			cty.NumberFloatVal(1.5),
			``,
		},
		"single int implied by parsability": {
			Standard,
			`12`,
			cty.Number,
			cty.NumberIntVal(12),
			``,
		},
		"single int negative implied by parsability": {
			Standard,
			`-12`,
			cty.Number,
			cty.NumberIntVal(-12),
			``,
		},
		"single int short tag": {
			Standard,
			`!!int 1`,
			cty.Number,
			cty.NumberIntVal(1),
			``,
		},
		"single positive infinity implied": {
			Standard,
			`+Inf`,
			cty.Number,
			cty.PositiveInfinity,
			``,
		},
		"single negative infinity implied": {
			Standard,
			`-Inf`,
			cty.Number,
			cty.NegativeInfinity,
			``,
		},
		"single NaN implied": {
			Standard,
			`.NaN`,
			cty.Number,
			cty.NilVal,
			`floating point NaN is not supported`,
		},
		"single timestamp implied": {
			Standard,
			`2006-1-2`,
			cty.String,
			cty.StringVal("2006-01-02T00:00:00Z"),
			``,
		},
		"single timestamp short tag": {
			Standard,
			`!!timestamp 2006-1-2`,
			cty.String,
			cty.StringVal("2006-01-02T00:00:00Z"),
			``,
		},
		"single binary short tag": {
			Standard,
			`!!binary 'aGVsbG8='`,
			cty.String,
			cty.StringVal("aGVsbG8="),
			``,
		},
		"single binary short tag invalid base64": {
			Standard,
			`!!binary '>>>>>>>>>'`,
			cty.String,
			cty.NilVal,
			`cannot parse ">>>>>>>>>" as tag:yaml.org,2002:binary: not valid base64`,
		},
		"single null implied": {
			Standard,
			`null`,
			cty.String,
			cty.NullVal(cty.String),
			``,
		},
		"single scalar invalid tag": {
			Standard,
			`!!nope foo`,
			cty.String,
			cty.NilVal,
			`unsupported tag "tag:yaml.org,2002:nope"`,
		},

		"mapping empty flow mode": {
			Standard,
			`{}`,
			cty.Map(cty.String),
			cty.MapValEmpty(cty.String),
			``,
		},
		"mapping flow mode": {
			Standard,
			`{a: 1, b: true}`,
			cty.Object(map[string]cty.Type{
				"a": cty.Number,
				"b": cty.Bool,
			}),
			cty.ObjectVal(map[string]cty.Value{
				"a": cty.NumberIntVal(1),
				"b": cty.True,
			}),
			``,
		},
		"mapping multi-line mode": {
			Standard,
			`
a: 1
b: true
`,
			cty.Object(map[string]cty.Type{
				"a": cty.Number,
				"b": cty.Bool,
			}),
			cty.ObjectVal(map[string]cty.Value{
				"a": cty.NumberIntVal(1),
				"b": cty.True,
			}),
			``,
		},

		"mapping with sequence multi-line mode": {
			Standard,
			`
a: 1
b:
  - foo
  - <<
  - baz
`,
			cty.Object(map[string]cty.Type{
				"a": cty.Number,
				"b": cty.List(cty.String),
			}),
			cty.ObjectVal(map[string]cty.Value{
				"a": cty.NumberIntVal(1),
				"b": cty.ListVal([]cty.Value{
					cty.StringVal("foo"),
					cty.StringVal("<<"),
					cty.StringVal("baz"),
				}),
			}),
			``,
		},
		"sequence empty flow mode": {
			Standard,
			`[]`,
			cty.Set(cty.String),
			cty.SetValEmpty(cty.String),
			``,
		},
		"sequence flow mode": {
			Standard,
			`[a, b, true]`,
			cty.Tuple([]cty.Type{
				cty.String,
				cty.String,
				cty.Bool,
			}),
			cty.TupleVal([]cty.Value{
				cty.StringVal("a"),
				cty.StringVal("b"),
				cty.True,
			}),
			``,
		},
		"sequence multi-line mode": {
			Standard,
			`
- a
- <<
- true
`,
			cty.Tuple([]cty.Type{
				cty.String,
				cty.String,
				cty.Bool,
			}),
			cty.TupleVal([]cty.Value{
				cty.StringVal("a"),
				cty.StringVal("<<"),
				cty.True,
			}),
			``,
		},

		"alias": {
			Standard,
			`
foo: &bar
  - x
bar: *bar
`,
			cty.Map(cty.List(cty.String)),
			cty.MapVal(map[string]cty.Value{
				"foo": cty.ListVal([]cty.Value{
					cty.StringVal("x"),
				}),
				"bar": cty.ListVal([]cty.Value{
					cty.StringVal("x"),
				}),
			}),
			``,
		},
		"alias cyclic": {
			Standard,
			`
foo: &bar
  - x
  - *bar
`,
			cty.DynamicPseudoType,
			cty.NilVal,
			`on line 3, column 5: cannot refer to anchor "bar" from inside its own definition`,
		},
		"alias merge": {
			Standard,
			`
foo: &bar
  a: b
bar:
  <<: *bar
  c: d
`,
			cty.Map(cty.Map(cty.String)),
			cty.MapVal(map[string]cty.Value{
				"foo": cty.MapVal(map[string]cty.Value{
					"a": cty.StringVal("b"),
				}),
				"bar": cty.MapVal(map[string]cty.Value{
					"a": cty.StringVal("b"),
					"c": cty.StringVal("d"),
				}),
			}),
			``,
		},
		"alias scalar": {
			Standard,
			`
- &foo a
- b
- *foo
`,
			cty.List(cty.String),
			cty.ListVal([]cty.Value{
				cty.StringVal("a"),
				cty.StringVal("b"),
				cty.StringVal("a"),
			}),
			``,
		},
	}

	for name, test := range tests {
		t.Run(name, func(t *testing.T) {
			got, gotErr := test.converter.Unmarshal([]byte(test.src), test.ty)

			if gotErr != nil {
				if test.wantErr == "" {
					t.Fatalf("wrong error\ngot:  %s\nwant: (no error)", gotErr.Error())
				}
				if got, want := gotErr.Error(), test.wantErr; got != want {
					t.Fatalf("wrong error\ngot:  %s\nwant: %s", got, want)
				}
				return
			}

			if test.wantErr != "" {
				t.Fatalf("wrong error\ngot:  (no error)\nwant: %s", test.wantErr)
			}
			if !test.want.RawEquals(got) {
				t.Fatalf("wrong result\ngot:  %#v\nwant: %#v", got, test.want)
			}
		})
	}
}
<|endoftext|>


<commit_before>package gorma

const modelTmpl = `// {{if .Description}}{{.Description}}{{else}}app.{{gotypename . 0}} storage type{{end}}
// Identifier: {{ $typeName :=  gotypename . 0}}{{$typeName := demodel $typeName}}
{{$td := gotypedef . 0 true false}}type {{$typeName}} {{modeldef $td .}}
{{ $belongsto := index .Metadata "github.com/bketelsen/gorma#belongsto" }}
func {{$typeName}}FromCreatePayload(ctx *app.Create{{demodel $typeName}}Context) {{$typeName}} {
	payload := ctx.Payload
	m := {{$typeName}}{}
	copier.Copy(&m, payload)
	{{ if ne $belongsto "" }} m.{{ $belongsto }}ID=uint(ctx.{{ demodel $belongsto }}ID){{end}}
	return m
}

func {{$typeName}}FromUpdatePayload(ctx *app.Update{{demodel $typeName}}Context) {{$typeName}} {
	payload := ctx.Payload
	m := {{$typeName}}{}
	copier.Copy(&m, payload)
	return m
}
func (m {{$typeName}}) ToApp() *app.{{$typeName}} {
	target := app.{{demodel $typeName}}{}
	copier.Copy(&target, &m)
	return &target
}

type {{$typeName}}Storage interface {
	List(ctx *app.List{{demodel $typeName}}Context) []{{$typeName}}
	Get(ctx *app.Show{{demodel $typeName }}Context) ({{$typeName}}, error)
	Add(ctx *app.Create{{demodel $typeName}}Context) ({{$typeName}}, error)
	Update(ctx *app.Update{{demodel $typeName}}Context) (error)
	Delete(ctx *app.Delete{{demodel $typeName}}Context) (error)
}

type {{$typeName}}DB struct {
	DB gorm.DB
}
{{ if ne $belongsto "" }}
// would prefer to just pass a context in here, but they're all different, so can't
func {{$typeName}}Filter(parentid int, originaldb *gorm.DB) func(db *gorm.DB) *gorm.DB {
	if parentid > 0 {
		return func(db *gorm.DB) *gorm.DB {
			return db.Where("{{ snake $belongsto }}_id = ?", parentid)
		}
	} else {
		return func(db *gorm.DB) *gorm.DB {
			return db
		}
	}
}{{end}}
func New{{$typeName}}DB(db gorm.DB) *{{$typeName}}DB {
	return &{{$typeName}}DB{DB: db}
}

func (m *{{$typeName}}DB) List(ctx *app.List{{demodel $typeName}}Context) []{{$typeName}} {

	var objs []{{$typeName}}
    {{ if ne $belongsto "" }}m.DB.Scopes({{$typeName}}Filter(ctx.{{demodel $belongsto}}ID, &m.DB)).Find(&objs){{ else }} m.DB.Find(&objs) {{end}}
	return objs
}

func (m *{{$typeName}}DB) Get(ctx *app.Show{{demodel $typeName}}Context) ({{$typeName}}, error) {

	var obj {{$typeName}}

	err := m.DB.Find(&obj, ctx.{{demodel $typeName}}ID).Error
	if err != nil {
		ctx.Error(err.Error())
	}
	return obj, err
}

func (m *{{$typeName}}DB) Add(ctx *app.Create{{demodel $typeName}}Context) ({{$typeName}}, error) {
	model := {{$typeName}}FromCreatePayload(ctx)
	err := m.DB.Create(&model).Error
	return model, err
}
func (m *{{$typeName}}DB) Update(ctx *app.Update{{demodel $typeName}}Context) error {
	getCtx, err := app.NewShow{{demodel $typeName}}Context(ctx.Context)
	if err != nil {
		return  err
	}
	obj, err := m.Get(getCtx)
	if err != nil {
		return  err
	}
	err = m.DB.Model(&obj).Updates({{$typeName}}FromUpdatePayload(ctx)).Error
	if err != nil {
		ctx.Error(err.Error())
	}
	return err
}
func (m *{{$typeName}}DB) Delete(ctx *app.Delete{{demodel $typeName}}Context)  error {
	var obj {{$typeName}}
	err := m.DB.Delete(&obj, ctx.{{demodel $typeName}}ID).Error
	if err != nil {
		ctx.Logger.Error(err.Error())
		return  err
	}
	return  nil
}

type Mock{{$typeName}}Storage struct {
	{{$typeName}}List  map[uint]{{$typeName}}
	nextID uint
	mut sync.Mutex
}
{{if ne $belongsto ""}}
func filter{{$typeName}}By{{$belongsto}}(parent int, list []{{$typeName}}) []{{$typeName}} {
	filtered := make([]{{$typeName}},0)
	for _,o := range list {
		if o.{{$belongsto}}ID == uint(parent) {
			filtered = append(filtered,o)
		}
	}
	return filtered
}
{{end}}

func NewMock{{$typeName}}Storage() *Mock{{$typeName}}Storage {
	ml := make(map[uint]{{$typeName}}, 0)
	return &Mock{{$typeName}}Storage{ {{$typeName}}List: ml}
}

func (db *Mock{{$typeName}}Storage) List(ctx *app.List{{demodel $typeName}}Context) []{{$typeName}} {
	var list []{{$typeName}} = make([]{{$typeName}}, 0)
	for _, v := range db.{{$typeName}}List {
		list = append(list, v)
	}
{{if ne $belongsto ""}}
return filter{{$typeName}}By{{$belongsto}}(ctx.{{$belongsto}}ID, list) {{else}}return list{{end}}
}

func (db *Mock{{$typeName}}Storage) Get(ctx *app.Show{{demodel $typeName}}Context) ({{$typeName}}, error) {

	var obj {{$typeName}}

	obj, ok := db.{{$typeName}}List[uint(ctx.{{demodel $typeName}}ID)]
	if ok {
		return obj, nil
	} else {
		return obj, errors.New("{{$typeName}} does not exist")
	}
}

func (db *Mock{{$typeName}}Storage) Add(ctx *app.Create{{demodel $typeName}}Context)  ({{$typeName}}, error) {
	u := {{$typeName}}FromCreatePayload(ctx)
	db.mut.Lock()
	db.nextID = db.nextID + 1
	u.ID = db.nextID
	db.mut.Unlock()

	db.{{$typeName}}List[u.ID] = u
	return u, nil
}

func (db *Mock{{$typeName}}Storage) Update(ctx *app.Update{{demodel $typeName}}Context) error {
	id := uint(ctx.{{demodel $typeName}}ID)
	_, ok := db.{{$typeName}}List[id]
	if ok {
		db.{{$typeName}}List[id] = {{$typeName}}FromUpdatePayload(ctx)
		return  nil
	} else {
		return errors.New("{{$typeName}} does not exist")
	}
}

func (db *Mock{{$typeName}}Storage) Delete(ctx *app.Delete{{demodel $typeName}}Context)  error {
	_, ok := db.{{$typeName}}List[uint(ctx.{{demodel $typeName}}ID)]
	if ok {
		delete(db.{{$typeName}}List, uint(ctx.{{demodel $typeName}}ID))
		return  nil
	} else {
		return  errors.New("Could not delete this user")
	}
}
`
<commit_msg>fix model<commit_after>package gorma

const modelTmpl = `// {{if .Description}}{{.Description}}{{else}}app.{{gotypename . 0}} storage type{{end}}
// Identifier: {{ $typeName :=  gotypename . 0}}{{$typeName := demodel $typeName}}
{{$td := gotypedef . 0 true false}}type {{$typeName}} {{modeldef $td .}}
{{ $belongsto := index .Metadata "github.com/bketelsen/gorma#belongsto" }}
func {{$typeName}}FromCreatePayload(ctx *app.Create{{demodel $typeName}}Context) {{$typeName}} {
	payload := ctx.Payload
	m := {{$typeName}}{}
	copier.Copy(&m, payload)
	{{ if ne $belongsto "" }} m.{{ $belongsto }}ID=uint(ctx.{{ demodel $belongsto }}ID){{end}}
	return m
}

func {{$typeName}}FromUpdatePayload(ctx *app.Update{{demodel $typeName}}Context) {{$typeName}} {
	payload := ctx.Payload
	m := {{$typeName}}{}
	copier.Copy(&m, payload)
	return m
}
func (m {{$typeName}}) ToApp() *app.{{demodel $typeName}} {
	target := app.{{demodel $typeName}}{}
	copier.Copy(&target, &m)
	return &target
}

type {{$typeName}}Storage interface {
	List(ctx *app.List{{demodel $typeName}}Context) []{{$typeName}}
	Get(ctx *app.Show{{demodel $typeName }}Context) ({{$typeName}}, error)
	Add(ctx *app.Create{{demodel $typeName}}Context) ({{$typeName}}, error)
	Update(ctx *app.Update{{demodel $typeName}}Context) (error)
	Delete(ctx *app.Delete{{demodel $typeName}}Context) (error)
}

type {{$typeName}}DB struct {
	DB gorm.DB
}
{{ if ne $belongsto "" }}
// would prefer to just pass a context in here, but they're all different, so can't
func {{$typeName}}Filter(parentid int, originaldb *gorm.DB) func(db *gorm.DB) *gorm.DB {
	if parentid > 0 {
		return func(db *gorm.DB) *gorm.DB {
			return db.Where("{{ snake $belongsto }}_id = ?", parentid)
		}
	} else {
		return func(db *gorm.DB) *gorm.DB {
			return db
		}
	}
}{{end}}
func New{{$typeName}}DB(db gorm.DB) *{{$typeName}}DB {
	return &{{$typeName}}DB{DB: db}
}

func (m *{{$typeName}}DB) List(ctx *app.List{{demodel $typeName}}Context) []{{$typeName}} {

	var objs []{{$typeName}}
    {{ if ne $belongsto "" }}m.DB.Scopes({{$typeName}}Filter(ctx.{{demodel $belongsto}}ID, &m.DB)).Find(&objs){{ else }} m.DB.Find(&objs) {{end}}
	return objs
}

func (m *{{$typeName}}DB) Get(ctx *app.Show{{demodel $typeName}}Context) ({{$typeName}}, error) {

	var obj {{$typeName}}

	err := m.DB.Find(&obj, ctx.{{demodel $typeName}}ID).Error
	if err != nil {
		ctx.Error(err.Error())
	}
	return obj, err
}

func (m *{{$typeName}}DB) Add(ctx *app.Create{{demodel $typeName}}Context) ({{$typeName}}, error) {
	model := {{$typeName}}FromCreatePayload(ctx)
	err := m.DB.Create(&model).Error
	return model, err
}
func (m *{{$typeName}}DB) Update(ctx *app.Update{{demodel $typeName}}Context) error {
	getCtx, err := app.NewShow{{demodel $typeName}}Context(ctx.Context)
	if err != nil {
		return  err
	}
	obj, err := m.Get(getCtx)
	if err != nil {
		return  err
	}
	err = m.DB.Model(&obj).Updates({{$typeName}}FromUpdatePayload(ctx)).Error
	if err != nil {
		ctx.Error(err.Error())
	}
	return err
}
func (m *{{$typeName}}DB) Delete(ctx *app.Delete{{demodel $typeName}}Context)  error {
	var obj {{$typeName}}
	err := m.DB.Delete(&obj, ctx.{{demodel $typeName}}ID).Error
	if err != nil {
		ctx.Logger.Error(err.Error())
		return  err
	}
	return  nil
}

type Mock{{$typeName}}Storage struct {
	{{$typeName}}List  map[uint]{{$typeName}}
	nextID uint
	mut sync.Mutex
}
{{if ne $belongsto ""}}
func filter{{$typeName}}By{{$belongsto}}(parent int, list []{{$typeName}}) []{{$typeName}} {
	filtered := make([]{{$typeName}},0)
	for _,o := range list {
		if o.{{$belongsto}}ID == uint(parent) {
			filtered = append(filtered,o)
		}
	}
	return filtered
}
{{end}}

func NewMock{{$typeName}}Storage() *Mock{{$typeName}}Storage {
	ml := make(map[uint]{{$typeName}}, 0)
	return &Mock{{$typeName}}Storage{ {{$typeName}}List: ml}
}

func (db *Mock{{$typeName}}Storage) List(ctx *app.List{{demodel $typeName}}Context) []{{$typeName}} {
	var list []{{$typeName}} = make([]{{$typeName}}, 0)
	for _, v := range db.{{$typeName}}List {
		list = append(list, v)
	}
{{if ne $belongsto ""}}
return filter{{$typeName}}By{{$belongsto}}(ctx.{{$belongsto}}ID, list) {{else}}return list{{end}}
}

func (db *Mock{{$typeName}}Storage) Get(ctx *app.Show{{demodel $typeName}}Context) ({{$typeName}}, error) {

	var obj {{$typeName}}

	obj, ok := db.{{$typeName}}List[uint(ctx.{{demodel $typeName}}ID)]
	if ok {
		return obj, nil
	} else {
		return obj, errors.New("{{$typeName}} does not exist")
	}
}

func (db *Mock{{$typeName}}Storage) Add(ctx *app.Create{{demodel $typeName}}Context)  ({{$typeName}}, error) {
	u := {{$typeName}}FromCreatePayload(ctx)
	db.mut.Lock()
	db.nextID = db.nextID + 1
	u.ID = db.nextID
	db.mut.Unlock()

	db.{{$typeName}}List[u.ID] = u
	return u, nil
}

func (db *Mock{{$typeName}}Storage) Update(ctx *app.Update{{demodel $typeName}}Context) error {
	id := uint(ctx.{{demodel $typeName}}ID)
	_, ok := db.{{$typeName}}List[id]
	if ok {
		db.{{$typeName}}List[id] = {{$typeName}}FromUpdatePayload(ctx)
		return  nil
	} else {
		return errors.New("{{$typeName}} does not exist")
	}
}

func (db *Mock{{$typeName}}Storage) Delete(ctx *app.Delete{{demodel $typeName}}Context)  error {
	_, ok := db.{{$typeName}}List[uint(ctx.{{demodel $typeName}}ID)]
	if ok {
		delete(db.{{$typeName}}List, uint(ctx.{{demodel $typeName}}ID))
		return  nil
	} else {
		return  errors.New("Could not delete this user")
	}
}
`
<|endoftext|>