master
Kar 2024-01-08 14:00:41 +05:30
commit dbe86acfac
31 changed files with 1793 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
Gopkg.lock
Gopkg.toml
vendor

View File

@ -0,0 +1,76 @@
package appcontext
import (
"context"
"github.com/google/uuid"
"strings"
)
type customContextType string
const (
AppCtx customContextType = "appCtx"
)
type AppExtContext struct {
UserEmail string // Email of the user
RequestID string // RequestID - used to track logs across a request-response cycle
Locale string // Locale for language
Application string // application for dynamic application auth
}
type AppContext struct {
context.Context
AppExtContext
}
func GetAppCtx(ctx context.Context) (AppExtContext, bool) {
if ctx == nil {
return AppExtContext{}, false
}
appCtx, exists := ctx.Value(AppCtx).(AppExtContext)
return appCtx, exists
}
func WithAppCtx(ctx context.Context, appCtx AppExtContext) context.Context {
return context.WithValue(ctx, AppCtx, appCtx)
}
func UpgradeCtx(ctx context.Context) AppContext {
var appCtx AppContext
tCtx, _ := GetAppCtx(ctx)
appCtx.Context = ctx
appCtx.AppExtContext = tCtx
return appCtx
}
func NewAppContext() AppContext {
return AppContext{
Context: context.Background(),
AppExtContext: AppExtContext{},
}
}
func CopyAppContext(ctx context.Context) AppContext {
appCtx, _ := GetAppCtx(ctx)
return AppContext{
Context: context.Background(),
AppExtContext: appCtx,
}
}
func New(id ...string) AppContext {
var requestID string
if len(id) > 0 {
requestID = id[0]
}
if len(requestID) == 0 {
requestID = strings.ReplaceAll(uuid.NewString(), "-", "")
}
appCtx := AppExtContext{
RequestID: requestID,
}
ctx := UpgradeCtx(WithAppCtx(context.Background(), appCtx))
return ctx
}

61
core/appserver/brotli.go Normal file
View File

@ -0,0 +1,61 @@
package appserver
import (
"github.com/andybalholm/brotli"
"net/http"
"sync"
)
type brotliResponseWriter struct {
http.ResponseWriter
w *brotli.Writer
statusCode int
headerWritten bool
}
var (
poolbr = sync.Pool{
New: func() interface{} {
w := brotli.NewWriterLevel(nil, brotli.BestSpeed)
return &brotliResponseWriter{
w: w,
}
},
}
)
func (br *brotliResponseWriter) WriteHeader(statusCode int) {
br.statusCode = statusCode
br.headerWritten = true
if br.statusCode != http.StatusNotModified && br.statusCode != http.StatusNoContent {
br.ResponseWriter.Header().Del("Content-Length")
br.ResponseWriter.Header().Set("Content-Encoding", "br")
}
br.ResponseWriter.WriteHeader(statusCode)
}
func (br *brotliResponseWriter) Write(b []byte) (int, error) {
if _, ok := br.Header()["Content-Type"]; !ok {
// If no content type, apply sniffing algorithm to un-gzipped body.
br.ResponseWriter.Header().Set("Content-Type", http.DetectContentType(b))
}
if !br.headerWritten {
// This is exactly what Go would also do if it hasn't been written yet.
br.WriteHeader(http.StatusOK)
}
return br.w.Write(b)
}
func (br *brotliResponseWriter) Flush() {
if br.w != nil {
br.w.Flush()
}
if fw, ok := br.ResponseWriter.(http.Flusher); ok {
fw.Flush()
}
}

62
core/appserver/gzip.go Normal file
View File

@ -0,0 +1,62 @@
package appserver
import (
"compress/gzip"
"net/http"
"sync"
)
type gzipResponseWriter struct {
http.ResponseWriter
w *gzip.Writer
statusCode int
headerWritten bool
}
var (
pool = sync.Pool{
New: func() interface{} {
w, _ := gzip.NewWriterLevel(nil, gzip.BestSpeed)
return &gzipResponseWriter{
w: w,
}
},
}
)
func (gzr *gzipResponseWriter) WriteHeader(statusCode int) {
gzr.statusCode = statusCode
gzr.headerWritten = true
if gzr.statusCode != http.StatusNotModified && gzr.statusCode != http.StatusNoContent {
gzr.ResponseWriter.Header().Del("Content-Length")
gzr.ResponseWriter.Header().Set("Content-Encoding", "gzip")
}
gzr.ResponseWriter.WriteHeader(statusCode)
}
func (gzr *gzipResponseWriter) Write(b []byte) (int, error) {
if _, ok := gzr.Header()["Content-Type"]; !ok {
// If no content type, apply sniffing algorithm to un-gzipped body.
gzr.ResponseWriter.Header().Set("Content-Type", http.DetectContentType(b))
}
if !gzr.headerWritten {
// This is exactly what Go would also do if it hasn't been written yet.
gzr.WriteHeader(http.StatusOK)
}
return gzr.w.Write(b)
}
func (gzr *gzipResponseWriter) Flush() {
if gzr.w != nil {
gzr.w.Flush()
}
if fw, ok := gzr.ResponseWriter.(http.Flusher); ok {
fw.Flush()
}
}

View File

