forked from benbjohnson/wtf
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dial.go
158 lines (133 loc) · 5.59 KB
/
dial.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package wtf
import (
"context"
"fmt"
"time"
"unicode/utf8"
)
// Dial constants.
const (
MaxDialNameLen = 100
)
// Dial represents an aggregate WTF level. They are used to roll up the WTF
// levels of multiple members and show an average WTF level.
//
// A dial is created by a user and can only be edited & deleted by the user who
// created it. Members can be added by sharing an invite link and accepting the
// invitation.
//
// The WTF level for the dial will immediately change when a member's WTF level
// changes and the change will be announced to all other members in real-time.
//
// See the EventService for more information about notifications.
type Dial struct {
ID int `json:"id"`
// Owner of the dial. Only the owner may delete the dial.
UserID int `json:"userID"`
User *User `json:"user"`
// Human-readable name of the dial.
Name string `json:"name"`
// Code used to share the dial with other users.
// It allows the creation of a shareable link without explicitly inviting users.
InviteCode string `json:"inviteCode,omitempty"`
// Aggregate WTF level for the dial. This is a computed field based on the
// average value of each member's WTF level.
Value int `json:"value"`
// Timestamps for dial creation & last update.
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
// List of associated members and their contributing WTF level.
// This is only set when returning a single dial.
Memberships []*DialMembership `json:"memberships,omitempty"`
}
// MembershipByUserID returns the membership attached to the dial for a given user.
// Returns nil if user is not associated with the dial or if memberships is unset.
func (d *Dial) MembershipByUserID(userID int) *DialMembership {
for _, m := range d.Memberships {
if m.UserID == userID {
return m
}
}
return nil
}
// Validate returns an error if dial has invalid fields. Only performs basic validation.
func (d *Dial) Validate() error {
if d.Name == "" {
return Errorf(EINVALID, "Dial name required.")
} else if utf8.RuneCountInString(d.Name) > MaxDialNameLen {
return Errorf(EINVALID, "Dial name too long.")
} else if d.UserID == 0 {
return Errorf(EINVALID, "Dial creator required.")
}
return nil
}
// CanEditDial returns true if the current user can edit the dial.
// Only the dial owner can edit the dial.
func CanEditDial(ctx context.Context, dial *Dial) bool {
return dial.UserID == UserIDFromContext(ctx)
}
// DialService represents a service for managing dials.
type DialService interface {
// Retrieves a single dial by ID along with associated memberships. Only
// the dial owner & members can see a dial. Returns ENOTFOUND if dial does
// not exist or user does not have permission to view it.
FindDialByID(ctx context.Context, id int) (*Dial, error)
// Retrieves a list of dials based on a filter. Only returns dials that
// the user owns or is a member of. Also returns a count of total matching
// dials which may different from the number of returned dials if the
// "Limit" field is set.
FindDials(ctx context.Context, filter DialFilter) ([]*Dial, int, error)
// Creates a new dial and assigns the current user as the owner.
// The owner will automatically be added as a member of the new dial.
CreateDial(ctx context.Context, dial *Dial) error
// Updates an existing dial by ID. Only the dial owner can update a dial.
// Returns the new dial state even if there was an error during update.
//
// Returns ENOTFOUND if dial does not exist. Returns EUNAUTHORIZED if user
// is not the dial owner.
UpdateDial(ctx context.Context, id int, upd DialUpdate) (*Dial, error)
// Permanently removes a dial by ID. Only the dial owner may delete a dial.
// Returns ENOTFOUND if dial does not exist. Returns EUNAUTHORIZED if user
// is not the dial owner.
DeleteDial(ctx context.Context, id int) error
// Sets the value of the user's membership in a dial. This works the same
// as calling UpdateDialMembership() although it doesn't require that the
// user know their membership ID. Only the dial ID.
//
// Returns ENOTFOUND if the membership does not exist.
SetDialMembershipValue(ctx context.Context, dialID, value int) error
// AverageDialValueReport returns a report of the average dial value across
// all dials that the user is a member of. Average values are computed
// between start & end time and are slotted into given intervals. The
// minimum interval size is one minute.
AverageDialValueReport(ctx context.Context, start, end time.Time, interval time.Duration) (*DialValueReport, error)
}
// DialFilter represents a filter used by FindDials().
type DialFilter struct {
// Filtering fields.
ID *int `json:"id"`
InviteCode *string `json:"inviteCode"`
// Restrict to subset of range.
Offset int `json:"offset"`
Limit int `json:"limit"`
}
// DialUpdate represents a set of fields to update on a dial.
type DialUpdate struct {
Name *string `json:"name"`
}
// DialValueReport represents a report generated by AverageDialValueReport().
// Each record represents the average value within an interval of time.
type DialValueReport struct {
Records []*DialValueRecord
}
// DialValueRecord represents an average dial value at a given point in time
// for the DialValueReport.
type DialValueRecord struct {
Value int `json:"value"`
Timestamp time.Time `json:"timestamp"`
}
// GoString prints a more easily readable representation for debugging.
// The timestamp field is represented as an RFC 3339 string instead of a pointer.
func (r *DialValueRecord) GoString() string {
return fmt.Sprintf("&wtf.DialValueRecord{Value:%d, Timestamp:%q}", r.Value, r.Timestamp.Format(time.RFC3339))
}