Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
node_modules/
node_modules/

# Ignore .NET build outputs
bin/
obj/
**/bin/
**/obj/
2 changes: 2 additions & 0 deletions code/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bin/
obj/
23 changes: 23 additions & 0 deletions code/CommBank.Tests/CommBank.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Moq" Version="4.20.0" />
<PackageReference Include="coverlet.collector" Version="3.2.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CommBank\CommBank.csproj" />
</ItemGroup>

</Project>
45 changes: 45 additions & 0 deletions code/CommBank.Tests/Fakes/FakeGoalsService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CommBank.Models;
using CommBank.Services;

namespace CommBank.Tests.Fakes;

public class FakeGoalsService : IGoalsService
{
private readonly List<Goal> _goals;

public FakeGoalsService(List<Goal> goals)
{
_goals = goals;
}

public Task<List<Goal>> GetAsync() =>
Task.FromResult(_goals);

public Task<Goal?> GetAsync(string id) =>
Task.FromResult(_goals.FirstOrDefault(g => g.Id == id));

public Task CreateAsync(Goal newGoal)
{
_goals.Add(newGoal);
return Task.CompletedTask;
}

public Task UpdateAsync(string id, Goal updatedGoal)
{
var idx = _goals.FindIndex(g => g.Id == id);
if (idx >= 0) _goals[idx] = updatedGoal;
return Task.CompletedTask;
}

public Task RemoveAsync(string id)
{
_goals.RemoveAll(g => g.Id == id);
return Task.CompletedTask;
}

public Task<List<Goal>> GetForUserAsync(string userId) =>
Task.FromResult(_goals.Where(g => g.UserId == userId).ToList());
}
39 changes: 39 additions & 0 deletions code/CommBank.Tests/GoalControllerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
using CommBank.Controllers;
using CommBank.Models;
using CommBank.Tests.Fakes;


public class GoalControllerTests
{
[Fact]
public async Task GetForUser_ReturnsOnlyGoalsForThatUser()
{
// Arrange
var userId = "62a3f5e0102e921da1253d33";

var goals = new List<Goal>
{
new Goal { Id = "62a3f5e0102e921da1253d32", UserId = userId, Name = "G1" },
new Goal { Id = "62a3f5e0102e921da1253d34", UserId = userId, Name = "G2" },
new Goal { Id = "62a3f5e0102e921da1253d99", UserId = "62a3f5e0102e921da1253d00", Name = "Other user" },
};

var goalsService = new FakeGoalsService(goals);
var controller = new GoalController(goalsService);

// Act
var result = await controller.GetForUser(userId);

// Assert
Assert.NotNull(result);

foreach (var goal in result)
{
Assert.IsAssignableFrom<Goal>(goal);
Assert.Equal(userId, goal.UserId);
}
}
}
15 changes: 15 additions & 0 deletions code/CommBank/CommBank.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.24" />
<PackageReference Include="MongoDB.Driver" Version="3.6.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions code/CommBank/CommBank.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@CommBank_HostAddress = http://localhost:5117

GET {{CommBank_HostAddress}}/weatherforecast/
Accept: application/json

###
75 changes: 75 additions & 0 deletions code/CommBank/Controllers/GoalController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using Microsoft.AspNetCore.Mvc;
using CommBank.Models;
using CommBank.Services;

namespace CommBank.Controllers;

[ApiController]
[Route("api/[controller]")]
public class GoalController : ControllerBase
{
private readonly IGoalsService _goalsService;

public GoalController(IGoalsService goalsService) =>
_goalsService = goalsService;

[HttpGet]
public async Task<List<Goal>> Get() =>
await _goalsService.GetAsync();

[HttpGet("{id:length(24)}")]
public async Task<ActionResult<Goal>> Get(string id)
{
var goal = await _goalsService.GetAsync(id);
if (goal is null)
{
return NotFound();
}
return goal;

}

[HttpGet("user/{userId:length(24)}")]
public async Task<List<Goal>> GetForUser(string userId) =>
await _goalsService.GetForUserAsync(userId);


[HttpPost]
public async Task<IActionResult> Post(Goal newGoal)
{
await _goalsService.CreateAsync(newGoal);
return CreatedAtAction(nameof(Get), new{ id = newGoal.Id}, newGoal);

}

[HttpPut("{id:length(24)}")]
public async Task<IActionResult> Update(string id, Goal updatedGoal)
{
var goal = await _goalsService.GetAsync(id);
if (goal is null)
{
return NotFound();
}

updatedGoal.Id = goal.Id;

await _goalsService.UpdateAsync(id, updatedGoal);
return NoContent();
}

[HttpDelete("{id:length(24)}")]
public async Task<IActionResult> Delete(string id)
{
var goal = await _goalsService.GetAsync(id);

if(goal is null)
{
return NotFound();
}

await _goalsService.RemoveAsync(id);
return NoContent();
}


}
35 changes: 35 additions & 0 deletions code/CommBank/Models/Goal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

