Skip to content

Environment UI Indicator

Executive Summary

This feature provides clear visual indication of the current environment (Production, Staging, Preview) and displays comprehensive version information for all deployed services. It prevents confusion between test and production data while providing developers and users with essential debugging information.

Key Objectives:

  1. Make environment and database clearly visible to prevent data confusion
  2. Display all service versions (K8s and Lambda) with pre-release indicators
  3. Provide non-disruptive UI that maintains accurate production preview
  4. Show clear warnings when users dismiss environment indicators

Implementation Status

Phase Status Description
Phase 1: Backend Endpoint ✅ Complete API service /api/application/info endpoint implemented
Phase 2: Frontend Core ✅ Complete Config interfaces, env-mapping.yaml, Helm chart values updated
Phase 3: Components ✅ Complete Environment banner, indicator, and warning dialog created
Phase 4: Dialog Enhancement ✅ Complete VersionInfoDialog enhanced with environment info and PM service versions
Phase 5: Integration ✅ Complete Components integrated in app.component
Health Endpoint Enhancement ✅ Complete /health/live endpoints now include version info in JSON response

Recent Changes (2025-12-28):

  • Added VersionHealthCheck class to SyRF.WebHostConfig.Common for dual-purpose health endpoints
  • Updated SyrfHealthCheckExtensions to register version health check and output JSON with version data
  • Modified AddSyrfInstrumentationAndHealthChecks to include version health check automatically
  • All .NET services (API, PM, Quartz) now expose version info via /health/live
  • PM service added /api/application/info endpoint via ApplicationController.cs
  • Frontend VersionInfoDialog fetches version info from both API and PM services

Pending:

  • Deploy to staging for validation
  • Manual testing of all environments

Problem Statement

Current Issues

  1. No environment visibility: Users cannot easily tell if they're on staging, preview, or production
  2. Database confusion: No indication of which database is being used (especially important for PR previews with isolated databases)
  3. Version information incomplete: Current Version Info Dialog shows Web and API versions but:
  4. PM Service shows as "unavailable" (no pmOrigin configured)
  5. Quartz Service not shown
  6. S3 Notifier Lambda not shown
  7. Database name not shown
  8. No warning on non-production: Users may enter data thinking it's production

User Stories

  • As a developer, I need to know which database my PR preview is using so I don't confuse test data with production data
  • As a tester, I need to see all service versions to verify the correct build is deployed
  • As a support engineer, I need to quickly identify the environment and versions when debugging user issues
  • As a user, I need clear warning that I'm not on production to avoid entering real data in test environments

Solution Design

UX Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│                              UX STATE MACHINE                                │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  [App Load - Non-Production]                                                 │
│         │                                                                    │
│         ▼                                                                    │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │ ⚠️ STAGING │ Database: syrf_staging │ Click for details            [×] ││
│  └─────────────────────────────────────────────────────────────────────────┘│
│         │                                              │                     │
│         │ (click banner text)                          │ (click × dismiss)   │
│         ▼                                              ▼                     │
│  ┌──────────────────────┐                 ┌──────────────────────────────┐  │
│  │ VERSION INFO DIALOG  │                 │ ENVIRONMENT WARNING DIALOG   │  │
│  │ (full service info)  │                 │                              │  │
│  │                      │                 │ ⚠️ NON-PRODUCTION            │  │
│  │ See "Dialog Layout"  │                 │                              │  │
│  │ section below        │                 │ You are using STAGING.       │  │
│  │                      │                 │                              │  │
│  └──────────────────────┘                 │ • Data may be reset anytime  │  │
│                                           │ • Changes won't affect prod  │  │
│                                           │ • For testing purposes only  │  │
│                                           │                              │  │
│                                           │ Database: syrf_staging       │  │
│                                           │                              │  │
│                                           │ [Keep Banner] [Hide Banner]  │  │
│                                           └──────────────────────────────┘  │
│                                                    │              │          │
│                                      (Keep Banner) │              │ (Hide)   │
│                                                    │              │          │
│                           ┌────────────────────────┘              │          │
│                           │                                       ▼          │
│                           │              ┌───────────────────────────────┐  │
│                           │              │                     [STG ●]   │  │
│                           │              │                               │  │
│                           │              │  Discrete corner indicator:   │  │
│                           │              │  - Does NOT disrupt layout    │  │
│                           │              │  - Accurate production preview│  │
│                           │              │  - Click → Version Info Dialog│  │
│                           │              └───────────────────────────────┘  │
│                           │                          │                       │
│                           │                          │ (click indicator)     │
│                           ▼                          ▼                       │
│                    [Banner restored]          [Version Info Dialog]          │
│                                                                              │
│  ─────────────────────────────────────────────────────────────────────────  │
│  [App Load - Production]                                                     │
│         │                                                                    │
│         ▼                                                                    │
│  No banner shown. Footer version link opens Version Info Dialog.            │
│  Dialog shows "Production" badge with database: syrftest                    │
└─────────────────────────────────────────────────────────────────────────────┘

