package database import ( "context" "errors" "git.anthrove.art/Anthrove/otter-space-sdk/v5/internal/utils" otterError "git.anthrove.art/Anthrove/otter-space-sdk/v5/pkg/error" "git.anthrove.art/Anthrove/otter-space-sdk/v5/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() utils.HandleEvent(span, localLogger, "Starting source creation") if client == nil { 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{}, utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DuplicateKey}) } return models.Source{}, utils.HandleError(ctx, span, localLogger, result.Error) } localLogger = localLogger.WithFields(log.Fields{ "source_id": source.ID, }) span.SetAttributes( attribute.String("source_id", string(source.ID)), ) 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 = localLogger.WithFields(log.Fields{ "source_count": len(source), "batch_size": batchSize, }) span.SetAttributes( attribute.Int("batch_size", batchSize), attribute.Int("source_count", len(source)), ) utils.HandleEvent(span, localLogger, "Starting batch source creation") if client == nil { return utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DatabaseIsNotConnected}) } if source == nil || len(source) == 0 { return utils.HandleError(ctx, span, localLogger, &otterError.EntityValidationFailed{Reason: otterError.SourceListIsEmpty}) } if batchSize == 0 { 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 utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DuplicateKey}) } return utils.HandleError(ctx, span, localLogger, result.Error) } utils.HandleEvent(span, localLogger, "Batch sources created successfully") return nil } // UpdateSource updates th source information in the database. // Only a few parameter can be updated: // - DisplayName // - Domain // - Icon func UpdateSource(ctx context.Context, source models.Source) error { ctx, span, localLogger := utils.SetupTracing(ctx, tracer, "UpdateSource") defer span.End() localLogger = 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 utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DatabaseIsNotConnected}) } if len(source.ID) == 0 { return utils.HandleError(ctx, span, localLogger, &otterError.EntityValidationFailed{Reason: otterError.SourceIDIsEmpty}) } updateSource := models.Source{ BaseModel: models.BaseModel[models.SourceID]{ ID: source.ID, }, DisplayName: source.DisplayName, Domain: source.Domain, Icon: source.Icon, } result := client.WithContext(ctx).Updates(&updateSource) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.NoDataFound}) } 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 = 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{}, utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DatabaseIsNotConnected}) } if len(id) == 0 { return models.Source{}, utils.HandleError(ctx, span, localLogger, &otterError.EntityValidationFailed{Reason: otterError.SourceIDIsEmpty}) } if len(id) != 25 { return models.Source{}, utils.HandleError(ctx, span, localLogger, &otterError.EntityValidationFailed{Reason: otterError.SourceIDIsWrongLength}) } result := client.WithContext(ctx).First(&source, "id = ?", id) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return models.Source{}, utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.NoDataFound}) } 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 = 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{}, utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DatabaseIsNotConnected}) } if len(sourceDomain) == 0 { 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{}, utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.NoDataFound}) } 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 = 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 utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.DatabaseIsNotConnected}) } if len(id) == 0 { return utils.HandleError(ctx, span, localLogger, &otterError.EntityValidationFailed{Reason: otterError.SourceIDIsEmpty}) } if len(id) != 25 { return utils.HandleError(ctx, span, localLogger, &otterError.EntityValidationFailed{Reason: otterError.SourceIDIsWrongLength}) } result := client.WithContext(ctx).Delete(&source, id) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return utils.HandleError(ctx, span, localLogger, &otterError.Database{Reason: otterError.NoDataFound}) } return utils.HandleError(ctx, span, localLogger, result.Error) } utils.HandleEvent(span, localLogger, "Source deleted successfully") return nil }