aboutsummaryrefslogtreecommitdiff
path: root/backend/internal/server2/idb
diff options
context:
space:
mode:
Diffstat (limited to 'backend/internal/server2/idb')
-rw-r--r--backend/internal/server2/idb/stock/v1/stock.go63
-rw-r--r--backend/internal/server2/idb/user/v1/user.go94
2 files changed, 157 insertions, 0 deletions
diff --git a/backend/internal/server2/idb/stock/v1/stock.go b/backend/internal/server2/idb/stock/v1/stock.go
new file mode 100644
index 0000000..3a94c82
--- /dev/null
+++ b/backend/internal/server2/idb/stock/v1/stock.go
@@ -0,0 +1,63 @@
+package stock
+
+import (
+ "context"
+ "fmt"
+ "log/slog"
+
+ pb "ibd-trader/api/gen/idb/stock/v1"
+ "ibd-trader/internal/database"
+ "ibd-trader/internal/leader/manager/ibd/scrape"
+ "ibd-trader/internal/redis/taskqueue"
+
+ "cloud.google.com/go/longrunning/autogen/longrunningpb"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+ "google.golang.org/protobuf/types/known/anypb"
+ "google.golang.org/protobuf/types/known/timestamppb"
+)
+
+const ScrapeOperationPrefix = "scrape"
+
+type Server struct {
+ pb.UnimplementedStockServiceServer
+
+ db database.StockStore
+ queue taskqueue.TaskQueue[scrape.TaskInfo]
+}
+
+func New(db database.StockStore, queue taskqueue.TaskQueue[scrape.TaskInfo]) *Server {
+ return &Server{db: db, queue: queue}
+}
+
+func (s *Server) CreateStock(ctx context.Context, request *pb.CreateStockRequest) (*longrunningpb.Operation, error) {
+ task, err := s.queue.Enqueue(ctx, scrape.TaskInfo{Symbol: request.Symbol})
+ if err != nil {
+ slog.ErrorContext(ctx, "failed to enqueue task", "err", err)
+ return nil, status.New(codes.Internal, "failed to enqueue task").Err()
+ }
+ op := &longrunningpb.Operation{
+ Name: fmt.Sprintf("%s/%s", ScrapeOperationPrefix, task.ID.String()),
+ Metadata: new(anypb.Any),
+ Done: false,
+ Result: nil,
+ }
+ err = op.Metadata.MarshalFrom(&pb.StockScrapeOperationMetadata{
+ Symbol: request.Symbol,
+ StartTime: timestamppb.New(task.ID.Timestamp()),
+ })
+ if err != nil {
+ slog.ErrorContext(ctx, "failed to marshal metadata", "err", err)
+ return nil, status.New(codes.Internal, "failed to marshal metadata").Err()
+ }
+ return op, nil
+}
+
+func (s *Server) GetStock(ctx context.Context, request *pb.GetStockRequest) (*pb.GetStockResponse, error) {
+
+}
+
+func (s *Server) ListStocks(ctx context.Context, request *pb.ListStocksRequest) (*pb.ListStocksResponse, error) {
+ //TODO implement me
+ panic("implement me")
+}
diff --git a/backend/internal/server2/idb/user/v1/user.go b/backend/internal/server2/idb/user/v1/user.go
new file mode 100644
index 0000000..1866944
--- /dev/null
+++ b/backend/internal/server2/idb/user/v1/user.go
@@ -0,0 +1,94 @@
+package user
+
+import (
+ "context"
+ "errors"
+
+ pb "ibd-trader/api/gen/idb/user/v1"
+ "ibd-trader/internal/database"
+
+ "github.com/mennanov/fmutils"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+ "google.golang.org/protobuf/proto"
+)
+
+type Server struct {
+ pb.UnimplementedUserServiceServer
+
+ db database.UserStore
+}
+
+func New(db database.UserStore) *Server {
+ return &Server{db: db}
+}
+
+func (u *Server) CreateUser(ctx context.Context, request *pb.CreateUserRequest) (*pb.CreateUserResponse, error) {
+ err := u.db.AddUser(ctx, request.Subject)
+ if err != nil {
+ return nil, status.Errorf(codes.Internal, "unable to create user: %v", err)
+ }
+
+ user, err := u.db.GetUser(ctx, request.Subject)
+ if err != nil {
+ return nil, status.Errorf(codes.Internal, "unable to get user: %v", err)
+ }
+
+ return &pb.CreateUserResponse{
+ User: &pb.User{
+ Subject: user.Subject,
+ IbdUsername: user.IBDUsername,
+ IbdPassword: nil,
+ },
+ }, nil
+}
+
+func (u *Server) GetUser(ctx context.Context, request *pb.GetUserRequest) (*pb.GetUserResponse, error) {
+ user, err := u.db.GetUser(ctx, request.Subject)
+ if errors.Is(err, database.ErrUserNotFound) {
+ return nil, status.New(codes.NotFound, "user not found").Err()
+ }
+ if err != nil {
+ return nil, status.Errorf(codes.Internal, "unable to get user: %v", err)
+ }
+
+ return &pb.GetUserResponse{
+ User: &pb.User{
+ Subject: user.Subject,
+ IbdUsername: user.IBDUsername,
+ IbdPassword: nil,
+ },
+ }, nil
+}
+
+func (u *Server) UpdateUser(ctx context.Context, request *pb.UpdateUserRequest) (*pb.UpdateUserResponse, error) {
+ request.UpdateMask.Normalize()
+ if !request.UpdateMask.IsValid(request.User) {
+ return nil, status.Errorf(codes.InvalidArgument, "invalid update mask")
+ }
+
+ existingUserRes, err := u.GetUser(ctx, &pb.GetUserRequest{Subject: request.User.Subject})
+ if err != nil {
+ return nil, err
+ }
+ existingUser := existingUserRes.User
+
+ newUser := proto.Clone(existingUser).(*pb.User)
+ fmutils.Overwrite(request.User, newUser, request.UpdateMask.Paths)
+
+ // if IDB creds are both set and are different, update them
+ if (newUser.IbdPassword != nil && newUser.IbdUsername != nil) &&
+ (newUser.IbdPassword != existingUser.IbdPassword ||
+ newUser.IbdUsername != existingUser.IbdUsername) {
+ // Update IBD creds
+ err = u.db.AddIBDCreds(ctx, newUser.Subject, *newUser.IbdUsername, *newUser.IbdPassword)
+ if err != nil {
+ return nil, status.Errorf(codes.Internal, "unable to update user: %v", err)
+ }
+ }
+
+ newUser.IbdPassword = nil
+ return &pb.UpdateUserResponse{
+ User: newUser,
+ }, nil
+}