Version Info Dialog Layout

┌──────────────────────────────────────────────────────────────────────────────┐
│                        Version & Environment Info                             │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│  ENVIRONMENT                                                                  │
│  ────────────────────────────────────────────────────────────────────────    │
│  Environment    [STAGING]  (badge with environment-specific color)           │
│  Database       syrf_staging                                                  │
│  PR Number      N/A  (or "2234" for preview environments)                    │
│                                                                               │
│  SERVICE VERSIONS (Kubernetes)                                                │
│  ────────────────────────────────────────────────────────────────────────    │
│  Web Application      v5.0.1                                    ✓ Healthy    │
│  API Service          v8.21.0                                   ✓ Healthy    │
│  PM Service           v10.45.0-PullRequest2234.1               ✓ Pre-release│
│  Quartz Service       v0.5.2                                    ✓ Healthy    │
│                                                                               │
│  SERVICE VERSIONS (Lambda)                                                    │
│  ────────────────────────────────────────────────────────────────────────    │
│  S3 File Notifier     v1.2.0                                    ✓ Healthy    │
│                                                                               │
│  BUILD INFORMATION                                                            │
│  ────────────────────────────────────────────────────────────────────────    │
│  Monorepo SHA         abc1234def (global across all services)                │
│  Web Full Version     5.0.1-PullRequest2234.1+abc1234def                     │
│                                                                               │
│                                          [Copy All Info]  [Close]            │
└──────────────────────────────────────────────────────────────────────────────┘

Environment-Specific Styling

Environment Banner Color Badge Color Indicator Text
Production (not shown) Green #4caf50 (not shown)
Staging Orange #ff9800 Orange #ff9800 STG
Preview Blue #2196f3 Blue #2196f3 PR-{number}
Development Pink #e91e63 Pink #e91e63 DEV

Discrete Indicator Specification

The indicator shown when banner is dismissed must be:

  1. Non-disruptive: Does not affect page layout or content positioning
  2. Fixed position: Bottom-right corner, overlaying content
  3. Small footprint: Approximately 48x24 pixels
  4. Semi-transparent: 85% opacity, increases to 100% on hover
  5. Accessible: Click opens Version Info Dialog
.environment-indicator {
  position: fixed;
  bottom: 16px;
  right: 16px;
  z-index: 1000;
  padding: 4px 12px;
  border-radius: 12px;
  font-size: 11px;
  font-weight: 600;
  opacity: 0.85;
  cursor: pointer;
  transition: opacity 0.2s;
}
.environment-indicator:hover {
  opacity: 1;
}

Technical Architecture

Backend Changes

New Endpoint: GET /api/application/info

File: src/services/api/SyRF.API.Endpoint/Controllers/ApplicationController.cs

[AllowAnonymous]
[HttpGet("application/info")]
[ProducesResponseType(typeof(ApplicationInfoDto), 200)]
public ActionResult<ApplicationInfoDto> GetApplicationInfo()
{
    return Ok(new ApplicationInfoDto
    {
        Environment = _configuration["runtimeEnvironment"] ?? "Unknown",
        DatabaseName = _mongoSettings.DatabaseName,
        PrNumber = _syrfSettings.PrNumber,
        IsProduction = _configuration["runtimeEnvironment"]?.Equals("Production",
            StringComparison.OrdinalIgnoreCase) ?? false,
        ApiVersion = _gitVersion.SemVer,
        ApiFullVersion = _gitVersion.InformationalVersion,
        MonorepoSha = _gitVersion.Sha
    });
}

New DTO: src/services/api/SyRF.API.Endpoint/DTOs/ApplicationInfoDto.cs

public class ApplicationInfoDto
{
    public string Environment { get; set; } = string.Empty;
    public string DatabaseName { get; set; } = string.Empty;
    public string? PrNumber { get; set; }
    public bool IsProduction { get; set; }
    public string ApiVersion { get; set; } = string.Empty;
    public string ApiFullVersion { get; set; } = string.Empty;
    public string MonorepoSha { get; set; } = string.Empty;
}

