// Copyright (c) 2020 Alexander Kiryukhin <a.kiryukhin@mail.ru>
// 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
}
// NewMarusia is API constructor
func NewMarusia(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
}