From 77922994a9893d89daa877d7388bad45035503d5 Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Tue, 25 Jun 2024 00:51:07 +0700 Subject: [PATCH 01/42] feat: ci --- .air.toml | 51 +++++++++++++++++++ .env.template | 8 +++ .../pull_request_template.md | 21 ++++++++ .github/workflows/build-deploy.yml | 42 +++++++++++++++ .github/workflows/run-unit-test.yml | 38 ++++++++++++++ .github/workflows/test-build.yml | 32 ++++++++++++ .gitignore | 34 +++++++++++++ Dockerfile | 22 ++++++++ docker-compose.yaml | 21 ++++++++ 9 files changed, 269 insertions(+) create mode 100644 .air.toml create mode 100644 .env.template create mode 100644 .github/PULL_REQUEST_TEMPLATE/pull_request_template.md create mode 100644 .github/workflows/build-deploy.yml create mode 100644 .github/workflows/run-unit-test.yml create mode 100644 .github/workflows/test-build.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 docker-compose.yaml diff --git a/.air.toml b/.air.toml new file mode 100644 index 0000000..7648371 --- /dev/null +++ b/.air.toml @@ -0,0 +1,51 @@ +root = "." +testdata_dir = "testdata" +tmp_dir = "tmp" + +[build] + args_bin = [] + bin = "./tmp/main" + cmd = "go build -o ./tmp/main ./cmd/main.go" + delay = 1000 + exclude_dir = ["assets", "tmp", "vendor", "testdata"] + exclude_file = [] + exclude_regex = ["_test.go"] + exclude_unchanged = false + follow_symlink = false + full_bin = "" + include_dir = [] + include_ext = ["go", "tpl", "tmpl", "html"] + include_file = [] + kill_delay = "0s" + log = "build-errors.log" + poll = false + poll_interval = 0 + post_cmd = [] + pre_cmd = [] + rerun = false + rerun_delay = 500 + send_interrupt = false + stop_on_error = false + +[color] + app = "" + build = "yellow" + main = "magenta" + runner = "green" + watcher = "cyan" + +[log] + main_only = false + time = false + +[misc] + clean_on_exit = false + +[proxy] + app_port = 0 + enabled = false + proxy_port = 0 + +[screen] + clear_on_rebuild = false + keep_scroll = true \ No newline at end of file diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..570934f --- /dev/null +++ b/.env.template @@ -0,0 +1,8 @@ +APP_PORT=300x +APP_ENV=development +APP_MAX_FILE_SIZE_MB=1024 + +DB_URL=postgres://root:1234@localhost:5432/rpkm67_db + +STORE_BUCKET_NAME=rpkm67-bucket +STORE_REGION=us-east-1 \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 100644 index 0000000..3833a16 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1,21 @@ +## Change made + +- [ ] New features +- [ ] Bug fixes +- [ ] Breaking changes + +## Describe what you have done + +- + +### New Features + +- + +### Fix + +- + +### Others + +- diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml new file mode 100644 index 0000000..a1d012b --- /dev/null +++ b/.github/workflows/build-deploy.yml @@ -0,0 +1,42 @@ +name: Build + +on: + workflow_dispatch: + push: + branches: + - main + - dev + tags: + - v* + +env: + IMAGE_NAME: ghcr.io/${{ github.repository }} + +jobs: + build: + name: Build + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to the Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + + - name: Build and Push Docker Image + uses: docker/build-push-action@v3 + with: + push: true + tags: ${{ env.IMAGE_NAME }}:${{ github.ref_type == 'tag' && github.ref_name || github.sha }} + cache-from: type=registry,ref=${{ env.IMAGE_NAME }}:buildcache + cache-to: type=registry,ref=${{ env.IMAGE_NAME }}:buildcache,mode=max +# docker pull --platform linux/x86_64 diff --git a/.github/workflows/run-unit-test.yml b/.github/workflows/run-unit-test.yml new file mode 100644 index 0000000..f5be40f --- /dev/null +++ b/.github/workflows/run-unit-test.yml @@ -0,0 +1,38 @@ +name: "Pull request/Push: Run unit test" + +on: + pull_request: + branches: + - dev + - master + - main + - beta + push: + branches: + - dev + - master + - main + - beta + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.22 + + - name: Download dependencies + run: go mod download + + - name: Vet + run: | + go vet ./... + + - name: Test + run: | + go test -v -coverpkg ./internal/... -coverprofile coverage.out -covermode count ./internal/... + go tool cover -func="./coverage.out" diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml new file mode 100644 index 0000000..762f358 --- /dev/null +++ b/.github/workflows/test-build.yml @@ -0,0 +1,32 @@ +name: Lint + +on: + push: + branches: + - main + - dev + tags: + - v* + pull_request: + +permissions: + contents: read + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: "1.22.4" + cache: false + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.55 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a4b79a --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +### Go template +# 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/ + +# config file +config.yaml +.env +.env.prod + +# idea +.idea + +# volumes +volumes + +# coverage report +coverage.out +coverage.html + +tmp +staff.json \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0b2e2f5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM golang:1.22.4-alpine3.20 as builder +WORKDIR /app + +COPY go.mod go.sum ./ + +RUN go mod download + +COPY . . + +RUN go build -o server ./cmd/main.go + + +FROM alpine AS runner +WORKDIR /app + +COPY --from=builder /app/server ./ + +ENV GO_ENV production + +EXPOSE 3000 + +CMD ["./server"] \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..3f27365 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,21 @@ +version: "3.9" + +services: + db: + image: postgres:15.1-alpine3.17 + container_name: db + restart: unless-stopped + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: "1234" + POSTGRES_DB: rpkm67_db + networks: + - rpkm67 + volumes: + - ./volumes/postgres:/var/lib/postgresql/data + ports: + - "5432:5432" + +networks: + rpkm67: + name: rpkm67 \ No newline at end of file From f1fb72bf1a5c587b88d32b21fe16811d3f5ec667 Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Tue, 25 Jun 2024 00:51:16 +0700 Subject: [PATCH 02/42] feat: package --- Makefile | 21 +++++++++++++++++++++ go.mod | 33 +++++++++++++++++++++++++++++++++ go.sum | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 Makefile create mode 100644 go.mod create mode 100644 go.sum diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4e70aa5 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +docker: + docker-compose up + +server: + go run cmd/main.go + +watch: + air +mock-gen: + +test: + go vet ./... + go test -v -coverpkg ./internal/... -coverprofile coverage.out -covermode count ./internal/... + go tool cover -func=coverage.out + go tool cover -html=coverage.out -o coverage.html + +proto: + go get github.com/isd-sgcu/rpkm67-go-proto@latest + +swagger: + swag init -d ./internal/file -g ../../cmd/main.go -o ./docs -md ./docs/markdown --parseDependency --parseInternal \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..be8fb01 --- /dev/null +++ b/go.mod @@ -0,0 +1,33 @@ +module github.com/isd-sgcu/rpkm67-store + +go 1.21.5 + +require ( + github.com/joho/godotenv v1.5.1 + go.uber.org/zap v1.27.0 +) + +require ( + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.5.5 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/protobuf v1.34.2 // indirect +) + +require ( + github.com/google/uuid v1.6.0 + github.com/isd-sgcu/rpkm67-go-proto v0.1.3 + go.uber.org/multierr v1.10.0 // indirect + google.golang.org/grpc v1.64.0 + gorm.io/driver/postgres v1.5.9 + gorm.io/gorm v1.25.10 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..58ded4a --- /dev/null +++ b/go.sum @@ -0,0 +1,55 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/isd-sgcu/rpkm67-go-proto v0.1.3 h1:9+wrvKUUY5GDot1SmU9HtUgf8JzhPLHWKtCfmBQU+Yw= +github.com/isd-sgcu/rpkm67-go-proto v0.1.3/go.mod h1:Z5SYz5kEe4W+MdqPouF0zEOiaqvg+s9I1S5d0q6e+Jw= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= +gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= +gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= From efa6bacfb70f51aedf9076a5e2872b064465639d Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Tue, 25 Jun 2024 00:51:30 +0700 Subject: [PATCH 03/42] feat: config --- cmd/main.go | 129 ++++++++++++++++++++++++++++++++++++++ config/config.go | 67 ++++++++++++++++++++ database/db.connection.go | 29 +++++++++ logger/logger.go | 18 ++++++ 4 files changed, 243 insertions(+) create mode 100644 cmd/main.go create mode 100644 config/config.go create mode 100644 database/db.connection.go create mode 100644 logger/logger.go diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..53c3174 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,129 @@ +package main + +import ( + "context" + "fmt" + "net" + "os" + "os/signal" + "sync" + "syscall" + "time" + + objectProto "github.com/isd-sgcu/rpkm67-go-proto/rpkm67/store/object/v1" + "github.com/isd-sgcu/rpkm67-store/config" + "github.com/isd-sgcu/rpkm67-store/database" + "github.com/isd-sgcu/rpkm67-store/internal/object" + "github.com/isd-sgcu/rpkm67-store/logger" + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/health" + "google.golang.org/grpc/health/grpc_health_v1" + "google.golang.org/grpc/reflection" +) + +func main() { + conf, err := config.LoadConfig() + if err != nil { + panic(fmt.Sprintf("Failed to load config: %v", err)) + } + + logger := logger.New(conf) + + db, err := database.InitDatabase(&conf.DB, conf.App.IsDevelopment()) + if err != nil { + panic(fmt.Sprintf("Failed to connect to database: %v", err)) + } + + objectRepo := object.NewRepository(db) + + listener, err := net.Listen("tcp", fmt.Sprintf(":%v", conf.App.Port)) + if err != nil { + panic(fmt.Sprintf("Failed to listen: %v", err)) + } + + grpcServer := grpc.NewServer() + grpc_health_v1.RegisterHealthServer(grpcServer, health.NewServer()) + objectProto.RegisterObjectServiceServer(grpcServer, object.NewService(objectRepo, logger)) + + reflection.Register(grpcServer) + + go func() { + logger.Sugar().Infof("RPKM67 Auth starting at port %v", conf.App.Port) + + if err := grpcServer.Serve(listener); err != nil { + logger.Fatal("Failed to start RPKM67 Auth service", zap.Error(err)) + } + }() + + wait := gracefulShutdown(context.Background(), 2*time.Second, logger, map[string]operation{ + "server": func(ctx context.Context) error { + grpcServer.GracefulStop() + return nil + }, + "database": func(ctx context.Context) error { + sqlDB, err := db.DB() + if err != nil { + return nil + } + return sqlDB.Close() + }, + }) + + <-wait + + grpcServer.GracefulStop() + logger.Info("Closing the listener") + listener.Close() + logger.Info("RPKM67 Store service has been shutdown gracefully") +} + +type operation func(ctx context.Context) error + +func gracefulShutdown(ctx context.Context, timeout time.Duration, log *zap.Logger, ops map[string]operation) <-chan struct{} { + wait := make(chan struct{}) + go func() { + s := make(chan os.Signal, 1) + + signal.Notify(s, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) + sig := <-s + + log.Named("graceful shutdown").Sugar(). + Infof("got signal \"%v\" shutting down service", sig) + + timeoutFunc := time.AfterFunc(timeout, func() { + log.Named("graceful shutdown").Sugar(). + Errorf("timeout %v ms has been elapsed, force exit", timeout.Milliseconds()) + os.Exit(0) + }) + + defer timeoutFunc.Stop() + + var wg sync.WaitGroup + + for key, op := range ops { + wg.Add(1) + innerOp := op + innerKey := key + go func() { + defer wg.Done() + + log.Named("graceful shutdown").Sugar(). + Infof("cleaning up: %v", innerKey) + if err := innerOp(ctx); err != nil { + log.Named("graceful shutdown").Sugar(). + Errorf("%v: clean up failed: %v", innerKey, err.Error()) + return + } + + log.Named("graceful shutdown").Sugar(). + Infof("%v was shutdown gracefully", innerKey) + }() + } + + wg.Wait() + close(wait) + }() + + return wait +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..d509f83 --- /dev/null +++ b/config/config.go @@ -0,0 +1,67 @@ +package config + +import ( + "os" + "strconv" + + "github.com/joho/godotenv" +) + +type App struct { + Port string + Env string + MaxFileSize int64 +} + +type DB struct { + Url string +} + +type Store struct { + BucketName string `mapstructure:"bucket_name"` + Region string `mapstructure:"region"` +} + +type Config struct { + App App `mapstructure:"app"` + Store Store `mapstructure:"store"` + DB DB `mapstructure:"db"` +} + +func LoadConfig() (config *Config, err error) { + if os.Getenv("APP_ENV") == "" { + err := godotenv.Load(".env") + if err != nil { + return nil, err + } + } + + maxFileSizeMB, err := strconv.ParseInt(os.Getenv("APP_MAX_FILE_SIZE_MB"), 10, 64) + if err != nil { + return nil, err + } + appConfig := App{ + Port: os.Getenv("APP_PORT"), + Env: os.Getenv("APP_ENV"), + MaxFileSize: maxFileSizeMB, + } + + storeConfig := Store{ + BucketName: os.Getenv("STORE_BUCKET_NAME"), + Region: os.Getenv("STORE_REGION"), + } + + dbConfig := DB{ + Url: os.Getenv("DB_URL"), + } + + return &Config{ + App: appConfig, + Store: storeConfig, + DB: dbConfig, + }, nil +} + +func (a *App) IsDevelopment() bool { + return a.Env == "development" +} diff --git a/database/db.connection.go b/database/db.connection.go new file mode 100644 index 0000000..4f92d9c --- /dev/null +++ b/database/db.connection.go @@ -0,0 +1,29 @@ +package database + +import ( + "github.com/isd-sgcu/rpkm67-store/config" + "github.com/isd-sgcu/rpkm67-store/internal/model" + "gorm.io/driver/postgres" + "gorm.io/gorm" + gormLogger "gorm.io/gorm/logger" +) + +func InitDatabase(conf *config.DB, isDebug bool) (db *gorm.DB, err error) { + gormConf := &gorm.Config{TranslateError: true} + + if !isDebug { + gormConf.Logger = gormLogger.Default.LogMode(gormLogger.Silent) + } + + db, err = gorm.Open(postgres.Open(conf.Url), gormConf) + if err != nil { + return nil, err + } + + err = db.AutoMigrate(&model.Object{}) + if err != nil { + return nil, err + } + + return +} diff --git a/logger/logger.go b/logger/logger.go new file mode 100644 index 0000000..e0eb338 --- /dev/null +++ b/logger/logger.go @@ -0,0 +1,18 @@ +package logger + +import ( + "github.com/isd-sgcu/rpkm67-store/config" + "go.uber.org/zap" +) + +func New(conf *config.Config) *zap.Logger { + var logger *zap.Logger + + if conf.App.IsDevelopment() { + logger = zap.Must(zap.NewDevelopment()) + } else { + logger = zap.Must(zap.NewProduction()) + } + + return logger +} From 50ce1ca778338081f42459e2a31a3b87d660d7ae Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Tue, 25 Jun 2024 00:51:44 +0700 Subject: [PATCH 04/42] feat: model --- internal/model/common.model.go | 21 +++++++++++++++++++++ internal/model/object.model.go | 12 ++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 internal/model/common.model.go create mode 100644 internal/model/object.model.go diff --git a/internal/model/common.model.go b/internal/model/common.model.go new file mode 100644 index 0000000..6c15b42 --- /dev/null +++ b/internal/model/common.model.go @@ -0,0 +1,21 @@ +package model + +import ( + "time" + + "github.com/google/uuid" + "gorm.io/gorm" +) + +type Base struct { + ID uuid.UUID `json:"id" gorm:"primary_key"` + CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;autoCreateTime:nano"` + UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;autoUpdateTime:nano"` + DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index;type:timestamp"` +} + +func (b *Base) BeforeCreate(_ *gorm.DB) error { + b.ID = uuid.New() + + return nil +} diff --git a/internal/model/object.model.go b/internal/model/object.model.go new file mode 100644 index 0000000..8066721 --- /dev/null +++ b/internal/model/object.model.go @@ -0,0 +1,12 @@ +package model + +import ( + "github.com/google/uuid" +) + +type Object struct { + Base + ID uuid.UUID `json:"id" gorm:"primary_key"` + ImageUrl string `json:"image_url" gorm:"mediumtext"` + ObjectKey string `json:"object_key" gorm:"mediumtext"` +} From c8fa578b9c2479074281647d33d3fe75be17b754 Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Tue, 25 Jun 2024 00:51:51 +0700 Subject: [PATCH 05/42] feat: internal --- internal/object/object.repository.go | 32 +++++++++++++++++++++++++ internal/object/object.service.go | 35 ++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 internal/object/object.repository.go create mode 100644 internal/object/object.service.go diff --git a/internal/object/object.repository.go b/internal/object/object.repository.go new file mode 100644 index 0000000..8ad7dce --- /dev/null +++ b/internal/object/object.repository.go @@ -0,0 +1,32 @@ +package object + +import ( + "github.com/isd-sgcu/rpkm67-store/internal/model" + "gorm.io/gorm" +) + +type Repository interface { + FindByKey(key string, result *model.Object) error + Upload(file *model.Object) error + DeleteByKey(key string) error +} + +type repositoryImpl struct { + db *gorm.DB +} + +func NewRepository(db *gorm.DB) Repository { + return &repositoryImpl{db: db} +} + +func (r *repositoryImpl) DeleteByKey(key string) error { + return r.db.Where("object_key = ?", key).Delete(&model.Object{}).Error +} + +func (r *repositoryImpl) FindByKey(key string, result *model.Object) error { + return r.db.Where("object_key = ?", key).First(result).Error +} + +func (r *repositoryImpl) Upload(file *model.Object) error { + return r.db.Create(&file).Error +} diff --git a/internal/object/object.service.go b/internal/object/object.service.go new file mode 100644 index 0000000..e34df44 --- /dev/null +++ b/internal/object/object.service.go @@ -0,0 +1,35 @@ +package object + +import ( + "context" + + proto "github.com/isd-sgcu/rpkm67-go-proto/rpkm67/store/object/v1" + "go.uber.org/zap" +) + +type Service interface { + proto.ObjectServiceServer +} + +type serviceImpl struct { + proto.UnimplementedObjectServiceServer + repo Repository + log *zap.Logger + // client +} + +func NewService(repo Repository, log *zap.Logger) proto.ObjectServiceServer { + return &serviceImpl{repo: repo, log: log} +} + +func (s *serviceImpl) Upload(_ context.Context, req *proto.UploadObjectRequest) (*proto.UploadObjectResponse, error) { + return nil, nil +} + +func (s *serviceImpl) FindByKey(_ context.Context, req *proto.FindByKeyObjectRequest) (*proto.FindByKeyObjectResponse, error) { + return nil, nil +} + +func (s *serviceImpl) DeleteByKey(_ context.Context, req *proto.DeleteByKeyObjectRequest) (*proto.DeleteByKeyObjectResponse, error) { + return nil, nil +} From ef470410937152cf2526c9e63c9c2ff5ccc099ab Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Tue, 25 Jun 2024 17:49:46 +0700 Subject: [PATCH 06/42] fix: config --- config/config.go | 10 ++++++++++ constant/error.constant.go | 11 +++++++++++ go.mod | 10 +++++++++- go.sum | 39 ++++++++++++++++++++++++++++++-------- 4 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 constant/error.constant.go diff --git a/config/config.go b/config/config.go index d509f83..40d3261 100644 --- a/config/config.go +++ b/config/config.go @@ -18,8 +18,13 @@ type DB struct { } type Store struct { + Endpoint string `mapstructure:"endpoint"` + AccessKey string `mapstructure:"access_key"` + SecretKey string `mapstructure:"secret_key"` + UseSSL bool `mapstructure:"use_ssl"` BucketName string `mapstructure:"bucket_name"` Region string `mapstructure:"region"` + Token string `mapstructure:"token"` } type Config struct { @@ -49,6 +54,11 @@ func LoadConfig() (config *Config, err error) { storeConfig := Store{ BucketName: os.Getenv("STORE_BUCKET_NAME"), Region: os.Getenv("STORE_REGION"), + Endpoint: os.Getenv("STORE_ENDPOINT"), + AccessKey: os.Getenv("STORE_ACCESS_KEY"), + SecretKey: os.Getenv("STORE_SECRET_KEY"), + UseSSL: os.Getenv("STORE_USE_SSL") == "true", + Token: os.Getenv("STORE_TOKEN"), } dbConfig := DB{ diff --git a/constant/error.constant.go b/constant/error.constant.go new file mode 100644 index 0000000..017f61f --- /dev/null +++ b/constant/error.constant.go @@ -0,0 +1,11 @@ +package constant + +const InvalidTokenErrorMessage = "Invalid token" +const InternalServerErrorMessage = "Internal server error" + +const FileNotFoundErrorMessage = "File cannot be empty" +const InvalidFileTypeErrorMessage = "Invalid file type" +const InvalidFileSizeErrorMessage = "Invalid file size" +const InvalidFileErrorMessage = "Invalid file" +const InvalidFileKeyErrorMessage = "Invalid file key" +const InvalidFileUrlErrorMessage = "Invalid file url" diff --git a/go.mod b/go.mod index be8fb01..0ee5e18 100644 --- a/go.mod +++ b/go.mod @@ -8,24 +8,32 @@ require ( ) require ( + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.5.5 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/klauspost/compress v1.17.6 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/minio/md5-simd v1.1.2 // indirect + github.com/rs/xid v1.5.0 // indirect golang.org/x/crypto v0.21.0 // indirect - golang.org/x/net v0.22.0 // indirect + golang.org/x/net v0.23.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect ) require ( github.com/google/uuid v1.6.0 github.com/isd-sgcu/rpkm67-go-proto v0.1.3 + github.com/minio/minio-go/v7 v7.0.72 go.uber.org/multierr v1.10.0 // indirect google.golang.org/grpc v1.64.0 gorm.io/driver/postgres v1.5.9 diff --git a/go.sum b/go.sum index 58ded4a..749e965 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,12 @@ 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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/isd-sgcu/rpkm67-go-proto v0.1.3 h1:9+wrvKUUY5GDot1SmU9HtUgf8JzhPLHWKtCfmBQU+Yw= @@ -17,24 +25,37 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= +github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= +github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.0.72 h1:ZSbxs2BfJensLyHdVOgHv+pfmvxYraaUy07ER04dWnA= +github.com/minio/minio-go/v7 v7.0.72/go.mod h1:4yBA8v80xGA30cfM3fz0DKYMXunWl/AV/6tWEs9ryzo= +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/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= 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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= @@ -43,12 +64,14 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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= gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= From 98b2fab82eda807ac8e7b4e531aa5157a70392b9 Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Tue, 25 Jun 2024 17:49:55 +0700 Subject: [PATCH 07/42] fix: main.go --- cmd/main.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/cmd/main.go b/cmd/main.go index 53c3174..771ae0e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net" + "net/http" "os" "os/signal" "sync" @@ -13,8 +14,11 @@ import ( objectProto "github.com/isd-sgcu/rpkm67-go-proto/rpkm67/store/object/v1" "github.com/isd-sgcu/rpkm67-store/config" "github.com/isd-sgcu/rpkm67-store/database" + "github.com/isd-sgcu/rpkm67-store/internal/client/store" "github.com/isd-sgcu/rpkm67-store/internal/object" "github.com/isd-sgcu/rpkm67-store/logger" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/health" @@ -28,6 +32,17 @@ func main() { panic(fmt.Sprintf("Failed to load config: %v", err)) } + minioClient, err := minio.New(conf.Store.Endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(conf.Store.AccessKey, conf.Store.SecretKey, conf.Store.Token), + Secure: conf.Store.UseSSL, + }) + if err != nil { + panic(fmt.Sprintf("Failed to connect to Minio: %v", err)) + } + + storeClient := store.NewClient(minioClient) + httpClient := &http.Client{} + logger := logger.New(conf) db, err := database.InitDatabase(&conf.DB, conf.App.IsDevelopment()) @@ -36,6 +51,7 @@ func main() { } objectRepo := object.NewRepository(db) + objectSvc := object.NewService(objectRepo, *conf, storeClient, *httpClient, logger) listener, err := net.Listen("tcp", fmt.Sprintf(":%v", conf.App.Port)) if err != nil { @@ -44,7 +60,7 @@ func main() { grpcServer := grpc.NewServer() grpc_health_v1.RegisterHealthServer(grpcServer, health.NewServer()) - objectProto.RegisterObjectServiceServer(grpcServer, object.NewService(objectRepo, logger)) + objectProto.RegisterObjectServiceServer(grpcServer, objectSvc) reflection.Register(grpcServer) From 4f2b726d90f0a4f6f9af8073ff805afd7fa81445 Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Tue, 25 Jun 2024 17:50:08 +0700 Subject: [PATCH 08/42] fix: object service --- internal/object/object.service.go | 61 ++++++++++++++++++++++++++++--- internal/object/object.utils.go | 20 ++++++++++ 2 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 internal/object/object.utils.go diff --git a/internal/object/object.service.go b/internal/object/object.service.go index e34df44..c691c0a 100644 --- a/internal/object/object.service.go +++ b/internal/object/object.service.go @@ -1,10 +1,18 @@ package object import ( + "bytes" "context" + "net/http" proto "github.com/isd-sgcu/rpkm67-go-proto/rpkm67/store/object/v1" + "github.com/isd-sgcu/rpkm67-store/config" + "github.com/isd-sgcu/rpkm67-store/constant" + "github.com/isd-sgcu/rpkm67-store/internal/client/store" + "github.com/minio/minio-go/v7" "go.uber.org/zap" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type Service interface { @@ -13,17 +21,54 @@ type Service interface { type serviceImpl struct { proto.UnimplementedObjectServiceServer - repo Repository - log *zap.Logger - // client + conf config.Config + repo Repository + log *zap.Logger + storeClient store.Client + httpClient http.Client } -func NewService(repo Repository, log *zap.Logger) proto.ObjectServiceServer { - return &serviceImpl{repo: repo, log: log} +func NewService(repo Repository, conf config.Config, storeClient store.Client, httpClient http.Client, log *zap.Logger) proto.ObjectServiceServer { + return &serviceImpl{ + conf: conf, + repo: repo, + log: log, + storeClient: storeClient, + httpClient: httpClient, + } } func (s *serviceImpl) Upload(_ context.Context, req *proto.UploadObjectRequest) (*proto.UploadObjectResponse, error) { - return nil, nil + if req.Data == nil { + s.log.Named("Upload").Error(constant.FileNotFoundErrorMessage) + return nil, status.Error(codes.NotFound, constant.FileNotFoundErrorMessage) + } + + randomString, err := GenerateRandomString(10) + if err != nil { + s.log.Named("Upload").Error("GenerateRandomString: ", zap.Error(err)) + } + + objectKey := req.Filename + "_" + randomString + buffer := bytes.NewReader(req.Data) + + uploadOutput, err := s.storeClient.Upload(s.conf.Store.BucketName, objectKey, buffer, buffer.Size(), minio.PutObjectOptions{ + ContentType: "application/octet-stream", + }) + + if err != nil { + s.log.Named("Upload").Error("Upload: ", zap.Error(err)) + return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage) + } + + objectResp := &proto.Object{ + Url: s.GetURL(s.conf.Store.BucketName, objectKey), + Key: uploadOutput.Key, + } + + return &proto.UploadObjectResponse{ + Object: objectResp, + }, nil } func (s *serviceImpl) FindByKey(_ context.Context, req *proto.FindByKeyObjectRequest) (*proto.FindByKeyObjectResponse, error) { @@ -33,3 +78,7 @@ func (s *serviceImpl) FindByKey(_ context.Context, req *proto.FindByKeyObjectReq func (s *serviceImpl) DeleteByKey(_ context.Context, req *proto.DeleteByKeyObjectRequest) (*proto.DeleteByKeyObjectResponse, error) { return nil, nil } + +func (s *serviceImpl) GetURL(bucketName string, objectKey string) string { + return "https://" + s.conf.Store.Endpoint + "/" + bucketName + "/" + objectKey +} diff --git a/internal/object/object.utils.go b/internal/object/object.utils.go new file mode 100644 index 0000000..cfd1e88 --- /dev/null +++ b/internal/object/object.utils.go @@ -0,0 +1,20 @@ +package object + +import ( + "crypto/rand" + "encoding/base64" +) + +func GenerateRandomString(length int) (string, error) { + numBytes := (length * 6) / 8 + + randomBytes := make([]byte, numBytes) + _, err := rand.Read(randomBytes) + if err != nil { + return "", err + } + + randomString := base64.URLEncoding.EncodeToString(randomBytes) + + return randomString[:length], nil +} From 61fcebffc1aba9d96fa8b9657c3b5ff1fc7277ca Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Tue, 25 Jun 2024 17:50:16 +0700 Subject: [PATCH 09/42] fix: client --- internal/client/store/http/http.client.go | 21 +++++++++++++ internal/client/store/store.client.go | 38 +++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 internal/client/store/http/http.client.go create mode 100644 internal/client/store/store.client.go diff --git a/internal/client/store/http/http.client.go b/internal/client/store/http/http.client.go new file mode 100644 index 0000000..e6e0ddf --- /dev/null +++ b/internal/client/store/http/http.client.go @@ -0,0 +1,21 @@ +package http + +import ( + "net/http" +) + +type Client interface { + Get(url string) (resp *http.Response, err error) +} + +type clientImpl struct { + *http.Client +} + +func NewClient(httpClient *http.Client) Client { + return &clientImpl{httpClient} +} + +func (c *clientImpl) Get(url string) (resp *http.Response, err error) { + return c.Client.Get(url) +} diff --git a/internal/client/store/store.client.go b/internal/client/store/store.client.go new file mode 100644 index 0000000..f951634 --- /dev/null +++ b/internal/client/store/store.client.go @@ -0,0 +1,38 @@ +package store + +import ( + "context" + "io" + "time" + + "github.com/minio/minio-go/v7" +) + +type Client interface { + Upload(bucketName, name string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (info minio.UploadInfo, err error) + DeleteByKey(bucketName, key string, opts minio.RemoveObjectOptions) error +} + +type clientImpl struct { + *minio.Client +} + +func (c *clientImpl) DeleteByKey(bucketName string, key string, opts minio.RemoveObjectOptions) error { + ctx := context.Background() + ctx, cancel := context.WithTimeout(ctx, 50*time.Second) + defer cancel() + + return c.Client.RemoveObject(ctx, bucketName, key, opts) +} + +func (c *clientImpl) Upload(bucketName string, name string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (info minio.UploadInfo, err error) { + ctx := context.Background() + ctx, cancel := context.WithTimeout(ctx, 50*time.Second) + defer cancel() + + return c.Client.PutObject(ctx, bucketName, name, reader, objectSize, opts) +} + +func NewClient(minioClient *minio.Client) Client { + return &clientImpl{minioClient} +} From 7e476c8e8c8a7307d12eb5c51a01b1ef8bdf7f94 Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Tue, 25 Jun 2024 18:13:18 +0700 Subject: [PATCH 10/42] feat: delete by key service --- constant/error.constant.go | 3 ++- .../client/{store => }/http/http.client.go | 0 internal/object/object.service.go | 19 ++++++++++++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) rename internal/client/{store => }/http/http.client.go (100%) diff --git a/constant/error.constant.go b/constant/error.constant.go index 017f61f..6756ef4 100644 --- a/constant/error.constant.go +++ b/constant/error.constant.go @@ -7,5 +7,6 @@ const FileNotFoundErrorMessage = "File cannot be empty" const InvalidFileTypeErrorMessage = "Invalid file type" const InvalidFileSizeErrorMessage = "Invalid file size" const InvalidFileErrorMessage = "Invalid file" -const InvalidFileKeyErrorMessage = "Invalid file key" const InvalidFileUrlErrorMessage = "Invalid file url" + +const KeyEmptyErrorMessage = "Key is empty" diff --git a/internal/client/store/http/http.client.go b/internal/client/http/http.client.go similarity index 100% rename from internal/client/store/http/http.client.go rename to internal/client/http/http.client.go diff --git a/internal/object/object.service.go b/internal/object/object.service.go index c691c0a..ddcf7fd 100644 --- a/internal/object/object.service.go +++ b/internal/object/object.service.go @@ -76,7 +76,24 @@ func (s *serviceImpl) FindByKey(_ context.Context, req *proto.FindByKeyObjectReq } func (s *serviceImpl) DeleteByKey(_ context.Context, req *proto.DeleteByKeyObjectRequest) (*proto.DeleteByKeyObjectResponse, error) { - return nil, nil + if req.Key == "" { + s.log.Named("DeleteByKey").Error("Key is empty") + return &proto.DeleteByKeyObjectResponse{ + Success: false, + }, status.Error(codes.InvalidArgument, constant.KeyEmptyErrorMessage) + } + + err := s.storeClient.DeleteByKey(s.conf.Store.BucketName, req.Key, minio.RemoveObjectOptions{}) + if err != nil { + s.log.Named("DeleteByKey").Error("DeleteByKey: ", zap.Error(err)) + return &proto.DeleteByKeyObjectResponse{ + Success: false, + }, status.Error(codes.Internal, constant.InternalServerErrorMessage) + } + + return &proto.DeleteByKeyObjectResponse{ + Success: true, + }, nil } func (s *serviceImpl) GetURL(bucketName string, objectKey string) string { From f26799e3c1b20ffff2e002cae1c1805e432f4449 Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Tue, 25 Jun 2024 18:18:18 +0700 Subject: [PATCH 11/42] fix: add repo actions in service --- internal/object/object.service.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/internal/object/object.service.go b/internal/object/object.service.go index ddcf7fd..0e355fb 100644 --- a/internal/object/object.service.go +++ b/internal/object/object.service.go @@ -9,6 +9,7 @@ import ( "github.com/isd-sgcu/rpkm67-store/config" "github.com/isd-sgcu/rpkm67-store/constant" "github.com/isd-sgcu/rpkm67-store/internal/client/store" + "github.com/isd-sgcu/rpkm67-store/internal/model" "github.com/minio/minio-go/v7" "go.uber.org/zap" "google.golang.org/grpc/codes" @@ -66,6 +67,15 @@ func (s *serviceImpl) Upload(_ context.Context, req *proto.UploadObjectRequest) Key: uploadOutput.Key, } + err = s.repo.Upload(&model.Object{ + ImageUrl: objectResp.Url, + ObjectKey: objectResp.Key, + }) + if err != nil { + s.log.Named("Upload").Error("Upload: ", zap.Error(err)) + return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage) + } + return &proto.UploadObjectResponse{ Object: objectResp, }, nil @@ -91,6 +101,11 @@ func (s *serviceImpl) DeleteByKey(_ context.Context, req *proto.DeleteByKeyObjec }, status.Error(codes.Internal, constant.InternalServerErrorMessage) } + err = s.repo.DeleteByKey(req.Key) + if err != nil { + s.log.Named("DeleteByKey").Error("DeleteByKey: ", zap.Error(err)) + } + return &proto.DeleteByKeyObjectResponse{ Success: true, }, nil From 4fa8a9a9dd230724d826cc480d4d322c0e26d961 Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Tue, 25 Jun 2024 18:59:40 +0700 Subject: [PATCH 12/42] feat: update proto --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 0ee5e18..30d29fe 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( require ( github.com/google/uuid v1.6.0 - github.com/isd-sgcu/rpkm67-go-proto v0.1.3 + github.com/isd-sgcu/rpkm67-go-proto v0.1.4 github.com/minio/minio-go/v7 v7.0.72 go.uber.org/multierr v1.10.0 // indirect google.golang.org/grpc v1.64.0 diff --git a/go.sum b/go.sum index 749e965..48c12f6 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/isd-sgcu/rpkm67-go-proto v0.1.3 h1:9+wrvKUUY5GDot1SmU9HtUgf8JzhPLHWKtCfmBQU+Yw= github.com/isd-sgcu/rpkm67-go-proto v0.1.3/go.mod h1:Z5SYz5kEe4W+MdqPouF0zEOiaqvg+s9I1S5d0q6e+Jw= +github.com/isd-sgcu/rpkm67-go-proto v0.1.4 h1:2nZRoBrHZUMN6QogR46yzqi7RoydmqRyF496udEknGg= +github.com/isd-sgcu/rpkm67-go-proto v0.1.4/go.mod h1:Z5SYz5kEe4W+MdqPouF0zEOiaqvg+s9I1S5d0q6e+Jw= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= From 94b8a213269982f7895e58376b2f799ef165d310 Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Tue, 25 Jun 2024 18:59:55 +0700 Subject: [PATCH 13/42] feat: find by key object service --- constant/error.constant.go | 1 + internal/object/object.service.go | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/constant/error.constant.go b/constant/error.constant.go index 6756ef4..b2c89c1 100644 --- a/constant/error.constant.go +++ b/constant/error.constant.go @@ -10,3 +10,4 @@ const InvalidFileErrorMessage = "Invalid file" const InvalidFileUrlErrorMessage = "Invalid file url" const KeyEmptyErrorMessage = "Key is empty" +const ObjectNotFoundErrorMessage = "Object not found" diff --git a/internal/object/object.service.go b/internal/object/object.service.go index 0e355fb..f8fc31b 100644 --- a/internal/object/object.service.go +++ b/internal/object/object.service.go @@ -82,7 +82,25 @@ func (s *serviceImpl) Upload(_ context.Context, req *proto.UploadObjectRequest) } func (s *serviceImpl) FindByKey(_ context.Context, req *proto.FindByKeyObjectRequest) (*proto.FindByKeyObjectResponse, error) { - return nil, nil + if req.Key == "" { + s.log.Named("FindByKey").Error("Key is empty") + return nil, status.Error(codes.InvalidArgument, constant.KeyEmptyErrorMessage) + } + + object := model.Object{} + + err := s.repo.FindByKey(req.Key, &object) + if err != nil { + s.log.Named("FindByKey").Error("FindByKey: ", zap.Error(err)) + return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage) + } + + return &proto.FindByKeyObjectResponse{ + Object: &proto.Object{ + Url: object.ImageUrl, + Key: object.ObjectKey, + }, + }, nil } func (s *serviceImpl) DeleteByKey(_ context.Context, req *proto.DeleteByKeyObjectRequest) (*proto.DeleteByKeyObjectResponse, error) { From a6f718f5556abfb44b10b178fba0db9f9dc717a1 Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Thu, 27 Jun 2024 17:33:47 +0700 Subject: [PATCH 14/42] feat: svc, repo + test repo success case --- .env.template | 13 +-- Makefile | 6 ++ cmd/main.go | 29 ++---- config/config.go | 32 +++---- database/db.connection.go | 29 ------ docker-compose.yaml | 21 ----- go.mod | 29 +++--- go.sum | 81 ++++++++-------- internal/client/store/store.client.go | 25 ++--- internal/model/common.model.go | 21 ----- internal/model/object.model.go | 12 --- internal/object/object.repository.go | 86 ++++++++++++++--- internal/object/object.service.go | 85 ++++++----------- .../object/test/object.repository_test.go | 45 +++++++++ internal/object/test/object.service_test.go | 31 +++++++ .../object.utils.go => utils/random.utils.go} | 14 ++- mocks/client/http/http.client.go | 50 ++++++++++ mocks/client/store/store.client.go | 66 +++++++++++++ mocks/object/object.repository.go | 93 +++++++++++++++++++ mocks/object/object.service.go | 93 +++++++++++++++++++ mocks/utils/random/random.utils.go | 49 ++++++++++ 21 files changed, 638 insertions(+), 272 deletions(-) delete mode 100644 database/db.connection.go delete mode 100644 docker-compose.yaml delete mode 100644 internal/model/common.model.go delete mode 100644 internal/model/object.model.go create mode 100644 internal/object/test/object.repository_test.go create mode 100644 internal/object/test/object.service_test.go rename internal/{object/object.utils.go => utils/random.utils.go} (54%) create mode 100644 mocks/client/http/http.client.go create mode 100644 mocks/client/store/store.client.go create mode 100644 mocks/object/object.repository.go create mode 100644 mocks/object/object.service.go create mode 100644 mocks/utils/random/random.utils.go diff --git a/.env.template b/.env.template index 570934f..c91303a 100644 --- a/.env.template +++ b/.env.template @@ -1,8 +1,9 @@ -APP_PORT=300x +APP_PORT=3005 APP_ENV=development -APP_MAX_FILE_SIZE_MB=1024 +APP_MAX_FILE_SIZE_MB= -DB_URL=postgres://root:1234@localhost:5432/rpkm67_db - -STORE_BUCKET_NAME=rpkm67-bucket -STORE_REGION=us-east-1 \ No newline at end of file +ENDPOINT= +ACCESS_KEY= +SECRET_KEY= +USE_SSL= +BUCKET_NAME= \ No newline at end of file diff --git a/Makefile b/Makefile index 4e70aa5..028056e 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,13 @@ server: watch: air + mock-gen: + mockgen -source ./internal/object/object.repository.go -destination ./mocks/object/object.repository.go + mockgen -source ./internal/object/object.service.go -destination ./mocks/object/object.service.go + mockgen -source ./internal/client/http/http.client.go -destination ./mocks/client/http/http.client.go + mockgen -source ./internal/client/store/store.client.go -destination ./mocks/client/store/store.client.go + mockgen -source ./internal/utils/random.utils.go -destination ./mocks/utils/random/random.utils.go test: go vet ./... diff --git a/cmd/main.go b/cmd/main.go index 771ae0e..b4dbbeb 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -13,9 +13,9 @@ import ( objectProto "github.com/isd-sgcu/rpkm67-go-proto/rpkm67/store/object/v1" "github.com/isd-sgcu/rpkm67-store/config" - "github.com/isd-sgcu/rpkm67-store/database" "github.com/isd-sgcu/rpkm67-store/internal/client/store" "github.com/isd-sgcu/rpkm67-store/internal/object" + "github.com/isd-sgcu/rpkm67-store/internal/utils" "github.com/isd-sgcu/rpkm67-store/logger" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" @@ -31,9 +31,12 @@ func main() { if err != nil { panic(fmt.Sprintf("Failed to load config: %v", err)) } + fmt.Println("conf: ", conf) + + logger := logger.New(conf) minioClient, err := minio.New(conf.Store.Endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(conf.Store.AccessKey, conf.Store.SecretKey, conf.Store.Token), + Creds: credentials.NewStaticV4(conf.Store.AccessKey, conf.Store.SecretKey, ""), Secure: conf.Store.UseSSL, }) if err != nil { @@ -43,15 +46,10 @@ func main() { storeClient := store.NewClient(minioClient) httpClient := &http.Client{} - logger := logger.New(conf) + randomUtils := utils.NewRandomUtils() - db, err := database.InitDatabase(&conf.DB, conf.App.IsDevelopment()) - if err != nil { - panic(fmt.Sprintf("Failed to connect to database: %v", err)) - } - - objectRepo := object.NewRepository(db) - objectSvc := object.NewService(objectRepo, *conf, storeClient, *httpClient, logger) + objectRepo := object.NewRepository(&conf.Store, storeClient, httpClient) + objectSvc := object.NewService(objectRepo, &conf.Store, logger.Named("objectSvc"), randomUtils) listener, err := net.Listen("tcp", fmt.Sprintf(":%v", conf.App.Port)) if err != nil { @@ -65,10 +63,10 @@ func main() { reflection.Register(grpcServer) go func() { - logger.Sugar().Infof("RPKM67 Auth starting at port %v", conf.App.Port) + logger.Sugar().Infof("RPKM67 Store starting at port %v", conf.App.Port) if err := grpcServer.Serve(listener); err != nil { - logger.Fatal("Failed to start RPKM67 Auth service", zap.Error(err)) + logger.Fatal("Failed to start RPKM67 Store service", zap.Error(err)) } }() @@ -77,13 +75,6 @@ func main() { grpcServer.GracefulStop() return nil }, - "database": func(ctx context.Context) error { - sqlDB, err := db.DB() - if err != nil { - return nil - } - return sqlDB.Close() - }, }) <-wait diff --git a/config/config.go b/config/config.go index 40d3261..2975c9a 100644 --- a/config/config.go +++ b/config/config.go @@ -18,19 +18,18 @@ type DB struct { } type Store struct { - Endpoint string `mapstructure:"endpoint"` - AccessKey string `mapstructure:"access_key"` - SecretKey string `mapstructure:"secret_key"` - UseSSL bool `mapstructure:"use_ssl"` - BucketName string `mapstructure:"bucket_name"` - Region string `mapstructure:"region"` - Token string `mapstructure:"token"` + Endpoint string + AccessKey string + SecretKey string + UseSSL bool + BucketName string + Region string + Token string } type Config struct { App App `mapstructure:"app"` Store Store `mapstructure:"store"` - DB DB `mapstructure:"db"` } func LoadConfig() (config *Config, err error) { @@ -52,23 +51,16 @@ func LoadConfig() (config *Config, err error) { } storeConfig := Store{ - BucketName: os.Getenv("STORE_BUCKET_NAME"), - Region: os.Getenv("STORE_REGION"), - Endpoint: os.Getenv("STORE_ENDPOINT"), - AccessKey: os.Getenv("STORE_ACCESS_KEY"), - SecretKey: os.Getenv("STORE_SECRET_KEY"), - UseSSL: os.Getenv("STORE_USE_SSL") == "true", - Token: os.Getenv("STORE_TOKEN"), - } - - dbConfig := DB{ - Url: os.Getenv("DB_URL"), + BucketName: os.Getenv("BUCKET_NAME"), + Endpoint: os.Getenv("ENDPOINT"), + AccessKey: os.Getenv("ACCESS_KEY"), + SecretKey: os.Getenv("SECRET_KEY"), + UseSSL: os.Getenv("USE_SSL") == "true", } return &Config{ App: appConfig, Store: storeConfig, - DB: dbConfig, }, nil } diff --git a/database/db.connection.go b/database/db.connection.go deleted file mode 100644 index 4f92d9c..0000000 --- a/database/db.connection.go +++ /dev/null @@ -1,29 +0,0 @@ -package database - -import ( - "github.com/isd-sgcu/rpkm67-store/config" - "github.com/isd-sgcu/rpkm67-store/internal/model" - "gorm.io/driver/postgres" - "gorm.io/gorm" - gormLogger "gorm.io/gorm/logger" -) - -func InitDatabase(conf *config.DB, isDebug bool) (db *gorm.DB, err error) { - gormConf := &gorm.Config{TranslateError: true} - - if !isDebug { - gormConf.Logger = gormLogger.Default.LogMode(gormLogger.Silent) - } - - db, err = gorm.Open(postgres.Open(conf.Url), gormConf) - if err != nil { - return nil, err - } - - err = db.AutoMigrate(&model.Object{}) - if err != nil { - return nil, err - } - - return -} diff --git a/docker-compose.yaml b/docker-compose.yaml deleted file mode 100644 index 3f27365..0000000 --- a/docker-compose.yaml +++ /dev/null @@ -1,21 +0,0 @@ -version: "3.9" - -services: - db: - image: postgres:15.1-alpine3.17 - container_name: db - restart: unless-stopped - environment: - POSTGRES_USER: root - POSTGRES_PASSWORD: "1234" - POSTGRES_DB: rpkm67_db - networks: - - rpkm67 - volumes: - - ./volumes/postgres:/var/lib/postgresql/data - ports: - - "5432:5432" - -networks: - rpkm67: - name: rpkm67 \ No newline at end of file diff --git a/go.mod b/go.mod index 30d29fe..53b7a19 100644 --- a/go.mod +++ b/go.mod @@ -4,38 +4,35 @@ go 1.21.5 require ( github.com/joho/godotenv v1.5.1 + github.com/stretchr/testify v1.9.0 go.uber.org/zap v1.27.0 ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jackc/pgx/v5 v5.5.5 // indirect - github.com/jackc/puddle/v2 v2.2.1 // indirect - github.com/jinzhu/inflection v1.0.0 // indirect - github.com/jinzhu/now v1.1.5 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/klauspost/compress v1.17.6 // indirect - github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/minio/md5-simd v1.1.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rs/xid v1.5.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( - github.com/google/uuid v1.6.0 - github.com/isd-sgcu/rpkm67-go-proto v0.1.4 + github.com/golang/mock v1.6.0 + github.com/isd-sgcu/rpkm67-go-proto v0.1.5 github.com/minio/minio-go/v7 v7.0.72 + github.com/pkg/errors v0.9.1 go.uber.org/multierr v1.10.0 // indirect google.golang.org/grpc v1.64.0 - gorm.io/driver/postgres v1.5.9 - gorm.io/gorm v1.25.10 ) diff --git a/go.sum b/go.sum index 48c12f6..b889de0 100644 --- a/go.sum +++ b/go.sum @@ -1,80 +1,83 @@ -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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/isd-sgcu/rpkm67-go-proto v0.1.3 h1:9+wrvKUUY5GDot1SmU9HtUgf8JzhPLHWKtCfmBQU+Yw= -github.com/isd-sgcu/rpkm67-go-proto v0.1.3/go.mod h1:Z5SYz5kEe4W+MdqPouF0zEOiaqvg+s9I1S5d0q6e+Jw= -github.com/isd-sgcu/rpkm67-go-proto v0.1.4 h1:2nZRoBrHZUMN6QogR46yzqi7RoydmqRyF496udEknGg= -github.com/isd-sgcu/rpkm67-go-proto v0.1.4/go.mod h1:Z5SYz5kEe4W+MdqPouF0zEOiaqvg+s9I1S5d0q6e+Jw= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= -github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= -github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/isd-sgcu/rpkm67-go-proto v0.1.5 h1:WShrm8DeVqIY1ZelgM88vW8LMnLxF6dTt650A0YkC8U= +github.com/isd-sgcu/rpkm67-go-proto v0.1.5/go.mod h1:Z5SYz5kEe4W+MdqPouF0zEOiaqvg+s9I1S5d0q6e+Jw= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= -github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/minio-go/v7 v7.0.72 h1:ZSbxs2BfJensLyHdVOgHv+pfmvxYraaUy07ER04dWnA= github.com/minio/minio-go/v7 v7.0.72/go.mod h1:4yBA8v80xGA30cfM3fz0DKYMXunWl/AV/6tWEs9ryzo= +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/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +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.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +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.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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= -gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= -gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= -gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= -gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= diff --git a/internal/client/store/store.client.go b/internal/client/store/store.client.go index f951634..cd0dd94 100644 --- a/internal/client/store/store.client.go +++ b/internal/client/store/store.client.go @@ -3,36 +3,27 @@ package store import ( "context" "io" - "time" "github.com/minio/minio-go/v7" ) type Client interface { - Upload(bucketName, name string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (info minio.UploadInfo, err error) - DeleteByKey(bucketName, key string, opts minio.RemoveObjectOptions) error + PutObject(ctx context.Context, bucketName string, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (info minio.UploadInfo, err error) + RemoveObject(ctx context.Context, bucketName string, objectName string, opts minio.RemoveObjectOptions) error } type clientImpl struct { *minio.Client } -func (c *clientImpl) DeleteByKey(bucketName string, key string, opts minio.RemoveObjectOptions) error { - ctx := context.Background() - ctx, cancel := context.WithTimeout(ctx, 50*time.Second) - defer cancel() - - return c.Client.RemoveObject(ctx, bucketName, key, opts) +func NewClient(minioClient *minio.Client) Client { + return &clientImpl{minioClient} } -func (c *clientImpl) Upload(bucketName string, name string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (info minio.UploadInfo, err error) { - ctx := context.Background() - ctx, cancel := context.WithTimeout(ctx, 50*time.Second) - defer cancel() - - return c.Client.PutObject(ctx, bucketName, name, reader, objectSize, opts) +func (c *clientImpl) PutObject(ctx context.Context, bucketName string, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (info minio.UploadInfo, err error) { + return c.Client.PutObject(ctx, bucketName, objectName, reader, objectSize, opts) } -func NewClient(minioClient *minio.Client) Client { - return &clientImpl{minioClient} +func (c *clientImpl) RemoveObject(ctx context.Context, bucketName string, objectName string, opts minio.RemoveObjectOptions) error { + return c.Client.RemoveObject(ctx, bucketName, objectName, opts) } diff --git a/internal/model/common.model.go b/internal/model/common.model.go deleted file mode 100644 index 6c15b42..0000000 --- a/internal/model/common.model.go +++ /dev/null @@ -1,21 +0,0 @@ -package model - -import ( - "time" - - "github.com/google/uuid" - "gorm.io/gorm" -) - -type Base struct { - ID uuid.UUID `json:"id" gorm:"primary_key"` - CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;autoCreateTime:nano"` - UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;autoUpdateTime:nano"` - DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index;type:timestamp"` -} - -func (b *Base) BeforeCreate(_ *gorm.DB) error { - b.ID = uuid.New() - - return nil -} diff --git a/internal/model/object.model.go b/internal/model/object.model.go deleted file mode 100644 index 8066721..0000000 --- a/internal/model/object.model.go +++ /dev/null @@ -1,12 +0,0 @@ -package model - -import ( - "github.com/google/uuid" -) - -type Object struct { - Base - ID uuid.UUID `json:"id" gorm:"primary_key"` - ImageUrl string `json:"image_url" gorm:"mediumtext"` - ObjectKey string `json:"object_key" gorm:"mediumtext"` -} diff --git a/internal/object/object.repository.go b/internal/object/object.repository.go index 8ad7dce..0c3dc2d 100644 --- a/internal/object/object.repository.go +++ b/internal/object/object.repository.go @@ -1,32 +1,90 @@ package object import ( - "github.com/isd-sgcu/rpkm67-store/internal/model" - "gorm.io/gorm" + "bytes" + "context" + "fmt" + "net/http" + "time" + + "github.com/isd-sgcu/rpkm67-store/config" + httpClient "github.com/isd-sgcu/rpkm67-store/internal/client/http" + storeClient "github.com/isd-sgcu/rpkm67-store/internal/client/store" + "github.com/minio/minio-go/v7" + "github.com/pkg/errors" ) type Repository interface { - FindByKey(key string, result *model.Object) error - Upload(file *model.Object) error - DeleteByKey(key string) error + Upload(file []byte, bucketName string, objectKey string) (url string, key string, err error) + Delete(bucketName string, objectKey string) (err error) + Get(bucketName string, objectKey string) (url string, err error) + GetURL(bucketName string, objectKey string) string } type repositoryImpl struct { - db *gorm.DB + conf *config.Store + storeClient storeClient.Client + httpClient httpClient.Client } -func NewRepository(db *gorm.DB) Repository { - return &repositoryImpl{db: db} +func NewRepository(conf *config.Store, storeClient storeClient.Client, httpClient httpClient.Client) Repository { + return &repositoryImpl{ + conf: conf, + storeClient: storeClient, + httpClient: httpClient, + } } -func (r *repositoryImpl) DeleteByKey(key string) error { - return r.db.Where("object_key = ?", key).Delete(&model.Object{}).Error +func (r *repositoryImpl) Upload(file []byte, bucketName string, objectKey string) (url string, key string, err error) { + ctx := context.Background() + _, cancel := context.WithTimeout(ctx, 50*time.Second) + defer cancel() + + buffer := bytes.NewReader(file) + + uploadOutput, err := r.storeClient.PutObject(ctx, bucketName, objectKey, buffer, + buffer.Size(), minio.PutObjectOptions{}) + if err != nil { + return "", "", errors.Wrap(err, fmt.Sprintf("Couldn't upload object to %v/%v.", bucketName, objectKey)) + } + + return r.GetURL(bucketName, objectKey), uploadOutput.Key, nil } -func (r *repositoryImpl) FindByKey(key string, result *model.Object) error { - return r.db.Where("object_key = ?", key).First(result).Error +func (r *repositoryImpl) Delete(bucketName string, objectKey string) (err error) { + ctx := context.Background() + _, cancel := context.WithTimeout(ctx, 50*time.Second) + defer cancel() + + opts := minio.RemoveObjectOptions{ + GovernanceBypass: true, + } + err = r.storeClient.RemoveObject(ctx, bucketName, objectKey, opts) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("Couldn't delete object %v/%v.", bucketName, objectKey)) + } + + return nil +} + +func (r *repositoryImpl) Get(bucketName string, objectKey string) (url string, err error) { + ctx := context.Background() + _, cancel := context.WithTimeout(ctx, 50*time.Second) + defer cancel() + + url = r.GetURL(bucketName, objectKey) + + resp, err := r.httpClient.Get(url) + if err != nil { + return "", errors.Wrap(err, fmt.Sprintf("Couldn't get object %v/%v.", bucketName, objectKey)) + } + if resp.StatusCode != http.StatusOK { + return "", nil + } + + return url, nil } -func (r *repositoryImpl) Upload(file *model.Object) error { - return r.db.Create(&file).Error +func (r *repositoryImpl) GetURL(bucketName string, objectKey string) string { + return "https://" + r.conf.Endpoint + "/" + bucketName + "/" + objectKey } diff --git a/internal/object/object.service.go b/internal/object/object.service.go index f8fc31b..4cd6146 100644 --- a/internal/object/object.service.go +++ b/internal/object/object.service.go @@ -1,16 +1,13 @@ package object import ( - "bytes" "context" - "net/http" + "fmt" proto "github.com/isd-sgcu/rpkm67-go-proto/rpkm67/store/object/v1" "github.com/isd-sgcu/rpkm67-store/config" "github.com/isd-sgcu/rpkm67-store/constant" - "github.com/isd-sgcu/rpkm67-store/internal/client/store" - "github.com/isd-sgcu/rpkm67-store/internal/model" - "github.com/minio/minio-go/v7" + "github.com/isd-sgcu/rpkm67-store/internal/utils" "go.uber.org/zap" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -22,62 +19,41 @@ type Service interface { type serviceImpl struct { proto.UnimplementedObjectServiceServer - conf config.Config - repo Repository - log *zap.Logger - storeClient store.Client - httpClient http.Client + conf *config.Store + repo Repository + utils utils.Utils + log *zap.Logger } -func NewService(repo Repository, conf config.Config, storeClient store.Client, httpClient http.Client, log *zap.Logger) proto.ObjectServiceServer { +func NewService(repo Repository, conf *config.Store, log *zap.Logger, utils utils.Utils) Service { return &serviceImpl{ - conf: conf, - repo: repo, - log: log, - storeClient: storeClient, - httpClient: httpClient, + repo: repo, + conf: conf, + utils: utils, + log: log, } } func (s *serviceImpl) Upload(_ context.Context, req *proto.UploadObjectRequest) (*proto.UploadObjectResponse, error) { - if req.Data == nil { - s.log.Named("Upload").Error(constant.FileNotFoundErrorMessage) - return nil, status.Error(codes.NotFound, constant.FileNotFoundErrorMessage) - } - - randomString, err := GenerateRandomString(10) + randomString, err := s.utils.GenerateRandomString(10) if err != nil { s.log.Named("Upload").Error("GenerateRandomString: ", zap.Error(err)) - } - - objectKey := req.Filename + "_" + randomString - buffer := bytes.NewReader(req.Data) - - uploadOutput, err := s.storeClient.Upload(s.conf.Store.BucketName, objectKey, buffer, buffer.Size(), minio.PutObjectOptions{ - ContentType: "application/octet-stream", - }) - - if err != nil { - s.log.Named("Upload").Error("Upload: ", zap.Error(err)) return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage) } - objectResp := &proto.Object{ - Url: s.GetURL(s.conf.Store.BucketName, objectKey), - Key: uploadOutput.Key, - } + objectKey := req.Filename + "_" + randomString - err = s.repo.Upload(&model.Object{ - ImageUrl: objectResp.Url, - ObjectKey: objectResp.Key, - }) + url, key, err := s.repo.Upload(req.Data, s.conf.BucketName, objectKey) if err != nil { s.log.Named("Upload").Error("Upload: ", zap.Error(err)) return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage) } return &proto.UploadObjectResponse{ - Object: objectResp, + Object: &proto.Object{ + Url: url, + Key: key, + }, }, nil } @@ -87,18 +63,20 @@ func (s *serviceImpl) FindByKey(_ context.Context, req *proto.FindByKeyObjectReq return nil, status.Error(codes.InvalidArgument, constant.KeyEmptyErrorMessage) } - object := model.Object{} - - err := s.repo.FindByKey(req.Key, &object) + url, err := s.repo.Get(s.conf.BucketName, req.Key) if err != nil { - s.log.Named("FindByKey").Error("FindByKey: ", zap.Error(err)) + s.log.Named("FindByKey").Error("Get: ", zap.Error(err)) return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage) } + if url == "" { + s.log.Named("FindByKey").Error(fmt.Sprintf("Object with key %v not found", req.Key)) + return nil, status.Error(codes.NotFound, constant.ObjectNotFoundErrorMessage) + } return &proto.FindByKeyObjectResponse{ Object: &proto.Object{ - Url: object.ImageUrl, - Key: object.ObjectKey, + Url: url, + Key: req.Key, }, }, nil } @@ -111,24 +89,19 @@ func (s *serviceImpl) DeleteByKey(_ context.Context, req *proto.DeleteByKeyObjec }, status.Error(codes.InvalidArgument, constant.KeyEmptyErrorMessage) } - err := s.storeClient.DeleteByKey(s.conf.Store.BucketName, req.Key, minio.RemoveObjectOptions{}) + err := s.repo.Delete(s.conf.BucketName, req.Key) if err != nil { - s.log.Named("DeleteByKey").Error("DeleteByKey: ", zap.Error(err)) + s.log.Named("DeleteByKey").Error("Delete: ", zap.Error(err)) return &proto.DeleteByKeyObjectResponse{ Success: false, }, status.Error(codes.Internal, constant.InternalServerErrorMessage) } - err = s.repo.DeleteByKey(req.Key) - if err != nil { - s.log.Named("DeleteByKey").Error("DeleteByKey: ", zap.Error(err)) - } - return &proto.DeleteByKeyObjectResponse{ Success: true, }, nil } func (s *serviceImpl) GetURL(bucketName string, objectKey string) string { - return "https://" + s.conf.Store.Endpoint + "/" + bucketName + "/" + objectKey + return "https://" + s.conf.Endpoint + "/" + bucketName + "/" + objectKey } diff --git a/internal/object/test/object.repository_test.go b/internal/object/test/object.repository_test.go new file mode 100644 index 0000000..f10d94a --- /dev/null +++ b/internal/object/test/object.repository_test.go @@ -0,0 +1,45 @@ +package test + +import ( + "testing" + + "github.com/golang/mock/gomock" + "github.com/isd-sgcu/rpkm67-store/config" + "github.com/isd-sgcu/rpkm67-store/internal/object" + storeClient "github.com/isd-sgcu/rpkm67-store/mocks/client/store" + "github.com/minio/minio-go/v7" + "github.com/stretchr/testify/suite" +) + +type ObjectRepositoryTest struct { + suite.Suite + conf *config.Store + controller *gomock.Controller +} + +func TestObjectRepository(t *testing.T) { + suite.Run(t, new(ObjectRepositoryTest)) +} + +func (t *ObjectRepositoryTest) SetupTest() { + t.conf = &config.Store{ + Endpoint: "mock-endpoint", + } + t.controller = gomock.NewController(t.T()) +} + +func (t *ObjectRepositoryTest) TestCreateObjectSuccess() { + storeClient := storeClient.NewMockClient(t.controller) + storeClient.EXPECT(). + PutObject(gomock.Any(), "mock-bucket", "mock-key", gomock.Any(), int64(0), gomock.Any()). + Return(minio.UploadInfo{ + Key: "mock-key", + }, nil) + + repo := object.NewRepository(t.conf, storeClient, nil) + + url, key, err := repo.Upload([]byte{}, "mock-bucket", "mock-key") + t.Nil(err) + t.Equal("mock-key", key) + t.Equal(repo.GetURL("mock-bucket", "mock-key"), url) +} diff --git a/internal/object/test/object.service_test.go b/internal/object/test/object.service_test.go new file mode 100644 index 0000000..4f69693 --- /dev/null +++ b/internal/object/test/object.service_test.go @@ -0,0 +1,31 @@ +package test + +import ( + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/suite" + "go.uber.org/zap" + + "github.com/isd-sgcu/rpkm67-store/config" +) + +type ObjectServiceTest struct { + suite.Suite + controller *gomock.Controller + conf *config.Store + logger *zap.Logger +} + +func TestObjectService(t *testing.T) { + suite.Run(t, new(ObjectServiceTest)) +} + +func (t *ObjectServiceTest) SetupTest() { + t.controller = gomock.NewController(t.T()) + t.logger = zap.NewNop() + t.conf = &config.Store{ + BucketName: "mock-bucket", + Endpoint: "mock-endpoint", + } +} diff --git a/internal/object/object.utils.go b/internal/utils/random.utils.go similarity index 54% rename from internal/object/object.utils.go rename to internal/utils/random.utils.go index cfd1e88..769de93 100644 --- a/internal/object/object.utils.go +++ b/internal/utils/random.utils.go @@ -1,11 +1,21 @@ -package object +package utils import ( "crypto/rand" "encoding/base64" ) -func GenerateRandomString(length int) (string, error) { +type Utils interface { + GenerateRandomString(length int) (string, error) +} + +type randomUtils struct{} + +func NewRandomUtils() Utils { + return &randomUtils{} +} + +func (u *randomUtils) GenerateRandomString(length int) (string, error) { numBytes := (length * 6) / 8 randomBytes := make([]byte, numBytes) diff --git a/mocks/client/http/http.client.go b/mocks/client/http/http.client.go new file mode 100644 index 0000000..bd409e7 --- /dev/null +++ b/mocks/client/http/http.client.go @@ -0,0 +1,50 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./internal/client/http/http.client.go + +// Package mock_http is a generated GoMock package. +package mock_http + +import ( + http "net/http" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockClient is a mock of Client interface. +type MockClient struct { + ctrl *gomock.Controller + recorder *MockClientMockRecorder +} + +// MockClientMockRecorder is the mock recorder for MockClient. +type MockClientMockRecorder struct { + mock *MockClient +} + +// NewMockClient creates a new mock instance. +func NewMockClient(ctrl *gomock.Controller) *MockClient { + mock := &MockClient{ctrl: ctrl} + mock.recorder = &MockClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClient) EXPECT() *MockClientMockRecorder { + return m.recorder +} + +// Get mocks base method. +func (m *MockClient) Get(url string) (*http.Response, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", url) + ret0, _ := ret[0].(*http.Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockClientMockRecorder) Get(url interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockClient)(nil).Get), url) +} diff --git a/mocks/client/store/store.client.go b/mocks/client/store/store.client.go new file mode 100644 index 0000000..4a25698 --- /dev/null +++ b/mocks/client/store/store.client.go @@ -0,0 +1,66 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./internal/client/store/store.client.go + +// Package mock_store is a generated GoMock package. +package mock_store + +import ( + context "context" + io "io" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + minio "github.com/minio/minio-go/v7" +) + +// MockClient is a mock of Client interface. +type MockClient struct { + ctrl *gomock.Controller + recorder *MockClientMockRecorder +} + +// MockClientMockRecorder is the mock recorder for MockClient. +type MockClientMockRecorder struct { + mock *MockClient +} + +// NewMockClient creates a new mock instance. +func NewMockClient(ctrl *gomock.Controller) *MockClient { + mock := &MockClient{ctrl: ctrl} + mock.recorder = &MockClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClient) EXPECT() *MockClientMockRecorder { + return m.recorder +} + +// PutObject mocks base method. +func (m *MockClient) PutObject(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (minio.UploadInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PutObject", ctx, bucketName, objectName, reader, objectSize, opts) + ret0, _ := ret[0].(minio.UploadInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PutObject indicates an expected call of PutObject. +func (mr *MockClientMockRecorder) PutObject(ctx, bucketName, objectName, reader, objectSize, opts interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutObject", reflect.TypeOf((*MockClient)(nil).PutObject), ctx, bucketName, objectName, reader, objectSize, opts) +} + +// RemoveObject mocks base method. +func (m *MockClient) RemoveObject(ctx context.Context, bucketName, objectName string, opts minio.RemoveObjectOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RemoveObject", ctx, bucketName, objectName, opts) + ret0, _ := ret[0].(error) + return ret0 +} + +// RemoveObject indicates an expected call of RemoveObject. +func (mr *MockClientMockRecorder) RemoveObject(ctx, bucketName, objectName, opts interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveObject", reflect.TypeOf((*MockClient)(nil).RemoveObject), ctx, bucketName, objectName, opts) +} diff --git a/mocks/object/object.repository.go b/mocks/object/object.repository.go new file mode 100644 index 0000000..994b752 --- /dev/null +++ b/mocks/object/object.repository.go @@ -0,0 +1,93 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./internal/object/object.repository.go + +// Package mock_object is a generated GoMock package. +package mock_object + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockRepository is a mock of Repository interface. +type MockRepository struct { + ctrl *gomock.Controller + recorder *MockRepositoryMockRecorder +} + +// MockRepositoryMockRecorder is the mock recorder for MockRepository. +type MockRepositoryMockRecorder struct { + mock *MockRepository +} + +// NewMockRepository creates a new mock instance. +func NewMockRepository(ctrl *gomock.Controller) *MockRepository { + mock := &MockRepository{ctrl: ctrl} + mock.recorder = &MockRepositoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder { + return m.recorder +} + +// Delete mocks base method. +func (m *MockRepository) Delete(bucketName, objectKey string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", bucketName, objectKey) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockRepositoryMockRecorder) Delete(bucketName, objectKey interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockRepository)(nil).Delete), bucketName, objectKey) +} + +// Get mocks base method. +func (m *MockRepository) Get(bucketName, objectKey string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", bucketName, objectKey) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockRepositoryMockRecorder) Get(bucketName, objectKey interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRepository)(nil).Get), bucketName, objectKey) +} + +// GetURL mocks base method. +func (m *MockRepository) GetURL(bucketName, objectKey string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetURL", bucketName, objectKey) + ret0, _ := ret[0].(string) + return ret0 +} + +// GetURL indicates an expected call of GetURL. +func (mr *MockRepositoryMockRecorder) GetURL(bucketName, objectKey interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetURL", reflect.TypeOf((*MockRepository)(nil).GetURL), bucketName, objectKey) +} + +// Upload mocks base method. +func (m *MockRepository) Upload(file []byte, bucketName, objectKey string) (string, string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Upload", file, bucketName, objectKey) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(string) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// Upload indicates an expected call of Upload. +func (mr *MockRepositoryMockRecorder) Upload(file, bucketName, objectKey interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upload", reflect.TypeOf((*MockRepository)(nil).Upload), file, bucketName, objectKey) +} diff --git a/mocks/object/object.service.go b/mocks/object/object.service.go new file mode 100644 index 0000000..d1b2e22 --- /dev/null +++ b/mocks/object/object.service.go @@ -0,0 +1,93 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./internal/object/object.service.go + +// Package mock_object is a generated GoMock package. +package mock_object + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + v1 "github.com/isd-sgcu/rpkm67-go-proto/rpkm67/store/object/v1" +) + +// MockService is a mock of Service interface. +type MockService struct { + ctrl *gomock.Controller + recorder *MockServiceMockRecorder +} + +// MockServiceMockRecorder is the mock recorder for MockService. +type MockServiceMockRecorder struct { + mock *MockService +} + +// NewMockService creates a new mock instance. +func NewMockService(ctrl *gomock.Controller) *MockService { + mock := &MockService{ctrl: ctrl} + mock.recorder = &MockServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockService) EXPECT() *MockServiceMockRecorder { + return m.recorder +} + +// DeleteByKey mocks base method. +func (m *MockService) DeleteByKey(arg0 context.Context, arg1 *v1.DeleteByKeyObjectRequest) (*v1.DeleteByKeyObjectResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteByKey", arg0, arg1) + ret0, _ := ret[0].(*v1.DeleteByKeyObjectResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteByKey indicates an expected call of DeleteByKey. +func (mr *MockServiceMockRecorder) DeleteByKey(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteByKey", reflect.TypeOf((*MockService)(nil).DeleteByKey), arg0, arg1) +} + +// FindByKey mocks base method. +func (m *MockService) FindByKey(arg0 context.Context, arg1 *v1.FindByKeyObjectRequest) (*v1.FindByKeyObjectResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindByKey", arg0, arg1) + ret0, _ := ret[0].(*v1.FindByKeyObjectResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindByKey indicates an expected call of FindByKey. +func (mr *MockServiceMockRecorder) FindByKey(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindByKey", reflect.TypeOf((*MockService)(nil).FindByKey), arg0, arg1) +} + +// Upload mocks base method. +func (m *MockService) Upload(arg0 context.Context, arg1 *v1.UploadObjectRequest) (*v1.UploadObjectResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Upload", arg0, arg1) + ret0, _ := ret[0].(*v1.UploadObjectResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Upload indicates an expected call of Upload. +func (mr *MockServiceMockRecorder) Upload(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upload", reflect.TypeOf((*MockService)(nil).Upload), arg0, arg1) +} + +// mustEmbedUnimplementedObjectServiceServer mocks base method. +func (m *MockService) mustEmbedUnimplementedObjectServiceServer() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "mustEmbedUnimplementedObjectServiceServer") +} + +// mustEmbedUnimplementedObjectServiceServer indicates an expected call of mustEmbedUnimplementedObjectServiceServer. +func (mr *MockServiceMockRecorder) mustEmbedUnimplementedObjectServiceServer() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "mustEmbedUnimplementedObjectServiceServer", reflect.TypeOf((*MockService)(nil).mustEmbedUnimplementedObjectServiceServer)) +} diff --git a/mocks/utils/random/random.utils.go b/mocks/utils/random/random.utils.go new file mode 100644 index 0000000..5be8082 --- /dev/null +++ b/mocks/utils/random/random.utils.go @@ -0,0 +1,49 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./internal/utils/random.utils.go + +// Package mock_utils is a generated GoMock package. +package mock_utils + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockUtils is a mock of Utils interface. +type MockUtils struct { + ctrl *gomock.Controller + recorder *MockUtilsMockRecorder +} + +// MockUtilsMockRecorder is the mock recorder for MockUtils. +type MockUtilsMockRecorder struct { + mock *MockUtils +} + +// NewMockUtils creates a new mock instance. +func NewMockUtils(ctrl *gomock.Controller) *MockUtils { + mock := &MockUtils{ctrl: ctrl} + mock.recorder = &MockUtilsMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockUtils) EXPECT() *MockUtilsMockRecorder { + return m.recorder +} + +// GenerateRandomString mocks base method. +func (m *MockUtils) GenerateRandomString(length int) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GenerateRandomString", length) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GenerateRandomString indicates an expected call of GenerateRandomString. +func (mr *MockUtilsMockRecorder) GenerateRandomString(length interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateRandomString", reflect.TypeOf((*MockUtils)(nil).GenerateRandomString), length) +} From 200a0e96a39f2025a9bf90b20aeb59dca396696a Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Fri, 28 Jun 2024 18:57:05 +0700 Subject: [PATCH 15/42] fix: port 3005 --- .env.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.template b/.env.template index 570934f..3def5ec 100644 --- a/.env.template +++ b/.env.template @@ -1,4 +1,4 @@ -APP_PORT=300x +APP_PORT=3005 APP_ENV=development APP_MAX_FILE_SIZE_MB=1024 From 07b1a41d36388187979f45d6e8a09ae9367ae520 Mon Sep 17 00:00:00 2001 From: dostayesky Date: Fri, 28 Jun 2024 20:11:20 +0700 Subject: [PATCH 16/42] upload and delete tested --- .../object/test/object.repository_test.go | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/internal/object/test/object.repository_test.go b/internal/object/test/object.repository_test.go index f10d94a..703ff3f 100644 --- a/internal/object/test/object.repository_test.go +++ b/internal/object/test/object.repository_test.go @@ -1,6 +1,7 @@ package test import ( + "errors" "testing" "github.com/golang/mock/gomock" @@ -43,3 +44,54 @@ func (t *ObjectRepositoryTest) TestCreateObjectSuccess() { t.Equal("mock-key", key) t.Equal(repo.GetURL("mock-bucket", "mock-key"), url) } + +func (t *ObjectRepositoryTest) TestUploadSuccess() { + storeClient := storeClient.NewMockClient(t.controller) + storeClient.EXPECT().PutObject(gomock.Any(), "bucket", "object", gomock.Any(), int64(0), gomock.Any()).Return(minio.UploadInfo{Key: "object"}, nil) + + repo := object.NewRepository(t.conf, storeClient, nil) + + url, key, err := repo.Upload([]byte{}, "bucket", "object") + t.Nil(err) + t.Equal("object", key) + t.Equal(repo.GetURL("bucket", "object"), url) +} + +func (t *ObjectRepositoryTest) TestUploadError() { + storeClient := storeClient.NewMockClient(t.controller) + storeClient.EXPECT().PutObject(gomock.Any(), "bucket", "object", gomock.Any(), int64(0), gomock.Any()).Return(minio.UploadInfo{}, errors.New("error")) + + repo := object.NewRepository(t.conf, storeClient, nil) + + url, key, err := repo.Upload([]byte{}, "bucket", "object") + t.NotNil(err) + t.Empty(url) + t.Empty(key) +} + +func (t *ObjectRepositoryTest) TestDeleteSuccess() { + storeClient := storeClient.NewMockClient(t.controller) + storeClient.EXPECT().RemoveObject(gomock.Any(), "bucket", "object", gomock.Any()).Return(nil) + + repo := object.NewRepository(t.conf, storeClient, nil) + + err:= repo.Delete("bucket", "object") + t.Nil(err) +} + +func (t *ObjectRepositoryTest) TestDeleteFail() { + storeClient := storeClient.NewMockClient(t.controller) + storeClient.EXPECT().RemoveObject(gomock.Any(), "bucket", "object", gomock.Any()).Return(errors.New("error")) + + repo := object.NewRepository(t.conf, storeClient, nil) + + err:= repo.Delete("bucket", "object") + t.NotNil(err) +} + + + + + + + From 136d6448a93f7f2041accaa554a7ac1ab0f34f43 Mon Sep 17 00:00:00 2001 From: dostayesky Date: Fri, 28 Jun 2024 20:48:06 +0700 Subject: [PATCH 17/42] Get unittest done ,edit name func name --- .../object/test/object.repository_test.go | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/internal/object/test/object.repository_test.go b/internal/object/test/object.repository_test.go index 703ff3f..ca70d59 100644 --- a/internal/object/test/object.repository_test.go +++ b/internal/object/test/object.repository_test.go @@ -2,11 +2,13 @@ package test import ( "errors" + "net/http" "testing" "github.com/golang/mock/gomock" "github.com/isd-sgcu/rpkm67-store/config" "github.com/isd-sgcu/rpkm67-store/internal/object" + httpClient "github.com/isd-sgcu/rpkm67-store/mocks/client/http" storeClient "github.com/isd-sgcu/rpkm67-store/mocks/client/store" "github.com/minio/minio-go/v7" "github.com/stretchr/testify/suite" @@ -79,7 +81,7 @@ func (t *ObjectRepositoryTest) TestDeleteSuccess() { t.Nil(err) } -func (t *ObjectRepositoryTest) TestDeleteFail() { +func (t *ObjectRepositoryTest) TestDeleteError() { storeClient := storeClient.NewMockClient(t.controller) storeClient.EXPECT().RemoveObject(gomock.Any(), "bucket", "object", gomock.Any()).Return(errors.New("error")) @@ -89,6 +91,42 @@ func (t *ObjectRepositoryTest) TestDeleteFail() { t.NotNil(err) } +func (t *ObjectRepositoryTest) TestGetSuccess() { + httpClient := httpClient.NewMockClient(t.controller) + httpClient.EXPECT().Get("https://mock-endpoint/bucket/object").Return(&http.Response{ + StatusCode: http.StatusOK},nil) + + repo := object.NewRepository(t.conf, nil, httpClient) + + url,err:= repo.Get("bucket", "object") + t.Nil(err) + t.Assert().Equal(repo.GetURL("bucket","object"),url) +} + +func (t *ObjectRepositoryTest) TestGetError() { + httpClient := httpClient.NewMockClient(t.controller) + httpClient.EXPECT().Get("https://mock-endpoint/bucket/object").Return(&http.Response{ + StatusCode: http.StatusOK},errors.New("error")) + + repo := object.NewRepository(t.conf, nil, httpClient) + + url,err:= repo.Get("bucket", "object") + t.NotNil(err) + t.Empty(url) +} + +func (t *ObjectRepositoryTest) TestGetStatusNotOK() { + httpClient := httpClient.NewMockClient(t.controller) + httpClient.EXPECT().Get("https://mock-endpoint/bucket/object").Return(&http.Response{ + StatusCode: http.StatusNotFound},nil) + + repo := object.NewRepository(t.conf, nil, httpClient) + + url,err:= repo.Get("bucket", "object") + t.Nil(err) + t.Empty(url) +} + From a94c8535664bea9528c7d1f2324cde4fa4d5fde2 Mon Sep 17 00:00:00 2001 From: dostayesky Date: Fri, 28 Jun 2024 20:51:36 +0700 Subject: [PATCH 18/42] test getURL done --- internal/object/test/object.repository_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/object/test/object.repository_test.go b/internal/object/test/object.repository_test.go index ca70d59..78d3eb1 100644 --- a/internal/object/test/object.repository_test.go +++ b/internal/object/test/object.repository_test.go @@ -127,6 +127,11 @@ func (t *ObjectRepositoryTest) TestGetStatusNotOK() { t.Empty(url) } +func (t *ObjectRepositoryTest) TestGetURL() { + repo := object.NewRepository(t.conf, nil, nil) + url := repo.GetURL("bucket","object") + t.Assert().Equal("https://mock-endpoint/bucket/object",url) +} From 38e5a63afe1dcf71116fb22f603392a38c8ec242 Mon Sep 17 00:00:00 2001 From: dostayesky Date: Fri, 28 Jun 2024 20:54:41 +0700 Subject: [PATCH 19/42] delete un used line --- internal/object/test/object.repository_test.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/internal/object/test/object.repository_test.go b/internal/object/test/object.repository_test.go index 78d3eb1..e310156 100644 --- a/internal/object/test/object.repository_test.go +++ b/internal/object/test/object.repository_test.go @@ -131,10 +131,4 @@ func (t *ObjectRepositoryTest) TestGetURL() { repo := object.NewRepository(t.conf, nil, nil) url := repo.GetURL("bucket","object") t.Assert().Equal("https://mock-endpoint/bucket/object",url) -} - - - - - - +} \ No newline at end of file From fc0fd165f2d2f3b13b790e81462f647c41837d44 Mon Sep 17 00:00:00 2001 From: dostayesky Date: Sat, 29 Jun 2024 01:49:43 +0700 Subject: [PATCH 20/42] include mock-endpoint in test setup --- internal/object/test/object.repository_test.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/object/test/object.repository_test.go b/internal/object/test/object.repository_test.go index e310156..f54c82a 100644 --- a/internal/object/test/object.repository_test.go +++ b/internal/object/test/object.repository_test.go @@ -18,6 +18,7 @@ type ObjectRepositoryTest struct { suite.Suite conf *config.Store controller *gomock.Controller + mockEndpoint string } func TestObjectRepository(t *testing.T) { @@ -29,6 +30,7 @@ func (t *ObjectRepositoryTest) SetupTest() { Endpoint: "mock-endpoint", } t.controller = gomock.NewController(t.T()) + t.mockEndpoint = "https://mock-endpoint/bucket/object" } func (t *ObjectRepositoryTest) TestCreateObjectSuccess() { @@ -93,7 +95,7 @@ func (t *ObjectRepositoryTest) TestDeleteError() { func (t *ObjectRepositoryTest) TestGetSuccess() { httpClient := httpClient.NewMockClient(t.controller) - httpClient.EXPECT().Get("https://mock-endpoint/bucket/object").Return(&http.Response{ + httpClient.EXPECT().Get(t.mockEndpoint).Return(&http.Response{ StatusCode: http.StatusOK},nil) repo := object.NewRepository(t.conf, nil, httpClient) @@ -105,7 +107,7 @@ func (t *ObjectRepositoryTest) TestGetSuccess() { func (t *ObjectRepositoryTest) TestGetError() { httpClient := httpClient.NewMockClient(t.controller) - httpClient.EXPECT().Get("https://mock-endpoint/bucket/object").Return(&http.Response{ + httpClient.EXPECT().Get(t.mockEndpoint).Return(&http.Response{ StatusCode: http.StatusOK},errors.New("error")) repo := object.NewRepository(t.conf, nil, httpClient) @@ -117,7 +119,7 @@ func (t *ObjectRepositoryTest) TestGetError() { func (t *ObjectRepositoryTest) TestGetStatusNotOK() { httpClient := httpClient.NewMockClient(t.controller) - httpClient.EXPECT().Get("https://mock-endpoint/bucket/object").Return(&http.Response{ + httpClient.EXPECT().Get(t.mockEndpoint).Return(&http.Response{ StatusCode: http.StatusNotFound},nil) repo := object.NewRepository(t.conf, nil, httpClient) @@ -130,5 +132,5 @@ func (t *ObjectRepositoryTest) TestGetStatusNotOK() { func (t *ObjectRepositoryTest) TestGetURL() { repo := object.NewRepository(t.conf, nil, nil) url := repo.GetURL("bucket","object") - t.Assert().Equal("https://mock-endpoint/bucket/object",url) + t.Assert().Equal(t.mockEndpoint,url) } \ No newline at end of file From d6fdaabf875b5f81afc36ae57120bd09c8519cc7 Mon Sep 17 00:00:00 2001 From: dostayesky Date: Sat, 29 Jun 2024 02:32:43 +0700 Subject: [PATCH 21/42] replace t.Assert().Equal => t.Equal --- internal/object/test/object.repository_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/object/test/object.repository_test.go b/internal/object/test/object.repository_test.go index f54c82a..f12f8df 100644 --- a/internal/object/test/object.repository_test.go +++ b/internal/object/test/object.repository_test.go @@ -102,7 +102,7 @@ func (t *ObjectRepositoryTest) TestGetSuccess() { url,err:= repo.Get("bucket", "object") t.Nil(err) - t.Assert().Equal(repo.GetURL("bucket","object"),url) + t.Equal(repo.GetURL("bucket","object"),url) } func (t *ObjectRepositoryTest) TestGetError() { @@ -132,5 +132,5 @@ func (t *ObjectRepositoryTest) TestGetStatusNotOK() { func (t *ObjectRepositoryTest) TestGetURL() { repo := object.NewRepository(t.conf, nil, nil) url := repo.GetURL("bucket","object") - t.Assert().Equal(t.mockEndpoint,url) + t.Equal(t.mockEndpoint,url) } \ No newline at end of file From 477317e05cd0417783c5113c5d29773dbbc22f6b Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sat, 29 Jun 2024 11:30:42 +0700 Subject: [PATCH 22/42] feat: add latest tag --- .github/workflows/build-deploy.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index a1d012b..ac5db1e 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -11,6 +11,7 @@ on: env: IMAGE_NAME: ghcr.io/${{ github.repository }} + IMAGE_TAG: ${{ github.sha }} jobs: build: @@ -36,7 +37,8 @@ jobs: uses: docker/build-push-action@v3 with: push: true - tags: ${{ env.IMAGE_NAME }}:${{ github.ref_type == 'tag' && github.ref_name || github.sha }} + tags: ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }},${{ env.IMAGE_NAME }}:latest cache-from: type=registry,ref=${{ env.IMAGE_NAME }}:buildcache cache-to: type=registry,ref=${{ env.IMAGE_NAME }}:buildcache,mode=max -# docker pull --platform linux/x86_64 + +# docker pull --platform linux/x86_64 \ No newline at end of file From b289893312ac4b85dc758aa2d3a7130dd9b6b544 Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sat, 29 Jun 2024 11:50:07 +0700 Subject: [PATCH 23/42] fix: STORE_ env prefix --- .env.template | 10 +++++----- config/config.go | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.env.template b/.env.template index c91303a..9b666aa 100644 --- a/.env.template +++ b/.env.template @@ -2,8 +2,8 @@ APP_PORT=3005 APP_ENV=development APP_MAX_FILE_SIZE_MB= -ENDPOINT= -ACCESS_KEY= -SECRET_KEY= -USE_SSL= -BUCKET_NAME= \ No newline at end of file +STORE_ENDPOINT= +STORE_ACCESS_KEY= +STORE_SECRET_KEY= +STORE_USE_SSL= +STORE_BUCKET_NAME= \ No newline at end of file diff --git a/config/config.go b/config/config.go index 2975c9a..1da0599 100644 --- a/config/config.go +++ b/config/config.go @@ -51,11 +51,11 @@ func LoadConfig() (config *Config, err error) { } storeConfig := Store{ - BucketName: os.Getenv("BUCKET_NAME"), - Endpoint: os.Getenv("ENDPOINT"), - AccessKey: os.Getenv("ACCESS_KEY"), - SecretKey: os.Getenv("SECRET_KEY"), - UseSSL: os.Getenv("USE_SSL") == "true", + BucketName: os.Getenv("STORE_BUCKET_NAME"), + Endpoint: os.Getenv("STORE_ENDPOINT"), + AccessKey: os.Getenv("STORE_ACCESS_KEY"), + SecretKey: os.Getenv("STORE_SECRET_KEY"), + UseSSL: os.Getenv("STORE_USE_SSL") == "true", } return &Config{ From 3c43d552301a67c99d40db1c594baa986bd19964 Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sat, 29 Jun 2024 11:56:00 +0700 Subject: [PATCH 24/42] feat: docker-compose --- Makefile | 17 +++++++++ cmd/main.go | 1 - docker-compose.qa.yml | 84 +++++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 33 +++++++++++++++++ 4 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 docker-compose.qa.yml create mode 100644 docker-compose.yml diff --git a/Makefile b/Makefile index 028056e..650d0b9 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,23 @@ +pull-latest-mac: + docker pull --platform linux/x86_64 ghcr.io/isd-sgcu/rpkm67-gateway:latest + docker pull --platform linux/x86_64 ghcr.io/isd-sgcu/rpkm67-auth:latest + docker pull --platform linux/x86_64 ghcr.io/isd-sgcu/rpkm67-backend:latest + docker pull --platform linux/x86_64 ghcr.io/isd-sgcu/rpkm67-checkin:latest + docker pull --platform linux/x86_64 ghcr.io/isd-sgcu/rpkm67-store:latest + +pull-latest-windows: + docker pull ghcr.io/isd-sgcu/rpkm67-gateway:latest + docker pull ghcr.io/isd-sgcu/rpkm67-auth:latest + docker pull ghcr.io/isd-sgcu/rpkm67-backend:latest + docker pull ghcr.io/isd-sgcu/rpkm67-checkin:latest + docker pull ghcr.io/isd-sgcu/rpkm67-store:latest + docker: docker-compose up +docker-qa: + docker-compose -f docker-compose.qa.yml up + server: go run cmd/main.go diff --git a/cmd/main.go b/cmd/main.go index b4dbbeb..d3ea55e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -31,7 +31,6 @@ func main() { if err != nil { panic(fmt.Sprintf("Failed to load config: %v", err)) } - fmt.Println("conf: ", conf) logger := logger.New(conf) diff --git a/docker-compose.qa.yml b/docker-compose.qa.yml new file mode 100644 index 0000000..e06f4bd --- /dev/null +++ b/docker-compose.qa.yml @@ -0,0 +1,84 @@ +version: "3.9" + +services: + gateway: + image: ghcr.io/isd-sgcu/rpkm67-gateway:latest + container_name: gateway + restart: unless-stopped + environment: + APP_PORT: 3001 + APP_ENV: development + APP_MAX_FILE_SIZE_MB: 10 + CORS_ORIGINS: http://localhost:3000 + SERVICE_AUTH: auth:3002 + SERVICE_BACKEND: backend:3003 + SERVICE_CHECKIN: checkin:3004 + SERVICE_STORE: http://localhost:3005 + networks: + - rpkm67 + ports: + - "3001:3001" + + backend: + image: ghcr.io/isd-sgcu/rpkm67-backend:latest + container_name: backend + restart: unless-stopped + environment: + APP_PORT: 3003 + APP_ENV: development + DB_URL: postgres://root:1234@db:5432/rpkm67_db + REDIS_HOST: cache + REDIS_PORT: 6379 + REDIS_PASSWORD: 5678 + PIN_WORKSHOP_CODE: workshop + PIN_WORKSHOP_COUNT: 5 + PIN_LANDMARK_CODE: landmark + PIN_LANDMARK_COUNT: 4 + networks: + - rpkm67 + ports: + - "3003:3003" + + checkin: + image: ghcr.io/isd-sgcu/rpkm67-checkin:latest + container_name: checkin + restart: unless-stopped + environment: + APP_PORT: 3004 + APP_ENV: development + DB_URL: postgres://root:1234@db:5432/rpkm67_db + networks: + - rpkm67 + ports: + - "3004:3004" + + db: + image: postgres:15.1-alpine3.17 + container_name: db + restart: unless-stopped + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: "1234" + POSTGRES_DB: rpkm67_db + networks: + - rpkm67 + volumes: + - ./volumes/postgres:/var/lib/postgresql/data + ports: + - "5432:5432" + + cache: + image: redis:7.2.3-alpine + container_name: cache + restart: unless-stopped + environment: + REDIS_HOST: localhost + REDIS_PASSWORD: "5678" + networks: + - rpkm67 + ports: + - "6379:6379" + +networks: + rpkm67: + name: rpkm67 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..25dd95d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,33 @@ +version: "3.9" + +services: + db: + image: postgres:15.1-alpine3.17 + container_name: db + restart: unless-stopped + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: "1234" + POSTGRES_DB: rpkm67_db + networks: + - rpkm67 + volumes: + - ./volumes/postgres:/var/lib/postgresql/data + ports: + - "5432:5432" + + cache: + image: redis:7.2.3-alpine + container_name: cache + restart: unless-stopped + environment: + REDIS_HOST: localhost + REDIS_PASSWORD: "5678" + networks: + - rpkm67 + ports: + - "6379:6379" + +networks: + rpkm67: + name: rpkm67 From af085aa628695c03288ea8b371f05a06186dd3f8 Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sat, 29 Jun 2024 12:44:11 +0700 Subject: [PATCH 25/42] fix: qa --- .gitignore | 3 +- Makefile | 2 ++ docker-compose.qa.yml | 84 ------------------------------------------- 3 files changed, 4 insertions(+), 85 deletions(-) delete mode 100644 docker-compose.qa.yml diff --git a/.gitignore b/.gitignore index 6a4b79a..8ed44de 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,5 @@ coverage.out coverage.html tmp -staff.json \ No newline at end of file +staff.json +docker-compose.qa.yml \ No newline at end of file diff --git a/Makefile b/Makefile index 650d0b9..d99286e 100644 --- a/Makefile +++ b/Makefile @@ -13,9 +13,11 @@ pull-latest-windows: docker pull ghcr.io/isd-sgcu/rpkm67-store:latest docker: + docker rm -v -f $$(docker ps -qa) docker-compose up docker-qa: + docker rm -v -f $$(docker ps -qa) docker-compose -f docker-compose.qa.yml up server: diff --git a/docker-compose.qa.yml b/docker-compose.qa.yml deleted file mode 100644 index e06f4bd..0000000 --- a/docker-compose.qa.yml +++ /dev/null @@ -1,84 +0,0 @@ -version: "3.9" - -services: - gateway: - image: ghcr.io/isd-sgcu/rpkm67-gateway:latest - container_name: gateway - restart: unless-stopped - environment: - APP_PORT: 3001 - APP_ENV: development - APP_MAX_FILE_SIZE_MB: 10 - CORS_ORIGINS: http://localhost:3000 - SERVICE_AUTH: auth:3002 - SERVICE_BACKEND: backend:3003 - SERVICE_CHECKIN: checkin:3004 - SERVICE_STORE: http://localhost:3005 - networks: - - rpkm67 - ports: - - "3001:3001" - - backend: - image: ghcr.io/isd-sgcu/rpkm67-backend:latest - container_name: backend - restart: unless-stopped - environment: - APP_PORT: 3003 - APP_ENV: development - DB_URL: postgres://root:1234@db:5432/rpkm67_db - REDIS_HOST: cache - REDIS_PORT: 6379 - REDIS_PASSWORD: 5678 - PIN_WORKSHOP_CODE: workshop - PIN_WORKSHOP_COUNT: 5 - PIN_LANDMARK_CODE: landmark - PIN_LANDMARK_COUNT: 4 - networks: - - rpkm67 - ports: - - "3003:3003" - - checkin: - image: ghcr.io/isd-sgcu/rpkm67-checkin:latest - container_name: checkin - restart: unless-stopped - environment: - APP_PORT: 3004 - APP_ENV: development - DB_URL: postgres://root:1234@db:5432/rpkm67_db - networks: - - rpkm67 - ports: - - "3004:3004" - - db: - image: postgres:15.1-alpine3.17 - container_name: db - restart: unless-stopped - environment: - POSTGRES_USER: root - POSTGRES_PASSWORD: "1234" - POSTGRES_DB: rpkm67_db - networks: - - rpkm67 - volumes: - - ./volumes/postgres:/var/lib/postgresql/data - ports: - - "5432:5432" - - cache: - image: redis:7.2.3-alpine - container_name: cache - restart: unless-stopped - environment: - REDIS_HOST: localhost - REDIS_PASSWORD: "5678" - networks: - - rpkm67 - ports: - - "6379:6379" - -networks: - rpkm67: - name: rpkm67 From 55dd740ac1d5687c2ec4ab6a993b9545f84a0c3a Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sat, 29 Jun 2024 12:45:02 +0700 Subject: [PATCH 26/42] feat: new qa --- docker-compose.qa.template.yml | 123 +++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 docker-compose.qa.template.yml diff --git a/docker-compose.qa.template.yml b/docker-compose.qa.template.yml new file mode 100644 index 0000000..e4c4345 --- /dev/null +++ b/docker-compose.qa.template.yml @@ -0,0 +1,123 @@ +version: "3.9" + +services: + gateway: + image: ghcr.io/isd-sgcu/rpkm67-gateway:latest + container_name: gateway + restart: unless-stopped + environment: + APP_PORT: 3001 + APP_ENV: development + APP_MAX_FILE_SIZE_MB: 10 + CORS_ORIGINS: http://localhost:3000 + SERVICE_AUTH: http://localhost:3002 + SERVICE_BACKEND: backend:3003 + SERVICE_CHECKIN: checkin:3004 + SERVICE_STORE: store:3005 + networks: + - rpkm67 + ports: + - "3001:3001" + + auth: + image: ghcr.io/isd-sgcu/rpkm67-auth:latest + container_name: auth + restart: unless-stopped + environment: + APP_PORT: 3002 + APP_ENV: development + DB_URL: postgres://root:1234@db:5432/rpkm67_db + REDIS_HOST: localhost + REDIS_PORT: 6379 + REDIS_PASSWORD: 5678 + JWT_SECRET: secret + JWT_ACCESS_TTL: 3600 + JWT_REFRESH_TTL: 259200 + JWT_ISSUER: rpkm67.sgcu.in.th + JWT_RESET_TOKEN_TTL: 900 + networks: + - rpkm67 + ports: + - "3002:3002" + + backend: + image: ghcr.io/isd-sgcu/rpkm67-backend:latest + container_name: backend + restart: unless-stopped + environment: + APP_PORT: 3003 + APP_ENV: development + DB_URL: postgres://root:1234@db:5432/rpkm67_db + REDIS_HOST: cache + REDIS_PORT: 6379 + REDIS_PASSWORD: 5678 + PIN_WORKSHOP_CODE: workshop + PIN_WORKSHOP_COUNT: 5 + PIN_LANDMARK_CODE: landmark + PIN_LANDMARK_COUNT: 4 + networks: + - rpkm67 + ports: + - "3003:3003" + + checkin: + image: ghcr.io/isd-sgcu/rpkm67-checkin:latest + container_name: checkin + restart: unless-stopped + environment: + APP_PORT: 3004 + APP_ENV: development + DB_URL: postgres://root:1234@db:5432/rpkm67_db + networks: + - rpkm67 + ports: + - "3004:3004" + + # store: + # image: ghcr.io/isd-sgcu/rpkm67-store:latest + # container_name: store + # restart: unless-stopped + # environment: + # APP_PORT: 3005 + # APP_ENV: development + # APP_MAX_FILE_SIZE_MB: 20 + # STORE_ENDPOINT: endpoint + # STORE_ACCESS_KEY: access_key + # STORE_SECRET_KEY: secret_key + # STORE_USE_SSL: true + # STORE_BUCKET_NAME: rpkm67-local + # networks: + # - rpkm67 + # ports: + # - "3005:3005" + + db: + image: postgres:15.1-alpine3.17 + container_name: db + restart: unless-stopped + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: "1234" + POSTGRES_DB: rpkm67_db + networks: + - rpkm67 + volumes: + - ./volumes/postgres:/var/lib/postgresql/data + ports: + - "5432:5432" + + cache: + image: redis:7.2.3-alpine + container_name: cache + restart: unless-stopped + environment: + REDIS_HOST: localhost + REDIS_PASSWORD: "5678" + networks: + - rpkm67 + ports: + - "6379:6379" + +networks: + rpkm67: + name: rpkm67 From 11741a29abcbe57d9ccdd42db1660218f7fd80d9 Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sat, 29 Jun 2024 13:17:59 +0700 Subject: [PATCH 27/42] feat: update proto --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 53b7a19..e018d59 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( require ( github.com/golang/mock v1.6.0 - github.com/isd-sgcu/rpkm67-go-proto v0.1.5 + github.com/isd-sgcu/rpkm67-go-proto v0.1.7 github.com/minio/minio-go/v7 v7.0.72 github.com/pkg/errors v0.9.1 go.uber.org/multierr v1.10.0 // indirect diff --git a/go.sum b/go.sum index b889de0..1a68134 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/isd-sgcu/rpkm67-go-proto v0.1.5 h1:WShrm8DeVqIY1ZelgM88vW8LMnLxF6dTt650A0YkC8U= github.com/isd-sgcu/rpkm67-go-proto v0.1.5/go.mod h1:Z5SYz5kEe4W+MdqPouF0zEOiaqvg+s9I1S5d0q6e+Jw= +github.com/isd-sgcu/rpkm67-go-proto v0.1.7 h1:FO8B4A7iuVS8PM4cEXe3HxZxlqdDY8va1zxy7TAnkqY= +github.com/isd-sgcu/rpkm67-go-proto v0.1.7/go.mod h1:Z5SYz5kEe4W+MdqPouF0zEOiaqvg+s9I1S5d0q6e+Jw= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= From 47b848d8e1dfe9a08cc13e83b3fcafff99fb57e1 Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sat, 29 Jun 2024 13:59:10 +0700 Subject: [PATCH 28/42] fix: auth vol qa --- docker-compose.qa.template.yml | 2 ++ microservices/auth/staff.template.json | 6 ++++++ 2 files changed, 8 insertions(+) create mode 100644 microservices/auth/staff.template.json diff --git a/docker-compose.qa.template.yml b/docker-compose.qa.template.yml index e4c4345..b8b1d98 100644 --- a/docker-compose.qa.template.yml +++ b/docker-compose.qa.template.yml @@ -37,6 +37,8 @@ services: JWT_RESET_TOKEN_TTL: 900 networks: - rpkm67 + volumes: + - ./microservices/auth:/app/config/staffs ports: - "3002:3002" diff --git a/microservices/auth/staff.template.json b/microservices/auth/staff.template.json new file mode 100644 index 0000000..2d355e8 --- /dev/null +++ b/microservices/auth/staff.template.json @@ -0,0 +1,6 @@ +{ + "staffs": [ + "6932203021", + "6932203121" + ] +} \ No newline at end of file From 2e275d3bbff34c329dbe5128746ad24d19865c23 Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sat, 29 Jun 2024 14:13:54 +0700 Subject: [PATCH 29/42] fix: makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d99286e..b42dea0 100644 --- a/Makefile +++ b/Makefile @@ -13,11 +13,11 @@ pull-latest-windows: docker pull ghcr.io/isd-sgcu/rpkm67-store:latest docker: - docker rm -v -f $$(docker ps -qa) + docker rm -v -f $$(docker ps -qa) || echo "No containers found. Skipping removal." docker-compose up docker-qa: - docker rm -v -f $$(docker ps -qa) + docker rm -v -f $$(docker ps -qa) || echo "No containers found. Skipping removal." docker-compose -f docker-compose.qa.yml up server: From 4d8f208eb9947cb3c1ba92a6949d6608451b9fe8 Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sat, 29 Jun 2024 14:59:42 +0700 Subject: [PATCH 30/42] fix: add oauth env qa --- docker-compose.qa.template.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker-compose.qa.template.yml b/docker-compose.qa.template.yml index b8b1d98..828d0d6 100644 --- a/docker-compose.qa.template.yml +++ b/docker-compose.qa.template.yml @@ -35,6 +35,9 @@ services: JWT_REFRESH_TTL: 259200 JWT_ISSUER: rpkm67.sgcu.in.th JWT_RESET_TOKEN_TTL: 900 + OAUTH_CLIENT_ID: client_id + OAUTH_CLIENT_SECRET: client_secret + OAUTH_REDIRECT_URI: http://localhost:3000 networks: - rpkm67 volumes: From ea88e272e35bcf27ef9c9117fdef65cc05673451 Mon Sep 17 00:00:00 2001 From: Teejuta Sriwaranon Date: Sat, 29 Jun 2024 16:28:46 +0700 Subject: [PATCH 31/42] feat: service tests --- internal/object/test/object.service_test.go | 147 +++++++++++++++++++- 1 file changed, 144 insertions(+), 3 deletions(-) diff --git a/internal/object/test/object.service_test.go b/internal/object/test/object.service_test.go index 4f69693..edd79e4 100644 --- a/internal/object/test/object.service_test.go +++ b/internal/object/test/object.service_test.go @@ -1,9 +1,16 @@ package test import ( + "context" + "fmt" "testing" "github.com/golang/mock/gomock" + + proto "github.com/isd-sgcu/rpkm67-go-proto/rpkm67/store/object/v1" + "github.com/isd-sgcu/rpkm67-store/internal/object" + "github.com/isd-sgcu/rpkm67-store/internal/utils" + mock_object "github.com/isd-sgcu/rpkm67-store/mocks/object" "github.com/stretchr/testify/suite" "go.uber.org/zap" @@ -12,9 +19,10 @@ import ( type ObjectServiceTest struct { suite.Suite - controller *gomock.Controller - conf *config.Store - logger *zap.Logger + controller *gomock.Controller + conf *config.Store + logger *zap.Logger + uploadObjectRequest *proto.UploadObjectRequest } func TestObjectService(t *testing.T) { @@ -28,4 +36,137 @@ func (t *ObjectServiceTest) SetupTest() { BucketName: "mock-bucket", Endpoint: "mock-endpoint", } + t.uploadObjectRequest = &proto.UploadObjectRequest{ + Filename: "object", + Data: []byte("data"), + } +} + +func (t *ObjectServiceTest) TestUploadInternalError() { + repo := mock_object.NewMockRepository(t.controller) + service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + + repo.EXPECT().Upload(t.uploadObjectRequest.Data, t.conf.BucketName, gomock.Any()).Return("", "", fmt.Errorf("error")) + + _, err := service.Upload(context.Background(), t.uploadObjectRequest) + + t.EqualError(err, "rpc error: code = Internal desc = Internal server error") +} + +func (t *ObjectServiceTest) TestUpload() { + repo := mock_object.NewMockRepository(t.controller) + service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + + repo.EXPECT().Upload(t.uploadObjectRequest.Data, t.conf.BucketName, gomock.Any()).Return("url", "key", nil) + + _, err := service.Upload(context.Background(), t.uploadObjectRequest) + + if err != nil { + fmt.Println(err) + } +} + +func (t *ObjectServiceTest) TestFindByKeyEmptyError() { + repo := mock_object.NewMockRepository(t.controller) + service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + + findByKeyInput := &proto.FindByKeyObjectRequest{ + Key: "", + } + + _, err := service.FindByKey(context.Background(), findByKeyInput) + + t.EqualError(err, "rpc error: code = InvalidArgument desc = Key is empty") +} + +func (t *ObjectServiceTest) TestFindByKeyInternalError() { + repo := mock_object.NewMockRepository(t.controller) + service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + + findByKeyInput := &proto.FindByKeyObjectRequest{ + Key: "key", + } + + repo.EXPECT().Get(t.conf.BucketName, findByKeyInput.Key).Return("", fmt.Errorf("error")) + + _, err := service.FindByKey(context.Background(), findByKeyInput) + + t.EqualError(err, "rpc error: code = Internal desc = Internal server error") +} + +func (t *ObjectServiceTest) TestFindByKeyNotFoundError() { + repo := mock_object.NewMockRepository(t.controller) + service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + + findByKeyInput := &proto.FindByKeyObjectRequest{ + Key: "key", + } + + repo.EXPECT().Get(t.conf.BucketName, findByKeyInput.Key).Return("", nil) + + _, err := service.FindByKey(context.Background(), findByKeyInput) + + t.EqualError(err, "rpc error: code = NotFound desc = Object not found") +} + +func (t *ObjectServiceTest) TestFindByKey() { + repo := mock_object.NewMockRepository(t.controller) + service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + + findByKeyInput := &proto.FindByKeyObjectRequest{ + Key: "key", + } + + repo.EXPECT().Get(t.conf.BucketName, findByKeyInput.Key).Return("url", nil) + + _, err := service.FindByKey(context.Background(), findByKeyInput) + + if err != nil { + fmt.Println(err) + } +} + +func (t *ObjectServiceTest) TestDeleteByKeyEmptyError() { + repo := mock_object.NewMockRepository(t.controller) + service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + + deleteByKeyInput := &proto.DeleteByKeyObjectRequest{ + Key: "", + } + + _, err := service.DeleteByKey(context.Background(), deleteByKeyInput) + + t.EqualError(err, "rpc error: code = InvalidArgument desc = Key is empty") +} + +func (t *ObjectServiceTest) TestDeleteByKeyInternalError() { + repo := mock_object.NewMockRepository(t.controller) + service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + + deleteByKeyInput := &proto.DeleteByKeyObjectRequest{ + Key: "key", + } + + repo.EXPECT().Delete(t.conf.BucketName, deleteByKeyInput.Key).Return(fmt.Errorf("error")) + + _, err := service.DeleteByKey(context.Background(), deleteByKeyInput) + + t.EqualError(err, "rpc error: code = Internal desc = Internal server error") +} + +func (t *ObjectServiceTest) TestDeleteByKey() { + repo := mock_object.NewMockRepository(t.controller) + service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + + deleteByKeyInput := &proto.DeleteByKeyObjectRequest{ + Key: "key", + } + + repo.EXPECT().Delete(t.conf.BucketName, deleteByKeyInput.Key).Return(nil) + + _, err := service.DeleteByKey(context.Background(), deleteByKeyInput) + + if err != nil { + fmt.Println(err) + } } From 5b77a23f5fb3739f23311cc92ef216dd4d882174 Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sat, 29 Jun 2024 16:33:59 +0700 Subject: [PATCH 32/42] fix: REDIS_HOST: cache --- docker-compose.qa.template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.qa.template.yml b/docker-compose.qa.template.yml index 828d0d6..9a378ce 100644 --- a/docker-compose.qa.template.yml +++ b/docker-compose.qa.template.yml @@ -27,7 +27,7 @@ services: APP_PORT: 3002 APP_ENV: development DB_URL: postgres://root:1234@db:5432/rpkm67_db - REDIS_HOST: localhost + REDIS_HOST: cache REDIS_PORT: 6379 REDIS_PASSWORD: 5678 JWT_SECRET: secret From e25b89f3cbbcc1211519c43950dfdb7a2fb1e08c Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sat, 29 Jun 2024 17:14:23 +0700 Subject: [PATCH 33/42] fix: SERVICE_STORE --- docker-compose.qa.template.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.qa.template.yml b/docker-compose.qa.template.yml index 9a378ce..6ec8c1c 100644 --- a/docker-compose.qa.template.yml +++ b/docker-compose.qa.template.yml @@ -10,10 +10,10 @@ services: APP_ENV: development APP_MAX_FILE_SIZE_MB: 10 CORS_ORIGINS: http://localhost:3000 - SERVICE_AUTH: http://localhost:3002 + SERVICE_AUTH: auth:3002 SERVICE_BACKEND: backend:3003 SERVICE_CHECKIN: checkin:3004 - SERVICE_STORE: store:3005 + SERVICE_STORE: localhost:3005 networks: - rpkm67 ports: From 9cd1d0fe7b1cf24f3d39e64f696797ddffffb49b Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sat, 29 Jun 2024 17:20:43 +0700 Subject: [PATCH 34/42] host.docker.internal --- docker-compose.qa.template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.qa.template.yml b/docker-compose.qa.template.yml index 6ec8c1c..3cd0c66 100644 --- a/docker-compose.qa.template.yml +++ b/docker-compose.qa.template.yml @@ -13,7 +13,7 @@ services: SERVICE_AUTH: auth:3002 SERVICE_BACKEND: backend:3003 SERVICE_CHECKIN: checkin:3004 - SERVICE_STORE: localhost:3005 + SERVICE_STORE: host.docker.internal:3005 networks: - rpkm67 ports: From b72c7ca65793df893848a762cee458846d0e450a Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sat, 29 Jun 2024 23:21:16 +0700 Subject: [PATCH 35/42] feat: prometheus grafana --- README.md | 56 +++++++++++++++++++++++++ docker-compose.qa.template.yml | 24 +++++++++++ microservices/prometheus/prometheus.yml | 9 ++++ 3 files changed, 89 insertions(+) create mode 100644 microservices/prometheus/prometheus.yml diff --git a/README.md b/README.md index 15711a5..c9a164d 100644 --- a/README.md +++ b/README.md @@ -1 +1,57 @@ # rpkm67-store + +## Stack + +- golang +- gRPC +- postgresql +- redis +- minio + +## Getting Started + +### Prerequisites + +- 💻 +- golang 1.22 or [later](https://go.dev) +- docker +- makefile +- [Go Air](https://github.com/air-verse/air) + +### Installation + +1. Clone this repo +2. Run `go mod download` to download all the dependencies. + +### Running only this service +1. Copy `.env.template` and paste it in the same directory as `.env`. Fill in the appropriate values. +2. Run `make docker`. +3. Run `make server` or `air` for hot-reload. + +### Running all RPKM67 services (all other services are run as containers) +1. Copy `docker-compose.qa.template.yml` and paste it in the same directory as `docker-compose.qa.yml`. Fill in the appropriate values. +2. In `microservices/auth` folder, copy `staff.template.json` and paste it in the same directory as `staff.json`. It is the staffs' student id list (given `staff` roles instead of `user`). +3. Run `make pull-latest-mac` or `make pull-latest-windows` to pull the latest images of other services. +4. Run `make docker-qa`. +5. Run `make server` or `air` for hot-reload. + +### Unit Testing +1. Run `make test` + +## API +When run locally, the gateway url will be available at `localhost:3001`. +- Swagger UI: `localhost:3001/api/v1/docs/index.html#/` +- Grafana: `localhost:3006` (username: admin, password: 1234) +- Prometheus: `localhost:9090` +- Gateway's metrics endpoint: `localhost:3001/metrics` + +## Other microservices/repositories of RPKM67 +- [gateway](https://github.com/isd-sgcu/rpkm67-gateway): Routing and request handling +- [auth](https://github.com/isd-sgcu/rpkm67-auth): Authentication and user service +- [backend](https://github.com/isd-sgcu/rpkm67-backend): Group, Baan selection and Stamp, Pin business logic +- [checkin](https://github.com/isd-sgcu/rpkm67-checkin): Checkin for events service +- [store](https://github.com/isd-sgcu/rpkm67-store): Object storage service for user profile pictures +- [model](https://github.com/isd-sgcu/rpkm67-model): SQL table schema and models +- [proto](https://github.com/isd-sgcu/rpkm67-proto): Protobuf files generator +- [go-proto](https://github.com/isd-sgcu/rpkm67-go-proto): Generated protobuf files for golang +- [frontend](https://github.com/isd-sgcu/firstdate-rpkm67-frontend): Frontend web application diff --git a/docker-compose.qa.template.yml b/docker-compose.qa.template.yml index 3cd0c66..c8e39db 100644 --- a/docker-compose.qa.template.yml +++ b/docker-compose.qa.template.yml @@ -123,6 +123,30 @@ services: ports: - "6379:6379" + prometheus: + image: prom/prometheus:latest + container_name: prometheus + restart: unless-stopped + networks: + - rpkm67 + volumes: + - ./microservices/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml + ports: + - "9090:9090" + + grafana: + image: grafana/grafana:latest + container_name: grafana + restart: unless-stopped + environment: + - GF_SECURITY_ADMIN_PASSWORD=1234 + networks: + - rpkm67 + volumes: + - ./volumes/grafana:/var/lib/grafana + ports: + - "3006:3000" + networks: rpkm67: name: rpkm67 diff --git a/microservices/prometheus/prometheus.yml b/microservices/prometheus/prometheus.yml new file mode 100644 index 0000000..f6215f4 --- /dev/null +++ b/microservices/prometheus/prometheus.yml @@ -0,0 +1,9 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + - job_name: gateway-api + static_configs: + - targets: ['host.docker.internal:3001'] + metrics_path: '/api/v1/metrics' \ No newline at end of file From 0676a62e6a230668cb46d3b44b4144fba49b0ce5 Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sun, 30 Jun 2024 00:13:07 +0700 Subject: [PATCH 36/42] fix: prom target --- microservices/prometheus/prometheus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microservices/prometheus/prometheus.yml b/microservices/prometheus/prometheus.yml index f6215f4..3d752c6 100644 --- a/microservices/prometheus/prometheus.yml +++ b/microservices/prometheus/prometheus.yml @@ -5,5 +5,5 @@ global: scrape_configs: - job_name: gateway-api static_configs: - - targets: ['host.docker.internal:3001'] + - targets: ['gateway:3001'] metrics_path: '/api/v1/metrics' \ No newline at end of file From 308f5e815ba516485e688512e062e9274168890e Mon Sep 17 00:00:00 2001 From: Teejuta Sriwaranon Date: Sun, 30 Jun 2024 11:41:42 +0700 Subject: [PATCH 37/42] test: service tests --- internal/object/test/object.service_test.go | 111 +++++++++++--------- 1 file changed, 63 insertions(+), 48 deletions(-) diff --git a/internal/object/test/object.service_test.go b/internal/object/test/object.service_test.go index edd79e4..6ef4bd2 100644 --- a/internal/object/test/object.service_test.go +++ b/internal/object/test/object.service_test.go @@ -6,8 +6,11 @@ import ( "testing" "github.com/golang/mock/gomock" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" proto "github.com/isd-sgcu/rpkm67-go-proto/rpkm67/store/object/v1" + "github.com/isd-sgcu/rpkm67-store/constant" "github.com/isd-sgcu/rpkm67-store/internal/object" "github.com/isd-sgcu/rpkm67-store/internal/utils" mock_object "github.com/isd-sgcu/rpkm67-store/mocks/object" @@ -44,129 +47,141 @@ func (t *ObjectServiceTest) SetupTest() { func (t *ObjectServiceTest) TestUploadInternalError() { repo := mock_object.NewMockRepository(t.controller) - service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) - repo.EXPECT().Upload(t.uploadObjectRequest.Data, t.conf.BucketName, gomock.Any()).Return("", "", fmt.Errorf("error")) - _, err := service.Upload(context.Background(), t.uploadObjectRequest) + svc := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + + expectedErr := status.Error(codes.Internal, constant.InternalServerErrorMessage).Error() + + actual, err := svc.Upload(context.Background(), t.uploadObjectRequest) - t.EqualError(err, "rpc error: code = Internal desc = Internal server error") + t.Nil(actual) + t.EqualError(err, expectedErr) } -func (t *ObjectServiceTest) TestUpload() { +func (t *ObjectServiceTest) TestUploadSuccess() { repo := mock_object.NewMockRepository(t.controller) - service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) - repo.EXPECT().Upload(t.uploadObjectRequest.Data, t.conf.BucketName, gomock.Any()).Return("url", "key", nil) - _, err := service.Upload(context.Background(), t.uploadObjectRequest) + svc := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) - if err != nil { - fmt.Println(err) - } + _, err := svc.Upload(context.Background(), t.uploadObjectRequest) + + t.Nil(err) } func (t *ObjectServiceTest) TestFindByKeyEmptyError() { repo := mock_object.NewMockRepository(t.controller) - service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + srv := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) findByKeyInput := &proto.FindByKeyObjectRequest{ Key: "", } - _, err := service.FindByKey(context.Background(), findByKeyInput) + expectedErr := status.Error(codes.InvalidArgument, constant.KeyEmptyErrorMessage).Error() + + actual, err := srv.FindByKey(context.Background(), findByKeyInput) - t.EqualError(err, "rpc error: code = InvalidArgument desc = Key is empty") + t.Nil(actual) + t.EqualError(err, expectedErr) } func (t *ObjectServiceTest) TestFindByKeyInternalError() { - repo := mock_object.NewMockRepository(t.controller) - service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) - findByKeyInput := &proto.FindByKeyObjectRequest{ Key: "key", } + repo := mock_object.NewMockRepository(t.controller) repo.EXPECT().Get(t.conf.BucketName, findByKeyInput.Key).Return("", fmt.Errorf("error")) - _, err := service.FindByKey(context.Background(), findByKeyInput) + srv := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + + expectedErr := status.Error(codes.Internal, constant.InternalServerErrorMessage).Error() - t.EqualError(err, "rpc error: code = Internal desc = Internal server error") + actual, err := srv.FindByKey(context.Background(), findByKeyInput) + + t.Nil(actual) + t.EqualError(err, expectedErr) } func (t *ObjectServiceTest) TestFindByKeyNotFoundError() { - repo := mock_object.NewMockRepository(t.controller) - service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) - findByKeyInput := &proto.FindByKeyObjectRequest{ Key: "key", } + repo := mock_object.NewMockRepository(t.controller) repo.EXPECT().Get(t.conf.BucketName, findByKeyInput.Key).Return("", nil) - _, err := service.FindByKey(context.Background(), findByKeyInput) + srv := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) - t.EqualError(err, "rpc error: code = NotFound desc = Object not found") -} + expectedErr := status.Error(codes.NotFound, constant.ObjectNotFoundErrorMessage).Error() -func (t *ObjectServiceTest) TestFindByKey() { - repo := mock_object.NewMockRepository(t.controller) - service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + actual, err := srv.FindByKey(context.Background(), findByKeyInput) + t.Nil(actual) + t.EqualError(err, expectedErr) +} + +func (t *ObjectServiceTest) TestFindByKeySuccess() { findByKeyInput := &proto.FindByKeyObjectRequest{ Key: "key", } + repo := mock_object.NewMockRepository(t.controller) repo.EXPECT().Get(t.conf.BucketName, findByKeyInput.Key).Return("url", nil) - _, err := service.FindByKey(context.Background(), findByKeyInput) + srv := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) - if err != nil { - fmt.Println(err) - } + _, err := srv.FindByKey(context.Background(), findByKeyInput) + + t.Nil(err) } func (t *ObjectServiceTest) TestDeleteByKeyEmptyError() { repo := mock_object.NewMockRepository(t.controller) - service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + srv := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) deleteByKeyInput := &proto.DeleteByKeyObjectRequest{ Key: "", } - _, err := service.DeleteByKey(context.Background(), deleteByKeyInput) + expectedErr := status.Error(codes.InvalidArgument, constant.KeyEmptyErrorMessage).Error() + + actual, err := srv.DeleteByKey(context.Background(), deleteByKeyInput) - t.EqualError(err, "rpc error: code = InvalidArgument desc = Key is empty") + t.Equal(actual.Success, false) + t.EqualError(err, expectedErr) } func (t *ObjectServiceTest) TestDeleteByKeyInternalError() { - repo := mock_object.NewMockRepository(t.controller) - service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) - deleteByKeyInput := &proto.DeleteByKeyObjectRequest{ Key: "key", } + repo := mock_object.NewMockRepository(t.controller) repo.EXPECT().Delete(t.conf.BucketName, deleteByKeyInput.Key).Return(fmt.Errorf("error")) - _, err := service.DeleteByKey(context.Background(), deleteByKeyInput) + srv := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) - t.EqualError(err, "rpc error: code = Internal desc = Internal server error") -} + expectedErr := status.Error(codes.Internal, constant.InternalServerErrorMessage).Error() -func (t *ObjectServiceTest) TestDeleteByKey() { - repo := mock_object.NewMockRepository(t.controller) - service := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) + actual, err := srv.DeleteByKey(context.Background(), deleteByKeyInput) + + t.Equal(actual.Success, false) + t.EqualError(err, expectedErr) +} +func (t *ObjectServiceTest) TestDeleteByKeySuccess() { deleteByKeyInput := &proto.DeleteByKeyObjectRequest{ Key: "key", } + repo := mock_object.NewMockRepository(t.controller) repo.EXPECT().Delete(t.conf.BucketName, deleteByKeyInput.Key).Return(nil) - _, err := service.DeleteByKey(context.Background(), deleteByKeyInput) + srv := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) - if err != nil { - fmt.Println(err) - } + _, err := srv.DeleteByKey(context.Background(), deleteByKeyInput) + + t.Nil(err) } From 34e7d5a40f930c115563da8d8c1ecdebb409a551 Mon Sep 17 00:00:00 2001 From: Teejuta Sriwaranon Date: Sun, 30 Jun 2024 13:31:32 +0700 Subject: [PATCH 38/42] test: service tests --- internal/object/test/object.service_test.go | 23 ++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/internal/object/test/object.service_test.go b/internal/object/test/object.service_test.go index 6ef4bd2..416baab 100644 --- a/internal/object/test/object.service_test.go +++ b/internal/object/test/object.service_test.go @@ -65,9 +65,17 @@ func (t *ObjectServiceTest) TestUploadSuccess() { svc := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) - _, err := svc.Upload(context.Background(), t.uploadObjectRequest) + expected := &proto.UploadObjectResponse{ + Object: &proto.Object{ + Key: "key", + Url: "url", + }, + } + + actual, err := svc.Upload(context.Background(), t.uploadObjectRequest) t.Nil(err) + t.Equal(expected, actual) } func (t *ObjectServiceTest) TestFindByKeyEmptyError() { @@ -132,9 +140,17 @@ func (t *ObjectServiceTest) TestFindByKeySuccess() { srv := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) - _, err := srv.FindByKey(context.Background(), findByKeyInput) + expected := &proto.FindByKeyObjectResponse{ + Object: &proto.Object{ + Key: findByKeyInput.Key, + Url: "url", + }, + } + + actual, err := srv.FindByKey(context.Background(), findByKeyInput) t.Nil(err) + t.Equal(expected, actual) } func (t *ObjectServiceTest) TestDeleteByKeyEmptyError() { @@ -181,7 +197,8 @@ func (t *ObjectServiceTest) TestDeleteByKeySuccess() { srv := object.NewService(repo, t.conf, t.logger, utils.NewRandomUtils()) - _, err := srv.DeleteByKey(context.Background(), deleteByKeyInput) + actual, err := srv.DeleteByKey(context.Background(), deleteByKeyInput) t.Nil(err) + t.Equal(actual.Success, true) } From d2a4166b90cf16929f0d40053e57d0635efc5e2c Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Sun, 30 Jun 2024 16:23:06 +0700 Subject: [PATCH 39/42] update proto --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index e018d59..9de9cab 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( require ( github.com/golang/mock v1.6.0 - github.com/isd-sgcu/rpkm67-go-proto v0.1.7 + github.com/isd-sgcu/rpkm67-go-proto v0.2.0 github.com/minio/minio-go/v7 v7.0.72 github.com/pkg/errors v0.9.1 go.uber.org/multierr v1.10.0 // indirect diff --git a/go.sum b/go.sum index 1a68134..a97f9e4 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,8 @@ github.com/isd-sgcu/rpkm67-go-proto v0.1.5 h1:WShrm8DeVqIY1ZelgM88vW8LMnLxF6dTt6 github.com/isd-sgcu/rpkm67-go-proto v0.1.5/go.mod h1:Z5SYz5kEe4W+MdqPouF0zEOiaqvg+s9I1S5d0q6e+Jw= github.com/isd-sgcu/rpkm67-go-proto v0.1.7 h1:FO8B4A7iuVS8PM4cEXe3HxZxlqdDY8va1zxy7TAnkqY= github.com/isd-sgcu/rpkm67-go-proto v0.1.7/go.mod h1:Z5SYz5kEe4W+MdqPouF0zEOiaqvg+s9I1S5d0q6e+Jw= +github.com/isd-sgcu/rpkm67-go-proto v0.2.0 h1:tPfNgCuqS4g0f+2hzcpY+8hYXSa7DZDPvRejRzOk2cI= +github.com/isd-sgcu/rpkm67-go-proto v0.2.0/go.mod h1:Z5SYz5kEe4W+MdqPouF0zEOiaqvg+s9I1S5d0q6e+Jw= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= From 9d6715a62c14b7ff7c0739397692cee938751f79 Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Sun, 30 Jun 2024 20:01:09 +0700 Subject: [PATCH 40/42] fix: file extension --- internal/object/object.service.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/internal/object/object.service.go b/internal/object/object.service.go index 4cd6146..bf3d4b8 100644 --- a/internal/object/object.service.go +++ b/internal/object/object.service.go @@ -3,6 +3,8 @@ package object import ( "context" "fmt" + "path/filepath" + "strings" proto "github.com/isd-sgcu/rpkm67-go-proto/rpkm67/store/object/v1" "github.com/isd-sgcu/rpkm67-store/config" @@ -41,7 +43,11 @@ func (s *serviceImpl) Upload(_ context.Context, req *proto.UploadObjectRequest) return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage) } - objectKey := req.Filename + "_" + randomString + ext := filepath.Ext(req.Filename) + filename := strings.TrimSuffix(req.Filename, ext) + objectKey := filename + "_" + randomString + ext + + fmt.Println("objectKey: ", objectKey) url, key, err := s.repo.Upload(req.Data, s.conf.BucketName, objectKey) if err != nil { From 31060d1e340b0b9b629b590d3b13738a21084aef Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Sun, 30 Jun 2024 20:02:36 +0700 Subject: [PATCH 41/42] chore --- internal/object/object.service.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/object/object.service.go b/internal/object/object.service.go index bf3d4b8..d80f0b3 100644 --- a/internal/object/object.service.go +++ b/internal/object/object.service.go @@ -47,8 +47,6 @@ func (s *serviceImpl) Upload(_ context.Context, req *proto.UploadObjectRequest) filename := strings.TrimSuffix(req.Filename, ext) objectKey := filename + "_" + randomString + ext - fmt.Println("objectKey: ", objectKey) - url, key, err := s.repo.Upload(req.Data, s.conf.BucketName, objectKey) if err != nil { s.log.Named("Upload").Error("Upload: ", zap.Error(err)) From 433ecd7003f5ff25bab088974889875dc0a0c963 Mon Sep 17 00:00:00 2001 From: Idhibhat Pankam Date: Fri, 12 Jul 2024 23:23:45 +0700 Subject: [PATCH 42/42] new wk --- .../pull_request_template.md | 21 --- .github/pull_request_template.md | 14 ++ .github/workflows/build-deploy.yml | 61 ++++++- .../workflows/{test-build.yml => lint.yml} | 8 +- .../{run-unit-test.yml => unit-test.yml} | 13 +- docker-compose.qa.template.yml | 152 ------------------ 6 files changed, 81 insertions(+), 188 deletions(-) delete mode 100644 .github/PULL_REQUEST_TEMPLATE/pull_request_template.md create mode 100644 .github/pull_request_template.md rename .github/workflows/{test-build.yml => lint.yml} (84%) rename .github/workflows/{run-unit-test.yml => unit-test.yml} (86%) delete mode 100644 docker-compose.qa.template.yml diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md deleted file mode 100644 index 3833a16..0000000 --- a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +++ /dev/null @@ -1,21 +0,0 @@ -## Change made - -- [ ] New features -- [ ] Bug fixes -- [ ] Breaking changes - -## Describe what you have done - -- - -### New Features - -- - -### Fix - -- - -### Others - -- diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..c38d93b --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,14 @@ +## Change made + +- [ ]  New features +- [ ]  Bug fixes +- [ ]  Breaking changes +- [ ] Refactor +## Describe what you have done +- +### New Features +- +### Fix +- +### Others +- diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index ac5db1e..db600c6 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -2,27 +2,47 @@ name: Build on: workflow_dispatch: - push: + pull_request: + types: + - closed branches: - main - dev - tags: - - v* env: + SERVICE_NAME: rpkm67-store IMAGE_NAME: ghcr.io/${{ github.repository }} - IMAGE_TAG: ${{ github.sha }} + IMAGE_TAG: jobs: build: name: Build runs-on: ubuntu-latest + if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' + outputs: + IMAGE_TAG: ${{ steps.tag_action.outputs.new_tag }} permissions: - contents: read + contents: write packages: write steps: + - uses: actions/checkout@v4 + with: + fetch-depth: '0' + + - name: Bump version and push tag + uses: anothrNick/github-tag-action@1.64.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + WITH_V: true + RELEASE_BRANCHES: dev + DEFAULT_BUMP: patch + id: tag_action + + - name: Set IMAGE_TAG + run: echo "IMAGE_TAG=${{ steps.tag_action.outputs.new_tag }}" >> $GITHUB_ENV + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 @@ -40,5 +60,34 @@ jobs: tags: ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }},${{ env.IMAGE_NAME }}:latest cache-from: type=registry,ref=${{ env.IMAGE_NAME }}:buildcache cache-to: type=registry,ref=${{ env.IMAGE_NAME }}:buildcache,mode=max + + cd: + name: Continuous Deployment + needs: build + runs-on: ubuntu-latest + env: + IMAGE_TAG: ${{ needs.build.outputs.IMAGE_TAG }} -# docker pull --platform linux/x86_64 \ No newline at end of file + steps: + - name: Checkout DevOps repository + uses: actions/checkout@v4 + with: + repository: isd-sgcu/rpkm67-devops + token: ${{ secrets.RPKM67_DEVOPS_TOKEN }} + + - name: Update image tag in dev + uses: mikefarah/yq@master + with: + cmd: yq -i '.[0].value = "${{ env.IMAGE_NAME }}:" + strenv(IMAGE_TAG)' isd/${{ env.SERVICE_NAME }}/deployment.yaml + + - name: Update image tag in prod + uses: mikefarah/yq@master + if: github.ref == 'refs/heads/main' + with: + cmd: yq -i '.[0].value = "${{ env.IMAGE_NAME }}:" + strenv(IMAGE_TAG)' prod/${{ env.SERVICE_NAME }}/deployment.yaml + + - name: Commit & Push changes + uses: actions-js/push@v1.4 + with: + repository: isd-sgcu/rpkm67-devops + github_token: ${{ secrets.RPKM67_DEVOPS_TOKEN }} diff --git a/.github/workflows/test-build.yml b/.github/workflows/lint.yml similarity index 84% rename from .github/workflows/test-build.yml rename to .github/workflows/lint.yml index 762f358..ffba9ce 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/lint.yml @@ -1,13 +1,17 @@ name: Lint on: + workflow_dispatch: + pull_request: + branches: + - main + - dev push: branches: - main - dev tags: - v* - pull_request: permissions: contents: read @@ -24,7 +28,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: - go-version: "1.22.4" + go-version: '1.22.4' cache: false - name: golangci-lint uses: golangci/golangci-lint-action@v3 diff --git a/.github/workflows/run-unit-test.yml b/.github/workflows/unit-test.yml similarity index 86% rename from .github/workflows/run-unit-test.yml rename to .github/workflows/unit-test.yml index f5be40f..e0696c9 100644 --- a/.github/workflows/run-unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -1,18 +1,17 @@ -name: "Pull request/Push: Run unit test" +name: Unit Tests on: + workflow_dispatch: pull_request: branches: - - dev - - master - main - - beta + - dev push: branches: - - dev - - master - main - - beta + - dev + tags: + - v* jobs: build: diff --git a/docker-compose.qa.template.yml b/docker-compose.qa.template.yml deleted file mode 100644 index c8e39db..0000000 --- a/docker-compose.qa.template.yml +++ /dev/null @@ -1,152 +0,0 @@ -version: "3.9" - -services: - gateway: - image: ghcr.io/isd-sgcu/rpkm67-gateway:latest - container_name: gateway - restart: unless-stopped - environment: - APP_PORT: 3001 - APP_ENV: development - APP_MAX_FILE_SIZE_MB: 10 - CORS_ORIGINS: http://localhost:3000 - SERVICE_AUTH: auth:3002 - SERVICE_BACKEND: backend:3003 - SERVICE_CHECKIN: checkin:3004 - SERVICE_STORE: host.docker.internal:3005 - networks: - - rpkm67 - ports: - - "3001:3001" - - auth: - image: ghcr.io/isd-sgcu/rpkm67-auth:latest - container_name: auth - restart: unless-stopped - environment: - APP_PORT: 3002 - APP_ENV: development - DB_URL: postgres://root:1234@db:5432/rpkm67_db - REDIS_HOST: cache - REDIS_PORT: 6379 - REDIS_PASSWORD: 5678 - JWT_SECRET: secret - JWT_ACCESS_TTL: 3600 - JWT_REFRESH_TTL: 259200 - JWT_ISSUER: rpkm67.sgcu.in.th - JWT_RESET_TOKEN_TTL: 900 - OAUTH_CLIENT_ID: client_id - OAUTH_CLIENT_SECRET: client_secret - OAUTH_REDIRECT_URI: http://localhost:3000 - networks: - - rpkm67 - volumes: - - ./microservices/auth:/app/config/staffs - ports: - - "3002:3002" - - backend: - image: ghcr.io/isd-sgcu/rpkm67-backend:latest - container_name: backend - restart: unless-stopped - environment: - APP_PORT: 3003 - APP_ENV: development - DB_URL: postgres://root:1234@db:5432/rpkm67_db - REDIS_HOST: cache - REDIS_PORT: 6379 - REDIS_PASSWORD: 5678 - PIN_WORKSHOP_CODE: workshop - PIN_WORKSHOP_COUNT: 5 - PIN_LANDMARK_CODE: landmark - PIN_LANDMARK_COUNT: 4 - networks: - - rpkm67 - ports: - - "3003:3003" - - checkin: - image: ghcr.io/isd-sgcu/rpkm67-checkin:latest - container_name: checkin - restart: unless-stopped - environment: - APP_PORT: 3004 - APP_ENV: development - DB_URL: postgres://root:1234@db:5432/rpkm67_db - networks: - - rpkm67 - ports: - - "3004:3004" - - # store: - # image: ghcr.io/isd-sgcu/rpkm67-store:latest - # container_name: store - # restart: unless-stopped - # environment: - # APP_PORT: 3005 - # APP_ENV: development - # APP_MAX_FILE_SIZE_MB: 20 - # STORE_ENDPOINT: endpoint - # STORE_ACCESS_KEY: access_key - # STORE_SECRET_KEY: secret_key - # STORE_USE_SSL: true - # STORE_BUCKET_NAME: rpkm67-local - # networks: - # - rpkm67 - # ports: - # - "3005:3005" - - db: - image: postgres:15.1-alpine3.17 - container_name: db - restart: unless-stopped - environment: - POSTGRES_USER: root - POSTGRES_PASSWORD: "1234" - POSTGRES_DB: rpkm67_db - networks: - - rpkm67 - volumes: - - ./volumes/postgres:/var/lib/postgresql/data - ports: - - "5432:5432" - - cache: - image: redis:7.2.3-alpine - container_name: cache - restart: unless-stopped - environment: - REDIS_HOST: localhost - REDIS_PASSWORD: "5678" - networks: - - rpkm67 - ports: - - "6379:6379" - - prometheus: - image: prom/prometheus:latest - container_name: prometheus - restart: unless-stopped - networks: - - rpkm67 - volumes: - - ./microservices/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml - ports: - - "9090:9090" - - grafana: - image: grafana/grafana:latest - container_name: grafana - restart: unless-stopped - environment: - - GF_SECURITY_ADMIN_PASSWORD=1234 - networks: - - rpkm67 - volumes: - - ./volumes/grafana:/var/lib/grafana - ports: - - "3006:3000" - -networks: - rpkm67: - name: rpkm67