commit 1685a798369c659dde0f2f7b0473fecb8366a51c Author: Braeden Sowinski Date: Mon Sep 26 16:40:40 2022 -0700 first commit diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..726c348 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +export DB_URI=mongodb://localhost:27017 +export DB_NAME=SulliCartShare + +export PORT=8000 +export BASE_URL=localhost +export ENV=local \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f6bbca1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Environment variables +.env + +# Vendor +vendor* \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b5e0356 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +test:mocks + go test ./... + +run: + go run main.go + +cover:mocks + go test ./... -coverprofile=coverage.out && go tool cover -html=coverage.out + +mocks: + mockery --dir databases --all --output ./databases/mocks diff --git a/api/handlers/api.go b/api/handlers/api.go new file mode 100644 index 0000000..9855164 --- /dev/null +++ b/api/handlers/api.go @@ -0,0 +1,77 @@ +package handlers + +import ( + "encoding/json" + "io" + "net/http" + + "github.com/SowinskiBraeden/SulliCartShare/api" + "github.com/SowinskiBraeden/SulliCartShare/models" + "github.com/gorilla/mux" + "go.uber.org/zap" + + "github.com/SowinskiBraeden/SulliCartShare/config" + "github.com/SowinskiBraeden/SulliCartShare/databases" +) + +// App stores the router and db connection so it can be reused +type App struct { + Router *mux.Router + DB databases.CollectionHelper + Config config.Config + dbHelper databases.DatabaseHelper +} + +// New creates a new mux router and all the routes +func (a *App) New() *mux.Router { + r := mux.NewRouter() + cow := Cow{DB: databases.NewCowDatabase(a.dbHelper)} + + // healthcheck + r.HandleFunc("/health", healthCheckHandler) + + apiCreate := r.PathPrefix("/api/v1").Subrouter() + + apiCreate.Handle("/cow/{cow_id}", api.Middleware(http.HandlerFunc(cow.CowByIDHandler))).Methods("GET") // By Object ID not CowCode + apiCreate.Handle("/cows", api.Middleware(http.HandlerFunc(cow.CowHandler))).Methods("GET") + apiCreate.Handle("/cows/new", api.Middleware(http.HandlerFunc(cow.NewCowHandler))).Methods("POST") + apiCreate.Handle("/cows/update/{cow_id}", api.Middleware(http.HandlerFunc(cow.UpdateCowHandler))).Methods("POST") // By Object ID not CowCode + + return r +} + +func (a *App) Initialize() error { + client, err := databases.NewClient(&a.Config) + if err != nil { + // if we fail to create a new database client, the kill the pod + zap.S().With(err).Error("failed to create new client") + return err + } + + a.dbHelper = databases.NewDatabase(&a.Config, client) + err = client.Connect() + if err != nil { + // if we fail to connect to the database, the kill the pod + zap.S().With(err).Error("failed to connect to database") + return err + } + zap.S().Info("SulliCartCheckout has connected to the database") + + // initialize api router + a.initializeRoutes() + return nil + +} + +func (a *App) initializeRoutes() { + a.Router = a.New() +} + +func healthCheckHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + b, _ := json.Marshal(models.HealthCheckResponse{ + Alive: true, + }) + _, _ = io.WriteString(w, string(b)) +} diff --git a/api/handlers/cow.go b/api/handlers/cow.go new file mode 100644 index 0000000..49ce6ca --- /dev/null +++ b/api/handlers/cow.go @@ -0,0 +1,142 @@ +package handlers + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/gorilla/mux" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + + "github.com/go-playground/validator/v10" + + "github.com/SowinskiBraeden/SulliCartShare/config" + "github.com/SowinskiBraeden/SulliCartShare/databases" + "github.com/SowinskiBraeden/SulliCartShare/models" +) + +var validate = validator.New() + +// Cow exported for testing purposes +type Cow struct { + DB databases.CowDatabase +} + +// CowHandler returns all cows +func (c Cow) CowHandler(w http.ResponseWriter, r *http.Request) { + dbResp, err := c.DB.Find(context.TODO(), bson.M{}) + if err != nil { + config.ErrorStatus("failed to get cows", http.StatusNotFound, w, err) + return + } + + // If len == 0 then we will just return an empty data object + if len(dbResp) == 0 { + dbResp = []models.Cow{} + } + b, err := json.Marshal(dbResp) + if err != nil { + config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err) + return + } + w.WriteHeader(http.StatusOK) + w.Write(b) +} + +// CowByIDHandler returns a cow by ID +func (c Cow) CowByIDHandler(w http.ResponseWriter, r *http.Request) { + cowID := mux.Vars(r)["cow_id"] + + cID, err := primitive.ObjectIDFromHex(cowID) + if err != nil { + config.ErrorStatus("failed to get objectID from Hex", http.StatusBadRequest, w, err) + return + } + + dbResp, err := c.DB.FindOne(context.Background(), bson.M{"_id": cID}) + if err != nil { + config.ErrorStatus("failed to get cow by ID", http.StatusNotFound, w, err) + return + } + + b, err := json.Marshal(dbResp) + if err != nil { + config.ErrorStatus("failed to marshal responce", http.StatusInternalServerError, w, err) + return + } + w.WriteHeader(http.StatusOK) + w.Write(b) +} + +// NewCowHandler inserts a new cow into the collection and returns a result and error +func (c Cow) NewCowHandler(w http.ResponseWriter, r *http.Request) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + var cowDetails models.CowDetails // Json data will represent the cow details model + defer cancel() + + //validate the request body + if err := json.NewDecoder(r.Body).Decode(&cowDetails); err != nil { + w.WriteHeader(http.StatusBadRequest) + response := models.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: map[string]interface{}{"error": err.Error()}} + json.NewEncoder(w).Encode(response) + return + } + + //use the validator library to validate required fields + if validationErr := validate.Struct(&cowDetails); validationErr != nil { + w.WriteHeader(http.StatusBadRequest) + response := models.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: map[string]interface{}{"error": validationErr.Error()}} + json.NewEncoder(w).Encode(response) + return + } + + fmt.Print(cowDetails) + + newCow := models.Cow{ + ID: primitive.NewObjectID().Hex(), + Details: cowDetails, + } + + result, err := c.DB.InsertOne(ctx, newCow) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + response := models.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"error": err.Error()}} + json.NewEncoder(w).Encode(response) + return + } + + w.WriteHeader(http.StatusCreated) + response := models.UserResponse{Status: http.StatusCreated, Message: "success", Data: map[string]interface{}{"result": result}} + json.NewEncoder(w).Encode(response) +} + +// TODO: fix all update handlers because they don't work + +// UpdateCowHandler gets updates the data for an existing cow and returns a result and error +func (c Cow) UpdateCowHandler(w http.ResponseWriter, r *http.Request) { + cowID := mux.Vars(r)["cow_id"] + + // TODO: Collect data to update from passed json + update := bson.M{ + "$set": bson.M{ + "cow.CowCode": "123", + }, + } + + dbResp, err := c.DB.UpdateOne(context.Background(), bson.M{"_id": cowID}, update) + if err != nil { + config.ErrorStatus("failed to update cow by ID", http.StatusNotFound, w, err) + return + } + + b, err := json.Marshal(dbResp) // TODO: get rid of this warning cause its bothering me + if err != nil { + config.ErrorStatus("failed ot marshal response", http.StatusInternalServerError, w, err) + return + } + w.WriteHeader(http.StatusOK) + w.Write(b) +} diff --git a/api/middleware.go b/api/middleware.go new file mode 100644 index 0000000..ceb2fc4 --- /dev/null +++ b/api/middleware.go @@ -0,0 +1,13 @@ +package api + +import ( + "net/http" +) + +// Middleware adds some basic header authentication around accessing the routes +func Middleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Do Authentication stuff? + next.ServeHTTP(w, r) + }) +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..d4397d5 --- /dev/null +++ b/config/config.go @@ -0,0 +1,69 @@ +package config + +import ( + "encoding/json" + "fmt" + "net/http" + "os" + "runtime" + + "go.uber.org/zap" + + "github.com/SowinskiBraeden/SulliCartShare/models" + "github.com/joho/godotenv" +) + +// Config holds the project config values +type Config struct { + URL string + DatabaseName string + BaseURL string + Port string +} + +// New sets up all config related services +func New() *Config { + + if runtime.GOOS == "windows" { + _ = godotenv.Load() + } + + //setup zap logger and replace default Logger + logger, err := setLogger(os.Getenv("ENV")) + if err != nil { + // if we get an error, we will just set the default to debug and move on + zap.S().With(err).Warn("issue setting logger") + } + defer logger.Sync() + _ = zap.ReplaceGlobals(logger) + + return &Config{ + URL: os.Getenv("DB_URI"), + DatabaseName: os.Getenv("DB_NAME"), + BaseURL: os.Getenv("BASE_URL"), + Port: os.Getenv("PORT"), + } +} + +// ErrorStatus is a useful function that will log, write http headers and body for a +// given message, status code and error +func ErrorStatus(message string, httpStatusCode int, w http.ResponseWriter, err error) { + zap.S().With(err).Error(message) + w.WriteHeader(httpStatusCode) + b, _ := json.Marshal(models.ErrorMessageResponse{Response: models.MessageError{Message: message, Error: err.Error()}}) + w.Write(b) + return +} + +// setLogger is a helper function to set the Logger based on the environment +func setLogger(env string) (*zap.Logger, error) { + if env == "production" { + return zap.NewProduction() + } else if env == "development" { + return zap.NewDevelopment() + } else if env == "local" { + return zap.NewExample(), nil + } else { + return zap.NewExample(), fmt.Errorf("cannot find ENV car so defaulting to debug level logging") + } +} diff --git a/databases/cow.go b/databases/cow.go new file mode 100644 index 0000000..a608a52 --- /dev/null +++ b/databases/cow.go @@ -0,0 +1,67 @@ +package databases + +// go generate: mockery --name CowDatabase + +import ( + "context" + + "github.com/SowinskiBraeden/SulliCartShare/models" +) + +const cowName = "cows" + +// TODO: fix all update handlers because they don't work + +// CowDatabase contains the methods to use with the cow database +type CowDatabase interface { + FindOne(ctx context.Context, filter interface{}) (*models.Cow, error) + Find(ctx context.Context, filter interface{}) ([]models.Cow, error) + InsertOne(ctx context.Context, filter interface{}) (mongoInsertOneResult, error) + UpdateOne(ctx context.Context, filter, document interface{}) (mongoUpdateResult, error) +} + +type cowDatabase struct { + db DatabaseHelper +} + +// NewCowDatabase initialized a new instance of a cow database with the provided db conntection +func NewCowDatabase(db DatabaseHelper) CowDatabase { + return &cowDatabase{ + db: db, + } +} + +func (c *cowDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Cow, error) { + cow := &models.Cow{} + err := c.db.Collection(cowName).FindOne(ctx, filter).Decode(&cow) + if err != nil { + return nil, err + } + return cow, nil +} + +func (c *cowDatabase) Find(ctx context.Context, filter interface{}) ([]models.Cow, error) { + var cows []models.Cow + err := c.db.Collection(cowName).Find(ctx, filter).Decode(&cows) + if err != nil { + return nil, err + } + return cows, nil +} + +// Returns the result (document id) and error +func (c *cowDatabase) InsertOne(ctx context.Context, document interface{}) (mongoInsertOneResult, error) { + result, err := c.db.Collection(cowName).InsertOne(ctx, document) + if err != nil { + return mongoInsertOneResult{}, err + } + return result, nil +} + +func (c *cowDatabase) UpdateOne(ctx context.Context, filter, update interface{}) (mongoUpdateResult, error) { + result, err := c.db.Collection(cowName).UpdateOne(ctx, filter, update) + if err != nil { + return mongoUpdateResult{}, err + } + return result, nil +} diff --git a/databases/database.go b/databases/database.go new file mode 100644 index 0000000..f62d8ac --- /dev/null +++ b/databases/database.go @@ -0,0 +1,150 @@ +package databases + +import ( + "context" + + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + + "github.com/SowinskiBraeden/SulliCartShare/config" +) + +// TODO: fix all update handlers because they don't work + +// DatabaseHelper contains the collection and client to be used to access the methods +// defined below +type DatabaseHelper interface { + Collection(name string) CollectionHelper + Client() ClientHelper +} + +// CollectionHelper contains all the methods defined for collection in this project +type CollectionHelper interface { + FindOne(context.Context, interface{}) SingleResultHelper + Find(context.Context, interface{}) CursorHelper + InsertOne(context.Context, interface{}) (mongoInsertOneResult, error) + UpdateOne(context.Context, interface{}, interface{}) (mongoUpdateResult, error) +} + +// SingleResultHelper contains a single method to decode the result +type SingleResultHelper interface { + Decode(v interface{}) error +} + +// CursorHelper contains a method to decode the cursor +type CursorHelper interface { + Decode(v interface{}) error +} + +// ClientHelper defined to help at client creation inside main.go +type ClientHelper interface { + Database(string) DatabaseHelper + Connect() error + StartSession() (mongo.Session, error) +} + +type mongoClient struct { + cl *mongo.Client +} + +type mongoDatabase struct { + db *mongo.Database +} + +type mongoCollection struct { + coll *mongo.Collection +} + +type mongoSingleResult struct { + sr *mongo.SingleResult +} + +type mongoCursor struct { + cr *mongo.Cursor +} + +type mongoInsertOneResult struct { + ir *mongo.InsertOneResult +} + +type mongoUpdateResult struct { + ur *mongo.UpdateResult +} + +type mongoSession struct { + mongo.Session +} + +// NewClient uses the values from the config and returns a mongo client +func NewClient(conf *config.Config) (ClientHelper, error) { + c, err := mongo.NewClient(options.Client().ApplyURI(conf.URL)) + + return &mongoClient{cl: c}, err +} + +// NewDatabase uses the client from NewClient and sets the database name +func NewDatabase(conf *config.Config, client ClientHelper) DatabaseHelper { + return client.Database(conf.DatabaseName) +} + +func (mc *mongoClient) Database(dbName string) DatabaseHelper { + db := mc.cl.Database(dbName) + return &mongoDatabase{db: db} +} + +func (mc *mongoClient) StartSession() (mongo.Session, error) { + session, err := mc.cl.StartSession() + return &mongoSession{session}, err +} + +func (mc *mongoClient) Connect() error { + return mc.cl.Connect(context.TODO()) // use context.TODO() instead of nil cause good practice ¯\_(ツ)_/¯ +} + +func (md *mongoDatabase) Collection(colName string) CollectionHelper { + collection := md.db.Collection(colName) + return &mongoCollection{coll: collection} +} + +func (md *mongoDatabase) Client() ClientHelper { + client := md.db.Client() + return &mongoClient{cl: client} +} + +func (mc *mongoCollection) FindOne(ctx context.Context, filter interface{}) SingleResultHelper { + singleResult := mc.coll.FindOne(ctx, filter) + return &mongoSingleResult{sr: singleResult} +} + +func (mc *mongoCollection) Find(ctx context.Context, filter interface{}) CursorHelper { + cursor, _ := mc.coll.Find(ctx, filter) + return &mongoCursor{cr: cursor} +} + +func (mc *mongoCollection) InsertOne(ctx context.Context, document interface{}) (mongoInsertOneResult, error) { + insertOneResult, err := mc.coll.InsertOne(ctx, document) + if err != nil { + return mongoInsertOneResult{}, err + } + return mongoInsertOneResult{ir: insertOneResult}, nil +} + +func (mc *mongoCollection) UpdateOne(ctx context.Context, filter, update interface{}) (mongoUpdateResult, error) { + updateOneResult, err := mc.coll.UpdateOne(ctx, filter, update) + if err != nil { + return mongoUpdateResult{}, err + } + return mongoUpdateResult{ur: updateOneResult}, nil +} + +func (sr *mongoSingleResult) Decode(v interface{}) error { + return sr.sr.Decode(v) +} + +func (cr *mongoCursor) Decode(v interface{}) error { + return cr.All(context.Background(), v) +} + +func (cr *mongoCursor) All(ctx context.Context, results interface{}) error { + return cr.cr.All(ctx, results) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..21588da --- /dev/null +++ b/go.mod @@ -0,0 +1,31 @@ +module github.com/SowinskiBraeden/SulliCartShare + +go 1.19 + +require ( + github.com/go-playground/validator/v10 v10.11.1 + github.com/gorilla/mux v1.8.0 + github.com/joho/godotenv v1.4.0 + go.mongodb.org/mongo-driver v1.10.2 + go.uber.org/zap v1.23.0 +) + +require ( + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/golang/snappy v0.0.1 // indirect + github.com/klauspost/compress v1.13.6 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.1 // indirect + github.com/xdg-go/stringprep v1.0.3 // indirect + github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect + golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect + golang.org/x/text v0.3.7 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..56e6361 --- /dev/null +++ b/go.sum @@ -0,0 +1,91 @@ +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +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.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +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/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +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/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +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/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.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/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +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.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +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= +go.mongodb.org/mongo-driver v1.10.2 h1:4Wk3cnqOrQCn0P92L3/mmurMxzdvWWs5J9jinAVKD+k= +go.mongodb.org/mongo-driver v1.10.2/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +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/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +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/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..7510b9a --- /dev/null +++ b/main.go @@ -0,0 +1,25 @@ +package main + +import ( + "log" + "net/http" + + "go.uber.org/zap" + + "github.com/SowinskiBraeden/SulliCartShare/api/handlers" + "github.com/SowinskiBraeden/SulliCartShare/config" +) + +func main() { + a := handlers.App{} + a.Config = *config.New() + + err := a.Initialize() //initialize database and router + if err != nil { + zap.S().With(err).Error("error calling initialize") + return + } + + zap.S().Infow("SulliCartShare is up and running", "url", a.Config.BaseURL, "port", a.Config.Port) + log.Fatal(http.ListenAndServe(":"+a.Config.Port, a.Router)) +} diff --git a/models/cow.go b/models/cow.go new file mode 100644 index 0000000..c1be719 --- /dev/null +++ b/models/cow.go @@ -0,0 +1,13 @@ +package models + +// Cow holds the structure for the cow collection in mongo +type Cow struct { + ID string `json:"_id" bson:"_id"` + Details CowDetails `json:"cow" bson:"cow"` +} + +// CowDetails holds teh structure for the inner cow structure as +// defined in the cow collection in mongo +type CowDetails struct { + CowCode string `json:"CowCode" bson:"CowCode"` +} diff --git a/models/error.go b/models/error.go new file mode 100644 index 0000000..3c0f13a --- /dev/null +++ b/models/error.go @@ -0,0 +1,12 @@ +package models + +// ErrorMessageResponse returns the error message response struct +type ErrorMessageResponse struct { + Response MessageError +} + +// MessageError contains the inner details for the error message response +type MessageError struct { + Message string + Error string +} diff --git a/models/responses.go b/models/responses.go new file mode 100644 index 0000000..e86ccf7 --- /dev/null +++ b/models/responses.go @@ -0,0 +1,13 @@ +package models + +// HealthCheckResponse returns the health check response duh +type HealthCheckResponse struct { + Alive bool `json:"alive"` +} + +// UserResponse is a general response structure with a status, message and optional json data +type UserResponse struct { + Status int `json:"status"` + Message string `json:"message"` + Data map[string]interface{} `json:"data"` +}