diff options
author | Baitinq <manuelpalenzuelamerino@gmail.com> | 2024-05-27 21:30:54 +0200 |
---|---|---|
committer | Baitinq <manuelpalenzuelamerino@gmail.com> | 2024-05-27 23:52:44 +0200 |
commit | 81621c4f64f4a1c29906643f53314e2f71a014ae (patch) | |
tree | 503cc56b609dcad8f8eca6e64b6b9b15b02fd4fd | |
parent | payload-processor: fmt (diff) | |
download | fs-tracer-backend-81621c4f64f4a1c29906643f53314e2f71a014ae.tar.gz fs-tracer-backend-81621c4f64f4a1c29906643f53314e2f71a014ae.tar.bz2 fs-tracer-backend-81621c4f64f4a1c29906643f53314e2f71a014ae.zip |
rest-api: connect to db and add /file/ GET endpoint
TODO: Only get files for your specific user
-rw-r--r-- | k8s/rest-api/BUILD.bazel | 5 | ||||
-rw-r--r-- | k8s/rest-api/templates/deployment.yaml | 2 | ||||
-rw-r--r-- | k8s/rest-api/values.yaml | 3 | ||||
-rw-r--r-- | lib/BUILD.bazel | 8 | ||||
-rw-r--r-- | lib/model.go (renamed from src/payload-processor/processor/model.go) | 3 | ||||
-rw-r--r-- | src/payload-processor/processor/BUILD.bazel | 3 | ||||
-rw-r--r-- | src/payload-processor/processor/db.go | 5 | ||||
-rw-r--r-- | src/payload-processor/processor/mock_db.go | 3 | ||||
-rw-r--r-- | src/payload-processor/processor/processor.go | 3 | ||||
-rw-r--r-- | src/payload-processor/processor/processor_test.go | 3 | ||||
-rw-r--r-- | src/rest-api/cmd/BUILD.bazel | 1 | ||||
-rw-r--r-- | src/rest-api/cmd/main.go | 14 | ||||
-rw-r--r-- | src/rest-api/handler/BUILD.bazel | 27 | ||||
-rw-r--r-- | src/rest-api/handler/db.go | 37 | ||||
-rw-r--r-- | src/rest-api/handler/handler.go | 38 | ||||
-rw-r--r-- | src/rest-api/handler/handler_test.go | 31 | ||||
-rw-r--r-- | src/rest-api/handler/mock_db.go | 56 |
17 files changed, 228 insertions, 14 deletions
diff --git a/k8s/rest-api/BUILD.bazel b/k8s/rest-api/BUILD.bazel index 9ebdaf9..1364361 100644 --- a/k8s/rest-api/BUILD.bazel +++ b/k8s/rest-api/BUILD.bazel @@ -6,6 +6,9 @@ helm_chart( images = ["//src/rest-api/cmd:push"], install_name = "rest-api", stamp = 1, - substitutions = {"IMAGE_TAG": "rest-api-{STABLE_GIT_SHA}"}, + substitutions = { + "IMAGE_TAG": "rest-api-{STABLE_GIT_SHA}", + "DB_PASSWORD": "{DB_PASSWORD}", + }, values = "values.yaml", ) diff --git a/k8s/rest-api/templates/deployment.yaml b/k8s/rest-api/templates/deployment.yaml index f2b15d8..aa61cb6 100644 --- a/k8s/rest-api/templates/deployment.yaml +++ b/k8s/rest-api/templates/deployment.yaml @@ -50,6 +50,8 @@ spec: secretKeyRef: name: kafka-user-passwords key: client-passwords + - name: DB_PASSWORD + value: {{ .Values.db.password }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/k8s/rest-api/values.yaml b/k8s/rest-api/values.yaml index f79e04f..3d85a51 100644 --- a/k8s/rest-api/values.yaml +++ b/k8s/rest-api/values.yaml @@ -7,6 +7,9 @@ replicaCount: 1 +db: + password: "{DB_PASSWORD}" + image: repository: docker.io/baitinq/fs-tracer pullPolicy: IfNotPresent diff --git a/lib/BUILD.bazel b/lib/BUILD.bazel new file mode 100644 index 0000000..a9a0e52 --- /dev/null +++ b/lib/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "lib", + srcs = ["model.go"], + importpath = "github.com/Baitinq/fs-tracer-backend/lib", + visibility = ["//visibility:public"], +) diff --git a/src/payload-processor/processor/model.go b/lib/model.go index 4921f3d..be86378 100644 --- a/src/payload-processor/processor/model.go +++ b/lib/model.go @@ -1,8 +1,9 @@ -package processor +package lib import "time" type File struct { + Id int `db:"id"` User_id string `db:"user_id"` Absolute_path string `db:"absolute_path"` Contents string `db:"contents"` diff --git a/src/payload-processor/processor/BUILD.bazel b/src/payload-processor/processor/BUILD.bazel index d8d8afe..411300d 100644 --- a/src/payload-processor/processor/BUILD.bazel +++ b/src/payload-processor/processor/BUILD.bazel @@ -5,12 +5,12 @@ go_library( srcs = [ "db.go", "mock_db.go", - "model.go", "processor.go", ], importpath = "github.com/Baitinq/fs-tracer-backend/src/payload-processor/processor", visibility = ["//visibility:public"], deps = [ + "//lib", "@com_github_google_uuid//:uuid", "@com_github_jmoiron_sqlx//:sqlx", "@com_github_segmentio_kafka_go//:kafka-go", @@ -23,6 +23,7 @@ go_test( srcs = ["processor_test.go"], embed = [":processor"], deps = [ + "//lib", "@com_github_segmentio_kafka_go//:kafka-go", "@com_github_stretchr_testify//require", "@org_uber_go_mock//gomock", diff --git a/src/payload-processor/processor/db.go b/src/payload-processor/processor/db.go index ce7d5bb..45ec484 100644 --- a/src/payload-processor/processor/db.go +++ b/src/payload-processor/processor/db.go @@ -3,12 +3,13 @@ package processor import ( "context" + "github.com/Baitinq/fs-tracer-backend/lib" "github.com/jmoiron/sqlx" ) //go:generate mockgen -source=$GOFILE -package=$GOPACKAGE -destination=mock_$GOFILE type DB interface { - InsertFile(ctx context.Context, file File) error + InsertFile(ctx context.Context, file lib.File) error } type DBImpl struct { @@ -21,7 +22,7 @@ func NewDB(db *sqlx.DB) DB { return &DBImpl{db: db} } -func (db DBImpl) InsertFile(ctx context.Context, file File) error { +func (db DBImpl) InsertFile(ctx context.Context, file lib.File) error { _, err := db.db.NamedExecContext(ctx, "INSERT INTO private.file (user_id, absolute_path, contents, timestamp) VALUES (:user_id, :absolute_path, :contents, :timestamp)", file) return err } diff --git a/src/payload-processor/processor/mock_db.go b/src/payload-processor/processor/mock_db.go index 53e5491..1b283bd 100644 --- a/src/payload-processor/processor/mock_db.go +++ b/src/payload-processor/processor/mock_db.go @@ -13,6 +13,7 @@ import ( context "context" reflect "reflect" + lib "github.com/Baitinq/fs-tracer-backend/lib" gomock "go.uber.org/mock/gomock" ) @@ -40,7 +41,7 @@ func (m *MockDB) EXPECT() *MockDBMockRecorder { } // InsertFile mocks base method. -func (m *MockDB) InsertFile(ctx context.Context, file File) error { +func (m *MockDB) InsertFile(ctx context.Context, file lib.File) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertFile", ctx, file) ret0, _ := ret[0].(error) diff --git a/src/payload-processor/processor/processor.go b/src/payload-processor/processor/processor.go index 889f4c0..ab128cb 100644 --- a/src/payload-processor/processor/processor.go +++ b/src/payload-processor/processor/processor.go @@ -11,6 +11,7 @@ import ( "syscall" "time" + "github.com/Baitinq/fs-tracer-backend/lib" "github.com/google/uuid" "github.com/jmoiron/sqlx" "github.com/segmentio/kafka-go" @@ -92,7 +93,7 @@ func (p Processor) process(ctx context.Context, cancel context.CancelFunc) { func (p Processor) handleMessage(ctx context.Context, m kafka.Message) error { fmt.Printf("(%s): message at paritition %d: offset %d: %s = %s\n", time.Now().String(), m.Partition, m.Offset, string(m.Key), string(m.Value)) - var file File + var file lib.File err := json.Unmarshal(m.Value, &file) if err != nil { return err diff --git a/src/payload-processor/processor/processor_test.go b/src/payload-processor/processor/processor_test.go index d3778de..39e965e 100644 --- a/src/payload-processor/processor/processor_test.go +++ b/src/payload-processor/processor/processor_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/Baitinq/fs-tracer-backend/lib" "github.com/segmentio/kafka-go" "github.com/stretchr/testify/require" gomock "go.uber.org/mock/gomock" @@ -28,7 +29,7 @@ func TestProcessMessage(t *testing.T) { ctx := context.Background() - mockdb.EXPECT().InsertFile(ctx, File{ + mockdb.EXPECT().InsertFile(ctx, lib.File{ User_id: "1", Absolute_path: "/tmp/file.txt", Contents: "hello world", diff --git a/src/rest-api/cmd/BUILD.bazel b/src/rest-api/cmd/BUILD.bazel index 4ecded7..0d96a1f 100644 --- a/src/rest-api/cmd/BUILD.bazel +++ b/src/rest-api/cmd/BUILD.bazel @@ -11,6 +11,7 @@ go_library( visibility = ["//visibility:private"], deps = [ "//src/rest-api/handler", + "@com_github_jmoiron_sqlx//:sqlx", "@com_github_segmentio_kafka_go//:kafka-go", "@com_github_segmentio_kafka_go//sasl/plain", ], diff --git a/src/rest-api/cmd/main.go b/src/rest-api/cmd/main.go index d1e44b3..98f3722 100644 --- a/src/rest-api/cmd/main.go +++ b/src/rest-api/cmd/main.go @@ -9,6 +9,7 @@ import ( "time" "github.com/Baitinq/fs-tracer-backend/src/rest-api/handler" + "github.com/jmoiron/sqlx" "github.com/segmentio/kafka-go" "github.com/segmentio/kafka-go/sasl/plain" ) @@ -47,13 +48,22 @@ func main() { AllowAutoTopicCreation: true, } - handler := handler.NewHandler(kafka_writer) + db_password, ok := os.LookupEnv("DB_PASSWORD") + if !ok { + log.Fatal("DB_PASSWORD not set") + } + db, err := sqlx.Connect("postgres", fmt.Sprintf("postgres://postgres.slpoocycjgqsuoedhkbn:%s@aws-0-eu-central-1.pooler.supabase.com:5432/postgres", db_password)) + if err != nil { + log.Fatal("cannot initalize db client", err) + } + + handler := handler.NewHandler(db, kafka_writer) mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello folks!") }) - mux.Handle("/payload", handler) + mux.Handle("/file/", handler) http.ListenAndServe(":8080", mux) } diff --git a/src/rest-api/handler/BUILD.bazel b/src/rest-api/handler/BUILD.bazel index 89adc69..e588929 100644 --- a/src/rest-api/handler/BUILD.bazel +++ b/src/rest-api/handler/BUILD.bazel @@ -1,9 +1,30 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "handler", - srcs = ["handler.go"], + srcs = [ + "db.go", + "handler.go", + "mock_db.go", + ], importpath = "github.com/Baitinq/fs-tracer-backend/src/rest-api/handler", visibility = ["//visibility:public"], - deps = ["@com_github_segmentio_kafka_go//:kafka-go"], + deps = [ + "//lib", + "@com_github_jmoiron_sqlx//:sqlx", + "@com_github_lib_pq//:pq", + "@com_github_segmentio_kafka_go//:kafka-go", + "@org_uber_go_mock//gomock", + ], +) + +go_test( + name = "handler_test", + srcs = ["handler_test.go"], + embed = [":handler"], + deps = [ + "//lib", + "@com_github_stretchr_testify//require", + "@org_uber_go_mock//gomock", + ], ) diff --git a/src/rest-api/handler/db.go b/src/rest-api/handler/db.go new file mode 100644 index 0000000..0093c41 --- /dev/null +++ b/src/rest-api/handler/db.go @@ -0,0 +1,37 @@ +package handler + +import ( + "context" + + "github.com/Baitinq/fs-tracer-backend/lib" + "github.com/jmoiron/sqlx" +) + +//go:generate mockgen -source=$GOFILE -package=$GOPACKAGE -destination=mock_$GOFILE +type DB interface { + GetLatestFileByPath(ctx context.Context, path string) (*lib.File, error) +} + +type DBImpl struct { + db *sqlx.DB +} + +var _ DB = (*DBImpl)(nil) + +func NewDB(db *sqlx.DB) DB { + return &DBImpl{db: db} +} + +func (db DBImpl) GetLatestFileByPath(ctx context.Context, path string) (*lib.File, error) { + var file lib.File + err := db.db.GetContext(ctx, &file, ` + SELECT * FROM private.file + WHERE absolute_path = $1 + ORDER BY timestamp DESC + LIMIT 1 + `, path) + if err != nil { + return nil, err + } + return &file, nil +} diff --git a/src/rest-api/handler/handler.go b/src/rest-api/handler/handler.go index 9e44612..558e773 100644 --- a/src/rest-api/handler/handler.go +++ b/src/rest-api/handler/handler.go @@ -6,22 +6,58 @@ import ( "io" "log" "net/http" + "strings" "time" + "github.com/jmoiron/sqlx" + _ "github.com/lib/pq" "github.com/segmentio/kafka-go" ) type Handler struct { + db DB kafka_writer *kafka.Writer } -func NewHandler(kafka_writer *kafka.Writer) Handler { +func NewHandler(db *sqlx.DB, kafka_writer *kafka.Writer) Handler { return Handler{ + db: NewDB(db), kafka_writer: kafka_writer, } } func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + h.handleGet(w, r) + case http.MethodPost: + h.handlePost(w, r) + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} + +func (h Handler) handleGet(w http.ResponseWriter, r *http.Request) { + _, filePath, ok := strings.Cut(r.URL.Path, "/file/") + if !ok { + http.Error(w, "Invalid file path", http.StatusBadRequest) + return + } + + log.Println("File path: ", filePath) + + ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second) + defer cancel() + file, err := h.db.GetLatestFileByPath(ctx, filePath) + if err != nil { + http.Error(w, fmt.Sprintf("Internal server error: %s", err), http.StatusInternalServerError) + return + } + + fmt.Fprintln(w, "File: ", file) +} + +func (h Handler) handlePost(w http.ResponseWriter, r *http.Request) { bytes, err := io.ReadAll(io.Reader(r.Body)) if err != nil { log.Fatal(err) diff --git a/src/rest-api/handler/handler_test.go b/src/rest-api/handler/handler_test.go new file mode 100644 index 0000000..94b58e4 --- /dev/null +++ b/src/rest-api/handler/handler_test.go @@ -0,0 +1,31 @@ +package handler + +import ( + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/Baitinq/fs-tracer-backend/lib" + "github.com/stretchr/testify/require" + gomock "go.uber.org/mock/gomock" +) + +func TestHandleGet(t *testing.T) { + ctrl := gomock.NewController(t) + db := NewMockDB(ctrl) + recorder := httptest.NewRecorder() + + handler := Handler{db: db} + + file := &lib.File{ + Absolute_path: "/tmp/file.txt", + } + db.EXPECT().GetLatestFileByPath(gomock.Any(), "/tmp/file.txt").Return(file, nil) + + handler.handleGet(recorder, httptest.NewRequest(http.MethodGet, "/file/%2ftmp%2Ffile.txt", nil)) + + require.Equal(t, http.StatusOK, recorder.Code) + require.Equal(t, fmt.Sprintln("File: ", file), recorder.Body.String()) +} diff --git a/src/rest-api/handler/mock_db.go b/src/rest-api/handler/mock_db.go new file mode 100644 index 0000000..2d51a8f --- /dev/null +++ b/src/rest-api/handler/mock_db.go @@ -0,0 +1,56 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: src/rest-api/handler/db.go +// +// Generated by this command: +// +// mockgen -source src/rest-api/handler/db.go -package handler -destination=src/rest-api/handler/mock_db.go +// + +// Package handler is a generated GoMock package. +package handler + +import ( + context "context" + reflect "reflect" + + lib "github.com/Baitinq/fs-tracer-backend/lib" + gomock "go.uber.org/mock/gomock" +) + +// MockDB is a mock of DB interface. +type MockDB struct { + ctrl *gomock.Controller + recorder *MockDBMockRecorder +} + +// MockDBMockRecorder is the mock recorder for MockDB. +type MockDBMockRecorder struct { + mock *MockDB +} + +// NewMockDB creates a new mock instance. +func NewMockDB(ctrl *gomock.Controller) *MockDB { + mock := &MockDB{ctrl: ctrl} + mock.recorder = &MockDBMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDB) EXPECT() *MockDBMockRecorder { + return m.recorder +} + +// GetLatestFileByPath mocks base method. +func (m *MockDB) GetLatestFileByPath(ctx context.Context, path string) (*lib.File, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLatestFileByPath", ctx, path) + ret0, _ := ret[0].(*lib.File) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetLatestFileByPath indicates an expected call of GetLatestFileByPath. +func (mr *MockDBMockRecorder) GetLatestFileByPath(ctx, path any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestFileByPath", reflect.TypeOf((*MockDB)(nil).GetLatestFileByPath), ctx, path) +} |