1// Copyright © 2016 Aaron Longwell 2// 3// Use of this source code is governed by an MIT licese. 4// Details in the LICENSE file. 5 6package trello 7 8import ( 9 "fmt" 10 "time" 11) 12 13// Action represents Trello API actions 14// Actions are immutable event traces generated whenever an action occurs in Trello. 15// See https://developers.trello.com/reference/#actions. 16type Action struct { 17 ID string `json:"id"` 18 IDMemberCreator string `json:"idMemberCreator"` 19 Type string `json:"type"` 20 Date time.Time `json:"date"` 21 Data *ActionData `json:"data,omitempty"` 22 MemberCreator *Member `json:"memberCreator,omitempty"` 23 Member *Member `json:"member,omitempty"` 24} 25 26// ActionData represent the nested data of actions 27type ActionData struct { 28 Text string `json:"text,omitempty"` 29 List *List `json:"list,omitempty"` 30 Card *ActionDataCard `json:"card,omitempty"` 31 CardSource *ActionDataCard `json:"cardSource,omitempty"` 32 Board *Board `json:"board,omitempty"` 33 Old *ActionDataCard `json:"old,omitempty"` 34 ListBefore *List `json:"listBefore,omitempty"` 35 ListAfter *List `json:"listAfter,omitempty"` 36 DateLastEdited time.Time `json:"dateLastEdited"` 37 38 CheckItem *CheckItem `json:"checkItem"` 39 Checklist *Checklist `json:"checklist"` 40} 41 42// ActionDataCard represent the nested 'card' data attribute of actions 43type ActionDataCard struct { 44 ID string `json:"id"` 45 Name string `json:"name"` 46 IDShort int `json:"idShort"` 47 ShortLink string `json:"shortLink"` 48 Pos float64 `json:"pos"` 49 Closed bool `json:"closed"` 50} 51 52// GetActions make a GET call for a board's actions 53func (b *Board) GetActions(args Arguments) (actions ActionCollection, err error) { 54 path := fmt.Sprintf("boards/%s/actions", b.ID) 55 err = b.client.Get(path, args, &actions) 56 return 57} 58 59// GetActions makes a GET call for a list's actions 60func (l *List) GetActions(args Arguments) (actions ActionCollection, err error) { 61 path := fmt.Sprintf("lists/%s/actions", l.ID) 62 err = l.client.Get(path, args, &actions) 63 return 64} 65 66// GetActions makes a GET for a card's actions 67func (c *Card) GetActions(args Arguments) (actions ActionCollection, err error) { 68 path := fmt.Sprintf("cards/%s/actions", c.ID) 69 err = c.client.Get(path, args, &actions) 70 return 71} 72 73// GetListChangeActions retrieves a slice of Actions which resulted in changes 74// to the card's active List. This includes the createCard and copyCard action (which 75// place the card in its first list), and the updateCard:closed action (which remove it 76// from its last list). 77// 78// This function is just an alias for: 79// card.GetActions(Arguments{"filter": "createCard,copyCard,updateCard:idList,updateCard:closed", "limit": "1000"}) 80// 81func (c *Card) GetListChangeActions() (actions ActionCollection, err error) { 82 return c.GetActions(Arguments{"filter": "createCard,copyCard,updateCard:idList,updateCard:closed"}) 83} 84 85// GetMembershipChangeActions makes a GET call for a card's membership-change actions 86func (c *Card) GetMembershipChangeActions() (actions ActionCollection, err error) { 87 // We include updateCard:closed as if the member is implicitly removed from the card when it's closed. 88 // This allows us to "close out" the duration length. 89 return c.GetActions(Arguments{"filter": "addMemberToCard,removeMemberFromCard,updateCard:closed"}) 90} 91 92// DidCreateCard returns true if this action created a card, false otherwise. 93func (a *Action) DidCreateCard() bool { 94 switch a.Type { 95 case "createCard", "emailCard", "copyCard", "convertToCardFromCheckItem": 96 return true 97 case "moveCardToBoard": 98 return true // Unsure about this one 99 default: 100 return false 101 } 102} 103 104// DidArchiveCard returns true if the card was updated 105func (a *Action) DidArchiveCard() bool { 106 return (a.Type == "updateCard") && a.Data != nil && a.Data.Card != nil && a.Data.Card.Closed 107} 108 109// DidUnarchiveCard returns true if the card was unarchived 110func (a *Action) DidUnarchiveCard() bool { 111 return (a.Type == "updateCard") && a.Data != nil && a.Data.Old != nil && a.Data.Old.Closed 112} 113 114// DidChangeListForCard returns true if this action created the card (in which case it 115// caused it to enter its first list), archived the card (in which case it caused it to 116// leave its last List), or was an updateCard action involving a change to the list. This 117// is supporting functionality for ListDuration. 118// 119func (a *Action) DidChangeListForCard() bool { 120 if a.DidCreateCard() { 121 return true 122 } 123 if a.DidArchiveCard() { 124 return true 125 } 126 if a.DidUnarchiveCard() { 127 return true 128 } 129 if a.Type == "updateCard" { 130 if a.Data != nil && a.Data.ListAfter != nil { 131 return true 132 } 133 } 134 return false 135} 136 137// DidChangeCardMembership returns true if card's membership was changed 138func (a *Action) DidChangeCardMembership() bool { 139 switch a.Type { 140 case "addMemberToCard": 141 return true 142 case "removeMemberFromCard": 143 return true 144 default: 145 return false 146 } 147} 148 149// ListAfterAction calculates which List the card ended up in after this action 150// completed. Returns nil when the action resulted in the card being archived (in 151// which case we consider it to not be in a list anymore), or when the action isn't 152// related to a list at all (in which case this is a nonsensical question to ask). 153// 154func ListAfterAction(a *Action) *List { 155 switch a.Type { 156 case "createCard", "copyCard", "emailCard", "convertToCardFromCheckItem": 157 return a.Data.List 158 case "updateCard": 159 if a.DidArchiveCard() { 160 return nil 161 } else if a.DidUnarchiveCard() { 162 return a.Data.List 163 } 164 if a.Data.ListAfter != nil { 165 return a.Data.ListAfter 166 } 167 } 168 return nil 169} 170