@ -0,0 +1,108 @@
package appserver
import (
"errors"
"github.com/gorilla/context"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appcontext"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/log"
"go.elastic.co/apm/module/apmgorilla"
"net/http"
"net/http/pprof"
)
var (
AppName string
)
func (s *server) Start(ctx appcontext.AppContext) {
allowedOrigins := handlers.AllowedOrigins([]string{"*"}) // Allowing all origin as of now
allowedHeaders := handlers.AllowedHeaders([]string{
"Accept",
"Content-Type",
"contentType",
"Content-Length",
"Accept-Encoding",
"Client-Security-Token",
"X-CSRF-Token",
"X-Auth-Token",
"processData",
"Authorization",
"Access-Control-Request-Headers",
"Access-Control-Request-Method",
"Connection",
"Host",
"Origin",
"User-Agent",
"Referer",
"Cache-Control",
"X-header",
"X-Requested-With",
"timezone",
"locale",
"gzip-compress",
"task",
"access_token",
"application",
})
allowedMethods := handlers.AllowedMethods([]string{
"POST",
"GET",
"DELETE",
"PUT",
"PATCH",
"OPTIONS"})
allowCredential := handlers.AllowCredentials()
serverHandler := handlers.CORS(
allowedHeaders,
allowedMethods,
allowedOrigins,
allowCredential)(
context.ClearHandler(
s.newRouter(s.subRoute),
),
)
log.GenericInfo(ctx, "Starting Server",
log.FieldsMap{
"Port": s.port,
"SubRoute": s.subRoute,
"App": AppName,
})
err := http.ListenAndServe(":"+s.port, serverHandler)
if err != nil {
log.GenericError(ctx, errors.New("failed to start appserver"),
log.FieldsMap{
"Port": s.port,
"SubRoute": s.subRoute,
"App": AppName,
})
return
}
}
// Handles all incoming request who matches registered routes against the request.
func (s *server) newRouter(subRoute string) *mux.Router {
muxRouter := mux.NewRouter().StrictSlash(true)
muxRouter.HandleFunc(subRoute+"/debug/pprof", pprof.Index)
muxRouter.HandleFunc(subRoute+"/debug/pprof/cmdline", pprof.Cmdline)
muxRouter.HandleFunc(subRoute+"/debug/pprof/profile", pprof.Profile)
muxRouter.HandleFunc(subRoute+"/debug/pprof/symbol", pprof.Symbol)
muxRouter.HandleFunc(subRoute+"/debug/pprof/trace", pprof.Trace)
muxRouter.Handle(subRoute+"/debug/pprof/goroutine", pprof.Handler("goroutine"))
muxRouter.Handle(subRoute+"/debug/pprof/heap", pprof.Handler("heap"))
muxRouter.Handle(subRoute+"/debug/pprof/thread/create", pprof.Handler("threadcreate"))
muxRouter.Handle(subRoute+"/debug/pprof/block", pprof.Handler("block"))
muxRouter.Use(SetTraceID, apmgorilla.Middleware())
for _, r := range s.routes {
muxRouter.HandleFunc(subRoute+r.Pattern, r.HandlerFunc).Methods(r.Method)
}
return muxRouter
}

View File

@ -0,0 +1,195 @@
package appserver
import (
"bytes"
"encoding/json"
"fmt"
"github.com/felixge/httpsnoop"
"github.com/google/uuid"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appcontext"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/log"
"go.elastic.co/apm"
"go.elastic.co/apm/module/apmhttp"
"io/ioutil"
"net/http"
"runtime/debug"
"strings"
"time"
)
const TraceID = "traceid"
func SetTraceID(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var traceID apm.TraceID
if values := r.Header[apmhttp.W3CTraceparentHeader]; len(values) == 1 && values[0] != "" {
if c, err := apmhttp.ParseTraceparentHeader(values[0]); err == nil {
traceID = c.Trace
}
}
if err := traceID.Validate(); err != nil {
uuidId := uuid.New()
var spanID apm.SpanID
var traceOptions apm.TraceOptions
copy(traceID[:], uuidId[:])
copy(spanID[:], traceID[8:])
traceContext := apm.TraceContext{
Trace: traceID,
Span: spanID,
Options: traceOptions.WithRecorded(true),
}
r.Header.Set(apmhttp.W3CTraceparentHeader, apmhttp.FormatTraceparentHeader(traceContext))
}
w.Header().Set(TraceID, traceID.String())
r.Header.Set(requestID, traceID.String())
next.ServeHTTP(w, r)
})
}
func enableCompression(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "br") && !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
next(w, r)
return
} else if !strings.Contains(r.Header.Get("Accept-Encoding"), "br") {
gzr := pool.Get().(*gzipResponseWriter)
gzr.statusCode = 0
gzr.headerWritten = false
gzr.ResponseWriter = w
gzr.w.Reset(w)
defer func() {
// gzr.w.Close will write a footer even if no data has been written.
// StatusNotModified and StatusNoContent expect an empty body so don't close it.
if gzr.statusCode != http.StatusNotModified && gzr.statusCode != http.StatusNoContent {
if err := gzr.w.Close(); err != nil {
ctx := appcontext.UpgradeCtx(r.Context())
log.GenericError(ctx, err, nil)
}
}
pool.Put(gzr)
}()
next(gzr, r)
return
}
br := poolbr.Get().(*brotliResponseWriter)
br.statusCode = 0
br.headerWritten = false
br.ResponseWriter = w
br.w.Reset(w)
defer func() {
// brotli.w.Close will write a footer even if no data has been written.x
// StatusNotModified and StatusNoContent expect an empty body so don't close it.
if br.statusCode != http.StatusNotModified && br.statusCode != http.StatusNoContent {
if err := br.w.Close(); err != nil {
ctx := appcontext.UpgradeCtx(r.Context())
log.GenericError(ctx, err, nil)
}
}
poolbr.Put(br)
}()
next(br, r)
}
}
func recovery(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
defer func() {
ctx := appcontext.UpgradeCtx(r.Context())
rec := recover()
if rec != nil {
span, _ := apm.StartSpan(ctx.Context, "recovery", "custom")
defer span.End()
trace := string(debug.Stack())
trace = strings.Replace(trace, "\n", " ", -1)
trace = strings.Replace(trace, "\t", " ", -1)
log.GenericError(ctx, fmt.Errorf("%v", rec),
log.FieldsMap{
"msg": "recovering from panic",
"stackTrace": trace,
})
jsonBody, _ := json.Marshal(map[string]string{
"error": "There was an internal server error",
})
e := apm.DefaultTracer.Recovered(rec)
e.SetSpan(span)
e.Send()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
w.Write(jsonBody)
}
}()
next.ServeHTTP(w, r)
}
}
func logRequest(handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
m := httpsnoop.CaptureMetrics(handler, w, r)
log.HTTPLog(constructHTTPLog(r, m, time.Since(start)))
}
}
func constructHTTPLog(r *http.Request, m httpsnoop.Metrics, duration time.Duration) string {
ctx := r.Context().Value(appcontext.AppCtx)
rawBody, _ := ioutil.ReadAll(r.Body)
if len(rawBody) > 0 {
r.Body = ioutil.NopCloser(bytes.NewBuffer(rawBody))
}
var jsonBody interface{}
// For Testing
json.Unmarshal(rawBody, &jsonBody)
bodyJsonByte, _ := json.Marshal(jsonBody)
if ctx != nil {
tCtx := ctx.(appcontext.AppExtContext)
return fmt.Sprintf("|%s|%s|%s|%s|%s|%d|%d|%s|%s|%s|",
tCtx.UserEmail,
"requestId="+tCtx.RequestID,
r.RemoteAddr,
r.Method,
r.URL,
m.Code,
m.Written,
r.UserAgent(),
duration,
"Body:"+string(bodyJsonByte),
)
}
return fmt.Sprintf("|%s|%s|%s|%d|%d|%s|%s|%s|",
r.RemoteAddr,
r.Method,
r.URL,
m.Code,
m.Written,
r.UserAgent(),
duration,
"Body:"+string(rawBody),
)
}
func createContext(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
header := r.Header
ctx := r.Context()
reqID := header.Get(requestID)
if reqID == "" {
reqID = strings.ReplaceAll(uuid.NewString(), "-", "")
}
email, app := header.Get(userEmail), header.Get(application)
locale := header.Get(locale)
tempCtx := appcontext.AppExtContext{
RequestID: reqID,
UserEmail: email,
Locale: locale,
Application: app,
}
ctx = appcontext.WithAppCtx(ctx, tempCtx)
next.ServeHTTP(w, r.WithContext(ctx))
}
}

