Skip to content
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
26 changes: 26 additions & 0 deletions Asset Store/AssetStore.Database/AssetStore.Database.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.0-rc.2.25502.107" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0-rc.2.25502.107">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.0-rc.2.25502.107" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.0-rc.2.25502.107">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\Engine\SharpEngine.Shared\SharpEngine.Shared.csproj" />
</ItemGroup>

</Project>
44 changes: 44 additions & 0 deletions Asset Store/AssetStore.Database/DatabaseContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using AssetStore.Database.Models;
using Microsoft.EntityFrameworkCore;
using SharpEngine.Shared.Dto.Primitives;

namespace AssetStore.Database;

public class DatabaseContext : DbContext
{
public DbSet<Asset> Assets { get; set; }
public DbSet<Comment> Comments { get; set; }

public DatabaseContext()
{
}

protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFCoreExampleDB;Trusted_Connection=True;");
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity<Asset>(entity =>
{
entity.HasMany(asset => asset.Comments);

entity.Property(u => u.Id)
.HasConversion(
id => id.Value,
value => new AssetId(value));
});

modelBuilder.Entity<Comment>(entity =>
{
entity.Property(u => u.Id)
.HasConversion(
id => id.Value,
value => new CommentId(value));

});
}
}
36 changes: 36 additions & 0 deletions Asset Store/AssetStore.Database/Models/Asset.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using SharpEngine.Shared.Dto.AssetStore;
using SharpEngine.Shared.Dto.Primitives;

namespace AssetStore.Database.Models;

