Mailbox support + crafting api
Deploy Promiscuity Auth API / deploy (push) Successful in 48s
Deploy Promiscuity Character API / deploy (push) Successful in 46s
Deploy Promiscuity Crafting API / deploy (push) Successful in 1m9s
Deploy Promiscuity Inventory API / deploy (push) Successful in 46s
Deploy Promiscuity Locations API / deploy (push) Successful in 48s
Deploy Promiscuity Mail API / deploy (push) Successful in 46s
k8s smoke test / test (push) Successful in 8s
Deploy Promiscuity Auth API / deploy (push) Successful in 48s
Deploy Promiscuity Character API / deploy (push) Successful in 46s
Deploy Promiscuity Crafting API / deploy (push) Successful in 1m9s
Deploy Promiscuity Inventory API / deploy (push) Successful in 46s
Deploy Promiscuity Locations API / deploy (push) Successful in 48s
Deploy Promiscuity Mail API / deploy (push) Successful in 46s
k8s smoke test / test (push) Successful in 8s
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
using CraftingApi.Models;
|
||||
using CraftingApi.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace CraftingApi.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class CraftingController : ControllerBase
|
||||
{
|
||||
private readonly CraftingStore _crafting;
|
||||
|
||||
public CraftingController(CraftingStore crafting)
|
||||
{
|
||||
_crafting = crafting;
|
||||
}
|
||||
|
||||
[HttpGet("recipes")]
|
||||
[Authorize(Roles = "USER,SUPER")]
|
||||
public async Task<IActionResult> ListRecipes()
|
||||
{
|
||||
var recipes = await _crafting.ListRecipesAsync();
|
||||
return Ok(recipes.Select(CraftingRecipeResponse.FromModel).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("recipes/{recipeKey}")]
|
||||
[Authorize(Roles = "USER,SUPER")]
|
||||
public async Task<IActionResult> GetRecipe(string recipeKey)
|
||||
{
|
||||
var recipe = await _crafting.GetRecipeAsync(recipeKey);
|
||||
return recipe is null ? NotFound() : Ok(CraftingRecipeResponse.FromModel(recipe));
|
||||
}
|
||||
|
||||
[HttpPost("recipes/{recipeKey}")]
|
||||
[Authorize(Roles = "SUPER")]
|
||||
public async Task<IActionResult> CreateRecipe(string recipeKey, [FromBody] UpsertCraftingRecipeRequest request)
|
||||
{
|
||||
var validationError = ValidateRecipeRequest(recipeKey, request);
|
||||
if (validationError is not null)
|
||||
return validationError;
|
||||
|
||||
var recipe = _crafting.BuildRecipe(recipeKey, request);
|
||||
var created = await _crafting.CreateRecipeAsync(recipe);
|
||||
return created ? Ok(CraftingRecipeResponse.FromModel(recipe)) : Conflict("Recipe already exists");
|
||||
}
|
||||
|
||||
[HttpPut("recipes/{recipeKey}")]
|
||||
[Authorize(Roles = "SUPER")]
|
||||
public async Task<IActionResult> UpsertRecipe(string recipeKey, [FromBody] UpsertCraftingRecipeRequest request)
|
||||
{
|
||||
var validationError = ValidateRecipeRequest(recipeKey, request);
|
||||
if (validationError is not null)
|
||||
return validationError;
|
||||
|
||||
var recipe = await _crafting.UpsertRecipeAsync(recipeKey, request);
|
||||
return Ok(CraftingRecipeResponse.FromModel(recipe));
|
||||
}
|
||||
|
||||
[HttpGet("characters/{characterId}/available-recipes")]
|
||||
[Authorize(Roles = "USER,SUPER")]
|
||||
public async Task<IActionResult> AvailableRecipes(string characterId)
|
||||
{
|
||||
var access = await ResolveCharacterAccessAsync(characterId);
|
||||
if (access is IActionResult errorResult)
|
||||
return errorResult;
|
||||
|
||||
var available = await _crafting.GetAvailableRecipesAsync((CraftingStore.CharacterAccessResult)access!);
|
||||
return Ok(available);
|
||||
}
|
||||
|
||||
[HttpPost("characters/{characterId}/craft")]
|
||||
[Authorize(Roles = "USER,SUPER")]
|
||||
public async Task<IActionResult> Craft(string characterId, [FromBody] CraftRecipeRequest request)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.RecipeKey))
|
||||
return BadRequest("recipeKey required");
|
||||
if (request.Quantity <= 0)
|
||||
return BadRequest("quantity must be greater than 0");
|
||||
|
||||
var access = await ResolveCharacterAccessAsync(characterId);
|
||||
if (access is IActionResult errorResult)
|
||||
return errorResult;
|
||||
|
||||
var result = await _crafting.CraftAsync((CraftingStore.CharacterAccessResult)access!, request);
|
||||
return result.Status switch
|
||||
{
|
||||
CraftingStore.CraftStatus.RecipeNotFound => NotFound("Recipe not found"),
|
||||
CraftingStore.CraftStatus.MissingStation => Conflict("Required crafting station is not available"),
|
||||
CraftingStore.CraftStatus.MissingInputs => Conflict(new { missingRequirements = result.MissingRequirements }),
|
||||
CraftingStore.CraftStatus.InvalidRecipe => BadRequest(new { missingRequirements = result.MissingRequirements }),
|
||||
_ => Ok(new CraftRecipeResponse
|
||||
{
|
||||
CharacterId = characterId,
|
||||
RecipeKey = request.RecipeKey.Trim().ToLowerInvariant(),
|
||||
CraftedQuantity = result.CraftedQuantity,
|
||||
Consumed = result.Consumed,
|
||||
Produced = result.Produced
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<object> ResolveCharacterAccessAsync(string characterId)
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (string.IsNullOrWhiteSpace(userId))
|
||||
return Unauthorized();
|
||||
|
||||
var access = await _crafting.ResolveCharacterAsync(characterId, userId, User.IsInRole("SUPER"));
|
||||
if (!access.Exists)
|
||||
return NotFound();
|
||||
if (!access.IsAuthorized)
|
||||
return Forbid();
|
||||
|
||||
return access;
|
||||
}
|
||||
|
||||
private IActionResult? ValidateRecipeRequest(string recipeKey, UpsertCraftingRecipeRequest request)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(recipeKey))
|
||||
return BadRequest("recipeKey required");
|
||||
if (string.IsNullOrWhiteSpace(request.Name))
|
||||
return BadRequest("name required");
|
||||
if (request.Inputs.Count == 0)
|
||||
return BadRequest("At least one input is required");
|
||||
if (request.Outputs.Count == 0)
|
||||
return BadRequest("At least one output is required");
|
||||
if (request.Inputs.Any(i => string.IsNullOrWhiteSpace(i.ItemKey) || i.Quantity <= 0))
|
||||
return BadRequest("All inputs must have itemKey and quantity > 0");
|
||||
if (request.Outputs.Any(i => string.IsNullOrWhiteSpace(i.ItemKey) || i.Quantity <= 0))
|
||||
return BadRequest("All outputs must have itemKey and quantity > 0");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user