Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an API for creating shared items #185

Merged
merged 1 commit into from
Jun 30, 2024
Merged
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
1 change: 1 addition & 0 deletions .csharpierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/Migrations/**/*
49 changes: 49 additions & 0 deletions .github/workflows/api-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net

name: Api Tests

on:
push:
branches-ignore: [main]

jobs:
api-test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: password
POSTGRES_DB: liftlog
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
env:
ConnectionStrings__UserDataContext: Host=localhost;Port=5432;Database=liftlog;Username=postgres;Password=password
ConnectionStrings__RateLimitContext: Host=localhost;Port=5432;Database=liftlog;Username=postgres;Password=password
OpenAiApiKey: "sk-123"
WebAuthApiKey: "1234"
GooglePlayServiceAccountEmail: "123"
GooglePlayServiceAccountKeyBase64: "123"
IdEncodingService__Alphabet: "123abc"
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
working-directory: ./tests/LiftLog.Tests.Api
- name: Build
run: dotnet build
working-directory: ./tests/LiftLog.Tests.Api
- name: Test
run: dotnet test --no-restore --verbosity normal
working-directory: ./tests/LiftLog.Tests.Api
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net

name: Unit Tests
name: UI Unit Tests

on:
push:
Expand All @@ -19,7 +19,10 @@ jobs:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
working-directory: ./LiftLog.Tests
working-directory: ./tests/LiftLog.Tests.App
- name: Build
run: dotnet build
working-directory: ./tests/LiftLog.Tests.App
- name: Test
run: dotnet test --no-restore --verbosity normal
working-directory: ./LiftLog.Tests
working-directory: ./tests/LiftLog.Tests.App
79 changes: 79 additions & 0 deletions LiftLog.Api/Controllers/SharedItemController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System.Security.Cryptography.X509Certificates;
using FluentValidation;
using LiftLog.Api.Db;
using LiftLog.Api.Models;
using LiftLog.Api.Service;
using LiftLog.Lib.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace LiftLog.Api.Controllers;

[ApiController]
public class SharedItemController(
UserDataContext db,
PasswordService passwordService,
IdEncodingService idEncodingService
) : ControllerBase
{
[Route("[controller]")]
[HttpPost]
public async Task<IActionResult> CreateShared(
CreateSharedItemRequest request,
[FromServices] IValidator<CreateSharedItemRequest> validator
)
{
var validationResult = await validator.ValidateAsync(request);
if (!validationResult.IsValid)
{
return BadRequest(validationResult.Errors);
}
var user = await db.Users.FindAsync(request.UserId);
if (user == null)
{
return Unauthorized();
}

if (!passwordService.VerifyPassword(request.Password, user.HashedPassword, user.Salt))
{
return Unauthorized();
}

var sharedItem = new SharedItem
{
UserId = request.UserId,
EncryptedPayload = request.EncryptedPayload,
EncryptionIV = request.EncryptionIV,
Expiry = request.Expiry,
};

await db.SharedItems.AddAsync(sharedItem);
await db.SaveChangesAsync();
return Ok(new CreateSharedItemResponse(Id: idEncodingService.EncodeId(sharedItem.Id)));
}

[Route("[controller]/{id}")]
[HttpGet]
public async Task<IActionResult> GetSharedItem(string id)
{
if (!idEncodingService.TryDecodeId(id, out var idNumber))
{
return NotFound();
}
var sharedItem = await db
.SharedItems.Include(x => x.User)
.FirstOrDefaultAsync(x => x.Id == idNumber);
if (sharedItem == null)
{
return NotFound();
}

return Ok(
new GetSharedItemResponse(
RsaPublicKey: new Lib.Services.RsaPublicKey(sharedItem.User.RsaPublicKey),
EncryptedPayload: sharedItem.EncryptedPayload,
EncryptionIV: sharedItem.EncryptionIV
)
);
}
}
5 changes: 2 additions & 3 deletions LiftLog.Api/Controllers/UserController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,13 @@ [FromServices] IValidator<CreateUserRequest> validator
[HttpGet]
public async Task<IActionResult> GetUser(string idOrLookup)
{
User? user;
User? user = null;
if (Guid.TryParse(idOrLookup, out var id))
{
user = await db.Users.FindAsync(id);
}
else
else if (idEncodingService.TryDecodeId(idOrLookup, out var userNumber))
{
var userNumber = idEncodingService.DecodeId(idOrLookup);
user = await db.Users.FirstOrDefaultAsync(x => x.UserLookup == userNumber);
}
if (user == null)
Expand Down
8 changes: 8 additions & 0 deletions LiftLog.Api/Db/UserDataContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public class UserDataContext(DbContextOptions<UserDataContext> options) : DbCont

public DbSet<UserInboxItem> UserInboxItems { get; set; } = null!;

public DbSet<SharedItem> SharedItems { get; set; } = null!;

/// <summary>
/// Used to register the user event filter tuple type as a DbSet for use in FromSqlRaw.
/// </summary>
Expand Down Expand Up @@ -50,6 +52,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
.WithOne(x => x.User)
.OnDelete(DeleteBehavior.Cascade);

modelBuilder
.Entity<User>()
.HasMany<SharedItem>()
.WithOne(x => x.User)
.OnDelete(DeleteBehavior.Cascade);

modelBuilder.Entity<UserEvent>().HasIndex(x => x.Expiry);

modelBuilder.Entity<UserEvent>().HasKey(x => new { x.UserId, x.Id });
Expand Down
Loading
Loading