Required Injections:

  • IConfiguration - for runtimeEnvironment
  • MongoConnectionSettings - for DatabaseName
  • SyrfSettings - for PrNumber
  • GitVersion - for version info

Frontend Changes

Configuration Updates

File: src/services/web/src/assets/data/env.template.json

Add new configuration properties:

{
  "pmOrigin": "${SYRF__PmOrigin}",
  "quartzOrigin": "${SYRF__QuartzOrigin}",
  "s3NotifierVersion": "${SYRF__S3NotifierVersion}"
}

File: src/services/web/src/app/core/services/config/app-settings.interface.ts

export interface AppSettings {
  // ... existing properties
  pmOrigin?: string;
  quartzOrigin?: string;
  s3NotifierVersion?: string;
}

New Service: EnvironmentInfoService

File: src/services/web/src/app/core/services/environment-info/environment-info.service.ts

@Injectable({ providedIn: 'root' })
export class EnvironmentInfoService {
  private readonly http = inject(HttpClient);
  private readonly config = inject(AppConfigService).config;

  private readonly _environmentInfo = signal<EnvironmentInfo | null>(null);
  private readonly _bannerDismissed = signal<boolean>(false);

  readonly environmentInfo = this._environmentInfo.asReadonly();
  readonly bannerDismissed = this._bannerDismissed.asReadonly();
  readonly isNonProduction = computed(() =>
    this._environmentInfo()?.isProduction === false
  );
  readonly showBanner = computed(() =>
    this.isNonProduction() && !this._bannerDismissed()
  );
  readonly showIndicator = computed(() =>
    this.isNonProduction() && this._bannerDismissed()
  );

  constructor() {
    // Restore dismissed state from sessionStorage
    const dismissed = sessionStorage.getItem('env-banner-dismissed');
    if (dismissed === 'true') {
      this._bannerDismissed.set(true);
    }
  }

  fetchEnvironmentInfo(): Observable<EnvironmentInfo> {
    return this.http.get<EnvironmentInfo>(`${this.config.apiOrigin}/api/application/info`);
  }

  dismissBanner(): void {
    this._bannerDismissed.set(true);
    sessionStorage.setItem('env-banner-dismissed', 'true');
  }

  restoreBanner(): void {
    this._bannerDismissed.set(false);
    sessionStorage.removeItem('env-banner-dismissed');
  }
}

New Components

  1. EnvironmentBannerComponent - Top banner for non-production
  2. EnvironmentIndicatorComponent - Discrete corner indicator
  3. EnvironmentWarningDialogComponent - Warning dialog on dismiss

Enhanced Version Info Dialog

Modify existing VersionInfoDialogComponent to:

  1. Add Environment section at top
  2. Fetch PM and Quartz versions from their health endpoints
  3. Display S3 Notifier version from config
  4. Show pre-release indicator for versions containing -
  5. Group services by deployment type (Kubernetes vs Lambda)

Helm Chart Changes

File: src/services/web/.chart/templates/_env-blocks.tpl

Add environment variables for new service origins:

- name: SYRF__PmOrigin
  value: {{ .Values.pmOrigin | default (printf "http://pm-service.%s.svc.cluster.local" .Release.Namespace) }}
- name: SYRF__QuartzOrigin
  value: {{ .Values.quartzOrigin | default (printf "http://quartz-service.%s.svc.cluster.local" .Release.Namespace) }}
- name: SYRF__S3NotifierVersion
  value: {{ .Values.s3NotifierVersion | default "unknown" }}

Health Endpoint Design

Dual-Purpose Health Endpoints

The /health/live endpoints serve two purposes:

  1. Kubernetes Health Probes - Liveness/readiness checks for pod lifecycle
  2. Version Information - Expose service version for UI display

This dual-purpose design avoids creating separate version endpoints while maintaining full compatibility with Kubernetes health checking.

Response Schema

// GET /health/live
{
  "status": "Healthy",
  "version": "8.21.0",
  "gitVersion": "8.21.0",
  "gitSha": "abc1234def5678",
  "gitInformationalVersion": "8.21.0+abc1234def5678"
}

Kubernetes Compatibility

Health probes only check HTTP status codes (200 = healthy), not response body:

# Kubernetes probe configuration (existing)
livenessProbe:
  httpGet:
    path: /health/live
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10

Why this works:

  • Kubernetes probes ignore response body - only status code matters
  • Adding version fields to JSON response has zero impact on probe behavior
  • Response remains lightweight (< 200 bytes)

