diff --git a/src/AzureOpenAIProxy.ApiApp/Controllers/ManagementAccessCodeController.cs b/src/AzureOpenAIProxy.ApiApp/Controllers/ManagementAccessCodeController.cs
new file mode 100644
index 00000000..2752611b
--- /dev/null
+++ b/src/AzureOpenAIProxy.ApiApp/Controllers/ManagementAccessCodeController.cs
@@ -0,0 +1,165 @@
+using AzureOpenAIProxy.ApiApp.Models;
+
+using Microsoft.AspNetCore.Mvc;
+
+namespace AzureOpenAIProxy.ApiApp.Controllers;
+
+///
+/// This represents the controller entity for management.
+///
+public partial class ManagementController
+{
+ ///
+ /// Gets the list of access codes by event ID.
+ ///
+ /// API key.
+ /// Event ID.
+ /// Page number.
+ /// Page size.
+ /// Returns the instance.
+ [HttpGet("events/{eventId}/access-codes", Name = "GetListOfEventAccessCodes")]
+ public async Task GetEventAccessCodesByEventIdAsync(
+ [FromHeader(Name = "Authorization")] string apiKey,
+ [FromRoute] string eventId,
+ [FromQuery(Name = "page")] int? page = 0,
+ [FromQuery(Name = "size")] int? size = 20)
+ {
+ this._logger.LogInformation("Received a request to get the list of access codes for the given event");
+
+ var record = await this._auth.ValidateAsync(apiKey);
+ if (record == null)
+ {
+ this._logger.LogError("Invalid API key");
+
+ return new UnauthorizedResult();
+ }
+
+ if (string.IsNullOrWhiteSpace(eventId))
+ {
+ this._logger.LogError("No event ID");
+
+ return new NotFoundResult();
+ }
+
+ try
+ {
+ var result = await this._management.GetAccessCodesAsync(eventId, page, size);
+
+ this._logger.LogInformation("Completed the request to get the list of access codes for the given event");
+
+ return new OkObjectResult(result);
+ }
+ catch (Exception ex)
+ {
+ this._logger.LogError(ex, "Failed to get the list of access codes for the given event");
+
+ return new ObjectResult(ex.Message) { StatusCode = StatusCodes.Status500InternalServerError };
+ }
+ }
+
+ ///
+ /// Creates the access code.
+ ///
+ /// API key.
+ /// Event ID.
+ /// instance.
+ /// Returns the instance.
+ [HttpPost("events/{eventId}/access-codes", Name = "CreateEventAccessCode")]
+ public async Task CreateEventAccessCodeAsync(
+ [FromHeader(Name = "Authorization")] string apiKey,
+ [FromRoute] string eventId,
+ [FromBody] AccessCodeRequest req)
+ {
+ this._logger.LogInformation("Received a request to generate an access code");
+
+ var record = await this._auth.ValidateAsync(apiKey);
+ if (record == null)
+ {
+ this._logger.LogError("Invalid API key");
+
+ return new UnauthorizedResult();
+ }
+
+ if (string.IsNullOrWhiteSpace(eventId))
+ {
+ this._logger.LogError("No event ID");
+
+ return new NotFoundResult();
+ }
+
+ if (req == null)
+ {
+ this._logger.LogError("No request payload");
+
+ return new BadRequestResult();
+ }
+
+ try
+ {
+ var result = await this._management.CreateAccessCodeAsync(eventId, req);
+
+ this._logger.LogInformation("Completed the request to generate the access code");
+
+ return new OkObjectResult(result);
+ }
+ catch (Exception ex)
+ {
+ this._logger.LogError(ex, "Failed to generate the access code");
+
+ return new ObjectResult(ex.Message) { StatusCode = StatusCodes.Status500InternalServerError };
+ }
+ }
+
+ ///
+ /// Gets the access code by GitHub alias.
+ ///
+ /// API key.
+ /// Event ID.
+ /// GitHub alias.
+ /// Returns the instance.
+ [HttpGet("events/{eventId}/access-codes/{gitHubAlias}", Name = "GetEventAccessCodeByGitHubAlias")]
+ public async Task GetEventAccessCodeByGitHubAliasAsync(
+ [FromHeader(Name = "Authorization")] string apiKey,
+ [FromRoute] string eventId,
+ [FromRoute] string gitHubAlias)
+ {
+ this._logger.LogInformation("Received a request to get the access code belongs to the given GitHub alias");
+
+ var record = await this._auth.ValidateAsync(apiKey);
+ if (record == null)
+ {
+ this._logger.LogError("Invalid API key");
+
+ return new UnauthorizedResult();
+ }
+
+ if (string.IsNullOrWhiteSpace(eventId))
+ {
+ this._logger.LogError("No event ID");
+
+ return new NotFoundResult();
+ }
+
+ if (string.IsNullOrWhiteSpace(gitHubAlias))
+ {
+ this._logger.LogError("No GitHub alias");
+
+ return new NotFoundResult();
+ }
+
+ try
+ {
+ var result = await this._management.GetAccessCodeByGitHubAliasAsync(eventId, gitHubAlias);
+
+ this._logger.LogInformation("Completed the request to get the access code belongs to the given GitHub alias");
+
+ return new OkObjectResult(result);
+ }
+ catch (Exception ex)
+ {
+ this._logger.LogError(ex, "Failed to get the access code belongs to the given GitHub alias");
+
+ return new ObjectResult(ex.Message) { StatusCode = StatusCodes.Status500InternalServerError };
+ }
+ }
+}
diff --git a/src/AzureOpenAIProxy.ApiApp/Controllers/ManagementController.cs b/src/AzureOpenAIProxy.ApiApp/Controllers/ManagementEventController.cs
similarity index 64%
rename from src/AzureOpenAIProxy.ApiApp/Controllers/ManagementController.cs
rename to src/AzureOpenAIProxy.ApiApp/Controllers/ManagementEventController.cs
index 5093a5ac..7060dc2a 100644
--- a/src/AzureOpenAIProxy.ApiApp/Controllers/ManagementController.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Controllers/ManagementEventController.cs
@@ -13,7 +13,7 @@ namespace AzureOpenAIProxy.ApiApp.Controllers;
/// instance.
[ApiController]
[Route("api/management")]
-public class ManagementController(
+public partial class ManagementController(
[FromKeyedServices("management")] IAuthService auth,
IManagementService management,
ILogger logger) : ControllerBase
@@ -22,12 +22,18 @@ public class ManagementController(
private readonly IManagementService _management = management ?? throw new ArgumentNullException(nameof(management));
private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ ///
+ /// Gets the list of events.
+ ///
+ /// API key.
+ /// Page number.
+ /// Page size.
+ /// Returns the instance.
[HttpGet("events", Name = "GetListOfEvents")]
public async Task GetEventsAsync(
[FromHeader(Name = "Authorization")] string apiKey,
[FromQuery(Name = "page")] int? page = 0,
- [FromQuery(Name = "size")] int? size = 20
- )
+ [FromQuery(Name = "size")] int? size = 20)
{
this._logger.LogInformation("Received a request to get all events");
@@ -55,8 +61,14 @@ public async Task GetEventsAsync(
}
}
+ ///
+ /// Creates the event.
+ ///
+ /// API key.
+ /// instance.
+ /// Returns the instance.
[HttpPost("events", Name = "CreateEvent")]
- public async Task CreateEvent(
+ public async Task CreateEventAsync(
[FromHeader(Name = "Authorization")] string apiKey,
[FromBody] EventRequest req)
{
@@ -93,6 +105,12 @@ public async Task CreateEvent(
}
}
+ ///
+ /// Gets the event by ID.
+ ///
+ /// API key.
+ /// Event ID.
+ /// Returns the instance.
[HttpGet("events/{eventId}", Name = "GetEventById")]
public async Task GetEventByEventIdAsync(
[FromHeader(Name = "Authorization")] string apiKey,
@@ -130,72 +148,4 @@ public async Task GetEventByEventIdAsync(
return new ObjectResult(ex.Message) { StatusCode = StatusCodes.Status500InternalServerError };
}
}
-
- [HttpGet("events/{eventId}/access-codes", Name = "GetListOfEventAccessCodes")]
- public async Task GetAccessCodesByEventIdAsync(
- [FromHeader(Name = "Authorization")] string apiKey,
- [FromRoute] string eventId)
- {
- var result = new OkObjectResult("Pong");
-
- return await Task.FromResult(result);
- }
-
- [HttpPost("events/{eventId}/access-codes", Name = "CreateEventAccessCode")]
- public async Task CreateEvent(
- [FromHeader(Name = "Authorization")] string apiKey,
- [FromRoute] string eventId,
- [FromBody] AccessCodeRequest req)
- {
- this._logger.LogInformation("Received a request to generate an access code");
-
- var record = await this._auth.ValidateAsync(apiKey);
- if (record == null)
- {
- this._logger.LogError("Invalid API key");
-
- return new UnauthorizedResult();
- }
-
- if (string.IsNullOrWhiteSpace(eventId))
- {
- this._logger.LogError("No event ID");
-
- return new NotFoundResult();
- }
-
- if (req == null)
- {
- this._logger.LogError("No request payload");
-
- return new BadRequestResult();
- }
-
- try
- {
- req.EventId = eventId;
- var result = await this._management.CreateAccessCodeAsync(req);
-
- this._logger.LogInformation("Completed the request to generate the access code");
-
- return new OkObjectResult(result);
- }
- catch (Exception ex)
- {
- this._logger.LogError(ex, "Failed to generate the access code");
-
- return new ObjectResult(ex.Message) { StatusCode = StatusCodes.Status500InternalServerError };
- }
- }
-
- [HttpGet("events/{eventId}/access-codes/{gitHubAlias}", Name = "GetEventAccessCodeByGitHubAlias")]
- public async Task GetAccessCodesByEventIdAsync(
- [FromHeader(Name = "Authorization")] string apiKey,
- [FromRoute] string eventId,
- [FromRoute] string gitHubAlias)
- {
- var result = new OkObjectResult("Pong");
-
- return await Task.FromResult(result);
- }
}
diff --git a/src/AzureOpenAIProxy.ApiApp/Models/AccessCodeResponse.cs b/src/AzureOpenAIProxy.ApiApp/Models/AccessCodeResponse.cs
index e760a508..8aae7914 100644
--- a/src/AzureOpenAIProxy.ApiApp/Models/AccessCodeResponse.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Models/AccessCodeResponse.cs
@@ -5,7 +5,7 @@ namespace AzureOpenAIProxy.ApiApp.Models;
///
/// This represents the response entity for access code.
///
-public class AccessCodeResponse : AccessCodeRequest
+public class AccessCodeResponse : AccessCodeRequest, IEntityResponse
{
///
/// Initializes a new instance of the class.
@@ -48,4 +48,4 @@ public AccessCodeResponse(AccessCodeRecord record)
///
[JsonPropertyName("maxTokens")]
public int? MaxTokens { get; set; }
-}
\ No newline at end of file
+}
diff --git a/src/AzureOpenAIProxy.ApiApp/Models/AccessCodeResponseCollection.cs b/src/AzureOpenAIProxy.ApiApp/Models/AccessCodeResponseCollection.cs
new file mode 100644
index 00000000..35775a6c
--- /dev/null
+++ b/src/AzureOpenAIProxy.ApiApp/Models/AccessCodeResponseCollection.cs
@@ -0,0 +1,8 @@
+namespace AzureOpenAIProxy.ApiApp.Models;
+
+///
+/// This represents the response entity collection for access code.
+///
+public class AccessCodeResponseCollection : EntityResponseCollection
+{
+}
diff --git a/src/AzureOpenAIProxy.ApiApp/Models/EntityResponseCollection.cs b/src/AzureOpenAIProxy.ApiApp/Models/EntityResponseCollection.cs
new file mode 100644
index 00000000..ff0b91f8
--- /dev/null
+++ b/src/AzureOpenAIProxy.ApiApp/Models/EntityResponseCollection.cs
@@ -0,0 +1,39 @@
+namespace AzureOpenAIProxy.ApiApp.Models;
+
+///
+/// This provides interfaces to the response entity classes.
+///
+public interface IEntityResponse
+{
+ ///
+ /// Gets or sets the entity ID.
+ ///
+ string? Id { get; set; }
+}
+
+///
+/// This represents the response entity collection. This MUST be inherited.
+///
+/// Type of response
+public abstract class EntityResponseCollection where T : IEntityResponse
+{
+ ///
+ /// Gets or sets the current page number.
+ ///
+ public int? CurrentPage { get; set; }
+
+ ///
+ /// Gets or sets the page size.
+ ///
+ public int? PageSize { get; set; }
+
+ ///
+ /// Gets or sets the total number of items.
+ ///
+ public int? Total { get; set; }
+
+ ///
+ /// Gets or sets the list of instances.
+ ///
+ public List Items { get; set; } = [];
+}
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.ApiApp/Models/EventRecord.cs b/src/AzureOpenAIProxy.ApiApp/Models/EventRecord.cs
index 92cacd5e..b5e7fa20 100644
--- a/src/AzureOpenAIProxy.ApiApp/Models/EventRecord.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Models/EventRecord.cs
@@ -54,6 +54,11 @@ public class EventRecord : ITableEntity
///
public string? ApiKey { get; set; }
+ ///
+ /// Gets or sets the maximum number of tokens for the event. Defaults to 4096.
+ ///
+ public int? MaxTokens { get; set; } = 4096;
+
///
public DateTimeOffset? Timestamp { get; set; }
diff --git a/src/AzureOpenAIProxy.ApiApp/Models/EventResponse.cs b/src/AzureOpenAIProxy.ApiApp/Models/EventResponse.cs
index ce8bad82..0a292d83 100644
--- a/src/AzureOpenAIProxy.ApiApp/Models/EventResponse.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Models/EventResponse.cs
@@ -5,7 +5,7 @@ namespace AzureOpenAIProxy.ApiApp.Models;
///
/// This represents the response entity for event.
///
-public class EventResponse : EventRequest
+public class EventResponse : EventRequest, IEntityResponse
{
///
/// Initializes a new instance of the class.
diff --git a/src/AzureOpenAIProxy.ApiApp/Models/EventResponseCollection.cs b/src/AzureOpenAIProxy.ApiApp/Models/EventResponseCollection.cs
index 56384486..4ba30a58 100644
--- a/src/AzureOpenAIProxy.ApiApp/Models/EventResponseCollection.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Models/EventResponseCollection.cs
@@ -3,25 +3,6 @@
///
/// This represents the response entity collection for event.
///
-public class EventResponseCollection
+public class EventResponseCollection : EntityResponseCollection
{
- ///
- /// Gets or sets the current page number.
- ///
- public int? CurrentPage { get; set; }
-
- ///
- /// Gets or sets the page size.
- ///
- public int? PageSize { get; set; }
-
- ///
- /// Gets or sets the total number of items.
- ///
- public int? Total { get; set; }
-
- ///
- /// Gets or sets the list of instances.
- ///
- public List Items { get; set; } = [];
}
diff --git a/src/AzureOpenAIProxy.ApiApp/Services/ManagementAccessCodeService.cs b/src/AzureOpenAIProxy.ApiApp/Services/ManagementAccessCodeService.cs
new file mode 100644
index 00000000..53969184
--- /dev/null
+++ b/src/AzureOpenAIProxy.ApiApp/Services/ManagementAccessCodeService.cs
@@ -0,0 +1,116 @@
+using AzureOpenAIProxy.ApiApp.Models;
+
+namespace AzureOpenAIProxy.ApiApp.Services;
+
+///
+/// This provides interfaces to the class.
+///
+public partial interface IManagementService
+{
+ ///
+ /// Gets the list of access codes by event ID.
+ ///
+ /// Event ID.
+ /// Page number.
+ /// Page size.
+ /// Returns the instance.
+ Task GetAccessCodesAsync(string eventId, int? page = 0, int? size = 20);
+
+ ///
+ /// Creates the access code.
+ ///
+ /// Event ID.
+ /// instance.
+ /// Returns the instance.
+ Task CreateAccessCodeAsync(string eventId, AccessCodeRequest req);
+
+ ///
+ /// Gets the access code by GitHub alias.
+ ///
+ /// Event ID.
+ /// GitHub alias.
+ /// Returns the instance.
+ Task GetAccessCodeByGitHubAliasAsync(string eventId, string gitHubAlias);
+}
+
+///
+/// This represents the service entity for management.
+///
+public partial class ManagementService
+{
+ ///
+ public async Task GetAccessCodesAsync(string eventId, int? page = 0, int? size = 20)
+ {
+ if (string.IsNullOrWhiteSpace(eventId))
+ {
+ throw new ArgumentNullException(nameof(eventId));
+ }
+
+ var results = this._accessCodes.QueryAsync(p => p.PartitionKey
+ .Equals(eventId, StringComparison.InvariantCultureIgnoreCase));
+ var records = new List();
+ await foreach (var result in results.AsPages())
+ {
+ records.AddRange(result.Values.Select(p => new AccessCodeResponse(p)));
+ }
+
+ var skip = page.Value * size.Value;
+ var take = size.Value;
+
+ var response = new AccessCodeResponseCollection()
+ {
+ CurrentPage = page,
+ PageSize = size,
+ Total = records.Count,
+ Items = records.Skip(skip).Take(take).ToList(),
+ };
+
+ return response;
+ }
+
+ ///
+ public async Task CreateAccessCodeAsync(string eventId, AccessCodeRequest req)
+ {
+ var @event = await this._managements
+ .GetEntityIfExistsAsync(
+ ManagementsTablePartitionKey,
+ eventId ?? throw new ArgumentNullException(nameof(eventId)))
+ .ConfigureAwait(false)
+ ?? throw new KeyNotFoundException($"Event ID not found: {eventId}");
+
+ var accessCodeId = Guid.NewGuid().ToString();
+ var apiKey = Guid.NewGuid().ToString();
+ var record = new AccessCodeRecord()
+ {
+ PartitionKey = eventId,
+ RowKey = req.GitHubAlias,
+ AccessCodeId = accessCodeId,
+ EventId = eventId,
+ Name = req.Name,
+ Email = req.Email,
+ GitHubAlias = req.GitHubAlias,
+ ApiKey = apiKey,
+ MaxTokens = @event?.Value?.MaxTokens ?? DefaultMaxTokens,
+ EventDateStart = @event?.Value?.EventDateStart,
+ EventDateEnd = @event?.Value?.EventDateEnd,
+ DateCreated = DateTimeOffset.UtcNow,
+ };
+
+ await this._accessCodes.UpsertEntityAsync(record).ConfigureAwait(false);
+ var result = await this._accessCodes.GetEntityIfExistsAsync(eventId, req.GitHubAlias).ConfigureAwait(false);
+
+ return new AccessCodeResponse(result.Value);
+ }
+
+ ///
+ public async Task GetAccessCodeByGitHubAliasAsync(string eventId, string gitHubAlias)
+ {
+ var result = await this._accessCodes.GetEntityIfExistsAsync(
+ eventId ?? throw new ArgumentNullException(nameof(eventId)),
+ gitHubAlias ?? throw new ArgumentNullException(nameof(gitHubAlias)))
+ .ConfigureAwait(false)
+ ?? throw new KeyNotFoundException($"Either Event ID: {eventId} or GitHub alias: {gitHubAlias} not found");
+
+ return new AccessCodeResponse(result.Value);
+ }
+}
diff --git a/src/AzureOpenAIProxy.ApiApp/Services/ManagementService.cs b/src/AzureOpenAIProxy.ApiApp/Services/ManagementEventService.cs
similarity index 72%
rename from src/AzureOpenAIProxy.ApiApp/Services/ManagementService.cs
rename to src/AzureOpenAIProxy.ApiApp/Services/ManagementEventService.cs
index 1e5923a1..9d62700d 100644
--- a/src/AzureOpenAIProxy.ApiApp/Services/ManagementService.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Services/ManagementEventService.cs
@@ -7,7 +7,7 @@ namespace AzureOpenAIProxy.ApiApp.Services;
///
/// This provides interfaces to the class.
///
-public interface IManagementService
+public partial interface IManagementService
{
///
/// Gets the list of events.
@@ -25,18 +25,11 @@ public interface IManagementService
Task CreateEventAsync(EventRequest req);
///
- /// Get the event by ID.
+ /// Gets the event by ID.
///
/// Event ID.
/// Returns the instance.
Task GetEvenByIdAsync(string eventId);
-
- ///
- /// Creates the access code.
- ///
- /// instance.
- /// Returns the instance.
- Task CreateAccessCodeAsync(AccessCodeRequest req);
}
///
@@ -44,11 +37,12 @@ public interface IManagementService
///
/// instance.
/// instance.
-public class ManagementService(TableServiceClient client, ILogger logger) : IManagementService
+public partial class ManagementService(TableServiceClient client, ILogger logger) : IManagementService
{
private const string AccessCodesTableName = "accesscodes";
private const string ManagementsTableName = "managements";
private const string ManagementsTablePartitionKey = "management";
+ private const int DefaultMaxTokens = 4096;
private readonly TableClient _accessCodes = client?.GetTableClient(AccessCodesTableName) ?? throw new ArgumentNullException(nameof(client));
private readonly TableClient _managements = client?.GetTableClient(ManagementsTableName) ?? throw new ArgumentNullException(nameof(client));
@@ -96,6 +90,7 @@ public async Task CreateEventAsync(EventRequest req)
EventDateStart = req.EventDateStart.Value.ToUniversalTime(),
EventDateEnd = req.EventDateEnd.Value.ToUniversalTime(),
ApiKey = apiKey,
+ MaxTokens = DefaultMaxTokens,
};
await this._managements.UpsertEntityAsync(record).ConfigureAwait(false);
@@ -110,37 +105,9 @@ public async Task GetEvenByIdAsync(string eventId)
var result = await this._managements.GetEntityIfExistsAsync(
ManagementsTablePartitionKey,
eventId ?? throw new ArgumentNullException(nameof(eventId)))
- .ConfigureAwait(false);
+ .ConfigureAwait(false)
+ ?? throw new KeyNotFoundException($"Event ID not found: {eventId}");
return new EventResponse(result.Value);
}
-
- ///
- public async Task CreateAccessCodeAsync(AccessCodeRequest req)
- {
- var @event = await this._managements
- .GetEntityIfExistsAsync(ManagementsTablePartitionKey, req.EventId)
- .ConfigureAwait(false)
- ?? throw new KeyNotFoundException($"Event ID not found: {req.EventId}");
-
- var apiKey = Guid.NewGuid().ToString();
- var record = new AccessCodeRecord()
- {
- PartitionKey = req.EventId,
- RowKey = req.GitHubAlias,
- EventId = req.EventId,
- Name = req.Name,
- Email = req.Email,
- GitHubAlias = req.GitHubAlias,
- ApiKey = apiKey,
- EventDateStart = @event?.Value?.EventDateStart,
- EventDateEnd = @event?.Value?.EventDateEnd,
- DateCreated = DateTimeOffset.UtcNow,
- };
-
- await this._accessCodes.UpsertEntityAsync(record).ConfigureAwait(false);
- var result = await this._accessCodes.GetEntityIfExistsAsync(req.EventId, apiKey).ConfigureAwait(false);
-
- return new AccessCodeResponse(result.Value);
- }
}