auth

Paddy 2015-07-15 Parent:8ecb60d29b0d

178:0a2c3d677161 Go to Latest

auth/request_test.go

Update to use a generic event emitter. Rather can creating a purpose-built event emitter for each and every event we need to emit (I'm looking at you, login verification event) which is _downright silly_, we're now using a generic event publisher that's based on saying "HEY A MODEL UPDATED". This means we need to change all our setup code in authd to use events.NewNSQPublisher or events.NewStdoutPublisher instead of our homegrown solutions. Which also means updating our config to take an events.Publisher instead of our LoginVerificationNotifier (blergh). Our Context also now uses an events.Publisher instead of a LoginVerificationNotifier. Party all around! We also replaced our SendLoginVerification helper method on Context with a SendModelEvent helper method on Context, which is just a light wrapper around events.PublishModelEvent. Of course, all this means we need to update our email_verification listener to listen to the correct channel (based on the model we want updates about) and filter down to a Created action or our new custom action for "the customer wants their verification resent", which I'm OK making a special case and not generic, because c'mon. But we had a subtle change to all our constants, some of which are unofficial constants now. I'm unsure how I feel about this. We also updated our email_verification listener so that we're unmarshalling to a custom loginEvent, which is just an events.Event that overwrites the Data property to be an auth.Login instance. This is to make sure we don't need to wrangle a map[string]interface{}, which is no fun. I'm also OK with special-casing like this, because it's 1) a tiny amount of code, 2) properly utilising composition, and 3) the only way I can think of to cleanly accomplish what I want. I also added a note about GetLogin's deficient handling of logins, namely that it doesn't recognise admins and return Verification codes to them, which would be a useful property for internal tools to take advantage of. Ah well. I updated the Profile and Login implementations so they're now event.Model instances, mainly by just exporting some strings from them through getters that will let us automatically build an Event from them. This lets us use the PublishModelEvent helper. I updated our CreateProfileHandler to properly mangle the login Verification property, and to fire off the ActionCreated events for the new Login and the new Profile. I updated our GetLoginHandler and UpdateLoginHandler to properly mangle the loginVerification property. God that's annoying. :-/ You'll note I didn't start publishing the events.ActionUpdated or events.ActionDeleted events for Profiles or Logins yet, and didn't bother publishing any events for literally any other type. That's because I'm a lazy piece of crap and will end up publishing them when I absolutely have to. Part of that is because if a channel isn't created/being read for a topic, the messages will just stack up in NSQ, and I don't want that. But mostly I'm lazy. Finally, I got to delete the entire profile_verification.go file, because we're no longer special-casing that. Hooray!

