using System.Text.Json; using AutomationDAL.CustomCode.ScriptVersion; using AutomationDAL.DTO.ScriptVersion; using GB5Shared.DTO.Framework.Login; using GB5Shared.Resource.Response; using GB5Shared.Telemetry; using Microsoft.Extensions.Logging; namespace AutomationBLL.ScriptVersion; public class ScriptVersionBLL : IScriptVersionBLL { private readonly IScriptVersionDAL _dal; private readonly ILogger _logger; public ScriptVersionBLL(IScriptVersionDAL dal, ILogger logger) { _dal = dal; _logger = logger; } public async Task GetById(int versionId, LoginDTO login, CancellationToken ct) => await _dal.GetById(versionId, login, ct).ConfigureAwait(false); public async Task> GetByScript(int scriptId, LoginDTO login, CancellationToken ct) => await _dal.GetByScript(scriptId, login, ct).ConfigureAwait(false); public async Task GetLatestReleased(int scriptId, LoginDTO login, CancellationToken ct) => await _dal.GetLatestReleased(scriptId, login, ct).ConfigureAwait(false); public async Task RegisterVersion(ScriptVersionDTO dto, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-script-version", new { dto.ScriptId, dto.VersionLabel }); if (dto.ScriptId <= 0) throw new InvalidOperationException("ScriptId is required."); if (string.IsNullOrWhiteSpace(dto.VersionLabel)) throw new InvalidOperationException("VersionLabel is required."); GB5Trace.Step("save-script-version", new { dto.ScriptId, dto.VersionLabel }); int newId = await _dal.Insert(dto, login, ct).ConfigureAwait(false); return $"{SuccessResponse.SaveSuccessMessage} {newId}"; } catch (Exception ex) { GB5Trace.MarkFailed("register-script-version-failed", ex); _logger.LogError(ex, "RegisterVersion failed for ScriptId {ScriptId}", dto.ScriptId); throw; } } // Phase 1 pre-release checks are a lightweight completeness gate (required fields, distribution // reference present). Build/lint/sample-run/secrets-scan integration is out of scope until the // agent pool (Phase 3) exists to actually execute a sample run. public async Task RunPreReleaseChecks(int versionId, LoginDTO login, CancellationToken ct) { try { var version = await _dal.GetById(versionId, login, ct).ConfigureAwait(false) ?? throw new InvalidOperationException($"ScriptVersion {versionId} not found."); GB5Trace.Step("run-prerelease-checks", new { versionId }); var failures = new List(); if (string.IsNullOrWhiteSpace(version.GitCommit) && string.IsNullOrWhiteSpace(version.ImageTag)) failures.Add("Either GitCommit or ImageTag must be set (distribution reference missing)."); if (string.IsNullOrWhiteSpace(version.VersionLabel)) failures.Add("VersionLabel is missing."); var passed = failures.Count == 0; var details = JsonSerializer.Serialize(new { CheckedOn = DateTime.UtcNow, Checks = new[] { "RequiredFieldsPresent", "DistributionReferencePresent" }, Failures = failures }); await _dal.UpdateCheckResult(versionId, passed ? "Passed" : "Failed", details, login, ct).ConfigureAwait(false); return passed ? "Pre-release checks passed." : $"Pre-release checks failed: {string.Join("; ", failures)}"; } catch (Exception ex) { GB5Trace.MarkFailed("run-prerelease-checks-failed", ex); _logger.LogError(ex, "RunPreReleaseChecks failed for VersionId {VersionId}", versionId); throw; } } public async Task ReleaseVersion(int versionId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("release-script-version", new { versionId }); int rows = await _dal.Release(versionId, login, ct).ConfigureAwait(false); if (rows == 0) throw new InvalidOperationException( "Cannot release: version must have CheckStatus=Passed and currently be in Draft status."); return SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("release-script-version-failed", ex); _logger.LogError(ex, "ReleaseVersion failed for VersionId {VersionId}", versionId); throw; } } }