Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
mkilc committed May 18, 2023
0 parents commit d7abe02
Show file tree
Hide file tree
Showing 28 changed files with 2,046 additions and 0 deletions.
33 changes: 33 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
vendor/

# IDEs and editors
/.idea
.idea

# IDE - VSCode
.vscode/*

#System Files
.DS_Store
Thumbs.db

.env
app.env
cred.json

#Docker data
data
19 changes: 19 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM golang:1.19-alpine as base_build
ENV CGO_ENABLED=0

RUN mkdir /app
WORKDIR /app

COPY go.mod .
COPY go.sum .
RUN go mod download

COPY .. .

RUN go build -o ./cmd/main ./cmd/main.go

CMD ["/app/cmd/main"]

# FROM scratch
# COPY --from=base_build ./app .
# CMD ["./compiled"]
47 changes: 47 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.SILENT: deps lint clean gen-mock test build run run-docker

CYAN=\033[0;36m
RESET=\033[0m

pprint = echo -e "${CYAN}::>${RESET} ${1}"
completed = $(call pprint,Completed!)

deps:
$(call pprint, Downloading go libraries...)
go mod download
$(call completed)

lint:
$(call pprint, Runnning linter...)
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.52.2
./bin/golangci-lint --version
./bin/golangci-lint run ./...
$(call completed)

clean:
$(call pprint,Cleaning up...)
rm -rf ./bin
find . -name "mocks" | xargs rm -rf {}
find . -name ".cover.out" | xargs rm -rf {}
$(call completed)

gen-mock: clean deps
$(call pprint, Generating mocks for tests...)
go get github.com/golang/mock/mockgen
go generate ./...
$(call completed)

test:
$(call pprint, Runnning tests...)
go test ./... -coverprofile .cover.out
$(call completed)

build: clean deps
$(call pprint, Building app...)
GOOS=linux GOARCH=amd64 go build -o airdao-mobile-api ./cmd/main.go
$(call completed)

run:
$(call pprint, Running app...)
go run ./cmd/main.go
$(call completed)
149 changes: 149 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package main

import (
"airdao-mobile-api/config"
"airdao-mobile-api/pkg/firebase"
cloudmessaging "airdao-mobile-api/pkg/firebase/cloud-messaging"
"airdao-mobile-api/pkg/logger"
"airdao-mobile-api/pkg/mongodb"
"airdao-mobile-api/services/health"
"airdao-mobile-api/services/watcher"
"context"
"errors"
"log"
"os/signal"
"syscall"

"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"go.uber.org/zap"
)

func main() {
cfg, err := config.GetConfig()
if err != nil {
log.Fatalf("failed to load config: %v", err)
}

// Init logger
newLogger, err := logger.NewLogger(cfg.Environment)
if err != nil {
log.Fatalf("can't create logger: %v", err)
}

zapLogger, err := newLogger.SetupZapLogger()
if err != nil {
log.Fatalf("can't setup zap logger: %v", err)
}
defer func(zapLogger *zap.SugaredLogger) {
err := zapLogger.Sync()
if err != nil && !errors.Is(err, syscall.ENOTTY) {
log.Fatalf("can't setup zap logger: %v", err)
}
}(zapLogger)

// Connect to database
db, ctx, cancel, err := mongodb.NewConnection(cfg)
if err != nil {
zapLogger.Fatalf("failed to connect to mongodb: %s", err)
}
defer mongodb.Close(db, ctx, cancel)

// Ping db
err = mongodb.Ping(db, ctx)
if err != nil {
zapLogger.Fatal(err)
}
zapLogger.Info("DB connected successfully")

// Firebase
firebaseClient, err := firebase.NewClient(cfg.Firebase.CredPath)
if err != nil {
zapLogger.Fatalf("failed to create firebase messaging client - %v", err)
}

cloudMessagingClient, err := firebaseClient.CreateCloudMessagingClient()
if err != nil {
zapLogger.Fatalf("failed to create firebase cloud messaging client - %v", err)
}

// Firebase message
cloudMessagingService, err := cloudmessaging.NewCloudMessagingService(cloudMessagingClient, cfg.Firebase.AndroidChannelName)
if err != nil {
zapLogger.Fatalf("failed to create firebase message service - %v", err)
}

// Repository
watcherRepository, err := watcher.NewRepository(db, cfg.MongoDb.MongoDbName, zapLogger)
if err != nil {
zapLogger.Fatalf("failed to create watcher repository - %v", err)
}

// Services
watcherService, err := watcher.NewService(watcherRepository, cloudMessagingService, zapLogger)
if err != nil {
zapLogger.Fatalf("failed to create watcher service - %v", err)
}

if err := watcherService.Init(context.Background()); err != nil {
zapLogger.Fatalf("failed to init watchers - %v", err)
}

// Handlers
healthHandler := health.NewHandler()

watcherHandler, err := watcher.NewHandler(watcherService)
if err != nil {
zapLogger.Fatalf("failed to create watcher handler - %v", err)
}

// Create config variable
config := fiber.Config{
ServerHeader: "AIRDAO-Mobile-Api", // add custom server header
}

// Create fiber app
app := fiber.New(config)

// Init cors
app.Use(cors.New(cors.Config{
AllowOrigins: "*",
AllowHeaders: "Origin, Content-Type, Accept, Content-Length, Accept-Encoding, Authorization",
AllowMethods: "GET, POST, PUT, DELETE, OPTIONS",
AllowCredentials: true,
}))

// Set-up Route
app.Route("/api/v1", func(router fiber.Router) {
healthHandler.SetupRoutes(router)
watcherHandler.SetupRoutes(router)
})

// Handle 404 page
app.Use(func(c *fiber.Ctx) error {
return c.Status(fiber.StatusNotFound).JSON(map[string]string{"error": "page not found"})
})

// Start the server
go func() {
if err := app.Listen(":" + cfg.Port); err != nil {
zapLogger.Fatal(err)
}
}()

zapLogger.Infof("Server started on port %v\n", cfg.Port)

// Create a context that will be used to gracefully shut down the server
ctx, cancel = signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()

// Wait for the termination signal
<-ctx.Done()

// Perform the graceful shutdown by closing the server
if err := app.Shutdown(); err != nil {
zapLogger.Fatal(err)
}

zapLogger.Info("Server gracefully stopped")
}
47 changes: 47 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package config

import (
"sync"

"github.com/joho/godotenv"
"github.com/kelseyhightower/envconfig"
)

type Config struct {
Environment string `required:"true" envconfig:"APP_ENV"`
Port string `required:"true" envconfig:"PORT"`

MongoDb
Firebase
}

type MongoDb struct {
MongoDbName string `required:"true" envconfig:"MONGO_DB_NAME"`
MongoDbUrl string `required:"true" envconfig:"MONGO_DB_URL"`
}

type Firebase struct {
CredPath string `required:"true" envconfig:"FIREBASE_CRED_PATH"`
AndroidChannelName string `required:"true" envconfig:"ANDROID_CHANNEL_NAME"`
}

var (
once sync.Once
config *Config
)

func GetConfig() (*Config, error) {
var err error
once.Do(func() {
var cfg Config
_ = godotenv.Load(".env")

if err = envconfig.Process("", &cfg); err != nil {
return
}

config = &cfg
})

return config, err
}
83 changes: 83 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package config_test

import (
"os"
"reflect"
"testing"

"airdao-mobile-api/config"
)

func TestInit(t *testing.T) {
type env struct {
environment string
port string
mongoDbName string
mongoDbUrl string
firebaseCredPath string
androidChannel string
}

type args struct {
env env
}

setEnv := func(env env) {
os.Setenv("APP_ENV", env.environment)
os.Setenv("PORT", env.port)
os.Setenv("MONGO_DB_NAME", env.mongoDbName)
os.Setenv("MONGO_DB_URL", env.mongoDbUrl)
os.Setenv("FIREBASE_CRED_PATH", env.firebaseCredPath)
os.Setenv("ANDROID_CHANNEL_NAME", env.androidChannel)
}

tests := []struct {
name string
args args
want *config.Config
wantError bool
}{
{
name: "Test config file!",
args: args{
env: env{
environment: "development",
port: "5000",
mongoDbName: "example",
mongoDbUrl: "http://127.0.0.1",
firebaseCredPath: "./example.json",
androidChannel: "example",
},
},
want: &config.Config{
Environment: "development",
Port: "5000",
MongoDb: config.MongoDb{
MongoDbName: "example",
MongoDbUrl: "http://127.0.0.1",
},
Firebase: config.Firebase{
CredPath: "./example.json",
AndroidChannelName: "example",
},
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
setEnv(test.args.env)

got, err := config.GetConfig()
if (err != nil) != test.wantError {
t.Errorf("Init() error = %s, wantErr %v", err, test.wantError)

return
}

if !reflect.DeepEqual(got, test.want) {
t.Errorf("Init() got = %v, want %v", got, test.want)
}
})
}
}
Loading

0 comments on commit d7abe02

Please sign in to comment.