View File

@ -0,0 +1,19 @@
package appserver
import "net/http"
func (s *server) AddNoAuthRoutes(methodName string, methodType string, mRoute string, handlerFunc http.HandlerFunc) {
r := route{
Name: methodName,
Method: methodType,
Pattern: mRoute,
HandlerFunc: useMiddleware(handlerFunc, recovery, enableCompression, logRequest, createContext)}
s.routes = append(s.routes, r)
}
func useMiddleware(h http.HandlerFunc, middleware ...func(http.HandlerFunc) http.HandlerFunc) http.HandlerFunc {
for _, m := range middleware {
h = m(h)
}
return h
}

40
core/appserver/server.go Normal file
View File

@ -0,0 +1,40 @@
package appserver
import (
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appcontext"
"net/http"
)
type route struct {
Name string
Method string
Pattern string
Modules []string
ResourcesPermissionMap interface{}
HandlerFunc http.HandlerFunc
}
type server struct {
port string
subRoute string
routes []route
}
const (
requestID = "requestId"
userEmail = "email"
application = "application"
locale = "locale"
)
type AppServer interface {
Start(ctx appcontext.AppContext)
AddNoAuthRoutes(methodName string, methodType string, mRoute string, handlerFunc http.HandlerFunc)
}
func NewAppServer(port, subRoute string) AppServer {
return &server{
port: port,
subRoute: subRoute,
}
}

260
core/log/log.go Normal file
View File

@ -0,0 +1,260 @@
package log
import (
"encoding/json"
"fmt"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appcontext"
"io"
"log"
"os"
"strings"
)
const (
//INFO level 1
INFO = iota
//HTTP level 2
HTTP
//ERROR level 3
ERROR
//TRACE level 4
TRACE
//WARNING level 5
WARNING
)
var (
setLevel = WARNING
trace *log.Logger
info *log.Logger
warning *log.Logger
httplog *log.Logger
errorlog *log.Logger
)
const (
clusterType = "CLUSTER_TYPE"
clusterTypeLocal = "local"
clusterTypeDev = "dev1"
)
// FieldsMap map of key value pair to log
type FieldsMap map[string]interface{}
func init() {
logInit(os.Stdout,
os.Stdout,
os.Stdout,
os.Stdout,
os.Stderr)
}
func logInit(traceHandle, infoHandle, warningHandle, httpHandle, errorHandle io.Writer) {
flagWithClusterType := log.LUTC | log.LstdFlags | log.Lshortfile
flagWithoutClusterType := log.LUTC | log.LstdFlags
var flag int
if os.Getenv(clusterType) == clusterTypeLocal || os.Getenv(clusterType) == clusterTypeDev {
flag = flagWithClusterType
} else {
flag = flagWithoutClusterType
}
trace = log.New(traceHandle, "TRACE|", flag)
info = log.New(infoHandle, "INFO|", flag)
warning = log.New(warningHandle, "WARNING|", flag)
httplog = log.New(httpHandle, "HTTP|", flag)
errorlog = log.New(errorHandle, "ERROR|", flagWithClusterType)
}
func doLog(cLog *log.Logger, level, callDepth int, v ...interface{}) {
if level <= setLevel {
if level == ERROR {
cLog.SetOutput(os.Stderr)
cLog.SetFlags(log.Llongfile)
}
cLog.Output(callDepth, fmt.Sprintln(v...))
}
}
func generatePrefix(ctx appcontext.AppContext) string {
// TODO: Add departmentName once that is implemented fully
return strings.Join([]string{ctx.UserEmail}, ":")
}
func generateTrackingIDs(ctx appcontext.AppContext) (retString string) {
requestID := ctx.RequestID
if requestID != "" {
retString = "requestId=" + requestID
}
return
}
// Trace system gives facility to helps you isolate your system problems by monitoring selected events Ex. entry and exit
func traceLog(v ...interface{}) {
doLog(trace, TRACE, 4, v...)
}
// Info dedicated for logging valuable information
func infoLog(v ...interface{}) {
doLog(info, INFO, 4, v...)
}
// Warning for critical error
func warningLog(v ...interface{}) {
doLog(warning, WARNING, 4, v...)
}
// Error logging error
func errorLog(v ...interface{}) {
doLog(errorlog, ERROR, 4, v...)
}
func fatalLog(v ...interface{}) {
doLog(errorlog, ERROR, 4, v...)
os.Exit(1)
}
func HTTPLog(logMessage string) {
doLog(httplog, HTTP, 6, logMessage)
}
func GenericTrace(ctx appcontext.AppContext, traceMessage string, data ...FieldsMap) {
var fields FieldsMap
if len(data) > 0 {
fields = data[0]
}
if os.Getenv("SERVICE_TRACE") == "true" {
prefix := generatePrefix(ctx)
trackingIDs := generateTrackingIDs(ctx)
msg := fmt.Sprintf("|%s|%s|",
prefix,
trackingIDs)
if fields != nil && len(fields) > 0 {
fieldsBytes, _ := json.Marshal(fields)
fieldsString := string(fieldsBytes)
traceLog(msg, traceMessage, "|", fieldsString)
} else {
traceLog(msg, traceMessage)
}
}
}
func GenericInfo(ctx appcontext.AppContext, infoMessage string, data ...FieldsMap) {
var fields FieldsMap
if len(data) > 0 {
fields = data[0]
}
prefix := generatePrefix(ctx)
trackingIDs := generateTrackingIDs(ctx)
fieldsBytes, _ := json.Marshal(fields)
fieldsString := string(fieldsBytes)
msg := fmt.Sprintf("|%s|%s|",
prefix,
trackingIDs)
if fields != nil && len(fields) > 0 {
infoLog(msg, infoMessage, "|", fieldsString)
} else {
infoLog(msg, infoMessage)
}
}
func GenericWarning(ctx appcontext.AppContext, warnMessage string, data ...FieldsMap) {
var fields FieldsMap
if len(data) > 0 {
fields = data[0]
}
if os.Getenv("SERVICE_WARN") == "true" {
prefix := generatePrefix(ctx)
trackingIDs := generateTrackingIDs(ctx)
msg := fmt.Sprintf("|%s|%s|",
prefix,
trackingIDs)
if fields != nil && len(fields) > 0 {
fieldsBytes, _ := json.Marshal(fields)
fieldsString := string(fieldsBytes)
warningLog(msg, warnMessage, "|", fieldsString)
} else {
warningLog(msg, warnMessage)
}
}
}
func GenericError(ctx appcontext.AppContext, e error, data ...FieldsMap) {
var fields FieldsMap
if len(data) > 0 {
fields = data[0]
}
prefix := generatePrefix(ctx)
trackingIDs := generateTrackingIDs(ctx)
msg := ""
if e != nil {
msg = fmt.Sprintf("|%s|%s|%s", prefix, trackingIDs, e.Error())
} else {
msg = fmt.Sprintf("|%s|%s", prefix, trackingIDs)
}
if fields != nil && len(fields) > 0 {
fieldsBytes, _ := json.Marshal(fields)
fieldsString := string(fieldsBytes)
errorLog(msg, "|", fieldsString)
} else {
errorLog(msg)
}
}
func CriticalError(ctx appcontext.AppContext, e error, data ...FieldsMap) {
var fields FieldsMap
if len(data) > 0 {
fields = data[0]
}
prefix := generatePrefix(ctx)
trackingIDs := generateTrackingIDs(ctx)
msg := ""
if e != nil {
msg = fmt.Sprintf("|%s|%s|%s", prefix, trackingIDs, e.Error())
} else {
msg = fmt.Sprintf("|%s|%s", prefix, trackingIDs)
}
if fields != nil && len(fields) > 0 {
fieldsBytes, _ := json.Marshal(fields)
fieldsString := string(fieldsBytes)
errorLog(msg, "|", fieldsString)
} else {
errorLog(msg)
}
}
func FatalLog(ctx appcontext.AppContext, e error, data ...FieldsMap) {
var fields FieldsMap
if len(data) > 0 {
fields = data[0]
}
prefix := generatePrefix(ctx)
trackingIDs := generateTrackingIDs(ctx)
msg := ""
if e != nil {
msg = fmt.Sprintf("|%s|%s|%s", prefix, trackingIDs, e.Error())
} else {
msg = fmt.Sprintf("|%s|%s", prefix, trackingIDs)
}
if fields != nil && len(fields) > 0 {
fieldsBytes, _ := json.Marshal(fields)
fieldsString := string(fieldsBytes)
fatalLog(msg, "|", fieldsString)
} else {
fatalLog(msg)
}
}