namespace CommBank.Models;

public class Goal
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string? Id { get; set; }

public string? Name { get; set; }

public UInt64 TargetAmount { get; set; } = 0;

public DateTime TargetDate { get; set; }

public double Balance { get; set; } = 0.00;

public DateTime Created { get; set; } = DateTime.Now;

[BsonRepresentation(BsonType.ObjectId)]
public string? AccountId { get; set; }

[BsonRepresentation(BsonType.ObjectId)]
public List<string>? TransactionIds { get; set; }

[BsonRepresentation(BsonType.ObjectId)]
public List<string>? TagIds { get; set; }

[BsonRepresentation(BsonType.ObjectId)]
public string? UserId { get; set; }

public string? Icon { get; set; }
}
37 changes: 37 additions & 0 deletions code/CommBank/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using CommBank.Services;
using MongoDB.Driver;

var builder = WebApplication.CreateBuilder(args);

builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("Secrets.json");

// mongo + db instance
var MongoClient = new MongoClient(builder.Configuration.GetConnectionString("Commbank"));
var mongoDatabase = MongoClient.GetDatabase("data");

// test connection to db
try
{
await mongoDatabase.RunCommandAsync((Command<MongoDB.Bson.BsonDocument>)"{ping:1}");
Console.WriteLine("✅ MongoDB connection OK (ping succeeded)");
}
catch (Exception ex)
{
Console.WriteLine("❌ MongoDB connection failed: " + ex.Message);
}


// register dependencies for DI
IGoalsService goalsService = new GoalsService(mongoDatabase);
builder.Services.AddSingleton(goalsService);

// enable controoller to handle routes
builder.Services.AddControllers();

var app = builder.Build();

// map routes
app.MapControllers();

app.Run();
41 changes: 41 additions & 0 deletions code/CommBank/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:13994",
"sslPort": 44316
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5117",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7298;http://localhost:5117",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
5 changes: 5 additions & 0 deletions code/CommBank/Secrets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"ConnectionStrings": {
"CommBank": "mongodb+srv://user1:fYvb7tH3TQaKuPtd@cbafencer.y1186pi.mongodb.net/?appName=cbaFencer"
}
}
34 changes: 34 additions & 0 deletions code/CommBank/Services/GoalsService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Microsoft.Extensions.Options;
using CommBank.Models;
using MongoDB.Driver;

namespace CommBank.Services;

public class GoalsService : IGoalsService
{
private readonly IMongoCollection<Goal> _goalsCollection;

public GoalsService(IMongoDatabase mongoDatabase)
{
_goalsCollection = mongoDatabase.GetCollection<Goal>("Goals");

}
public async Task<List<Goal>> GetAsync() =>
await _goalsCollection.Find(_ => true).ToListAsync();

public async Task<Goal?> GetAsync(string id) =>
await _goalsCollection.Find(x => x.Id == id).FirstOrDefaultAsync();

public async Task CreateAsync(Goal newGoal) =>
await _goalsCollection.InsertOneAsync(newGoal);

public async Task UpdateAsync(string id, Goal updatedGoal) =>
await _goalsCollection.ReplaceOneAsync(x => x.Id == id, updatedGoal);

public async Task RemoveAsync(string id) =>
await _goalsCollection.DeleteOneAsync(x => x.Id == id);

public async Task<List<Goal>> GetForUserAsync(string userId) =>
await _goalsCollection.Find(g => g.UserId == userId).ToListAsync();

}
Loading