From ab2ce57d552f13feb58deeed3df883628d8890aa Mon Sep 17 00:00:00 2001 From: Pascal Fischer Date: Thu, 12 Jun 2025 20:32:18 +0200 Subject: [PATCH] add template module --- internals/modules/template/interface.go | 22 +++ internals/modules/template/manager/api.go | 123 +++++++++++++++++ .../modules/template/manager/api_test.go | 1 + internals/modules/template/manager/manager.go | 41 ++++++ .../modules/template/manager/manager_mock.go | 111 ++++++++++++++++ .../modules/template/manager/manager_test.go | 1 + .../modules/template/manager/repository.go | 61 +++++++++ .../template/manager/repository_mock.go | 125 ++++++++++++++++++ .../template/manager/repository_test.go | 1 + internals/modules/template/type.go | 31 +++++ 10 files changed, 517 insertions(+) create mode 100644 internals/modules/template/interface.go create mode 100644 internals/modules/template/manager/api.go create mode 100644 internals/modules/template/manager/api_test.go create mode 100644 internals/modules/template/manager/manager.go create mode 100644 internals/modules/template/manager/manager_mock.go create mode 100644 internals/modules/template/manager/manager_test.go create mode 100644 internals/modules/template/manager/repository.go create mode 100644 internals/modules/template/manager/repository_mock.go create mode 100644 internals/modules/template/manager/repository_test.go create mode 100644 internals/modules/template/type.go diff --git a/internals/modules/template/interface.go b/internals/modules/template/interface.go new file mode 100644 index 0000000..a1e57de --- /dev/null +++ b/internals/modules/template/interface.go @@ -0,0 +1,22 @@ +package template + +import ( + "context" + + "github.com/netbirdio/management-refactor/internals/shared/db" +) + +type Manager interface { + // Create + CreateTemplate(ctx context.Context, tx db.Transaction, template *Template) (*Template, error) + + // Read + GetAllTemplates(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountID string) ([]*Template, error) + GetTemplateByID(ctx context.Context, tx db.Transaction, strength db.LockingStrength, id string) (*Template, error) + + // Update + UpdateTemplate(ctx context.Context, tx db.Transaction, template *Template) (*Template, error) + + // Delete + DeleteTemplate(ctx context.Context, tx db.Transaction, id string) error +} diff --git a/internals/modules/template/manager/api.go b/internals/modules/template/manager/api.go new file mode 100644 index 0000000..9791ab7 --- /dev/null +++ b/internals/modules/template/manager/api.go @@ -0,0 +1,123 @@ +package manager + +import ( + "encoding/json" + "net/http" + + "github.com/gorilla/mux" + "github.com/netbirdio/management-refactor/integrations/shared/api" + nbcontext "github.com/netbirdio/netbird/management/server/context" + + "github.com/netbirdio/management-refactor/internals/modules/template" + "github.com/netbirdio/management-refactor/internals/modules/users" + "github.com/netbirdio/management-refactor/internals/shared/db" + "github.com/netbirdio/management-refactor/internals/shared/permissions" + "github.com/netbirdio/management-refactor/internals/shared/permissions/modules" + "github.com/netbirdio/management-refactor/internals/shared/permissions/operations" +) + +type handler struct { + manager template.Manager +} + +func RegisterEndpoints(router *mux.Router, permissionsManager permissions.Manager, manager template.Manager) { + h := &handler{ + manager: manager, + } + + router.HandleFunc("/templates", permissionsManager.WithPermission(modules.Template, operations.Write, h.createTemplate)).Methods("POST", "OPTIONS") + router.HandleFunc("/templates", permissionsManager.WithPermission(modules.Template, operations.Read, h.getAllTemplates)).Methods("GET", "OPTIONS") + router.HandleFunc("/templates/{templateId}", permissionsManager.WithPermission(modules.Template, operations.Read, h.getTemplate)).Methods("GET", "OPTIONS") + router.HandleFunc("/templates/{templateId}", permissionsManager.WithPermission(modules.Template, operations.Write, h.updateTemplate)).Methods("PUT", "OPTIONS") + router.HandleFunc("/templates/{templateId}", permissionsManager.WithPermission(modules.Template, operations.Write, h.deleteTemplate)).Methods("DELETE", "OPTIONS") +} + +func (h *handler) getAllTemplates(w http.ResponseWriter, r *http.Request, userAuth *nbcontext.UserAuth) { + users, err := h.manager.GetAllTemplates(r.Context(), nil, db.LockingStrengthShare, userAuth.AccountId) + if err != nil { + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + _ = json.NewEncoder(w).Encode(users) +} + +func (h *handler) getTemplate(w http.ResponseWriter, r *http.Request, userAuth *nbcontext.UserAuth) { + vars := mux.Vars(r) + templateId := vars["templateId"] + + user, err := h.manager.GetTemplateByID(r.Context(), nil, db.LockingStrengthShare, templateId) + if err != nil { + http.Error(w, "Not Found", http.StatusNotFound) + return + } + _ = json.NewEncoder(w).Encode(user) +} + +func (h *handler) createTemplate(w http.ResponseWriter, r *http.Request, userAuth *nbcontext.UserAuth) { + var templateReq api.Template + if err := json.NewDecoder(r.Body).Decode(&templateReq); err != nil { + http.Error(w, "Bad Request", http.StatusBadRequest) + return + } + + templateObj := &template.Template{} + templateObj.FromAPIRequest(templateReq) + + err := templateObj.Validate() + if err != nil { + http.Error(w, "Bad Request: "+err.Error(), http.StatusBadRequest) + return + } + + templateResp, err := h.manager.CreateTemplate(r.Context(), nil, &templateReq) + if err != nil { + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusCreated) + _ = json.NewEncoder(w).Encode(templateResp) +} + +func (h *handler) updateTemplate(w http.ResponseWriter, r *http.Request, userAuth *nbcontext.UserAuth) { + vars := mux.Vars(r) + templateId := vars["templateId"] + + var templateReq api.Template + if err := json.NewDecoder(r.Body).Decode(&templateReq); err != nil { + http.Error(w, "Bad Request", http.StatusBadRequest) + return + } + + templateObj := &template.Template{} + templateObj.FromAPIRequest(templateReq) + templateObj.Id = templateId + + err := templateObj.Validate() + if err != nil { + http.Error(w, "Bad Request: "+err.Error(), http.StatusBadRequest) + return + } + + templateResp, err := h.manager.UpdateTemplate(r.Context(), nil, &templateReq) + if err != nil { + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(templateResp) +} + +func (h *handler) deleteTemplate(w http.ResponseWriter, r *http.Request, userAuth *nbcontext.UserAuth) { + vars := mux.Vars(r) + templateId := vars["templateId"] + + err := h.manager.DeleteTemplate(r.Context(), nil, templateId) + if err != nil { + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusNoContent) +} diff --git a/internals/modules/template/manager/api_test.go b/internals/modules/template/manager/api_test.go new file mode 100644 index 0000000..5d04392 --- /dev/null +++ b/internals/modules/template/manager/api_test.go @@ -0,0 +1 @@ +package manager diff --git a/internals/modules/template/manager/manager.go b/internals/modules/template/manager/manager.go new file mode 100644 index 0000000..9acfea1 --- /dev/null +++ b/internals/modules/template/manager/manager.go @@ -0,0 +1,41 @@ +package manager + +//go:generate go run github.com/golang/mock/mockgen -package manager -destination=manager_mock.go -source=../interface.go -build_flags=-mod=mod + +import ( + "context" + + "github.com/netbirdio/management-refactor/internals/modules/template" + "github.com/netbirdio/management-refactor/internals/shared/db" +) + +var _ template.Manager = (*Manager)(nil) + +type Manager struct { + repo Repository +} + +func NewManager(repo Repository) *Manager { + return &Manager{repo: repo} +} + +func (m *Manager) CreateTemplate(ctx context.Context, tx db.Transaction, template *template.Template) (*template.Template, error) { + return m.repo.CreateTemplate(ctx, tx, template) +} + +func (m *Manager) GetAllTemplates(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountID string) ([]*template.Template, error) { + return m.repo.GetAllTemplates(ctx, tx, strength, accountID) +} + +func (m *Manager) GetTemplateByID(ctx context.Context, tx db.Transaction, strength db.LockingStrength, id string) (*template.Template, error) { + return m.repo.GetTemplateByID(ctx, tx, strength, id) +} + +func (m *Manager) UpdateTemplate(ctx context.Context, tx db.Transaction, template *template.Template) (*template.Template, error) { + return m.repo.UpdateTemplate(ctx, tx, template) +} + +func (m *Manager) DeleteTemplate(ctx context.Context, tx db.Transaction, id string) error { + template := &template.Template{Id: id} + return m.repo.DeleteTemplate(ctx, tx, template) +} diff --git a/internals/modules/template/manager/manager_mock.go b/internals/modules/template/manager/manager_mock.go new file mode 100644 index 0000000..ca11d63 --- /dev/null +++ b/internals/modules/template/manager/manager_mock.go @@ -0,0 +1,111 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../interface.go + +// Package manager is a generated GoMock package. +package manager + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + template "github.com/netbirdio/management-refactor/internals/modules/template" + db "github.com/netbirdio/management-refactor/internals/shared/db" +) + +// MockManager is a mock of Manager interface. +type MockManager struct { + ctrl *gomock.Controller + recorder *MockManagerMockRecorder +} + +// MockManagerMockRecorder is the mock recorder for MockManager. +type MockManagerMockRecorder struct { + mock *MockManager +} + +// NewMockManager creates a new mock instance. +func NewMockManager(ctrl *gomock.Controller) *MockManager { + mock := &MockManager{ctrl: ctrl} + mock.recorder = &MockManagerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockManager) EXPECT() *MockManagerMockRecorder { + return m.recorder +} + +// CreateTemplate mocks base method. +func (m *MockManager) CreateTemplate(ctx context.Context, tx db.Transaction, template *template.Template) (*template.Template, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateTemplate", ctx, tx, template) + ret0, _ := ret[0].(*template.Template) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateTemplate indicates an expected call of CreateTemplate. +func (mr *MockManagerMockRecorder) CreateTemplate(ctx, tx, template interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTemplate", reflect.TypeOf((*MockManager)(nil).CreateTemplate), ctx, tx, template) +} + +// DeleteTemplate mocks base method. +func (m *MockManager) DeleteTemplate(ctx context.Context, tx db.Transaction, id string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteTemplate", ctx, tx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteTemplate indicates an expected call of DeleteTemplate. +func (mr *MockManagerMockRecorder) DeleteTemplate(ctx, tx, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTemplate", reflect.TypeOf((*MockManager)(nil).DeleteTemplate), ctx, tx, id) +} + +// GetAllTemplates mocks base method. +func (m *MockManager) GetAllTemplates(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountID string) ([]*template.Template, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllTemplates", ctx, tx, strength, accountID) + ret0, _ := ret[0].([]*template.Template) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAllTemplates indicates an expected call of GetAllTemplates. +func (mr *MockManagerMockRecorder) GetAllTemplates(ctx, tx, strength, accountID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllTemplates", reflect.TypeOf((*MockManager)(nil).GetAllTemplates), ctx, tx, strength, accountID) +} + +// GetTemplateByID mocks base method. +func (m *MockManager) GetTemplateByID(ctx context.Context, tx db.Transaction, strength db.LockingStrength, id string) (*template.Template, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTemplateByID", ctx, tx, strength, id) + ret0, _ := ret[0].(*template.Template) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTemplateByID indicates an expected call of GetTemplateByID. +func (mr *MockManagerMockRecorder) GetTemplateByID(ctx, tx, strength, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTemplateByID", reflect.TypeOf((*MockManager)(nil).GetTemplateByID), ctx, tx, strength, id) +} + +// UpdateTemplate mocks base method. +func (m *MockManager) UpdateTemplate(ctx context.Context, tx db.Transaction, template *template.Template) (*template.Template, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateTemplate", ctx, tx, template) + ret0, _ := ret[0].(*template.Template) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateTemplate indicates an expected call of UpdateTemplate. +func (mr *MockManagerMockRecorder) UpdateTemplate(ctx, tx, template interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTemplate", reflect.TypeOf((*MockManager)(nil).UpdateTemplate), ctx, tx, template) +} diff --git a/internals/modules/template/manager/manager_test.go b/internals/modules/template/manager/manager_test.go new file mode 100644 index 0000000..5d04392 --- /dev/null +++ b/internals/modules/template/manager/manager_test.go @@ -0,0 +1 @@ +package manager diff --git a/internals/modules/template/manager/repository.go b/internals/modules/template/manager/repository.go new file mode 100644 index 0000000..9c43c68 --- /dev/null +++ b/internals/modules/template/manager/repository.go @@ -0,0 +1,61 @@ +package manager + +//go:generate go run github.com/golang/mock/mockgen -package manager -destination=repository_mock.go -source=./repository.go -build_flags=-mod=mod + +import ( + "context" + + log "github.com/sirupsen/logrus" + + "github.com/netbirdio/management-refactor/internals/modules/template" + "github.com/netbirdio/management-refactor/internals/shared/db" +) + +var _ Repository = (*repository)(nil) + +type Repository interface { + RunInTx(fn func(tx db.Transaction) error) error + CreateTemplate(ctx context.Context, tx db.Transaction, template *template.Template) (*template.Template, error) + GetAllTemplates(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountId string) ([]*template.Template, error) + GetTemplateByID(ctx context.Context, tx db.Transaction, strength db.LockingStrength, id string) (*template.Template, error) + UpdateTemplate(ctx context.Context, tx db.Transaction, template *template.Template) (*template.Template, error) + DeleteTemplate(ctx context.Context, tx db.Transaction, template *template.Template) error +} + +type repository struct { + store *db.Store +} + +func NewRepository(s *db.Store) Repository { + err := s.AutoMigrate(template.Template{}) + if err != nil { + log.Fatalf("Failed to auto migrate: %v", err) + } + return &repository{store: s} +} + +func (r *repository) RunInTx(fn func(tx db.Transaction) error) error { + return r.store.RunInTx(fn) +} + +func (r *repository) CreateTemplate(ctx context.Context, tx db.Transaction, template *template.Template) (*template.Template, error) { + return template, r.store.Create(tx, template) +} + +func (r *repository) GetAllTemplates(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountId string) (templates []*template.Template, err error) { + err = r.store.GetMany(tx, strength, templates, "account_id = ?", accountId) + return +} + +func (r *repository) GetTemplateByID(ctx context.Context, tx db.Transaction, strength db.LockingStrength, id string) (template *template.Template, err error) { + err = r.store.GetOne(tx, strength, &template, "id = ?", id) + return +} + +func (r *repository) UpdateTemplate(ctx context.Context, tx db.Transaction, template *template.Template) (*template.Template, error) { + return template, r.store.Update(tx, template) +} + +func (r *repository) DeleteTemplate(ctx context.Context, tx db.Transaction, template *template.Template) error { + return r.store.Delete(tx, template) +} diff --git a/internals/modules/template/manager/repository_mock.go b/internals/modules/template/manager/repository_mock.go new file mode 100644 index 0000000..5126262 --- /dev/null +++ b/internals/modules/template/manager/repository_mock.go @@ -0,0 +1,125 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./repository.go + +// Package manager is a generated GoMock package. +package manager + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + template "github.com/netbirdio/management-refactor/internals/modules/template" + db "github.com/netbirdio/management-refactor/internals/shared/db" +) + +// 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 +} + +// CreateTemplate mocks base method. +func (m *MockRepository) CreateTemplate(ctx context.Context, tx db.Transaction, template *template.Template) (*template.Template, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateTemplate", ctx, tx, template) + ret0, _ := ret[0].(*template.Template) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateTemplate indicates an expected call of CreateTemplate. +func (mr *MockRepositoryMockRecorder) CreateTemplate(ctx, tx, template interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTemplate", reflect.TypeOf((*MockRepository)(nil).CreateTemplate), ctx, tx, template) +} + +// DeleteTemplate mocks base method. +func (m *MockRepository) DeleteTemplate(ctx context.Context, tx db.Transaction, template *template.Template) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteTemplate", ctx, tx, template) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteTemplate indicates an expected call of DeleteTemplate. +func (mr *MockRepositoryMockRecorder) DeleteTemplate(ctx, tx, template interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTemplate", reflect.TypeOf((*MockRepository)(nil).DeleteTemplate), ctx, tx, template) +} + +// GetAllTemplates mocks base method. +func (m *MockRepository) GetAllTemplates(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountId string) ([]*template.Template, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllTemplates", ctx, tx, strength, accountId) + ret0, _ := ret[0].([]*template.Template) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAllTemplates indicates an expected call of GetAllTemplates. +func (mr *MockRepositoryMockRecorder) GetAllTemplates(ctx, tx, strength, accountId interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllTemplates", reflect.TypeOf((*MockRepository)(nil).GetAllTemplates), ctx, tx, strength, accountId) +} + +// GetTemplateByID mocks base method. +func (m *MockRepository) GetTemplateByID(ctx context.Context, tx db.Transaction, strength db.LockingStrength, id string) (*template.Template, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTemplateByID", ctx, tx, strength, id) + ret0, _ := ret[0].(*template.Template) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTemplateByID indicates an expected call of GetTemplateByID. +func (mr *MockRepositoryMockRecorder) GetTemplateByID(ctx, tx, strength, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTemplateByID", reflect.TypeOf((*MockRepository)(nil).GetTemplateByID), ctx, tx, strength, id) +} + +// RunInTx mocks base method. +func (m *MockRepository) RunInTx(fn func(db.Transaction) error) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RunInTx", fn) + ret0, _ := ret[0].(error) + return ret0 +} + +// RunInTx indicates an expected call of RunInTx. +func (mr *MockRepositoryMockRecorder) RunInTx(fn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunInTx", reflect.TypeOf((*MockRepository)(nil).RunInTx), fn) +} + +// UpdateTemplate mocks base method. +func (m *MockRepository) UpdateTemplate(ctx context.Context, tx db.Transaction, template *template.Template) (*template.Template, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateTemplate", ctx, tx, template) + ret0, _ := ret[0].(*template.Template) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateTemplate indicates an expected call of UpdateTemplate. +func (mr *MockRepositoryMockRecorder) UpdateTemplate(ctx, tx, template interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTemplate", reflect.TypeOf((*MockRepository)(nil).UpdateTemplate), ctx, tx, template) +} diff --git a/internals/modules/template/manager/repository_test.go b/internals/modules/template/manager/repository_test.go new file mode 100644 index 0000000..5d04392 --- /dev/null +++ b/internals/modules/template/manager/repository_test.go @@ -0,0 +1 @@ +package manager diff --git a/internals/modules/template/type.go b/internals/modules/template/type.go new file mode 100644 index 0000000..927525f --- /dev/null +++ b/internals/modules/template/type.go @@ -0,0 +1,31 @@ +package template + +import ( + "github.com/netbirdio/netbird/management/server/http/api" +) + +type Template struct { + Id string `gorm:"primary_key"` +} + +// NewTemplate creates a new Template object +func NewTemplate() *Template { + return &Template{} +} + +// Copy the Template object +func (u *Template) Copy() *Template { + return &Template{} +} + +func (u *Template) ToApiResponse() *api.Template { + +} + +func (n *Template) FromAPIRequest(req *api.Template) { + +} + +func (n *Template) Validate() error { + return nil +}