View File

@ -0,0 +1,239 @@
package mongomanager
import (
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appcontext"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func (client *mongoService) Disconnect(ctx appcontext.AppContext) error {
return client.DB.Disconnect(ctx)
}
func (client *mongoService) CreateOne(ctx appcontext.AppContext, database, collectionName string, d interface{}) (*mongo.InsertOneResult, error) {
collection := client.DB.Database(database).Collection(collectionName)
insertOneResult, err := collection.InsertOne(ctx.Context, d)
if err != nil {
return nil, err
}
return insertOneResult, nil
}
// CreateMany - inserts many data into mongo database
func (client *mongoService) CreateMany(ctx appcontext.AppContext, database, collectionName string, d []interface{}) (*mongo.InsertManyResult, error) {
collection := client.DB.Database(database).Collection(collectionName)
insertManyRslt, err := collection.InsertMany(ctx.Context, d)
if err != nil {
return nil, err
}
return insertManyRslt, nil
}
// ReadOne - reads single document from mongo database
func (client *mongoService) ReadOne(ctx appcontext.AppContext, database, collectionName string, filter, data interface{}) error {
collection := client.DB.Database(database).Collection(collectionName)
err := collection.FindOne(ctx.Context, filter).Decode(data)
if err != nil {
return err
}
return nil
}
// ReadAll - reads multiple documents from mongo database
func (client *mongoService) ReadAll(ctx appcontext.AppContext, database, collectionName string, filter, data interface{}, opts ...*options.FindOptions) error {
var findOptions *options.FindOptions
if len(opts) > 0 {
findOptions = opts[0]
}
collection := client.DB.Database(database).Collection(collectionName)
cursor, err := collection.Find(ctx.Context, filter, findOptions)
if err != nil {
return err
}
defer cursor.Close(ctx.Context)
err = cursor.All(ctx.Context, data)
if err != nil {
return err
}
return nil
}
// Update - updates data into mongo database
func (client *mongoService) Update(ctx appcontext.AppContext, database, collectionName string, filter, update interface{}, options ...*options.UpdateOptions) (*mongo.UpdateResult, error) {
collection := client.DB.Database(database).Collection(collectionName)
updateResult, err := collection.UpdateOne(ctx.Context, filter, update, options...)
if err != nil {
return nil, err
}
return updateResult, nil
}
// ReplaceOne - replace one document into mongo database
func (client *mongoService) ReplaceOne(ctx appcontext.AppContext, database, collectionName string, filter, replacement interface{}, opts ...*options.ReplaceOptions) (*mongo.UpdateResult, error) {
collection := client.DB.Database(database).Collection(collectionName)
updateResult, err := collection.ReplaceOne(ctx.Context, filter, replacement, opts...)
if err != nil {
return nil, err
}
return updateResult, nil
}
// UpdateAndReturn - updates data into mongo database and returns the updated document
func (client *mongoService) UpdateAndReturn(ctx appcontext.AppContext, database, collectionName string, filter, update, data interface{}) error {
collection := client.DB.Database(database).Collection(collectionName)
after := options.After
opts := options.FindOneAndUpdateOptions{
ReturnDocument: &after,
}
err := collection.FindOneAndUpdate(ctx.Context, filter, update, &opts).Decode(data)
if err != nil {
return err
}
return nil
}
// UpdateAll - updates multiple documents into mongo database
func (client *mongoService) UpdateAll(ctx appcontext.AppContext, database, collectionName string, filter, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) {
collection := client.DB.Database(database).Collection(collectionName)
updateResult, err := collection.UpdateMany(ctx.Context, filter, update, opts...)
if err != nil {
return nil, err
}
return updateResult, nil
}
func (client *mongoService) Upsert(ctx appcontext.AppContext, database, collectionName string, filter,
update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) {
collection := client.DB.Database(database).Collection(collectionName)
updateOptions := options.Update()
if len(opts) >= 1 {
updateOptions = opts[0]
}
updateOptions.SetUpsert(true)
updateResult, err := collection.UpdateOne(ctx.Context, filter, update, updateOptions)
if err != nil {
return nil, err
}
return updateResult, nil
}
// Delete - removes single doc data from the database
func (client *mongoService) Delete(ctx appcontext.AppContext, database, collectionName string, filter interface{}) (*mongo.DeleteResult, error) {
collection := client.DB.Database(database).Collection(collectionName)
deleteResult, err := collection.DeleteOne(ctx.Context, filter)
if err != nil {
return nil, err
}
return deleteResult, nil
}
// DeleteAll - removes all doc data from the database
func (client *mongoService) DeleteAll(ctx appcontext.AppContext, database, collectionName string, filter interface{}) (*mongo.DeleteResult, error) {
collection := client.DB.Database(database).Collection(collectionName)
deleteResult, err := collection.DeleteMany(ctx.Context, filter)
if err != nil {
return nil, err
}
return deleteResult, nil
}
// CountDocuments returns document count of a collection
func (client *mongoService) CountDocuments(ctx appcontext.AppContext, database, collectionName string, filter interface{}, opts ...*options.CountOptions) (int64, error) {
var countOptions *options.CountOptions
if len(opts) > 0 {
countOptions = opts[0]
}
collection := client.DB.Database(database).Collection(collectionName)
count, err := collection.CountDocuments(ctx.Context, filter, countOptions)
if err != nil {
return 0, err
}
return count, nil
}
// Exist verifies if document is present or not
// if it returns error then there is a connection error else boolean value specifies whether doc is present or not
func (client *mongoService) Exist(ctx appcontext.AppContext, database, collectionName string, filter interface{}) (bool, error) {
var i interface{}
collection := client.DB.Database(database).Collection(collectionName)
err := collection.FindOne(ctx.Context, filter).Decode(&i)
if err != nil {
return false, nil
}
return true, nil
}
// GetDistinct gets the distinct values for the field name provided
func (client *mongoService) GetDistinct(ctx appcontext.AppContext, database, collectionName, fieldName string, filter interface{}) (interface{}, error) {
collection := client.DB.Database(database).Collection(collectionName)
result, err := collection.Distinct(ctx.Context, fieldName, filter, nil)
if err != nil {
return nil, err
}
return result, nil
}
// AggregateAll executes aggregation query on a collection
// query []bson.M, data is a pointer to an array
func (client *mongoService) AggregateAll(ctx appcontext.AppContext, database, collectionName string, query, data interface{}, options ...*options.AggregateOptions) error {
collection := client.DB.Database(database).Collection(collectionName)
cursor, err := collection.Aggregate(ctx.Context, query, options...)
if err != nil {
return err
}
err = cursor.All(ctx.Context, data)
return err
}
// FindOneAndUpdate executes a findAndModify command to update at most one document in the collection and returns the
// document as it appeared before updating.
func (client *mongoService) FindOneAndUpdate(ctx appcontext.AppContext, database, collectionName string, filter, update, data interface{},
opts ...*options.FindOneAndUpdateOptions) error {
after := options.After
option := &options.FindOneAndUpdateOptions{
ReturnDocument: &after,
}
if len(opts) > 0 {
option = opts[0]
}
collection := client.DB.Database(database).Collection(collectionName)
result := collection.FindOneAndUpdate(ctx.Context, filter, update, option)
if result.Err() != nil {
return result.Err()
}
decodeErr := result.Decode(data)
if decodeErr != nil {
return decodeErr
}
return nil
}
func (client *mongoService) BulkWrite(ctx appcontext.AppContext, database, collectionName string, operations []mongo.WriteModel, bulkOption *options.BulkWriteOptions) (*mongo.BulkWriteResult, error) {
var err error
collection := client.DB.Database(database).Collection(collectionName)
result, err := collection.BulkWrite(ctx.Context, operations, bulkOption)
if err != nil {
return nil, err
}
return result, nil
}

