diff --git a/frontend/cloud-ui/src/app/deployment-targets/deployment-targets.component.html b/frontend/cloud-ui/src/app/deployment-targets/deployment-targets.component.html index ea5bd1d0..c6162c5e 100644 --- a/frontend/cloud-ui/src/app/deployment-targets/deployment-targets.component.html +++ b/frontend/cloud-ui/src/app/deployment-targets/deployment-targets.component.html @@ -72,6 +72,7 @@ --> Deployment Target + Status Type @if (fullVersion) { Creation Date @@ -97,10 +98,28 @@ -->
- Docker +
+ Docker + @if (dt.currentStatus) { + + } @else { + + } +
{{ dt.name }}
+ +
+ @if (dt.currentStatus) { +
+ OK + } @else { +
+ Unknown + } +
+ diff --git a/frontend/cloud-ui/src/app/types/deployment-target.ts b/frontend/cloud-ui/src/app/types/deployment-target.ts index 37ccd912..117d5923 100644 --- a/frontend/cloud-ui/src/app/types/deployment-target.ts +++ b/frontend/cloud-ui/src/app/types/deployment-target.ts @@ -5,4 +5,9 @@ export interface DeploymentTarget extends BaseModel, Named { name: string; type: string; geolocation?: Geolocation; + currentStatus?: DeploymentTargetStatus; +} + +export interface DeploymentTargetStatus extends BaseModel { + message: string; } diff --git a/internal/db/deployment_targets.go b/internal/db/deployment_targets.go index 95a17d91..f0e10bf9 100644 --- a/internal/db/deployment_targets.go +++ b/internal/db/deployment_targets.go @@ -14,15 +14,28 @@ import ( const ( deploymentTargetOutputExpr = ` - id, created_at, name, type, - CASE WHEN geolocation_lat IS NOT NULL AND geolocation_lon IS NOT NULL - THEN (geolocation_lat, geolocation_lon) END AS geolocation + dt.id, dt.created_at, dt.name, dt.type, + CASE WHEN dt.geolocation_lat IS NOT NULL AND dt.geolocation_lon IS NOT NULL + THEN (dt.geolocation_lat, dt.geolocation_lon) END AS geolocation ` + deploymentTargetWithStatusOutputExpr = deploymentTargetOutputExpr + `, + CASE WHEN status.id IS NOT NULL + THEN (status.id, status.created_at, status.message) END AS current_status + ` + deploymentTargetFromExpr = ` + FROM DeploymentTarget dt LEFT JOIN DeploymentTargetStatus status ON dt.id = status.deployment_target_id + WHERE ( + status.id IS NULL OR status.created_at = ( + SELECT max(s.created_at) FROM DeploymentTargetStatus s WHERE s.deployment_target_id = status.deployment_target_id + ) + ) +` ) func GetDeploymentTargets(ctx context.Context) ([]types.DeploymentTarget, error) { db := internalctx.GetDb(ctx) - if rows, err := db.Query(ctx, "SELECT "+deploymentTargetOutputExpr+" FROM DeploymentTarget"); err != nil { + if rows, err := db.Query(ctx, + "SELECT "+deploymentTargetWithStatusOutputExpr+" "+deploymentTargetFromExpr); err != nil { return nil, fmt.Errorf("failed to query DeploymentTargets: %w", err) } else if result, err := pgx.CollectRows(rows, pgx.RowToStructByName[types.DeploymentTarget]); err != nil { return nil, fmt.Errorf("failed to get DeploymentTargets: %w", err) @@ -34,7 +47,7 @@ func GetDeploymentTargets(ctx context.Context) ([]types.DeploymentTarget, error) func GetDeploymentTarget(ctx context.Context, id string) (*types.DeploymentTarget, error) { db := internalctx.GetDb(ctx) rows, err := db.Query(ctx, - "SELECT "+deploymentTargetOutputExpr+" FROM DeploymentTarget WHERE id = @id", + "SELECT "+deploymentTargetWithStatusOutputExpr+" "+deploymentTargetFromExpr+" AND dt.id = @id", pgx.NamedArgs{"id": id}) if err != nil { return nil, fmt.Errorf("failed to query DeploymentTargets: %w", err) @@ -56,14 +69,14 @@ func CreateDeploymentTarget(ctx context.Context, dt *types.DeploymentTarget) err maps.Copy(args, pgx.NamedArgs{"lat": dt.Geolocation.Lat, "lon": dt.Geolocation.Lon}) } rows, err := db.Query(ctx, - "INSERT INTO DeploymentTarget (name, type, geolocation_lat, geolocation_lon) "+ + "INSERT INTO DeploymentTarget AS dt (name, type, geolocation_lat, geolocation_lon) "+ "VALUES (@name, @type, @lat, @lon) RETURNING "+ deploymentTargetOutputExpr, args) if err != nil { return fmt.Errorf("failed to query DeploymentTargets: %w", err) } - result, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[types.DeploymentTarget]) + result, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByNameLax[types.DeploymentTarget]) if err != nil { return fmt.Errorf("could not save DeploymentTarget: %w", err) } else { @@ -79,13 +92,14 @@ func UpdateDeploymentTarget(ctx context.Context, dt *types.DeploymentTarget) err maps.Copy(args, pgx.NamedArgs{"lat": dt.Geolocation.Lat, "lon": dt.Geolocation.Lon}) } rows, err := db.Query(ctx, - "UPDATE DeploymentTarget SET name = @name, geolocation_lat = @lat, geolocation_lon = @lon "+ + "UPDATE DeploymentTarget AS dt SET name = @name, geolocation_lat = @lat, geolocation_lon = @lon "+ " WHERE id = @id RETURNING "+ deploymentTargetOutputExpr, args) if err != nil { return fmt.Errorf("could not update DeploymentTarget: %w", err) - } else if updated, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[types.DeploymentTarget]); err != nil { + } else if updated, err := + pgx.CollectExactlyOneRow(rows, pgx.RowToStructByNameLax[types.DeploymentTarget]); err != nil { return fmt.Errorf("could not get updated DeploymentTarget: %w", err) } else { *dt = updated diff --git a/internal/handlers/deployment_targets.go b/internal/handlers/deployment_targets.go index bd2632fc..4fb2ada9 100644 --- a/internal/handlers/deployment_targets.go +++ b/internal/handlers/deployment_targets.go @@ -95,7 +95,7 @@ func deploymentTargetMiddelware(wh http.Handler) http.Handler { if errors.Is(err, apierrors.NotFound) { w.WriteHeader(http.StatusNotFound) } else if err != nil { - internalctx.GetLogger(r.Context()).Error("failed to get application", zap.Error(err)) + internalctx.GetLogger(r.Context()).Error("failed to get DeploymentTarget", zap.Error(err)) w.WriteHeader(http.StatusInternalServerError) } else { ctx = internalctx.WithDeploymentTarget(ctx, deploymentTarget) diff --git a/internal/types/data.go b/internal/types/data.go index 8e4e5099..b3ccab88 100644 --- a/internal/types/data.go +++ b/internal/types/data.go @@ -10,7 +10,7 @@ type Application struct { } type ApplicationVersion struct { - // unfortunately Base nested type doesn't work when ApplicationVersion is a nested row in an SQL query + // TODO unfortunately Base nested type doesn't work when ApplicationVersion is a nested row in an SQL query ID string `db:"id" json:"id"` CreatedAt time.Time `db:"created_at" json:"createdAt"` Name string `db:"name" json:"name"` @@ -20,7 +20,16 @@ type ApplicationVersion struct { type DeploymentTarget struct { Base - Name string `db:"name" json:"name"` - Type DeploymentType `db:"type" json:"type"` - Geolocation *Geolocation `db:"geolocation" json:"geolocation,omitempty"` + Name string `db:"name" json:"name"` + Type DeploymentType `db:"type" json:"type"` + Geolocation *Geolocation `db:"geolocation" json:"geolocation,omitempty"` + CurrentStatus *DeploymentTargetStatus `db:"current_status" json:"currentStatus,omitempty"` +} + +type DeploymentTargetStatus struct { + // TODO unfortunately Base nested type doesn't work when ApplicationVersion is a nested row in an SQL query + ID string `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"createdAt"` + Message string `db:"message" json:"message"` + DeploymentTargetId string `db:"deployment_target_id" json:"-"` }