Current Implementation

The existing health endpoints already include version info via ASP.NET Core health checks:

// In Program.cs or Startup.cs
builder.Services.AddHealthChecks()
    .AddCheck<VersionHealthCheck>("version");

The VersionHealthCheck injects GitVersion and adds version metadata:

public class VersionHealthCheck : IHealthCheck
{
    private readonly GitVersion _gitVersion;

    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default)
    {
        var data = new Dictionary<string, object>
        {
            ["version"] = _gitVersion.SemVer,
            ["gitSha"] = _gitVersion.Sha,
            ["gitInformationalVersion"] = _gitVersion.InformationalVersion
        };

        return Task.FromResult(HealthCheckResult.Healthy("OK", data));
    }
}

Service Health Endpoint Matrix

Service Endpoint Response Includes Notes
API /health/live version, gitSha Already implemented
PM /health/live version, gitSha Already implemented
Quartz /health/live version, gitSha Already implemented
Web N/A (static) Via config NGINX serves static files
S3 Notifier N/A (Lambda) Via config Version injected at deploy time

Frontend Fetching Strategy

// Fetch versions from health endpoints
async fetchServiceVersions(): Promise<ServiceVersions> {
  const [apiHealth, pmHealth, quartzHealth] = await Promise.allSettled([
    this.http.get<HealthResponse>(`${this.config.apiOrigin}/health/live`).toPromise(),
    this.http.get<HealthResponse>(`${this.config.pmOrigin}/health/live`).toPromise(),
    this.http.get<HealthResponse>(`${this.config.quartzOrigin}/health/live`).toPromise(),
  ]);

  return {
    api: this.extractVersion(apiHealth),
    pm: this.extractVersion(pmHealth),
    quartz: this.extractVersion(quartzHealth),
    s3Notifier: this.config.s3NotifierVersion,  // From config, not health check
    web: this.config.gitVersion,                 // From config
  };
}

Error Handling

If a health endpoint is unreachable:

  • Show "Unavailable" status in Version Info Dialog
  • Don't block other version fetches (use Promise.allSettled)
  • Log warning but don't show error to user

Version Information Strategy

Service-Specific SemVer

Each service maintains its own semantic version:

Service Version Source Example
Web gitVersion in config v5.0.1
API /health/live endpoint v8.21.0
PM /health/live endpoint v10.45.0
Quartz /health/live endpoint v0.5.2
S3 Notifier Config (set at deploy time) v1.2.0

Pre-Release Detection

Versions containing - are pre-release builds:

  • v10.45.0-PullRequest2234.1 → Pre-release (PR build)
  • v10.45.0-alpha.1 → Pre-release (alpha)
  • v10.45.0 → Stable release

Display pre-release versions with visual indicator in dialog.

Global Monorepo SHA

The Git SHA is shared across all services in a deployment:

  • Stored in each service's GitVersion.Sha property
  • Represents the monorepo commit that produced the build
  • Useful for tracing exact code version across all services

Files Summary

Files to Create

File Purpose
src/services/api/SyRF.API.Endpoint/DTOs/ApplicationInfoDto.cs Response DTO for /application/info
src/services/web/src/app/core/services/environment-info/environment-info.service.ts Environment state management
src/services/web/src/app/shared/components/environment-banner/ Top banner component
src/services/web/src/app/shared/components/environment-indicator/ Corner indicator component
src/services/web/src/app/shared/dialogs/environment-warning-dialog/ Dismiss warning dialog

Files to Modify

File Change
ApplicationController.cs Add /api/application/info endpoint
version-info-dialog.component.* Add environment section, all service versions
app.component.ts Add banner/indicator integration
app.component.html Add banner/indicator templates
app.component.scss Add banner and indicator styles
env.template.json Add pmOrigin, quartzOrigin, s3NotifierVersion
app-settings.interface.ts Add new config properties
_env-blocks.tpl Add new environment variables

Testing Strategy

Unit Tests

  1. EnvironmentInfoService
  2. Fetches environment info correctly
  3. Handles API errors gracefully
  4. Persists/restores banner dismissed state
  5. Computes isNonProduction correctly

  6. EnvironmentBannerComponent

  7. Shows only for non-production
  8. Opens Version Info Dialog on click
  9. Opens Warning Dialog on dismiss
  10. Hides when dismissed

  11. EnvironmentIndicatorComponent

  12. Shows only when banner dismissed
  13. Opens Version Info Dialog on click
  14. Displays correct abbreviation (STG, PR-N, DEV)

  15. EnvironmentWarningDialogComponent

  16. Displays correct environment name
  17. Keep Banner restores banner
  18. Hide Banner dismisses and shows indicator

  19. Backend Endpoint

  20. Returns correct environment info
  21. Returns database name without credentials
  22. Handles missing configuration gracefully