View File

@ -0,0 +1,89 @@
package mongomanager
import (
"context"
"errors"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appcontext"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/log"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"time"
)
type mongoService struct {
DB *mongo.Client
}
type MongoDB interface {
Disconnect(ctx appcontext.AppContext) error
CreateOne(ctx appcontext.AppContext, database, collectionName string, d interface{}) (*mongo.InsertOneResult, error)
CreateMany(ctx appcontext.AppContext, database, collectionName string, d []interface{}) (*mongo.InsertManyResult, error)
ReadOne(ctx appcontext.AppContext, database, collectionName string, filter, data interface{}) error
ReadAll(ctx appcontext.AppContext, database, collectionName string, filter, data interface{}, opts ...*options.FindOptions) error
Update(ctx appcontext.AppContext, database, collectionName string, filter, update interface{}, options ...*options.UpdateOptions) (*mongo.UpdateResult, error)
ReplaceOne(ctx appcontext.AppContext, database, collectionName string, filter, replacement interface{}, opts ...*options.ReplaceOptions) (*mongo.UpdateResult, error)
Upsert(ctx appcontext.AppContext, database, collectionName string, filter, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error)
UpdateAndReturn(ctx appcontext.AppContext, database, collectionName string, filter, update, data interface{}) error
UpdateAll(ctx appcontext.AppContext, database, collectionName string, filter, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error)
Delete(ctx appcontext.AppContext, database, collectionName string, filter interface{}) (*mongo.DeleteResult, error)
DeleteAll(ctx appcontext.AppContext, database, collectionName string, filter interface{}) (*mongo.DeleteResult, error)
CountDocuments(ctx appcontext.AppContext, database, collectionName string, filter interface{}, opts ...*options.CountOptions) (int64, error)
Exist(ctx appcontext.AppContext, database, collectionName string, filter interface{}) (bool, error)
GetDistinct(ctx appcontext.AppContext, database, collectionName, fieldName string, filter interface{}) (interface{}, error)
AggregateAll(ctx appcontext.AppContext, database, collectionName string, query, data interface{}, options ...*options.AggregateOptions) error
FindOneAndUpdate(ctx appcontext.AppContext, database, collectionName string, filter, update, data interface{}, opts ...*options.FindOneAndUpdateOptions) error
BulkWrite(ctx appcontext.AppContext, database, collectionName string, operations []mongo.WriteModel, bulkOption *options.BulkWriteOptions) (*mongo.BulkWriteResult, error)
}
func NewMongoClient(url, appName string) (MongoDB, error) {
ctx := appcontext.NewAppContext()
client, err := connect(url, appName)
if err != nil {
log.GenericError(ctx, errors.New("can't connect to db: "+err.Error()), nil)
return &mongoService{DB: client}, err
}
return &mongoService{DB: client}, nil
}
func connect(host, appName string) (*mongo.Client, error) {
var client *mongo.Client
servSelecTimeout := time.Duration(15) * time.Second
connTimeout := time.Duration(10) * time.Second
idleTime := time.Duration(2) * time.Minute
socketTimeout := time.Duration(2) * time.Minute
maxPooling := uint64(100)
clientOptions := &options.ClientOptions{
AppName: &appName,
ServerSelectionTimeout: &servSelecTimeout,
ConnectTimeout: &connTimeout,
MaxConnIdleTime: &idleTime,
MaxPoolSize: &maxPooling,
SocketTimeout: &socketTimeout,
}
clientOptions = clientOptions.ApplyURI(host)
err := clientOptions.Validate()
if err != nil {
return client, err
}
client, err = mongo.NewClient(clientOptions)
if err != nil {
return client, err
}
err = client.Connect(context.TODO())
if err != nil {
return client, err
}
err = client.Ping(context.TODO(), nil)
if err != nil {
return client, err
}
return client, nil
}

