github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/core/description/user.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package description 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/names" 11 "github.com/juju/schema" 12 ) 13 14 type users struct { 15 Version int `yaml:"version"` 16 Users_ []*user `yaml:"users"` 17 } 18 19 type UserArgs struct { 20 Name names.UserTag 21 DisplayName string 22 CreatedBy names.UserTag 23 DateCreated time.Time 24 LastConnection time.Time 25 ReadOnly bool 26 } 27 28 func newUser(args UserArgs) *user { 29 u := &user{ 30 Name_: args.Name.Canonical(), 31 DisplayName_: args.DisplayName, 32 CreatedBy_: args.CreatedBy.Canonical(), 33 DateCreated_: args.DateCreated, 34 ReadOnly_: args.ReadOnly, 35 } 36 if !args.LastConnection.IsZero() { 37 value := args.LastConnection 38 u.LastConnection_ = &value 39 } 40 return u 41 } 42 43 type user struct { 44 Name_ string `yaml:"name"` 45 DisplayName_ string `yaml:"display-name,omitempty"` 46 CreatedBy_ string `yaml:"created-by"` 47 DateCreated_ time.Time `yaml:"date-created"` 48 // Can't use omitempty with time.Time, it just doesn't work, 49 // so use a pointer in the struct. 50 LastConnection_ *time.Time `yaml:"last-connection,omitempty"` 51 ReadOnly_ bool `yaml:"read-only,omitempty"` 52 } 53 54 // Name implements User. 55 func (u *user) Name() names.UserTag { 56 return names.NewUserTag(u.Name_) 57 } 58 59 // DisplayName implements User. 60 func (u *user) DisplayName() string { 61 return u.DisplayName_ 62 } 63 64 // CreatedBy implements User. 65 func (u *user) CreatedBy() names.UserTag { 66 return names.NewUserTag(u.CreatedBy_) 67 } 68 69 // DateCreated implements User. 70 func (u *user) DateCreated() time.Time { 71 return u.DateCreated_ 72 } 73 74 // LastConnection implements User. 75 func (u *user) LastConnection() time.Time { 76 var zero time.Time 77 if u.LastConnection_ == nil { 78 return zero 79 } 80 return *u.LastConnection_ 81 } 82 83 // ReadOnly implements User. 84 func (u *user) ReadOnly() bool { 85 return u.ReadOnly_ 86 } 87 88 func importUsers(source map[string]interface{}) ([]*user, error) { 89 checker := versionedChecker("users") 90 coerced, err := checker.Coerce(source, nil) 91 if err != nil { 92 return nil, errors.Annotatef(err, "users version schema check failed") 93 } 94 valid := coerced.(map[string]interface{}) 95 96 version := int(valid["version"].(int64)) 97 importFunc, ok := userDeserializationFuncs[version] 98 if !ok { 99 return nil, errors.NotValidf("version %d", version) 100 } 101 sourceList := valid["users"].([]interface{}) 102 return importUserList(sourceList, importFunc) 103 } 104 105 func importUserList(sourceList []interface{}, importFunc userDeserializationFunc) ([]*user, error) { 106 result := make([]*user, 0, len(sourceList)) 107 for i, value := range sourceList { 108 source, ok := value.(map[string]interface{}) 109 if !ok { 110 return nil, errors.Errorf("unexpected value for user %d, %T", i, value) 111 } 112 user, err := importFunc(source) 113 if err != nil { 114 return nil, errors.Annotatef(err, "user %d", i) 115 } 116 result = append(result, user) 117 } 118 return result, nil 119 } 120 121 type userDeserializationFunc func(map[string]interface{}) (*user, error) 122 123 var userDeserializationFuncs = map[int]userDeserializationFunc{ 124 1: importUserV1, 125 } 126 127 func importUserV1(source map[string]interface{}) (*user, error) { 128 fields := schema.Fields{ 129 "name": schema.String(), 130 "display-name": schema.String(), 131 "created-by": schema.String(), 132 "read-only": schema.Bool(), 133 "date-created": schema.Time(), 134 "last-connection": schema.Time(), 135 } 136 137 // Some values don't have to be there. 138 defaults := schema.Defaults{ 139 "display-name": "", 140 "last-connection": time.Time{}, 141 "read-only": false, 142 } 143 checker := schema.FieldMap(fields, defaults) 144 coerced, err := checker.Coerce(source, nil) 145 if err != nil { 146 return nil, errors.Annotatef(err, "user v1 schema check failed") 147 } 148 valid := coerced.(map[string]interface{}) 149 // From here we know that the map returned from the schema coercion 150 // contains fields of the right type. 151 152 result := &user{ 153 Name_: valid["name"].(string), 154 DisplayName_: valid["display-name"].(string), 155 CreatedBy_: valid["created-by"].(string), 156 DateCreated_: valid["date-created"].(time.Time), 157 ReadOnly_: valid["read-only"].(bool), 158 } 159 160 lastConn := valid["last-connection"].(time.Time) 161 if !lastConn.IsZero() { 162 result.LastConnection_ = &lastConn 163 } 164 165 return result, nil 166 167 }