// Copyright (c) 2020 Alexander Kiryukhin // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package marusia import ( "context" "encoding/json" "net/http" "strconv" "strings" ) // Marusia is main API object type Marusia struct { handler MessageHandler ctx context.Context errorLogger func(error) } // SetCtx sets optional parent context func (m *Marusia) SetCtx(ctx context.Context) *Marusia { m.ctx = ctx return m } // SetErrorLogger sets optional error logger func (m *Marusia) SetErrorLogger(errorLogger func(error)) *Marusia { m.errorLogger = errorLogger return m } // New is API constructor func New(handler MessageHandler) *Marusia { return &Marusia{handler: handler, ctx: context.Background()} } // MessageHandler is http.MessageHandler that proceed requests from Marusia and sends responses to Marusia func (m *Marusia) Handler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() reqEnvelope := new(requestEnvelope) if err := json.NewDecoder(r.Body).Decode(reqEnvelope); err != nil { w.WriteHeader(http.StatusBadRequest) if m.errorLogger != nil { m.errorLogger(err) } return } ctx := getContext(m.ctx, reqEnvelope) resp, err := m.handler(ctx, reqEnvelope.Request) if err != nil { if m.errorLogger != nil { m.errorLogger(err) } w.WriteHeader(http.StatusInternalServerError) return } respEnvelope := &responseEnvelope{ Response: resp, Session: reqEnvelope.Session, Version: version, } w.WriteHeader(http.StatusOK) if err := json.NewEncoder(w).Encode(respEnvelope); err != nil { if m.errorLogger != nil { m.errorLogger(err) } w.WriteHeader(http.StatusInternalServerError) } }) } func getContext(parent context.Context, req *requestEnvelope) context.Context { data := map[string]string{ CtxSessionID: req.Session.SessionID, CtxUserID: req.Session.UserID, CtxSkillID: req.Session.SkillID, CtxMessageID: strconv.Itoa(req.Session.MessageID), CtxClientID: req.Meta.ClientID, CtxLocale: req.Meta.Locale, CtxTimezone: req.Meta.Timezone, } var interfaces []string for iface := range req.Meta.Interfaces { interfaces = append(interfaces, iface) } data[CtxInterfaces] = strings.Join(interfaces, ",") if req.Session.New { data[CtxNew] = "true" } ctx := parent for k, v := range data { ctx = context.WithValue(ctx, k, v) } return ctx }