| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- package service
- import (
- "context"
- "net/http"
- "strings"
- "cmr-backend/internal/apperr"
- "cmr-backend/internal/store/postgres"
- )
- type AdminPipelineService struct {
- store *postgres.Store
- configService *ConfigService
- }
- type AdminReleaseView struct {
- ID string `json:"id"`
- ReleaseNo int `json:"releaseNo"`
- ConfigLabel string `json:"configLabel"`
- ManifestURL string `json:"manifestUrl"`
- ManifestChecksumSha256 *string `json:"manifestChecksumSha256,omitempty"`
- RouteCode *string `json:"routeCode,omitempty"`
- BuildID *string `json:"buildId,omitempty"`
- Status string `json:"status"`
- PublishedAt string `json:"publishedAt"`
- }
- type AdminEventPipelineView struct {
- EventID string `json:"eventId"`
- CurrentRelease *AdminReleaseView `json:"currentRelease,omitempty"`
- Sources []EventConfigSourceView `json:"sources"`
- Builds []EventConfigBuildView `json:"builds"`
- Releases []AdminReleaseView `json:"releases"`
- }
- type AdminRollbackReleaseInput struct {
- ReleaseID string `json:"releaseId"`
- }
- func NewAdminPipelineService(store *postgres.Store, configService *ConfigService) *AdminPipelineService {
- return &AdminPipelineService{
- store: store,
- configService: configService,
- }
- }
- func (s *AdminPipelineService) GetEventPipeline(ctx context.Context, eventPublicID string, limit int) (*AdminEventPipelineView, error) {
- event, err := s.store.GetEventByPublicID(ctx, strings.TrimSpace(eventPublicID))
- if err != nil {
- return nil, err
- }
- if event == nil {
- return nil, apperr.New(http.StatusNotFound, "event_not_found", "event not found")
- }
- sources, err := s.configService.ListEventConfigSources(ctx, event.PublicID, limit)
- if err != nil {
- return nil, err
- }
- buildRecords, err := s.store.ListEventConfigBuildsByEventID(ctx, event.ID, limit)
- if err != nil {
- return nil, err
- }
- releaseRecords, err := s.store.ListEventReleasesByEventID(ctx, event.ID, limit)
- if err != nil {
- return nil, err
- }
- builds := make([]EventConfigBuildView, 0, len(buildRecords))
- for i := range buildRecords {
- item, err := buildEventConfigBuildView(&buildRecords[i])
- if err != nil {
- return nil, err
- }
- builds = append(builds, *item)
- }
- releases := make([]AdminReleaseView, 0, len(releaseRecords))
- for _, item := range releaseRecords {
- releases = append(releases, buildAdminReleaseView(item))
- }
- result := &AdminEventPipelineView{
- EventID: event.PublicID,
- Sources: sources,
- Builds: builds,
- Releases: releases,
- }
- if event.CurrentReleasePubID != nil {
- result.CurrentRelease = &AdminReleaseView{
- ID: *event.CurrentReleasePubID,
- ConfigLabel: derefStringOrEmpty(event.ConfigLabel),
- ManifestURL: derefStringOrEmpty(event.ManifestURL),
- ManifestChecksumSha256: event.ManifestChecksum,
- RouteCode: event.RouteCode,
- Status: "published",
- }
- }
- return result, nil
- }
- func (s *AdminPipelineService) BuildSource(ctx context.Context, sourceID string) (*EventConfigBuildView, error) {
- return s.configService.BuildPreview(ctx, BuildPreviewInput{SourceID: sourceID})
- }
- func (s *AdminPipelineService) GetBuild(ctx context.Context, buildID string) (*EventConfigBuildView, error) {
- return s.configService.GetEventConfigBuild(ctx, buildID)
- }
- func (s *AdminPipelineService) PublishBuild(ctx context.Context, buildID string) (*PublishedReleaseView, error) {
- return s.configService.PublishBuild(ctx, PublishBuildInput{BuildID: buildID})
- }
- func (s *AdminPipelineService) RollbackRelease(ctx context.Context, eventPublicID string, input AdminRollbackReleaseInput) (*AdminReleaseView, error) {
- event, err := s.store.GetEventByPublicID(ctx, strings.TrimSpace(eventPublicID))
- if err != nil {
- return nil, err
- }
- if event == nil {
- return nil, apperr.New(http.StatusNotFound, "event_not_found", "event not found")
- }
- input.ReleaseID = strings.TrimSpace(input.ReleaseID)
- if input.ReleaseID == "" {
- return nil, apperr.New(http.StatusBadRequest, "invalid_params", "releaseId is required")
- }
- release, err := s.store.GetEventReleaseByPublicID(ctx, input.ReleaseID)
- if err != nil {
- return nil, err
- }
- if release == nil {
- return nil, apperr.New(http.StatusNotFound, "release_not_found", "release not found")
- }
- if release.EventID != event.ID {
- return nil, apperr.New(http.StatusConflict, "release_not_belong_to_event", "release does not belong to event")
- }
- if release.Status != "published" {
- return nil, apperr.New(http.StatusConflict, "release_not_publishable", "release is not published")
- }
- tx, err := s.store.Begin(ctx)
- if err != nil {
- return nil, err
- }
- defer tx.Rollback(ctx)
- if err := s.store.SetCurrentEventRelease(ctx, tx, event.ID, release.ID); err != nil {
- return nil, err
- }
- if err := tx.Commit(ctx); err != nil {
- return nil, err
- }
- view := buildAdminReleaseView(*release)
- return &view, nil
- }
- func buildAdminReleaseView(item postgres.EventRelease) AdminReleaseView {
- return AdminReleaseView{
- ID: item.PublicID,
- ReleaseNo: item.ReleaseNo,
- ConfigLabel: item.ConfigLabel,
- ManifestURL: item.ManifestURL,
- ManifestChecksumSha256: item.ManifestChecksum,
- RouteCode: item.RouteCode,
- BuildID: item.BuildID,
- Status: item.Status,
- PublishedAt: item.PublishedAt.Format(timeRFC3339),
- }
- }
- func derefStringOrEmpty(value *string) string {
- if value == nil {
- return ""
- }
- return *value
- }
|