public class Asset
{
public AssetId Id { get; set; }
public required string Name { get; set; }
public string Description { get; set; } = string.Empty;
public decimal Price { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime LastUpdatedAt { get; set; } = DateTime.UtcNow;
public IReadOnlyList<string> KeyWords { get; set; } = [];
public IReadOnlyList<Comment> Comments { get; set; } = [];
public UserId AuthorId { get; set; }
public bool Tombstoned { get; set; } = false;
public Version Version { get; set; }
// (e.g., model, texture, sound, etc.)
// public AssetType

public required string BlobUri { get; set; }
public required string FileSize { get; set; }

public required string Checksum { get; set; }

public static explicit operator Asset(AssetDto dto)
{
throw new NotImplementedException();
}

public static explicit operator AssetDto(Asset? model)
{
throw new NotImplementedException();
}
}
14 changes: 14 additions & 0 deletions Asset Store/AssetStore.Database/Models/Comment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using SharpEngine.Shared.Dto.Primitives;

namespace AssetStore.Database.Models;

public class Comment
{
public CommentId Id { get; set; }
public AssetId AssetId { get; set; }
public UserId UserId { get; set; }
public required string Content { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public Comment? ParentComment { get; set; }
public IReadOnlyList<Comment> Replies { get; set; } = [];
}
16 changes: 16 additions & 0 deletions Asset Store/AssetStore.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Solution>
<Folder Name="/Core/">
<Project Path="../Engine/SharpEngine.Rest/SharpEngine.Rest.csproj" />
<Project Path="../Engine/SharpEngine.Shared/SharpEngine.Shared.csproj" />
</Folder>
<Folder Name="/Solution Items/">
<File Path="Directory.Build.props" />
<File Path="Readme.md" />
</Folder>
<Folder Name="/Tests/">
<File Path="Tests/Directory.Build.props" />
<Project Path="Tests/AssetStore.API.Tests/AssetStore.Api.Tests.csproj" Id="19d37056-65ed-4a44-bf21-3db060e32aba" />
</Folder>
<Project Path="AssetStore.Database/AssetStore.Database.csproj" Id="5fdeeb5e-a89f-4e87-888b-ee44ac83d6fc" />
<Project Path="AssetStore/AssetStore.Api.csproj" />
</Solution>
21 changes: 21 additions & 0 deletions Asset Store/AssetStore/AssetStore.Api.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<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="10.0.0-rc.2.25502.107" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\AssetStore.Database\AssetStore.Database.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Services\Comments\" />
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions Asset Store/AssetStore/AssetStore.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@AssetStore_HostAddress = http://localhost:5274

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

###
112 changes: 112 additions & 0 deletions Asset Store/AssetStore/Controllers/AssetController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using AssetStore.Database.Models;
using AssetStore.Services.Asset;
using Microsoft.AspNetCore.Mvc;
using SharpEngine.Shared.Dto.AssetStore;

namespace AssetStore.Controllers
{
[ApiController]
[Route("[controller]")]
public class AssetController : ControllerBase
{
private readonly ILogger<AssetController> _logger;
private readonly IAssetService _service;

public AssetController(ILogger<AssetController> logger, IAssetService service)
{
_logger = logger;
_service = service;
}

[HttpGet]
public ActionResult<IEnumerable<Asset>> GetAllAssets()
{
_logger.LogDebug("Retrieving assets.");

var assets = _service.GetAssets();
_logger.LogDebug("Found {Count} assets.", assets.Count());

return StatusCode(StatusCodes.Status200OK, assets);
}

[HttpGet("{assetId}")]
public ActionResult<Asset> GetById(Guid assetId)
{
_logger.LogDebug("Retrieving asset with id '{assetId}'.", assetId);
var asset = _service.GetAsset(assetId);

if (asset is null)
return StatusCode(StatusCodes.Status204NoContent, "Unable to find asset.");

return StatusCode(StatusCodes.Status200OK, asset);
}

[HttpGet("/keyword")]
public ActionResult<IEnumerable<Asset>> GetAllAssetsByKeyWord([FromBody] IReadOnlyList<string> keywords)
{
var joinedKeyWords = string.Join(", ", keywords);
_logger.LogDebug("Retrieving assets for the given keywords: {KeyWords}.", joinedKeyWords);

Check failure

Code scanning / CodeQL

Log entries created from user input High

This log entry depends on a
user-provided value
.

var assets = _service.GetAssets(keywords);
_logger.LogDebug("Found {Count} assets for the given keywords: {KeyWords}.", assets.Count(), joinedKeyWords);

Check failure

Code scanning / CodeQL

Log entries created from user input High

This log entry depends on a
user-provided value
.

if (!assets.Any())
return StatusCode(StatusCodes.Status204NoContent, $"Unable to find assets with keywords '{joinedKeyWords}'.");

return StatusCode(StatusCodes.Status200OK, assets);
}

[HttpGet("/author/{author}")]
public ActionResult<IEnumerable<Asset>> GetAllAssetsByAuthor(Guid authorId)
{
_logger.LogDebug("Retrieving assets for author with id: '{Author}'.", authorId);

var assets = _service.GetAssets(authorId);
_logger.LogDebug("Found {Count} assets for the given author: '{AuthorId}'.", assets.Count(), authorId);

if (!assets.Any())
return StatusCode(StatusCodes.Status204NoContent, $"Unable to find assets by author with id '{authorId}'.");

return StatusCode(StatusCodes.Status200OK, assets);
}

[HttpPost]
public async Task<ActionResult<AssetDto>> CreateAsset([FromBody] AssetDto asset)
{
_logger.LogDebug("Checking if asset already exists.");
if (asset.Id.Value != Guid.Empty)
{
_logger.LogDebug("Asset already exists.");
return ReviseAsset(asset);
}

_logger.LogDebug("Creating asset '{Asset}'. Published by '{Author}'.", asset.Name, asset.AuthorId);

Check failure

Code scanning / CodeQL

Log entries created from user input High

This log entry depends on a
user-provided value
.

Check failure

Code scanning / CodeQL

Log entries created from user input High

This log entry depends on a
user-provided value
.
var result = await _service.CreateAsset(asset);

_logger.LogDebug("Successfully created asset '{Asset}'. Published by '{Author}'.", asset.Name, asset.AuthorId);

Check failure

Code scanning / CodeQL

Log entries created from user input High

This log entry depends on a
user-provided value
.

Check failure

Code scanning / CodeQL

Log entries created from user input High

This log entry depends on a
user-provided value
.
return result;
}

[HttpPut]
public AssetDto ReviseAsset([FromBody] AssetDto asset)
{
// TODO: versioning logic

// TODO: Replace existing with revised asset
var result = _service.ReviseAsset(asset);

_logger.LogDebug("Successfully revised asset '{Asset}'. Published by '{Author}'.", asset.Name, asset.AuthorId);

Check failure

Code scanning / CodeQL

Log entries created from user input High

This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.

Check failure

Code scanning / CodeQL

Log entries created from user input High

This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
return asset;
}

[HttpDelete]
public async Task<IActionResult> DeleteAsset(Guid assetId)
{
bool removed = await _service.DeleteAsset(assetId);
if (!removed)
return StatusCode(StatusCodes.Status404NotFound);

return StatusCode(StatusCodes.Status200OK, "Asset successfully deleted.");
}
}
}
30 changes: 30 additions & 0 deletions Asset Store/AssetStore/Controllers/CommentController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using AssetStore.Database.Models;
using Microsoft.AspNetCore.Mvc;
using SharpEngine.Shared.Dto.AssetStore;

namespace AssetStore.Controllers
{
[ApiController]
[Route("[controller]")]
public class CommentController : ControllerBase
{
private readonly ILogger<CommentController> _logger;

public CommentController(ILogger<CommentController> logger)
{
_logger = logger;
}

[HttpGet]
public IEnumerable<Comment> GetAssetComments(Guid assetId)
{
return new List<Comment>();
}

[HttpPost]
public CommentDto PostComment(Guid assetId, CommentDto comment)
{
return new CommentDto();
}
}
}
23 changes: 23 additions & 0 deletions Asset Store/AssetStore/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
23 changes: 23 additions & 0 deletions Asset Store/AssetStore/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5274",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7215;http://localhost:5274",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Loading
Loading