Ctrl K
ComunidadGuías…

internal/repository/channel_postgres.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package repository

import (
	"context"
	"errors"
	"fmt"
	"strconv"
	"time"
	"xprem/internal/database"
	"xprem/internal/database/postgres/pgdb"
	"xprem/internal/types"

	"github.com/jackc/pgx/v5"
)

type PostgresChannelRepository struct {
	engine *database.Engine
}

func NewPostgresChannelRepository(engine *database.Engine) *PostgresChannelRepository {
	return &PostgresChannelRepository{
		engine: engine,
	}
}

func (s *PostgresChannelRepository) InsertChannel(ctx context.Context, appId string, branchId *int64, channelName string) (int64, error) {
	pgAppID := ToPgUUID(appId)
	insertedId, err := s.engine.Queries.InsertChannel(ctx, pgdb.InsertChannelParams{
		AppID:    pgAppID,
		Name:     channelName,
		BranchID: branchId,
	})
	if err != nil {
		if database.IsUniqueViolation(err) {
			return 0, &ErrResourceAlreadyExists{Resource: "channel", Identifier: fmt.Sprintf("%s (appId: %s)", channelName, appId)}
		}
		return 0, fmt.Errorf("failed to create channel in database: %w", err)
	}
	return insertedId, nil
}

func (s *PostgresChannelRepository) DeleteChannel(ctx context.Context, channelName string, appId string) error {
	pgAppID := ToPgUUID(appId)
	commandTag, err := s.engine.Queries.DeleteChannelByName(ctx, pgdb.DeleteChannelByNameParams{
		AppID: pgAppID,
		Name:  channelName,
	})
	if err != nil {
		return fmt.Errorf("failed to delete channel from database: %w", err)
	}
	if commandTag.RowsAffected() == 0 {
		return &ErrResourceNotFound{Resource: "channel", Identifier: fmt.Sprintf("%s (appId: %s)", channelName, appId)}
	}
	return nil
}

func (s *PostgresChannelRepository) GetChannelNameByBranchName(ctx context.Context, appId string, branchName string) ([]string, error) {
	pgAppID := ToPgUUID(appId)
	return s.engine.Queries.GetChannelNamesByBranchName(ctx, pgdb.GetChannelNamesByBranchNameParams{
		Name:  branchName,
		AppID: pgAppID,
	})
}

func (s *PostgresChannelRepository) GetChannels(ctx context.Context, appId string) ([]types.ChannelMapping, error) {
	pgAppID := ToPgUUID(appId)
	appChannels, err := s.engine.Queries.GetChannelsByAppID(ctx, pgAppID)
	if err != nil {
		return nil, fmt.Errorf("failed to retrieve channels from database: %w", err)
	}
	channels := make([]types.ChannelMapping, len(appChannels))
	for i, channel := range appChannels {
		var branchIdPtr *string
		if channel.BranchID != nil {
			branchIdStr := strconv.FormatInt(*channel.BranchID, 10)
			branchIdPtr = &branchIdStr
		}
		var createdAtStr *string
		if channel.CreatedAt.Valid {
			timeStr := channel.CreatedAt.Time.Format(time.RFC3339)
			createdAtStr = &timeStr
		}
		mapping := types.ChannelMapping{
			ReleaseChannelName: channel.Name,
			ReleaseChannelId:   strconv.FormatInt(channel.ID, 10),
			BranchName:         channel.BranchName,
			BranchId:           branchIdPtr,
			CreatedAt:          createdAtStr,
			BranchCurrentUpdate: branchUpdateState(
				channel.BranchCurrentRuntimeVersion,
				channel.BranchCurrentCommitHash,
				channel.BranchCurrentUpdateCreatedAt,
				channel.BranchCurrentRolloutPercentage,
			),
			RolloutBranchCurrentUpdate: branchUpdateState(
				channel.RolloutBranchCurrentRuntimeVersion,
				channel.RolloutBranchCurrentCommitHash,
				channel.RolloutBranchCurrentUpdateCreatedAt,
				channel.RolloutBranchCurrentRolloutPercentage,
			),
			BranchSurfing: &types.BranchSurfing{
				Enabled: channel.BranchSurfingEnabled,
				Pattern: channel.BranchSurfingPattern,
			},
		}
		if channel.RolloutID.Valid && channel.BranchName != nil && channel.RolloutBranchName != nil && channel.RolloutPercentage != nil {
			mapping.Rollout = &types.ChannelRollout{
				ID:                channel.RolloutID.String(),
				ChannelName:       channel.Name,
				DefaultBranchName: *channel.BranchName,
				RolloutBranchName: *channel.RolloutBranchName,
				Percentage:        int(*channel.RolloutPercentage),
				CreatedAt:         channel.RolloutCreatedAt.Time.Format(time.RFC3339),
				UpdatedAt:         channel.RolloutUpdatedAt.Time.Format(time.RFC3339),
			}
		}
		channels[i] = mapping
	}
	return channels, nil
}

