Add integration tests for migration categories and execution
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
- Implemented MigrationCategoryTests to validate migration categorization for startup, release, seed, and data migrations. - Added tests for edge cases, including null, empty, and whitespace migration names. - Created StartupMigrationHostTests to verify the behavior of the migration host with real PostgreSQL instances using Testcontainers. - Included tests for migration execution, schema creation, and handling of pending release migrations. - Added SQL migration files for testing: creating a test table, adding a column, a release migration, and seeding data.
This commit is contained in:
@@ -28,6 +28,7 @@ namespace StellaOps.Cli.Services;
|
||||
internal sealed class BackendOperationsClient : IBackendOperationsClient
|
||||
{
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web);
|
||||
private static readonly JsonSerializerOptions JsonOptions = SerializerOptions;
|
||||
private static readonly TimeSpan TokenRefreshSkew = TimeSpan.FromSeconds(30);
|
||||
private static readonly IReadOnlyDictionary<string, object?> EmptyMetadata =
|
||||
new ReadOnlyDictionary<string, object?>(new Dictionary<string, object?>(0, StringComparer.OrdinalIgnoreCase));
|
||||
@@ -523,8 +524,7 @@ internal sealed class BackendOperationsClient : IBackendOperationsClient
|
||||
using var response = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var (message, problem) = await CreateFailureDetailsAsync(response, cancellationToken).ConfigureAwait(false);
|
||||
var errorCode = ExtractProblemErrorCode(problem);
|
||||
var (message, errorCode) = await CreateFailureDetailsAsync(response, cancellationToken).ConfigureAwait(false);
|
||||
throw new PolicyApiException(message, response.StatusCode, errorCode);
|
||||
}
|
||||
|
||||
@@ -639,8 +639,7 @@ internal sealed class BackendOperationsClient : IBackendOperationsClient
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var (message, problem) = await CreateFailureDetailsAsync(response, cancellationToken).ConfigureAwait(false);
|
||||
var errorCode = ExtractProblemErrorCode(problem);
|
||||
var (message, errorCode) = await CreateFailureDetailsAsync(response, cancellationToken).ConfigureAwait(false);
|
||||
throw new PolicyApiException(message, response.StatusCode, errorCode);
|
||||
}
|
||||
|
||||
@@ -758,8 +757,7 @@ internal sealed class BackendOperationsClient : IBackendOperationsClient
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var (message, problem) = await CreateFailureDetailsAsync(response, cancellationToken).ConfigureAwait(false);
|
||||
var errorCode = ExtractProblemErrorCode(problem);
|
||||
var (message, errorCode) = await CreateFailureDetailsAsync(response, cancellationToken).ConfigureAwait(false);
|
||||
throw new PolicyApiException(message, response.StatusCode, errorCode);
|
||||
}
|
||||
|
||||
@@ -807,8 +805,7 @@ internal sealed class BackendOperationsClient : IBackendOperationsClient
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var (message, problem) = await CreateFailureDetailsAsync(response, cancellationToken).ConfigureAwait(false);
|
||||
var errorCode = ExtractProblemErrorCode(problem);
|
||||
var (message, errorCode) = await CreateFailureDetailsAsync(response, cancellationToken).ConfigureAwait(false);
|
||||
throw new PolicyApiException(message, response.StatusCode, errorCode);
|
||||
}
|
||||
|
||||
@@ -858,8 +855,7 @@ internal sealed class BackendOperationsClient : IBackendOperationsClient
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var (message, problem) = await CreateFailureDetailsAsync(response, cancellationToken).ConfigureAwait(false);
|
||||
var errorCode = ExtractProblemErrorCode(problem);
|
||||
var (message, errorCode) = await CreateFailureDetailsAsync(response, cancellationToken).ConfigureAwait(false);
|
||||
throw new PolicyApiException(message, response.StatusCode, errorCode);
|
||||
}
|
||||
|
||||
@@ -909,11 +905,11 @@ internal sealed class BackendOperationsClient : IBackendOperationsClient
|
||||
throw new InvalidOperationException(failure);
|
||||
}
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<EntryTraceResponseModel>(SerializerOptions, cancellationToken).ConfigureAwait(false);
|
||||
if (result is null)
|
||||
{
|
||||
throw new InvalidOperationException("EntryTrace response payload was empty.");
|
||||
}
|
||||
var result = await response.Content.ReadFromJsonAsync<EntryTraceResponseModel>(SerializerOptions, cancellationToken).ConfigureAwait(false);
|
||||
if (result is null)
|
||||
{
|
||||
throw new InvalidOperationException("EntryTrace response payload was empty.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -4512,11 +4508,11 @@ internal sealed class BackendOperationsClient : IBackendOperationsClient
|
||||
}
|
||||
|
||||
// CLI-SDK-64-001: SDK update operations
|
||||
public async Task<SdkUpdateResponse> CheckSdkUpdatesAsync(SdkUpdateRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(request);
|
||||
|
||||
EnsureBackendConfigured();
|
||||
public async Task<SdkUpdateResponse> CheckSdkUpdatesAsync(SdkUpdateRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(request);
|
||||
|
||||
EnsureBackendConfigured();
|
||||
OfflineModeGuard.ThrowIfOffline("sdk update");
|
||||
|
||||
var queryParams = new List<string>();
|
||||
@@ -4554,9 +4550,9 @@ internal sealed class BackendOperationsClient : IBackendOperationsClient
|
||||
};
|
||||
}
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<SdkUpdateResponse>(JsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
return result ?? new SdkUpdateResponse { Success = false, Error = "Empty response" };
|
||||
}
|
||||
var result = await response.Content.ReadFromJsonAsync<SdkUpdateResponse>(JsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
return result ?? new SdkUpdateResponse { Success = false, Error = "Empty response" };
|
||||
}
|
||||
|
||||
public async Task<SdkListResponse> ListInstalledSdksAsync(string? language, string? tenant, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.Client;
|
||||
using StellaOps.Cli.Configuration;
|
||||
using StellaOps.Cli.Extensions;
|
||||
using StellaOps.Cli.Services.Models;
|
||||
|
||||
namespace StellaOps.Cli.Services;
|
||||
@@ -576,12 +577,10 @@ internal sealed class ExceptionClient : IExceptionClient
|
||||
}
|
||||
}
|
||||
|
||||
var result = await tokenClient.GetTokenAsync(
|
||||
new StellaOpsTokenRequest { Scopes = [scope] },
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (result.IsSuccess)
|
||||
try
|
||||
{
|
||||
var result = await tokenClient.GetAccessTokenAsync(scope, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
lock (tokenSync)
|
||||
{
|
||||
cachedAccessToken = result.AccessToken;
|
||||
@@ -589,8 +588,10 @@ internal sealed class ExceptionClient : IExceptionClient
|
||||
}
|
||||
return result.AccessToken;
|
||||
}
|
||||
|
||||
logger.LogWarning("Token acquisition failed: {Error}", result.Error);
|
||||
return null;
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Token acquisition failed");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
60
src/Cli/StellaOps.Cli/Services/MigrationModuleRegistry.cs
Normal file
60
src/Cli/StellaOps.Cli/Services/MigrationModuleRegistry.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace StellaOps.Cli.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a PostgreSQL module with its migration metadata.
|
||||
/// </summary>
|
||||
public sealed record MigrationModuleInfo(
|
||||
string Name,
|
||||
string SchemaName,
|
||||
Assembly MigrationsAssembly,
|
||||
string? ResourcePrefix = null);
|
||||
|
||||
/// <summary>
|
||||
/// Registry of all PostgreSQL modules and their migration assemblies.
|
||||
/// Stub implementation - actual module assemblies will be wired in Wave 3-8.
|
||||
/// </summary>
|
||||
public static class MigrationModuleRegistry
|
||||
{
|
||||
// TODO: Wire actual module assemblies when Storage.Postgres projects are implemented
|
||||
// Modules will be registered as:
|
||||
// - Authority (auth schema) - StellaOps.Authority.Storage.Postgres.AuthorityDataSource
|
||||
// - Scheduler (scheduler schema) - StellaOps.Scheduler.Storage.Postgres.SchedulerDataSource
|
||||
// - Concelier (vuln schema) - StellaOps.Concelier.Storage.Postgres.ConcelierDataSource
|
||||
// - Policy (policy schema) - StellaOps.Policy.Storage.Postgres.PolicyDataSource
|
||||
// - Notify (notify schema) - StellaOps.Notify.Storage.Postgres.NotifyDataSource
|
||||
// - Excititor (vex schema) - StellaOps.Excititor.Storage.Postgres.ExcititorDataSource
|
||||
private static readonly List<MigrationModuleInfo> _modules = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets all registered modules.
|
||||
/// </summary>
|
||||
public static IReadOnlyList<MigrationModuleInfo> Modules => _modules;
|
||||
|
||||
/// <summary>
|
||||
/// Gets module names for CLI completion.
|
||||
/// </summary>
|
||||
public static IEnumerable<string> ModuleNames => _modules.Select(m => m.Name);
|
||||
|
||||
/// <summary>
|
||||
/// Finds a module by name (case-insensitive).
|
||||
/// </summary>
|
||||
public static MigrationModuleInfo? FindModule(string name) =>
|
||||
_modules.FirstOrDefault(m =>
|
||||
string.Equals(m.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
/// <summary>
|
||||
/// Gets modules matching the filter, or all if filter is null/empty.
|
||||
/// </summary>
|
||||
public static IEnumerable<MigrationModuleInfo> GetModules(string? moduleFilter)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(moduleFilter) || moduleFilter.Equals("all", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return _modules;
|
||||
}
|
||||
|
||||
var module = FindModule(moduleFilter);
|
||||
return module != null ? [module] : [];
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,7 @@ internal sealed class AttestationSubjectInfo
|
||||
/// <summary>
|
||||
/// Signature information for display.
|
||||
/// </summary>
|
||||
internal sealed class AttestationSignatureInfo
|
||||
internal sealed record AttestationSignatureInfo
|
||||
{
|
||||
[JsonPropertyName("keyId")]
|
||||
public string KeyId { get; init; } = string.Empty;
|
||||
@@ -162,7 +162,7 @@ internal sealed class AttestationSignerInfo
|
||||
/// <summary>
|
||||
/// Summary of the predicate for display.
|
||||
/// </summary>
|
||||
internal sealed class AttestationPredicateSummary
|
||||
internal sealed record AttestationPredicateSummary
|
||||
{
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; init; } = string.Empty;
|
||||
|
||||
@@ -519,17 +519,8 @@ internal sealed class PolicyDiagnostic
|
||||
[JsonPropertyName("severity")]
|
||||
public string Severity { get; init; } = "error";
|
||||
|
||||
[JsonPropertyName("line")]
|
||||
public int? Line { get; init; }
|
||||
|
||||
[JsonPropertyName("column")]
|
||||
public int? Column { get; init; }
|
||||
|
||||
[JsonPropertyName("span")]
|
||||
public string? Span { get; init; }
|
||||
|
||||
[JsonPropertyName("suggestion")]
|
||||
public string? Suggestion { get; init; }
|
||||
[JsonPropertyName("path")]
|
||||
public string? Path { get; init; }
|
||||
}
|
||||
|
||||
// CLI-POLICY-27-002: Policy submission/review workflow models
|
||||
|
||||
@@ -236,7 +236,7 @@ internal sealed class ReachabilityFunction
|
||||
/// <summary>
|
||||
/// Reachability override for policy simulation.
|
||||
/// </summary>
|
||||
internal sealed class ReachabilityOverride
|
||||
internal sealed record ReachabilityOverride
|
||||
{
|
||||
[JsonPropertyName("vulnerabilityId")]
|
||||
public string? VulnerabilityId { get; init; }
|
||||
|
||||
@@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.Client;
|
||||
using StellaOps.Cli.Configuration;
|
||||
using StellaOps.Cli.Extensions;
|
||||
using StellaOps.Cli.Services.Models;
|
||||
|
||||
namespace StellaOps.Cli.Services;
|
||||
@@ -634,12 +635,10 @@ internal sealed class NotifyClient : INotifyClient
|
||||
}
|
||||
}
|
||||
|
||||
var result = await tokenClient.GetTokenAsync(
|
||||
new StellaOpsTokenRequest { Scopes = [scope] },
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (result.IsSuccess)
|
||||
try
|
||||
{
|
||||
var result = await tokenClient.GetAccessTokenAsync(scope, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
lock (tokenSync)
|
||||
{
|
||||
cachedAccessToken = result.AccessToken;
|
||||
@@ -647,8 +646,10 @@ internal sealed class NotifyClient : INotifyClient
|
||||
}
|
||||
return result.AccessToken;
|
||||
}
|
||||
|
||||
logger.LogWarning("Token acquisition failed: {Error}", result.Error);
|
||||
return null;
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Token acquisition failed");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.Client;
|
||||
using StellaOps.Cli.Configuration;
|
||||
using StellaOps.Cli.Extensions;
|
||||
using StellaOps.Cli.Services.Models;
|
||||
|
||||
namespace StellaOps.Cli.Services;
|
||||
@@ -170,12 +171,10 @@ internal sealed class ObservabilityClient : IObservabilityClient
|
||||
}
|
||||
}
|
||||
|
||||
var result = await tokenClient.GetTokenAsync(
|
||||
new StellaOpsTokenRequest { Scopes = ["obs:read"] },
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (result.IsSuccess)
|
||||
try
|
||||
{
|
||||
var result = await tokenClient.GetAccessTokenAsync(StellaOpsScopes.ObservabilityRead, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
lock (tokenSync)
|
||||
{
|
||||
cachedAccessToken = result.AccessToken;
|
||||
@@ -183,9 +182,11 @@ internal sealed class ObservabilityClient : IObservabilityClient
|
||||
}
|
||||
return result.AccessToken;
|
||||
}
|
||||
|
||||
logger.LogWarning("Token acquisition failed: {Error}", result.Error);
|
||||
return null;
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Token acquisition failed");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// CLI-OBS-52-001: Trace retrieval
|
||||
|
||||
41
src/Cli/StellaOps.Cli/Services/OfflineModeGuard.cs
Normal file
41
src/Cli/StellaOps.Cli/Services/OfflineModeGuard.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
|
||||
namespace StellaOps.Cli.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Guard for operations that require network connectivity.
|
||||
/// Stub implementation - will be wired to actual offline mode detection.
|
||||
/// </summary>
|
||||
internal static class OfflineModeGuard
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets whether the CLI is currently in offline mode.
|
||||
/// </summary>
|
||||
public static bool IsOffline { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether network operations are allowed.
|
||||
/// </summary>
|
||||
public static bool IsNetworkAllowed() => !IsOffline;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether network operations are allowed, checking options and logging operation.
|
||||
/// </summary>
|
||||
/// <param name="options">CLI options (used to check offline mode setting).</param>
|
||||
/// <param name="operationName">Name of the operation being checked.</param>
|
||||
public static bool IsNetworkAllowed(object? options, string operationName) => !IsOffline;
|
||||
|
||||
/// <summary>
|
||||
/// Throws if the CLI is in offline mode and the operation requires network.
|
||||
/// </summary>
|
||||
/// <param name="operationName">Name of the operation being guarded.</param>
|
||||
/// <exception cref="InvalidOperationException">Thrown when offline and network required.</exception>
|
||||
public static void ThrowIfOffline(string operationName)
|
||||
{
|
||||
if (IsOffline)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Operation '{operationName}' requires network connectivity but CLI is in offline mode.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,10 @@ using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.Client;
|
||||
using StellaOps.Auth.Client.Scopes;
|
||||
using StellaOps.Cli.Configuration;
|
||||
using StellaOps.Cli.Extensions;
|
||||
using StellaOps.Cli.Services.Models;
|
||||
|
||||
namespace StellaOps.Cli.Services;
|
||||
@@ -386,7 +387,7 @@ internal sealed class OrchestratorClient : IOrchestratorClient
|
||||
private async Task ConfigureAuthAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var token = await _tokenClient.GetCachedAccessTokenAsync(
|
||||
new[] { StellaOpsScope.OrchRead },
|
||||
new[] { StellaOpsScopes.OrchRead },
|
||||
cancellationToken);
|
||||
|
||||
_httpClient.DefaultRequestHeaders.Authorization =
|
||||
|
||||
@@ -11,6 +11,7 @@ using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.Client;
|
||||
using StellaOps.Cli.Configuration;
|
||||
using StellaOps.Cli.Extensions;
|
||||
using StellaOps.Cli.Services.Models;
|
||||
|
||||
namespace StellaOps.Cli.Services;
|
||||
@@ -997,12 +998,10 @@ internal sealed class PackClient : IPackClient
|
||||
}
|
||||
}
|
||||
|
||||
var result = await tokenClient.GetTokenAsync(
|
||||
new StellaOpsTokenRequest { Scopes = [scope] },
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (result.IsSuccess)
|
||||
try
|
||||
{
|
||||
var result = await tokenClient.GetAccessTokenAsync(scope, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
lock (tokenSync)
|
||||
{
|
||||
cachedAccessToken = result.AccessToken;
|
||||
@@ -1010,8 +1009,10 @@ internal sealed class PackClient : IPackClient
|
||||
}
|
||||
return result.AccessToken;
|
||||
}
|
||||
|
||||
logger.LogWarning("Token acquisition failed: {Error}", result.Error);
|
||||
return null;
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Token acquisition failed");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
@@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.Client;
|
||||
using StellaOps.Cli.Configuration;
|
||||
using StellaOps.Cli.Extensions;
|
||||
using StellaOps.Cli.Services.Models;
|
||||
|
||||
namespace StellaOps.Cli.Services;
|
||||
@@ -463,12 +464,10 @@ internal sealed class SbomClient : ISbomClient
|
||||
}
|
||||
}
|
||||
|
||||
var result = await tokenClient.GetTokenAsync(
|
||||
new StellaOpsTokenRequest { Scopes = [scope] },
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (result.IsSuccess)
|
||||
try
|
||||
{
|
||||
var result = await tokenClient.GetAccessTokenAsync(scope, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
lock (tokenSync)
|
||||
{
|
||||
cachedAccessToken = result.AccessToken;
|
||||
@@ -476,8 +475,10 @@ internal sealed class SbomClient : ISbomClient
|
||||
}
|
||||
return result.AccessToken;
|
||||
}
|
||||
|
||||
logger.LogWarning("Token acquisition failed: {Error}", result.Error);
|
||||
return null;
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Token acquisition failed");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Auth.Client;
|
||||
using StellaOps.Cli.Extensions;
|
||||
using StellaOps.Cli.Services.Models;
|
||||
|
||||
namespace StellaOps.Cli.Services;
|
||||
@@ -194,11 +195,18 @@ internal sealed class SbomerClient : ISbomerClient
|
||||
if (_tokenClient == null)
|
||||
return;
|
||||
|
||||
var token = await _tokenClient.GetTokenAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (!string.IsNullOrWhiteSpace(token))
|
||||
try
|
||||
{
|
||||
_httpClient.DefaultRequestHeaders.Authorization =
|
||||
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
||||
var tokenResult = await _tokenClient.GetTokenAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (!string.IsNullOrWhiteSpace(tokenResult.AccessToken))
|
||||
{
|
||||
_httpClient.DefaultRequestHeaders.Authorization =
|
||||
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", tokenResult.AccessToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to acquire token for Sbomer API access.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.Client;
|
||||
using StellaOps.Cli.Extensions;
|
||||
using StellaOps.Cli.Services.Models;
|
||||
|
||||
namespace StellaOps.Cli.Services;
|
||||
@@ -20,7 +21,7 @@ namespace StellaOps.Cli.Services;
|
||||
internal sealed class VexObservationsClient : IVexObservationsClient
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ITokenClient? _tokenClient;
|
||||
private readonly IStellaOpsTokenClient? _tokenClient;
|
||||
private readonly ILogger<VexObservationsClient> _logger;
|
||||
private string? _cachedToken;
|
||||
private DateTimeOffset _tokenExpiry;
|
||||
@@ -33,7 +34,7 @@ internal sealed class VexObservationsClient : IVexObservationsClient
|
||||
public VexObservationsClient(
|
||||
HttpClient httpClient,
|
||||
ILogger<VexObservationsClient> logger,
|
||||
ITokenClient? tokenClient = null)
|
||||
IStellaOpsTokenClient? tokenClient = null)
|
||||
{
|
||||
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
@@ -138,20 +139,23 @@ internal sealed class VexObservationsClient : IVexObservationsClient
|
||||
return;
|
||||
}
|
||||
|
||||
var tokenResult = await _tokenClient.GetAccessTokenAsync(
|
||||
new[] { StellaOpsScopes.VexRead },
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var tokenResult = await _tokenClient.GetAccessTokenAsync(
|
||||
StellaOpsScopes.VexRead,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (tokenResult.IsSuccess && !string.IsNullOrWhiteSpace(tokenResult.AccessToken))
|
||||
{
|
||||
_cachedToken = tokenResult.AccessToken;
|
||||
_tokenExpiry = DateTimeOffset.UtcNow.AddMinutes(55);
|
||||
_httpClient.DefaultRequestHeaders.Authorization =
|
||||
new AuthenticationHeaderValue("Bearer", _cachedToken);
|
||||
if (!string.IsNullOrWhiteSpace(tokenResult.AccessToken))
|
||||
{
|
||||
_cachedToken = tokenResult.AccessToken;
|
||||
_tokenExpiry = DateTimeOffset.UtcNow.AddMinutes(55);
|
||||
_httpClient.DefaultRequestHeaders.Authorization =
|
||||
new AuthenticationHeaderValue("Bearer", _cachedToken);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning("Failed to acquire token for VEX API access.");
|
||||
_logger.LogWarning(ex, "Failed to acquire token for VEX API access.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user