diff --git a/src/dotnet/HoldFast.Backend.slnx b/src/dotnet/HoldFast.Backend.slnx index 9d5b9778..b698bfe0 100644 --- a/src/dotnet/HoldFast.Backend.slnx +++ b/src/dotnet/HoldFast.Backend.slnx @@ -1,5 +1,6 @@ + diff --git a/src/dotnet/src/HoldFast.Data.ClickHouse/CursorHelper.cs b/src/dotnet/src/HoldFast.Analytics/CursorHelper.cs similarity index 87% rename from src/dotnet/src/HoldFast.Data.ClickHouse/CursorHelper.cs rename to src/dotnet/src/HoldFast.Analytics/CursorHelper.cs index 35a514d9..88b5f2c7 100644 --- a/src/dotnet/src/HoldFast.Data.ClickHouse/CursorHelper.cs +++ b/src/dotnet/src/HoldFast.Analytics/CursorHelper.cs @@ -1,11 +1,16 @@ using System.Globalization; using System.Text; -namespace HoldFast.Data.ClickHouse; +namespace HoldFast.Analytics; /// /// Encodes and decodes pagination cursors matching Go's cursor.go format. /// Format: base64("{RFC3339},{uuid}") +/// +/// Lives in HoldFast.Analytics (not a backend-specific project) because the +/// cursor format is part of the public GraphQL API contract — every backend +/// must produce/consume the same cursors so frontend pagination state survives +/// a backend swap. /// public static class CursorHelper { diff --git a/src/dotnet/src/HoldFast.Analytics/HoldFast.Analytics.csproj b/src/dotnet/src/HoldFast.Analytics/HoldFast.Analytics.csproj new file mode 100644 index 00000000..465c570a --- /dev/null +++ b/src/dotnet/src/HoldFast.Analytics/HoldFast.Analytics.csproj @@ -0,0 +1,23 @@ + + + + + + + + + net10.0 + enable + enable + + + diff --git a/src/dotnet/src/HoldFast.Analytics/IAlertStateStore.cs b/src/dotnet/src/HoldFast.Analytics/IAlertStateStore.cs new file mode 100644 index 00000000..80cb3b72 --- /dev/null +++ b/src/dotnet/src/HoldFast.Analytics/IAlertStateStore.cs @@ -0,0 +1,37 @@ +using HoldFast.Analytics.Models; + +namespace HoldFast.Analytics; + +/// +/// Backend-neutral store for alert state-change history. Alert evaluation +/// reads recent state changes to detect transitions and avoid duplicate +/// notifications; the worker writes new state changes after evaluation. +/// +public interface IAlertStateStore +{ + Task> GetLastAlertStateChangesAsync( + int projectId, + int alertId, + DateTime startDate, + DateTime endDate, + CancellationToken ct = default); + + Task> GetAlertingAlertStateChangesAsync( + int projectId, + int alertId, + DateTime startDate, + DateTime endDate, + CancellationToken ct = default); + + Task> GetLastAlertingStatesAsync( + int projectId, + int alertId, + DateTime startDate, + DateTime endDate, + CancellationToken ct = default); + + Task WriteAlertStateChangesAsync( + int projectId, + IEnumerable rows, + CancellationToken ct = default); +} diff --git a/src/dotnet/src/HoldFast.Analytics/IErrorAnalyticsStore.cs b/src/dotnet/src/HoldFast.Analytics/IErrorAnalyticsStore.cs new file mode 100644 index 00000000..48337539 --- /dev/null +++ b/src/dotnet/src/HoldFast.Analytics/IErrorAnalyticsStore.cs @@ -0,0 +1,47 @@ +using HoldFast.Analytics.Models; + +namespace HoldFast.Analytics; + +/// +/// Backend-neutral store for error analytics (error groups + error objects +/// search, histograms, key/value discovery for the dashboard errors filter +/// UI, and worker-side ingest writes). +/// +public interface IErrorAnalyticsStore +{ + Task<(List Ids, long Total)> QueryErrorGroupIdsAsync( + int projectId, + QueryInput query, + int count, + int page, + CancellationToken ct = default); + + Task> ReadErrorObjectsHistogramAsync( + int projectId, + QueryInput query, + CancellationToken ct = default); + + Task> GetErrorsKeysAsync( + int projectId, + DateTime startDate, + DateTime endDate, + string? query, + CancellationToken ct = default); + + Task> GetErrorsKeyValuesAsync( + int projectId, + string keyName, + DateTime startDate, + DateTime endDate, + string? query, + int? count, + CancellationToken ct = default); + + Task WriteErrorGroupsAsync( + IEnumerable errorGroups, + CancellationToken ct = default); + + Task WriteErrorObjectsAsync( + IEnumerable errorObjects, + CancellationToken ct = default); +} diff --git a/src/dotnet/src/HoldFast.Analytics/IEventFieldStore.cs b/src/dotnet/src/HoldFast.Analytics/IEventFieldStore.cs new file mode 100644 index 00000000..12c97821 --- /dev/null +++ b/src/dotnet/src/HoldFast.Analytics/IEventFieldStore.cs @@ -0,0 +1,28 @@ +using HoldFast.Analytics.Models; + +namespace HoldFast.Analytics; + +/// +/// Backend-neutral store for the events-key/value discovery surface +/// (used by the dashboard's event-search autocomplete on top of session events). +/// +public interface IEventFieldStore +{ + Task> GetEventsKeysAsync( + int projectId, + DateTime startDate, + DateTime endDate, + string? query, + string? eventName, + CancellationToken ct = default); + + Task> GetEventsKeyValuesAsync( + int projectId, + string keyName, + DateTime startDate, + DateTime endDate, + string? query, + int? count, + string? eventName, + CancellationToken ct = default); +} diff --git a/src/dotnet/src/HoldFast.Analytics/ILogStore.cs b/src/dotnet/src/HoldFast.Analytics/ILogStore.cs new file mode 100644 index 00000000..b097474f --- /dev/null +++ b/src/dotnet/src/HoldFast.Analytics/ILogStore.cs @@ -0,0 +1,44 @@ +using HoldFast.Analytics.Models; + +namespace HoldFast.Analytics; + +/// +/// Backend-neutral store for application log analytics. +/// One implementation per analytics backend (currently ClickHouse only; +/// HOL-26+ will add Postgres). +/// +public interface ILogStore +{ + Task ReadLogsAsync( + int projectId, + QueryInput query, + ClickHousePagination pagination, + CancellationToken ct = default); + + Task> ReadLogsHistogramAsync( + int projectId, + QueryInput query, + CancellationToken ct = default); + + Task> GetLogKeysAsync( + int projectId, + QueryInput query, + CancellationToken ct = default); + + Task> GetLogKeyValuesAsync( + int projectId, + string key, + QueryInput query, + CancellationToken ct = default); + + Task CountLogsAsync( + int projectId, + string? query, + DateTime startDate, + DateTime endDate, + CancellationToken ct = default); + + Task WriteLogsAsync( + IEnumerable logs, + CancellationToken ct = default); +} diff --git a/src/dotnet/src/HoldFast.Analytics/IMetricStore.cs b/src/dotnet/src/HoldFast.Analytics/IMetricStore.cs new file mode 100644 index 00000000..b938b664 --- /dev/null +++ b/src/dotnet/src/HoldFast.Analytics/IMetricStore.cs @@ -0,0 +1,28 @@ +using HoldFast.Analytics.Models; + +namespace HoldFast.Analytics; + +/// +/// Backend-neutral store for metric time-series queries and writes. +/// +public interface IMetricStore +{ + Task ReadMetricsAsync( + int projectId, + QueryInput query, + string bucketBy, + List? groupBy, + string aggregator, + string? column, + CancellationToken ct = default); + + Task WriteMetricAsync( + int projectId, + string metricName, + double metricValue, + string? category, + DateTime timestamp, + Dictionary? tags, + string? sessionSecureId, + CancellationToken ct = default); +} diff --git a/src/dotnet/src/HoldFast.Analytics/ISessionAnalyticsStore.cs b/src/dotnet/src/HoldFast.Analytics/ISessionAnalyticsStore.cs new file mode 100644 index 00000000..ba293541 --- /dev/null +++ b/src/dotnet/src/HoldFast.Analytics/ISessionAnalyticsStore.cs @@ -0,0 +1,48 @@ +using HoldFast.Analytics.Models; + +namespace HoldFast.Analytics; + +/// +/// Backend-neutral store for session analytics queries (sessions search, +/// histograms, key/value discovery for the dashboard sessions filter UI). +/// +/// Note: actual session-replay payloads (events, snapshots) live in blob +/// storage, not in the analytics store. This interface only covers the +/// search/aggregation surface. +/// +public interface ISessionAnalyticsStore +{ + Task> ReadSessionsHistogramAsync( + int projectId, + QueryInput query, + CancellationToken ct = default); + + Task<(List Ids, long Total)> QuerySessionIdsAsync( + int projectId, + QueryInput query, + int count, + int page, + string? sortField = null, + bool sortDesc = true, + CancellationToken ct = default); + + Task> GetSessionsKeysAsync( + int projectId, + DateTime startDate, + DateTime endDate, + string? query, + CancellationToken ct = default); + + Task> GetSessionsKeyValuesAsync( + int projectId, + string keyName, + DateTime startDate, + DateTime endDate, + string? query, + int? count, + CancellationToken ct = default); + + Task WriteSessionsAsync( + IEnumerable sessions, + CancellationToken ct = default); +} diff --git a/src/dotnet/src/HoldFast.Analytics/ITraceStore.cs b/src/dotnet/src/HoldFast.Analytics/ITraceStore.cs new file mode 100644 index 00000000..895c5928 --- /dev/null +++ b/src/dotnet/src/HoldFast.Analytics/ITraceStore.cs @@ -0,0 +1,36 @@ +using HoldFast.Analytics.Models; + +namespace HoldFast.Analytics; + +/// +/// Backend-neutral store for distributed-trace span analytics. +/// +public interface ITraceStore +{ + Task ReadTracesAsync( + int projectId, + QueryInput query, + ClickHousePagination pagination, + bool omitBody = false, + CancellationToken ct = default); + + Task> ReadTracesHistogramAsync( + int projectId, + QueryInput query, + CancellationToken ct = default); + + Task> GetTraceKeysAsync( + int projectId, + QueryInput query, + CancellationToken ct = default); + + Task> GetTraceKeyValuesAsync( + int projectId, + string key, + QueryInput query, + CancellationToken ct = default); + + Task WriteTracesAsync( + IEnumerable traces, + CancellationToken ct = default); +} diff --git a/src/dotnet/src/HoldFast.Data.ClickHouse/Models/AlertStateChangeRow.cs b/src/dotnet/src/HoldFast.Analytics/Models/AlertStateChangeRow.cs similarity index 78% rename from src/dotnet/src/HoldFast.Data.ClickHouse/Models/AlertStateChangeRow.cs rename to src/dotnet/src/HoldFast.Analytics/Models/AlertStateChangeRow.cs index 29e873bf..e8c81363 100644 --- a/src/dotnet/src/HoldFast.Data.ClickHouse/Models/AlertStateChangeRow.cs +++ b/src/dotnet/src/HoldFast.Analytics/Models/AlertStateChangeRow.cs @@ -1,7 +1,7 @@ -namespace HoldFast.Data.ClickHouse.Models; +namespace HoldFast.Analytics.Models; /// -/// Row from the alert_state_changes ClickHouse table. +/// Row from the alert_state_changes table. /// Mirrors Go's clickhouse.AlertStateChangeRow. /// public class AlertStateChangeRow diff --git a/src/dotnet/src/HoldFast.Data.ClickHouse/Models/LogRow.cs b/src/dotnet/src/HoldFast.Analytics/Models/LogRow.cs similarity index 90% rename from src/dotnet/src/HoldFast.Data.ClickHouse/Models/LogRow.cs rename to src/dotnet/src/HoldFast.Analytics/Models/LogRow.cs index d3bc4d8c..e83bf36a 100644 --- a/src/dotnet/src/HoldFast.Data.ClickHouse/Models/LogRow.cs +++ b/src/dotnet/src/HoldFast.Analytics/Models/LogRow.cs @@ -1,10 +1,11 @@ +using HoldFast.Analytics; using HoldFast.Domain.Enums; -namespace HoldFast.Data.ClickHouse.Models; +namespace HoldFast.Analytics.Models; /// -/// Represents a row in the ClickHouse logs table. -/// Read-only model — logs are written via Kafka consumers, read via ClickHouse queries. +/// Represents a row in the logs table. +/// Read-only model — logs are written via the ingest pipeline, read via analytics queries. /// public class LogRow { diff --git a/src/dotnet/src/HoldFast.Data.ClickHouse/Models/MetricsBucket.cs b/src/dotnet/src/HoldFast.Analytics/Models/MetricsBucket.cs similarity index 87% rename from src/dotnet/src/HoldFast.Data.ClickHouse/Models/MetricsBucket.cs rename to src/dotnet/src/HoldFast.Analytics/Models/MetricsBucket.cs index f010ddf9..f44b4d48 100644 --- a/src/dotnet/src/HoldFast.Data.ClickHouse/Models/MetricsBucket.cs +++ b/src/dotnet/src/HoldFast.Analytics/Models/MetricsBucket.cs @@ -1,7 +1,7 @@ -namespace HoldFast.Data.ClickHouse.Models; +namespace HoldFast.Analytics.Models; /// -/// A time-bucketed aggregation result from ClickHouse metrics queries. +/// A time-bucketed aggregation result from metrics queries. /// public class MetricsBucket { @@ -65,7 +65,10 @@ public class QueryInput } /// -/// Pagination parameters for cursor-based ClickHouse queries. +/// Pagination parameters for cursor-based analytics queries. +/// +/// Naming: kept as ClickHousePagination to minimize churn across ~20 callers. +/// A future cleanup PR can rename to CursorPagination. /// public class ClickHousePagination { diff --git a/src/dotnet/src/HoldFast.Data.ClickHouse/Models/TraceRow.cs b/src/dotnet/src/HoldFast.Analytics/Models/TraceRow.cs similarity index 92% rename from src/dotnet/src/HoldFast.Data.ClickHouse/Models/TraceRow.cs rename to src/dotnet/src/HoldFast.Analytics/Models/TraceRow.cs index 9e766d02..e21d6c4e 100644 --- a/src/dotnet/src/HoldFast.Data.ClickHouse/Models/TraceRow.cs +++ b/src/dotnet/src/HoldFast.Analytics/Models/TraceRow.cs @@ -1,10 +1,11 @@ +using HoldFast.Analytics; using HoldFast.Domain.Enums; -namespace HoldFast.Data.ClickHouse.Models; +namespace HoldFast.Analytics.Models; /// -/// Represents a row in the ClickHouse traces table. -/// Read-only model — traces are written via OTLP collector, read via ClickHouse queries. +/// Represents a row in the traces table. +/// Read-only model — traces are written via OTLP ingest, read via analytics queries. /// public class TraceRow { diff --git a/src/dotnet/src/HoldFast.Data.ClickHouse/Models/WriteInputs.cs b/src/dotnet/src/HoldFast.Analytics/Models/WriteInputs.cs similarity index 93% rename from src/dotnet/src/HoldFast.Data.ClickHouse/Models/WriteInputs.cs rename to src/dotnet/src/HoldFast.Analytics/Models/WriteInputs.cs index e1f370eb..f800d38d 100644 --- a/src/dotnet/src/HoldFast.Data.ClickHouse/Models/WriteInputs.cs +++ b/src/dotnet/src/HoldFast.Analytics/Models/WriteInputs.cs @@ -1,7 +1,7 @@ -namespace HoldFast.Data.ClickHouse.Models; +namespace HoldFast.Analytics.Models; /// -/// Input for writing a log row to ClickHouse. +/// Input for writing a log row. /// Matches the log_rows table schema. /// public class LogRowInput @@ -22,7 +22,7 @@ public class LogRowInput } /// -/// Input for writing a trace span row to ClickHouse. +/// Input for writing a trace span row. /// Matches the trace_rows table schema. /// public class TraceRowInput @@ -46,7 +46,7 @@ public class TraceRowInput } /// -/// Input for writing a session row to ClickHouse. +/// Input for writing a session row to the analytics store. /// Matches the sessions table schema used for analytics/search. /// public class SessionRowInput @@ -76,7 +76,7 @@ public class SessionRowInput } /// -/// Input for writing an error group row to ClickHouse. +/// Input for writing an error group row to the analytics store. /// Matches the error_groups table schema used for analytics/search. /// public class ErrorGroupRowInput @@ -94,7 +94,7 @@ public class ErrorGroupRowInput } /// -/// Input for writing an error object row to ClickHouse. +/// Input for writing an error object row to the analytics store. /// Matches the error_objects table schema used for analytics/search. /// public class ErrorObjectRowInput diff --git a/src/dotnet/src/HoldFast.Api/Program.cs b/src/dotnet/src/HoldFast.Api/Program.cs index cbffe36e..d44c2b2d 100644 --- a/src/dotnet/src/HoldFast.Api/Program.cs +++ b/src/dotnet/src/HoldFast.Api/Program.cs @@ -150,7 +150,23 @@ req.RequestUri is null || // ── ClickHouse ──────────────────────────────────────────────────────── builder.Services.Configure( builder.Configuration.GetSection("ClickHouse")); -builder.Services.AddSingleton(); + +// HOL-25: ClickHouseService implements both the legacy IClickHouseService +// and the seven backend-neutral domain stores. Register the singleton once +// and resolve all eight interfaces through it — different callers can hold +// any subset and DI hands back the same instance. When HOL-26+ lands the +// Postgres backend, the ILogStore/etc. registrations swap to the PG impl +// (driven by Storage:Analytics config) without disturbing IClickHouseService +// callers, which migrate to the per-domain interfaces incrementally. +builder.Services.AddSingleton(); +builder.Services.AddSingleton(sp => sp.GetRequiredService()); +builder.Services.AddSingleton(sp => sp.GetRequiredService()); +builder.Services.AddSingleton(sp => sp.GetRequiredService()); +builder.Services.AddSingleton(sp => sp.GetRequiredService()); +builder.Services.AddSingleton(sp => sp.GetRequiredService()); +builder.Services.AddSingleton(sp => sp.GetRequiredService()); +builder.Services.AddSingleton(sp => sp.GetRequiredService()); +builder.Services.AddSingleton(sp => sp.GetRequiredService()); // Migration runner — applies src/backend/clickhouse/migrations/*.up.sql at // startup, idempotently. Disable via ClickHouse__Migrations__Disabled=true diff --git a/src/dotnet/src/HoldFast.Data.ClickHouse/ClickHouseService.cs b/src/dotnet/src/HoldFast.Data.ClickHouse/ClickHouseService.cs index 3deb90e9..7b3c2b4a 100644 --- a/src/dotnet/src/HoldFast.Data.ClickHouse/ClickHouseService.cs +++ b/src/dotnet/src/HoldFast.Data.ClickHouse/ClickHouseService.cs @@ -2,7 +2,8 @@ using ClickHouse.Client.ADO; using ClickHouse.Client.Utility; using Dapper; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics; +using HoldFast.Analytics.Models; using HoldFast.Domain.Enums; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -12,8 +13,24 @@ namespace HoldFast.Data.ClickHouse; /// /// Concrete ClickHouse analytics service using ClickHouse.Client + Dapper. /// Mirrors Go's clickhouse.Client query methods. +/// +/// HOL-25: this class now implements both the legacy kitchen-sink +/// IClickHouseService and the seven backend-neutral domain stores +/// (ILogStore, ITraceStore, …). Method signatures are identical across +/// the two API shapes — DI registers the same instance for all eight +/// interfaces. Existing callers stay on IClickHouseService until they +/// migrate to the per-domain interfaces in follow-up PRs. /// -public class ClickHouseService : IClickHouseService, IDisposable +public class ClickHouseService : + IClickHouseService, + ILogStore, + ITraceStore, + ISessionAnalyticsStore, + IErrorAnalyticsStore, + IMetricStore, + IEventFieldStore, + IAlertStateStore, + IDisposable { private readonly ClickHouseConnection _conn; private readonly ClickHouseConnection _readonlyConn; diff --git a/src/dotnet/src/HoldFast.Data.ClickHouse/HoldFast.Data.ClickHouse.csproj b/src/dotnet/src/HoldFast.Data.ClickHouse/HoldFast.Data.ClickHouse.csproj index 33288517..e855ad7d 100644 --- a/src/dotnet/src/HoldFast.Data.ClickHouse/HoldFast.Data.ClickHouse.csproj +++ b/src/dotnet/src/HoldFast.Data.ClickHouse/HoldFast.Data.ClickHouse.csproj @@ -1,6 +1,7 @@  + diff --git a/src/dotnet/src/HoldFast.Data.ClickHouse/IClickHouseService.cs b/src/dotnet/src/HoldFast.Data.ClickHouse/IClickHouseService.cs index 695f2cfc..0ec6ff2a 100644 --- a/src/dotnet/src/HoldFast.Data.ClickHouse/IClickHouseService.cs +++ b/src/dotnet/src/HoldFast.Data.ClickHouse/IClickHouseService.cs @@ -1,4 +1,4 @@ -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; namespace HoldFast.Data.ClickHouse; diff --git a/src/dotnet/src/HoldFast.GraphQL.Private/PrivateQuery.cs b/src/dotnet/src/HoldFast.GraphQL.Private/PrivateQuery.cs index b727e64a..d8114a46 100644 --- a/src/dotnet/src/HoldFast.GraphQL.Private/PrivateQuery.cs +++ b/src/dotnet/src/HoldFast.GraphQL.Private/PrivateQuery.cs @@ -1,7 +1,7 @@ using System.Security.Claims; using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain; using HoldFast.Domain.Entities; using HoldFast.Domain.Enums; diff --git a/src/dotnet/src/HoldFast.GraphQL.Private/ResponseTypes.cs b/src/dotnet/src/HoldFast.GraphQL.Private/ResponseTypes.cs index fde77118..c192e494 100644 --- a/src/dotnet/src/HoldFast.GraphQL.Private/ResponseTypes.cs +++ b/src/dotnet/src/HoldFast.GraphQL.Private/ResponseTypes.cs @@ -1,4 +1,4 @@ -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Domain.Enums; using HotChocolate; diff --git a/src/dotnet/src/HoldFast.GraphQL.Private/Types/LogsTypeExtension.cs b/src/dotnet/src/HoldFast.GraphQL.Private/Types/LogsTypeExtension.cs index 63dd6850..9cf10f2c 100644 --- a/src/dotnet/src/HoldFast.GraphQL.Private/Types/LogsTypeExtension.cs +++ b/src/dotnet/src/HoldFast.GraphQL.Private/Types/LogsTypeExtension.cs @@ -1,5 +1,5 @@ using System.Text.Json; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HotChocolate; using HotChocolate.Types; diff --git a/src/dotnet/src/HoldFast.GraphQL.Private/Types/SessionTypeExtension.cs b/src/dotnet/src/HoldFast.GraphQL.Private/Types/SessionTypeExtension.cs index 8bcab33d..317fc7df 100644 --- a/src/dotnet/src/HoldFast.GraphQL.Private/Types/SessionTypeExtension.cs +++ b/src/dotnet/src/HoldFast.GraphQL.Private/Types/SessionTypeExtension.cs @@ -1,6 +1,6 @@ using HoldFast.Domain.Entities; using HoldFast.Domain.Enums; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HotChocolate; using HotChocolate.Types; diff --git a/src/dotnet/src/HoldFast.Worker/DataSyncWorker.cs b/src/dotnet/src/HoldFast.Worker/DataSyncWorker.cs index 3479964e..bf060c31 100644 --- a/src/dotnet/src/HoldFast.Worker/DataSyncWorker.cs +++ b/src/dotnet/src/HoldFast.Worker/DataSyncWorker.cs @@ -1,6 +1,6 @@ using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; diff --git a/src/dotnet/src/HoldFast.Worker/FrontendErrorsWorker.cs b/src/dotnet/src/HoldFast.Worker/FrontendErrorsWorker.cs index 28e0c83d..dd2b58f3 100644 --- a/src/dotnet/src/HoldFast.Worker/FrontendErrorsWorker.cs +++ b/src/dotnet/src/HoldFast.Worker/FrontendErrorsWorker.cs @@ -1,7 +1,7 @@ using System.Text.Json; using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Shared.AlertEvaluation; using HoldFast.Shared.ErrorGrouping; using HoldFast.Shared.Kafka; diff --git a/src/dotnet/src/HoldFast.Worker/LogAlertWatcherWorker.cs b/src/dotnet/src/HoldFast.Worker/LogAlertWatcherWorker.cs index e24940d1..43bc76bb 100644 --- a/src/dotnet/src/HoldFast.Worker/LogAlertWatcherWorker.cs +++ b/src/dotnet/src/HoldFast.Worker/LogAlertWatcherWorker.cs @@ -1,6 +1,6 @@ using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Shared.Notifications; using Microsoft.EntityFrameworkCore; diff --git a/src/dotnet/src/HoldFast.Worker/LogIngestionWorker.cs b/src/dotnet/src/HoldFast.Worker/LogIngestionWorker.cs index 31866858..21e48669 100644 --- a/src/dotnet/src/HoldFast.Worker/LogIngestionWorker.cs +++ b/src/dotnet/src/HoldFast.Worker/LogIngestionWorker.cs @@ -1,5 +1,5 @@ using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Shared.Kafka; using HoldFast.Shared.Messaging; using Microsoft.Extensions.DependencyInjection; diff --git a/src/dotnet/src/HoldFast.Worker/MetricAlertWatcherWorker.cs b/src/dotnet/src/HoldFast.Worker/MetricAlertWatcherWorker.cs index 44375d5b..9b18b37f 100644 --- a/src/dotnet/src/HoldFast.Worker/MetricAlertWatcherWorker.cs +++ b/src/dotnet/src/HoldFast.Worker/MetricAlertWatcherWorker.cs @@ -1,6 +1,6 @@ using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Shared.Notifications; using Microsoft.EntityFrameworkCore; @@ -168,10 +168,10 @@ private async Task GetMetricValueAsync( // For other product types, use the metrics query var buckets = await clickHouse.ReadMetricsAsync( alert.ProjectId, - new HoldFast.Data.ClickHouse.Models.QueryInput + new HoldFast.Analytics.Models.QueryInput { Query = alert.Query ?? "", - DateRange = new HoldFast.Data.ClickHouse.Models.DateRangeRequiredInput { StartDate = startDate, EndDate = endDate }, + DateRange = new HoldFast.Analytics.Models.DateRangeRequiredInput { StartDate = startDate, EndDate = endDate }, }, bucketBy: "timestamp", groupBy: alert.GroupByKey != null ? [alert.GroupByKey] : null, diff --git a/src/dotnet/src/HoldFast.Worker/TraceIngestionWorker.cs b/src/dotnet/src/HoldFast.Worker/TraceIngestionWorker.cs index d6e13b7f..f9764fb3 100644 --- a/src/dotnet/src/HoldFast.Worker/TraceIngestionWorker.cs +++ b/src/dotnet/src/HoldFast.Worker/TraceIngestionWorker.cs @@ -1,5 +1,5 @@ using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Shared.Kafka; using HoldFast.Shared.Messaging; using Microsoft.Extensions.DependencyInjection; diff --git a/src/dotnet/tests/HoldFast.Api.Tests/ClickHouseHealthCheckTests.cs b/src/dotnet/tests/HoldFast.Api.Tests/ClickHouseHealthCheckTests.cs index acaf2b78..79a40c45 100644 --- a/src/dotnet/tests/HoldFast.Api.Tests/ClickHouseHealthCheckTests.cs +++ b/src/dotnet/tests/HoldFast.Api.Tests/ClickHouseHealthCheckTests.cs @@ -1,6 +1,6 @@ using HoldFast.Api; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using Microsoft.Extensions.Diagnostics.HealthChecks; using Xunit; diff --git a/src/dotnet/tests/HoldFast.GraphQL.Tests/AnalyticsQueryTests.cs b/src/dotnet/tests/HoldFast.GraphQL.Tests/AnalyticsQueryTests.cs index eb80a91a..0fde0fae 100644 --- a/src/dotnet/tests/HoldFast.GraphQL.Tests/AnalyticsQueryTests.cs +++ b/src/dotnet/tests/HoldFast.GraphQL.Tests/AnalyticsQueryTests.cs @@ -2,7 +2,7 @@ using System.Text; using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Domain.Enums; using HoldFast.GraphQL.Private; diff --git a/src/dotnet/tests/HoldFast.GraphQL.Tests/CompoundQueryTests.cs b/src/dotnet/tests/HoldFast.GraphQL.Tests/CompoundQueryTests.cs index 976b0a0f..2fbaa3c8 100644 --- a/src/dotnet/tests/HoldFast.GraphQL.Tests/CompoundQueryTests.cs +++ b/src/dotnet/tests/HoldFast.GraphQL.Tests/CompoundQueryTests.cs @@ -1,7 +1,7 @@ using System.Security.Claims; using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.GraphQL.Private; using HoldFast.Shared.Auth; diff --git a/src/dotnet/tests/HoldFast.GraphQL.Tests/EdgeCaseQueryTests.cs b/src/dotnet/tests/HoldFast.GraphQL.Tests/EdgeCaseQueryTests.cs index d84c3db8..ec44dd38 100644 --- a/src/dotnet/tests/HoldFast.GraphQL.Tests/EdgeCaseQueryTests.cs +++ b/src/dotnet/tests/HoldFast.GraphQL.Tests/EdgeCaseQueryTests.cs @@ -2,7 +2,7 @@ using System.Text; using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Domain.Enums; using HoldFast.GraphQL.Private; diff --git a/src/dotnet/tests/HoldFast.GraphQL.Tests/KeysAndMetricsQueryTests.cs b/src/dotnet/tests/HoldFast.GraphQL.Tests/KeysAndMetricsQueryTests.cs index a45daf4f..ee2c6789 100644 --- a/src/dotnet/tests/HoldFast.GraphQL.Tests/KeysAndMetricsQueryTests.cs +++ b/src/dotnet/tests/HoldFast.GraphQL.Tests/KeysAndMetricsQueryTests.cs @@ -2,7 +2,7 @@ using System.Text; using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Domain.Enums; using HoldFast.GraphQL.Private; diff --git a/src/dotnet/tests/HoldFast.GraphQL.Tests/NewPrivateQueryTests.cs b/src/dotnet/tests/HoldFast.GraphQL.Tests/NewPrivateQueryTests.cs index d48f9e0e..c767e510 100644 --- a/src/dotnet/tests/HoldFast.GraphQL.Tests/NewPrivateQueryTests.cs +++ b/src/dotnet/tests/HoldFast.GraphQL.Tests/NewPrivateQueryTests.cs @@ -2,7 +2,7 @@ using System.Text; using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Domain.Enums; using HoldFast.GraphQL.Private; diff --git a/src/dotnet/tests/HoldFast.GraphQL.Tests/PrivateQueryAnalyticsTests.cs b/src/dotnet/tests/HoldFast.GraphQL.Tests/PrivateQueryAnalyticsTests.cs index 5ff9e8bd..c525a95a 100644 --- a/src/dotnet/tests/HoldFast.GraphQL.Tests/PrivateQueryAnalyticsTests.cs +++ b/src/dotnet/tests/HoldFast.GraphQL.Tests/PrivateQueryAnalyticsTests.cs @@ -1,7 +1,7 @@ using System.Security.Claims; using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Domain.Enums; using HoldFast.GraphQL.Private; diff --git a/src/dotnet/tests/HoldFast.GraphQL.Tests/SessionAndAlertTests.cs b/src/dotnet/tests/HoldFast.GraphQL.Tests/SessionAndAlertTests.cs index 11540d84..e3f1b906 100644 --- a/src/dotnet/tests/HoldFast.GraphQL.Tests/SessionAndAlertTests.cs +++ b/src/dotnet/tests/HoldFast.GraphQL.Tests/SessionAndAlertTests.cs @@ -2,7 +2,7 @@ using System.Text; using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Domain.Enums; using HoldFast.GraphQL.Private; diff --git a/src/dotnet/tests/HoldFast.Shared.Tests/Analytics/MockableStoreTests.cs b/src/dotnet/tests/HoldFast.Shared.Tests/Analytics/MockableStoreTests.cs new file mode 100644 index 00000000..dac16f7a --- /dev/null +++ b/src/dotnet/tests/HoldFast.Shared.Tests/Analytics/MockableStoreTests.cs @@ -0,0 +1,150 @@ +using HoldFast.Analytics; +using HoldFast.Analytics.Models; + +namespace HoldFast.Shared.Tests.Analytics; + +/// +/// HOL-25 acceptance test: prove the new analytics interfaces are mockable +/// in unit tests without spinning up a ClickHouse container. +/// +/// Before HOL-25, IClickHouseService was a 25-method kitchen-sink — mocking +/// it required stubbing every method even when a test only cared about one. +/// The seven domain interfaces let a test depend on the smallest contract +/// it actually needs. +/// +/// These are *seam* tests — they verify the abstraction itself, not any +/// ClickHouse behavior. The full ClickHouse query logic is exercised by +/// the existing 3,000+ tests against IClickHouseService. +/// +public class MockableStoreTests +{ + [Fact] + public async Task FakeLogStore_can_substitute_for_real_implementation() + { + // Arrange — a fake ILogStore returning deterministic data. + // Implements only ILogStore (6 methods), not the 25-method IClickHouseService. + ILogStore store = new FakeLogStore(); + + // Act + var logs = await store.ReadLogsAsync( + projectId: 42, + new QueryInput { Query = "level:error" }, + new ClickHousePagination { Limit = 10 }); + + // Assert + Assert.Single(logs.Edges); + Assert.Equal("test-uuid", logs.Edges[0].Node.UUID); + Assert.Equal("ERROR", logs.Edges[0].Node.SeverityText); + Assert.False(logs.PageInfo.HasNextPage); + } + + [Fact] + public async Task FakeLogStore_records_writes_so_callers_can_assert() + { + // Arrange + var store = new FakeLogStore(); + var input = new LogRowInput + { + ProjectId = 42, + Timestamp = new DateTime(2026, 5, 9, 0, 0, 0, DateTimeKind.Utc), + Body = "hello world", + SeverityText = "INFO", + }; + + // Act + await store.WriteLogsAsync([input]); + + // Assert + Assert.Single(store.Written); + Assert.Equal("hello world", store.Written[0].Body); + Assert.Equal(42, store.Written[0].ProjectId); + } + + [Fact] + public void All_seven_domain_interfaces_compile_independently() + { + // Compile-time assertion: each interface stands on its own. + // If a future refactor accidentally merges two interfaces or pulls + // an unrelated method into one, this test fails to compile and + // catches the regression before it lands. + Assert.True(typeof(ILogStore).IsInterface); + Assert.True(typeof(ITraceStore).IsInterface); + Assert.True(typeof(ISessionAnalyticsStore).IsInterface); + Assert.True(typeof(IErrorAnalyticsStore).IsInterface); + Assert.True(typeof(IMetricStore).IsInterface); + Assert.True(typeof(IEventFieldStore).IsInterface); + Assert.True(typeof(IAlertStateStore).IsInterface); + } + + [Fact] + public void Domain_interfaces_live_in_HoldFast_Analytics_assembly() + { + // Guards against future refactors that might pull the abstractions + // back into a backend-specific assembly. The whole point of HOL-25 + // is that these interfaces sit in a project that does NOT depend on + // any client SDK (ClickHouse.Client, Npgsql, Dapper, …). + var asm = typeof(ILogStore).Assembly; + Assert.Equal("HoldFast.Analytics", asm.GetName().Name); + } + + /// + /// Minimal ILogStore stub: returns one synthetic log row, records writes, + /// throws NotImplementedException for the methods this test doesn't exercise. + /// + private sealed class FakeLogStore : ILogStore + { + public List Written { get; } = []; + + public Task ReadLogsAsync( + int projectId, QueryInput query, ClickHousePagination pagination, + CancellationToken ct = default) => + Task.FromResult(new LogConnection + { + Edges = + [ + new LogEdge + { + Node = new LogRow + { + UUID = "test-uuid", + ProjectId = projectId, + SeverityText = "ERROR", + Body = "synthetic", + Timestamp = DateTime.UtcNow, + }, + Cursor = "test-cursor", + }, + ], + PageInfo = new PageInfo + { + HasNextPage = false, + HasPreviousPage = false, + StartCursor = "test-cursor", + EndCursor = "test-cursor", + }, + }); + + public Task WriteLogsAsync(IEnumerable logs, CancellationToken ct = default) + { + Written.AddRange(logs); + return Task.CompletedTask; + } + + public Task> ReadLogsHistogramAsync( + int projectId, QueryInput query, CancellationToken ct = default) => + throw new NotImplementedException("not exercised by this test"); + + public Task> GetLogKeysAsync( + int projectId, QueryInput query, CancellationToken ct = default) => + throw new NotImplementedException("not exercised by this test"); + + public Task> GetLogKeyValuesAsync( + int projectId, string key, QueryInput query, CancellationToken ct = default) => + throw new NotImplementedException("not exercised by this test"); + + public Task CountLogsAsync( + int projectId, string? query, DateTime startDate, DateTime endDate, + CancellationToken ct = default) => + throw new NotImplementedException("not exercised by this test"); + } +} diff --git a/src/dotnet/tests/HoldFast.Shared.Tests/AnalyticsAndCommentTests.cs b/src/dotnet/tests/HoldFast.Shared.Tests/AnalyticsAndCommentTests.cs index 6e8e6c8e..7c365e56 100644 --- a/src/dotnet/tests/HoldFast.Shared.Tests/AnalyticsAndCommentTests.cs +++ b/src/dotnet/tests/HoldFast.Shared.Tests/AnalyticsAndCommentTests.cs @@ -1,7 +1,7 @@ using System.Security.Claims; using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Domain.Enums; using HoldFast.GraphQL.Private; diff --git a/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/ClickHouseModelsTests.cs b/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/ClickHouseModelsTests.cs index 3a6a0e5b..709a0ecc 100644 --- a/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/ClickHouseModelsTests.cs +++ b/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/ClickHouseModelsTests.cs @@ -1,5 +1,6 @@ +using HoldFast.Analytics; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Enums; namespace HoldFast.Shared.Tests.ClickHouse; diff --git a/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/ClickHouseQueryResolverTests.cs b/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/ClickHouseQueryResolverTests.cs index a3dfcad0..307ee261 100644 --- a/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/ClickHouseQueryResolverTests.cs +++ b/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/ClickHouseQueryResolverTests.cs @@ -1,7 +1,7 @@ using System.Security.Claims; using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Domain.Enums; using HoldFast.GraphQL.Private; // SortDirection, MetricExpressionInput, DateHistogramOptions diff --git a/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/CursorHelperTests.cs b/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/CursorHelperTests.cs index c8d6ab21..a0c26937 100644 --- a/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/CursorHelperTests.cs +++ b/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/CursorHelperTests.cs @@ -1,3 +1,4 @@ +using HoldFast.Analytics; using HoldFast.Data.ClickHouse; namespace HoldFast.Shared.Tests.ClickHouse; diff --git a/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/KeyDiscoveryTests.cs b/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/KeyDiscoveryTests.cs index dc85a05e..4c7a6fb5 100644 --- a/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/KeyDiscoveryTests.cs +++ b/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/KeyDiscoveryTests.cs @@ -1,5 +1,5 @@ using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using Xunit; namespace HoldFast.Shared.Tests.ClickHouse; diff --git a/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/PageInfoComputationTests.cs b/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/PageInfoComputationTests.cs index aff0bd26..0ea5e9e8 100644 --- a/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/PageInfoComputationTests.cs +++ b/src/dotnet/tests/HoldFast.Shared.Tests/ClickHouse/PageInfoComputationTests.cs @@ -1,5 +1,6 @@ +using HoldFast.Analytics; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; namespace HoldFast.Shared.Tests.ClickHouse; diff --git a/src/dotnet/tests/HoldFast.Shared.Tests/PrivateQueryExtendedTests.cs b/src/dotnet/tests/HoldFast.Shared.Tests/PrivateQueryExtendedTests.cs index d432d8ae..2d511813 100644 --- a/src/dotnet/tests/HoldFast.Shared.Tests/PrivateQueryExtendedTests.cs +++ b/src/dotnet/tests/HoldFast.Shared.Tests/PrivateQueryExtendedTests.cs @@ -1,7 +1,7 @@ using System.Security.Claims; using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.GraphQL.Private; using HoldFast.Shared.Auth; diff --git a/src/dotnet/tests/HoldFast.Worker.Tests/ConsumerPipelineTests.cs b/src/dotnet/tests/HoldFast.Worker.Tests/ConsumerPipelineTests.cs index 7ce9c7e8..9c8c7544 100644 --- a/src/dotnet/tests/HoldFast.Worker.Tests/ConsumerPipelineTests.cs +++ b/src/dotnet/tests/HoldFast.Worker.Tests/ConsumerPipelineTests.cs @@ -1,6 +1,6 @@ using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Shared.AlertEvaluation; using HoldFast.Shared.ErrorGrouping; diff --git a/src/dotnet/tests/HoldFast.Worker.Tests/ConsumerProcessAsyncTests.cs b/src/dotnet/tests/HoldFast.Worker.Tests/ConsumerProcessAsyncTests.cs index c5826fa2..c5b7e4f6 100644 --- a/src/dotnet/tests/HoldFast.Worker.Tests/ConsumerProcessAsyncTests.cs +++ b/src/dotnet/tests/HoldFast.Worker.Tests/ConsumerProcessAsyncTests.cs @@ -1,5 +1,5 @@ using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Worker; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; diff --git a/src/dotnet/tests/HoldFast.Worker.Tests/DataSyncWorkerTests.cs b/src/dotnet/tests/HoldFast.Worker.Tests/DataSyncWorkerTests.cs index ed79caa7..d7352f76 100644 --- a/src/dotnet/tests/HoldFast.Worker.Tests/DataSyncWorkerTests.cs +++ b/src/dotnet/tests/HoldFast.Worker.Tests/DataSyncWorkerTests.cs @@ -1,6 +1,6 @@ using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Domain.Enums; using HoldFast.Worker; diff --git a/src/dotnet/tests/HoldFast.Worker.Tests/LogAlertWatcherWorkerTests.cs b/src/dotnet/tests/HoldFast.Worker.Tests/LogAlertWatcherWorkerTests.cs index 0e455db9..39d926ea 100644 --- a/src/dotnet/tests/HoldFast.Worker.Tests/LogAlertWatcherWorkerTests.cs +++ b/src/dotnet/tests/HoldFast.Worker.Tests/LogAlertWatcherWorkerTests.cs @@ -1,6 +1,6 @@ using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Domain.Enums; using HoldFast.Shared.Notifications; diff --git a/src/dotnet/tests/HoldFast.Worker.Tests/MetricAlertWatcherWorkerTests.cs b/src/dotnet/tests/HoldFast.Worker.Tests/MetricAlertWatcherWorkerTests.cs index 509a81c6..1baa5bc5 100644 --- a/src/dotnet/tests/HoldFast.Worker.Tests/MetricAlertWatcherWorkerTests.cs +++ b/src/dotnet/tests/HoldFast.Worker.Tests/MetricAlertWatcherWorkerTests.cs @@ -1,6 +1,6 @@ using HoldFast.Data; using HoldFast.Data.ClickHouse; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Domain.Entities; using HoldFast.Domain.Enums; using HoldFast.Shared.Notifications; diff --git a/src/dotnet/tests/HoldFast.Worker.Tests/WorkerMessageTests.cs b/src/dotnet/tests/HoldFast.Worker.Tests/WorkerMessageTests.cs index 38ceaf79..f4e75380 100644 --- a/src/dotnet/tests/HoldFast.Worker.Tests/WorkerMessageTests.cs +++ b/src/dotnet/tests/HoldFast.Worker.Tests/WorkerMessageTests.cs @@ -1,5 +1,5 @@ using System.Text.Json; -using HoldFast.Data.ClickHouse.Models; +using HoldFast.Analytics.Models; using HoldFast.Worker; using Xunit;