Initial commit
This commit is contained in:
53
backend/gen/notes/v1/notes.pb.go
Normal file
53
backend/gen/notes/v1/notes.pb.go
Normal file
@@ -0,0 +1,53 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: notes/v1/notes.proto
|
||||
//
|
||||
// ЭТО СГЕНЕРИРОВАННЫЙ ФАЙЛ — не редактируй вручную.
|
||||
// Перегенерируй командой: cd proto && buf generate
|
||||
//
|
||||
// Для демо: создан вручную, чтобы проект компилировался
|
||||
// без установки protoc. В реальном проекте этот файл
|
||||
// генерируется автоматически из proto/notes/v1/notes.proto
|
||||
|
||||
package notesv1
|
||||
|
||||
import (
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// Note — заметка. Сгенерирована из message Note в proto.
|
||||
type Note struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
|
||||
Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"`
|
||||
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
|
||||
UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
|
||||
}
|
||||
|
||||
type CreateNoteRequest struct {
|
||||
Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"`
|
||||
Content string `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"`
|
||||
}
|
||||
|
||||
type GetNoteRequest struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateNoteRequest struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
|
||||
Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"`
|
||||
}
|
||||
|
||||
type DeleteNoteRequest struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
}
|
||||
|
||||
type ListNotesRequest struct {
|
||||
Page int32 `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"`
|
||||
PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
|
||||
}
|
||||
|
||||
type ListNotesResponse struct {
|
||||
Notes []*Note `protobuf:"bytes,1,rep,name=notes,proto3" json:"notes,omitempty"`
|
||||
TotalCount int32 `protobuf:"varint,2,opt,name=total_count,json=totalCount,proto3" json:"total_count,omitempty"`
|
||||
}
|
||||
158
backend/gen/notes/v1/notes_grpc.pb.go
Normal file
158
backend/gen/notes/v1/notes_grpc.pb.go
Normal file
@@ -0,0 +1,158 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// source: notes/v1/notes.proto
|
||||
//
|
||||
// ЭТО СГЕНЕРИРОВАННЫЙ ФАЙЛ — не редактируй вручную.
|
||||
// Здесь определены gRPC интерфейсы сервиса.
|
||||
// Go требует имплементировать все методы интерфейса —
|
||||
// если добавишь новый rpc в proto, код не скомпилируется,
|
||||
// пока не реализуешь метод в service/note_service.go
|
||||
|
||||
package notesv1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
// NoteServiceServer — интерфейс, который нужно реализовать.
|
||||
// Сгенерирован из service NoteService в proto.
|
||||
type NoteServiceServer interface {
|
||||
CreateNote(context.Context, *CreateNoteRequest) (*Note, error)
|
||||
GetNote(context.Context, *GetNoteRequest) (*Note, error)
|
||||
UpdateNote(context.Context, *UpdateNoteRequest) (*Note, error)
|
||||
DeleteNote(context.Context, *DeleteNoteRequest) (*emptypb.Empty, error)
|
||||
ListNotes(context.Context, *ListNotesRequest) (*ListNotesResponse, error)
|
||||
mustEmbedUnimplementedNoteServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedNoteServiceServer — базовая реализация.
|
||||
// Встраивается в твой сервис для forward-compatibility:
|
||||
// если в proto добавится новый rpc, а ты его ещё не реализовал,
|
||||
// он вернёт "not implemented" вместо падения.
|
||||
type UnimplementedNoteServiceServer struct{}
|
||||
|
||||
func (UnimplementedNoteServiceServer) CreateNote(context.Context, *CreateNoteRequest) (*Note, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateNote not implemented")
|
||||
}
|
||||
func (UnimplementedNoteServiceServer) GetNote(context.Context, *GetNoteRequest) (*Note, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetNote not implemented")
|
||||
}
|
||||
func (UnimplementedNoteServiceServer) UpdateNote(context.Context, *UpdateNoteRequest) (*Note, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateNote not implemented")
|
||||
}
|
||||
func (UnimplementedNoteServiceServer) DeleteNote(context.Context, *DeleteNoteRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DeleteNote not implemented")
|
||||
}
|
||||
func (UnimplementedNoteServiceServer) ListNotes(context.Context, *ListNotesRequest) (*ListNotesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListNotes not implemented")
|
||||
}
|
||||
func (UnimplementedNoteServiceServer) mustEmbedUnimplementedNoteServiceServer() {}
|
||||
|
||||
// RegisterNoteServiceServer регистрирует сервис на gRPC сервере.
|
||||
func RegisterNoteServiceServer(s grpc.ServiceRegistrar, srv NoteServiceServer) {
|
||||
s.RegisterService(&NoteService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
// NoteService_ServiceDesc — описание сервиса для gRPC.
|
||||
var NoteService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "notes.v1.NoteService",
|
||||
HandlerType: (*NoteServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "CreateNote",
|
||||
Handler: _NoteService_CreateNote_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetNote",
|
||||
Handler: _NoteService_GetNote_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateNote",
|
||||
Handler: _NoteService_UpdateNote_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteNote",
|
||||
Handler: _NoteService_DeleteNote_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListNotes",
|
||||
Handler: _NoteService_ListNotes_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "notes/v1/notes.proto",
|
||||
}
|
||||
|
||||
func _NoteService_CreateNote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreateNoteRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NoteServiceServer).CreateNote(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/notes.v1.NoteService/CreateNote"}
|
||||
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NoteServiceServer).CreateNote(ctx, req.(*CreateNoteRequest))
|
||||
})
|
||||
}
|
||||
|
||||
func _NoteService_GetNote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetNoteRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NoteServiceServer).GetNote(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/notes.v1.NoteService/GetNote"}
|
||||
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NoteServiceServer).GetNote(ctx, req.(*GetNoteRequest))
|
||||
})
|
||||
}
|
||||
|
||||
func _NoteService_UpdateNote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateNoteRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NoteServiceServer).UpdateNote(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/notes.v1.NoteService/UpdateNote"}
|
||||
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NoteServiceServer).UpdateNote(ctx, req.(*UpdateNoteRequest))
|
||||
})
|
||||
}
|
||||
|
||||
func _NoteService_DeleteNote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeleteNoteRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NoteServiceServer).DeleteNote(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/notes.v1.NoteService/DeleteNote"}
|
||||
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NoteServiceServer).DeleteNote(ctx, req.(*DeleteNoteRequest))
|
||||
})
|
||||
}
|
||||
|
||||
func _NoteService_ListNotes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListNotesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NoteServiceServer).ListNotes(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/notes.v1.NoteService/ListNotes"}
|
||||
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NoteServiceServer).ListNotes(ctx, req.(*ListNotesRequest))
|
||||
})
|
||||
}
|
||||
16
backend/go.mod
Normal file
16
backend/go.mod
Normal file
@@ -0,0 +1,16 @@
|
||||
module backend
|
||||
|
||||
go 1.25.1
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.6.0
|
||||
google.golang.org/grpc v1.79.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
)
|
||||
38
backend/go.sum
Normal file
38
backend/go.sum
Normal file
@@ -0,0 +1,38 @@
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
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=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
|
||||
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
|
||||
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
|
||||
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
|
||||
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
||||
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
33
backend/main.go
Normal file
33
backend/main.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"backend/service"
|
||||
|
||||
notesv1 "backend/gen/notes/v1"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection"
|
||||
)
|
||||
|
||||
func main() {
|
||||
lis, err := net.Listen("tcp", ":50051")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to listen: %v", err)
|
||||
}
|
||||
|
||||
grpcServer := grpc.NewServer()
|
||||
|
||||
// Регистрируем наш сервис заметок
|
||||
notesv1.RegisterNoteServiceServer(grpcServer, service.NewNoteService())
|
||||
|
||||
// Reflection — для отладки через grpcurl / Postman
|
||||
reflection.Register(grpcServer)
|
||||
|
||||
log.Println("gRPC server listening on :50051")
|
||||
if err := grpcServer.Serve(lis); err != nil {
|
||||
log.Fatalf("failed to serve: %v", err)
|
||||
}
|
||||
}
|
||||
147
backend/service/note_service.go
Normal file
147
backend/service/note_service.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
notesv1 "backend/gen/notes/v1"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// NoteService — реализация gRPC сервиса, сгенерированного из proto.
|
||||
// Типы notesv1.Note, notesv1.CreateNoteRequest и т.д. —
|
||||
// это всё автоматически сгенерированные из proto файла структуры.
|
||||
// Если ты изменишь proto (добавишь поле, переименуешь) и
|
||||
// перегенерируешь — этот код перестанет компилироваться,
|
||||
// пока ты не обновишь реализацию. Вот в чём магия.
|
||||
type NoteService struct {
|
||||
notesv1.UnimplementedNoteServiceServer
|
||||
mu sync.RWMutex
|
||||
notes map[string]*notesv1.Note
|
||||
}
|
||||
|
||||
func NewNoteService() *NoteService {
|
||||
return &NoteService{
|
||||
notes: make(map[string]*notesv1.Note),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *NoteService) CreateNote(
|
||||
ctx context.Context,
|
||||
req *notesv1.CreateNoteRequest,
|
||||
) (*notesv1.Note, error) {
|
||||
if req.Title == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "title is required")
|
||||
}
|
||||
|
||||
now := timestamppb.New(time.Now())
|
||||
note := ¬esv1.Note{
|
||||
Id: uuid.New().String(),
|
||||
Title: req.Title,
|
||||
Content: req.Content,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
s.notes[note.Id] = note
|
||||
s.mu.Unlock()
|
||||
|
||||
return note, nil
|
||||
}
|
||||
|
||||
func (s *NoteService) GetNote(
|
||||
ctx context.Context,
|
||||
req *notesv1.GetNoteRequest,
|
||||
) (*notesv1.Note, error) {
|
||||
s.mu.RLock()
|
||||
note, ok := s.notes[req.Id]
|
||||
s.mu.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.NotFound, "note %s not found", req.Id)
|
||||
}
|
||||
return note, nil
|
||||
}
|
||||
|
||||
func (s *NoteService) UpdateNote(
|
||||
ctx context.Context,
|
||||
req *notesv1.UpdateNoteRequest,
|
||||
) (*notesv1.Note, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
note, ok := s.notes[req.Id]
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.NotFound, "note %s not found", req.Id)
|
||||
}
|
||||
|
||||
note.Title = req.Title
|
||||
note.Content = req.Content
|
||||
note.UpdatedAt = timestamppb.New(time.Now())
|
||||
|
||||
return note, nil
|
||||
}
|
||||
|
||||
func (s *NoteService) DeleteNote(
|
||||
ctx context.Context,
|
||||
req *notesv1.DeleteNoteRequest,
|
||||
) (*emptypb.Empty, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if _, ok := s.notes[req.Id]; !ok {
|
||||
return nil, status.Errorf(codes.NotFound, "note %s not found", req.Id)
|
||||
}
|
||||
|
||||
delete(s.notes, req.Id)
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *NoteService) ListNotes(
|
||||
ctx context.Context,
|
||||
req *notesv1.ListNotesRequest,
|
||||
) (*notesv1.ListNotesResponse, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
all := make([]*notesv1.Note, 0, len(s.notes))
|
||||
for _, n := range s.notes {
|
||||
all = append(all, n)
|
||||
}
|
||||
|
||||
// Простая пагинация
|
||||
total := int32(len(all))
|
||||
pageSize := req.PageSize
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
page := req.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
start := (page - 1) * pageSize
|
||||
if start >= total {
|
||||
return ¬esv1.ListNotesResponse{
|
||||
Notes: []*notesv1.Note{},
|
||||
TotalCount: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
end := start + pageSize
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
|
||||
return ¬esv1.ListNotesResponse{
|
||||
Notes: all[start:end],
|
||||
TotalCount: total,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user