Integration Tests

  1. Banner → Dialog flow
  2. Banner → Warning → Indicator flow
  3. Indicator → Dialog flow
  4. Session persistence across page reload

Manual Testing Checklist

  • Local development shows DEV banner
  • Staging shows STAGING banner with orange color
  • PR preview shows PR-{number} with blue color
  • Production shows no banner
  • Version Info Dialog shows all service versions
  • Pre-release versions show indicator
  • Copy Info copies all version details
  • Dismissed state persists on page reload
  • Indicator is discrete and doesn't disrupt layout

Rollout Plan

Phase 1: Backend Endpoint (0.5 day)

  1. Create ApplicationInfoDto
  2. Add /api/application/info endpoint
  3. Add unit test
  4. Deploy to staging

Phase 2: Frontend Core (1 day)

  1. Add EnvironmentInfoService
  2. Update config interfaces
  3. Update env.template.json
  4. Add Helm chart changes

Phase 3: Components (1 day)

  1. Create EnvironmentBannerComponent
  2. Create EnvironmentIndicatorComponent
  3. Create EnvironmentWarningDialogComponent
  4. Add component tests

Phase 4: Dialog Enhancement (0.5 day)

  1. Enhance VersionInfoDialogComponent
  2. Add PM, Quartz, S3 Notifier versions
  3. Add environment section
  4. Update styling

Phase 5: Integration (0.5 day)

  1. Integrate in app.component
  2. Wire up dismiss flow
  3. End-to-end testing
  4. Deploy to staging for validation


Appendix: Environment Warning Messages

Staging Environment (Isolated Database)

You are using the STAGING environment.

  • Data may be reset at any time during deployments
  • Changes you make will NOT affect production data
  • This environment is for testing purposes only
  • The database may be switched - your data could be replaced with fresh test data

Database: syrf_staging

Staging Environment (Production Database - Legacy)

⚠️ WARNING: You are using STAGING with the PRODUCTION database.

  • Changes you make WILL affect real production data
  • This configuration is temporary during migration
  • The database may be switched to an isolated staging database
  • When switched, any data entered here will no longer be visible

Database: syrftest (Production)

Preview Environment (Isolated Database)

You are using a PR PREVIEW environment.

  • This is an isolated environment for PR #{number}
  • Data is seeded from test fixtures, not production
  • This environment will be deleted when the PR closes
  • The database may be reset via workflow dispatch or label triggers

Database: syrf_pr_{number}

Preview Environment (Production Database - Legacy)

⚠️ WARNING: You are using a PR PREVIEW with the PRODUCTION database.

  • Changes you make WILL affect real production data
  • This configuration may occur before database isolation is complete
  • The database may be switched to an isolated PR database
  • When switched, you will see seeded test data instead of production data

Database: syrftest (Production)

Development Environment

You are using LOCAL DEVELOPMENT.

  • Data is stored in your local/development database
  • Changes will not affect any deployed environment
  • The database connection may change based on your local configuration

Database: {configured database name}


Appendix: Database Configuration States

The warning dialog dynamically adjusts based on two factors:

Environment Database Type Warning Level Key Message
Staging Isolated (syrf_staging) Medium Data may be reset
Staging Production (syrftest) High Affects real data, may be switched
Preview Isolated (syrf_pr_N) Medium Data is seeded, will be deleted
Preview Production (syrftest) High Affects real data, may be switched
Development Any Low Local only
Production Production (syrftest) None No warning shown

Detection Logic

// Determine warning severity based on environment + database combination
function getWarningSeverity(env: string, dbName: string): 'high' | 'medium' | 'low' | 'none' {
  const isProductionDb = dbName === 'syrftest';

  if (env === 'Production') return 'none';
  if (isProductionDb) return 'high';  // Non-prod env using prod DB
  if (env === 'Development') return 'low';
  return 'medium';  // Staging/Preview with isolated DB
}

Visual Differentiation

Warning Level Banner Color Icon Additional Styling
High Red #f44336 warning Pulsing animation
Medium Orange #ff9800 info Standard
Low Pink #e91e63 code Standard
None (not shown) - -