Refacto framework exports

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-09-04 20:03:54 +02:00
parent 055c860a24
commit 7000ccc246

View File

@@ -192,15 +192,38 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
} }
func (s *Service) ExportFrameworkJob(ctx context.Context) error { func (s *Service) ExportFrameworkJob(ctx context.Context) error {
return s.pg.WithTx( fe, scope, err := s.lockExport(ctx)
ctx, if err != nil {
return fmt.Errorf("cannot lock framework export: %w", err)
}
fe, buildErr := s.buildAndUploadExport(ctx, scope, fe)
if buildErr != nil {
if err := s.commitFailedExport(ctx, scope, fe); err != nil {
return fmt.Errorf(
"cannot build and upload framework export: %w, and cannot commit failed export: %w",
buildErr,
err,
)
}
return fmt.Errorf("cannot build and upload framework export: %w", buildErr)
}
return nil
}
func (s *Service) lockExport(ctx context.Context) (*coredata.FrameworkExport, coredata.Scoper, error) {
fe := &coredata.FrameworkExport{}
var scope coredata.Scoper
err := s.pg.WithTx(ctx,
func(tx pg.Conn) error { func(tx pg.Conn) error {
fe := &coredata.FrameworkExport{}
if err := fe.LoadNextPendingForUpdateSkipLocked(ctx, tx); err != nil { if err := fe.LoadNextPendingForUpdateSkipLocked(ctx, tx); err != nil {
return err return fmt.Errorf("cannot load next pending framework export: %w", err)
} }
scope := coredata.NewScope(fe.ID.TenantID()) scope = coredata.NewScope(fe.ID.TenantID())
fe.Status = coredata.FrameworkExportStatusProcessing fe.Status = coredata.FrameworkExportStatusProcessing
fe.StartedAt = ref.Ref(time.Now()) fe.StartedAt = ref.Ref(time.Now())
@@ -208,73 +231,50 @@ func (s *Service) ExportFrameworkJob(ctx context.Context) error {
return fmt.Errorf("cannot update framework export: %w", err) return fmt.Errorf("cannot update framework export: %w", err)
} }
return nil
},
)
if err != nil {
return nil, nil, fmt.Errorf("cannot lock framework export: %w", err)
}
return fe, scope, nil
}
func (s *Service) buildAndUploadExport(ctx context.Context, scope coredata.Scoper, fe *coredata.FrameworkExport) (*coredata.FrameworkExport, error) {
err := s.pg.WithTx(
ctx,
func(tx pg.Conn) error {
framework := &coredata.Framework{} framework := &coredata.Framework{}
if err := framework.LoadByID(ctx, tx, scope, fe.FrameworkID); err != nil { if err := framework.LoadByID(ctx, tx, scope, fe.FrameworkID); err != nil {
fe.Status = coredata.FrameworkExportStatusFailed
fe.CompletedAt = ref.Ref(time.Now())
if err := fe.Update(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot update framework export: %w", err)
}
return fmt.Errorf("cannot load framework: %w", err) return fmt.Errorf("cannot load framework: %w", err)
} }
tenantService := s.WithTenant(fe.ID.TenantID())
tempDir := os.TempDir() tempDir := os.TempDir()
tempFile, err := os.CreateTemp(tempDir, "probo-framework-export-*.zip") tempFile, err := os.CreateTemp(tempDir, "probo-framework-export-*.zip")
if err != nil { if err != nil {
fe.Status = coredata.FrameworkExportStatusFailed
fe.CompletedAt = ref.Ref(time.Now())
if err := fe.Update(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot update framework export: %w", err)
}
return fmt.Errorf("cannot create temp file: %w", err) return fmt.Errorf("cannot create temp file: %w", err)
} }
defer tempFile.Close() defer tempFile.Close()
defer os.Remove(tempFile.Name()) defer os.Remove(tempFile.Name())
tenantService := s.WithTenant(scope.GetTenantID())
err = tenantService.Frameworks.Export(ctx, fe.FrameworkID, tempFile) err = tenantService.Frameworks.Export(ctx, fe.FrameworkID, tempFile)
if err != nil { if err != nil {
fe.Status = coredata.FrameworkExportStatusFailed
fe.CompletedAt = ref.Ref(time.Now())
if err := fe.Update(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot update framework export: %w", err)
}
return fmt.Errorf("cannot export framework: %w", err) return fmt.Errorf("cannot export framework: %w", err)
} }
uuid, err := uuid.NewV4() uuid, err := uuid.NewV4()
if err != nil { if err != nil {
fe.Status = coredata.FrameworkExportStatusFailed return fmt.Errorf("cannot generate uuid: %w", err)
fe.CompletedAt = ref.Ref(time.Now())
if err := fe.Update(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot update framework export: %w", err)
}
return fmt.Errorf("cannot update framework export: %w", err)
} }
if _, err := tempFile.Seek(0, 0); err != nil { if _, err := tempFile.Seek(0, 0); err != nil {
fe.Status = coredata.FrameworkExportStatusFailed
fe.CompletedAt = ref.Ref(time.Now())
if err := fe.Update(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot update framework export: %w", err)
}
return fmt.Errorf("cannot seek temp file: %w", err) return fmt.Errorf("cannot seek temp file: %w", err)
} }
fileInfo, err := tempFile.Stat() fileInfo, err := tempFile.Stat()
if err != nil { if err != nil {
fe.Status = coredata.FrameworkExportStatusFailed
fe.CompletedAt = ref.Ref(time.Now())
if err := fe.Update(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot update framework export: %w", err)
}
return fmt.Errorf("cannot stat temp file: %w", err) return fmt.Errorf("cannot stat temp file: %w", err)
} }
@@ -293,12 +293,6 @@ func (s *Service) ExportFrameworkJob(ctx context.Context) error {
}, },
) )
if err != nil { if err != nil {
fe.Status = coredata.FrameworkExportStatusFailed
fe.CompletedAt = ref.Ref(time.Now())
if err := fe.Update(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot update framework export: %w", err)
}
return fmt.Errorf("cannot upload file to S3: %w", err) return fmt.Errorf("cannot upload file to S3: %w", err)
} }
@@ -308,7 +302,7 @@ func (s *Service) ExportFrameworkJob(ctx context.Context) error {
ID: gid.New(fe.ID.TenantID(), coredata.FileEntityType), ID: gid.New(fe.ID.TenantID(), coredata.FileEntityType),
BucketName: s.bucket, BucketName: s.bucket,
MimeType: "application/zip", MimeType: "application/zip",
FileName: fmt.Sprintf("%s Archive %s.zip", framework.Name, time.Now().Format("2006-01-02")), FileName: fmt.Sprintf("%s Archive %s.zip", framework.Name, now.Format("2006-01-02")),
FileKey: uuid.String(), FileKey: uuid.String(),
FileSize: int(fileInfo.Size()), FileSize: int(fileInfo.Size()),
CreatedAt: now, CreatedAt: now,
@@ -320,7 +314,7 @@ func (s *Service) ExportFrameworkJob(ctx context.Context) error {
} }
fe.FileID = &file.ID fe.FileID = &file.ID
fe.CompletedAt = &now fe.CompletedAt = ref.Ref(time.Now())
fe.Status = coredata.FrameworkExportStatusCompleted fe.Status = coredata.FrameworkExportStatusCompleted
if err := fe.Update(ctx, tx, scope); err != nil { if err := fe.Update(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot update framework export: %w", err) return fmt.Errorf("cannot update framework export: %w", err)
@@ -329,4 +323,26 @@ func (s *Service) ExportFrameworkJob(ctx context.Context) error {
return nil return nil
}, },
) )
if err != nil {
return fe, fmt.Errorf("cannot build and upload export: %w", err)
}
return fe, nil
}
func (s *Service) commitFailedExport(ctx context.Context, scope coredata.Scoper, fe *coredata.FrameworkExport) error {
fe.CompletedAt = ref.Ref(time.Now())
fe.Status = coredata.FrameworkExportStatusFailed
return s.pg.WithConn(
ctx,
func(conn pg.Conn) error {
if err := fe.Update(ctx, conn, scope); err != nil {
return fmt.Errorf("cannot update framework export: %w", err)
}
return nil
},
)
} }