# Azure Data Explorer

## Description

Azure Data Explorer (Kusto) can be used as the backing implementation for Flex time-series operations. Your application code should depend on `IFlexTimeSeriesStore`, while Flex provides the provider bridge and wiring.

## Important concepts

* **`IFlexTimeSeriesStore` is the contract**: app code writes and queries time-series data via a shared interface.
* **Write + query patterns**: generated handlers typically call `WriteAsync<TPoint>(point)`, and generated queries use `GetLatestAsync<TPoint>(seriesId)` / `QueryAsync<TPoint>(seriesId, start, end)`.
* **Provider bridge**: the Azure Data Explorer implementation is exposed via an `IFlexTimeSeriesStoreBridge` internally, but most consumers only need `IFlexTimeSeriesStore`.

## Configuration in DI

Add the provider in your DI composition root (commonly in `EndPoints/...CommonConfigs/OtherApplicationServicesConfig.cs` or wherever you centralize registrations).

```csharp
// using Sumeru.Flex; // IFlexTimeSeriesStore

public static class OtherApplicationServicesConfig
{
	public static IServiceCollection AddOtherApplicationServices(
		this IServiceCollection services,
		IConfiguration configuration)
	{
		// Registers Azure Data Explorer as the IFlexTimeSeriesStore bridge.
		// Flex auto-wires generated Queries/Handlers that *use* IFlexTimeSeriesStore.
		services.AddFlexAzureDataExplorerTimeSeriesStore(configuration);

		return services;
	}
}
```

## appsettings.json

Configuration is read from `FlexBase:DataStores:TimeSeries:AzureDataExplorer`.

```json
{
  "FlexBase": {
	"DataStores": {
	  "TimeSeries": {
		"AzureDataExplorer": {
		  "ClusterUri": "https://yourcluster.region.kusto.windows.net",
		  "Database": "your-database",
		  "ClientId": null,
		  "ClientSecret": null,
		  "TenantId": null
		}
	  }
	}
  }
}
```

## Examples (template-based)

These examples mirror the generated Query and PostBusHandler templates. You do **not** register these types manually—Flex discovers and wires generated Queries/Handlers automatically.

### Write a data point (PostBusHandler)

```csharp
using Microsoft.Extensions.Logging;
using Sumeru.Flex;
using System;
using System.Threading.Tasks;

namespace {YourApplication}.Handlers.TimeSeries;

public partial class WriteTelemetryPointHandler : IAmFlexCommandHandler<WriteTelemetryPointCommand>
{
	protected string EventCondition = "";

	protected readonly ILogger<WriteTelemetryPointHandler> _logger;
	protected readonly IFlexHost _flexHost;
	protected readonly IFlexTimeSeriesStore _timeSeriesStore;

	protected TelemetryPoint? _model;
	protected FlexAppContextBridge? _flexAppContext;

	public WriteTelemetryPointHandler(
		ILogger<WriteTelemetryPointHandler> logger,
		IFlexHost flexHost,
		IFlexTimeSeriesStore timeSeriesStore)
	{
		_logger = logger;
		_flexHost = flexHost;
		_timeSeriesStore = timeSeriesStore;
	}

	public virtual async Task Execute(WriteTelemetryPointCommand cmd, IFlexServiceBusContext serviceBusContext)
	{
		_logger.LogDebug("Starting Write for {EntityType}", nameof(TelemetryPoint));
		_flexAppContext = cmd.Dto.GetAppContext();  //do not remove this line

		_model = _flexHost.GetDomainModel<TelemetryPoint>().Create(cmd);
		await _timeSeriesStore.WriteAsync<TelemetryPoint>(_model);

		await this.Fire(EventCondition, serviceBusContext);
	}
}

public class TelemetryPoint
{
	public string SeriesId { get; set; }
	public DateTime Timestamp { get; set; }
}
```

### Get the latest point (Query)

```csharp
using Microsoft.Extensions.Logging;
using Sumeru.Flex;
using System.Threading.Tasks;

namespace {YourApplication}.Queries.TimeSeries;

public class TelemetryGetLatest : FlexiQueryBridgeAsync<TelemetryPointDto>
{
	protected readonly ILogger<TelemetryGetLatest> _logger;
	protected readonly IFlexHost _flexHost;
	protected readonly IFlexTimeSeriesStore _timeSeriesStore;
	protected TelemetryGetLatestParams _params;
	protected FlexAppContextBridge _flexAppContext;

	public TelemetryGetLatest(ILogger<TelemetryGetLatest> logger, IFlexHost flexHost, IFlexTimeSeriesStore timeSeriesStore)
	{
		_logger = logger;
		_flexHost = flexHost;
		_timeSeriesStore = timeSeriesStore;
	}

	public virtual TelemetryGetLatest AssignParameters(TelemetryGetLatestParams @params)
	{
		_params = @params;
		return this;
	}

	public virtual async Task<TelemetryPointDto?> Fetch()
	{
		_flexAppContext = _params.GetAppContext();
		var latest = await _timeSeriesStore.GetLatestAsync<TelemetryPoint>(_params.SeriesId);
		// TODO: map TelemetryPoint -> TelemetryPointDto
		return latest as TelemetryPointDto;
	}
}

public class TelemetryGetLatestParams : DtoBridge
{
	public string SeriesId { get; set; }
}
```

## Azure Data Explorer considerations

* `ClusterUri` and `Database` are required for cluster mode.
* If you need custom authentication behavior (managed identity vs app registration), handle it via environment/Azure identity configuration or by overriding the generated bridge in your application.
