| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- package gorm
- import (
- "context"
- "encoding/json"
- "errors"
- "strconv"
- "time"
- "github.com/porter-dev/porter/internal/telemetry"
- "github.com/google/uuid"
- "github.com/porter-dev/porter/internal/models"
- "github.com/porter-dev/porter/internal/repository"
- "github.com/porter-dev/porter/internal/repository/gorm/helpers"
- "gorm.io/gorm"
- )
- // PorterAppEventRepository uses gorm.DB for querying the database
- type PorterAppEventRepository struct {
- db *gorm.DB
- }
- // NewPorterAppEventRepository returns a PorterAppEventRepository which uses
- // gorm.DB for querying the database
- func NewPorterAppEventRepository(db *gorm.DB) repository.PorterAppEventRepository {
- return &PorterAppEventRepository{db}
- }
- func (repo *PorterAppEventRepository) ListEventsByPorterAppID(ctx context.Context, porterAppID uint, opts ...helpers.QueryOption) ([]*models.PorterAppEvent, helpers.PaginatedResult, error) {
- apps := []*models.PorterAppEvent{}
- paginatedResult := helpers.PaginatedResult{}
- id := strconv.Itoa(int(porterAppID))
- if id == "" {
- return nil, paginatedResult, errors.New("invalid porter app id supplied")
- }
- db := repo.db.Model(&models.PorterAppEvent{})
- resultDB := db.Where("porter_app_id = ?", id).Order("created_at DESC")
- resultDB = resultDB.Scopes(helpers.Paginate(db, &paginatedResult, opts...))
- if err := resultDB.Find(&apps).Error; err != nil {
- if !errors.Is(err, gorm.ErrRecordNotFound) {
- return nil, paginatedResult, err
- }
- }
- return apps, paginatedResult, nil
- }
- // ListEventsByPorterAppIDAndDeploymentTargetID returns a list of events for a given porter app id and deployment target id
- func (repo *PorterAppEventRepository) ListEventsByPorterAppIDAndDeploymentTargetID(ctx context.Context, porterAppID uint, deploymentTargetID uuid.UUID, opts ...helpers.QueryOption) ([]*models.PorterAppEvent, helpers.PaginatedResult, error) {
- ctx, span := telemetry.NewSpan(ctx, "list-events-by-porter-app-id-and-deployment-target-id")
- defer span.End()
- telemetry.WithAttributes(span,
- telemetry.AttributeKV{Key: "porter-app-id", Value: porterAppID},
- telemetry.AttributeKV{Key: "deployment-target-id", Value: deploymentTargetID},
- )
- apps := []*models.PorterAppEvent{}
- paginatedResult := helpers.PaginatedResult{}
- id := strconv.Itoa(int(porterAppID))
- if id == "" {
- return nil, paginatedResult, telemetry.Error(ctx, span, nil, "invalid porter app id supplied")
- }
- db := repo.db.Model(&models.PorterAppEvent{})
- resultDB := db.Where("porter_app_id = ? AND deployment_target_id = ?", id, deploymentTargetID).Order("created_at DESC")
- resultDB = resultDB.Scopes(helpers.Paginate(db, &paginatedResult, opts...))
- if err := resultDB.Find(&apps).Error; err != nil {
- if !errors.Is(err, gorm.ErrRecordNotFound) {
- return nil, paginatedResult, telemetry.Error(ctx, span, err, "error finding events by porter app id and deployment target id")
- }
- }
- return apps, paginatedResult, nil
- }
- // ListBuildDeployEventsByPorterAppIDAndDeploymentTargetID returns a list of events for a given porter app id and deployment target id, withholding notification and app_event type events
- // This is used to display on build, pre-deploy and deploy events in the activity feed
- // TODO: remove this once notifications are stored in a separate table
- func (repo *PorterAppEventRepository) ListBuildDeployEventsByPorterAppIDAndDeploymentTargetID(ctx context.Context, porterAppID uint, deploymentTargetID uuid.UUID, opts ...helpers.QueryOption) ([]*models.PorterAppEvent, helpers.PaginatedResult, error) {
- ctx, span := telemetry.NewSpan(ctx, "list-build-deploy-events-by-porter-app-id-and-deployment-target-id")
- defer span.End()
- telemetry.WithAttributes(span,
- telemetry.AttributeKV{Key: "porter-app-id", Value: porterAppID},
- telemetry.AttributeKV{Key: "deployment-target-id", Value: deploymentTargetID},
- )
- apps := []*models.PorterAppEvent{}
- paginatedResult := helpers.PaginatedResult{}
- id := strconv.Itoa(int(porterAppID))
- if id == "" {
- return nil, paginatedResult, telemetry.Error(ctx, span, nil, "invalid porter app id supplied")
- }
- db := repo.db.Model(&models.PorterAppEvent{})
- resultDB := db.Where("porter_app_id = ? AND deployment_target_id = ? AND type != 'APP_EVENT' AND type != 'NOTIFICATION'", id, deploymentTargetID).Order("created_at DESC")
- resultDB = resultDB.Scopes(helpers.Paginate(db, &paginatedResult, opts...))
- if err := resultDB.Find(&apps).Error; err != nil {
- if !errors.Is(err, gorm.ErrRecordNotFound) {
- return nil, paginatedResult, telemetry.Error(ctx, span, err, "error finding events by porter app id and deployment target id")
- }
- }
- return apps, paginatedResult, nil
- }
- func (repo *PorterAppEventRepository) CreateEvent(ctx context.Context, appEvent *models.PorterAppEvent) error {
- if appEvent.ID == uuid.Nil {
- appEvent.ID = uuid.New()
- }
- if appEvent.CreatedAt.IsZero() {
- appEvent.CreatedAt = time.Now().UTC()
- }
- if appEvent.UpdatedAt.IsZero() {
- appEvent.UpdatedAt = time.Now().UTC()
- }
- if appEvent.PorterAppID == 0 {
- return errors.New("invalid porter app id supplied to create event")
- }
- if err := repo.db.Create(appEvent).Error; err != nil {
- return err
- }
- return nil
- }
- // UpdateEvent will set all values in the database to the values of the passed in appEvent
- func (repo *PorterAppEventRepository) UpdateEvent(ctx context.Context, appEvent *models.PorterAppEvent) error {
- if appEvent.PorterAppID == 0 {
- return errors.New("invalid porter app id supplied to update event")
- }
- if appEvent.ID == uuid.Nil {
- return errors.New("invalid porter app event id supplied to update event")
- }
- if appEvent.UpdatedAt.IsZero() {
- appEvent.UpdatedAt = time.Now().UTC()
- }
- if err := repo.db.Model(appEvent).Updates(appEvent).Error; err != nil {
- return err
- }
- return nil
- }
- func (repo *PorterAppEventRepository) ReadEvent(ctx context.Context, id uuid.UUID) (models.PorterAppEvent, error) {
- appEvent := models.PorterAppEvent{}
- if id == uuid.Nil {
- return appEvent, errors.New("invalid porter app event id supplied")
- }
- strID := id.String()
- if err := repo.db.Where("id = ?", strID).First(&appEvent).Error; err != nil {
- return appEvent, err
- }
- return appEvent, nil
- }
- // ReadNotificationsByAppRevisionID returns a list of notifications for a given porter app instance id and app revision ID
- func (repo *PorterAppEventRepository) ReadNotificationsByAppRevisionID(ctx context.Context, porterAppInstanceId uuid.UUID, appRevisionId string) ([]*models.PorterAppEvent, error) {
- notifications := []*models.PorterAppEvent{}
- if appRevisionId == "" {
- return notifications, errors.New("invalid app revision ID supplied")
- }
- if porterAppInstanceId == uuid.Nil {
- return notifications, errors.New("invalid porter app instance ID supplied")
- }
- // TODO: make app_revision_id a column in porter_app_event table: https://linear.app/porter/issue/POR-2096/add-app-revision-id-column-to-porter-app-events-table
- if err := repo.db.Where("app_instance_id = ? AND type = 'NOTIFICATION' AND metadata->>'app_revision_id' = ?", porterAppInstanceId, appRevisionId).Find(¬ifications).Error; err != nil {
- return notifications, err
- }
- return notifications, nil
- }
- // NotificationByID returns a notification by the notification id
- func (repo *PorterAppEventRepository) NotificationByID(ctx context.Context, notificationID string) (*models.PorterAppEvent, error) {
- notification := &models.PorterAppEvent{}
- // TODO: make app_revision_id a column in porter_app_event table: https://linear.app/porter/issue/POR-2096/add-app-revision-id-column-to-porter-app-events-table
- if err := repo.db.Where("type = 'NOTIFICATION' AND metadata->>'id' = ?", notificationID).Find(¬ification).Error; err != nil {
- return notification, err
- }
- return notification, nil
- }
- func (repo *PorterAppEventRepository) ReadDeployEventByRevision(ctx context.Context, porterAppID uint, revision float64) (models.PorterAppEvent, error) {
- appEvent := models.PorterAppEvent{}
- if porterAppID == 0 {
- return appEvent, errors.New("invalid porter app ID supplied")
- }
- // Convert porterAppID to string
- strAppID := strconv.Itoa(int(porterAppID))
- // Convert revision to JSON number string
- revJSON, err := json.Marshal(revision)
- if err != nil {
- return appEvent, errors.New("unable to marshal revision")
- }
- strRevision := string(revJSON)
- if err := repo.db.Where("porter_app_id = ? AND type = 'DEPLOY' AND metadata->>'revision' = ?", strAppID, strRevision).First(&appEvent).Error; err != nil {
- return appEvent, err
- }
- return appEvent, nil
- }
- // ReadDeployEventByAppRevisionID returns a deploy event for a given porter app id and app revision ID
- func (repo *PorterAppEventRepository) ReadDeployEventByAppRevisionID(ctx context.Context, porterAppID uint, appRevisionID string) (models.PorterAppEvent, error) {
- appEvent := models.PorterAppEvent{}
- if porterAppID == 0 {
- return appEvent, errors.New("invalid porter app ID supplied")
- }
- if appRevisionID == "" {
- return appEvent, errors.New("no app revision ID supplied")
- }
- // Convert porterAppID to string
- strAppID := strconv.Itoa(int(porterAppID))
- if err := repo.db.Where("porter_app_id = ? AND type = 'DEPLOY' AND metadata->>'app_revision_id' = ?", strAppID, appRevisionID).First(&appEvent).Error; err != nil {
- return appEvent, err
- }
- return appEvent, nil
- }
|