func (s *PostgresChannelRepository) GetBranchSurfing(ctx context.Context, appId string, channelName string) (*types.BranchSurfing, error) {
	pgAppID := ToPgUUID(appId)
	row, err := s.engine.Queries.GetChannelBranchSurfing(ctx, pgdb.GetChannelBranchSurfingParams{
		AppID: pgAppID,
		Name:  channelName,
	})
	if err != nil {
		// An unknown channel is a 404 for the caller, not a server error; match
		// GetChannelBranchMapping's (nil, nil).
		if errors.Is(err, pgx.ErrNoRows) {
			return nil, nil
		}
		return nil, fmt.Errorf("failed to retrieve channel branch surfing from database: %w", err)
	}
	return &types.BranchSurfing{
		Enabled: row.BranchSurfingEnabled,
		Pattern: row.BranchSurfingPattern,
	}, nil
}

func (s *PostgresChannelRepository) SetBranchSurfing(ctx context.Context, appId string, channelName string, surfing types.BranchSurfing) error {
	pgAppID := ToPgUUID(appId)
	commandTag, err := s.engine.Queries.UpdateChannelBranchSurfing(ctx, pgdb.UpdateChannelBranchSurfingParams{
		AppID:                pgAppID,
		Name:                 channelName,
		BranchSurfingEnabled: surfing.Enabled,
		BranchSurfingPattern: surfing.Pattern,
	})
	if err != nil {
		return fmt.Errorf("failed to update channel branch surfing in database: %w", err)
	}
	if commandTag.RowsAffected() == 0 {
		return &ErrResourceNotFound{Resource: "channel", Identifier: fmt.Sprintf("%s (appId: %s)", channelName, appId)}
	}
	return nil
}

func (s *PostgresChannelRepository) GetUpdatesByRunTimeVersionAndBranchName(ctx context.Context, appId string, runtimeVersion string, branchName string) ([]pgdb.GetUpdatesByByBranchNameAndRuntimeVersionRow, error) {
	pgAppID := ToPgUUID(appId)
	return s.engine.Queries.GetUpdatesByByBranchNameAndRuntimeVersion(ctx, pgdb.GetUpdatesByByBranchNameAndRuntimeVersionParams{
		ID:      pgAppID,
		Version: runtimeVersion,
		Name:    branchName,
	})
}

func (s *PostgresChannelRepository) GetChannelBranchMapping(ctx context.Context, appId string, channelName string) (*types.ChannelResolution, error) {
	pgAppID := ToPgUUID(appId)
	mapping, err := s.engine.Queries.GetChannelBranchMapping(ctx, pgdb.GetChannelBranchMappingParams{
		AppID: pgAppID,
		Name:  channelName,
	})
	if err != nil {
		// An unknown channel, or one left unmapped, is a 404 for the caller, not a
		// server error; match the bucket backend's (nil, nil) so ResolveUpdateForDevice's
		// nil-check works in DB mode too.
		if errors.Is(err, pgx.ErrNoRows) {
			return nil, nil
		}
		return nil, fmt.Errorf("failed to retrieve channel mapping from database: %w", err)
	}
	mappingStr := strconv.FormatInt(mapping.ID, 10)
	result := &types.ChannelResolution{
		Id:         mappingStr,
		BranchName: mapping.BranchName,
	}
	if mapping.RolloutID.Valid && mapping.RolloutBranchName != nil && mapping.RolloutPercentage != nil {
		result.Rollout = &types.ChannelRolloutInfo{
			ID:         mapping.RolloutID.String(),
			BranchName: *mapping.RolloutBranchName,
			Percentage: int(*mapping.RolloutPercentage),
		}
	}
	return result, nil
}