repository attachment api
This commit is contained in:
parent
da2246d45a
commit
58128495a6
@ -53,6 +53,7 @@ func (a *App) Initialize(cfg *config.Config) error {
|
|||||||
onlyOfficeHandler := handler.NewOnlyOfficeHandler(services.onlyOfficeService)
|
onlyOfficeHandler := handler.NewOnlyOfficeHandler(services.onlyOfficeService)
|
||||||
analyticsHandler := handler.NewAnalyticsHandler(services.analyticsService)
|
analyticsHandler := handler.NewAnalyticsHandler(services.analyticsService)
|
||||||
notificationHandler := handler.NewNotificationHandler(services.notificationService)
|
notificationHandler := handler.NewNotificationHandler(services.notificationService)
|
||||||
|
repositoryAttachmentHandler := handler.NewRepositoryAttachmentHandler(services.repositoryAttachmentService)
|
||||||
|
|
||||||
a.router = router.NewRouter(
|
a.router = router.NewRouter(
|
||||||
cfg,
|
cfg,
|
||||||
@ -70,6 +71,7 @@ func (a *App) Initialize(cfg *config.Config) error {
|
|||||||
onlyOfficeHandler,
|
onlyOfficeHandler,
|
||||||
analyticsHandler,
|
analyticsHandler,
|
||||||
notificationHandler,
|
notificationHandler,
|
||||||
|
repositoryAttachmentHandler,
|
||||||
)
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -147,6 +149,7 @@ type repositories struct {
|
|||||||
approvalFlowRepo *repository.ApprovalFlowRepository
|
approvalFlowRepo *repository.ApprovalFlowRepository
|
||||||
letterOutgoingApprovalRepo *repository.LetterOutgoingApprovalRepository
|
letterOutgoingApprovalRepo *repository.LetterOutgoingApprovalRepository
|
||||||
analyticsRepo *repository.AnalyticsRepository
|
analyticsRepo *repository.AnalyticsRepository
|
||||||
|
repositoryAttachmentRepo *repository.RepositoryAttachmentRepositoryImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) initRepositories() *repositories {
|
func (a *App) initRepositories() *repositories {
|
||||||
@ -181,6 +184,7 @@ func (a *App) initRepositories() *repositories {
|
|||||||
approvalFlowRepo: repository.NewApprovalFlowRepository(a.db),
|
approvalFlowRepo: repository.NewApprovalFlowRepository(a.db),
|
||||||
letterOutgoingApprovalRepo: repository.NewLetterOutgoingApprovalRepository(a.db),
|
letterOutgoingApprovalRepo: repository.NewLetterOutgoingApprovalRepository(a.db),
|
||||||
analyticsRepo: repository.NewAnalyticsRepository(a.db),
|
analyticsRepo: repository.NewAnalyticsRepository(a.db),
|
||||||
|
repositoryAttachmentRepo: repository.NewRepositoryAttachmentRepositoryImpl(a.db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,13 +202,14 @@ type processors struct {
|
|||||||
letterDispositionProcessor *processor.LetterDispositionProcessorImpl
|
letterDispositionProcessor *processor.LetterDispositionProcessorImpl
|
||||||
letterDispositionDeptProcessor *processor.LetterDispositionDepartmentProcessorImpl
|
letterDispositionDeptProcessor *processor.LetterDispositionDepartmentProcessorImpl
|
||||||
// Modular processors for letter outgoing
|
// Modular processors for letter outgoing
|
||||||
letterValidationProcessor processor.LetterValidationProcessor
|
letterValidationProcessor processor.LetterValidationProcessor
|
||||||
letterCreationProcessor processor.LetterCreationProcessor
|
letterCreationProcessor processor.LetterCreationProcessor
|
||||||
letterApprovalProcessor processor.LetterApprovalProcessor
|
letterApprovalProcessor processor.LetterApprovalProcessor
|
||||||
letterAttachmentProcessor processor.LetterAttachmentProcessor
|
letterAttachmentProcessor processor.LetterAttachmentProcessor
|
||||||
letterOutgoingRecipientProcessor processor.LetterOutgoingRecipientProcessor
|
letterOutgoingRecipientProcessor processor.LetterOutgoingRecipientProcessor
|
||||||
letterActivityProcessor processor.LetterActivityProcessor
|
letterActivityProcessor processor.LetterActivityProcessor
|
||||||
txManager *repository.TxManager
|
repositoryAttachmentProcessor *processor.RepositoryAttachmentProcessorImpl
|
||||||
|
txManager *repository.TxManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processors {
|
func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processors {
|
||||||
@ -327,6 +332,9 @@ func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processor
|
|||||||
repos.letterRepo,
|
repos.letterRepo,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
repositoryAttachmentProc := processor.NewRepositoryAttachmentProcessor(
|
||||||
|
repos.repositoryAttachmentRepo)
|
||||||
|
|
||||||
return &processors{
|
return &processors{
|
||||||
userProcessor: userProc,
|
userProcessor: userProc,
|
||||||
cachedUserProcessor: cachedUserProc,
|
cachedUserProcessor: cachedUserProc,
|
||||||
@ -347,23 +355,25 @@ func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processor
|
|||||||
letterAttachmentProcessor: letterAttachmentProc,
|
letterAttachmentProcessor: letterAttachmentProc,
|
||||||
letterOutgoingRecipientProcessor: letterOutgoingRecipientProc,
|
letterOutgoingRecipientProcessor: letterOutgoingRecipientProc,
|
||||||
letterActivityProcessor: letterActivityProc,
|
letterActivityProcessor: letterActivityProc,
|
||||||
|
repositoryAttachmentProcessor: repositoryAttachmentProc,
|
||||||
txManager: txMgr,
|
txManager: txMgr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type services struct {
|
type services struct {
|
||||||
userService *service.UserServiceImpl
|
userService *service.UserServiceImpl
|
||||||
authService *service.AuthServiceImpl
|
authService *service.AuthServiceImpl
|
||||||
fileService *service.FileServiceImpl
|
fileService *service.FileServiceImpl
|
||||||
rbacService *service.RBACServiceImpl
|
rbacService *service.RBACServiceImpl
|
||||||
masterService *service.MasterServiceImpl
|
masterService *service.MasterServiceImpl
|
||||||
letterService *service.LetterServiceImpl
|
letterService *service.LetterServiceImpl
|
||||||
letterOutgoingService *service.LetterOutgoingServiceImpl
|
letterOutgoingService *service.LetterOutgoingServiceImpl
|
||||||
approvalFlowService *service.ApprovalFlowServiceImpl
|
approvalFlowService *service.ApprovalFlowServiceImpl
|
||||||
dispositionRouteService *service.DispositionRouteServiceImpl
|
dispositionRouteService *service.DispositionRouteServiceImpl
|
||||||
onlyOfficeService *service.OnlyOfficeServiceImpl
|
onlyOfficeService *service.OnlyOfficeServiceImpl
|
||||||
analyticsService *service.AnalyticsServiceImpl
|
analyticsService *service.AnalyticsServiceImpl
|
||||||
notificationService *service.NotificationServiceImpl
|
notificationService *service.NotificationServiceImpl
|
||||||
|
repositoryAttachmentService *service.RepositoryAttachmentServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) initServices(processors *processors, repos *repositories, cfg *config.Config) *services {
|
func (a *App) initServices(processors *processors, repos *repositories, cfg *config.Config) *services {
|
||||||
@ -423,19 +433,22 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con
|
|||||||
novuConfig := internalConfig.LoadNovuConfig(cfg)
|
novuConfig := internalConfig.LoadNovuConfig(cfg)
|
||||||
notificationSvc := service.NewNotificationService(novuConfig, processors.userProcessor)
|
notificationSvc := service.NewNotificationService(novuConfig, processors.userProcessor)
|
||||||
|
|
||||||
|
repositoryAttachmentSvc := service.NewRepositoryAttachmentService(processors.repositoryAttachmentProcessor)
|
||||||
|
|
||||||
return &services{
|
return &services{
|
||||||
userService: userSvc,
|
userService: userSvc,
|
||||||
authService: authService,
|
authService: authService,
|
||||||
fileService: fileSvc,
|
fileService: fileSvc,
|
||||||
rbacService: rbacSvc,
|
rbacService: rbacSvc,
|
||||||
masterService: masterSvc,
|
masterService: masterSvc,
|
||||||
letterService: letterSvc,
|
letterService: letterSvc,
|
||||||
letterOutgoingService: letterOutgoingSvc,
|
letterOutgoingService: letterOutgoingSvc,
|
||||||
approvalFlowService: approvalFlowSvc,
|
approvalFlowService: approvalFlowSvc,
|
||||||
dispositionRouteService: dispRouteSvc,
|
dispositionRouteService: dispRouteSvc,
|
||||||
onlyOfficeService: onlyOfficeSvc,
|
onlyOfficeService: onlyOfficeSvc,
|
||||||
analyticsService: analyticsSvc,
|
analyticsService: analyticsSvc,
|
||||||
notificationService: notificationSvc,
|
notificationService: notificationSvc,
|
||||||
|
repositoryAttachmentService: repositoryAttachmentSvc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
35
internal/contract/repository_attachment_contract.go
Normal file
35
internal/contract/repository_attachment_contract.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package contract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateRepositoryAttachmentRequest struct {
|
||||||
|
FileURL string `json:"file_url" validate:"required"`
|
||||||
|
FileName string `json:"file_name" validate:"required"`
|
||||||
|
FileType string `json:"file_type" validate:"required"`
|
||||||
|
Category string `json:"category" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RepositoryAttachmentsResponse struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
FileURL string `json:"file_url"`
|
||||||
|
FileName string `json:"file_name"`
|
||||||
|
FileType string `json:"file_type"`
|
||||||
|
Category string `json:"category"`
|
||||||
|
UploadBy uuid.UUID `json:"upload_by"`
|
||||||
|
UploadAt time.Time `json:"upload_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListRepositoryAttachmentsResponse struct {
|
||||||
|
Attachments []RepositoryAttachmentsResponse `json:"attachments"`
|
||||||
|
Pagination PaginationResponse `json:"pagination"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListRepositoryAttachmentsRequest struct {
|
||||||
|
Page int `json:"page"`
|
||||||
|
Limit int `json:"limit"`
|
||||||
|
Search *string `json:"search,omitempty"`
|
||||||
|
}
|
||||||
19
internal/entities/repository_attachment.go
Normal file
19
internal/entities/repository_attachment.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepositoryAttachment struct {
|
||||||
|
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||||
|
FileURL string `gorm:"not null" json:"file_url"`
|
||||||
|
FileName string `gorm:"not null" json:"file_name"`
|
||||||
|
FileType string `gorm:"not null" json:"file_type"`
|
||||||
|
Category string `gorm:"not null" json:"category"`
|
||||||
|
UploadedBy *uuid.UUID `json:"uploaded_by,omitempty"`
|
||||||
|
UploadedAt time.Time `gorm:"autoCreateTime" json:"uploaded_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (RepositoryAttachment) TableName() string { return "repository_attachments" }
|
||||||
137
internal/handler/repository_attachment_handler.go
Normal file
137
internal/handler/repository_attachment_handler.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"eslogad-be/internal/constants"
|
||||||
|
"eslogad-be/internal/contract"
|
||||||
|
"eslogad-be/internal/logger"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepositoryAttachmentHandler struct {
|
||||||
|
attachmentService RepositoryAttachmentService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRepositoryAttachmentHandler(attachmentService RepositoryAttachmentService) *RepositoryAttachmentHandler {
|
||||||
|
return &RepositoryAttachmentHandler{
|
||||||
|
attachmentService: attachmentService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RepositoryAttachmentHandler) CreateAttachment(c *gin.Context) {
|
||||||
|
var req contract.CreateRepositoryAttachmentRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
logger.FromContext(c).WithError(err).Error("UserHandler::CreateAttachment -> request binding failed")
|
||||||
|
h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userResponse, err := h.attachmentService.CreateAttachment(c.Request.Context(), &req)
|
||||||
|
if err != nil {
|
||||||
|
logger.FromContext(c).WithError(err).Error("UserHandler::CreateAttachment -> Failed to create user from service")
|
||||||
|
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.FromContext(c).Infof("UserHandler::CreateUser -> Successfully created repository attachment = %+v", userResponse)
|
||||||
|
c.JSON(http.StatusOK, contract.BuildSuccessResponse(userResponse))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RepositoryAttachmentHandler) DeleteAttachment(c *gin.Context) {
|
||||||
|
attachmentIDStr := c.Param("id")
|
||||||
|
attachmentID, err := uuid.Parse(attachmentIDStr)
|
||||||
|
if err != nil {
|
||||||
|
logger.FromContext(c).WithError(err).Error("UserHandler::DeleteAttachment -> Invalid attachment id")
|
||||||
|
h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.attachmentService.DeleteAttachment(c.Request.Context(), attachmentID)
|
||||||
|
if err != nil {
|
||||||
|
logger.FromContext(c).WithError(err).Error("UserHandler::DeleteAttachment -> Failed to delete attachment from service")
|
||||||
|
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.FromContext(c).Info("UserHandler::DeleteAttachment -> Successfully deleted attachment")
|
||||||
|
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "User deleted successfully"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RepositoryAttachmentHandler) GetAttachment(c *gin.Context) {
|
||||||
|
attachmentIDStr := c.Param("id")
|
||||||
|
attachmentID, err := uuid.Parse(attachmentIDStr)
|
||||||
|
if err != nil {
|
||||||
|
logger.FromContext(c).WithError(err).Error("UserHandler::GetAttachment -> Invalid attachment ID")
|
||||||
|
h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
attachmentResponse, err := h.attachmentService.GetById(c.Request.Context(), attachmentID)
|
||||||
|
if err != nil {
|
||||||
|
logger.FromContext(c).WithError(err).Error("UserHandler::GetAttachment -> Failed to get attachment from service")
|
||||||
|
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.FromContext(c).Infof("UserHandler::GetAttachment -> Successfully retrieved attachment = %+v", attachmentResponse)
|
||||||
|
c.JSON(http.StatusOK, attachmentResponse)
|
||||||
|
}
|
||||||
|
func (h *RepositoryAttachmentHandler) ListAttachment(c *gin.Context) {
|
||||||
|
ctx := c.Request.Context()
|
||||||
|
|
||||||
|
req := &contract.ListRepositoryAttachmentsRequest{
|
||||||
|
Page: 1,
|
||||||
|
Limit: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
if page := c.Query("page"); page != "" {
|
||||||
|
if p, err := strconv.Atoi(page); err == nil {
|
||||||
|
req.Page = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if limit := c.Query("limit"); limit != "" {
|
||||||
|
if l, err := strconv.Atoi(limit); err == nil {
|
||||||
|
req.Limit = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attachmentsResponse, err := h.attachmentService.ListAttachment(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
logger.FromContext(c).WithError(err).Error("UserHandler::ListUsers -> Failed to list users from service")
|
||||||
|
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.FromContext(c).Infof("UserHandler::ListUsers -> Successfully listed users = %+v", attachmentsResponse)
|
||||||
|
c.JSON(http.StatusOK, contract.BuildSuccessResponse(attachmentsResponse))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RepositoryAttachmentHandler) sendValidationErrorResponse(c *gin.Context, message string, errorCode string) {
|
||||||
|
statusCode := constants.HttpErrorMap[errorCode]
|
||||||
|
if statusCode == 0 {
|
||||||
|
statusCode = http.StatusBadRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
errorResponse := &contract.ErrorResponse{
|
||||||
|
Error: message,
|
||||||
|
Code: statusCode,
|
||||||
|
Details: map[string]interface{}{
|
||||||
|
"error_code": errorCode,
|
||||||
|
"entity": constants.UserValidatorEntity,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.JSON(statusCode, errorResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RepositoryAttachmentHandler) sendErrorResponse(c *gin.Context, message string, statusCode int) {
|
||||||
|
errorResponse := &contract.ErrorResponse{
|
||||||
|
Error: message,
|
||||||
|
Code: statusCode,
|
||||||
|
Details: map[string]interface{}{},
|
||||||
|
}
|
||||||
|
c.JSON(statusCode, errorResponse)
|
||||||
|
}
|
||||||
15
internal/handler/repository_attachment_service.go
Normal file
15
internal/handler/repository_attachment_service.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"eslogad-be/internal/contract"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepositoryAttachmentService interface {
|
||||||
|
CreateAttachment(ctx context.Context, req *contract.CreateRepositoryAttachmentRequest) (*contract.RepositoryAttachmentsResponse, error)
|
||||||
|
DeleteAttachment(ctx context.Context, id uuid.UUID) error
|
||||||
|
GetById(ctx context.Context, id uuid.UUID) (*contract.RepositoryAttachmentsResponse, error)
|
||||||
|
ListAttachment(ctx context.Context, req *contract.ListRepositoryAttachmentsRequest) (*contract.ListRepositoryAttachmentsResponse, error)
|
||||||
|
}
|
||||||
78
internal/processor/repository_attachment_processor.go
Normal file
78
internal/processor/repository_attachment_processor.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package processor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"eslogad-be/internal/appcontext"
|
||||||
|
"eslogad-be/internal/contract"
|
||||||
|
"eslogad-be/internal/transformer"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepositoryAttachmentProcessorImpl struct {
|
||||||
|
attachmentRepo RepositoryAttachmentRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRepositoryAttachmentProcessor(attachmentRepo RepositoryAttachmentRepository) *RepositoryAttachmentProcessorImpl {
|
||||||
|
return &RepositoryAttachmentProcessorImpl{
|
||||||
|
attachmentRepo: attachmentRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RepositoryAttachmentProcessorImpl) CreateAttachment(ctx context.Context, req *contract.CreateRepositoryAttachmentRequest) (*contract.RepositoryAttachmentsResponse, error) {
|
||||||
|
userID := getUserIDFromContext(ctx)
|
||||||
|
|
||||||
|
attachmentEntity := transformer.CreateRepositoryAttachmentRequestToEntity(req, userID)
|
||||||
|
|
||||||
|
err := p.attachmentRepo.Create(ctx, attachmentEntity)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create repository attachment: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformer.RepositoryAttachmentEntityToContract(attachmentEntity), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserIDFromContext(ctx context.Context) uuid.UUID {
|
||||||
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
|
if appCtx != nil {
|
||||||
|
return appCtx.UserID
|
||||||
|
}
|
||||||
|
return uuid.New()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RepositoryAttachmentProcessorImpl) DeleteAttachment(ctx context.Context, id uuid.UUID) error {
|
||||||
|
_, err := p.attachmentRepo.GetByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("repository attachment not found: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.attachmentRepo.Delete(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete repository attachment: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RepositoryAttachmentProcessorImpl) GetById(ctx context.Context, id uuid.UUID) (*contract.RepositoryAttachmentsResponse, error) {
|
||||||
|
attachment, err := p.attachmentRepo.GetByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("repository attachment not found: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := transformer.RepositoryAttachmentEntityToContract(attachment)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RepositoryAttachmentProcessorImpl) ListAttachment(ctx context.Context, search *string, limit, offset int) ([]contract.RepositoryAttachmentsResponse, int, error) {
|
||||||
|
attachments, totalCount, err := p.attachmentRepo.List(ctx, search, limit, offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("failed to get users: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
responses := transformer.RepositoryAttachmentEntityToContracts(attachments)
|
||||||
|
|
||||||
|
return responses, int(totalCount), nil
|
||||||
|
}
|
||||||
16
internal/processor/repository_attachment_repository.go
Normal file
16
internal/processor/repository_attachment_repository.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package processor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"eslogad-be/internal/entities"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepositoryAttachmentRepository interface {
|
||||||
|
Create(ctx context.Context, user *entities.RepositoryAttachment) error
|
||||||
|
GetByID(ctx context.Context, id uuid.UUID) (*entities.RepositoryAttachment, error)
|
||||||
|
Update(ctx context.Context, user *entities.RepositoryAttachment) error
|
||||||
|
Delete(ctx context.Context, id uuid.UUID) error
|
||||||
|
List(ctx context.Context, search *string, limit, offset int) ([]*entities.RepositoryAttachment, int64, error)
|
||||||
|
}
|
||||||
74
internal/repository/repository_attachment_repository.go
Normal file
74
internal/repository/repository_attachment_repository.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"eslogad-be/internal/entities"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepositoryAttachmentRepositoryImpl struct {
|
||||||
|
b *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRepositoryAttachmentRepositoryImpl(db *gorm.DB) *RepositoryAttachmentRepositoryImpl {
|
||||||
|
return &RepositoryAttachmentRepositoryImpl{
|
||||||
|
b: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RepositoryAttachmentRepositoryImpl) Create(ctx context.Context, user *entities.RepositoryAttachment) error {
|
||||||
|
return r.b.WithContext(ctx).Create(user).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RepositoryAttachmentRepositoryImpl) GetByID(ctx context.Context, id uuid.UUID) (*entities.RepositoryAttachment, error) {
|
||||||
|
var attachment entities.RepositoryAttachment
|
||||||
|
err := r.b.WithContext(ctx).
|
||||||
|
First(&attachment, "id = ?", id).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &attachment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RepositoryAttachmentRepositoryImpl) Update(ctx context.Context, user *entities.RepositoryAttachment) error {
|
||||||
|
return r.b.WithContext(ctx).Save(user).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RepositoryAttachmentRepositoryImpl) Delete(ctx context.Context, id uuid.UUID) error {
|
||||||
|
return r.b.WithContext(ctx).Delete(&entities.RepositoryAttachment{}, "id = ?", id).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RepositoryAttachmentRepositoryImpl) List(ctx context.Context, search *string, limit, offset int) ([]*entities.RepositoryAttachment, int64, error) {
|
||||||
|
var attachments []*entities.RepositoryAttachment
|
||||||
|
var total int64
|
||||||
|
|
||||||
|
baseQuery := r.b.WithContext(ctx).Model(&entities.RepositoryAttachment{})
|
||||||
|
|
||||||
|
if search != nil && *search != "" {
|
||||||
|
like := "%" + *search + "%"
|
||||||
|
baseQuery = baseQuery.Where("name ILIKE ? OR email ILIKE ?", like, like)
|
||||||
|
}
|
||||||
|
|
||||||
|
countQuery := baseQuery
|
||||||
|
if err := countQuery.Count(&total).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dataQuery := r.b.WithContext(ctx).Model(&entities.RepositoryAttachment{})
|
||||||
|
|
||||||
|
if search != nil && *search != "" {
|
||||||
|
like := "%" + *search + "%"
|
||||||
|
dataQuery = dataQuery.Where("name ILIKE ? OR category ILIKE ?", like, like)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dataQuery.
|
||||||
|
Limit(limit).
|
||||||
|
Offset(offset).
|
||||||
|
Find(&attachments).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return attachments, total, nil
|
||||||
|
}
|
||||||
@ -180,3 +180,10 @@ type NotificationHandler interface {
|
|||||||
GetCurrentUserSubscriber(c *gin.Context)
|
GetCurrentUserSubscriber(c *gin.Context)
|
||||||
UpdateCurrentUserSubscriberChannel(c *gin.Context)
|
UpdateCurrentUserSubscriberChannel(c *gin.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RepositoryAttachmentHandler interface {
|
||||||
|
CreateAttachment(c *gin.Context)
|
||||||
|
DeleteAttachment(c *gin.Context)
|
||||||
|
GetAttachment(c *gin.Context)
|
||||||
|
ListAttachment(c *gin.Context)
|
||||||
|
}
|
||||||
|
|||||||
@ -8,21 +8,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
config *config.Config
|
config *config.Config
|
||||||
authHandler AuthHandler
|
authHandler AuthHandler
|
||||||
healthHandler HealthHandler
|
healthHandler HealthHandler
|
||||||
authMiddleware AuthMiddleware
|
authMiddleware AuthMiddleware
|
||||||
userHandler UserHandler
|
userHandler UserHandler
|
||||||
fileHandler FileHandler
|
fileHandler FileHandler
|
||||||
rbacHandler RBACHandler
|
rbacHandler RBACHandler
|
||||||
masterHandler MasterHandler
|
masterHandler MasterHandler
|
||||||
letterHandler LetterHandler
|
letterHandler LetterHandler
|
||||||
letterOutgoingHandler LetterOutgoingHandler
|
letterOutgoingHandler LetterOutgoingHandler
|
||||||
adminApprovalFlowHandler AdminApprovalFlowHandler
|
adminApprovalFlowHandler AdminApprovalFlowHandler
|
||||||
dispRouteHandler DispositionRouteHandler
|
dispRouteHandler DispositionRouteHandler
|
||||||
onlyOfficeHandler OnlyOfficeHandler
|
onlyOfficeHandler OnlyOfficeHandler
|
||||||
analyticsHandler AnalyticsHandler
|
analyticsHandler AnalyticsHandler
|
||||||
notificationHandler NotificationHandler
|
notificationHandler NotificationHandler
|
||||||
|
repositoryAttachmentHandler RepositoryAttachmentHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouter(
|
func NewRouter(
|
||||||
@ -41,23 +42,25 @@ func NewRouter(
|
|||||||
onlyOfficeHandler OnlyOfficeHandler,
|
onlyOfficeHandler OnlyOfficeHandler,
|
||||||
analyticsHandler AnalyticsHandler,
|
analyticsHandler AnalyticsHandler,
|
||||||
notificationHandler NotificationHandler,
|
notificationHandler NotificationHandler,
|
||||||
|
repositoryAttachmentHandler RepositoryAttachmentHandler,
|
||||||
) *Router {
|
) *Router {
|
||||||
return &Router{
|
return &Router{
|
||||||
config: cfg,
|
config: cfg,
|
||||||
authHandler: authHandler,
|
authHandler: authHandler,
|
||||||
authMiddleware: authMiddleware,
|
authMiddleware: authMiddleware,
|
||||||
healthHandler: healthHandler,
|
healthHandler: healthHandler,
|
||||||
userHandler: userHandler,
|
userHandler: userHandler,
|
||||||
fileHandler: fileHandler,
|
fileHandler: fileHandler,
|
||||||
rbacHandler: rbacHandler,
|
rbacHandler: rbacHandler,
|
||||||
masterHandler: masterHandler,
|
masterHandler: masterHandler,
|
||||||
letterHandler: letterHandler,
|
letterHandler: letterHandler,
|
||||||
letterOutgoingHandler: letterOutgoingHandler,
|
letterOutgoingHandler: letterOutgoingHandler,
|
||||||
adminApprovalFlowHandler: adminApprovalFlowHandler,
|
adminApprovalFlowHandler: adminApprovalFlowHandler,
|
||||||
dispRouteHandler: dispRouteHandler,
|
dispRouteHandler: dispRouteHandler,
|
||||||
onlyOfficeHandler: onlyOfficeHandler,
|
onlyOfficeHandler: onlyOfficeHandler,
|
||||||
analyticsHandler: analyticsHandler,
|
analyticsHandler: analyticsHandler,
|
||||||
notificationHandler: notificationHandler,
|
notificationHandler: notificationHandler,
|
||||||
|
repositoryAttachmentHandler: repositoryAttachmentHandler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,6 +239,15 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
|
|||||||
droutes.PUT("/:id/active", r.dispRouteHandler.SetActive)
|
droutes.PUT("/:id/active", r.dispRouteHandler.SetActive)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
repoattachsch := v1.Group("/repository-attachments")
|
||||||
|
repoattachsch.Use(r.authMiddleware.RequireAuth())
|
||||||
|
{
|
||||||
|
repoattachsch.POST("", r.repositoryAttachmentHandler.CreateAttachment)
|
||||||
|
repoattachsch.GET("", r.repositoryAttachmentHandler.ListAttachment)
|
||||||
|
repoattachsch.DELETE("/:id", r.repositoryAttachmentHandler.DeleteAttachment)
|
||||||
|
repoattachsch.GET("/:id", r.repositoryAttachmentHandler.GetAttachment)
|
||||||
|
}
|
||||||
|
|
||||||
admin := v1.Group("/setting")
|
admin := v1.Group("/setting")
|
||||||
admin.Use(r.authMiddleware.RequireAuth())
|
admin.Use(r.authMiddleware.RequireAuth())
|
||||||
{
|
{
|
||||||
|
|||||||
15
internal/service/repository_attachment_processor.go
Normal file
15
internal/service/repository_attachment_processor.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"eslogad-be/internal/contract"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepositoryAttachmentProcessor interface {
|
||||||
|
CreateAttachment(ctx context.Context, req *contract.CreateRepositoryAttachmentRequest) (*contract.RepositoryAttachmentsResponse, error)
|
||||||
|
DeleteAttachment(ctx context.Context, id uuid.UUID) error
|
||||||
|
GetById(ctx context.Context, id uuid.UUID) (*contract.RepositoryAttachmentsResponse, error)
|
||||||
|
ListAttachment(ctx context.Context, search *string, limit, offset int) ([]contract.RepositoryAttachmentsResponse, int, error)
|
||||||
|
}
|
||||||
59
internal/service/repository_attachment_service.go
Normal file
59
internal/service/repository_attachment_service.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"eslogad-be/internal/contract"
|
||||||
|
"eslogad-be/internal/transformer"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepositoryAttachmentServiceImpl struct {
|
||||||
|
attachmentProcessor RepositoryAttachmentProcessor
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRepositoryAttachmentService(attachmentProcessor RepositoryAttachmentProcessor) *RepositoryAttachmentServiceImpl {
|
||||||
|
return &RepositoryAttachmentServiceImpl{
|
||||||
|
attachmentProcessor: attachmentProcessor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RepositoryAttachmentServiceImpl) CreateAttachment(ctx context.Context, req *contract.CreateRepositoryAttachmentRequest) (*contract.RepositoryAttachmentsResponse, error) {
|
||||||
|
return s.attachmentProcessor.CreateAttachment(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RepositoryAttachmentServiceImpl) DeleteAttachment(ctx context.Context, id uuid.UUID) error {
|
||||||
|
return s.attachmentProcessor.DeleteAttachment(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RepositoryAttachmentServiceImpl) GetById(ctx context.Context, id uuid.UUID) (*contract.RepositoryAttachmentsResponse, error) {
|
||||||
|
return s.attachmentProcessor.GetById(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RepositoryAttachmentServiceImpl) ListAttachment(ctx context.Context, req *contract.ListRepositoryAttachmentsRequest) (*contract.ListRepositoryAttachmentsResponse, error) {
|
||||||
|
page := req.Page
|
||||||
|
if page <= 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
limit := req.Limit
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
if limit > 100 {
|
||||||
|
limit = 100 // Max limit to prevent performance issues
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := (page - 1) * limit
|
||||||
|
|
||||||
|
// Pass calculated offset and limit to processor
|
||||||
|
attachmentResponses, totalCount, err := s.attachmentProcessor.ListAttachment(ctx, req.Search, limit, offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &contract.ListRepositoryAttachmentsResponse{
|
||||||
|
Attachments: attachmentResponses,
|
||||||
|
Pagination: transformer.CreatePaginationResponse(totalCount, page, limit),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
49
internal/transformer/repository_attachment_transformer.go
Normal file
49
internal/transformer/repository_attachment_transformer.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package transformer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"eslogad-be/internal/contract"
|
||||||
|
"eslogad-be/internal/entities"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateRepositoryAttachmentRequestToEntity(req *contract.CreateRepositoryAttachmentRequest, userId uuid.UUID) *entities.RepositoryAttachment {
|
||||||
|
if req == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &entities.RepositoryAttachment{
|
||||||
|
FileName: req.FileName,
|
||||||
|
FileType: req.FileType,
|
||||||
|
FileURL: req.FileURL,
|
||||||
|
UploadedBy: &userId,
|
||||||
|
Category: req.Category,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RepositoryAttachmentEntityToContract(entity *entities.RepositoryAttachment) *contract.RepositoryAttachmentsResponse {
|
||||||
|
resp := &contract.RepositoryAttachmentsResponse{
|
||||||
|
ID: entity.ID,
|
||||||
|
FileName: entity.FileName,
|
||||||
|
FileType: entity.FileType,
|
||||||
|
FileURL: entity.FileURL,
|
||||||
|
Category: entity.Category,
|
||||||
|
UploadBy: *entity.UploadedBy,
|
||||||
|
UploadAt: entity.UploadedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
func RepositoryAttachmentEntityToContracts(attachments []*entities.RepositoryAttachment) []contract.RepositoryAttachmentsResponse {
|
||||||
|
if attachments == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
responses := make([]contract.RepositoryAttachmentsResponse, len(attachments))
|
||||||
|
for i, u := range attachments {
|
||||||
|
resp := RepositoryAttachmentEntityToContract(u)
|
||||||
|
if resp != nil {
|
||||||
|
responses[i] = *resp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return responses
|
||||||
|
}
|
||||||
13
migrations/000044_create_repository_attachments_table.up.sql
Normal file
13
migrations/000044_create_repository_attachments_table.up.sql
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS repository_attachments (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
file_url TEXT NOT NULL,
|
||||||
|
file_name TEXT NOT NULL,
|
||||||
|
file_type TEXT NOT NULL,
|
||||||
|
category TEXT NOT NULL,
|
||||||
|
uploaded_by UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||||
|
uploaded_at TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
Loading…
x
Reference in New Issue
Block a user