func (h UpdatePreferencesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request, context stack.Context) { database := context.Get("database").(DatabaseInterface) connection := database.Connection() token := context.Get("token").(*jwt.Token) if _, ok := token.Claims["user_id"]; !ok { h.errorWriter.Write(w, webutil.MissingUserTokenError("Missing user_id from token claims.")) return } userID := token.Claims["user_id"].(string) builder := services.NewPreferencesBuilder() validator := valiant.NewValidator(req.Body) err := validator.Validate(&builder) if err != nil { h.errorWriter.Write(w, webutil.ValidationError([]string{err.Error()})) return } preferences, err := builder.ToPreferences() if err != nil { h.errorWriter.Write(w, webutil.ValidationError([]string{err.Error()})) return } transaction := connection.Transaction() transaction.Begin() err = h.preferences.Update(transaction, preferences, builder.GlobalUnsubscribe, userID) if err != nil { transaction.Rollback() switch err.(type) { case services.MissingKindOrClientError, services.CriticalKindError: h.errorWriter.Write(w, webutil.ValidationError([]string{err.Error()})) default: h.errorWriter.Write(w, err) } return } err = transaction.Commit() if err != nil { h.errorWriter.Write(w, models.NewTransactionCommitError(err.Error())) return } w.WriteHeader(http.StatusNoContent) }
func NewTemplateParams(body io.ReadCloser) (TemplateParams, error) { defer body.Close() var template TemplateParams validator := valiant.NewValidator(body) err := validator.Validate(&template) if err != nil { switch err.(type) { case valiant.RequiredFieldError: return template, webutil.ValidationError([]string{err.Error()}) default: return template, webutil.ParseError{} } } if template.Metadata == nil { template.Metadata = json.RawMessage("{}") } err = template.validateSyntax() if err != nil { return TemplateParams{}, err } template.setDefaults() return template, nil }
func (h UpdateUserPreferencesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request, context stack.Context) { database := context.Get("database").(DatabaseInterface) connection := database.Connection() userGUID := regexp.MustCompile(".*/user_preferences/(.*)").FindStringSubmatch(req.URL.Path)[1] builder := services.NewPreferencesBuilder() validator := valiant.NewValidator(req.Body) err := validator.Validate(&builder) if err != nil { h.errorWriter.Write(w, webutil.ValidationError([]string{err.Error()})) return } preferences, err := builder.ToPreferences() if err != nil { h.errorWriter.Write(w, err) return } transaction := connection.Transaction() transaction.Begin() err = h.preferences.Update(transaction, preferences, builder.GlobalUnsubscribe, userGUID) if err != nil { transaction.Rollback() switch err.(type) { case services.MissingKindOrClientError, services.CriticalKindError: h.errorWriter.Write(w, webutil.ValidationError([]string{err.Error()})) default: h.errorWriter.Write(w, err) } return } err = transaction.Commit() if err != nil { h.errorWriter.Write(w, models.NewTransactionCommitError(err.Error())) return } w.WriteHeader(http.StatusNoContent) }
func NewNotificationParams(body io.Reader) (NotificationUpdateParams, error) { var params NotificationUpdateParams validator := valiant.NewValidator(body) err := validator.Validate(¶ms) if err != nil { switch err.(type) { case valiant.RequiredFieldError: return params, webutil.ValidationError([]string{err.Error()}) default: return params, webutil.ParseError{} } } return params, nil }
func (t TemplateParams) validateSyntax() error { toValidate := map[string]string{ "Subject": t.Subject, "Text": t.Text, "HTML": t.HTML, } for field, contents := range toValidate { _, err := template.New("test").Parse(contents) if err != nil { return webutil.ValidationError([]string{field + " syntax is malformed please check your braces"}) } } return nil }
Metadata: `{"hello": true}`, })) }) Context("when the request is not valid", func() { It("indicates that fields are missing", func() { body := `{ "name": "Defaultish Template", "subject": "{{.Subject}}", "metadata": {} }` request, err := http.NewRequest("PUT", "/default_template", strings.NewReader(body)) Expect(err).NotTo(HaveOccurred()) handler.ServeHTTP(writer, request, context) Expect(errorWriter.WriteCall.Receives.Error).To(BeAssignableToTypeOf(webutil.ValidationError([]string{}))) }) }) Context("when the updater errors", func() { It("delegates the error handling to the error writer", func() { updater.UpdateCall.Returns.Error = errors.New("updating default template error") handler.ServeHTTP(writer, request, context) Expect(errorWriter.WriteCall.Receives.Error).To(MatchError(errors.New("updating default template error"))) }) }) })
context.Set("token", token) handler.ServeHTTP(writer, request, context) Expect(errorWriter.WriteCall.Receives.Error).To(BeAssignableToTypeOf(webutil.MissingUserTokenError(""))) Expect(transaction.BeginCall.WasCalled).To(BeFalse()) Expect(transaction.CommitCall.WasCalled).To(BeFalse()) Expect(transaction.RollbackCall.WasCalled).To(BeFalse()) }) }) It("delegates MissingKindOrClientErrors as webutil.ValidationError to the ErrorWriter", func() { updater.ExecuteCall.Returns.Error = services.MissingKindOrClientError("BOOM!") handler.ServeHTTP(writer, request, context) Expect(errorWriter.WriteCall.Receives.Error).To(Equal(webutil.ValidationError([]string{"BOOM!"}))) Expect(transaction.BeginCall.WasCalled).To(BeTrue()) Expect(transaction.CommitCall.WasCalled).To(BeFalse()) Expect(transaction.RollbackCall.WasCalled).To(BeTrue()) }) It("delegates CriticalKindErrors as webutil.ValidationError to the ErrorWriter", func() { updater.ExecuteCall.Returns.Error = services.CriticalKindError("BOOM!") handler.ServeHTTP(writer, request, context) Expect(errorWriter.WriteCall.Receives.Error).To(Equal(webutil.ValidationError([]string{"BOOM!"}))) Expect(transaction.BeginCall.WasCalled).To(BeTrue()) Expect(transaction.CommitCall.WasCalled).To(BeFalse())
Expect(parameters.Subject).To(Equal("{{.Subject}}")) Expect(parameters.Metadata).To(Equal(json.RawMessage("{}"))) }) Context("when the template has invalid syntax", func() { Context("when subject template has invalid syntax", func() { It("returns a validation error", func() { body := buildTemplateRequestBody(templates.TemplateParams{ Name: "Template name", Text: "Textual template", HTML: "HTML template", Subject: "{{.bad}", }) _, err := templates.NewTemplateParams(ioutil.NopCloser(body)) Expect(err).To(HaveOccurred()) Expect(err).To(BeAssignableToTypeOf(webutil.ValidationError([]string{}))) }) }) Context("when text template has invalid syntax", func() { It("returns a validation error", func() { body := buildTemplateRequestBody(templates.TemplateParams{ Name: "Template name", Text: "You should feel {{.BAD}", HTML: "<h1> Amazing </h1>", Subject: "Great Subject", }) _, err := templates.NewTemplateParams(ioutil.NopCloser(body)) Expect(err).To(HaveOccurred()) Expect(err).To(BeAssignableToTypeOf(webutil.ValidationError([]string{}))) })
func (h Notify) Execute(connection ConnectionInterface, req *http.Request, context stack.Context, guid string, strategy Dispatcher, validator ValidatorInterface, vcapRequestID string) ([]byte, error) { parameters, err := NewNotifyParams(req.Body) if err != nil { return []byte{}, err } if !validator.Validate(¶meters) { return []byte{}, webutil.ValidationError(parameters.Errors) } requestReceivedTime, ok := context.Get(RequestReceivedTime).(time.Time) if !ok { panic("programmer error: missing RequestReceivedTime in http context") } token := context.Get("token").(*jwt.Token) // TODO: (rm) get rid of the context object, just pass in the token clientID := token.Claims["client_id"].(string) tokenIssuerURL, err := url.Parse(token.Claims["iss"].(string)) if err != nil { return []byte{}, errors.New("Token issuer URL invalid") } uaaHost := tokenIssuerURL.Scheme + "://" + tokenIssuerURL.Host client, kind, err := h.finder.ClientAndKind(context.Get("database").(DatabaseInterface), clientID, parameters.KindID) if err != nil { return []byte{}, err } if kind.Critical && !h.hasCriticalNotificationsWriteScope(token.Claims["scope"]) { return []byte{}, postal.NewCriticalNotificationError(kind.ID) } err = h.registrar.Register(connection, client, []models.Kind{kind}) if err != nil { return []byte{}, err } var responses []services.Response responses, err = strategy.Dispatch(services.Dispatch{ GUID: guid, Connection: connection, Role: parameters.Role, Client: services.DispatchClient{ ID: clientID, Description: client.Description, }, Kind: services.DispatchKind{ ID: parameters.KindID, Description: kind.Description, }, UAAHost: uaaHost, VCAPRequest: services.DispatchVCAPRequest{ ID: vcapRequestID, ReceiptTime: requestReceivedTime, }, Message: services.DispatchMessage{ To: parameters.To, ReplyTo: parameters.ReplyTo, Subject: parameters.Subject, Text: parameters.Text, HTML: services.HTML{ BodyContent: parameters.ParsedHTML.BodyContent, BodyAttributes: parameters.ParsedHTML.BodyAttributes, Head: parameters.ParsedHTML.Head, Doctype: parameters.ParsedHTML.Doctype, }, }, }) if err != nil { return []byte{}, err } output, err := json.Marshal(responses) if err != nil { panic(err) } return output, nil }
It("returns a 400 when the request cannot be parsed due to syntatically invalid JSON", func() { writer.Write(recorder, webutil.ParseError{}) Expect(recorder.Code).To(Equal(400)) body := make(map[string]interface{}) err := json.Unmarshal(recorder.Body.Bytes(), &body) if err != nil { panic(err) } Expect(body["errors"]).To(ContainElement("Request body could not be parsed")) }) It("returns a 422 when the requests are not valid due to semantically invalid JSON", func() { writer.Write(recorder, webutil.ValidationError([]string{"something", "another"})) Expect(recorder.Code).To(Equal(422)) body := make(map[string]interface{}) err := json.Unmarshal(recorder.Body.Bytes(), &body) if err != nil { panic(err) } Expect(body["errors"]).To(ContainElement("something")) Expect(body["errors"]).To(ContainElement("another")) }) It("returns a 422 when trying to send a critical notification without correct scope", func() { writer.Write(recorder, postal.NewCriticalNotificationError("raptors"))