5
db/db.go Normal file
View File

@ -0,0 +1,5 @@
package db
type Databases interface {
Movies
}

6
db/mongo/const.go Normal file
View File

@ -0,0 +1,6 @@
package mongo
const (
obdDatabase = "global"
movieCollection = "movies"
)

18
db/mongo/mongo.go Normal file
View File

@ -0,0 +1,18 @@
package mongo
import (
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/mongomanager"
"gitlab.com/arkadooti.sarkar/go-boilerplate/db"
)
var _ db.Databases = (*ServicesMongo)(nil)
type ServicesMongo struct {
Db mongomanager.MongoDB
}
func NewMongoService(mongoClient mongomanager.MongoDB) db.Databases {
return &ServicesMongo{
Db: mongoClient,
}
}

28
db/mongo/movieDetails.go Normal file
View File

@ -0,0 +1,28 @@
package mongo
import (
"errors"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appcontext"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/log"
"gitlab.com/arkadooti.sarkar/go-boilerplate/models"
"go.mongodb.org/mongo-driver/bson"
)
func (s *ServicesMongo) GetMovieDetails(ctx appcontext.AppContext, movieName string) (models.MovieDetails, error) {
details := models.MovieDetails{}
b := bson.M{"title": bson.M{"$regex": movieName, "$options": "i"}}
err := s.Db.ReadOne(ctx, obdDatabase, movieCollection, b, &details)
if err != nil {
log.GenericError(ctx, errors.New("GetMovieDetails DB error: "+err.Error()), log.FieldsMap{"title": movieName})
return models.MovieDetails{}, err
}
return details, nil
}
func (s *ServicesMongo) SetMovieDetails(ctx appcontext.AppContext, movieDetails models.MovieDetails) error {
_, err := s.Db.CreateOne(ctx, obdDatabase, movieCollection, movieDetails)
if err != nil {
log.GenericError(ctx, errors.New("SetMovieDetails DB error: "+err.Error()))
return err
}
return nil
}

11
db/movies.go Normal file
View File

@ -0,0 +1,11 @@
package db
import (
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appcontext"
"gitlab.com/arkadooti.sarkar/go-boilerplate/models"
)
type Movies interface {
GetMovieDetails(ctx appcontext.AppContext, movieName string) (models.MovieDetails, error)
SetMovieDetails(ctx appcontext.AppContext, movieDetails models.MovieDetails) error
}

5
domain/domain.go Normal file
View File

@ -0,0 +1,5 @@
package domain
type Domain interface {
Movies
}

10
domain/movies.go Normal file
View File

@ -0,0 +1,10 @@
package domain
import (
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appcontext"
"gitlab.com/arkadooti.sarkar/go-boilerplate/models"
)
type Movies interface {
GetMovieDetailsFromApi(ctx appcontext.AppContext, movieName string) (models.MovieDetails, error)
}

View File

@ -0,0 +1,21 @@
package standard
import (
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appcontext"
"gitlab.com/arkadooti.sarkar/go-boilerplate/models"
"gitlab.com/arkadooti.sarkar/go-boilerplate/utils"
)
func (s *Standard) GetMovieDetailsFromApi(ctx appcontext.AppContext, movieName string) (models.MovieDetails, error) {
apiParam := utils.ApiParam{
Url: "http://www.omdbapi.com/?apikey=d2bd086",
Query: map[string]string{"t": movieName},
}
movieDetails := models.MovieDetails{}
err := apiParam.CallAPI(ctx, &movieDetails)
if err != nil {
return models.MovieDetails{}, err
}
return movieDetails, nil
}

View File

@ -0,0 +1,13 @@
package standard
import "gitlab.com/arkadooti.sarkar/go-boilerplate/db"
type Standard struct {
DbMongo db.Databases
}
func NewDomainService(mongo db.Databases) *Standard {
return &Standard{
DbMongo: mongo,
}
}

45
go.mod Normal file
View File

@ -0,0 +1,45 @@
module gitlab.com/arkadooti.sarkar/go-boilerplate
go 1.18
require (
github.com/google/uuid v1.4.0
github.com/gorilla/context v1.1.2
github.com/gorilla/handlers v1.5.2
github.com/gorilla/mux v1.8.1
go.elastic.co/apm v1.15.0
go.elastic.co/apm/module/apmgorilla v1.15.0
go.elastic.co/apm/module/apmhttp v1.15.0
)
require (
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/elastic/go-licenser v0.3.1 // indirect
github.com/elastic/go-sysinfo v1.1.1 // indirect
github.com/elastic/go-windows v1.0.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/jcchavezs/porto v0.1.0 // indirect
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0 // indirect
github.com/santhosh-tekuri/jsonschema v1.2.4 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
go.elastic.co/fastjson v1.1.0 // indirect
go.mongodb.org/mongo-driver v1.13.1 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/tools v0.1.12 // indirect
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect
)

