diff --git a/pkg/database/source.go b/pkg/database/source.go index 89116a2..c5691d0 100644 --- a/pkg/database/source.go +++ b/pkg/database/source.go @@ -3,62 +3,105 @@ package database import ( "context" "errors" + + "git.anthrove.art/Anthrove/otter-space-sdk/v2/internal/utils" otterError "git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/error" "git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/models" + log "github.com/sirupsen/logrus" + "go.opentelemetry.io/otel/attribute" "gorm.io/gorm" ) func CreateSource(ctx context.Context, source models.Source) (models.Source, error) { + ctx, span, localLogger := utils.SetupTracing(ctx, tracer, "CreateSource") + defer span.End() + + localLogger.WithFields(log.Fields{ + "source_id": source.ID, + }) + + span.SetAttributes( + attribute.String("source_id", string(source.ID)), + ) + + utils.HandleEvent(span, localLogger, "Starting source creation") + if client == nil { - return models.Source{}, &otterError.Database{Reason: otterError.DatabaseIsNotConnected} + return models.Source{}, utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DatabaseIsNotConnected}) } result := client.WithContext(ctx).Create(&source) if result.Error != nil { if errors.Is(result.Error, gorm.ErrDuplicatedKey) { - return models.Source{}, &otterError.Database{Reason: otterError.DuplicateKey} + return models.Source{}, utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DuplicateKey}) } - return models.Source{}, result.Error + return models.Source{}, utils.HandleError(ctx, span, localLogger, result.Error) } + utils.HandleEvent(span, localLogger, "Source created successfully") return source, nil } func CreateSourceInBatch(ctx context.Context, source []models.Source, batchSize int) error { + ctx, span, localLogger := utils.SetupTracing(ctx, tracer, "CreateSourceInBatch") + defer span.End() + + localLogger.WithFields(log.Fields{ + "source_count": len(source), + "batch_size": batchSize, + }) + + span.SetAttributes( + attribute.Int64("batch_size", int64(batchSize)), + attribute.Int64("source_count", int64(len(source))), + ) + + utils.HandleEvent(span, localLogger, "Starting batch source creation") + if client == nil { - return &otterError.Database{Reason: otterError.DatabaseIsNotConnected} + return utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DatabaseIsNotConnected}) } - if source == nil { - return &otterError.EntityValidationFailed{Reason: otterError.SourceListIsEmpty} - } - - if len(source) == 0 { - return &otterError.EntityValidationFailed{Reason: otterError.SourceListIsEmpty} + if source == nil || len(source) == 0 { + return utils.HandleError(ctx, span, localLogger, &otterError.EntityValidationFailed{Reason: otterError.SourceListIsEmpty}) } if batchSize == 0 { - return &otterError.EntityValidationFailed{Reason: otterError.BatchSizeIsEmpty} + return utils.HandleError(ctx, span, localLogger, &otterError.EntityValidationFailed{Reason: otterError.BatchSizeIsEmpty}) } result := client.WithContext(ctx).CreateInBatches(&source, batchSize) if result.Error != nil { if errors.Is(result.Error, gorm.ErrDuplicatedKey) { - return &otterError.Database{Reason: otterError.DuplicateKey} + return utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DuplicateKey}) } - return result.Error + return utils.HandleError(ctx, span, localLogger, result.Error) } + utils.HandleEvent(span, localLogger, "Batch sources created successfully") return nil } func UpdateSource(ctx context.Context, source models.Source) error { + ctx, span, localLogger := utils.SetupTracing(ctx, tracer, "UpdateSource") + defer span.End() + + localLogger.WithFields(log.Fields{ + "source_id": source.ID, + }) + + span.SetAttributes( + attribute.String("source_id", string(source.ID)), + ) + + utils.HandleEvent(span, localLogger, "Starting source update") + if client == nil { - return &otterError.Database{Reason: otterError.DatabaseIsNotConnected} + return utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DatabaseIsNotConnected}) } if len(source.ID) == 0 { - return &otterError.EntityValidationFailed{Reason: otterError.SourceIDIsEmpty} + return utils.HandleError(ctx, span, localLogger, &otterError.EntityValidationFailed{Reason: otterError.SourceIDIsEmpty}) } updateSource := models.Source{ @@ -70,84 +113,127 @@ func UpdateSource(ctx context.Context, source models.Source) error { result := client.WithContext(ctx).Model(&updateSource).Update("deleted_at", gorm.DeletedAt{}) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { - return &otterError.Database{Reason: otterError.NoDataFound} + return utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.NoDataFound}) } - return result.Error + return utils.HandleError(ctx, span, localLogger, result.Error) } + utils.HandleEvent(span, localLogger, "Source updated successfully") return nil } func GetSourceByID(ctx context.Context, id models.SourceID) (models.Source, error) { + ctx, span, localLogger := utils.SetupTracing(ctx, tracer, "GetSourceByID") + defer span.End() + + localLogger.WithFields(log.Fields{ + "source_id": id, + }) + + span.SetAttributes( + attribute.String("source_id", string(id)), + ) + + utils.HandleEvent(span, localLogger, "Starting get source by ID") + var source models.Source if client == nil { - return models.Source{}, &otterError.Database{Reason: otterError.DatabaseIsNotConnected} + return models.Source{}, utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DatabaseIsNotConnected}) } if len(id) == 0 { - return models.Source{}, &otterError.EntityValidationFailed{Reason: otterError.SourceIDIsEmpty} + return models.Source{}, utils.HandleError(ctx, span, localLogger, &otterError.EntityValidationFailed{Reason: otterError.SourceIDIsEmpty}) } if len(id) != 25 { - return models.Source{}, &otterError.EntityValidationFailed{Reason: otterError.SourceIDToShort} + return models.Source{}, utils.HandleError(ctx, span, localLogger, &otterError.EntityValidationFailed{Reason: otterError.SourceIDToShort}) } result := client.WithContext(ctx).First(&source, "id = ?", id) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { - return models.Source{}, &otterError.Database{Reason: otterError.NoDataFound} + return models.Source{}, utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.NoDataFound}) } - return models.Source{}, result.Error + return models.Source{}, utils.HandleError(ctx, span, localLogger, result.Error) } + utils.HandleEvent(span, localLogger, "Source retrieved successfully") return source, nil } func GetSourceByDomain(ctx context.Context, sourceDomain models.SourceDomain) (models.Source, error) { + ctx, span, localLogger := utils.SetupTracing(ctx, tracer, "GetSourceByDomain") + defer span.End() + + localLogger.WithFields(log.Fields{ + "source_domain": sourceDomain, + }) + + span.SetAttributes( + attribute.String("source_domain", string(sourceDomain)), + ) + + utils.HandleEvent(span, localLogger, "Starting get source by domain") + var source models.Source if client == nil { - return models.Source{}, &otterError.Database{Reason: otterError.DatabaseIsNotConnected} + return models.Source{}, utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DatabaseIsNotConnected}) } if len(sourceDomain) == 0 { - return models.Source{}, &otterError.EntityValidationFailed{Reason: otterError.SourceDomainIsEmpty} + return models.Source{}, utils.HandleError(ctx, span, localLogger, &otterError.EntityValidationFailed{Reason: otterError.SourceDomainIsEmpty}) } result := client.WithContext(ctx).First(&source, "domain = ?", sourceDomain) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { - return models.Source{}, &otterError.Database{Reason: otterError.NoDataFound} + return models.Source{}, utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.NoDataFound}) } - return models.Source{}, result.Error + return models.Source{}, utils.HandleError(ctx, span, localLogger, result.Error) } + utils.HandleEvent(span, localLogger, "Source retrieved successfully") return source, nil } func DeleteSource(ctx context.Context, id models.SourceID) error { + ctx, span, localLogger := utils.SetupTracing(ctx, tracer, "DeleteSource") + defer span.End() + + localLogger.WithFields(log.Fields{ + "source_id": id, + }) + + span.SetAttributes( + attribute.String("source_id", string(id)), + ) + + utils.HandleEvent(span, localLogger, "Starting delete source") + var source models.Source if client == nil { - return &otterError.Database{Reason: otterError.DatabaseIsNotConnected} + return utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DatabaseIsNotConnected}) } if len(id) == 0 { - return &otterError.EntityValidationFailed{Reason: otterError.SourceIDIsEmpty} + return utils.HandleError(ctx, span, localLogger, &otterError.EntityValidationFailed{Reason: otterError.SourceIDIsEmpty}) } if len(id) != 25 { - return &otterError.EntityValidationFailed{Reason: otterError.SourceIDToShort} + return utils.HandleError(ctx, span, localLogger, &otterError.EntityValidationFailed{Reason: otterError.SourceIDToShort}) } result := client.WithContext(ctx).Delete(&source, id) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { - return &otterError.Database{Reason: otterError.NoDataFound} + return utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.NoDataFound}) } - return result.Error + return utils.HandleError(ctx, span, localLogger, result.Error) } + utils.HandleEvent(span, localLogger, "Source deleted successfully") return nil }