History
paddy@116 1 package auth
paddy@116 2
paddy@116 3 import "fmt"
paddy@116 4
paddy@172 5 func compareErrors(err1, err2 RequestError) (success bool, field string, val1, val2 interface{}) {
paddy@116 6 if err1.Slug != err2.Slug {
paddy@116 7 return false, "Slug", err1.Slug, err2.Slug
paddy@116 8 }
paddy@116 9 if err1.Field != err2.Field {
paddy@116 10 return false, "Field", err1.Field, err2.Field
paddy@116 11 }
paddy@116 12 if err1.Param != err2.Param {
paddy@116 13 return false, "Param", err1.Param, err2.Param
paddy@116 14 }
paddy@116 15 if err1.Header != err2.Header {
paddy@116 16 return false, "Header", err1.Header, err2.Header
paddy@116 17 }
paddy@116 18 return true, "", nil, nil
paddy@116 19 }
paddy@116 20
paddy@172 21 func compareResponses(resp1, resp2 Response) (success bool, field string, val1, val2 interface{}) {
paddy@116 22 if len(resp1.Errors) != len(resp2.Errors) {
paddy@116 23 return false, "Errors", resp1.Errors, resp2.Errors
paddy@116 24 }
paddy@116 25 if len(resp1.Logins) != len(resp2.Logins) {
paddy@116 26 return false, "Logins", resp1.Logins, resp2.Logins
paddy@116 27 }
paddy@116 28 if len(resp1.Profiles) != len(resp2.Profiles) {
paddy@116 29 return false, "Profiles", resp1.Profiles, resp2.Profiles
paddy@116 30 }
paddy@116 31 if len(resp1.Clients) != len(resp2.Clients) {
paddy@116 32 return false, "Clients", resp1.Clients, resp2.Clients
paddy@116 33 }
paddy@116 34 if len(resp1.Endpoints) != len(resp2.Endpoints) {
paddy@116 35 return false, "Endpoints", resp1.Endpoints, resp2.Endpoints
paddy@116 36 }
paddy@116 37 for pos := range resp1.Errors {
paddy@116 38 success, field, val1, val2 = compareErrors(resp1.Errors[pos], resp2.Errors[pos])
paddy@116 39 if !success {
paddy@116 40 field = fmt.Sprintf("Error %d %s", pos, field)
paddy@116 41 return
paddy@116 42 }
paddy@116 43 }
paddy@116 44 for pos := range resp1.Logins {
paddy@116 45 success, field, val1, val2 = compareLogins(resp1.Logins[pos], resp2.Logins[pos])
paddy@116 46 if !success {
paddy@116 47 field = fmt.Sprintf("Login %d %s", pos, field)
paddy@116 48 return
paddy@116 49 }
paddy@116 50 }
paddy@116 51 for pos := range resp1.Profiles {
paddy@116 52 success, field, val1, val2 = compareProfiles(resp1.Profiles[pos], resp2.Profiles[pos])
paddy@116 53 if !success {
paddy@116 54 field = fmt.Sprintf("Profile %d %s", pos, field)
paddy@116 55 return
paddy@116 56 }
paddy@116 57 }
paddy@116 58 for pos := range resp1.Clients {
paddy@116 59 success, field, val1, val2 = compareClients(resp1.Clients[pos], resp2.Clients[pos])
paddy@116 60 if !success {
paddy@116 61 field = fmt.Sprintf("Client %d %s", pos, field)
paddy@116 62 return
paddy@116 63 }
paddy@116 64 }
paddy@116 65 for pos := range resp1.Endpoints {
paddy@116 66 success, field, val1, val2 = compareEndpoints(resp1.Endpoints[pos], resp2.Endpoints[pos])
paddy@116 67 if !success {
paddy@116 68 field = fmt.Sprintf("Endpoint %d %s", pos, field)
paddy@116 69 return
paddy@116 70 }
paddy@116 71 }
paddy@116 72 return true, "", nil, nil
paddy@116 73 }
paddy@116 74
paddy@172 75 func fillInServerGenerated(expectation, result Response) {
paddy@116 76 if len(expectation.Profiles) > 0 {
paddy@116 77 for pos, profile := range expectation.Profiles {
paddy@116 78 profile.ID = result.Profiles[pos].ID
paddy@116 79 profile.Created = result.Profiles[pos].Created
paddy@116 80 profile.LastSeen = result.Profiles[pos].LastSeen
paddy@116 81 expectation.Profiles[pos] = profile
paddy@116 82 }
paddy@116 83 }
paddy@116 84 if len(expectation.Logins) > 0 {
paddy@116 85 for pos, login := range expectation.Logins {
paddy@116 86 login.ProfileID = result.Logins[pos].ProfileID
paddy@116 87 login.Created = result.Logins[pos].Created
paddy@116 88 login.LastUsed = result.Logins[pos].LastUsed
paddy@116 89 expectation.Logins[pos] = login
paddy@116 90 }
paddy@116 91 }
paddy@116 92 if len(expectation.Clients) > 0 {
paddy@116 93 for pos, client := range expectation.Clients {
paddy@116 94 client.ID = result.Clients[pos].ID
paddy@116 95 client.Secret = result.Clients[pos].Secret
paddy@116 96 client.OwnerID = result.Clients[pos].OwnerID
paddy@116 97 expectation.Clients[pos] = client
paddy@116 98 }
paddy@116 99 }
paddy@116 100 if len(expectation.Endpoints) > 0 {
paddy@116 101 for pos, endpoint := range expectation.Endpoints {
paddy@116 102 endpoint.ID = result.Endpoints[pos].ID
paddy@116 103 endpoint.ClientID = result.Endpoints[pos].ClientID
paddy@116 104 endpoint.Added = result.Endpoints[pos].Added
paddy@116 105 expectation.Endpoints[pos] = endpoint
paddy@116 106 }
paddy@116 107 }
paddy@116 108 }