144
go.sum Normal file
View File

@ -0,0 +1,144 @@
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/elastic/go-licenser v0.3.1 h1:RmRukU/JUmts+rpexAw0Fvt2ly7VVu6mw8z4HrEzObU=
github.com/elastic/go-licenser v0.3.1/go.mod h1:D8eNQk70FOCVBl3smCGQt/lv7meBeQno2eI1S5apiHQ=
github.com/elastic/go-sysinfo v1.1.1 h1:ZVlaLDyhVkDfjwPGU55CQRCRolNpc7P0BbyhhQZQmMI=
github.com/elastic/go-sysinfo v1.1.1/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0=
github.com/elastic/go-windows v1.0.0 h1:qLURgZFkkrYyTTkvYpsZIgf83AUsdIHfvlJaqaZ7aSY=
github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/jcchavezs/porto v0.1.0 h1:Xmxxn25zQMmgE7/yHYmh19KcItG81hIwfbEEFnd6w/Q=
github.com/jcchavezs/porto v0.1.0/go.mod h1:fESH0gzDHiutHRdX2hv27ojnOVFco37hg1W6E9EZF4A=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0 h1:c8R11WC8m7KNMkTv/0+Be8vvwo4I3/Ut9AC2FW8fX3U=
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis=
github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.elastic.co/apm v1.15.0 h1:uPk2g/whK7c7XiZyz/YCUnAUBNPiyNeE3ARX3G6Gx7Q=
go.elastic.co/apm v1.15.0/go.mod h1:dylGv2HKR0tiCV+wliJz1KHtDyuD8SPe69oV7VyK6WY=
go.elastic.co/apm/module/apmgorilla v1.15.0 h1:1yTAksffgaFXYEIwlLRiQnxLfy3p3RtpDw8HDupIJfY=
go.elastic.co/apm/module/apmgorilla v1.15.0/go.mod h1:+23mZudYvZ9VgxCQjseLo9EF5gkKEr0KSQBupw+rzP8=
go.elastic.co/apm/module/apmhttp v1.15.0 h1:Le/DhI0Cqpr9wG/NIGOkbz7+rOMqJrfE4MRG6q/+leU=
go.elastic.co/apm/module/apmhttp v1.15.0/go.mod h1:NruY6Jq8ALLzWUVUQ7t4wIzn+onKoiP5woJJdTV7GMg=
go.elastic.co/fastjson v1.1.0 h1:3MrGBWWVIxe/xvsbpghtkFoPciPhOCmjsR/HfwEeQR4=
go.elastic.co/fastjson v1.1.0/go.mod h1:boNGISWMjQsUPy/t6yqt2/1Wx4YNPSe+mZjlyw9vKKI=
go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk=
go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5 h1:MeC2gMlMdkd67dn17MEby3rGXRxZtWeiRXOnISfTQ74=
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M=
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=

31
main.go Normal file
View File

@ -0,0 +1,31 @@
package main
import (
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appcontext"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/log"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/mongomanager"
"gitlab.com/arkadooti.sarkar/go-boilerplate/db/mongo"
"gitlab.com/arkadooti.sarkar/go-boilerplate/domain/standard"
"gitlab.com/arkadooti.sarkar/go-boilerplate/webService"
"os"
)
const servicePort = "4100"
const serviceRoute = "/boilerplate"
func main() {
ctx := appcontext.New()
mongoClient, err := mongomanager.NewMongoClient(os.Getenv("MongoHost"), "myapp")
if err != nil {
log.GenericError(ctx, err)
}
mongoService := mongo.NewMongoService(mongoClient)
domain := standard.NewDomainService(mongoService)
newService := webService.NewWebService(domain, mongoService, serviceRoute, servicePort)
newService.Run(ctx)
}

5
models/common.go Normal file
View File

@ -0,0 +1,5 @@
package models
type Ping struct {
Ping string `json:"ping"`
}

34
models/movies.go Normal file
View File

@ -0,0 +1,34 @@
package models
type MovieDetails struct {
Title string `json:"Title" bson:"title"`
Year string `json:"Year" bson:"year"`
Rated string `json:"Rated" bson:"rated"`
Released string `json:"Released" bson:"released"`
Runtime string `json:"Runtime" bson:"runtime"`
Genre string `json:"Genre" bson:"genre"`
Director string `json:"Director" bson:"director"`
Writer string `json:"Writer" bson:"writer"`
Actors string `json:"Actors" bson:"actors"`
Plot string `json:"Plot" bson:"plot"`
Language string `json:"Language" bson:"language"`
Country string `json:"Country" bson:"country"`
Awards string `json:"Awards" bson:"awards"`
Poster string `json:"Poster" bson:"poster"`
Ratings []MovieDetailsRating `json:"Ratings" bson:"ratings"`
Metascore string `json:"Metascore" bson:"metascore"`
ImdbRating string `json:"imdbRating" bson:"imdbRating"`
ImdbVotes string `json:"imdbVotes" bson:"imdbVotes"`
ImdbID string `json:"imdbID" bson:"imdbID"`
Type string `json:"Type" bson:"type"`
DVD string `json:"DVD" bson:"DVD"`
BoxOffice string `json:"BoxOffice" bson:"boxOffice"`
Production string `json:"Production" bson:"production"`
Website string `json:"Website" bson:"website"`
Response string `json:"Response" bson:"response"`
}
type MovieDetailsRating struct {
Source string `json:"Source" bson:"source"`
Value string `json:"Value" bson:"value"`
}

168
utils/apiUtils.go Normal file
View File

