/
user.go
174 lines (151 loc) · 4.46 KB
/
user.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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Copyright 2016 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package description
import (
"time"
"github.com/juju/errors"
"github.com/juju/schema"
"gopkg.in/juju/names.v2"
)
// User represents a user of the model. Users are able to connect to, and
// depending on the read only flag, modify the model.
type User interface {
Name() names.UserTag
DisplayName() string
CreatedBy() names.UserTag
DateCreated() time.Time
LastConnection() time.Time
Access() string
}
type users struct {
Version int `yaml:"version"`
Users_ []*user `yaml:"users"`
}
type UserArgs struct {
Name names.UserTag
DisplayName string
CreatedBy names.UserTag
DateCreated time.Time
LastConnection time.Time
Access string
}
func newUser(args UserArgs) *user {
u := &user{
Name_: args.Name.Id(),
DisplayName_: args.DisplayName,
CreatedBy_: args.CreatedBy.Id(),
DateCreated_: args.DateCreated,
Access_: args.Access,
}
if !args.LastConnection.IsZero() {
value := args.LastConnection
u.LastConnection_ = &value
}
return u
}
type user struct {
Name_ string `yaml:"name"`
DisplayName_ string `yaml:"display-name,omitempty"`
CreatedBy_ string `yaml:"created-by"`
DateCreated_ time.Time `yaml:"date-created"`
Access_ string `yaml:"access"`
// Can't use omitempty with time.Time, it just doesn't work,
// so use a pointer in the struct.
LastConnection_ *time.Time `yaml:"last-connection,omitempty"`
}
// Name implements User.
func (u *user) Name() names.UserTag {
return names.NewUserTag(u.Name_)
}
// DisplayName implements User.
func (u *user) DisplayName() string {
return u.DisplayName_
}
// CreatedBy implements User.
func (u *user) CreatedBy() names.UserTag {
return names.NewUserTag(u.CreatedBy_)
}
// DateCreated implements User.
func (u *user) DateCreated() time.Time {
return u.DateCreated_
}
// LastConnection implements User.
func (u *user) LastConnection() time.Time {
var zero time.Time
if u.LastConnection_ == nil {
return zero
}
return *u.LastConnection_
}
// Access implements User.
func (u *user) Access() string {
return u.Access_
}
func importUsers(source map[string]interface{}) ([]*user, error) {
checker := versionedChecker("users")
coerced, err := checker.Coerce(source, nil)
if err != nil {
return nil, errors.Annotatef(err, "users version schema check failed")
}
valid := coerced.(map[string]interface{})
version := int(valid["version"].(int64))
importFunc, ok := userDeserializationFuncs[version]
if !ok {
return nil, errors.NotValidf("version %d", version)
}
sourceList := valid["users"].([]interface{})
return importUserList(sourceList, importFunc)
}
func importUserList(sourceList []interface{}, importFunc userDeserializationFunc) ([]*user, error) {
result := make([]*user, 0, len(sourceList))
for i, value := range sourceList {
source, ok := value.(map[string]interface{})
if !ok {
return nil, errors.Errorf("unexpected value for user %d, %T", i, value)
}
user, err := importFunc(source)
if err != nil {
return nil, errors.Annotatef(err, "user %d", i)
}
result = append(result, user)
}
return result, nil
}
type userDeserializationFunc func(map[string]interface{}) (*user, error)
var userDeserializationFuncs = map[int]userDeserializationFunc{
1: importUserV1,
}
func importUserV1(source map[string]interface{}) (*user, error) {
fields := schema.Fields{
"name": schema.String(),
"display-name": schema.String(),
"created-by": schema.String(),
"read-only": schema.Bool(),
"date-created": schema.Time(),
"last-connection": schema.Time(),
"access": schema.String(),
}
// Some values don't have to be there.
defaults := schema.Defaults{
"display-name": "",
"last-connection": schema.Omit,
"read-only": false,
}
checker := schema.FieldMap(fields, defaults)
coerced, err := checker.Coerce(source, nil)
if err != nil {
return nil, errors.Annotatef(err, "user v1 schema check failed")
}
valid := coerced.(map[string]interface{})
// From here we know that the map returned from the schema coercion
// contains fields of the right type.
result := &user{
Name_: valid["name"].(string),
DisplayName_: valid["display-name"].(string),
CreatedBy_: valid["created-by"].(string),
DateCreated_: valid["date-created"].(time.Time),
Access_: valid["access"].(string),
LastConnection_: fieldToTimePtr(valid, "last-connection"),
}
return result, nil
}