From f26ff6e04a117361c162b8ddc906318cf11eff36 Mon Sep 17 00:00:00 2001 From: Chris McInnes Date: Tue, 24 Mar 2026 12:29:07 +1030 Subject: [PATCH] Initial API setup and basic program functioning --- Emergence.api/Emergence.api.csproj | 18 +++++++++ Emergence.api/Emergence.api.http | 11 ++++++ Emergence.api/Endpoints/UserEndpoints.cs | 30 ++++++++++++++ Emergence.api/Extensions/EndpointExtension.cs | 39 +++++++++++++++++++ Emergence.api/Interfaces/IEndpoint.cs | 7 ++++ Emergence.api/Program.cs | 22 +++++++++++ Emergence.api/Properties/launchSettings.json | 23 +++++++++++ Emergence.api/appsettings.Development.json | 8 ++++ Emergence.api/appsettings.json | 9 +++++ Emergence.models/Emergence.models.csproj | 9 +++++ Emergence.models/UserModel.cs | 13 +++++++ Emergence.services/Emergence.services.csproj | 17 ++++++++ .../Extensions/ServiceBuilderExtension.cs | 19 +++++++++ Emergence.services/Interface/IService.cs | 12 ++++++ Emergence.services/Interface/IUserService.cs | 11 ++++++ Emergence.services/Services/UserService.cs | 26 +++++++++++++ Emergence.slnx | 5 +++ 17 files changed, 279 insertions(+) create mode 100644 Emergence.api/Emergence.api.csproj create mode 100644 Emergence.api/Emergence.api.http create mode 100644 Emergence.api/Endpoints/UserEndpoints.cs create mode 100644 Emergence.api/Extensions/EndpointExtension.cs create mode 100644 Emergence.api/Interfaces/IEndpoint.cs create mode 100644 Emergence.api/Program.cs create mode 100644 Emergence.api/Properties/launchSettings.json create mode 100644 Emergence.api/appsettings.Development.json create mode 100644 Emergence.api/appsettings.json create mode 100644 Emergence.models/Emergence.models.csproj create mode 100644 Emergence.models/UserModel.cs create mode 100644 Emergence.services/Emergence.services.csproj create mode 100644 Emergence.services/Extensions/ServiceBuilderExtension.cs create mode 100644 Emergence.services/Interface/IService.cs create mode 100644 Emergence.services/Interface/IUserService.cs create mode 100644 Emergence.services/Services/UserService.cs create mode 100644 Emergence.slnx diff --git a/Emergence.api/Emergence.api.csproj b/Emergence.api/Emergence.api.csproj new file mode 100644 index 0000000..e00a355 --- /dev/null +++ b/Emergence.api/Emergence.api.csproj @@ -0,0 +1,18 @@ + + + + net10.0 + enable + enable + + + + + + + + + + + + diff --git a/Emergence.api/Emergence.api.http b/Emergence.api/Emergence.api.http new file mode 100644 index 0000000..20f1a6b --- /dev/null +++ b/Emergence.api/Emergence.api.http @@ -0,0 +1,11 @@ +@Emergence.api_HostAddress = http://localhost:5235 + +GET {{Emergence.api_HostAddress}}/users/ +Accept: application/json +### +GET {{Emergence.api_HostAddress}}/users/2 +Accept: application/json +### +GET {{Emergence.api_HostAddress}}/openapi/emergence.yaml +Accept: application/json +### \ No newline at end of file diff --git a/Emergence.api/Endpoints/UserEndpoints.cs b/Emergence.api/Endpoints/UserEndpoints.cs new file mode 100644 index 0000000..a92ed08 --- /dev/null +++ b/Emergence.api/Endpoints/UserEndpoints.cs @@ -0,0 +1,30 @@ +using Emergence.api.Interfaces; +using Emergence.models; +using Emergence.services.Interface; +using Microsoft.AspNetCore.Http.HttpResults; + +namespace Emergence.api.Endpoints; + +public class UserEndpoints : IEndpoint +{ + private readonly IUserService _userService; + + public UserEndpoints(IUserService userService) + { + _userService = userService; + } + + public void MapEndPoint(IEndpointRouteBuilder app) + { + var endpoints = app.MapGroup("/users") + .WithTags("User Items"); + + endpoints.MapGet("/", GetAll).Produces>(StatusCodes.Status200OK).Produces(StatusCodes.Status404NotFound).WithName("Get All Users"); + endpoints.MapGet("/{id}", GetById).Produces(StatusCodes.Status200OK).Produces(StatusCodes.Status404NotFound).WithName("Get User By Id"); + } + + public async Task>, NotFound>> GetAll() => + await _userService.GetAllAsync() is List result ? TypedResults.Ok(result) : TypedResults.NotFound(); + public async Task, NotFound>> GetById(int id) => + await _userService.GetByIdAsync(id) is UserModel result ? TypedResults.Ok(result) : TypedResults.NotFound(); +} diff --git a/Emergence.api/Extensions/EndpointExtension.cs b/Emergence.api/Extensions/EndpointExtension.cs new file mode 100644 index 0000000..714ddc5 --- /dev/null +++ b/Emergence.api/Extensions/EndpointExtension.cs @@ -0,0 +1,39 @@ +using Emergence.api.Interfaces; +using Microsoft.Extensions.DependencyInjection.Extensions; +using System.Reflection; + +namespace Emergence.api.Extensions; + +public static class EndpointExtension +{ + public static IServiceCollection AddEndpoints(this IServiceCollection services, Assembly assembly) + { + // Find all classes that implement IEndpoint and register them for DI + ServiceDescriptor[] serviceDescriptors = assembly + .DefinedTypes + .Where(type => type is { IsAbstract: false, IsInterface: false } && + type.IsAssignableTo(typeof(IEndpoint))) + .Select(type => ServiceDescriptor.Transient(typeof(IEndpoint), type)) + .ToArray(); + + services.TryAddEnumerable(serviceDescriptors); + + return services; + } + + public static IApplicationBuilder MapEndpoints(this WebApplication app, RouteGroupBuilder? routeGroupBuilder = null) + { + IEnumerable endpoints = app.Services + .GetRequiredService>(); + + IEndpointRouteBuilder builder = + routeGroupBuilder is null ? app : routeGroupBuilder; + + foreach (IEndpoint endpoint in endpoints) + { + endpoint.MapEndPoint(builder); + } + + return app; + } +} diff --git a/Emergence.api/Interfaces/IEndpoint.cs b/Emergence.api/Interfaces/IEndpoint.cs new file mode 100644 index 0000000..4584542 --- /dev/null +++ b/Emergence.api/Interfaces/IEndpoint.cs @@ -0,0 +1,7 @@ +namespace Emergence.api.Interfaces +{ + public interface IEndpoint + { + void MapEndPoint(IEndpointRouteBuilder app); + } +} diff --git a/Emergence.api/Program.cs b/Emergence.api/Program.cs new file mode 100644 index 0000000..ae644fc --- /dev/null +++ b/Emergence.api/Program.cs @@ -0,0 +1,22 @@ +using Emergence.api.Extensions; +using Emergence.services.Extensions; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi +builder.Services.AddOpenApi(); +builder.Services.AddServices(); +builder.Services.AddEndpoints(typeof(Program).Assembly); + +var app = builder.Build(); + + +app.UseHttpsRedirection(); +app.MapEndpoints(); +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi("/openapi/emergence.yaml"); +} +app.Run(); diff --git a/Emergence.api/Properties/launchSettings.json b/Emergence.api/Properties/launchSettings.json new file mode 100644 index 0000000..9d92552 --- /dev/null +++ b/Emergence.api/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5235", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7228;http://localhost:5235", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Emergence.api/appsettings.Development.json b/Emergence.api/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/Emergence.api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Emergence.api/appsettings.json b/Emergence.api/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/Emergence.api/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Emergence.models/Emergence.models.csproj b/Emergence.models/Emergence.models.csproj new file mode 100644 index 0000000..b760144 --- /dev/null +++ b/Emergence.models/Emergence.models.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + diff --git a/Emergence.models/UserModel.cs b/Emergence.models/UserModel.cs new file mode 100644 index 0000000..bdfec25 --- /dev/null +++ b/Emergence.models/UserModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Emergence.models +{ + public class UserModel + { + public int Id { get; set; } + public string Username { get; set; } + public string Email { get; set; } + } +} diff --git a/Emergence.services/Emergence.services.csproj b/Emergence.services/Emergence.services.csproj new file mode 100644 index 0000000..ede46b0 --- /dev/null +++ b/Emergence.services/Emergence.services.csproj @@ -0,0 +1,17 @@ + + + + net10.0 + enable + enable + + + + + + + + + + + diff --git a/Emergence.services/Extensions/ServiceBuilderExtension.cs b/Emergence.services/Extensions/ServiceBuilderExtension.cs new file mode 100644 index 0000000..cbc68ff --- /dev/null +++ b/Emergence.services/Extensions/ServiceBuilderExtension.cs @@ -0,0 +1,19 @@ +using Emergence.services.Interface; +using Emergence.services.Services; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Emergence.services.Extensions +{ + public static class ServiceBuilderExtension + { + public static IServiceCollection AddServices(this IServiceCollection services) + { + services = services.AddTransient(); + + return services; + } + } +} diff --git a/Emergence.services/Interface/IService.cs b/Emergence.services/Interface/IService.cs new file mode 100644 index 0000000..a59ffb4 --- /dev/null +++ b/Emergence.services/Interface/IService.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Emergence.services.Interface +{ + public interface IService where T : class + { + public Task> GetAllAsync(); + public Task GetByIdAsync(int id); + } +} diff --git a/Emergence.services/Interface/IUserService.cs b/Emergence.services/Interface/IUserService.cs new file mode 100644 index 0000000..da13160 --- /dev/null +++ b/Emergence.services/Interface/IUserService.cs @@ -0,0 +1,11 @@ +using Emergence.models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Emergence.services.Interface +{ + public interface IUserService : IService + { + } +} diff --git a/Emergence.services/Services/UserService.cs b/Emergence.services/Services/UserService.cs new file mode 100644 index 0000000..a5a1a71 --- /dev/null +++ b/Emergence.services/Services/UserService.cs @@ -0,0 +1,26 @@ +using Emergence.models; +using Emergence.services.Interface; + +namespace Emergence.services.Services; + +public class UserService : IUserService +{ + private List testList = [ + new UserModel {Id = 1, Username = "chris", Email ="chris@fjp.com.au" }, + new UserModel {Id = 2, Username = "kim", Email ="kim@fjp.com.au" }, + new UserModel {Id = 3, Username = "amanda", Email ="amanda@fjp.com.au" }, + new UserModel {Id = 4, Username = "reception", Email ="reception@fjp.com.au" }, + ]; + + public async Task> GetAllAsync() + { + return testList; + } + + public async Task GetByIdAsync(int id) + { +#pragma warning disable CS8603 // Possible null reference return. + return testList.FirstOrDefault(f => f.Id == id); +#pragma warning restore CS8603 // Possible null reference return. + } +} diff --git a/Emergence.slnx b/Emergence.slnx new file mode 100644 index 0000000..3469523 --- /dev/null +++ b/Emergence.slnx @@ -0,0 +1,5 @@ + + + + +