Skip to content

Commit

Permalink
Add endpoint for event management (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
justinyoo authored Dec 5, 2023
1 parent 35c7d82 commit e89c796
Show file tree
Hide file tree
Showing 13 changed files with 290 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace AzureOpenAIProxy.ApiApp.Controllers;
/// <param name="openai"><see cref="IOpenAIService"/> instance.</param>
/// <param name="logger"><see cref="ILogger{TCategoryName}"/> instance.</param>
[ApiController]
[Route("openai")]
[Route("api/openai")]
public class ChatCompletionsController(
[FromKeyedServices("accesscode")] IAuthService<AccessCodeRecord> auth,
IOpenAIService openai,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace AzureOpenAIProxy.ApiApp.Controllers;
/// <param name="openai"><see cref="IOpenAIService"/> instance.</param>
/// <param name="logger"><see cref="ILogger{TCategoryName}"/> instance.</param>
[ApiController]
[Route("openai")]
[Route("api/openai")]
public class CompletionsController(
[FromKeyedServices("accesscode")] IAuthService<AccessCodeRecord> auth,
IOpenAIService openai,
Expand Down
130 changes: 118 additions & 12 deletions src/AzureOpenAIProxy.ApiApp/Controllers/ManagementController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,42 +12,123 @@ namespace AzureOpenAIProxy.ApiApp.Controllers;
/// <param name="management"><see cref="IManagementService"/> instance.</param>
/// <param name="logger"><see cref="ILogger{TCategoryName}"/> instance.</param>
[ApiController]
[Route("management")]
[Route("api/management")]
public class ManagementController(
[FromKeyedServices("management")] IAuthService<ManagementRecord> auth,
[FromKeyedServices("management")] IAuthService<EventRecord> auth,
IManagementService management,
ILogger<ManagementController> logger) : ControllerBase
{
private readonly IAuthService<ManagementRecord> _auth = auth ?? throw new ArgumentNullException(nameof(auth));
private readonly IAuthService<EventRecord> _auth = auth ?? throw new ArgumentNullException(nameof(auth));
private readonly IManagementService _management = management ?? throw new ArgumentNullException(nameof(management));
private readonly ILogger<ManagementController> _logger = logger ?? throw new ArgumentNullException(nameof(logger));

[HttpGet("events", Name = "GetListOfEvents")]
public async Task<IActionResult> GetEventsAsync([FromHeader(Name = "Authorization")] string apiKey)
public async Task<IActionResult> GetEventsAsync(
[FromHeader(Name = "Authorization")] string apiKey,
[FromQuery(Name = "page")] int? page = 0,
[FromQuery(Name = "size")] int? size = 20
)
{
var result = new OkObjectResult("Pong");
this._logger.LogInformation("Received a request to get all events");

return await Task.FromResult(result);
var record = await this._auth.ValidateAsync(apiKey);
if (record == null)
{
this._logger.LogError("Invalid API key");

return new UnauthorizedResult();
}

try
{
var result = await this._management.GetEventsAsync(page, size);

this._logger.LogInformation("Completed the request to get all event");

return new OkObjectResult(result);
}
catch (Exception ex)
{
this._logger.LogError(ex, "Failed to get all event");

return new ObjectResult(ex.Message) { StatusCode = StatusCodes.Status500InternalServerError };
}
}

[HttpPost("events", Name = "CreateEvent")]
public async Task<IActionResult> CreateEvent(
[FromHeader(Name = "Authorization")] string apiKey,
[FromBody] ManagementRequest req)
[FromBody] EventRequest req)
{
var result = new OkObjectResult("Pong");
this._logger.LogInformation("Received a request to generate an event");

return await Task.FromResult(result);
var record = await this._auth.ValidateAsync(apiKey);
if (record == null)
{
this._logger.LogError("Invalid API key");

return new UnauthorizedResult();
}

if (req == null)
{
this._logger.LogError("No request payload");

return new BadRequestResult();
}

try
{
var result = await this._management.CreateEventAsync(req);

this._logger.LogInformation("Completed the request to generate the event");

return new OkObjectResult(result);
}
catch (Exception ex)
{
this._logger.LogError(ex, "Failed to generate the event");

return new ObjectResult(ex.Message) { StatusCode = StatusCodes.Status500InternalServerError };
}
}

[HttpGet("events/{eventId}", Name = "GetEventById")]
public async Task<IActionResult> GetEventByEventIdAsync(
[FromHeader(Name = "Authorization")] string apiKey,
[FromRoute] string eventId)
{
var result = new OkObjectResult("Pong");
this._logger.LogInformation("Received a request to get an event details by event ID");

return await Task.FromResult(result);
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.GetEvenByIdAsync(eventId);

this._logger.LogInformation("Completed the request to get an event details by event ID");

return new OkObjectResult(result);
}
catch (Exception ex)
{
this._logger.LogError(ex, "Failed to get an event details by event ID");

return new ObjectResult(ex.Message) { StatusCode = StatusCodes.Status500InternalServerError };
}
}

[HttpGet("events/{eventId}/access-codes", Name = "GetListOfEventAccessCodes")]
Expand All @@ -62,8 +143,8 @@ public async Task<IActionResult> GetAccessCodesByEventIdAsync(

[HttpPost("events/{eventId}/access-codes", Name = "CreateEventAccessCode")]
public async Task<IActionResult> CreateEvent(
[FromRoute] string eventId,
[FromHeader(Name = "Authorization")] string apiKey,
[FromRoute] string eventId,
[FromBody] AccessCodeRequest req)
{
this._logger.LogInformation("Received a request to generate an access code");
Expand All @@ -76,6 +157,20 @@ public async Task<IActionResult> CreateEvent(
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;
Expand All @@ -92,4 +187,15 @@ public async Task<IActionResult> CreateEvent(
return new ObjectResult(ex.Message) { StatusCode = StatusCodes.Status500InternalServerError };
}
}

[HttpGet("events/{eventId}/access-codes/{gitHubAlias}", Name = "GetEventAccessCodeByGitHubAlias")]
public async Task<IActionResult> GetAccessCodesByEventIdAsync(
[FromHeader(Name = "Authorization")] string apiKey,
[FromRoute] string eventId,
[FromRoute] string gitHubAlias)
{
var result = new OkObjectResult("Pong");

return await Task.FromResult(result);
}
}
6 changes: 3 additions & 3 deletions src/AzureOpenAIProxy.ApiApp/Models/AccessCodeRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@ public class AccessCodeRecord : ITableEntity
public string? ApiKey { get; set; }

/// <summary>
/// Gets or sets the date created.
/// Gets or sets the date created in UTC.
/// </summary>
public DateTimeOffset? DateCreated { get; set; }

/// <summary>
/// Gets or sets the event date start.
/// Gets or sets the event date start in UTC.
/// </summary>
public DateTimeOffset? EventDateStart { get; set; }

/// <summary>
/// Gets or sets the event date end.
/// Gets or sets the event date end in UTC.
/// </summary>
public DateTimeOffset? EventDateEnd { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
namespace AzureOpenAIProxy.ApiApp.Models;

/// <summary>
/// This represents the table entity for management.
/// This represents the table entity for event.
/// </summary>
public class ManagementRecord : ITableEntity
public class EventRecord : ITableEntity
{
/// <inheritdoc />
public string? PartitionKey { get; set; }
Expand All @@ -15,7 +15,7 @@ public class ManagementRecord : ITableEntity
public string? RowKey { get; set; }

/// <summary>
/// Gets or sets the event ID. It's equivalent to <see cref="ManagementRecord.RowKey"/>.
/// Gets or sets the event ID. It's equivalent to <see cref="EventRecord.RowKey"/>.
/// </summary>
public string? EventId { get; set; }

Expand All @@ -40,12 +40,12 @@ public class ManagementRecord : ITableEntity
public string? EventOrganiserEmail { get; set; }

/// <summary>
/// Gets or sets the event start date.
/// Gets or sets the event start date in UTC.
/// </summary>
public DateTimeOffset? EventDateStart { get; set; } = DateTimeOffset.MinValue;

/// <summary>
/// Gets or sets the event end date.
/// Gets or sets the event end date in UTC.
/// </summary>
public DateTimeOffset? EventDateEnd { get; set; } = DateTimeOffset.MaxValue;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
namespace AzureOpenAIProxy.ApiApp.Models;

/// <summary>
/// This represents the request entity for management.
/// This represents the request entity for event.
/// </summary>
public class ManagementRequest
public class EventRequest
{
/// <summary>
/// Gets or sets the event name.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@
namespace AzureOpenAIProxy.ApiApp.Models;

/// <summary>
/// This represents the response entity for management.
/// This represents the response entity for event.
/// </summary>
public class ManagementResponse : ManagementRequest
public class EventResponse : EventRequest
{
/// <summary>
/// Initializes a new instance of the <see cref="ManagementResponse"/> class.
/// Initializes a new instance of the <see cref="EventResponse"/> class.
/// </summary>
public ManagementResponse()
public EventResponse()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="ManagementResponse"/> class.
/// Initializes a new instance of the <see cref="EventResponse"/> class.
/// </summary>
/// <param name="record"><see cref="ManagementRecord"/> instance.</param>
public ManagementResponse(ManagementRecord record)
/// <param name="record"><see cref="EventRecord"/> instance.</param>
public EventResponse(EventRecord record)
{
this.Id = record.EventId;
this.AccessCode = record.ApiKey;
Expand Down
27 changes: 27 additions & 0 deletions src/AzureOpenAIProxy.ApiApp/Models/EventResponseCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace AzureOpenAIProxy.ApiApp.Models;

/// <summary>
/// This represents the response entity collection for event.
/// </summary>
public class EventResponseCollection
{
/// <summary>
/// Gets or sets the current page number.
/// </summary>
public int? CurrentPage { get; set; }

/// <summary>
/// Gets or sets the page size.
/// </summary>
public int? PageSize { get; set; }

/// <summary>
/// Gets or sets the total number of items.
/// </summary>
public int? Total { get; set; }

/// <summary>
/// Gets or sets the list of <see cref="EventResponse"/> instances.
/// </summary>
public List<EventResponse> Items { get; set; } = [];
}
3 changes: 2 additions & 1 deletion src/AzureOpenAIProxy.ApiApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
// Add services to the container.
builder.Services.AddSingleton<AoaiSettings>(p => p.GetService<IConfiguration>().GetSection(AoaiSettings.Name).Get<AoaiSettings>());
builder.Services.AddHttpClient<IOpenAIService, OpenAIService>();
builder.Services.AddScoped<IManagementService, ManagementService>();
builder.Services.AddKeyedScoped<IAuthService<AccessCodeRecord>, UserAuthService>("accesscode");
builder.Services.AddKeyedScoped<IAuthService<ManagementRecord>, ManagementAuthService>("management");
builder.Services.AddKeyedScoped<IAuthService<EventRecord>, ManagementAuthService>("management");

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
Expand Down
19 changes: 10 additions & 9 deletions src/AzureOpenAIProxy.ApiApp/Services/ManagementAuthService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,24 @@ namespace AzureOpenAIProxy.ApiApp.Services;
/// </summary>
/// <param name="client"><see cref="TableServiceClient"/> instance.</param>
/// <param name="logger"><see cref="ILogger{TCategoryName}"/> instance.</param>
public class ManagementAuthService(TableServiceClient client, ILogger<ManagementAuthService> logger) : AuthService<ManagementRecord>
public class ManagementAuthService(TableServiceClient client, ILogger<ManagementAuthService> logger) : AuthService<EventRecord>
{
private const string TableName = "managements";
private const string PartitionKeys = "master,management";
private const string PartitionKeyMaster = "master";
private const string PartitionKeyManagement = "management";

private readonly TableClient _table = client?.GetTableClient(TableName) ?? throw new ArgumentNullException(nameof(client));
private readonly ILogger<ManagementAuthService> _logger = logger ?? throw new ArgumentNullException(nameof(logger));

/// <inheritdoc />
public override async Task<ManagementRecord> ValidateAsync(string apiKey)
public override async Task<EventRecord> ValidateAsync(string apiKey)
{
var partitionKeys = PartitionKeys.Split(',', StringSplitOptions.RemoveEmptyEntries);
var results = this._table
.QueryAsync<ManagementRecord>(p => partitionKeys.Contains(p.PartitionKey)
&& p.ApiKey == apiKey);
.QueryAsync<EventRecord>(p => (p.PartitionKey.Equals(PartitionKeyMaster, StringComparison.InvariantCultureIgnoreCase) ||
p.PartitionKey.Equals(PartitionKeyManagement, StringComparison.InvariantCultureIgnoreCase))
&& p.ApiKey == apiKey);

var record = default(ManagementRecord);
var record = default(EventRecord);
await foreach (var result in results.AsPages())
{
if (result.Values.Count != 1)
Expand All @@ -39,7 +40,7 @@ record = result.Values.Single();

var now = DateTimeOffset.UtcNow;

if (record.PartitionKey == "master")
if (record.PartitionKey.Equals(PartitionKeyMaster, StringComparison.InvariantCultureIgnoreCase))
{
return record;
}
Expand All @@ -49,6 +50,6 @@ record = result.Values.Single();
return record;
}

return default(ManagementRecord);
return default(EventRecord);
}
}
Loading

0 comments on commit e89c796

Please sign in to comment.