@ -0,0 +1,168 @@
package utils
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appcontext"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/log"
"io"
"io/ioutil"
"net/http"
"net/url"
"time"
)
const (
RequestID = "requestId"
)
var Client HTTPClient
// HTTPClient interface
type HTTPClient interface {
Do(req *http.Request) (*http.Response, error)
}
func init() {
Client = &http.Client{
Transport: &http.Transport{
DisableKeepAlives: true,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
Timeout: time.Duration(35) * time.Second,
}
}
// ApiParam api parameters
type ApiParam struct {
Url string
Type string
Query map[string]string
Headers map[string]string
Body io.Reader
}
type responseMetaData struct { //to-do: it must be renamed to a generic response struct
Code int `json:"code,omitempty"`
Message string `json:"message"`
}
func (apiParam *ApiParam) CallAPI(ctx appcontext.AppContext, dst interface{}) error {
reqMethod := "GET"
if apiParam.Type != "" {
reqMethod = apiParam.Type
}
if apiParam.Url == "" {
return errors.New("unable to make request, url is missing")
}
u, err := url.Parse(apiParam.Url)
if err != nil {
return err
}
q := u.Query()
for key, val := range apiParam.Query {
q.Set(key, fmt.Sprintf("%v", val))
}
u.RawQuery = q.Encode()
req, err := http.NewRequest(reqMethod, u.String(), apiParam.Body)
if req != nil {
_, reqIDFound := apiParam.Headers[RequestID]
if !reqIDFound {
req.Header.Set(RequestID, ctx.RequestID)
}
for headerKey, headerVal := range apiParam.Headers {
req.Header.Set(headerKey, headerVal)
}
}
resp, err := Client.Do(req)
if resp != nil {
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
log.GenericError(ctx, err, nil)
}
}(resp.Body)
}
if err != nil {
return err
}
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
if dst != nil {
err = json.NewDecoder(resp.Body).Decode(dst)
if err != nil {
return errors.WithMessage(err, "failed to decode response\",")
}
}
_, err := io.Copy(ioutil.Discard, resp.Body)
if err != nil {
log.GenericError(ctx, err, nil)
}
return nil
}
bodyError := fmt.Errorf("unable to make request, error in statusCode: " + resp.Status)
if resp.Body != nil {
body, _ := ioutil.ReadAll(resp.Body)
bodyError = errors.New(string(body))
}
return bodyError
}
func ReturnResponse(ctx appcontext.AppContext, w http.ResponseWriter, statusCode int, response interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(statusCode)
var buf = new(bytes.Buffer)
enc := json.NewEncoder(buf)
err := enc.Encode(response)
if err != nil {
log.GenericError(ctx, err)
}
_, err = w.Write(buf.Bytes())
if err != nil {
log.GenericError(ctx, err)
}
}
func ReturnOKResponse(ctx appcontext.AppContext, w http.ResponseWriter, response interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
var buf = new(bytes.Buffer)
enc := json.NewEncoder(buf)
err := enc.Encode(response)
if err != nil {
log.GenericError(ctx, err)
}
_, err = w.Write(buf.Bytes())
if err != nil {
log.GenericError(ctx, err)
}
}
func ReturnErrorResponse(ctx appcontext.AppContext, w http.ResponseWriter, responseErrorMessage string, statusCode int, logError error, fields ...log.FieldsMap) {
var buf = new(bytes.Buffer)
encoder := json.NewEncoder(buf)
if logError != nil {
if len(fields) > 0 {
log.GenericError(ctx, logError, fields[0])
} else {
log.GenericError(ctx, logError, nil)
}
}
err := encoder.Encode(responseMetaData{Message: responseErrorMessage})
if err != nil {
log.GenericError(ctx, err)
}
w.WriteHeader(statusCode)
_, err = w.Write(buf.Bytes())
if err != nil {
log.GenericError(ctx, err)
}
}

1
utils/helpers.go Normal file
View File

@ -0,0 +1 @@
package utils

46
webService/movies.go Normal file
View File

@ -0,0 +1,46 @@
package webService
import (
"errors"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appcontext"
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/log"
"gitlab.com/arkadooti.sarkar/go-boilerplate/utils"
"net/http"
)
func (s *WebService) MovieDetails(w http.ResponseWriter, r *http.Request) {
ctx := appcontext.UpgradeCtx(r.Context())
movieName := r.URL.Query().Get("name")
details, err := s.DB.GetMovieDetails(ctx, movieName)
if err != nil {
log.GenericError(ctx, errors.New("not able to get movies from DB:"+err.Error()))
}
if details.Title != "" {
log.GenericInfo(ctx, "Sending response from DB")
utils.ReturnOKResponse(ctx, w, details)
return
}
details, err = s.Domain.GetMovieDetailsFromApi(ctx, movieName)
if err != nil {
utils.ReturnErrorResponse(ctx, w, "Invalid request or IMDB error", http.StatusBadRequest, err)
return
}
if details.Title == "" {
utils.ReturnErrorResponse(ctx, w, "No movie found with the title: "+movieName, http.StatusBadRequest, err)
return
}
if details.Title != "" {
err = s.DB.SetMovieDetails(ctx, details)
if err != nil {
log.GenericError(ctx, errors.New("not able to insert into DB: "+err.Error()))
}
}
log.GenericInfo(ctx, "Sending response from API endpoint")
utils.ReturnOKResponse(ctx, w, details)
}

10
webService/ping.go Normal file
View File

@ -0,0 +1,10 @@
package webService
import (
"fmt"
"net/http"
)
func (s *WebService) Ping(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprintf(w, "alive")
}

18
webService/routes.go Normal file
View File

@ -0,0 +1,18 @@
package webService
import (
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appcontext"
"net/http"
)
func (s *WebService) Run(ctx appcontext.AppContext) {
s.registerPublicApis()
s.server.Start(ctx)
}
func (s *WebService) registerPublicApis() {
s.server.AddNoAuthRoutes("Ping", http.MethodGet, "/public/ping", s.Ping)
//Send movie name in query (query key: 'name')
s.server.AddNoAuthRoutes("Ping", http.MethodGet, "/public/movie-details", s.MovieDetails)
}

22
webService/webservice.go Normal file
View File

@ -0,0 +1,22 @@
package webService
import (
"gitlab.com/arkadooti.sarkar/go-boilerplate/core/appserver"
"gitlab.com/arkadooti.sarkar/go-boilerplate/db"
"gitlab.com/arkadooti.sarkar/go-boilerplate/domain"
)
type WebService struct {
Domain domain.Domain
DB db.Databases
server appserver.AppServer
}
func NewWebService(domainService domain.Domain, db db.Databases, serviceRoute, servicePort string) *WebService {
server := appserver.NewAppServer(servicePort, serviceRoute)
return &WebService{
Domain: domainService,
DB: db,
server: server,
}
}