aboutsummaryrefslogblamecommitdiff
path: root/marusia.go
blob: 953c45cfaed126d05b5acb6bdbddc6c8a4500445 (plain) (tree)
















































                                                                                 

                                           






























































                                                                                                          
// 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
}

// 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
}