ironservices-dotnet/NotifyApi.cs

280 lines
8.9 KiB
C#

namespace IronServices.Client;
/// <summary>
/// API client for IronNotify operations.
/// Access via IronServicesClient.Notify
/// </summary>
public class NotifyApi
{
private readonly IronServicesClient _client;
private readonly string _baseUrl;
internal NotifyApi(IronServicesClient client, string baseUrl)
{
_client = client;
_baseUrl = baseUrl;
}
private string Url(string endpoint) => IronServicesClient.BuildUrl(_baseUrl, endpoint);
#region Notifications
/// <summary>
/// Get notifications for the current user.
/// </summary>
public async Task<List<NotificationDto>> GetNotificationsAsync(string? appSlug = null, bool unreadOnly = false, int page = 1, int pageSize = 50, CancellationToken ct = default)
{
var query = new List<string> { $"page={page}", $"pageSize={pageSize}" };
if (unreadOnly) query.Add("isRead=false");
if (!string.IsNullOrEmpty(appSlug)) query.Add($"type={appSlug}");
var queryString = "?" + string.Join("&", query);
var result = await _client.GetAsync<NotificationsResponse>(Url($"api/v1/notifications{queryString}"), ct);
return result?.Notifications ?? [];
}
/// <summary>
/// Get unread notification count.
/// </summary>
public async Task<int> GetUnreadCountAsync(CancellationToken ct = default)
{
var result = await _client.GetAsync<UnreadCountResponse>(Url("api/v1/notifications/unread-count"), ct);
return result?.Count ?? result?.UnreadCount ?? 0;
}
/// <summary>
/// Mark a notification as read.
/// </summary>
public async Task MarkAsReadAsync(Guid notificationId, CancellationToken ct = default)
{
await _client.PutAsync<object, object>(Url($"api/v1/notifications/{notificationId}/read"), new { }, ct);
}
/// <summary>
/// Mark all notifications as read.
/// </summary>
public async Task MarkAllAsReadAsync(CancellationToken ct = default)
{
await _client.PutAsync<object, object>(Url("api/v1/notifications/read-all"), new { }, ct);
}
/// <summary>
/// Acknowledge a notification (marks as read).
/// </summary>
public async Task AcknowledgeAsync(Guid notificationId, CancellationToken ct = default)
{
await MarkAsReadAsync(notificationId, ct);
}
/// <summary>
/// Get a specific notification by ID.
/// </summary>
public async Task<NotificationDto?> GetNotificationAsync(Guid notificationId, CancellationToken ct = default)
{
return await _client.GetAsync<NotificationDto>(Url($"api/v1/notifications/{notificationId}"), ct);
}
/// <summary>
/// Resolve a notification.
/// </summary>
public async Task ResolveAsync(Guid notificationId, string? resolution = null, CancellationToken ct = default)
{
await _client.PostAsync<object, object>(Url($"api/v1/notifications/{notificationId}/resolve"), new { resolution }, ct);
}
/// <summary>
/// Get notification preferences.
/// </summary>
public async Task<NotificationPreferences?> GetNotificationPreferencesAsync(CancellationToken ct = default)
{
return await _client.GetAsync<NotificationPreferences>(Url("api/v1/notifications/preferences"), ct);
}
/// <summary>
/// Update notification preferences.
/// </summary>
public async Task UpdateNotificationPreferencesAsync(NotificationPreferences prefs, CancellationToken ct = default)
{
await _client.PutAsync<NotificationPreferences, object>(Url("api/v1/notifications/preferences"), prefs, ct);
}
#endregion
#region Apps
/// <summary>
/// Get all apps for the tenant.
/// </summary>
public async Task<List<AppSubscription>> GetMyAppsAsync(CancellationToken ct = default)
{
return await _client.GetAsync<List<AppSubscription>>(Url("api/v1/apps"), ct) ?? [];
}
/// <summary>
/// Get a specific app by ID.
/// </summary>
public async Task<AppSubscription?> GetAppAsync(Guid appId, CancellationToken ct = default)
{
return await _client.GetAsync<AppSubscription>(Url($"api/v1/apps/{appId}"), ct);
}
/// <summary>
/// Get an app by slug.
/// </summary>
public async Task<AppSubscription?> GetAppBySlugAsync(string slug, CancellationToken ct = default)
{
return await _client.GetAsync<AppSubscription>(Url($"api/v1/apps/by-slug/{slug}"), ct);
}
#endregion
#region Members & On-Call
/// <summary>
/// Get all team members.
/// </summary>
public async Task<List<TeamMember>> GetTeamMembersAsync(CancellationToken ct = default)
{
return await _client.GetAsync<List<TeamMember>>(Url("api/v1/members"), ct) ?? [];
}
/// <summary>
/// Get current on-call member.
/// </summary>
public async Task<TeamMember?> GetOnCallMemberAsync(Guid? teamId = null, CancellationToken ct = default)
{
var query = teamId.HasValue ? $"?teamId={teamId}" : "";
return await _client.GetAsync<TeamMember>(Url($"api/v1/members/on-call{query}"), ct);
}
/// <summary>
/// Get on-call status for the current user.
/// </summary>
public async Task<OnCallStatus?> GetOnCallStatusAsync(CancellationToken ct = default)
{
var member = await GetOnCallMemberAsync(ct: ct);
if (member == null) return new OnCallStatus { IsOnCall = false };
return new OnCallStatus
{
IsOnCall = true,
OnCallUserName = member.Name,
OnCallUserEmail = member.Email
};
}
/// <summary>
/// Set on-call status for a member.
/// </summary>
public async Task<TeamMember?> SetOnCallAsync(Guid memberId, bool isOnCall, CancellationToken ct = default)
{
return await _client.PostAsync<object, TeamMember>(Url($"api/v1/members/{memberId}/on-call"), new { isOnCall }, ct);
}
/// <summary>
/// Take on-call shift for current user.
/// </summary>
public async Task TakeOnCallAsync(CancellationToken ct = default)
{
// This would need the current user's member ID - placeholder for now
await Task.CompletedTask;
}
/// <summary>
/// End on-call shift for current user.
/// </summary>
public async Task EndOnCallAsync(CancellationToken ct = default)
{
// This would need the current user's member ID - placeholder for now
await Task.CompletedTask;
}
/// <summary>
/// Request coverage from another team member.
/// </summary>
public async Task RequestCoverageAsync(Guid memberId, CancellationToken ct = default)
{
// Placeholder - would send notification to member
await Task.CompletedTask;
}
/// <summary>
/// Get today's on-call schedule.
/// </summary>
public async Task<List<ScheduleSlot>> GetScheduleAsync(CancellationToken ct = default)
{
// Placeholder - schedule endpoint not implemented in backend
return await Task.FromResult(new List<ScheduleSlot>());
}
#endregion
#region App Muting
/// <summary>
/// Mute notifications from an app.
/// </summary>
public async Task MuteAppAsync(string appSlug, int? minutes = null, CancellationToken ct = default)
{
// Placeholder - mute endpoint not implemented in backend
await Task.CompletedTask;
}
/// <summary>
/// Unmute notifications from an app.
/// </summary>
public async Task UnmuteAppAsync(string appSlug, CancellationToken ct = default)
{
// Placeholder - unmute endpoint not implemented in backend
await Task.CompletedTask;
}
#endregion
#region Teams
/// <summary>
/// Get all teams.
/// </summary>
public async Task<List<TeamInfo>> GetTeamsAsync(CancellationToken ct = default)
{
return await _client.GetAsync<List<TeamInfo>>(Url("api/v1/teams"), ct) ?? [];
}
/// <summary>
/// Get a specific team.
/// </summary>
public async Task<TeamInfo?> GetTeamAsync(Guid teamId, CancellationToken ct = default)
{
return await _client.GetAsync<TeamInfo>(Url($"api/v1/teams/{teamId}"), ct);
}
/// <summary>
/// Get members of a specific team.
/// </summary>
public async Task<List<TeamMember>> GetTeamMembersAsync(Guid teamId, CancellationToken ct = default)
{
return await _client.GetAsync<List<TeamMember>>(Url($"api/v1/teams/{teamId}/members"), ct) ?? [];
}
#endregion
}
public class NotificationsResponse
{
public List<NotificationDto> Notifications { get; set; } = [];
public int TotalCount { get; set; }
public int UnreadCount { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
}
public class TeamInfo
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public string? Description { get; set; }
public int MemberCount { get; set; }
public DateTime CreatedAt { get; set; }
}