From 0d170df610da263e561ef1ddf345425af90abf89 Mon Sep 17 00:00:00 2001 From: Maksym Sadovnychyy Date: Fri, 30 Aug 2024 21:06:24 +0200 Subject: [PATCH] (feature): init --- .gitignore | 4 - README.md | 215 +++++++++++++++++++++++ src/MaksIT.Results.sln | 25 +++ src/MaksIT.Results/MaksIT.Results.csproj | 16 +- src/MaksIT.Results/Result.cs | 14 +- src/Release-NuGetPackage.bat | 7 + src/Release-NuGetPackage.ps1 | 46 +++++ src/Release-NuGetPackage.sh | 49 ++++++ 8 files changed, 369 insertions(+), 7 deletions(-) create mode 100644 README.md create mode 100644 src/MaksIT.Results.sln create mode 100644 src/Release-NuGetPackage.bat create mode 100644 src/Release-NuGetPackage.ps1 create mode 100644 src/Release-NuGetPackage.sh diff --git a/.gitignore b/.gitignore index 5f09bf8..7fa7210 100644 --- a/.gitignore +++ b/.gitignore @@ -260,7 +260,3 @@ paket-files/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc - - -**/*docker-compose/LetsEncryptServer/acme -**/*docker-compose/LetsEncryptServer/cache \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..00c7073 --- /dev/null +++ b/README.md @@ -0,0 +1,215 @@ +# MaksIT.Results + +`MaksIT.Results` is a powerful library designed to streamline the creation and management of result objects in your ASP.NET Core applications. It provides a standardized way to handle method results and easily convert them to `IActionResult` for HTTP responses, ensuring consistent and clear API responses. + +## Features + +- **Standardized Result Handling**: Represent operation outcomes (success or failure) with appropriate HTTP status codes. +- **Seamless Conversion to `IActionResult`**: Convert result objects to HTTP responses (`IActionResult`) with detailed problem descriptions. +- **Flexible Result Types**: Supports both generic (`Result`) and non-generic (`Result`) results for handling various scenarios. +- **Predefined Results for All Standard HTTP Status Codes**: Includes predefined static methods to create results for all standard HTTP status codes (e.g., 200 OK, 404 Not Found, 500 Internal Server Error, etc.). + +## Installation + +To install `MaksIT.Results`, use the NuGet Package Manager: + +```bash +Install-Package MaksIT.Results +``` + +## Usage example + +Below is an example demonstrating how to use `MaksIT.Results` in a typical ASP.NET Core application where a controller interacts with a service. + +### Step 1: Define and Register the Service + +Define a service that uses `MaksIT.Results` to return operation results, handling different result types with proper casting and conversion. + +```csharp +public interface IVaultPersistanceService +{ + Result ReadOrganization(Guid organizationId); + Task DeleteOrganizationAsync(Guid organizationId); + // Additional method definitions... +} + +public class VaultPersistanceService : IVaultPersistanceService +{ + // Inject dependencies as needed + + public Result ReadOrganization(Guid organizationId) + { + var organizationResult = _organizationDataProvider.GetById(organizationId); + if (!organizationResult.IsSuccess || organizationResult.Value == null) + { + // Return a NotFound result when the organization isn't found + return Result.NotFound("Organization not found."); + } + + var organization = organizationResult.Value; + var applicationDtos = new List(); + + foreach (var applicationId in organization.Applications) + { + var applicationResult = _applicationDataProvider.GetById(applicationId); + if (!applicationResult.IsSuccess || applicationResult.Value == null) + { + // Transform the result from Result to Result + // Ensuring the return type matches the method signature (Result) + return applicationResult.WithNewValue(_ => null); + } + + var applicationDto = applicationResult.Value; + applicationDtos.Add(applicationDto); + } + + // Return the final result with all applications loaded + return Result.Ok(organization); + } + + public async Task DeleteOrganizationAsync(Guid organizationId) + { + var organizationResult = await _organizationDataProvider.GetByIdAsync(organizationId); + + if (!organizationResult.IsSuccess || organizationResult.Value == null) + { + // Convert Result to a non-generic Result + // The cast to (Result) allows for standardized response type + return (Result)organizationResult; + } + + // Proceed with the deletion if the organization is found + var deleteResult = await _organizationDataProvider.DeleteByIdAsync(organizationId); + + // Return the result of the delete operation directly + return deleteResult; + } +} +``` + +**Key Points to Note:** + +1. **Handling Different Result Types:** + - The `ReadOrganization` method demonstrates handling a `Result` and transforming other types as needed using `WithNewValue`. This ensures the method always returns the correct type. + +2. **Casting from `Result` to `Result`:** + - In `DeleteOrganizationAsync`, we cast `Result` to `Result` using `(Result)organizationResult`. This cast standardizes the result type, making it suitable for scenarios where only success or failure matters. + +Ensure this service is registered in your dependency injection container: + +```csharp +public void ConfigureServices(IServiceCollection services) +{ + services.AddScoped(); + // Other service registrations... +} +``` + +### Step 2: Use the Service in the Controller + +Inject the service into your controller and utilize `MaksIT.Results` to handle results efficiently: + +```csharp +using Microsoft.AspNetCore.Mvc; +using MaksIT.Results; + +public class OrganizationController : ControllerBase +{ + private readonly IVaultPersistanceService _vaultPersistanceService; + + public OrganizationController(IVaultPersistanceService vaultPersistanceService) + { + _vaultPersistanceService = vaultPersistanceService; + } + + [HttpGet("{organizationId}")] + public IActionResult GetOrganization(Guid organizationId) + { + var result = _vaultPersistanceService.ReadOrganization(organizationId); + + // Convert the Result to IActionResult using ToActionResult() + return result.ToActionResult(); + } + + [HttpDelete("{organizationId}")] + public async Task DeleteOrganization(Guid organizationId) + { + var result = await _vaultPersistanceService.DeleteOrganizationAsync(organizationId); + + // Convert the Result to IActionResult using ToActionResult() + return result.ToActionResult(); + } + + // Additional actions... +} +``` + +### Transforming Results + +You can also transform the result within the controller or service to adjust the output type as needed: + +```csharp +public IActionResult TransformResultExample() +{ + var result = _vaultPersistanceService.ReadOrganization(Guid.NewGuid()); + + // Transform the result to a different type if needed + var transformedResult = result.WithNewValue(org => (org?.Name ?? "").ToTitle()); + + return transformedResult.ToActionResult(); +} +``` + +### Predefined Results for All Standard HTTP Status Codes + +`MaksIT.Results` provides methods to easily create results for all standard HTTP status codes, simplifying the handling of responses: + +```csharp +return Result.Ok("Success").ToActionResult(); // 200 OK +return Result.NotFound("Resource not found").ToActionResult(); // 404 Not Found +return Result.InternalServerError("An unexpected error occurred").ToActionResult(); // 500 Internal Server Error +``` + +### Conclusion + +`MaksIT.Results` is a powerful tool for simplifying the handling of operation results in ASP.NET Core applications. It provides a robust framework for standardized result handling, seamless conversion to `IActionResult`, and flexible result types to handle various scenarios. By adopting this library, developers can create more maintainable and readable code, ensuring consistent and clear HTTP responses. + +## Contribution + +Contributions to this project are welcome! Please fork the repository and submit a pull request with your changes. If you encounter any issues or have feature requests, feel free to open an issue on GitHub. + +## License + +This project is licensed under the MIT License. See the full license text below. + +--- + +### MIT License + +``` +MIT License + +Copyright (c) 2024 Maksym Sadovnychyy (MAKS-IT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +## Contact + +For any questions or inquiries, please reach out via GitHub or [email](mailto:maksym.sadovnychyy@gmail.com). \ No newline at end of file diff --git a/src/MaksIT.Results.sln b/src/MaksIT.Results.sln new file mode 100644 index 0000000..f87941a --- /dev/null +++ b/src/MaksIT.Results.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34902.65 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MaksIT.Results", "MaksIT.Results\MaksIT.Results.csproj", "{E947F5FC-8FD9-4F1E-AA5F-29FED95B5A2D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E947F5FC-8FD9-4F1E-AA5F-29FED95B5A2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E947F5FC-8FD9-4F1E-AA5F-29FED95B5A2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E947F5FC-8FD9-4F1E-AA5F-29FED95B5A2D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E947F5FC-8FD9-4F1E-AA5F-29FED95B5A2D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C3627A51-0642-40DB-96BC-07C627FF8ACC} + EndGlobalSection +EndGlobal diff --git a/src/MaksIT.Results/MaksIT.Results.csproj b/src/MaksIT.Results/MaksIT.Results.csproj index 766e967..66cfbb2 100644 --- a/src/MaksIT.Results/MaksIT.Results.csproj +++ b/src/MaksIT.Results/MaksIT.Results.csproj @@ -4,10 +4,24 @@ net8.0 enable enable - + MaksIT.$(MSBuildProjectName.Replace(" ", "_")) + + MaksIT.Results + 1.0.0 + Maksym Sadovnychyy + MAKS-IT + MaksIT.Results + Library for standardized result handling and seamless conversion to IActionResult in ASP.NET Core applications. + aspnetcore;result;handling;api;dotnet + https://github.com/MAKS-IT-COM/maksit-results + MIT + false + README.md + + diff --git a/src/MaksIT.Results/Result.cs b/src/MaksIT.Results/Result.cs index 9d2c788..523c26e 100644 --- a/src/MaksIT.Results/Result.cs +++ b/src/MaksIT.Results/Result.cs @@ -14,6 +14,14 @@ namespace MaksIT.Results { StatusCode = statusCode; } + /// + /// Converts the current Result{T} to a non-generic Result. + /// + /// A non-generic Result object. + public Result ToResult() { + return new Result(IsSuccess, Messages, StatusCode); + } + /// /// Converts the current Result to an IActionResult. /// @@ -52,6 +60,8 @@ namespace MaksIT.Results { return new Result(newValueFunc(Value), IsSuccess, Messages, StatusCode); } + + /// /// Converts the current Result to an IActionResult. /// @@ -61,10 +71,10 @@ namespace MaksIT.Results { if (Value is not null) { return new ObjectResult(Value) { StatusCode = (int)StatusCode }; } - return ToActionResult(); + return base.ToActionResult(); } else { - return ToActionResult(); + return base.ToActionResult(); } } } diff --git a/src/Release-NuGetPackage.bat b/src/Release-NuGetPackage.bat new file mode 100644 index 0000000..ba9cefe --- /dev/null +++ b/src/Release-NuGetPackage.bat @@ -0,0 +1,7 @@ +@echo off + +REM Change directory to the location of the script +cd /d %~dp0 + +REM Invoke the PowerShell script (Release-NuGetPackage.ps1) in the same directory +powershell -ExecutionPolicy Bypass -File "%~dp0Release-NuGetPackage.ps1" diff --git a/src/Release-NuGetPackage.ps1 b/src/Release-NuGetPackage.ps1 new file mode 100644 index 0000000..e16529c --- /dev/null +++ b/src/Release-NuGetPackage.ps1 @@ -0,0 +1,46 @@ +# Retrieve the API key from the environment variable +$apiKey = $env:NUGET_MAKS_IT +if (-not $apiKey) { + Write-Host "Error: API key not found in environment variable NUGET_MAKS_IT." + exit 1 +} + +# NuGet source +$nugetSource = "https://api.nuget.org/v3/index.json" + +# Define paths +$solutionDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$projectDir = "$solutionDir\MaksIT.Results" +$outputDir = "$projectDir\bin\Release" + +# Clean previous builds +Write-Host "Cleaning previous builds..." +dotnet clean $projectDir -c Release + +# Build the project +Write-Host "Building the project..." +dotnet build $projectDir -c Release + +# Pack the NuGet package +Write-Host "Packing the project..." +dotnet pack $projectDir -c Release --no-build + +# Look for the .nupkg file +$packageFile = Get-ChildItem -Path $outputDir -Filter "*.nupkg" -Recurse | Sort-Object LastWriteTime -Descending | Select-Object -First 1 + +if ($packageFile) { + Write-Host "Package created successfully: $($packageFile.FullName)" + + # Push the package to NuGet + Write-Host "Pushing the package to NuGet..." + dotnet nuget push $packageFile.FullName -k $apiKey -s $nugetSource --skip-duplicate + + if ($LASTEXITCODE -eq 0) { + Write-Host "Package pushed successfully." + } else { + Write-Host "Failed to push the package." + } +} else { + Write-Host "Package creation failed. No .nupkg file found." + exit 1 +} diff --git a/src/Release-NuGetPackage.sh b/src/Release-NuGetPackage.sh new file mode 100644 index 0000000..832322d --- /dev/null +++ b/src/Release-NuGetPackage.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +# Retrieve the API key from the environment variable +apiKey=$NUGET_MAKS_IT +if [ -z "$apiKey" ]; then + echo "Error: API key not found in environment variable NUGET_MAKS_IT." + exit 1 +fi + +# NuGet source +nugetSource="https://api.nuget.org/v3/index.json" + +# Define paths +scriptDir=$(dirname "$0") +solutionDir=$(realpath "$scriptDir") +projectDir="$solutionDir/MaksIT.Results" +outputDir="$projectDir/bin/Release" + +# Clean previous builds +echo "Cleaning previous builds..." +dotnet clean "$projectDir" -c Release + +# Build the project +echo "Building the project..." +dotnet build "$projectDir" -c Release + +# Pack the NuGet package +echo "Packing the project..." +dotnet pack "$projectDir" -c Release --no-build + +# Look for the .nupkg file +packageFile=$(find "$outputDir" -name "*.nupkg" -print0 | xargs -0 ls -t | head -n 1) + +if [ -n "$packageFile" ]; then + echo "Package created successfully: $packageFile" + + # Push the package to NuGet + echo "Pushing the package to NuGet..." + dotnet nuget push "$packageFile" -k "$apiKey" -s "$nugetSource" --skip-duplicate + + if [ $? -eq 0 ]; then + echo "Package pushed successfully." + else + echo "Failed to push the package." + fi +else + echo "Package creation failed. No .nupkg file found." + exit 1 +fi