create ticket

This commit is contained in:
Skeris 2021-04-11 12:48:15 +03:00
parent 6dfcd7bbed
commit 7ab03b279c
11 changed files with 703 additions and 22 deletions

@ -1,14 +1,16 @@
package app
import (
"bitbucket.org/BlackBroker/heruvym/dal"
"bitbucket.org/BlackBroker/heruvym/middleware"
"bitbucket.org/BlackBroker/heruvym/service"
"bitbucket.org/BlackBroker/heruvym/version"
"github.com/skeris/authService/router"
"context"
"errors"
"fmt"
"github.com/BlackBroker/trashlog/wrappers/zaptg"
"github.com/skeris/appInit"
"github.com/skeris/authService/http_middleware"
"github.com/skeris/authService/router"
"go.uber.org/zap/zapcore"
"net/http"
"os"
@ -112,13 +114,25 @@ func New(ctx context.Context, opts interface{}) (appInit.CommonApp, error) {
logger := hlog.New(zapLogger)
logger.Emit(InfoSvcStarted{})
apiMux := router.NewRouter(http.DefaultServeMux)
apiMux := router.NewRouter(http.NewServeMux())
database, err := dal.New(
ctx,
options.MongoURI,
"support",
logger,
)
if err != nil {
return nil, err
}
heruvym := service.New(database, logger)
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
server := &http.Server{
Handler: http_middleware.Wrap(apiMux, zapLogger),
Handler: http_middleware.Wrap(heruvym.Register(apiMux), zapLogger),
Addr: fmt.Sprintf(":%s", options.NumberPortLocal),
}

@ -11,12 +11,15 @@ import (
"time"
)
const collMessages = "messages"
const (
collMessages = "messages"
collTickets = "tickets"
)
type DAL struct {
logger hlog.Logger
col *mongo.Collection
client *mongo.Client
logger hlog.Logger
colMsg, colTck *mongo.Collection
client *mongo.Client
}
type ErrorConnectToDB struct {
@ -31,7 +34,7 @@ type ErrorPingDB struct {
type InfoPing struct {
MongoURI string
Nanoseconds int
Nanoseconds int64
}
func New(
@ -48,7 +51,7 @@ func New(
return nil, err
}
before := time.Now().Nanosecond()
before := time.Now().Unix()
if err := client.Ping(ctx, readpref.PrimaryPreferred()); err != nil {
log.Emit(ErrorPingDB{
@ -61,32 +64,33 @@ func New(
log.Emit(InfoPing{
MongoURI: mongoURI,
Nanoseconds: time.Now().Nanosecond() - before,
Nanoseconds: time.Now().Unix() - before,
})
return &DAL{
client: client,
col: client.Database(database).Collection(collMessages),
colMsg: client.Database(database).Collection(collMessages),
colTck: client.Database(database).Collection(collTickets),
logger: log.Module("DAL"),
}, nil
}
type ErrorInsert struct {
Err error
Err error
UserID, SessionID string
}
func (d *DAL) PutMessage(
ctx context.Context,
title, message, userID, sessionID string,
message, userID, sessionID, ticketID string,
files []string,
) error {
) error {
if _, err := d.col.InsertOne(ctx, &model.Message{
if _, err := d.colMsg.InsertOne(ctx, &model.Message{
ID: xid.New().String(),
UserID: userID,
SessionID: sessionID,
Title: title,
TicketID: ticketID,
Message: message,
Files: files,
CreatedAt: time.Now(),
@ -102,3 +106,31 @@ func (d *DAL) PutMessage(
return nil
}
func (d *DAL) CreateTicket(
ctx context.Context,
userID,
sessionID,
title string,
) (string, error) {
tiketID := xid.New().String()
if _, err := d.colTck.InsertOne(ctx, &model.Ticket{
ID: tiketID,
UserID: userID,
SessionID: sessionID,
Title: title,
CreatedAt: time.Time{},
}); err != nil {
d.logger.Emit(ErrorInsert{
Err: err,
UserID: userID,
SessionID: sessionID,
})
return "", err
}
return tiketID, nil
}

5
go.mod

@ -3,12 +3,15 @@ module bitbucket.org/BlackBroker/heruvym
go 1.16
require (
bitbucket.org/skeris/profile v0.0.0-20210410182208-cf9db6d1452d
bitbucket.org/skeris/profile v0.0.0-20210411000338-496c859828a5 // indirect
github.com/BlackBroker/trashlog v0.0.0-20210406151703-e2c4874359bf
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/gorilla/mux v1.8.0
github.com/rs/xid v1.3.0
github.com/skeris/appInit v0.1.0
github.com/skeris/authService v1.1.1 // indirect
github.com/skeris/identity v0.0.7 // indirect
github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33 // indirect
github.com/themakers/hlog v0.0.0-20191205140925-235e0e4baddf
go.mongodb.org/mongo-driver v1.5.1
go.uber.org/zap v1.16.0

16
go.sum

@ -1,5 +1,9 @@
bitbucket.org/skeris/profile v0.0.0-20210410182208-cf9db6d1452d h1:WAp3CsNISnHvvbn60wYK2AbCYb/UxGk+6EkiTQFX7mw=
bitbucket.org/skeris/profile v0.0.0-20210410182208-cf9db6d1452d/go.mod h1:nFz19Zl7b9CNqFXWDdvJKrFS7S/th4dX3GnP5F0Yu5M=
bitbucket.org/skeris/profile v0.0.0-20210410222825-ad4fbe380f68 h1:OquUpSs9fjMRMzRErW3a2o3FF9QbKjBzvveT4MPnsY0=
bitbucket.org/skeris/profile v0.0.0-20210410222825-ad4fbe380f68/go.mod h1:nFz19Zl7b9CNqFXWDdvJKrFS7S/th4dX3GnP5F0Yu5M=
bitbucket.org/skeris/profile v0.0.0-20210411000338-496c859828a5 h1:nAtrodBJM8auGMZB4IrLFmdM/VpDsXjycC+e53SFNJ0=
bitbucket.org/skeris/profile v0.0.0-20210411000338-496c859828a5/go.mod h1:nFz19Zl7b9CNqFXWDdvJKrFS7S/th4dX3GnP5F0Yu5M=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@ -62,6 +66,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@ -187,6 +193,10 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mailjet/mailjet-apiv3-go v0.0.0-20201009050126-c24bc15a9394/go.mod h1:ogN8Sxy3n5VKLhQxbtSBM3ICG/VgjXS/akQJIoDSrgA=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
github.com/minio/minio-go/v7 v7.0.10/go.mod h1:td4gW1ldOsj1PbSNS+WYK43j+P1XVhX/8W8awaYlBFo=
@ -248,8 +258,11 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33 h1:N9f/Q+2Ssa+yDcbfaoLTYvXmdeyUUxsJKdPUVsjSmiA=
github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33/go.mod h1:rpcH99JknBh8seZmlOlUg51gasZH6QH34oXNsIwYT6E=
github.com/themakers/hlog v0.0.0-20191205140925-235e0e4baddf h1:TJJm6KcBssmbWzplF5lzixXl1RBAi/ViPs1GaSOkhwo=
github.com/themakers/hlog v0.0.0-20191205140925-235e0e4baddf/go.mod h1:1FsorU3vnXO9xS9SrhUp8fRb/6H/Zfll0rPt1i4GWaA=
github.com/themakers/identity v0.0.0-20200703212242-9142bb6b35e1 h1:DuzLgIqC0AxW3Gy+Mhwn15R9KvFe8nifO6Pbe3j07Ng=
github.com/themakers/identity v0.0.0-20200703212242-9142bb6b35e1/go.mod h1:lHYuLs7VL+KVZpEzfXnrv8YwlGXcuZgUZjV5pSRb+Fc=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
@ -398,6 +411,7 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -414,6 +428,8 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b h1:ggRgirZABFolTmi3sn6Ivd9SipZwLedQ5wR0aAKnFxU=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

119
jwt_adapter/jwt_adapter.go Normal file

@ -0,0 +1,119 @@
package jwt_adapter
import (
"context"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/rs/xid"
"github.com/skeris/identity/cookie"
"os"
"time"
)
var _ cookie.Cookie = new(JwtAdapter)
func init() {
aS := os.Getenv("JWT_SECRET")
if len(aS) != 0 {
accessSecret = aS
}
}
type JwtAdapter struct {
ID string
Session string
User string
Tariff uint8
Created int64
LastSeen int64
jwt.StandardClaims
}
func (c *JwtAdapter) Init() {
if c.ID == "" {
c.ID = xid.New().String()
}
c.Session = xid.New().String()
c.User = ""
c.Tariff = uint8(0)
t := Timestamp()
c.Created = t
c.LastSeen = t
}
func Get(ctx context.Context) *JwtAdapter {
if adapter, ok := ctx.Value(DefaultHeaderKey).(*JwtAdapter); ok {
return adapter
}
return nil
}
func (c *JwtAdapter) SetUserID(id string) {
c.User = id
}
func (c *JwtAdapter) GetUserID() string {
return c.User
}
func (c *JwtAdapter) GetTariff() uint8 {
return c.Tariff
}
func (c *JwtAdapter) SetTariff(status uint8) {
c.Tariff = status
}
func (c *JwtAdapter) GetSessionID() string {
return c.Session
}
func (c *JwtAdapter) SetSessionID(id string) {
c.Session = id
c.User = ""
}
const (
DefaultAccessSecret = "awesomeAC"
DefaultHeaderKey = "Authorization"
)
var accessSecret = DefaultAccessSecret
func (c *JwtAdapter) Encode() (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
ss, err := token.SignedString([]byte(accessSecret))
return ss, err
}
func Decode(tokenString string) (*JwtAdapter, error) {
claims := JwtAdapter{}
token, err := jwt.ParseWithClaims(tokenString, &claims, func(token *jwt.Token) (interface{}, error) {
//Make sure that the token method conform to "SigningMethodHMAC"
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(accessSecret), nil
})
if err != nil {
return nil, err
}
if _, ok := token.Claims.(jwt.Claims); !ok && !token.Valid {
return nil, fmt.Errorf("ErrorNoValidClaims")
}
return &claims, nil
}
func Timestamp() int64 {
return time.Now().UnixNano() / int64(time.Millisecond)
}

@ -0,0 +1,66 @@
package hijack
import (
"bufio"
"net"
"net/http"
"sync"
)
func New(w http.ResponseWriter, commit func(http.ResponseWriter)) (http.ResponseWriter, func()) {
responseWriter := &responseWriter{
commit: commit,
w: w,
}
if _, ok := w.(http.Hijacker); ok {
return &responseWriterHijacker{
responseWriter: responseWriter,
}, responseWriter.ensureCommitted
} else {
return responseWriter, responseWriter.ensureCommitted
}
}
var _ http.ResponseWriter = new(responseWriter)
var _ http.ResponseWriter = new(responseWriterHijacker)
var _ http.Hijacker = new(responseWriterHijacker)
type responseWriter struct {
commit func(http.ResponseWriter)
w http.ResponseWriter
hijacked bool
writeOnce sync.Once
}
func (rw *responseWriter) ensureCommitted() {
rw.writeOnce.Do(func() {
if rw.hijacked {
return
}
rw.commit(rw.w)
})
}
func (rw *responseWriter) Header() http.Header {
return rw.w.Header()
}
func (rw *responseWriter) Write(data []byte) (int, error) {
rw.ensureCommitted()
return rw.w.Write(data)
}
func (rw *responseWriter) WriteHeader(status int) {
rw.ensureCommitted()
rw.w.WriteHeader(status)
}
type responseWriterHijacker struct {
*responseWriter
}
func (rw *responseWriterHijacker) Hijack() (net.Conn, *bufio.ReadWriter, error) {
rw.hijacked = true
return rw.w.(http.Hijacker).Hijack()
}

@ -0,0 +1,234 @@
package http_middleware
import (
"bitbucket.org/BlackBroker/heruvym/jwt_adapter"
"bitbucket.org/BlackBroker/heruvym/middleware/hijack"
"context"
"fmt"
"github.com/skeris/authService/errors"
"net/http"
"runtime/debug"
"strings"
"go.uber.org/zap"
)
type MiddlewareFunc func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
type AfterFunc func(ctx context.Context, r *http.Request) (context.Context, error)
type SetCookieValueFunc func(w http.ResponseWriter, value string)
func DefaultChain(
log *zap.Logger,
recFn RecoverFunc,
afn AfterFunc,
setCookieValue SetCookieValueFunc,
mws ...MiddlewareFunc,
) http.HandlerFunc {
return Chain(
append(
[]MiddlewareFunc{
MiddlewareRecovery(log, recFn),
MiddlewareLogger(log),
DefaultCookieAndRecoveryMiddleware(
log,
recFn, afn, setCookieValue,
),
},
mws...,
)...,
)
}
func DefaultCookieAndRecoveryMiddleware(
log *zap.Logger,
recFn RecoverFunc,
afn AfterFunc,
setCookieValue SetCookieValueFunc,
) MiddlewareFunc {
const headerKey = jwt_adapter.DefaultHeaderKey
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
var err error
cookie := &jwt_adapter.JwtAdapter{}
tokenHeader := r.Header.Get(headerKey)
if tokenHeader == "" {
fmt.Println("ERROR NO authHEader")
cookie.Init()
} else {
splitted := strings.Split(tokenHeader, " ")
if len(splitted) != 2 {
w.WriteHeader(http.StatusForbidden)
w.Header().Add("Content-Type", "application/json")
return
}
tokenPart := splitted[1]
cookie, err = jwt_adapter.Decode(tokenPart)
if err != nil {
cookie.Init()
}
}
recovery := struct {
val interface{}
trace string
}{}
ctx := context.WithValue(r.Context(), headerKey, cookie)
if afn != nil {
c, err := afn(ctx, r)
if err != nil {
panic(err)
}
ctx = c
}
w, commit := hijack.New(w, func(w http.ResponseWriter) {
cookie.LastSeen = jwt_adapter.Timestamp()
if val, err := cookie.Encode(); err != nil {
panic(err)
} else {
setCookieValue(w, val)
}
if recovery.val != nil {
log.Error("handler recovered", zap.Any("recovered", recovery.val))
code, message := recFn(recovery.val, ctx)
w.WriteHeader(code)
if _, err := fmt.Fprint(w, message); err != nil {
log.Error("error writing panic response", zap.Error(err))
}
}
})
defer func() {
if rec := recover(); rec != nil {
recovery.val = rec
recovery.trace = string(debug.Stack())
}
commit()
}()
next(w, r.WithContext(ctx))
}
}
func Chain(mws ...MiddlewareFunc) http.HandlerFunc {
if len(mws) == 0 {
return func(w http.ResponseWriter, r *http.Request) {}
}
h := link(mws[len(mws)-1], nil)
for i := len(mws) - 2; i >= 0; i-- {
mw := mws[i]
h = link(mw, h)
}
return h
}
func link(mw MiddlewareFunc, next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
mw(w, r, next)
}
}
type RecoverFunc func(rec interface{}, ctx context.Context) (code int, message string)
func MiddlewareRecovery(log *zap.Logger, recFn RecoverFunc) MiddlewareFunc {
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
defer func() {
if rec := recover(); rec != nil {
code, message := recFn(rec, r.Context())
w.WriteHeader(code)
if _, err := fmt.Fprint(w, message); err != nil {
log.Error("error writing panic response", zap.Error(err))
}
log.Error("panic in http handler", zap.Int("code", code), zap.String("message", message), zap.Any("recovered", rec))
}
}()
next(w, r)
}
}
func MiddlewareLogger(log *zap.Logger) MiddlewareFunc {
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
log.Debug("http request", zap.String("url", r.URL.String()))
next(w, r)
}
}
func Handler(h http.HandlerFunc) MiddlewareFunc {
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
h.ServeHTTP(w, r)
if next != nil {
next(w, r)
}
}
}
func Wrap(mux *http.ServeMux, logger *zap.Logger) http.HandlerFunc {
return DefaultChain(
logger,
func(rec interface{}, ctx context.Context) (int, string) {
var (
code int
message string
)
if err, ok := rec.(error); ok {
if v, ok := errors.IsForbidden(err); ok {
code = http.StatusForbidden
message = v.Error()
} else if v, ok := errors.IsUnauthenticated(err); ok {
code = http.StatusUnauthorized
message = v.Error()
} else {
code = http.StatusInternalServerError
message = err.Error()
}
} else {
code = http.StatusInternalServerError
message = fmt.Sprintf("%v", rec)
}
return code, message
},
nil,
func(w http.ResponseWriter, val string) {
var setFn func(key, value string)
if w.Header().Get(jwt_adapter.DefaultHeaderKey) == "" {
setFn = w.Header().Add
} else {
setFn = w.Header().Set
}
setFn(jwt_adapter.DefaultHeaderKey, fmt.Sprintf("Bearer %s", val))
},
Handler((func() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
//todo specify origins
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Headers", "*")
w.Header().Set("Access-Control-Expose-Headers", "*")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
w.Header().Set("Pragma", "no-cache")
w.Header().Set("Expires", "0")
mux.ServeHTTP(w, r)
}
})()))
}

@ -3,14 +3,23 @@ package model
import "time"
type Message struct {
ID string `bson:"_id"`
TicketID string `bson:"TicketID"`
UserID string `bson:"UserID"`
SessionID string `bson:"SessionID"`
Message string `bson:"Messsage"`
Files []string `bson:"Files"`
CreatedAt time.Time `bson:"CreatedAt"`
}
type Ticket struct {
ID string `bson:"_id"`
UserID string `bson:"UserID"`
SessionID string `bson:"SessionID"`
Title string `bson:"Title"`
Message string `bson:"Messsage"`
Files []string `bson:"Files"`
Title string `bson:"Title"`
CreatedAt time.Time `bson:"CreatedAt"`
}

13
service/errors.go Normal file

@ -0,0 +1,13 @@
package service
type ErrorClose struct {
Err error
}
type ErrorMarshal struct {
Err error
}
type ErrorWrite struct {
Err error
}

111
service/service.go Normal file

@ -0,0 +1,111 @@
package service
import (
"bitbucket.org/BlackBroker/heruvym/dal"
"bitbucket.org/BlackBroker/heruvym/jwt_adapter"
"encoding/json"
"github.com/themakers/hlog"
"net/http"
)
type Heruvym struct {
logger hlog.Logger
dal *dal.DAL
}
func New(dataAccessLayer *dal.DAL, log hlog.Logger) *Heruvym {
return &Heruvym{
logger: log.Module("Service"),
dal: dataAccessLayer,
}
}
func (h Heruvym) Register(m *http.ServeMux) *http.ServeMux {
m.HandleFunc("/create", h.CreateTicket)
return m
}
type CreateTicketReq struct {
Title string `json:"Title"`
Message string `json:"Message"`
}
type CreateTicketResp struct {
Ticket string `json:"Ticket"`
}
func (h *Heruvym) CreateTicket(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := r.Body.Close(); err != nil {
h.logger.Emit(ErrorClose{
Err: err,
})
}
}()
var request CreateTicketReq
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
http.Error(w, "Invalid json", http.StatusBadRequest)
return
}
if request.Title == "" {
http.Error(w, "No Title", http.StatusNoContent)
return
}
if request.Message == "" {
http.Error(w, "No Message", http.StatusNoContent)
return
}
ctx := r.Context()
session := jwt_adapter.Get(ctx)
if session == nil {
http.Error(w, "No session", http.StatusMethodNotAllowed)
return
}
ticketID, err := h.dal.CreateTicket(
ctx,
session.User,
session.ID,
request.Title,
)
if err != nil {
http.Error(w, "CannotCreateTicket", http.StatusInternalServerError)
return
}
if err := h.dal.PutMessage(ctx,
request.Message,
session.User,
session.Session,
ticketID,
[]string{},
); err != nil {
http.Error(w, "CannotCreateMessage", http.StatusInternalServerError)
return
}
response, err := json.Marshal(CreateTicketResp{Ticket: ticketID})
if err != nil {
h.logger.Emit(ErrorMarshal{
Err: err,
})
http.Error(w, "CannotMarshalMessage", http.StatusInternalServerError)
return
}
if _, err := w.Write(response); err != nil {
h.logger.Emit(ErrorMarshal{
Err: err,
})
return
}
}

64
test/main_test.go Normal file

@ -0,0 +1,64 @@
package test
import (
"bitbucket.org/BlackBroker/heruvym/app"
"bitbucket.org/BlackBroker/heruvym/service"
"bitbucket.org/skeris/profile/jwt_adapter"
"bytes"
"context"
"encoding/json"
"github.com/themakers/bdd"
"net/http"
"testing"
"time"
)
func TestTicket(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
bdd.Scenario(t, "SupportChat", func(t *testing.T, runID string) {
go func () {
_, err := app.New(ctx, app.Options{
MongoURI: "mongodb://localhost:27017",
NumberPortLocal: "1488",
LoggerDevMode: false,
})
if err != nil {
panic(err)
}
}()
time.Sleep(time.Second * 2)
bdd.Act(t, "client", func() {
bdd.Test(t, "CreateTicket", func() {
buf, err := json.Marshal(service.CreateTicketReq{
Title: "TestTitle",
Message: "test",
})
if err != nil {
panic(err)
}
req, err :=http.NewRequestWithContext(
ctx,
"POST",
"http://localhost:1488/create", bytes.NewBuffer(buf))
if err != nil {
panic(err)
}
req.Header.Add(jwt_adapter.DefaultHeaderKey, "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6ImMxcDM0YjhqZHBmOG04Zm43cnIwIiwiU2Vzc2lvbiI6ImMxcDM0YjhqZHBmOG04Zm43cnJnIiwiVXNlciI6IiIsIlRhcmlmZiI6MCwiQ3JlYXRlZCI6MTYxODA5NjY4NTc4MCwiTGFzdFNlZW4iOjE2MTgwOTY2ODU3ODF9.ciJoJiOxzIPv0LY4h3rG8Tf3AsSBXXLcYEpyN9mIki0")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
if resp.StatusCode != http.StatusOK {
panic("NotAccepted")
}
})
})
})
}