├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── Demo.API ├── API.xml ├── Controllers │ └── v1 │ │ ├── DragonBallController.cs │ │ ├── ProfileController.cs │ │ ├── SignInController.cs │ │ ├── SingUpController.cs │ │ └── ValuesController.cs ├── Demo.API.csproj ├── Demo.API.xml ├── Migrations │ ├── 20181208205058_InitCreate.Designer.cs │ ├── 20181208205058_InitCreate.cs │ └── DbzMySqlContextModelSnapshot.cs ├── Program.cs ├── Properties │ └── launchSettings.json └── Startup.cs ├── Demo.Consumer.HandleMessages ├── .env ├── Demo.Consumer.HandleMessages.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs └── Tasks │ └── TesteMessageHandlerTask.cs ├── Demo.Core.Tests ├── Demo.Core.Tests.csproj ├── ExternalServices │ └── Google │ │ └── GoogleMapAPITests.cs └── Validations │ ├── DragonBall │ └── DragonBallPostRequestValidatorTest.cs │ └── SignUp │ └── SignUpPostRequestValidatorTest.cs ├── Demo.Core ├── Application.xml ├── Contracts │ ├── DragonBall │ │ └── Request │ │ │ ├── DragonBallPostRelativeRequest.cs │ │ │ └── DragonBallPostRequest.cs │ ├── GraphQL │ │ └── GraphQLParameter.cs │ ├── Shared │ │ └── AddressRequest.cs │ ├── SignIn │ │ └── SignInPostRequest.cs │ ├── SignUp │ │ └── SignUpPostRequest.cs │ └── Values │ │ └── PostMessageRequest.cs ├── Data │ ├── MongoDB │ │ ├── Entities │ │ │ └── UserEntity.cs │ │ ├── Repositories │ │ │ ├── IUserRepository.cs │ │ │ └── UserRepository.cs │ │ └── RepositoriesDI.cs │ └── MySql │ │ ├── DbzInitializer.cs │ │ ├── DbzMySqlContext.cs │ │ ├── Entities │ │ ├── CharacterEntity.cs │ │ └── FamilyEntity.cs │ │ ├── EntityModelBuilderExtensions.cs │ │ ├── MySqlExtensions.cs │ │ └── Repositories │ │ ├── CharacterRepository.cs │ │ ├── FamilyRepository.cs │ │ ├── ICharacterRepository.cs │ │ └── IFamilyRepository.cs ├── Demo.Core.csproj ├── ExternalServices │ ├── Google │ │ ├── GoogleApiConfiguration.cs │ │ ├── IGoogleMapsAPI.cs │ │ └── Views │ │ │ └── GoogleGeoCodeView.cs │ └── RegisterExternalServices.cs ├── GraphQL │ ├── DbzMutation.cs │ ├── DbzQuery.cs │ ├── DbzSchema.cs │ ├── GraphQLDI.cs │ ├── GraphQLUserContext.cs │ └── Types │ │ ├── Character │ │ ├── CharacterExtensions.cs │ │ ├── CharacterGraphInputType.cs │ │ ├── CharacterGraphType.cs │ │ ├── CharacterKindEnumType.cs │ │ └── Models │ │ │ └── CharacterModel.cs │ │ └── Family │ │ ├── FamilyExtensions.cs │ │ ├── Models │ │ └── RelativeModel.cs │ │ ├── RelativeGraphType.cs │ │ └── RelativeKindEnumType.cs ├── Messages │ └── RabbitMQ │ │ └── TesteMessage.cs ├── Services │ ├── CharacterServices.cs │ ├── GraphQL │ │ ├── CharacterGraphServices.cs │ │ ├── GraphServicesDI.cs │ │ └── ICharacterGraphServices.cs │ ├── ICharacterServices.cs │ ├── IProfileServices.cs │ ├── ISignInServices.cs │ ├── ISignUpServices.cs │ ├── IValuesServices.cs │ ├── ProfileServices.cs │ ├── ServicesDI.cs │ ├── SignInServices.cs │ ├── SignUpServices.cs │ └── ValuesServices.cs ├── Shared │ └── Enum │ │ ├── ECharecterKind.cs │ │ ├── EGender.cs │ │ └── ERelativeKind.cs └── Validations │ ├── DragonBall │ ├── DragonBallPostRelativeRequestValidator.cs │ └── DragonBallPostRequestValidator.cs │ ├── RegisterValidationsExtensions.cs │ ├── SignIn │ └── SignInPostRequestValidator.cs │ └── SignUp │ └── SignUpPostRequestValidator.cs ├── Demo.Worker ├── Demo.Worker.csproj ├── Program.cs ├── Startup.cs └── appsettings.json ├── Dockerfile ├── Framework.Core.Tests ├── Framework.Core.Tests.csproj └── Serializer │ └── JsonSerializerCommonTest.cs ├── Framework.Core ├── Framework.Core.csproj └── Serializer │ ├── ISerializer.cs │ └── JsonSerializerCommon.cs ├── Framework.Data.Tests ├── Framework.Data.Tests.csproj └── UnitTest1.cs ├── Framework.Data ├── CacheProviders │ └── Redis │ │ ├── CacheStoreExtensions.cs │ │ └── RedisExtensions.cs ├── EntityFramework │ ├── EFEntityBase.cs │ ├── EFRepositoryBase.cs │ └── IEFRepository.cs ├── Framework.Data.csproj ├── MongoDB │ ├── IMongoRepositoryBase.cs │ ├── MongoDBConnectionWraper.cs │ ├── MongoDBExtensions.cs │ ├── MongoEntityBase.cs │ └── MongoRepositoryBase.cs └── MySql │ └── MySqlExtensions.cs ├── Framework.MessageBroker.Tests ├── .env ├── Framework.MessageBroker.Tests.csproj ├── RabbitMQ │ ├── Messages │ │ ├── DefaultGeneratedNameMessage.cs │ │ ├── DefaultMessage.cs │ │ ├── DefaultRejectedMessage.cs │ │ ├── DirectMessage.cs │ │ ├── DirectedGeneratedNameMessage.cs │ │ └── NamedMessage.cs │ └── Tests.cs └── Startup.cs ├── Framework.MessageBroker ├── BaseMessage.cs ├── Framework.MessageBroker.csproj ├── IExchangeOptions.cs ├── IPublisher.cs ├── ISubscriber.cs └── RabbitMQ │ ├── Explorer │ ├── IRabbitExplorer.cs │ ├── RabbitMQExchangeDetail.cs │ ├── RabbitMQExplorer.cs │ └── RabbitMQQueueDetail.cs │ ├── IRabbitMQPublisher.cs │ ├── IRabbitMQSubscriber.cs │ ├── RabbitMQConnectionWrapper.cs │ ├── RabbitMQExchangeBuilderExtensions.cs │ ├── RabbitMQExchangeOptions.cs │ ├── RabbitMQExtensions.cs │ ├── RabbitMQPropertiesAttribute.cs │ ├── RabbitMQPublisher.cs │ ├── RabbitMQSettings.cs │ └── RabbitMQSubscriber.cs ├── Framework.Test ├── Framework.Test.csproj ├── TestHost.cs ├── TestStartup.cs └── appSettings.json ├── Framework ├── Framework.csproj ├── Services │ ├── BaseServices.cs │ └── ServicesResult.cs └── WebAPI │ ├── Controllers │ └── BaseController.cs │ ├── Documetation │ ├── CustomConfigurationOperationFilter.cs │ ├── LowercaseDocumentFilter.cs │ ├── SwaggerDefaultValues.cs │ └── SwaggerExtensions.cs │ ├── Extensions │ └── HttpRequestExtensions.cs │ ├── HealthCheck │ └── HealthCheckExtensions.cs │ ├── Hosting │ ├── BaseStartup.cs │ ├── Cors │ │ └── CorsExtensions.cs │ ├── Formatters │ │ └── ImageRawRequestBodyFormatter.cs │ ├── JWT │ │ ├── JWTExtensions.cs │ │ ├── SigningConfigurations.cs │ │ └── TokenConfigurations.cs │ └── Middlewares │ │ └── HttpExceptionMiddleware.cs │ ├── Responses │ └── NotOkDefaultReponse.cs │ └── Versioning │ └── ApiVersionExtensions.cs ├── README.md ├── Solution.sln ├── docker-compose-services.yml ├── docker-compose.yml ├── prometheus └── prometheus.yml └── sonarqube.Dockerfile /.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .vs 3 | .vscode -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Demo.API", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/Demo.API/bin/Debug/netcoreapp2.2/Demo.API.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/Demo.API", 16 | "stopAtEntry": false, 17 | "internalConsoleOptions": "openOnSessionStart", 18 | "launchBrowser": { 19 | "enabled": true, 20 | "args": "${auto-detect-url}", 21 | "windows": { 22 | "command": "cmd.exe", 23 | "args": "/C start ${auto-detect-url}" 24 | }, 25 | "osx": { 26 | "command": "open" 27 | }, 28 | "linux": { 29 | "command": "xdg-open" 30 | } 31 | }, 32 | "env": { 33 | "ASPNETCORE_ENVIRONMENT": "Development", 34 | "GoogleApiConfiguration:GeoCodeURI": "https://maps.googleapis.com/maps/api/geocode", 35 | "GoogleApiConfiguration:MapsKey": "AIzaSyD6DxZlq_qWFSYs640jw9k_IvFltjM-Uew", 36 | "TokenConfigurations:Audience": "http://localhost:5000/api/signup", 37 | "TokenConfigurations:Issuer": "demo.api", 38 | "TokenConfigurations:Seconds": "1800", 39 | "ConnectionStrings:MongoDB": "mongodb://localhost:27017/demodb", 40 | "ConnectionStrings:MYSQL": "Server=localhost;Database=dbz;User=root;Password=root;", 41 | "ConnectionStrings:RabbitMQ": "amqp://guest:guest@localhost", 42 | "ConnectionStrings:Redis": "localhost,name=Demo.Api,defaultDatabase=1" 43 | }, 44 | "sourceFileMap": { 45 | "/Views": "${workspaceFolder}/Views" 46 | } 47 | }, 48 | { 49 | "name": ".NET Core Attach", 50 | "type": "coreclr", 51 | "request": "attach", 52 | "processId": "${command:pickProcess}" 53 | }, 54 | { 55 | "name": "Demo.Consumer", 56 | "type": "coreclr", 57 | "request": "launch", 58 | "preLaunchTask": "build-consumer", 59 | "program": "${workspaceFolder}/Demo.Consumer.HandleMessages/bin/Debug/netcoreapp2.2/Demo.Consumer.HandleMessages.dll", 60 | "args": [], 61 | "cwd": "${workspaceFolder}/Demo.Consumer.HandleMessages", 62 | "stopAtEntry": false, 63 | "console": "internalConsole" 64 | }, 65 | ] 66 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/Solution.sln" 11 | ], 12 | "problemMatcher": "$msCompile" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /Demo.API/API.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | API 5 | 6 | 7 | 8 | 9 | RESTfull services for User profile 10 | 11 | 12 | 13 | 14 | Constructor 15 | 16 | 17 | 18 | 19 | 20 | RESTfull services for SignIn 21 | 22 | 23 | 24 | 25 | Controller 26 | 27 | Dependência do serviço 28 | 29 | 30 | 31 | Tenta logar um usuário a partir de e-mail e senha 32 | 33 | 34 | 35 | 36 | 37 | 38 | RESTfull services for SignUp 39 | 40 | 41 | 42 | 43 | Cria um novo usuário 44 | 45 | Dados do usuário 46 | 47 | 48 | 49 | RESTfull services for Caregivers 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Demo.API/Controllers/v1/DragonBallController.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.DragonBall.Request; 2 | using Demo.Core.Services; 3 | using Framework.WebAPI; 4 | using Microsoft.AspNetCore.Mvc; 5 | using System.Threading.Tasks; 6 | 7 | namespace Demo.API.Controllers.v1 8 | { 9 | /// 10 | /// RESTFull services for Dragon Ball 11 | /// 12 | [ApiController] 13 | [ApiVersion("1.0")] 14 | [Route("v{version:apiVersion}/[controller]")] 15 | public class DragonBallController : BaseController 16 | { 17 | private readonly ICharacterServices _services; 18 | // private readonly IDocumentExecuter _documentExecuter; 19 | // private readonly ISchema _schema; 20 | // IEnumerable _validationRules; 21 | 22 | public DragonBallController(ICharacterServices services) //, IDocumentExecuter documentExecuter, ISchema schema, IEnumerable validationRules) 23 | { 24 | _services = services; 25 | // _documentExecuter = documentExecuter; 26 | // _schema = schema; 27 | // _validationRules = validationRules; 28 | } 29 | 30 | /// 31 | /// Adiciona um novo personagem. 32 | /// 33 | /// Dados do personagem 34 | [HttpPost("character")] 35 | public async Task Post([FromBody]DragonBallPostRequest request) 36 | { 37 | var result = await _services.CreateAsync(request); 38 | 39 | return ParseResult(result, nameof(GetCharacter)); 40 | } 41 | 42 | 43 | /// 44 | /// Adiciona um familiar a um determinado personagem 45 | /// 46 | /// Dados personagem familiar 47 | /// ID do personagem 48 | [HttpPost("character/{id}/relative/")] 49 | public async Task PostRelative(int id, [FromBody]DragonBallPostRelativeRequest request) 50 | { 51 | var result = await _services.CreateRelative(id, request); 52 | 53 | return ParseResult(result, ""); 54 | } 55 | 56 | /// 57 | /// Obtém um determinado personagem por ID 58 | /// 59 | /// Código do personagem 60 | [HttpGet("character/{id}")] 61 | public async Task GetCharacter(int id) 62 | { 63 | var result = await _services.GetByIDAsync(id); 64 | 65 | return ParseResult(result); 66 | } 67 | 68 | //[HttpPost("graphql")] 69 | //public async Task Post([FromBody] GraphQLParameter query) 70 | //{ 71 | // if (query == null) 72 | // { throw new ArgumentNullException(nameof(query)); } 73 | // var inputs = query.Variables.ToInputs(); 74 | // var executionOptions = new ExecutionOptions 75 | // { 76 | // Schema = _schema, 77 | // Query = query.Query, 78 | // Inputs = inputs, 79 | // UserContext = new GraphQLUserContext 80 | // { 81 | // User = User 82 | // }, 83 | // ValidationRules = DocumentValidator.CoreRules().Concat(_validationRules).ToList() 84 | // }; 85 | 86 | // var result = await _documentExecuter.ExecuteAsync(executionOptions).ConfigureAwait(false); 87 | 88 | // if (result.Errors?.Count > 0) 89 | // { 90 | // return BadRequest(result); 91 | // } 92 | 93 | // return Ok(result); 94 | //} 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Demo.API/Controllers/v1/ProfileController.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Services; 2 | using Framework.WebAPI; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.AspNetCore.Mvc; 5 | using System.Threading.Tasks; 6 | 7 | namespace Demo.API.Controllers.v1 8 | { 9 | /// 10 | /// RESTfull services for User profile 11 | /// 12 | [Authorize("Bearer")] 13 | [ApiVersion("1.0")] 14 | [Route("v{version:apiVersion}/[controller]")] 15 | [ApiController] 16 | public class ProfileController : BaseController 17 | { 18 | private readonly IProfileServices _services; 19 | 20 | /// 21 | /// Constructor 22 | /// 23 | /// 24 | public ProfileController(IProfileServices services) 25 | { 26 | _services = services; 27 | } 28 | 29 | [HttpPost("photo")] 30 | public async Task Photo([FromBody] byte[] image) 31 | { 32 | var result = await _services.SaveUserPhotoAsync(GetUserId(), image); 33 | return ParseResult(result); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Demo.API/Controllers/v1/SignInController.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.SignIn; 2 | using Demo.Core.Services; 3 | using Framework.WebAPI; 4 | using Microsoft.AspNetCore.Authorization; 5 | using Microsoft.AspNetCore.Mvc; 6 | using System.Net; 7 | 8 | namespace Demo.API.Controllers.v1 9 | { 10 | /// 11 | /// RESTfull services for SignIn 12 | /// 13 | [AllowAnonymous] 14 | [ApiVersion("1.0")] 15 | [Route("v{version:apiVersion}/[controller]")] 16 | [ApiController] 17 | public class SignInController : BaseController 18 | { 19 | private readonly ISignInServices _services; 20 | 21 | /// 22 | /// Controller 23 | /// 24 | /// Dependência do serviço 25 | public SignInController(ISignInServices services) 26 | { 27 | _services = services; 28 | } 29 | 30 | /// 31 | /// Tenta logar um usuário a partir de e-mail e senha 32 | /// 33 | /// 34 | /// 35 | [HttpPost("token")] 36 | [ProducesResponseType((int)HttpStatusCode.OK)] 37 | [ProducesResponseType((int)HttpStatusCode.BadRequest)] 38 | [ProducesResponseType((int)HttpStatusCode.Conflict)] 39 | public IActionResult Post([FromBody]SignInPostRequest request) 40 | { 41 | var result = _services.GenerateToken(request); 42 | 43 | return ParseResult(result); 44 | } 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Demo.API/Controllers/v1/SingUpController.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.SignUp; 2 | using Demo.Core.Services; 3 | using Framework.WebAPI; 4 | using Microsoft.AspNetCore.Authorization; 5 | using Microsoft.AspNetCore.Mvc; 6 | using System.Net; 7 | 8 | namespace Demo.API.Controllers.v1 9 | { 10 | /// 11 | /// RESTfull services for SignUp 12 | /// 13 | [AllowAnonymous] 14 | [ApiVersion("1.0")] 15 | [Route("v{version:apiVersion}/[controller]")] 16 | [ApiController] 17 | public class SingUpController : BaseController 18 | { 19 | private readonly ISignUpServices _services; 20 | 21 | public SingUpController(ISignUpServices services) 22 | { 23 | _services = services; 24 | } 25 | 26 | /// 27 | /// Cria um novo usuário 28 | /// 29 | /// Dados do usuário 30 | [HttpPost] 31 | [ProducesResponseType((int)HttpStatusCode.OK)] 32 | [ProducesResponseType((int)HttpStatusCode.BadRequest)] 33 | [ProducesResponseType((int)HttpStatusCode.Conflict)] 34 | public IActionResult Post([FromBody]SignUpPostRequest request) 35 | { 36 | var result = _services.Post(request); 37 | 38 | return ParseResult(result); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Demo.API/Controllers/v1/ValuesController.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Demo.Core.Contracts.Values; 3 | using Demo.Core.ExternalServices.Google; 4 | using Demo.Core.Services; 5 | using Framework.WebAPI; 6 | using Microsoft.AspNetCore.Authentication.JwtBearer; 7 | using Microsoft.AspNetCore.Authorization; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.Extensions.Options; 10 | 11 | namespace Demo.API.Controllers 12 | { 13 | /// 14 | /// RESTfull services for Caregivers 15 | /// 16 | [Authorize(JwtBearerDefaults.AuthenticationScheme)] 17 | [ApiVersion("1.0")] 18 | [Route("v{version:apiVersion}/[controller]")] 19 | [ApiController] 20 | public class ValuesController : BaseController 21 | { 22 | private readonly IValuesServices _services; 23 | private readonly IGoogleMapsAPI _mapsAPI; 24 | private readonly GoogleApiConfiguration _googleSettings; 25 | 26 | public ValuesController(IValuesServices services, IGoogleMapsAPI mapsAPI, IOptions googleSettings) 27 | { 28 | _services = services; 29 | _mapsAPI = mapsAPI; 30 | _googleSettings = googleSettings.Value; 31 | } 32 | 33 | [HttpGet] 34 | public IActionResult Get() 35 | { 36 | return Ok(new { Name = "Alef" }); 37 | } 38 | 39 | [AllowAnonymous] 40 | [HttpPost("rabbit")] 41 | public async Task PostMessage([FromBody]PostMessageRequest request) 42 | { 43 | var result = await _services.PostRabbitMessageAsync(request); 44 | 45 | return ParseResult(result); 46 | } 47 | 48 | [HttpGet("cep")] 49 | [AllowAnonymous] 50 | public async Task SearchCEP(string cep) 51 | { 52 | return Ok(await _mapsAPI.SearchAsync(cep, _googleSettings.MapsKey)); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Demo.API/Demo.API.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 1701;1702 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | true 26 | $(NoWarn);1591 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Demo.API/Demo.API.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Demo.API 5 | 6 | 7 | 8 | 9 | RESTFull services for Dragon Ball 10 | 11 | 12 | 13 | 14 | Adiciona um novo personagem. 15 | 16 | Dados do personagem 17 | 18 | 19 | 20 | Adiciona um familiar a um determinado personagem 21 | 22 | Dados personagem familiar 23 | ID do personagem 24 | 25 | 26 | 27 | RESTfull services for User profile 28 | 29 | 30 | 31 | 32 | Constructor 33 | 34 | 35 | 36 | 37 | 38 | RESTfull services for SignIn 39 | 40 | 41 | 42 | 43 | Controller 44 | 45 | Dependência do serviço 46 | 47 | 48 | 49 | Tenta logar um usuário a partir de e-mail e senha 50 | 51 | 52 | 53 | 54 | 55 | 56 | RESTfull services for SignUp 57 | 58 | 59 | 60 | 61 | Cria um novo usuário 62 | 63 | Dados do usuário 64 | 65 | 66 | 67 | RESTfull services for Caregivers 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Demo.API/Migrations/20181208205058_InitCreate.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Demo.Core.Data.MySql; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | namespace Demo.API.Migrations 10 | { 11 | [DbContext(typeof(DbzMySqlContext))] 12 | [Migration("20181208205058_InitCreate")] 13 | partial class InitCreate 14 | { 15 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 16 | { 17 | #pragma warning disable 612, 618 18 | modelBuilder 19 | .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") 20 | .HasAnnotation("Relational:MaxIdentifierLength", 64); 21 | 22 | modelBuilder.Entity("Demo.Application.Data.MySql.Entities.CharacterEntity", b => 23 | { 24 | b.Property("ID") 25 | .ValueGeneratedOnAdd(); 26 | 27 | b.Property("BirthDate"); 28 | 29 | b.Property("CreatedDate"); 30 | 31 | b.Property("Kind"); 32 | 33 | b.Property("Name") 34 | .IsRequired() 35 | .HasColumnType("NVARCHAR(100)"); 36 | 37 | b.Property("UpdatedDate"); 38 | 39 | b.HasKey("ID"); 40 | 41 | b.ToTable("character"); 42 | }); 43 | 44 | modelBuilder.Entity("Demo.Application.Data.MySql.Entities.FamilyEntity", b => 45 | { 46 | b.Property("CharacterID"); 47 | 48 | b.Property("RelativeID"); 49 | 50 | b.Property("CreatedDate"); 51 | 52 | b.Property("Kind"); 53 | 54 | b.Property("UpdatedDate"); 55 | 56 | b.HasKey("CharacterID", "RelativeID"); 57 | 58 | b.HasIndex("RelativeID"); 59 | 60 | b.ToTable("family"); 61 | }); 62 | 63 | modelBuilder.Entity("Demo.Application.Data.MySql.Entities.FamilyEntity", b => 64 | { 65 | b.HasOne("Demo.Application.Data.MySql.Entities.CharacterEntity", "Character") 66 | .WithMany() 67 | .HasForeignKey("CharacterID") 68 | .OnDelete(DeleteBehavior.Cascade); 69 | 70 | b.HasOne("Demo.Application.Data.MySql.Entities.CharacterEntity", "Relative") 71 | .WithMany("Relatives") 72 | .HasForeignKey("RelativeID") 73 | .OnDelete(DeleteBehavior.Cascade); 74 | }); 75 | #pragma warning restore 612, 618 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Demo.API/Migrations/20181208205058_InitCreate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | using Microsoft.EntityFrameworkCore.Migrations; 4 | 5 | namespace Demo.API.Migrations 6 | { 7 | public partial class InitCreate : Migration 8 | { 9 | protected override void Up(MigrationBuilder migrationBuilder) 10 | { 11 | migrationBuilder.CreateTable( 12 | name: "character", 13 | columns: table => new 14 | { 15 | ID = table.Column(nullable: false) 16 | .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), 17 | CreatedDate = table.Column(nullable: false), 18 | UpdatedDate = table.Column(nullable: true), 19 | Name = table.Column(type: "NVARCHAR(100)", nullable: false), 20 | BirthDate = table.Column(nullable: true), 21 | Kind = table.Column(nullable: false) 22 | }, 23 | constraints: table => 24 | { 25 | table.PrimaryKey("PK_character", x => x.ID); 26 | }); 27 | 28 | migrationBuilder.CreateTable( 29 | name: "family", 30 | columns: table => new 31 | { 32 | CharacterID = table.Column(nullable: false), 33 | RelativeID = table.Column(nullable: false), 34 | CreatedDate = table.Column(nullable: false), 35 | UpdatedDate = table.Column(nullable: true), 36 | Kind = table.Column(nullable: false) 37 | }, 38 | constraints: table => 39 | { 40 | table.PrimaryKey("PK_family", x => new { x.CharacterID, x.RelativeID }); 41 | table.ForeignKey( 42 | name: "FK_family_character_CharacterID", 43 | column: x => x.CharacterID, 44 | principalTable: "character", 45 | principalColumn: "ID", 46 | onDelete: ReferentialAction.Cascade); 47 | table.ForeignKey( 48 | name: "FK_family_character_RelativeID", 49 | column: x => x.RelativeID, 50 | principalTable: "character", 51 | principalColumn: "ID", 52 | onDelete: ReferentialAction.Cascade); 53 | }); 54 | 55 | migrationBuilder.CreateIndex( 56 | name: "IX_family_RelativeID", 57 | table: "family", 58 | column: "RelativeID"); 59 | } 60 | 61 | protected override void Down(MigrationBuilder migrationBuilder) 62 | { 63 | migrationBuilder.DropTable( 64 | name: "family"); 65 | 66 | migrationBuilder.DropTable( 67 | name: "character"); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Demo.API/Migrations/DbzMySqlContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Demo.Core.Data.MySql; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | 8 | namespace Demo.API.Migrations 9 | { 10 | [DbContext(typeof(DbzMySqlContext))] 11 | partial class DbzMySqlContextModelSnapshot : ModelSnapshot 12 | { 13 | protected override void BuildModel(ModelBuilder modelBuilder) 14 | { 15 | #pragma warning disable 612, 618 16 | modelBuilder 17 | .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") 18 | .HasAnnotation("Relational:MaxIdentifierLength", 64); 19 | 20 | modelBuilder.Entity("Demo.Application.Data.MySql.Entities.CharacterEntity", b => 21 | { 22 | b.Property("ID") 23 | .ValueGeneratedOnAdd(); 24 | 25 | b.Property("BirthDate"); 26 | 27 | b.Property("CreatedDate"); 28 | 29 | b.Property("Kind"); 30 | 31 | b.Property("Name") 32 | .IsRequired() 33 | .HasColumnType("NVARCHAR(100)"); 34 | 35 | b.Property("UpdatedDate"); 36 | 37 | b.HasKey("ID"); 38 | 39 | b.ToTable("character"); 40 | }); 41 | 42 | modelBuilder.Entity("Demo.Application.Data.MySql.Entities.FamilyEntity", b => 43 | { 44 | b.Property("CharacterID"); 45 | 46 | b.Property("RelativeID"); 47 | 48 | b.Property("CreatedDate"); 49 | 50 | b.Property("Kind"); 51 | 52 | b.Property("UpdatedDate"); 53 | 54 | b.HasKey("CharacterID", "RelativeID"); 55 | 56 | b.HasIndex("RelativeID"); 57 | 58 | b.ToTable("family"); 59 | }); 60 | 61 | modelBuilder.Entity("Demo.Application.Data.MySql.Entities.FamilyEntity", b => 62 | { 63 | b.HasOne("Demo.Application.Data.MySql.Entities.CharacterEntity", "Character") 64 | .WithMany() 65 | .HasForeignKey("CharacterID") 66 | .OnDelete(DeleteBehavior.Cascade); 67 | 68 | b.HasOne("Demo.Application.Data.MySql.Entities.CharacterEntity", "Relative") 69 | .WithMany("Relatives") 70 | .HasForeignKey("RelativeID") 71 | .OnDelete(DeleteBehavior.Cascade); 72 | }); 73 | #pragma warning restore 612, 618 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Demo.API/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using App.Metrics; 4 | using App.Metrics.AspNetCore; 5 | using App.Metrics.Formatters.Prometheus; 6 | using Demo.Core.Data.MySql; 7 | using Microsoft.AspNetCore; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.Extensions.DependencyInjection; 10 | 11 | namespace Demo.API 12 | { 13 | public class Program 14 | { 15 | public static IMetricsRoot Metrics { get; set; } 16 | 17 | public static void Main(string[] args) 18 | { 19 | Metrics = AppMetrics.CreateDefaultBuilder() 20 | .OutputMetrics.AsPrometheusPlainText() 21 | // .OutputMetrics.AsPrometheusProtobuf() 22 | .Build(); 23 | 24 | 25 | var host = CreateWebHostBuilder(args).Build(); 26 | 27 | using (var scope = host.Services.CreateScope()) 28 | { 29 | var services = scope.ServiceProvider; 30 | try 31 | { 32 | var context = services.GetRequiredService(); 33 | DbzInitializer.Initialize(context); 34 | } 35 | catch (Exception ex) 36 | { 37 | //var logger = services.GetRequiredService>(); 38 | Console.WriteLine("An error occurred while seeding the database."); 39 | Console.WriteLine(ex.Message); 40 | } 41 | } 42 | 43 | 44 | host.Run(); 45 | } 46 | 47 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) 48 | { 49 | 50 | return WebHost.CreateDefaultBuilder(args) 51 | .ConfigureMetrics(Metrics) 52 | .UseMetrics( 53 | options => 54 | { 55 | options.EndpointOptions = endpointsOptions => 56 | { 57 | // endpointsOptions.MetricsTextEndpointOutputFormatter = Metrics.OutputMetricsFormatters.OfType().First(); 58 | endpointsOptions.MetricsEndpointOutputFormatter = Metrics.OutputMetricsFormatters.OfType().First(); 59 | }; 60 | }) 61 | .UseStartup(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Demo.API/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:62566", 8 | "sslPort": 0 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "api/values", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "API": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | }, 26 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Demo.API/Startup.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MongoDB; 2 | using Demo.Core.Data.MySql; 3 | using Demo.Core.ExternalServices; 4 | using Demo.Core.GraphQL; 5 | using Demo.Core.Services; 6 | using Demo.Core.Validations; 7 | using Framework.Data.CacheProviders; 8 | using Framework.Data.MongoDB; 9 | using Framework.MessageBroker.RabbitMQ; 10 | using Framework.WebAPI.Hosting; 11 | using Microsoft.AspNetCore.Builder; 12 | using Microsoft.AspNetCore.Hosting; 13 | using Microsoft.Extensions.Configuration; 14 | using Microsoft.Extensions.DependencyInjection; 15 | 16 | namespace Demo.API 17 | { 18 | public class Startup : BaseStartup 19 | { 20 | public Startup(IConfiguration configuration) : base(configuration) 21 | { 22 | } 23 | 24 | public override void AfterConfigureServices(IServiceCollection services) 25 | { 26 | //Adicioando as validações 27 | services.AddValidators(); 28 | 29 | //Services 30 | services.AddServices(); 31 | 32 | //Repositories 33 | services.AddMongoDB(Configuration); 34 | services.AddMongoRepositories(); 35 | 36 | services.AddMySql(Configuration); 37 | services.AddExternalServices(Configuration); 38 | 39 | //GraphQL 40 | services.AddGraphQLTypes(); 41 | 42 | //Redis 43 | services.AddRedisCache(Configuration); 44 | 45 | 46 | //RabbitMQ 47 | services.AddRabbitBroker("demo.api", Configuration); 48 | } 49 | 50 | public override void BeforeConfigureApp(IApplicationBuilder app, IHostingEnvironment env) 51 | { 52 | if (env.IsDevelopment()) 53 | { 54 | app.UseDeveloperExceptionPage(); 55 | } 56 | else 57 | { 58 | app.UseHsts(); 59 | app.UseHttpsRedirection(); 60 | } 61 | } 62 | 63 | public override void AfterConfigureApp(IApplicationBuilder app, IHostingEnvironment env) 64 | { 65 | app.AddGraphQLTypes(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Demo.Consumer.HandleMessages/.env: -------------------------------------------------------------------------------- 1 | ConnectionStrings__RabbitMQ=amqp://guest:guest@localhost 2 | RabbitMQSettings__QOS=5 3 | -------------------------------------------------------------------------------- /Demo.Consumer.HandleMessages/Demo.Consumer.HandleMessages.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Demo.Consumer.HandleMessages/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore; 3 | using Microsoft.AspNetCore.Hosting; 4 | 5 | namespace Demo.Consumer.HandleMessages 6 | { 7 | public class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | var host = CreateWebHostBuilder(args).Build(); 12 | 13 | host.Run(); 14 | } 15 | 16 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 17 | WebHost.CreateDefaultBuilder(args) 18 | .UseStartup(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Demo.Consumer.HandleMessages/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:60164/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "Demo.Consumer.HandleMessages": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:60165/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Demo.Consumer.HandleMessages/Startup.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Demo.Consumer.HandleMessages.Tasks; 3 | using Framework.Core.Serializer; 4 | using Framework.MessageBroker.RabbitMQ; 5 | using Framework.WebAPI.HealthCheck; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | 11 | namespace Demo.Consumer.HandleMessages 12 | { 13 | public class Startup 14 | { 15 | public IConfiguration Configuration { get; } 16 | 17 | public Startup(IConfiguration configuration) 18 | { 19 | Configuration = configuration; 20 | } 21 | 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | services.AddSingleton(); 25 | services.AddHealthCheck(); 26 | 27 | services.AddRabbitBroker("demo.consumer", Configuration); 28 | services.AddHostedService(); 29 | } 30 | 31 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 32 | { 33 | app.UseHealthCheck(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Demo.Consumer.HandleMessages/Tasks/TesteMessageHandlerTask.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Demo.Core.Messages.RabbitMQ; 4 | using Framework.MessageBroker.RabbitMQ; 5 | using Microsoft.Extensions.Hosting; 6 | using Microsoft.Extensions.Logging; 7 | 8 | namespace Demo.Consumer.HandleMessages.Tasks 9 | { 10 | public class TesteMessageHandlerTask : BackgroundService 11 | { 12 | private readonly IRabbitMQSubscriber _subscriber; 13 | private readonly ILogger _logger; 14 | 15 | public TesteMessageHandlerTask(IRabbitMQSubscriber subscriber, ILogger logger) 16 | { 17 | _subscriber = subscriber; 18 | _logger = logger; 19 | } 20 | 21 | public override void Dispose() 22 | { 23 | _subscriber.Dispose(); 24 | base.Dispose(); 25 | } 26 | 27 | protected override Task ExecuteAsync(CancellationToken stoppingToken) 28 | { 29 | _subscriber.StartConsume(ConsumeTesteMessage); 30 | return Task.CompletedTask; 31 | } 32 | 33 | private bool ConsumeTesteMessage(TesteMessage message) 34 | { 35 | _logger.LogInformation($"ConsumeTesteMessage, Campo = {message.Campo}"); 36 | return true; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Demo.Core.Tests/Demo.Core.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netcoreapp2.2 4 | 5 | false 6 | 7 | 8 | 9 | 10 | all 11 | runtime; build; native; contentfiles; analyzers 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Demo.Core.Tests/ExternalServices/Google/GoogleMapAPITests.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Threading.Tasks; 3 | using Demo.Core.ExternalServices.Google; 4 | using Refit; 5 | using RichardSzalay.MockHttp; 6 | using Shouldly; 7 | using Xunit; 8 | 9 | namespace Demo.Core.Tests.ExternalServices.Google 10 | { 11 | public class GoogleMapsRepositoryTests 12 | { 13 | private readonly MockHttpMessageHandler mockHandler; 14 | private readonly IGoogleMapsAPI _fixture; 15 | 16 | public GoogleMapsRepositoryTests() 17 | { 18 | mockHandler = new MockHttpMessageHandler(); 19 | 20 | var settings = new RefitSettings 21 | { 22 | HttpMessageHandlerFactory = () => mockHandler 23 | }; 24 | 25 | _fixture = RestService.For("http://uri", settings); 26 | } 27 | 28 | [Fact] 29 | public async Task GetGeoCodeByCEPAsync_ShouldSuccess() 30 | { 31 | //Act 32 | mockHandler.Expect(HttpMethod.Get, "http://uri/json") 33 | .Respond("application/json", "{status: 'ok'}"); 34 | 35 | var result = await _fixture.SearchAsync("09071-425", "key123"); 36 | 37 | //Assert 38 | result.ShouldNotBeNull(); 39 | result.status.ShouldBe("ok"); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Demo.Core.Tests/Validations/DragonBall/DragonBallPostRequestValidatorTest.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Shared.Enum; 2 | using Demo.Core.Validations.DragonBall; 3 | using FluentValidation.TestHelper; 4 | using Xunit; 5 | 6 | namespace Demo.Core.Tests.Validations.Dragonbal 7 | { 8 | public class DragonBallPostRequestValidatorTest 9 | { 10 | private DragonBallPostRequestValidator validator; 11 | 12 | public DragonBallPostRequestValidatorTest() 13 | { 14 | validator = new DragonBallPostRequestValidator(); 15 | } 16 | 17 | [Theory] 18 | [InlineData(null)] 19 | [InlineData("")] 20 | public void WhenNameNull_ShouldHaveError(string value) 21 | { 22 | validator.ShouldHaveValidationErrorFor(x => x.Name, value); 23 | } 24 | 25 | [Fact] 26 | public void WhenHaveName_ShouldHaveNoError() 27 | { 28 | validator.ShouldNotHaveValidationErrorFor(x => x.Name, "Alef"); 29 | } 30 | 31 | [Theory] 32 | [InlineData(null)] 33 | [InlineData("")] 34 | public void WhenBirthNull_ShouldHaveError(string value) 35 | { 36 | validator.ShouldHaveValidationErrorFor(x => x.BirthDate, value); 37 | } 38 | 39 | [Fact] 40 | public void WhenHaveBirth_ShouldHaveNoError() 41 | { 42 | validator.ShouldNotHaveValidationErrorFor(x => x.BirthDate, "18/11/1993"); 43 | } 44 | 45 | [Fact] 46 | public void WhenHaveKind_ShouldHaveNoError() 47 | { 48 | validator.ShouldNotHaveValidationErrorFor(x => x.Kind, ECharecterKind.Sayajin); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Demo.Core.Tests/Validations/SignUp/SignUpPostRequestValidatorTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Demo.Core.Contracts.SignUp; 3 | using Demo.Core.Shared.Enum; 4 | using Demo.Core.Validations.SignUp; 5 | using FluentValidation.TestHelper; 6 | using Xunit; 7 | 8 | namespace Demo.Core.Tests.Validations.SignUp 9 | { 10 | public class SignUpPostRequestValidatorTest 11 | { 12 | private readonly SignUpPostRequestValidator validator; 13 | public SignUpPostRequestValidatorTest() 14 | { 15 | validator = new SignUpPostRequestValidator(); 16 | } 17 | 18 | [Theory] 19 | [InlineData(null)] 20 | [InlineData("")] 21 | public void WhenNameNull_ShouldHaveError(string value) 22 | { 23 | validator.ShouldHaveValidationErrorFor(x => x.Name, value); 24 | } 25 | 26 | [Fact] 27 | public void WhenHaveGender_ShouldHaveNoError() 28 | { 29 | validator.ShouldNotHaveValidationErrorFor(x => x.Gender, EGender.Male); 30 | } 31 | 32 | [Theory] 33 | [InlineData("")] 34 | [InlineData("alef@.com")] 35 | [InlineData("alef")] 36 | [InlineData("@.com")] 37 | public void WhenEmailInvalid_ShouldHaveError(string value) 38 | { 39 | validator.ShouldHaveValidationErrorFor(x => x.Email, value); 40 | } 41 | 42 | [Theory] 43 | [InlineData("alef.carlos@gmai.com")] 44 | [InlineData("alef@alef.com.br")] 45 | public void WhenEmailValid_ShouldHaveNoError(string value) 46 | { 47 | validator.ShouldNotHaveValidationErrorFor(x => x.Email, value); 48 | } 49 | 50 | [Fact] 51 | public void WhenPasswordInvalid_ShouldHaveError() 52 | { 53 | validator.ShouldHaveValidationErrorFor(x => x.Password, string.Empty); 54 | } 55 | 56 | [Fact] 57 | public void WhenConfirmPasswordInvalid_ShouldHaveError() 58 | { 59 | //Arrange 60 | var request = new SignUpPostRequest 61 | { 62 | Password = "alef", 63 | ConfirmPassword = "alef2" 64 | }; 65 | 66 | 67 | validator.ShouldHaveValidationErrorFor(x => x.Password, request); 68 | } 69 | 70 | [Fact] 71 | public void WhenPasswordValid_ShouldHaveNoError() 72 | { 73 | //Arrange 74 | var request = new SignUpPostRequest 75 | { 76 | Password = "alef", 77 | ConfirmPassword = "alef" 78 | }; 79 | 80 | //Assert 81 | validator.ShouldNotHaveValidationErrorFor(x => x.ConfirmPassword, request); 82 | } 83 | 84 | [Fact] 85 | public void WhenBotnDateInvalid_ShouldHaveError() 86 | { 87 | validator.ShouldHaveValidationErrorFor(x => x.BornDate, DateTime.MinValue); 88 | } 89 | 90 | [Fact] 91 | public void WhenBotnDateInvalid_ShouldHaveNoError() 92 | { 93 | //Arrange 94 | var date = new DateTime(1993, 11, 18); 95 | //Assert 96 | validator.ShouldNotHaveValidationErrorFor(x => x.BornDate, date); 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /Demo.Core/Contracts/DragonBall/Request/DragonBallPostRelativeRequest.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Shared.Enum; 2 | 3 | namespace Demo.Core.Contracts.DragonBall.Request 4 | { 5 | public class DragonBallPostRelativeRequest 6 | { 7 | /// 8 | /// ID do personagem relativo 9 | /// 10 | public int RelativeId { get; set; } 11 | 12 | /// 13 | /// Grau de parentesco do personagem 14 | /// 15 | public ERelativeKind Kind { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Demo.Core/Contracts/DragonBall/Request/DragonBallPostRequest.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MySql.Entities; 2 | using Demo.Core.Shared.Enum; 3 | 4 | namespace Demo.Core.Contracts.DragonBall.Request 5 | { 6 | public class DragonBallPostRequest 7 | { 8 | /// 9 | /// Nome do personagem 10 | /// 11 | public string Name { get; set; } 12 | 13 | /// 14 | /// Informação de nascimento 15 | /// 16 | public string BirthDate { get; set; } 17 | 18 | /// 19 | /// Raça do personagem 20 | /// 21 | public ECharecterKind Kind { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Demo.Core/Contracts/GraphQL/GraphQLParameter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | 3 | namespace Demo.Core.Contracts.GraphQL 4 | { 5 | public class GraphQLParameter 6 | { 7 | public string OperationName { get; set; } 8 | public string NamedQuery { get; set; } 9 | public string Query { get; set; } 10 | public JObject Variables { get; set; } //https://github.com/graphql-dotnet/graphql-dotnet/issues/389 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Demo.Core/Contracts/Shared/AddressRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Demo.Core.Contracts.Shared 2 | { 3 | /// 4 | /// Contrato de request comum para informação de endereço 5 | /// 6 | public class AddressRequest 7 | { 8 | /// 9 | /// CEP 10 | /// 11 | public string PostalCode { get; set; } 12 | 13 | /// 14 | /// Nome da rua 15 | /// 16 | public string Street { get; set; } 17 | 18 | /// 19 | /// Número da casa 20 | /// 21 | public string Number { get; set; } 22 | 23 | /// 24 | /// Informações complementares 25 | /// 26 | public string AdditionalInformations { get; set; } 27 | 28 | /// 29 | /// Nome do bairro 30 | /// 31 | public string District { get; set; } 32 | 33 | /// 34 | /// Sigla do estado 35 | /// 36 | public string State { get; set; } 37 | 38 | /// 39 | /// Nome do país 40 | /// 41 | public string Country { get; set; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Demo.Core/Contracts/SignIn/SignInPostRequest.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.Shared; 2 | using Demo.Core.Shared.Enum; 3 | using System; 4 | 5 | namespace Demo.Core.Contracts.SignIn 6 | { 7 | /// 8 | /// Contrato de criação de usuário 9 | /// 10 | public class SignInPostRequest 11 | { 12 | /// 13 | /// E-mail do usuário 14 | /// 15 | public string Email { get; set; } 16 | 17 | /// 18 | /// Senha do usuário 19 | /// 20 | public string Password { get; set; } 21 | 22 | /// 23 | /// Nível de obtenção de token 24 | /// password - Gerar um novo token 25 | /// refresh_token - Gera um novo token através de um refresh_token 26 | /// 27 | public string GrantType { get; set; } 28 | 29 | /// 30 | /// Refresh token para tentar ser revalidado 31 | /// 32 | public string RefreshToken { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Demo.Core/Contracts/SignUp/SignUpPostRequest.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.Shared; 2 | using Demo.Core.Shared.Enum; 3 | using System; 4 | using System.ComponentModel.DataAnnotations; 5 | 6 | namespace Demo.Core.Contracts.SignUp 7 | { 8 | /// 9 | /// Contrato de criação de usuário 10 | /// 11 | public class SignUpPostRequest 12 | { 13 | /// 14 | /// Nome completo do usuário 15 | /// 16 | public string Name { get; set; } 17 | 18 | /// 19 | /// Data de nascimento 20 | /// 21 | public DateTime BornDate { get; set; } 22 | 23 | /// 24 | /// Sexo do cliente 25 | /// 26 | public EGender Gender { get; set; } 27 | 28 | /// 29 | /// Endereço completo do cliente 30 | /// 31 | public AddressRequest Address { get; set; } 32 | 33 | /// 34 | /// E-mail do usuário 35 | /// 36 | public string Email { get; set; } 37 | 38 | /// 39 | /// Senha do usuário 40 | /// 41 | public string Password { get; set; } 42 | 43 | /// 44 | /// Confirmação de senha do usuário 45 | /// 46 | public string ConfirmPassword { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Demo.Core/Contracts/Values/PostMessageRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Demo.Core.Contracts.Values 2 | { 3 | public class PostMessageRequest 4 | { 5 | public string Campo { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /Demo.Core/Data/MongoDB/Entities/UserEntity.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.SignUp; 2 | using Demo.Core.Shared.Enum; 3 | using Framework.Data.MongoDB; 4 | using System; 5 | 6 | namespace Demo.Core.Data.MongoDB.Entities 7 | { 8 | public class UserEntity : MongoEntityBase 9 | { 10 | public UserEntity(SignUpPostRequest request) 11 | { 12 | Name = request.Name; 13 | BornDate = request.BornDate; 14 | Gender = request.Gender; 15 | Email = request.Email; 16 | Password = request.Password; 17 | } 18 | 19 | /// 20 | /// Nome completo do usuário 21 | /// 22 | public string Name { get; set; } 23 | 24 | /// 25 | /// Data de nascimento 26 | /// 27 | public DateTime BornDate { get; set; } 28 | 29 | /// 30 | /// Sexo do cliente 31 | /// 32 | public EGender Gender { get; set; } 33 | 34 | /// 35 | /// E-mail do usuário 36 | /// 37 | public string Email { get; set; } 38 | 39 | /// 40 | /// Senha do usuário 41 | /// 42 | public string Password { get; set; } 43 | 44 | /// 45 | /// Token a ser utilizado para renovar sessão 46 | /// 47 | public string RefreshToken { get; set; } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Demo.Core/Data/MongoDB/Repositories/IUserRepository.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MongoDB.Entities; 2 | using Framework.Data.MongoDB; 3 | 4 | namespace Demo.Core.Data.MongoDB.Repositories 5 | { 6 | public interface IUserRepository : IMongoRepositoryBase 7 | { 8 | /// 9 | /// Obtém um determinado usuário por e-mail 10 | /// 11 | /// E-mail do usuário. 12 | UserEntity GetByEmail(string email); 13 | 14 | /// 15 | /// Obtém um determinado usuário por refreshToken 16 | /// 17 | /// RefreshToken do usuário. 18 | UserEntity GetByRefreshToken(string token); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Demo.Core/Data/MongoDB/Repositories/UserRepository.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MongoDB.Entities; 2 | using Framework.Data.MongoDB; 3 | 4 | namespace Demo.Core.Data.MongoDB.Repositories 5 | { 6 | public class UserRepository : MongoRepositoryBase, IUserRepository 7 | { 8 | public UserRepository(MongoDBConnectionWraper connection) : base(connection) 9 | { 10 | } 11 | 12 | /// 13 | /// Obtém um determinado usuário por e-mail 14 | /// 15 | /// 16 | public UserEntity GetByEmail(string email) => ReadFirstOrDefault(x => x.Email.Equals(email)); 17 | 18 | /// 19 | /// Obtém um determinado usuário por refreshToken 20 | /// 21 | /// RefreshToken do usuário. 22 | public UserEntity GetByRefreshToken(string token) => ReadFirstOrDefault(x => x.RefreshToken.Equals(token)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Demo.Core/Data/MongoDB/RepositoriesDI.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MongoDB.Repositories; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Demo.Core.Data.MongoDB 5 | { 6 | public static class RepositoriesDI 7 | { 8 | public static IServiceCollection AddMongoRepositories(this IServiceCollection services) 9 | { 10 | services.AddSingleton(); 11 | 12 | return services; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Demo.Core/Data/MySql/DbzInitializer.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MySql.Entities; 2 | using Demo.Core.Shared.Enum; 3 | using System.Linq; 4 | 5 | namespace Demo.Core.Data.MySql 6 | { 7 | public static class DbzInitializer 8 | { 9 | public static void Initialize(DbzMySqlContext context) 10 | { 11 | context.Database.EnsureCreated(); 12 | 13 | if (context.Characters.Any()) 14 | return; 15 | 16 | //Adicionar personagens 17 | var characters = new CharacterEntity[] 18 | { 19 | new CharacterEntity 20 | { 21 | ID =1, 22 | Name = "Goku", 23 | BirthDate = "Ano 737", 24 | Kind = ECharecterKind.Sayajin, 25 | }, 26 | new CharacterEntity 27 | { 28 | ID = 2, 29 | Name = "Vegeta", 30 | BirthDate = "Ano 732", 31 | Kind = ECharecterKind.Sayajin, 32 | }, 33 | new CharacterEntity 34 | { 35 | ID = 3, 36 | Name = "Kuririn", 37 | BirthDate = "Ano 736", 38 | Kind = ECharecterKind.Human, 39 | }, 40 | new CharacterEntity 41 | { 42 | ID = 4, 43 | Name = "Mestre Kame", 44 | BirthDate = "Ano 430", 45 | Kind = ECharecterKind.Human, 46 | }, 47 | new CharacterEntity 48 | { 49 | ID = 5, 50 | Name = "Bardock", 51 | BirthDate = "-", 52 | Kind = ECharecterKind.Sayajin, 53 | }, 54 | new CharacterEntity 55 | { 56 | ID = 6, 57 | Name = "Gohan", 58 | BirthDate = "18 de Maio, Ano 757", 59 | Kind = ECharecterKind.Human, 60 | }, 61 | new CharacterEntity 62 | { 63 | ID = 7, 64 | Name = "Trunks", 65 | BirthDate = "Ano 766", 66 | Kind = ECharecterKind.Human, 67 | }, 68 | new CharacterEntity 69 | { 70 | ID = 8, 71 | Name = "Goten", 72 | BirthDate = "Ano 767", 73 | Kind = ECharecterKind.Human, 74 | }, 75 | }; 76 | 77 | context.Characters.AddRange(characters); 78 | 79 | //Adicionar parentesco 80 | var families = new FamilyEntity[] 81 | { 82 | //Bardock - Goku 83 | new FamilyEntity 84 | { 85 | CharacterID =5, 86 | RelativeID = 1, 87 | Kind = ERelativeKind.Father 88 | }, 89 | //Goku - Bardock 90 | new FamilyEntity 91 | { 92 | CharacterID = 1, 93 | RelativeID = 5, 94 | Kind = ERelativeKind.Son 95 | }, 96 | //Goku - Gohan 97 | new FamilyEntity 98 | { 99 | CharacterID =1, 100 | RelativeID = 6, 101 | Kind = ERelativeKind.Father 102 | }, 103 | //Gohan - Goku 104 | new FamilyEntity 105 | { 106 | CharacterID =6, 107 | RelativeID = 1, 108 | Kind = ERelativeKind.Son 109 | }, 110 | //Goku - Goten 111 | new FamilyEntity 112 | { 113 | CharacterID =1, 114 | RelativeID = 8, 115 | Kind = ERelativeKind.Father 116 | }, 117 | //Goten - Goku 118 | new FamilyEntity 119 | { 120 | CharacterID =8, 121 | RelativeID = 1, 122 | Kind = ERelativeKind.Son 123 | }, 124 | //Goten - Gohan 125 | new FamilyEntity 126 | { 127 | CharacterID =8, 128 | RelativeID = 6, 129 | Kind = ERelativeKind.Brother 130 | }, 131 | //Gohan - Gohan 132 | new FamilyEntity 133 | { 134 | CharacterID =6, 135 | RelativeID = 8, 136 | Kind = ERelativeKind.Brother 137 | }, 138 | //Vegeta - Trunks 139 | new FamilyEntity 140 | { 141 | CharacterID =2, 142 | RelativeID = 7, 143 | Kind = ERelativeKind.Father 144 | }, 145 | //Trunks - Vegeta 146 | new FamilyEntity 147 | { 148 | CharacterID =7, 149 | RelativeID = 2, 150 | Kind = ERelativeKind.Son 151 | } 152 | }; 153 | 154 | context.Families.AddRange(families); 155 | 156 | context.SaveChanges(); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /Demo.Core/Data/MySql/DbzMySqlContext.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MySql.Entities; 2 | using Demo.Core.Data.MySql.ModelBuilders; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace Demo.Core.Data.MySql 6 | { 7 | /// 8 | /// Contexto de conexão para a base DBZ 9 | /// 10 | public class DbzMySqlContext : DbContext 11 | { 12 | public DbSet Characters { get; set; } 13 | public DbSet Families { get; set; } 14 | 15 | public DbzMySqlContext(DbContextOptions options) : base(options) 16 | { 17 | Database.Migrate(); 18 | } 19 | 20 | protected override void OnModelCreating(ModelBuilder modelBuilder) 21 | { 22 | modelBuilder.CharacterModelBuilder(); 23 | modelBuilder.FamilyModelBuilder(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Demo.Core/Data/MySql/Entities/CharacterEntity.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.DragonBall.Request; 2 | using Demo.Core.GraphQL.Types.Character.Models; 3 | using Demo.Core.Shared.Enum; 4 | using Framework.Data.EntityFramework; 5 | using System.Collections.Generic; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | 8 | namespace Demo.Core.Data.MySql.Entities 9 | { 10 | /// 11 | /// Entidade de personagem 12 | /// 13 | public class CharacterEntity : EFEntityBase 14 | { 15 | 16 | public CharacterEntity() 17 | { 18 | 19 | } 20 | 21 | public CharacterEntity(CharacterModel graphModel) 22 | { 23 | Name = graphModel.Name; 24 | Kind = graphModel.Kind; 25 | BirthDate = graphModel.BirthDate; 26 | } 27 | 28 | public CharacterEntity(DragonBallPostRequest request) 29 | { 30 | Name = request.Name; 31 | Kind = request.Kind; 32 | BirthDate = request.BirthDate; 33 | } 34 | 35 | /// 36 | /// ID do registro 37 | /// 38 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 39 | public int ID { get; set; } 40 | 41 | /// 42 | /// Nome do personagem 43 | /// 44 | public string Name { get; set; } 45 | 46 | /// 47 | /// Data de nascimento 48 | /// 49 | public string BirthDate { get; set; } 50 | 51 | /// 52 | /// Especie 53 | /// 54 | public ECharecterKind Kind { get; set; } 55 | 56 | /// 57 | /// Lista de parentes 58 | /// 59 | public List Relatives { get; set; } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Demo.Core/Data/MySql/Entities/FamilyEntity.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.DragonBall.Request; 2 | using Demo.Core.Shared.Enum; 3 | using Framework.Data.EntityFramework; 4 | 5 | namespace Demo.Core.Data.MySql.Entities 6 | { 7 | /// 8 | /// Entidade de familiares 9 | /// 10 | public class FamilyEntity : EFEntityBase 11 | { 12 | public FamilyEntity() 13 | { 14 | 15 | } 16 | 17 | public FamilyEntity(DragonBallPostRelativeRequest request) 18 | { 19 | RelativeID = request.RelativeId; 20 | Kind = request.Kind; 21 | } 22 | 23 | /// 24 | /// FK do personagem 25 | /// 26 | public int CharacterID { get; set; } 27 | public CharacterEntity Character { get; set; } 28 | 29 | /// 30 | /// FK do id do familiar 31 | /// 32 | public int RelativeID { get; set; } 33 | public CharacterEntity Relative { get; set; } 34 | 35 | /// 36 | /// Grau de parentesco 37 | /// 38 | public ERelativeKind Kind { get; set; } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Demo.Core/Data/MySql/EntityModelBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MySql.Entities; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 4 | 5 | namespace Demo.Core.Data.MySql.ModelBuilders 6 | { 7 | public static class EntityModelBuilderExtensions 8 | { 9 | public static EntityTypeBuilder CharacterModelBuilder(this ModelBuilder modelBuilder) 10 | { 11 | var entityBuilder = modelBuilder.Entity(); 12 | 13 | entityBuilder.ToTable("character"); 14 | 15 | entityBuilder 16 | .Property(x => x.Name) 17 | .HasColumnType("NVARCHAR(100)") 18 | .IsRequired(); 19 | 20 | entityBuilder 21 | .Property(x => x.Kind) 22 | .IsRequired(); 23 | 24 | return entityBuilder; 25 | } 26 | 27 | public static EntityTypeBuilder FamilyModelBuilder(this ModelBuilder modelBuilder) 28 | { 29 | var entityBuilder = modelBuilder.Entity(); 30 | 31 | entityBuilder.ToTable("family"); 32 | 33 | entityBuilder 34 | .HasKey(x => new { x.CharacterID, x.RelativeID }); 35 | 36 | //https://stackoverflow.com/questions/42745465/ef-core-many-to-many-relationship-on-a-class/44574378#44574378 37 | entityBuilder 38 | .HasOne(c => c.Character) 39 | .WithMany() 40 | .HasForeignKey(r => r.CharacterID); 41 | 42 | entityBuilder 43 | .HasOne(r => r.Relative) 44 | .WithMany(c => c.Relatives) 45 | .HasForeignKey(r => r.RelativeID); 46 | 47 | return entityBuilder; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Demo.Core/Data/MySql/MySqlExtensions.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MySql.Repositories; 2 | using Framework.Data.MySql; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Pomelo.EntityFrameworkCore.MySql.Infrastructure; 7 | using System; 8 | 9 | namespace Demo.Core.Data.MySql 10 | { 11 | public static class MySqlExtensions 12 | { 13 | public static IServiceCollection AddMySql(this IServiceCollection services, IConfiguration configuration) 14 | { 15 | var connection = configuration.GetConnectionString("MYSQL"); 16 | 17 | services.AddDbContextPool(options => 18 | { 19 | options.UseMySql(connection, mySqlOptions => 20 | { 21 | mySqlOptions.ServerVersion(new Version(5, 0), ServerType.MySql); 22 | mySqlOptions.MigrationsAssembly("Demo.API"); 23 | }); 24 | }); 25 | 26 | services.AddScoped(); 27 | services.AddScoped(); 28 | 29 | services.AddMySqlHealthCheck(configuration); 30 | 31 | return services; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Demo.Core/Data/MySql/Repositories/CharacterRepository.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MySql.Entities; 2 | using Framework.Data.EntityFramework; 3 | 4 | namespace Demo.Core.Data.MySql.Repositories 5 | { 6 | public class CharacterRepository : EFRepositoryBase, ICharacterRepository 7 | { 8 | public CharacterRepository(DbzMySqlContext context) : base(context) 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Demo.Core/Data/MySql/Repositories/FamilyRepository.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MySql.Entities; 2 | using Framework.Data.EntityFramework; 3 | using Microsoft.EntityFrameworkCore; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace Demo.Core.Data.MySql.Repositories 9 | { 10 | public class FamilyRepository : EFRepositoryBase, IFamilyRepository 11 | { 12 | public FamilyRepository(DbzMySqlContext context) : base(context) 13 | { 14 | } 15 | 16 | public Task> GetRelativesAsync(int characterId) 17 | { 18 | return Query() 19 | .AsNoTracking() 20 | .Include(x => x.Relative) 21 | .Where(x => x.CharacterID == characterId).ToListAsync(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Demo.Core/Data/MySql/Repositories/ICharacterRepository.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MySql.Entities; 2 | using Framework.Data.EntityFramework; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace Demo.Core.Data.MySql.Repositories 7 | { 8 | public interface ICharacterRepository : IEFRepository 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Demo.Core/Data/MySql/Repositories/IFamilyRepository.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MySql.Entities; 2 | using Framework.Data.EntityFramework; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace Demo.Core.Data.MySql.Repositories 7 | { 8 | public interface IFamilyRepository : IEFRepository 9 | { 10 | /// 11 | /// Obtém todos os parentes de um determinado personagem 12 | /// 13 | /// ID do personagem. 14 | Task> GetRelativesAsync(int characterId); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Demo.Core/Demo.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 1701;1702 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | true 35 | $(NoWarn);1591 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Demo.Core/ExternalServices/Google/GoogleApiConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Demo.Core.ExternalServices.Google 2 | { 3 | public class GoogleApiConfiguration 4 | { 5 | public string MapsKey 6 | { 7 | get; set; 8 | } 9 | 10 | public string GeoCodeURI { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Demo.Core/ExternalServices/Google/IGoogleMapsAPI.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Refit; 3 | 4 | namespace Demo.Core.ExternalServices.Google 5 | { 6 | public interface IGoogleMapsAPI 7 | { 8 | [Get("/json?address={cep}&sensor=true&key={key}")] 9 | Task SearchAsync(string cep, string key); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Demo.Core/ExternalServices/Google/Views/GoogleGeoCodeView.cs: -------------------------------------------------------------------------------- 1 | namespace Demo.Core.ExternalServices.Google 2 | { 3 | public class GoogleGeoCodeView 4 | { 5 | public Result[] results { get; set; } 6 | public string status { get; set; } 7 | } 8 | 9 | public class Result 10 | { 11 | public Address_Components[] address_components { get; set; } 12 | public string formatted_address { get; set; } 13 | public Geometry geometry { get; set; } 14 | public string place_id { get; set; } 15 | public string[] types { get; set; } 16 | } 17 | 18 | public class Geometry 19 | { 20 | public Bounds bounds { get; set; } 21 | public Location location { get; set; } 22 | public string location_type { get; set; } 23 | public Viewport viewport { get; set; } 24 | } 25 | 26 | public class Bounds 27 | { 28 | public Northeast northeast { get; set; } 29 | public Southwest southwest { get; set; } 30 | } 31 | 32 | public class Northeast 33 | { 34 | public float lat { get; set; } 35 | public float lng { get; set; } 36 | } 37 | 38 | public class Southwest 39 | { 40 | public float lat { get; set; } 41 | public float lng { get; set; } 42 | } 43 | 44 | public class Location 45 | { 46 | public float lat { get; set; } 47 | public float lng { get; set; } 48 | } 49 | 50 | public class Viewport 51 | { 52 | public Northeast1 northeast { get; set; } 53 | public Southwest1 southwest { get; set; } 54 | } 55 | 56 | public class Northeast1 57 | { 58 | public float lat { get; set; } 59 | public float lng { get; set; } 60 | } 61 | 62 | public class Southwest1 63 | { 64 | public float lat { get; set; } 65 | public float lng { get; set; } 66 | } 67 | 68 | public class Address_Components 69 | { 70 | public string long_name { get; set; } 71 | public string short_name { get; set; } 72 | public string[] types { get; set; } 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /Demo.Core/ExternalServices/RegisterExternalServices.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | using Demo.Core.ExternalServices.Google; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Options; 8 | using Polly; 9 | using Polly.Extensions.Http; 10 | using Polly.Timeout; 11 | using Refit; 12 | 13 | namespace Demo.Core.ExternalServices 14 | { 15 | public static class RegisterExternalServices 16 | { 17 | /// 18 | /// Adiciona todas as dependências de requisições http 19 | /// 20 | /// 21 | /// 22 | /// 23 | public static IServiceCollection AddExternalServices(this IServiceCollection services, IConfiguration configuration) 24 | { 25 | services.Configure(configuration.GetSection(nameof(GoogleApiConfiguration))); 26 | var configs = services.BuildServiceProvider().GetRequiredService>().Value; 27 | 28 | var timeoutPolicy = Policy.TimeoutAsync(10); 29 | 30 | services.AddRefitClient() 31 | .ConfigureHttpClient(c => c.BaseAddress = new Uri(configs.GeoCodeURI)) 32 | .AddPolicyHandler(GetRetryPolicy()) 33 | .AddPolicyHandler(timeoutPolicy); 34 | 35 | return services; 36 | } 37 | 38 | /// 39 | /// É adicionado uma política para tentar 3 vezes com uma repetição exponencial, começando em um segundo. 40 | /// 41 | /// 42 | static IAsyncPolicy GetRetryPolicy() 43 | { 44 | return HttpPolicyExtensions 45 | .HandleTransientHttpError() 46 | .OrResult(msg => msg.StatusCode == HttpStatusCode.InternalServerError) 47 | .Or() 48 | .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(1, retryAttempt))); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Demo.Core/GraphQL/DbzMutation.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.GraphQL.Types.Character; 2 | using Demo.Core.GraphQL.Types.Character.Models; 3 | using Demo.Core.Services.GraphQL; 4 | using GraphQL.Authorization; 5 | using GraphQL.Types; 6 | using Microsoft.AspNetCore.Mvc; 7 | 8 | namespace Demo.Core.GraphQL.Types 9 | { 10 | public class DbzMutation : ObjectGraphType 11 | { 12 | public DbzMutation([FromServices]ICharacterGraphServices characterGraphServices) 13 | { 14 | this.AuthorizeWith("AdminPolicy"); 15 | 16 | Name = "CreateCharacterMutation"; 17 | 18 | Field( 19 | "createCharacter", 20 | arguments: new QueryArguments( 21 | new QueryArgument> { Name = "character" } 22 | ), 23 | resolve: context => 24 | { 25 | var character = context.GetArgument("character"); 26 | return characterGraphServices.CreateAsync(character); 27 | }); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Demo.Core/GraphQL/DbzQuery.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.GraphQL.Types.Character; 2 | using Demo.Core.Services.GraphQL; 3 | using GraphQL.Types; 4 | using Microsoft.AspNetCore.Mvc; 5 | 6 | namespace Demo.Core.GraphQL.Types 7 | { 8 | public class DbzQuery : ObjectGraphType 9 | { 10 | public DbzQuery([FromServices]ICharacterGraphServices characterGraphServices) 11 | { 12 | Field( 13 | "character", 14 | arguments: new QueryArguments(new QueryArgument { Name = "id" }), 15 | resolve: context => characterGraphServices.GetByIDAsync(context.GetArgument("id"))); 16 | 17 | Field>( 18 | "characters", 19 | resolve: context => 20 | { 21 | return characterGraphServices.GetAllAsync(); 22 | }); 23 | } 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Demo.Core/GraphQL/DbzSchema.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.Types; 3 | 4 | namespace Demo.Core.GraphQL.Types 5 | { 6 | public class DbzSchema : Schema 7 | { 8 | public DbzSchema(IDependencyResolver resolver) : base(resolver) 9 | { 10 | Query = resolver.Resolve(); 11 | Mutation = resolver.Resolve(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Demo.Core/GraphQL/GraphQLDI.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.GraphQL.Types; 2 | using Demo.Core.GraphQL.Types.Character; 3 | using Demo.Core.GraphQL.Types.Family; 4 | using GraphQL; 5 | using GraphQL.Authorization; 6 | using GraphQL.Http; 7 | using GraphQL.Server; 8 | using GraphQL.Server.Ui.Playground; 9 | using GraphQL.Types; 10 | using GraphQL.Validation; 11 | using Microsoft.AspNetCore.Builder; 12 | using Microsoft.AspNetCore.Http; 13 | using Microsoft.Extensions.DependencyInjection; 14 | using Microsoft.Extensions.DependencyInjection.Extensions; 15 | using System.Security.Claims; 16 | 17 | namespace Demo.Core.GraphQL 18 | { 19 | public static class GraphQLDI 20 | { 21 | public static IServiceCollection AddGraphQLTypes(this IServiceCollection services) 22 | { 23 | services.AddSingleton(); 24 | 25 | services.AddSingleton(); 26 | services.AddSingleton(); 27 | services.AddGraphQLCharacterModels(); 28 | services.AddGraphQLFamilyModels(); 29 | 30 | var sp = services.BuildServiceProvider(); 31 | services.AddSingleton(new DbzSchema(new FuncDependencyResolver(type => sp.GetService(type)))); 32 | 33 | services.AddGraphQLAuth(); 34 | 35 | services.AddGraphQL(_ => 36 | { 37 | _.EnableMetrics = true; 38 | _.ExposeExceptions = true; 39 | }).AddUserContextBuilder(context => new GraphQLUserContext { User = context.User }); 40 | 41 | return services; 42 | } 43 | 44 | public static void AddGraphQLAuth(this IServiceCollection services) 45 | { 46 | services.TryAddSingleton(); 47 | services.TryAddSingleton(); 48 | services.AddTransient(); 49 | 50 | services.TryAddSingleton(s => 51 | { 52 | var authSettings = new AuthorizationSettings(); 53 | 54 | authSettings.AddPolicy("AdminPolicy", _ => _.RequireClaim(ClaimTypes.Role, "admin")); 55 | 56 | return authSettings; 57 | }); 58 | } 59 | 60 | public static IApplicationBuilder AddGraphQLTypes(this IApplicationBuilder app) 61 | { 62 | app.UseGraphQL(); 63 | 64 | // use graphql-playground at default url /ui/playground 65 | app.UseGraphQLPlayground(new GraphQLPlaygroundOptions 66 | { 67 | Path = "/ui/playground" 68 | }); 69 | 70 | return app; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Demo.Core/GraphQL/GraphQLUserContext.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Authorization; 2 | using System.Security.Claims; 3 | 4 | namespace Demo.Core.GraphQL 5 | { 6 | public class GraphQLUserContext : IProvideClaimsPrincipal 7 | { 8 | public ClaimsPrincipal User { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Demo.Core/GraphQL/Types/Character/CharacterExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Demo.Core.GraphQL.Types.Character 4 | { 5 | public static class CharacterExtensions 6 | { 7 | public static IServiceCollection AddGraphQLCharacterModels(this IServiceCollection services) 8 | { 9 | services.AddSingleton(); 10 | services.AddSingleton(); 11 | services.AddSingleton(); 12 | 13 | return services; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Demo.Core/GraphQL/Types/Character/CharacterGraphInputType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | 3 | namespace Demo.Core.GraphQL.Types.Character 4 | { 5 | public class CharacterGraphInputType : InputObjectGraphType 6 | { 7 | public CharacterGraphInputType() 8 | { 9 | Name = "CharacterInput"; 10 | Field>("name"); 11 | Field>("birthDate"); 12 | Field>("kind"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Demo.Core/GraphQL/Types/Character/CharacterGraphType.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.GraphQL.Types.Character.Models; 2 | using Demo.Core.GraphQL.Types.Family; 3 | using Demo.Core.Services.GraphQL; 4 | using GraphQL.Types; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace Demo.Core.GraphQL.Types.Character 8 | { 9 | /// 10 | /// Tipo para a entidade 11 | /// 12 | public class CharacterGraphType : ObjectGraphType 13 | { 14 | /// 15 | /// Constructor 16 | /// 17 | public CharacterGraphType([FromServices]ICharacterGraphServices characterGraphServices) 18 | { 19 | Name = "Character"; 20 | Description = "Um personagem do mundo de Dragon Ball Z"; 21 | 22 | Field(x => x.ID).Name("id"); 23 | Field(x => x.Name).Description("Nome do personagem"); 24 | Field("kind", "Raça do personagem"); 25 | Field(x => x.BirthDate).Description("Ano de nascimento do personagem"); 26 | Field>("relatives", resolve: context => characterGraphServices.GetRelativesAsync(context.Source.ID)).Description = "Lista de parentes"; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Demo.Core/GraphQL/Types/Character/CharacterKindEnumType.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MySql.Entities; 2 | using Demo.Core.Shared.Enum; 3 | using GraphQL.Types; 4 | 5 | namespace Demo.Core.GraphQL.Types.Character 6 | { 7 | public class CharacterKindEnum : EnumerationGraphType 8 | { 9 | public CharacterKindEnum() 10 | { 11 | Name = "Kind"; 12 | Description = "Raça do personagem"; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Demo.Core/GraphQL/Types/Character/Models/CharacterModel.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MySql.Entities; 2 | using Demo.Core.Shared.Enum; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Demo.Core.GraphQL.Types.Character.Models 7 | { 8 | public class CharacterModel 9 | { 10 | public CharacterModel() 11 | { 12 | 13 | } 14 | 15 | public CharacterModel(CharacterEntity entity) 16 | { 17 | ID = entity.ID; 18 | Name = entity.Name; 19 | BirthDate = entity.BirthDate; 20 | Kind = entity.Kind; 21 | } 22 | 23 | public int ID { get; set; } 24 | public string Name { get; set; } 25 | public string BirthDate { get; set; } 26 | public ECharecterKind Kind { get; set; } 27 | public CharacterModel[] Relatives { get; set; } 28 | 29 | public static List ParseEntities(params CharacterEntity[] entities) 30 | { 31 | return entities.Select(x => new CharacterModel(x)).ToList(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Demo.Core/GraphQL/Types/Family/FamilyExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Demo.Core.GraphQL.Types.Family 4 | { 5 | public static class FamilyExtensions 6 | { 7 | public static IServiceCollection AddGraphQLFamilyModels(this IServiceCollection services) 8 | { 9 | services.AddSingleton(); 10 | services.AddSingleton(); 11 | 12 | return services; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Demo.Core/GraphQL/Types/Family/Models/RelativeModel.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MySql.Entities; 2 | using Demo.Core.GraphQL.Types.Character.Models; 3 | using Demo.Core.Shared.Enum; 4 | 5 | namespace Demo.Core.GraphQL.Types.Family.Models 6 | { 7 | public class RelativeModel : CharacterModel 8 | { 9 | /// 10 | /// Grau de parentesco 11 | /// 12 | public ERelativeKind RelativeKind { get; set; } 13 | 14 | public RelativeModel() 15 | { 16 | 17 | } 18 | 19 | public RelativeModel(FamilyEntity entity) 20 | { 21 | ID = entity.Relative.ID; 22 | Name = entity.Relative.Name; 23 | BirthDate = entity.Relative.BirthDate; 24 | Kind = entity.Relative.Kind; 25 | RelativeKind = entity.Kind; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Demo.Core/GraphQL/Types/Family/RelativeGraphType.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.GraphQL.Types.Character; 2 | using Demo.Core.GraphQL.Types.Family.Models; 3 | using Demo.Core.Services.GraphQL; 4 | using GraphQL.Types; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace Demo.Core.GraphQL.Types.Family 8 | { 9 | /// 10 | /// Tipo para a entidade 11 | /// 12 | public class RelativeGraphType : ObjectGraphType 13 | { 14 | /// 15 | /// Constructor 16 | /// 17 | public RelativeGraphType([FromServices]ICharacterGraphServices characterGraphServices) 18 | { 19 | Name = "Relative"; 20 | Description = "Informações de parentesco de um personagem do Dragon Ball"; 21 | 22 | Field(x => x.ID).Name("id"); 23 | Field(x => x.Name).Description("Nome do personagem"); 24 | Field("kind", "Raça do personagem"); 25 | Field(x => x.BirthDate).Description("Ano de nascimento do personagem"); 26 | Field("relativeKind", "Grau de parentesco"); 27 | Field>("relatives", resolve: context => characterGraphServices.GetRelativesAsync(context.Source.ID)).Description = "Lista de parentes"; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Demo.Core/GraphQL/Types/Family/RelativeKindEnumType.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Shared.Enum; 2 | using GraphQL.Types; 3 | 4 | namespace Demo.Core.GraphQL.Types.Family 5 | { 6 | public class RelativeKindEnumType : EnumerationGraphType 7 | { 8 | public RelativeKindEnumType() 9 | { 10 | Name = "FamilyKind"; 11 | Description = "Grau parentesco"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Demo.Core/Messages/RabbitMQ/TesteMessage.cs: -------------------------------------------------------------------------------- 1 | using Framework.MessageBroker; 2 | using Framework.MessageBroker.RabbitMQ; 3 | 4 | namespace Demo.Core.Messages.RabbitMQ 5 | { 6 | public class TesteMessage : BaseMessage 7 | { 8 | public string Campo { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /Demo.Core/Services/CharacterServices.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.DragonBall.Request; 2 | using Demo.Core.Data.MySql.Entities; 3 | using Demo.Core.Data.MySql.Repositories; 4 | using Framework.Data.CacheProviders.Redis; 5 | using Framework.Services; 6 | using Microsoft.Extensions.Caching.Distributed; 7 | using System; 8 | using System.Threading.Tasks; 9 | 10 | namespace Demo.Core.Services 11 | { 12 | public class CharacterServices : BaseServices, ICharacterServices 13 | { 14 | private readonly ICharacterRepository _characterRepository; 15 | private readonly IFamilyRepository _familyRepository; 16 | private readonly IDistributedCache _redisProvider; 17 | 18 | public CharacterServices(ICharacterRepository characterRepository, IFamilyRepository familyRepository, IDistributedCache redisProvider) 19 | { 20 | _familyRepository = familyRepository; 21 | _redisProvider = redisProvider; 22 | _characterRepository = characterRepository; 23 | } 24 | 25 | public async Task CreateAsync(DragonBallPostRequest request) 26 | { 27 | var entity = await _characterRepository.CreateAsync(new CharacterEntity(request), true); 28 | return Created(entity); 29 | } 30 | 31 | public async Task CreateRelative(int id, DragonBallPostRelativeRequest request) 32 | { 33 | var entity = new FamilyEntity(request) 34 | { 35 | CharacterID = id 36 | }; 37 | 38 | var result = await _familyRepository.CreateAsync(entity, true); 39 | 40 | return Created(result); 41 | } 42 | 43 | public async Task GetByIDAsync(int characterId) 44 | { 45 | var entity = await _redisProvider.GetAsync($"character:{characterId}", TimeSpan.FromSeconds(30), () => _characterRepository.ReadAsync(characterId)); 46 | return Ok(entity); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Demo.Core/Services/GraphQL/CharacterGraphServices.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Data.MySql.Entities; 2 | using Demo.Core.Data.MySql.Repositories; 3 | using Demo.Core.GraphQL.Types.Character.Models; 4 | using Demo.Core.GraphQL.Types.Family.Models; 5 | using Framework.Data.CacheProviders.Redis; 6 | using Microsoft.Extensions.Caching.Distributed; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Threading.Tasks; 11 | 12 | namespace Demo.Core.Services.GraphQL 13 | { 14 | public class CharacterGraphServices : ICharacterGraphServices 15 | { 16 | private readonly ICharacterRepository _characterRepository; 17 | private readonly IFamilyRepository _familyRepository; 18 | private readonly IDistributedCache _redisProvider; 19 | 20 | public CharacterGraphServices(ICharacterRepository characterRepository, IFamilyRepository familyRepository, IDistributedCache cache) 21 | { 22 | _familyRepository = familyRepository; 23 | _characterRepository = characterRepository; 24 | _redisProvider = cache; 25 | } 26 | 27 | public async Task CreateAsync(CharacterModel model) 28 | { 29 | var result = await _characterRepository.CreateAsync(new CharacterEntity(model), true); 30 | if (result == null) 31 | return null; 32 | 33 | return new CharacterModel(result); 34 | } 35 | 36 | public async Task> GetAllAsync() 37 | { 38 | var characteres = await _characterRepository.ReadAsync(); 39 | 40 | return CharacterModel.ParseEntities(characteres.ToArray()); 41 | } 42 | 43 | 44 | public async Task GetByIDAsync(int characterId) 45 | { 46 | var entity = await _redisProvider.GetAsync($"character:{characterId}", TimeSpan.FromSeconds(30), () => _characterRepository.ReadAsync(characterId)); 47 | if (entity == null) 48 | return null; 49 | 50 | return new CharacterModel(entity); 51 | } 52 | 53 | public async Task> GetRelativesAsync(int characterId) 54 | { 55 | var relatives = await _familyRepository.GetRelativesAsync(characterId); 56 | 57 | return relatives.Select(x => new RelativeModel(x)).ToList(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Demo.Core/Services/GraphQL/GraphServicesDI.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Demo.Core.Services.GraphQL 4 | { 5 | public static class GraphServicesDI 6 | { 7 | public static IServiceCollection AddGraphServices(this IServiceCollection services) 8 | { 9 | services.AddSingleton(); 10 | 11 | return services; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Demo.Core/Services/GraphQL/ICharacterGraphServices.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.GraphQL.Types.Character.Models; 2 | using Demo.Core.GraphQL.Types.Family.Models; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace Demo.Core.Services.GraphQL 7 | { 8 | public interface ICharacterGraphServices 9 | { 10 | Task CreateAsync(CharacterModel model); 11 | Task GetByIDAsync(int characterId); 12 | Task> GetAllAsync(); 13 | Task> GetRelativesAsync(int characterId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Demo.Core/Services/ICharacterServices.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.DragonBall.Request; 2 | using Framework.Services; 3 | using System.Threading.Tasks; 4 | 5 | namespace Demo.Core.Services 6 | { 7 | public interface ICharacterServices 8 | { 9 | Task CreateAsync(DragonBallPostRequest model); 10 | Task GetByIDAsync(int characterId); 11 | Task CreateRelative(int id, DragonBallPostRelativeRequest request); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Demo.Core/Services/IProfileServices.cs: -------------------------------------------------------------------------------- 1 | using Framework.Services; 2 | using System.Threading.Tasks; 3 | 4 | namespace Demo.Core.Services 5 | { 6 | public interface IProfileServices 7 | { 8 | /// 9 | /// Salvar imagem de perfil do usuário 10 | /// 11 | /// Código do usuário 12 | /// 13 | Task SaveUserPhotoAsync(string userId, byte[] image); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Demo.Core/Services/ISignInServices.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.SignIn; 2 | using Framework.Services; 3 | 4 | namespace Demo.Core.Services 5 | { 6 | public interface ISignInServices 7 | { 8 | ServicesResult GenerateToken(SignInPostRequest request); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Demo.Core/Services/ISignUpServices.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.SignUp; 2 | using Framework.Services; 3 | 4 | namespace Demo.Core.Services 5 | { 6 | public interface ISignUpServices 7 | { 8 | ServicesResult Post(SignUpPostRequest request); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Demo.Core/Services/IValuesServices.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Demo.Core.Contracts.Values; 3 | using Framework.Services; 4 | 5 | namespace Demo.Core.Services 6 | { 7 | public interface IValuesServices 8 | { 9 | Task PostRabbitMessageAsync(PostMessageRequest request); 10 | } 11 | } -------------------------------------------------------------------------------- /Demo.Core/Services/ProfileServices.cs: -------------------------------------------------------------------------------- 1 | using Framework.Services; 2 | using System.Threading.Tasks; 3 | 4 | namespace Demo.Core.Services 5 | { 6 | public class ProfileServices : BaseServices, IProfileServices 7 | { 8 | public Task SaveUserPhotoAsync(string userId, byte[] image) 9 | { 10 | //Handle Image 11 | return Task.FromResult(Ok()); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Demo.Core/Services/ServicesDI.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Services.GraphQL; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Demo.Core.Services 5 | { 6 | /// 7 | /// Reponsável por adicionar as injeções de dependências de serviços 8 | /// 9 | public static class ServicesDI 10 | { 11 | public static IServiceCollection AddServices(this IServiceCollection services) 12 | { 13 | services.AddSingleton(); 14 | services.AddSingleton(); 15 | services.AddSingleton(); 16 | services.AddSingleton(); 17 | 18 | services.AddScoped(); 19 | 20 | //GraphQL Services 21 | services.AddGraphServices(); 22 | 23 | return services; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Demo.Core/Services/SignInServices.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.SignIn; 2 | using Demo.Core.Data.MongoDB.Entities; 3 | using Demo.Core.Data.MongoDB.Repositories; 4 | using Framework.Services; 5 | using Framework.WebAPI.Hosting.JWT; 6 | using Microsoft.IdentityModel.Tokens; 7 | using System; 8 | using System.IdentityModel.Tokens.Jwt; 9 | using System.Security.Claims; 10 | 11 | namespace Demo.Core.Services 12 | { 13 | /// 14 | /// Serivço SignIn 15 | /// 16 | public class SignInServices : BaseServices, ISignInServices 17 | { 18 | private readonly SigningConfigurations _signingConfigurations; 19 | private readonly TokenConfigurations _tokenConfigurations; 20 | private readonly IUserRepository _userRepository; 21 | 22 | public SignInServices(SigningConfigurations signingConfigurations, TokenConfigurations tokenConfigurations, IUserRepository userRepository) 23 | { 24 | _signingConfigurations = signingConfigurations; 25 | _tokenConfigurations = tokenConfigurations; 26 | _userRepository = userRepository; 27 | } 28 | 29 | public ServicesResult GenerateToken(SignInPostRequest request) 30 | { 31 | UserEntity userEntity; 32 | 33 | if (request.GrantType == "refresh_token") 34 | { 35 | //Obter usuário 36 | userEntity = _userRepository.GetByRefreshToken(request.RefreshToken); 37 | if (userEntity == null) 38 | return NotFound("RefreshToken informado é inválido"); 39 | 40 | // Gerar token 41 | return Ok(GenerateToken(userEntity, true)); 42 | } 43 | 44 | //Pesquisar usuário 45 | userEntity = _userRepository.GetByEmail(request.Email); 46 | if (userEntity == null) 47 | return NotFound("Usuário não cadastrado"); 48 | 49 | // Gerar token 50 | return Ok(GenerateToken(userEntity, false)); 51 | } 52 | 53 | private object GenerateToken(UserEntity userEntity, bool isRefreshToken) 54 | { 55 | //Gerar Token 56 | Claim[] claims = new[] 57 | { 58 | new Claim("user_id", userEntity.Id.ToString()), 59 | new Claim(ClaimTypes.Email, userEntity.Email), 60 | new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N")), 61 | new Claim(ClaimTypes.Role, "admin"), 62 | new Claim(ClaimTypes.Role, "zika") 63 | }; 64 | 65 | var identity = new ClaimsIdentity(claims, "Token"); 66 | 67 | DateTime dataCriacao = DateTime.Now; 68 | DateTime dataExpiracao = dataCriacao + 69 | TimeSpan.FromSeconds(_tokenConfigurations.Seconds); 70 | 71 | var handler = new JwtSecurityTokenHandler(); 72 | var securityToken = handler.CreateToken(new SecurityTokenDescriptor 73 | { 74 | Issuer = _tokenConfigurations.Issuer, 75 | Audience = _tokenConfigurations.Audience, 76 | SigningCredentials = _signingConfigurations.SigningCredentials, 77 | Subject = identity, 78 | NotBefore = dataCriacao, 79 | Expires = dataExpiracao 80 | }); 81 | var token = handler.WriteToken(securityToken); 82 | 83 | if (!isRefreshToken) 84 | { 85 | var refreshToken = Guid.NewGuid().ToString(); 86 | 87 | //Persistir refresh token para esse usuário 88 | userEntity.RefreshToken = refreshToken; 89 | _userRepository.Update(userEntity); 90 | 91 | return new 92 | { 93 | created = dataCriacao.ToString("yyyy-MM-dd HH:mm:ss"), 94 | expiration = dataExpiracao.ToString("yyyy-MM-dd HH:mm:ss"), 95 | accessToken = token, 96 | refreshToken 97 | }; 98 | } 99 | 100 | return new 101 | { 102 | created = dataCriacao.ToString("yyyy-MM-dd HH:mm:ss"), 103 | expiration = dataExpiracao.ToString("yyyy-MM-dd HH:mm:ss"), 104 | accessToken = token 105 | }; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Demo.Core/Services/SignUpServices.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.SignUp; 2 | using Demo.Core.Data.MongoDB.Entities; 3 | using Demo.Core.Data.MongoDB.Repositories; 4 | using Framework.Services; 5 | 6 | namespace Demo.Core.Services 7 | { 8 | /// 9 | /// Serivço SignIn 10 | /// 11 | public class SignUpServices : BaseServices, ISignUpServices 12 | { 13 | private readonly IUserRepository _userRepository; 14 | 15 | public SignUpServices(IUserRepository userRepository) 16 | { 17 | _userRepository = userRepository; 18 | } 19 | 20 | public ServicesResult Post(SignUpPostRequest request) 21 | { 22 | //Validar se usuário existe na base 23 | //Persistir usuário na base 24 | var entity = new UserEntity(request); 25 | _userRepository.Create(entity); 26 | 27 | return Ok(new { entity.Id }); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Demo.Core/Services/ValuesServices.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.Values; 2 | using Demo.Core.Messages.RabbitMQ; 3 | using Framework.MessageBroker.RabbitMQ; 4 | using Framework.Services; 5 | using System.Threading.Tasks; 6 | 7 | namespace Demo.Core.Services 8 | { 9 | public class ValuesServices : BaseServices, IValuesServices 10 | { 11 | private readonly IRabbitMQPublisher _publisher; 12 | 13 | public ValuesServices(IRabbitMQPublisher publisher) 14 | { 15 | _publisher = publisher; 16 | } 17 | 18 | public async Task PostRabbitMessageAsync(PostMessageRequest request) 19 | { 20 | var message = new TesteMessage 21 | { 22 | Campo = request.Campo 23 | }; 24 | 25 | 26 | await _publisher.PublishAsync(message); 27 | 28 | return Created(message.MessageId); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Demo.Core/Shared/Enum/ECharecterKind.cs: -------------------------------------------------------------------------------- 1 | namespace Demo.Core.Shared.Enum 2 | { 3 | /// 4 | /// Enumerador de especies 5 | /// 6 | public enum ECharecterKind 7 | { 8 | /// 9 | /// Humano 10 | /// 11 | Human = 1, 12 | 13 | /// 14 | /// Sayajin 15 | /// 16 | Sayajin 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Demo.Core/Shared/Enum/EGender.cs: -------------------------------------------------------------------------------- 1 | namespace Demo.Core.Shared.Enum 2 | { 3 | /// 4 | /// Enumerador de sexo 5 | /// 6 | public enum EGender 7 | { 8 | /// 9 | /// Masculino 10 | /// 11 | Male, 12 | 13 | /// 14 | /// Feminino 15 | /// 16 | Female 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Demo.Core/Shared/Enum/ERelativeKind.cs: -------------------------------------------------------------------------------- 1 | namespace Demo.Core.Shared.Enum 2 | { 3 | /// 4 | /// Enumerador de grau de parentesco 5 | /// 6 | public enum ERelativeKind 7 | { 8 | Brother = 1, 9 | Sister, 10 | Son, 11 | Daugther, 12 | Spouse, 13 | Father, 14 | Mother 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Demo.Core/Validations/DragonBall/DragonBallPostRelativeRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.DragonBall.Request; 2 | using FluentValidation; 3 | 4 | namespace Demo.Core.Validations.DragonBall 5 | { 6 | public class DragonBallPostRelativeRequestValidator : AbstractValidator 7 | { 8 | public DragonBallPostRelativeRequestValidator() 9 | { 10 | RuleFor(x => x.RelativeId) 11 | .NotEmpty() 12 | .WithMessage("O ID do personagem é obrigatório."); 13 | 14 | RuleFor(x => x.Kind) 15 | .IsInEnum() 16 | .WithMessage("A raça do personagem deve ser válida!"); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Demo.Core/Validations/DragonBall/DragonBallPostRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.DragonBall.Request; 2 | using FluentValidation; 3 | 4 | namespace Demo.Core.Validations.DragonBall 5 | { 6 | public class DragonBallPostRequestValidator : AbstractValidator 7 | { 8 | public DragonBallPostRequestValidator() 9 | { 10 | RuleFor(x => x.Name) 11 | .NotEmpty() 12 | .WithMessage("O nome do personagem é obrigatório."); 13 | 14 | RuleFor(x => x.Kind) 15 | .IsInEnum() 16 | .WithMessage("A raça do personagem deve ser válida!"); 17 | 18 | RuleFor(x => x.BirthDate) 19 | .NotEmpty() 20 | .WithMessage("A informação da data de nascimento é obrigatória!"); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Demo.Core/Validations/RegisterValidationsExtensions.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.DragonBall.Request; 2 | using Demo.Core.Contracts.SignIn; 3 | using Demo.Core.Contracts.SignUp; 4 | using Demo.Core.Validations.DragonBall; 5 | using Demo.Core.Validations.SignIn; 6 | using Demo.Core.Validations.SignUp; 7 | using FluentValidation; 8 | using Microsoft.Extensions.DependencyInjection; 9 | 10 | namespace Demo.Core.Validations 11 | { 12 | public static class RegisterValidationsExtensions 13 | { 14 | public static IServiceCollection AddValidators(this IServiceCollection services) 15 | { 16 | // can then manually register validators 17 | services.AddTransient, SignInPostRequestValidator>(); 18 | services.AddTransient, SignUpPostRequestValidator>(); 19 | services.AddTransient, DragonBallPostRequestValidator>(); 20 | services.AddTransient, DragonBallPostRelativeRequestValidator>(); 21 | 22 | return services; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Demo.Core/Validations/SignIn/SignInPostRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.SignIn; 2 | using FluentValidation; 3 | 4 | namespace Demo.Core.Validations.SignIn 5 | { 6 | public class SignInPostRequestValidator : AbstractValidator 7 | { 8 | public SignInPostRequestValidator() 9 | { 10 | //Validar GrantType 11 | RuleFor(x => x.GrantType) 12 | .Must(ValidGrantType) 13 | .WithMessage("Valores possíveis para grantType são: password e refresh_token"); 14 | 15 | //Validar refresh_token 16 | When(x => x.GrantType == "refresh_token", () => 17 | { 18 | RuleFor(x => x.Email) 19 | .Empty() 20 | .WithMessage("Quando grantType for refresh_token, não é possível passar email nem senha!"); 21 | 22 | RuleFor(x => x.Password) 23 | .Empty() 24 | .WithMessage("Quando grantType for refresh_token, não é possível passar email nem senha!"); 25 | 26 | RuleFor(x => x.RefreshToken) 27 | .NotEmpty() 28 | .WithMessage("Quando grantType for refresh_token, é obrigatório passar o valor de refreshToken!"); 29 | }); 30 | 31 | //Validar password 32 | When(x => x.GrantType == "password", () => 33 | { 34 | RuleFor(x => x.Email) 35 | .NotEmpty() 36 | .WithMessage("Quando grantType for password, email e password são obrigatórios!"); 37 | 38 | RuleFor(x => x.Password) 39 | .NotEmpty() 40 | .WithMessage("Quando grantType for password, email e password são obrigatórios!"); 41 | 42 | RuleFor(x => x.RefreshToken) 43 | .Empty() 44 | .WithMessage("Quando grantType for password, não é possível passar o valor de refreshToken!"); 45 | }); 46 | } 47 | 48 | private bool ValidGrantType(string value) => string.Equals(value, "password") || string.Equals(value, "refresh_token"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Demo.Core/Validations/SignUp/SignUpPostRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using Demo.Core.Contracts.SignUp; 2 | using FluentValidation; 3 | using System; 4 | 5 | namespace Demo.Core.Validations.SignUp 6 | { 7 | public class SignUpPostRequestValidator : AbstractValidator 8 | { 9 | public SignUpPostRequestValidator() 10 | { 11 | RuleFor(x => x.Name) 12 | .NotEmpty() 13 | .WithMessage("Nome completo é obrigatóio"); 14 | 15 | RuleFor(x => x.BornDate) 16 | .NotEqual(DateTime.MinValue) 17 | .WithMessage("Data de nascimento é obrigatória!"); 18 | 19 | RuleFor(x => x.Gender) 20 | .IsInEnum() 21 | .WithMessage("Valores possíveis para Sexo: 0 - Masculino, 1 - Feminino"); 22 | 23 | RuleFor(x => x.Email) 24 | .NotEmpty() 25 | .WithMessage("E-mail é obrigatório!") 26 | .EmailAddress() 27 | .WithMessage("O e-mail informado é inválido!"); 28 | 29 | RuleFor(x => x.Password) 30 | .NotEmpty() 31 | .WithMessage("Senha é obrigatória!") 32 | .Equal(x => x.ConfirmPassword) 33 | .WithMessage("Senhas não conferem!"); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Demo.Worker/Demo.Worker.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Demo.Worker/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace Demo.Worker 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | var startUp = new Startup(); 12 | 13 | using (var scope = startUp.Scope) 14 | { 15 | var logger = startUp.Scope.ServiceProvider.GetRequiredService>(); 16 | logger.LogInformation("Hello World."); 17 | Console.Read(); 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Demo.Worker/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | using System.IO; 6 | 7 | 8 | namespace Demo.Worker 9 | { 10 | public class Startup 11 | { 12 | public IConfiguration Configuration { get; } 13 | 14 | private ServiceProvider ServiceProvier { get; } 15 | 16 | public IServiceScope Scope => ServiceProvier.CreateScope(); 17 | 18 | public Startup() 19 | { 20 | //Obter a env 21 | var envName = Environment.GetEnvironmentVariable("ENVIRONMENT_NAME"); 22 | 23 | //setup our configuration 24 | var builder = new ConfigurationBuilder() 25 | .SetBasePath(Directory.GetCurrentDirectory()) 26 | .AddJsonFile("appsettings.json", optional: false); 27 | 28 | if (!string.IsNullOrWhiteSpace(envName)) 29 | builder.AddJsonFile($"appsettings.{envName}.json", optional: true); 30 | 31 | Configuration = builder.Build(); 32 | 33 | //setup our DI 34 | var servicesBuilder = new ServiceCollection() 35 | .AddLogging(config => 36 | { 37 | config.AddConfiguration(Configuration.GetSection("Logging")); 38 | config.AddConsole(); 39 | //Caso seja necessário logar no EventViewer 40 | //Microsoft.Extensions.Logging.EventLog; 41 | // config.AddEventLog(new EventLogSettings() 42 | // { 43 | // SourceName = "ServiceDiscoveryCache", 44 | // LogName = "ServiceDiscovery", 45 | // Filter = (x, y) => y >= LogLevel.Information 46 | // }); 47 | config.AddDebug(); 48 | }); 49 | 50 | ConfigureServices(servicesBuilder); 51 | ServiceProvier = servicesBuilder.BuildServiceProvider(); 52 | } 53 | 54 | public void ConfigureServices(IServiceCollection services) 55 | { 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Demo.Worker/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM microsoft/dotnet:2.2-sdk as builder 2 | COPY . / 3 | WORKDIR /Demo.API 4 | RUN dotnet restore --no-cache 5 | RUN dotnet publish --output /app/ -c Release --no-restore 6 | 7 | FROM microsoft/dotnet:2.2-aspnetcore-runtime-alpine 8 | WORKDIR /app 9 | COPY --from=builder /app . 10 | 11 | 12 | ENV ASPNETCORE_ENVIRONMENT Development 13 | ENV DOTNET_RUNNING_IN_CONTAINER true 14 | ENV ASPNETCORE_URLS=http://*:80 15 | 16 | EXPOSE 80 17 | ENTRYPOINT ["dotnet", "Demo.API.dll"] -------------------------------------------------------------------------------- /Framework.Core.Tests/Framework.Core.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | false 7 | 8 | 9 | 10 | 11 | runtime; build; native; contentfiles; analyzers 12 | all 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Framework.Core.Tests/Serializer/JsonSerializerCommonTest.cs: -------------------------------------------------------------------------------- 1 | using Framework.Core.Serializer; 2 | using Shouldly; 3 | using Xunit; 4 | 5 | namespace Framework.Core.Tests.Serializer 6 | { 7 | public class JsonSerializerCommonTest 8 | { 9 | private readonly JsonSerializerCommon _jsonSerializer; 10 | 11 | public JsonSerializerCommonTest() 12 | { 13 | _jsonSerializer = new JsonSerializerCommon(); 14 | } 15 | 16 | [Fact] 17 | public void Serializer_Success() 18 | { 19 | //Arrange 20 | var model = new Model 21 | { 22 | Name = "Alef", 23 | }; 24 | 25 | //Act 26 | var json = _jsonSerializer.Serialize(model); 27 | var obj = _jsonSerializer.Deserialize(json); 28 | 29 | //Assert 30 | json.ShouldNotBeNullOrWhiteSpace(); 31 | obj.Name.ShouldBe(model.Name); 32 | obj.Age.ShouldBe(model.Age); 33 | } 34 | 35 | [Fact] 36 | public void Deserialize_Success() 37 | { 38 | //Arrange 39 | var model = new Model 40 | { 41 | Name = "Alef", 42 | }; 43 | 44 | var json = "{\"name\":\"Alef\"}"; 45 | 46 | //Act 47 | var obj = _jsonSerializer.Deserialize(json); 48 | 49 | //Assert 50 | obj.ShouldNotBeNull(); 51 | obj.Name.ShouldBe(model.Name); 52 | obj.Age.ShouldBe(model.Age); 53 | } 54 | } 55 | 56 | public class Model 57 | { 58 | public string Name { get; set; } 59 | 60 | public int? Age { get; set; } 61 | } 62 | } -------------------------------------------------------------------------------- /Framework.Core/Framework.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Framework.Core/Serializer/ISerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Framework.Core.Serializer 2 | { 3 | public interface ISerializer 4 | { 5 | string Serialize(T data); 6 | T Deserialize(string serializedData); 7 | } 8 | } -------------------------------------------------------------------------------- /Framework.Core/Serializer/JsonSerializerCommon.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Converters; 3 | using Newtonsoft.Json.Serialization; 4 | 5 | namespace Framework.Core.Serializer 6 | { 7 | public class JsonSerializerCommon : ISerializer 8 | { 9 | private readonly JsonSerializerSettings _settings; 10 | 11 | public JsonSerializerCommon() 12 | { 13 | _settings = new Newtonsoft.Json.JsonSerializerSettings 14 | { 15 | ContractResolver = new CamelCasePropertyNamesContractResolver(), 16 | NullValueHandling = NullValueHandling.Ignore, 17 | DateTimeZoneHandling = DateTimeZoneHandling.Utc 18 | }; 19 | 20 | _settings.Converters.Add(new StringEnumConverter()); 21 | } 22 | 23 | public string Serialize(T value) 24 | { 25 | return JsonConvert.SerializeObject(value, _settings); 26 | } 27 | 28 | public T Deserialize(string value) 29 | { 30 | return JsonConvert.DeserializeObject(value, _settings); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Framework.Data.Tests/Framework.Data.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Framework.Data.Tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | 3 | namespace Framework.Data.Tests 4 | { 5 | [TestClass] 6 | public class UnitTest1 7 | { 8 | [TestMethod] 9 | public void TestMethod1() 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Framework.Data/CacheProviders/Redis/CacheStoreExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Threading.Tasks; 4 | using Microsoft.Extensions.Caching.Distributed; 5 | 6 | namespace Framework.Data.CacheProviders.Redis 7 | { 8 | public static class CacheStoreExtensions 9 | { 10 | public static T Get(this IDistributedCache source, string key) where T : class 11 | { 12 | var data = source.Get(key); 13 | var stringData = Encoding.UTF8.GetString(data); 14 | 15 | return Newtonsoft.Json.JsonConvert.DeserializeObject(stringData); 16 | } 17 | 18 | public static async Task GetAsync(this IDistributedCache source, string key) where T : class 19 | { 20 | var data = await source.GetAsync(key); 21 | var stringData = Encoding.UTF8.GetString(data); 22 | 23 | return Newtonsoft.Json.JsonConvert.DeserializeObject(stringData); 24 | } 25 | 26 | public static T Get(this IDistributedCache source, string key, TimeSpan time, Func fetch) where T : class 27 | { 28 | if (source.Exists(key)) 29 | return source.Get(key); 30 | 31 | var result = fetch(); 32 | 33 | if (result != null) 34 | { 35 | var stringToPersist = Newtonsoft.Json.JsonConvert.SerializeObject(result); 36 | var data = Encoding.UTF8.GetBytes(stringToPersist); 37 | 38 | var options = new DistributedCacheEntryOptions().SetSlidingExpiration(time); 39 | source.Set(key, data, options); 40 | } 41 | 42 | return result; 43 | } 44 | 45 | public static async Task GetAsync(this IDistributedCache source, string key, TimeSpan time, Func> fetch) where T : class 46 | { 47 | 48 | if (await source.ExistsAsync(key)) 49 | return await source.GetAsync(key); 50 | 51 | var result = await fetch(); 52 | 53 | if (result != null) 54 | { 55 | var stringToPersist = Newtonsoft.Json.JsonConvert.SerializeObject(result); 56 | var data = Encoding.UTF8.GetBytes(stringToPersist); 57 | 58 | var options = new DistributedCacheEntryOptions().SetSlidingExpiration(time); 59 | source.Set(key, data, options); 60 | 61 | } 62 | 63 | 64 | return result; 65 | } 66 | 67 | public static bool Exists(this IDistributedCache source, string key) => source.Get(key) != null; 68 | public static async Task ExistsAsync(this IDistributedCache source, string key) => await source.GetAsync(key) != null; 69 | 70 | } 71 | } -------------------------------------------------------------------------------- /Framework.Data/CacheProviders/Redis/RedisExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using StackExchange.Redis; 4 | using Microsoft.Extensions.Configuration; 5 | 6 | namespace Framework.Data.CacheProviders 7 | { 8 | public static class RedisExtensions 9 | { 10 | public static IServiceCollection AddRedisCache(this IServiceCollection services, IConfiguration configuration) 11 | { 12 | var redisUri = configuration.GetConnectionString("Redis"); 13 | var options = ConfigurationOptions.Parse(redisUri); 14 | 15 | if (!options.DefaultDatabase.HasValue) 16 | throw new ArgumentNullException("DefaultDatabase", "É obrigatório informar o database padrão do Redis."); 17 | 18 | services.AddStackExchangeRedisCache(config => 19 | { 20 | config.ConfigurationOptions = options; 21 | }); 22 | 23 | services.AddHealthChecks() 24 | .AddRedis(redisUri, "redis", tags: new string[] { "db", "redis", "cache" }); 25 | 26 | return services; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Framework.Data/EntityFramework/EFEntityBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Framework.Data.EntityFramework 4 | { 5 | public class EFEntityBase 6 | { 7 | /// 8 | /// Data de criação do registro 9 | /// 10 | public DateTime CreatedDate { get; set; } 11 | 12 | /// 13 | /// Data de atualização do registro 14 | /// 15 | public DateTime? UpdatedDate { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Framework.Data/EntityFramework/EFRepositoryBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Framework.Data.EntityFramework 8 | { 9 | public class EFRepositoryBase : IEFRepository where T : EFEntityBase 10 | { 11 | protected readonly DbContext _context = null; 12 | 13 | public EFRepositoryBase(DbContext context) 14 | { 15 | _context = context; 16 | } 17 | 18 | public int SaveChanges() 19 | { 20 | return _context.SaveChanges(); 21 | } 22 | 23 | public Task SaveChangesAsync() 24 | { 25 | return _context.SaveChangesAsync(); 26 | } 27 | 28 | public T Create(T entity, bool save) 29 | { 30 | entity.CreatedDate = DateTime.Now; 31 | _context.Set().Add(entity); 32 | 33 | if (save) 34 | SaveChanges(); 35 | 36 | return entity; 37 | } 38 | 39 | public async Task CreateAsync(T entity, bool save) 40 | { 41 | entity.CreatedDate = DateTime.Now; 42 | await _context.Set().AddAsync(entity); 43 | 44 | if (save) 45 | SaveChanges(); 46 | 47 | return entity; 48 | } 49 | 50 | public void Delete(bool save, params object[] keys) 51 | { 52 | var e = Read(keys); 53 | _context.Set().Remove(e); 54 | 55 | if (save) 56 | SaveChanges(); 57 | } 58 | 59 | public T Read(params object[] keys) => _context.Set().Find(keys); 60 | 61 | public Task ReadAsync(params object[] keys) => _context.Set().FindAsync(keys); 62 | 63 | public List Read() => 64 | Query().AsNoTracking().ToList(); 65 | 66 | public Task> ReadAsync() => Query().AsNoTracking().ToListAsync(); 67 | 68 | public IQueryable Query() => 69 | _context.Set().AsNoTracking(); 70 | 71 | public void Update(T entity, bool save) 72 | { 73 | entity.UpdatedDate = DateTime.Now; 74 | _context.Entry(entity).State = EntityState.Modified; 75 | 76 | if (save) 77 | SaveChanges(); 78 | } 79 | 80 | public void Dispose() 81 | { 82 | if (_context != null) 83 | _context.Dispose(); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Framework.Data/EntityFramework/IEFRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | namespace Framework.Data.EntityFramework 6 | { 7 | public interface IEFRepository where T : EFEntityBase 8 | { 9 | /// 10 | /// Obtém todos os registros. 11 | /// 12 | /// 13 | List Read(); 14 | 15 | /// 16 | /// Obtém todos os registros. 17 | /// 18 | /// 19 | Task> ReadAsync(); 20 | 21 | /// 22 | /// Obtém um registro no banco. 23 | /// 24 | /// 25 | /// 26 | T Read(params object[] keys); 27 | 28 | /// 29 | /// Obtém um registro no banco. 30 | /// 31 | /// 32 | /// 33 | Task ReadAsync(params object[] keys); 34 | 35 | /// 36 | /// Adiciona um registro no banco. 37 | /// 38 | /// 39 | /// 40 | T Create(T entity, bool save); 41 | 42 | /// 43 | /// Adiciona um registro no banco. 44 | /// 45 | /// 46 | /// 47 | Task CreateAsync(T entity, bool save); 48 | 49 | /// 50 | /// Deleta um registro no banco. 51 | /// 52 | /// 53 | /// 54 | void Delete(bool save, params object[] keys); 55 | 56 | /// 57 | /// Atualizar um registro no banco. 58 | /// 59 | /// 60 | /// 61 | void Update(T entity, bool save); 62 | 63 | /// 64 | /// Salvar as operações de fato no banco. 65 | /// 66 | int SaveChanges(); 67 | 68 | /// 69 | /// Salvar as operações de fato no banco. 70 | /// 71 | Task SaveChangesAsync(); 72 | 73 | /// 74 | /// Permite criar uma query personalizada. 75 | /// 76 | /// 77 | IQueryable Query(); 78 | 79 | void Dispose(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Framework.Data/Framework.Data.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Framework.Data/MongoDB/MongoDBConnectionWraper.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Driver; 2 | 3 | namespace Framework.Data.MongoDB 4 | { 5 | public class MongoDBConnectionWraper 6 | { 7 | public MongoClient MongoClient { get; set; } 8 | 9 | public MongoUrl MongoURL { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Framework.Data/MongoDB/MongoDBExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using MongoDB.Driver; 4 | 5 | namespace Framework.Data.MongoDB 6 | { 7 | public static class MongoDBExtensions 8 | { 9 | public static IServiceCollection AddMongoDB(this IServiceCollection services, IConfiguration configuration) 10 | { 11 | var mongoUri = configuration.GetConnectionString("MongoDB"); 12 | 13 | // MongoClient (Singleton) 14 | var mongoUrl = new MongoUrl(mongoUri); 15 | var mongoConnection = new MongoDBConnectionWraper 16 | { 17 | MongoURL = mongoUrl, 18 | MongoClient = new MongoClient(mongoUrl) 19 | }; 20 | 21 | services.AddSingleton(mongoConnection); 22 | 23 | services.AddHealthChecks() 24 | .AddMongoDb(mongoUri, "mongodb", failureStatus: null, tags: new string[] { "db", "mongodb" }); 25 | 26 | return services; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Framework.Data/MongoDB/MongoEntityBase.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | using System; 4 | 5 | namespace Framework.Data.MongoDB 6 | { 7 | public class MongoEntityBase 8 | { 9 | /// 10 | /// Identificador único do registro no banco de dados. 11 | /// 12 | [BsonId] 13 | [BsonRepresentation(BsonType.ObjectId)] 14 | public virtual string Id { get; set; } 15 | 16 | /// 17 | /// Data de criação do registro. 18 | /// 19 | public DateTime Created { get; set; } = DateTime.Now; 20 | 21 | /// 22 | /// Data da última atualização do registro. 23 | /// 24 | public DateTime Updated { get; set; } = DateTime.Now; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Framework.Data/MySql/MySqlExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Framework.Data.MySql 5 | { 6 | public static class MySqlExtensions 7 | { 8 | public static IServiceCollection AddMySqlHealthCheck(this IServiceCollection services, IConfiguration configuration) 9 | { 10 | services.AddHealthChecks() 11 | .AddMySql(configuration.GetConnectionString("MYSQL"), "mysql", tags: new string[] { "db", "mysql" }); 12 | 13 | return services; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Framework.MessageBroker.Tests/.env: -------------------------------------------------------------------------------- 1 | ConnectionStrings__RabbitMQ=amqp://guest:guest@localhost 2 | RabbitMQSettings__QOS=5 -------------------------------------------------------------------------------- /Framework.MessageBroker.Tests/Framework.MessageBroker.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | runtime; build; native; contentfiles; analyzers 14 | all 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Always 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Framework.MessageBroker.Tests/RabbitMQ/Messages/DefaultGeneratedNameMessage.cs: -------------------------------------------------------------------------------- 1 | using Framework.MessageBroker.RabbitMQ; 2 | 3 | namespace Framework.MessageBroker.Tests.RabbitMQ.Messages 4 | { 5 | [RabbitMQProperties(GenerateQueueName = true)] 6 | public class DefaultGeneratedNameMessage : BaseMessage 7 | { 8 | public string Campo { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Framework.MessageBroker.Tests/RabbitMQ/Messages/DefaultMessage.cs: -------------------------------------------------------------------------------- 1 | using Framework.MessageBroker; 2 | using Framework.MessageBroker.RabbitMQ; 3 | 4 | namespace Framework.MessageBroker.Tests.RabbitMQ.Messages 5 | { 6 | public class DefaultMessage : BaseMessage 7 | { 8 | public string Campo { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /Framework.MessageBroker.Tests/RabbitMQ/Messages/DefaultRejectedMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Framework.MessageBroker.Tests.RabbitMQ.Messages 6 | { 7 | public class DefaultRejectedMessage : BaseMessage 8 | { 9 | public string Campo { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Framework.MessageBroker.Tests/RabbitMQ/Messages/DirectMessage.cs: -------------------------------------------------------------------------------- 1 | using Framework.MessageBroker; 2 | using Framework.MessageBroker.RabbitMQ; 3 | 4 | namespace Framework.MessageBroker.Tests.RabbitMQ.Messages 5 | { 6 | [RabbitMQProperties(Durable = true, ExchangeType = EExchangeType.Direct)] 7 | public class DirectMessage : BaseMessage 8 | { 9 | public int Idade { get; set; } 10 | 11 | public string Campo { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Framework.MessageBroker.Tests/RabbitMQ/Messages/DirectedGeneratedNameMessage.cs: -------------------------------------------------------------------------------- 1 | using Framework.MessageBroker.RabbitMQ; 2 | 3 | namespace Framework.MessageBroker.Tests.RabbitMQ.Messages 4 | { 5 | [RabbitMQProperties(GenerateQueueName = true, ExchangeType = EExchangeType.Direct)] 6 | public class DirectedGeneratedNameMessage : BaseMessage 7 | { 8 | public string Campo { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Framework.MessageBroker.Tests/RabbitMQ/Messages/NamedMessage.cs: -------------------------------------------------------------------------------- 1 | using Framework.MessageBroker; 2 | using Framework.MessageBroker.RabbitMQ; 3 | 4 | namespace Framework.MessageBroker.Tests.RabbitMQ.Messages 5 | { 6 | [RabbitMQProperties(Durable = true, QueueName = "test_named")] 7 | public class NamedMessage : BaseMessage 8 | { 9 | public string Campo { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /Framework.MessageBroker.Tests/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Framework.MessageBroker.RabbitMQ; 3 | using Framework.Test; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Framework.MessageBroker.Tests 8 | { 9 | public class Startup : TestStartup 10 | { 11 | public override void ConfigureServices(IServiceCollection services, IConfiguration configuration) 12 | { 13 | services.AddRabbitBroker("testeapp", configuration, false); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Framework.MessageBroker/BaseMessage.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alefcarlos/aspnet-core-webapi/957b01fa9bb967a8882666e3cfd5f3c238c7c73d/Framework.MessageBroker/BaseMessage.cs -------------------------------------------------------------------------------- /Framework.MessageBroker/Framework.MessageBroker.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Framework.MessageBroker/IExchangeOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Framework.MessageBroker 2 | { 3 | /// 4 | /// Inteface de possíveis configuração de gerenciamento de Exchange/Queue 5 | /// 6 | public interface IExchangeOptions 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Framework.MessageBroker/IPublisher.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alefcarlos/aspnet-core-webapi/957b01fa9bb967a8882666e3cfd5f3c238c7c73d/Framework.MessageBroker/IPublisher.cs -------------------------------------------------------------------------------- /Framework.MessageBroker/ISubscriber.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alefcarlos/aspnet-core-webapi/957b01fa9bb967a8882666e3cfd5f3c238c7c73d/Framework.MessageBroker/ISubscriber.cs -------------------------------------------------------------------------------- /Framework.MessageBroker/RabbitMQ/Explorer/IRabbitExplorer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace Framework.MessageBroker.RabbitMQ.Explorer 5 | { 6 | public interface IRabbitMQExplorer 7 | { 8 | Task> GetQueues(string vhost = "%2f"); 9 | Task GetQueue(string name, string vhost = "%2f"); 10 | 11 | Task> GetExchanges(string vhost = "%2f"); 12 | Task GetExchange(string exchangeName, string vhost = "%2f"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Framework.MessageBroker/RabbitMQ/Explorer/RabbitMQExchangeDetail.cs: -------------------------------------------------------------------------------- 1 | namespace Framework.MessageBroker.RabbitMQ.Explorer 2 | { 3 | public class RabbitMQExchangeDetail 4 | { 5 | public bool auto_delete { get; set; } 6 | public bool durable { get; set; } 7 | public bool _internal { get; set; } 8 | public string name { get; set; } 9 | public string type { get; set; } 10 | public string user_who_performed_action { get; set; } 11 | public string vhost { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Framework.MessageBroker/RabbitMQ/Explorer/RabbitMQExplorer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Net.Http.Headers; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Microsoft.Extensions.Configuration; 8 | 9 | namespace Framework.MessageBroker.RabbitMQ.Explorer 10 | { 11 | public class RabbitMQExplorer : IRabbitMQExplorer 12 | { 13 | private readonly HttpClient _client; 14 | private readonly Uri _rabbitMQURI; 15 | 16 | public RabbitMQExplorer(HttpClient client, IConfiguration configuration) 17 | { 18 | var uri = configuration.GetConnectionString("RabbitMQ"); 19 | 20 | _rabbitMQURI = new Uri(uri); 21 | 22 | _client = client; 23 | _client.DefaultRequestHeaders.Accept.Clear(); 24 | _client.DefaultRequestHeaders.Accept.Add( 25 | new MediaTypeWithQualityHeaderValue("application/json")); 26 | 27 | var scheme = _rabbitMQURI.Scheme == "amqps" ? "https://" : "http://"; 28 | var port = _rabbitMQURI.IsDefaultPort ? 15672 : _rabbitMQURI.Port; 29 | 30 | _client.BaseAddress = new Uri($"{scheme}{_rabbitMQURI.Host}:{port}"); 31 | 32 | var byteArray = Encoding.ASCII.GetBytes(_rabbitMQURI.UserInfo); 33 | client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); 34 | } 35 | 36 | public async Task GetQueue(string name, string vhost = "%f2") 37 | { 38 | var response = await _client.GetAsync($"/api/queues/{vhost}/{name}"); 39 | 40 | if (!response.IsSuccessStatusCode) 41 | return null; 42 | 43 | var obj = await response.Content.ReadAsAsync(); 44 | 45 | return obj; 46 | } 47 | 48 | public async Task> GetQueues(string vhost = "%f2") 49 | { 50 | var response = await _client.GetAsync("/api/queues/"); 51 | 52 | if (!response.IsSuccessStatusCode) 53 | return null; 54 | 55 | var obj = await response.Content.ReadAsAsync>(); 56 | 57 | return obj; 58 | } 59 | 60 | public async Task GetExchange(string name, string vhost = "%f2") 61 | { 62 | var response = await _client.GetAsync($"/api/exchanges/{vhost}/{name}"); 63 | 64 | if (!response.IsSuccessStatusCode) 65 | return null; 66 | 67 | var obj = await response.Content.ReadAsAsync(); 68 | 69 | return obj; 70 | } 71 | 72 | public async Task> GetExchanges(string vhost = "%f2") 73 | { 74 | var response = await _client.GetAsync("/api/exchanges/"); 75 | 76 | if (!response.IsSuccessStatusCode) 77 | return null; 78 | 79 | var obj = await response.Content.ReadAsAsync>(); 80 | 81 | return obj; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Framework.MessageBroker/RabbitMQ/Explorer/RabbitMQQueueDetail.cs: -------------------------------------------------------------------------------- 1 | namespace Framework.MessageBroker.RabbitMQ.Explorer 2 | { 3 | public class RabbitMQQueueDetail 4 | { 5 | public bool auto_delete { get; set; } 6 | public bool durable { get; set; } 7 | public bool exclusive { get; set; } 8 | public string idle_since { get; set; } 9 | public string name { get; set; } 10 | public string node { get; set; } 11 | public string state { get; set; } 12 | public string vhost { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Framework.MessageBroker/RabbitMQ/IRabbitMQPublisher.cs: -------------------------------------------------------------------------------- 1 | namespace Framework.MessageBroker.RabbitMQ 2 | { 3 | /// 4 | /// Interface de Publisher para o Rabbit 5 | /// 6 | public interface IRabbitMQPublisher : IPublisher 7 | { 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /Framework.MessageBroker/RabbitMQ/IRabbitMQSubscriber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Framework.MessageBroker.RabbitMQ 4 | { 5 | /// 6 | /// Interface de subscriber para o RabbitMQ 7 | /// 8 | public interface IRabbitMQSubscriber : ISubscriber, IDisposable 9 | { 10 | 11 | } 12 | } -------------------------------------------------------------------------------- /Framework.MessageBroker/RabbitMQ/RabbitMQConnectionWrapper.cs: -------------------------------------------------------------------------------- 1 | using RabbitMQ.Client; 2 | using System; 3 | 4 | namespace Framework.MessageBroker.RabbitMQ 5 | { 6 | public class RabbitMQConnectionWrapper : IDisposable 7 | { 8 | public IConnection Connection { get; private set; } 9 | 10 | private readonly string _appName; 11 | 12 | private bool disposed; 13 | private ConnectionFactory factory = new ConnectionFactory(); 14 | 15 | public RabbitMQConnectionWrapper(string appName, string uri) 16 | { 17 | disposed = false; 18 | _appName = appName; 19 | 20 | factory.Uri = new Uri(uri); 21 | factory.AutomaticRecoveryEnabled = true; 22 | factory.RequestedHeartbeat = 60; 23 | 24 | Connection = factory.CreateConnection(_appName); 25 | } 26 | 27 | private void Dispose(bool disposing) 28 | { 29 | if (!disposed) 30 | { 31 | if (disposing) 32 | { 33 | Connection?.Close(); 34 | Connection?.Dispose(); 35 | } 36 | 37 | disposed = true; 38 | } 39 | } 40 | 41 | public void Dispose() 42 | { 43 | Dispose(true); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Framework.MessageBroker/RabbitMQ/RabbitMQExchangeBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using RabbitMQ.Client; 2 | 3 | namespace Framework.MessageBroker.RabbitMQ 4 | { 5 | public static class RabbitMQExchangeBuilderExtensions 6 | { 7 | public static void CreateModels(this IModel channel, RabbitMQExchangeOptions options, bool bindQueueToExchange = false) 8 | { 9 | if (options.ExchangeType == "default") 10 | options.QueueName = channel.CreateQueue(options); // Garantir que o nome da fila seja propagado 11 | else 12 | { 13 | channel.CreateExchange(options); 14 | 15 | if (bindQueueToExchange) 16 | { 17 | options.QueueName = channel.CreateQueue(options); 18 | channel.BindQueue(options); 19 | } 20 | } 21 | } 22 | 23 | public static string CreateQueue(this IModel channel, RabbitMQExchangeOptions options) 24 | { 25 | var queue = channel.QueueDeclare(queue: options.QueueName, 26 | durable: options.Durable, 27 | exclusive: false, 28 | autoDelete: false, 29 | arguments: null); 30 | 31 | return queue.QueueName; 32 | } 33 | 34 | public static void CreateExchange(this IModel channel, RabbitMQExchangeOptions options) 35 | { 36 | channel.ExchangeDeclare(exchange: options.ExchangeName, 37 | type: options.ExchangeType, 38 | durable: options.Durable, 39 | autoDelete: false, 40 | arguments: null); 41 | } 42 | 43 | public static void BindQueue(this IModel channel, RabbitMQExchangeOptions options) 44 | { 45 | channel.QueueBind(queue: options.QueueName, 46 | exchange: options.ExchangeName, 47 | routingKey: options.RoutingKey); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Framework.MessageBroker/RabbitMQ/RabbitMQExchangeOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Framework.MessageBroker.RabbitMQ 5 | { 6 | public class RabbitMQExchangeOptions : IExchangeOptions 7 | { 8 | public string ExchangeName { get; set; } 9 | 10 | public string QueueName { get; set; } 11 | 12 | public string RoutingKey { get; set; } 13 | 14 | public string ExchangeType { get; set; } 15 | 16 | public bool Durable { get; set; } 17 | 18 | public static RabbitMQExchangeOptions Build() where T : BaseMessage 19 | { 20 | var type = typeof(T); 21 | var defaultQueueName = type.FullName; 22 | var defaultExchangeName = $"{type.FullName}.Exchange"; 23 | 24 | //Validar se foi informado o atributo com configurações cutomizadas 25 | var attr = type.GetCustomAttributes(typeof(RabbitMQPropertiesAttribute), false); 26 | 27 | if (!attr.Any()) 28 | { 29 | return new RabbitMQExchangeOptions 30 | { 31 | ExchangeName = string.Empty, 32 | QueueName = defaultQueueName, 33 | ExchangeType = "default", 34 | RoutingKey = string.Empty 35 | }; 36 | } 37 | 38 | var info = attr[0] as RabbitMQPropertiesAttribute; 39 | 40 | //Se tipo da exchange for default, criaremos somente a fila, pois a exchange default 41 | //já é pré-declarada pelo Rabbit 42 | var exchangeName = string.Empty; 43 | 44 | //Caso seja diferente de default e tenha sido informado um nome 45 | if (string.IsNullOrWhiteSpace(info.ExchangeName) && info.ExchangeType != EExchangeType.Default) 46 | exchangeName = defaultExchangeName; 47 | else if (!string.IsNullOrWhiteSpace(info.ExchangeName)) 48 | exchangeName = info.ExchangeName; 49 | 50 | var queueName = defaultQueueName; 51 | 52 | //Validar se devemos criar automáticamente o nome da fila 53 | if (info.GenerateQueueName) 54 | queueName = $"{typeof(T).Name}-{Guid.NewGuid().ToString("N")}"; 55 | else 56 | queueName = string.IsNullOrWhiteSpace(info.QueueName) ? defaultQueueName : info.QueueName; //Se foi informado um nome para a fila, usar. 57 | 58 | //Se for exchangetype != default e GenerateQueueName, então deixa o Rabit gerar o nome 59 | if (info.GenerateQueueName && info.ExchangeType != EExchangeType.Default) 60 | queueName = string.Empty; 61 | 62 | if (info.ExchangeType != EExchangeType.Default && string.IsNullOrWhiteSpace(exchangeName)) 63 | throw new ArgumentNullException("ExchangeName", "O nome da Exchange deve ser informado."); 64 | 65 | return new RabbitMQExchangeOptions 66 | { 67 | ExchangeName = exchangeName, 68 | QueueName = queueName, 69 | ExchangeType = info.ExchangeType.GetName(), 70 | RoutingKey = info.RoutingKey, 71 | Durable = info.Durable 72 | }; 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /Framework.MessageBroker/RabbitMQ/RabbitMQExtensions.cs: -------------------------------------------------------------------------------- 1 | using Framework.MessageBroker.RabbitMQ.Explorer; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace Framework.MessageBroker.RabbitMQ 6 | { 7 | public static class RabbitMQExtensions 8 | { 9 | public static IServiceCollection AddRabbitBroker(this IServiceCollection services, string appName, IConfiguration configuration, bool addHealthCheck = true) 10 | { 11 | var uri = configuration.GetConnectionString("RabbitMQ"); 12 | 13 | //Adicionar publisher como singleton, pois devemos sempre compartilhar a conexão TCP 14 | services.AddSingleton((provider) => new RabbitMQConnectionWrapper(appName, uri)); 15 | 16 | services.AddSingleton(); 17 | 18 | //Adicionar como transiente para garantir que NUNCA compartilharemos as intâncias dos channels por thread 19 | services.AddTransient(); 20 | 21 | services.AddHttpClient(); 22 | 23 | if (addHealthCheck) 24 | { 25 | services.AddHealthChecks() 26 | .AddRabbitMQ(uri, name: "rabbitmq", tags: new string[] { "messagebroker", "rabbitmq" }); 27 | } 28 | 29 | return services; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Framework.MessageBroker/RabbitMQ/RabbitMQPropertiesAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Framework.MessageBroker.RabbitMQ 4 | { 5 | [System.AttributeUsage(System.AttributeTargets.Class, Inherited = false, AllowMultiple = false)] 6 | public sealed class RabbitMQPropertiesAttribute : System.Attribute 7 | { 8 | public RabbitMQPropertiesAttribute() 9 | { 10 | ExchangeName = string.Empty; 11 | QueueName = string.Empty; 12 | RoutingKey = string.Empty; 13 | } 14 | 15 | public string ExchangeName { get; set; } 16 | 17 | public bool GenerateQueueName { get; set; } 18 | 19 | public string QueueName { get; set; } 20 | 21 | public string RoutingKey { get; set; } 22 | 23 | public EExchangeType ExchangeType { get; set; } 24 | 25 | public bool Durable { get; set; } 26 | } 27 | 28 | public enum EExchangeType 29 | { 30 | Default, 31 | Direct, 32 | Fanout 33 | } 34 | 35 | public static class ExchangeTypeExtensions 36 | { 37 | public static string GetName(this EExchangeType enm) 38 | { 39 | 40 | switch (enm) 41 | { 42 | case EExchangeType.Default: 43 | return "default"; 44 | 45 | case EExchangeType.Direct: 46 | return "direct"; 47 | 48 | case EExchangeType.Fanout: 49 | return "fanout"; 50 | 51 | default: 52 | throw new Exception("O tipo de Exchange não é suportado."); 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Framework.MessageBroker/RabbitMQ/RabbitMQPublisher.cs: -------------------------------------------------------------------------------- 1 | using Framework.Core.Serializer; 2 | using RabbitMQ.Client; 3 | using System; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Framework.MessageBroker.RabbitMQ 8 | { 9 | public class RabbitMQPublisher : IRabbitMQPublisher 10 | { 11 | private readonly IConnection _connection; 12 | private readonly JsonSerializerCommon _serializer; 13 | 14 | public RabbitMQPublisher(RabbitMQConnectionWrapper connection, JsonSerializerCommon serializer) 15 | { 16 | _connection = connection.Connection; 17 | _serializer = serializer; 18 | 19 | } 20 | 21 | public void Publish(T model) where T : BaseMessage 22 | { 23 | ProxyPublish(model); 24 | } 25 | 26 | public async Task PublishAsync(T model) where T : BaseMessage 27 | { 28 | await Task.Run(() => { Publish(model); }); 29 | } 30 | 31 | public void Publish(T model, string queueName) where T : BaseMessage 32 | { 33 | ProxyPublish(model, queueName); 34 | } 35 | 36 | public async Task PublishAsync(T model, string queueName) where T : BaseMessage 37 | { 38 | await Task.Run(() => { ProxyPublish(model, queueName); }); 39 | } 40 | 41 | 42 | private void ProxyPublish(T model, string queueName = "") where T : BaseMessage 43 | { 44 | var json = _serializer.Serialize(model); 45 | var encoded = Encoding.UTF8.GetBytes(json); 46 | 47 | using (var channel = _connection.CreateModel()) 48 | { 49 | var options = RabbitMQExchangeOptions.Build(); 50 | 51 | if (!string.IsNullOrWhiteSpace(queueName)) 52 | options.QueueName = queueName; 53 | 54 | model.MessageId = Guid.NewGuid(); 55 | 56 | BasicPublish(channel, options, encoded); 57 | } 58 | } 59 | 60 | private void BasicPublish(IModel channel, RabbitMQExchangeOptions options, byte[] body) 61 | { 62 | var properties = channel.CreateBasicProperties(); 63 | properties.Persistent = true; 64 | 65 | channel.CreateModels(options); 66 | 67 | var routingKey = options.ExchangeType == "default" ? options.QueueName : options.RoutingKey; 68 | 69 | channel.BasicPublish( 70 | exchange: options.ExchangeName, 71 | routingKey: routingKey, 72 | basicProperties: properties, 73 | body: body); 74 | } 75 | 76 | } 77 | } -------------------------------------------------------------------------------- /Framework.MessageBroker/RabbitMQ/RabbitMQSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Framework.MessageBroker.RabbitMQ 2 | { 3 | public class RabbitMQSettings 4 | { 5 | public ushort QOS { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /Framework.MessageBroker/RabbitMQ/RabbitMQSubscriber.cs: -------------------------------------------------------------------------------- 1 | using Framework.Core.Serializer; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.Extensions.Options; 4 | using RabbitMQ.Client; 5 | using RabbitMQ.Client.Events; 6 | using System; 7 | using System.Text; 8 | 9 | namespace Framework.MessageBroker.RabbitMQ 10 | { 11 | public class RabbitMQSubscriber : IRabbitMQSubscriber 12 | { 13 | private readonly IConnection _connection; 14 | private readonly JsonSerializerCommon _serializer; 15 | private readonly ILogger _logger; 16 | 17 | private readonly RabbitMQSettings _settings; 18 | private IModel _channel; 19 | 20 | public RabbitMQSubscriber(RabbitMQConnectionWrapper connection, 21 | JsonSerializerCommon serializer, 22 | ILogger logger, 23 | IOptions settings) 24 | { 25 | _connection = connection.Connection; 26 | _serializer = serializer; 27 | _logger = logger; 28 | _settings = settings.Value; 29 | } 30 | 31 | private T DefaultMsgBinder(byte[] data) 32 | { 33 | var message = default(T); 34 | var json = ""; 35 | 36 | if (data != null) 37 | { 38 | json = Encoding.UTF8.GetString(data); 39 | message = _serializer.Deserialize(json); 40 | } 41 | 42 | return message; 43 | } 44 | 45 | public IExchangeOptions StartConsume(Func factory, Func msgBinder = null) where T : BaseMessage 46 | { 47 | _channel = _connection.CreateModel(); 48 | 49 | var options = RabbitMQExchangeOptions.Build(); 50 | 51 | //Devemos realizar as associações da queue/exchange 52 | _channel.CreateModels(options, true); 53 | 54 | //Verificar se existe env configurando o limite de mensagens 55 | var limit = _settings.QOS; 56 | 57 | if (limit > 0) 58 | _channel.BasicQos(0, limit, false); //Limtiar por consumer 59 | 60 | _logger.LogInformation("Waiting for messages."); 61 | var consumer = new EventingBasicConsumer(_channel); 62 | consumer.Received += (m, ea) => 63 | { 64 | T message; 65 | 66 | if (msgBinder != null) 67 | message = msgBinder(ea.Body); 68 | else 69 | message = DefaultMsgBinder(ea.Body); 70 | 71 | _logger.LogInformation($"New message, id {message.MessageId}"); 72 | 73 | var result = factory(message); 74 | 75 | 76 | if (result) 77 | _channel.BasicAck(ea.DeliveryTag, false); //Devemos indicar que a mensagem foi processado com sucesso. 78 | else 79 | //Devemos enviar a mensagem para a fila novamente, assim pode ser processado por outra instância desse consumer 80 | _channel.BasicReject(ea.DeliveryTag, true); 81 | }; 82 | 83 | _channel.BasicConsume(queue: options.QueueName, 84 | autoAck: false, 85 | consumer: consumer); 86 | 87 | return options; 88 | } 89 | 90 | public void Dispose() 91 | { 92 | if (_channel != null && _channel.IsOpen) 93 | { 94 | _channel.Close(); 95 | _channel.Dispose(); 96 | } 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /Framework.Test/Framework.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Always 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Framework.Test/TestHost.cs: -------------------------------------------------------------------------------- 1 | using Framework.Core.Serializer; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.IO; 7 | 8 | namespace Framework.Test 9 | { 10 | public abstract class TestHost where T : TestStartup 11 | { 12 | public TestHost() 13 | { 14 | //Configuration 15 | var configurationBuilder = new ConfigurationBuilder() 16 | .SetBasePath(Directory.GetCurrentDirectory()) 17 | .AddJsonFile("appSettings.json", optional: false) 18 | .Build(); 19 | 20 | Configuration = configurationBuilder; 21 | 22 | var services = new ServiceCollection(); 23 | 24 | services.AddSingleton(Configuration); 25 | 26 | services.AddSingleton(); 27 | services.AddLogging(opt => opt.AddConsole()); 28 | 29 | var startup = Activator.CreateInstance(); 30 | 31 | startup.ConfigureServices(services, Configuration); 32 | 33 | ServiceProvider = services.BuildServiceProvider(); 34 | } 35 | 36 | private IServiceProvider ServiceProvider { get; } 37 | 38 | private IServiceScope Scope => ServiceProvider.CreateScope(); 39 | 40 | public IConfiguration Configuration { get; } 41 | public E GetService() => Scope.ServiceProvider.GetRequiredService(); 42 | } 43 | } -------------------------------------------------------------------------------- /Framework.Test/TestStartup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Framework.Test 5 | { 6 | public abstract class TestStartup 7 | { 8 | public abstract void ConfigureServices(IServiceCollection services, IConfiguration configuration); 9 | } 10 | } -------------------------------------------------------------------------------- /Framework.Test/appSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "RabbitMQ": "amqp://guest:guest@localhost" 4 | }, 5 | "RabbitMQSettings": { 6 | "QOS": 5 7 | } 8 | } -------------------------------------------------------------------------------- /Framework/Framework.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.authentication.jwtbearer\2.1.1\lib\netstandard2.0\Microsoft.AspNetCore.Authentication.JwtBearer.dll 28 | 29 | 30 | ..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.identitymodel.tokens\5.2.0\lib\netstandard1.4\Microsoft.IdentityModel.Tokens.dll 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Framework/Services/BaseServices.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | namespace Framework.Services 6 | { 7 | /// 8 | /// Classe base para os serviços 9 | /// 10 | public class BaseServices 11 | { 12 | public ServicesResult Ok() => new ServicesResult(true, System.Net.HttpStatusCode.OK); 13 | 14 | public ServicesResult Ok(object data) => new ServicesResult(true, System.Net.HttpStatusCode.OK, data); 15 | 16 | public ServicesResult BadRequest(string message) => new ServicesResult(false, System.Net.HttpStatusCode.BadRequest, message); 17 | 18 | public ServicesResult NotFound(string message) => new ServicesResult(false, System.Net.HttpStatusCode.NotFound, message); 19 | 20 | public ServicesResult Conflict(string message) => new ServicesResult(false, System.Net.HttpStatusCode.Conflict, message); 21 | 22 | public ServicesResult Created() => new ServicesResult(true, System.Net.HttpStatusCode.Created); 23 | 24 | public ServicesResult Created(object data) => new ServicesResult(true, System.Net.HttpStatusCode.Created, data); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Framework/Services/ServicesResult.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace Framework.Services 4 | { 5 | public sealed class ServicesResult 6 | { 7 | /// 8 | /// Indica se a operação foi bem sucedida 9 | /// 10 | public bool Success { get; private set; } 11 | 12 | /// 13 | /// Caso a operação não tenha sido bem sucedidade, aqui conterá a mensagem 14 | /// 15 | public string Error { get; private set; } 16 | 17 | /// 18 | /// HttpStatusCode que deve ser emitido 19 | /// 20 | public HttpStatusCode StatusCode { get; private set; } 21 | 22 | public object Data { get; set; } 23 | 24 | public ServicesResult(bool success, HttpStatusCode statusCode, string errorMessage = "") 25 | { 26 | Success = success; 27 | StatusCode = statusCode; 28 | Error = errorMessage; 29 | } 30 | 31 | public ServicesResult(bool success, HttpStatusCode statusCode, object data) 32 | { 33 | Success = success; 34 | StatusCode = statusCode; 35 | Data = data; 36 | Error = string.Empty; 37 | } 38 | 39 | public void SetData(object data) => Data = data; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Framework/WebAPI/Controllers/BaseController.cs: -------------------------------------------------------------------------------- 1 | using Framework.Services; 2 | using Framework.WebAPI.Responses; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System; 5 | using System.Linq; 6 | using System.Security.Claims; 7 | 8 | namespace Framework.WebAPI 9 | { 10 | public class BaseController : ControllerBase 11 | { 12 | /// 13 | /// Obtém o ID do usuário logado 14 | /// 15 | protected string GetUserId() => HttpContext.User.Claims.First(c => c.Type == "user_id").Value; 16 | 17 | /// 18 | /// Obtém o e-mail do usuário logado 19 | /// 20 | /// 21 | protected string GetUserEmail() => HttpContext.User.Claims.First(c => c.Type == ClaimTypes.Email).Value; 22 | 23 | // protected IActionResult ParseResult(ServicesResult result) 24 | // { 25 | // if (result == null) 26 | // throw new ArgumentException("Informe um result", "result"); 27 | 28 | // if (result.Success && result.Data == null) 29 | // return NoContent(); 30 | 31 | // if (result.Success && result.Data != null) 32 | // return Ok(result.Data); 33 | 34 | // var reponse = new NotOkDefaultReponse 35 | // { 36 | // Message = result.Error 37 | // }; 38 | 39 | // return new ObjectResult(reponse) 40 | // { 41 | // StatusCode = (int)result.StatusCode 42 | // }; 43 | // } 44 | 45 | protected IActionResult ParseResult(ServicesResult result, string actionName = "") 46 | { 47 | if (result == null) 48 | throw new ArgumentException("Informe um result", "result"); 49 | 50 | if (!result.Success) 51 | { 52 | var reponse = new NotOkDefaultReponse 53 | { 54 | Message = result.Error 55 | }; 56 | 57 | return new ObjectResult(reponse) 58 | { 59 | StatusCode = (int)result.StatusCode 60 | }; 61 | } 62 | 63 | if (result.Data == null) 64 | return NoContent(); 65 | 66 | if (string.IsNullOrWhiteSpace(actionName)) 67 | { 68 | return new OkObjectResult(result.Data) 69 | { 70 | StatusCode = (int)result.StatusCode 71 | }; 72 | } 73 | 74 | //Obter o valor da prop ID 75 | var idProp = result.Data.GetType().GetProperty("ID"); 76 | if (idProp == null) 77 | return NoContent(); 78 | 79 | var idValue = idProp.GetValue(result.Data); 80 | 81 | return CreatedAtAction(actionName, new { id = idValue, version = Request.HttpContext.GetRequestedApiVersion().ToString() }, result.Data); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Framework/WebAPI/Documetation/CustomConfigurationOperationFilter.cs: -------------------------------------------------------------------------------- 1 | using Swashbuckle.AspNetCore.Swagger; 2 | using Swashbuckle.AspNetCore.SwaggerGen; 3 | 4 | namespace Framework.WebAPI.Documetation 5 | { 6 | /// 7 | /// Middleware para customização do comportamento do swagger. 8 | /// 9 | public class CustomConfigurationOperationFilter : IOperationFilter 10 | { 11 | /// 12 | /// Aplica a customização do comportamento do swagger. 13 | /// 14 | /// Operação do swagger. 15 | /// Não utilizado. 16 | public void Apply(Operation operation, OperationFilterContext _) 17 | { 18 | operation.Produces = new string[] { "application/json" }; 19 | operation.Consumes = new string[] { "application/json" }; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Framework/WebAPI/Documetation/LowercaseDocumentFilter.cs: -------------------------------------------------------------------------------- 1 | using Swashbuckle.AspNetCore.Swagger; 2 | using Swashbuckle.AspNetCore.SwaggerGen; 3 | using System.Collections.Generic; 4 | 5 | /// 6 | /// Exemplo de https://gist.github.com/rafalkasa/01d5e3b265e5aa075678e0adfd54e23f 7 | /// 8 | namespace Framework.WebAPI.Documetation 9 | { 10 | public class LowercaseDocumentFilter : IDocumentFilter 11 | { 12 | public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context) 13 | { 14 | //////// PATHS 15 | 16 | var paths = swaggerDoc.Paths; 17 | 18 | // generate the new keys 19 | var newPaths = new Dictionary(); 20 | var removeKeys = new List(); 21 | foreach (var path in paths) 22 | { 23 | var newKey = path.Key.ToLower(); 24 | if (newKey != path.Key) 25 | { 26 | removeKeys.Add(path.Key); 27 | newPaths.Add(newKey, path.Value); 28 | } 29 | } 30 | 31 | // add the new keys 32 | foreach (var path in newPaths) 33 | { 34 | swaggerDoc.Paths.Add(path.Key, path.Value); 35 | } 36 | 37 | // remove the old keys 38 | foreach (var key in removeKeys) 39 | { 40 | swaggerDoc.Paths.Remove(key); 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Framework/WebAPI/Documetation/SwaggerDefaultValues.cs: -------------------------------------------------------------------------------- 1 | using Swashbuckle.AspNetCore.Swagger; 2 | using Swashbuckle.AspNetCore.SwaggerGen; 3 | using System.Linq; 4 | 5 | namespace Framework.WebAPI.Documetation 6 | { 7 | /// 8 | /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter. 9 | /// 10 | /// This is only required due to bugs in the . 11 | /// Once they are fixed and published, this class can be removed. 12 | public class SwaggerDefaultValues : IOperationFilter 13 | { 14 | /// 15 | /// Applies the filter to the specified operation using the given context. 16 | /// 17 | /// The operation to apply the filter to. 18 | /// The current operation filter context. 19 | public void Apply(Operation operation, OperationFilterContext context) 20 | { 21 | if (operation.Parameters == null) 22 | { 23 | return; 24 | } 25 | 26 | // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412 27 | // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413 28 | foreach (var parameter in operation.Parameters.OfType()) 29 | { 30 | var description = context.ApiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); 31 | var routeInfo = description.RouteInfo; 32 | 33 | if (parameter.Description == null) 34 | { 35 | parameter.Description = description.ModelMetadata?.Description; 36 | } 37 | 38 | if (routeInfo == null) 39 | { 40 | continue; 41 | } 42 | 43 | if (parameter.Default == null) 44 | { 45 | parameter.Default = routeInfo.DefaultValue; 46 | } 47 | 48 | parameter.Required |= !routeInfo.IsOptional; 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /Framework/WebAPI/Documetation/SwaggerExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Mvc.ApiExplorer; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.PlatformAbstractions; 5 | using Swashbuckle.AspNetCore.Filters; 6 | using Swashbuckle.AspNetCore.Swagger; 7 | using Swashbuckle.AspNetCore.SwaggerGen; 8 | using System.IO; 9 | 10 | namespace Framework.WebAPI.Documetation 11 | { 12 | public static class SwaggerExtensions 13 | { 14 | public static IServiceCollection AddDocumentation(this IServiceCollection services) 15 | { 16 | services.AddSwaggerGen( 17 | options => 18 | { 19 | options.DescribeAllEnumsAsStrings(); 20 | options.DescribeStringEnumsInCamelCase(); 21 | options.OperationFilter(); 22 | options.OperationFilter(); // [SwaggerResponseHeader] 23 | options.DescribeAllParametersInCamelCase(); 24 | 25 | // resolve the IApiVersionDescriptionProvider service 26 | // note: that we have to build a temporary service provider here because one has not been created yet 27 | var provider = services.BuildServiceProvider().GetRequiredService(); 28 | 29 | // add a swagger document for each discovered API version 30 | // note: you might choose to skip or document deprecated API versions differently 31 | foreach (var description in provider.ApiVersionDescriptions) 32 | { 33 | options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); 34 | } 35 | 36 | // add a custom operation filter which sets default values 37 | options.OperationFilter(); 38 | 39 | 40 | options.DocumentFilter(); 41 | 42 | // integrate xml comments 43 | IncludeXMLS(options); 44 | }); 45 | 46 | return services; 47 | } 48 | 49 | public static IApplicationBuilder UseDocumentation(this IApplicationBuilder app, IApiVersionDescriptionProvider provider) 50 | { 51 | // Enable middleware to serve generated Swagger as a JSON endpoint. 52 | app.UseSwagger(); 53 | 54 | // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), 55 | // specifying the Swagger JSON endpoint. 56 | app.UseSwaggerUI(c => 57 | { 58 | // build a swagger endpoint for each discovered API version 59 | foreach (var description in provider.ApiVersionDescriptions) 60 | { 61 | c.SwaggerEndpoint($"./swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant()); 62 | c.RoutePrefix = string.Empty; 63 | } 64 | }); 65 | 66 | return app; 67 | } 68 | 69 | static Info CreateInfoForApiVersion(ApiVersionDescription description) 70 | { 71 | var info = new Info() 72 | { 73 | Title = $"API {description.ApiVersion}", 74 | Version = description.ApiVersion.ToString(), 75 | //Description = "A sample application with Swagger, Swashbuckle, and API versioning.", 76 | //Contact = new Contact() { Name = "Bill Mei", Email = "bill.mei@somewhere.com" }, 77 | //TermsOfService = "Shareware", 78 | //License = new License() { Name = "MIT", Url = "https://opensource.org/licenses/MIT" } 79 | }; 80 | 81 | if (description.IsDeprecated) 82 | { 83 | info.Description += " This API version has been deprecated."; 84 | } 85 | 86 | return info; 87 | } 88 | 89 | private static void IncludeXMLS(SwaggerGenOptions options) 90 | { 91 | var app = PlatformServices.Default.Application; 92 | var path = app.ApplicationBasePath; 93 | 94 | var files = Directory.GetFiles(path, "*.xml"); 95 | foreach (var item in files) 96 | options.IncludeXmlComments(item); 97 | 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Framework/WebAPI/Extensions/HttpRequestExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System.IO; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | //Example: https://weblog.west-wind.com/posts/2017/Sep/14/Accepting-Raw-Request-Body-Content-in-ASPNET-Core-API-Controllers 7 | namespace Framework.WebAPI.Extensions 8 | { 9 | public static class HttpRequestExtensions 10 | { 11 | 12 | /// 13 | /// Retrieve the raw body as a string from the Request.Body stream 14 | /// 15 | /// Request instance to apply to 16 | /// Optional - Encoding, defaults to UTF8 17 | /// 18 | public static async Task GetRawBodyStringAsync(this HttpRequest request, Encoding encoding = null) 19 | { 20 | if (encoding == null) 21 | encoding = Encoding.UTF8; 22 | 23 | using (StreamReader reader = new StreamReader(request.Body, encoding)) 24 | return await reader.ReadToEndAsync(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Framework/WebAPI/HealthCheck/HealthCheckExtensions.cs: -------------------------------------------------------------------------------- 1 | using HealthChecks.UI.Client; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Diagnostics.HealthChecks; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace Framework.WebAPI.HealthCheck 7 | { 8 | public static class HealthCheckExtensions 9 | { 10 | public static IServiceCollection AddHealthCheck(this IServiceCollection services) 11 | { 12 | services 13 | .AddHealthChecks(); 14 | 15 | return services; 16 | } 17 | 18 | public static IApplicationBuilder UseHealthCheck(this IApplicationBuilder app) 19 | { 20 | app.UseHealthChecks("/healthz", new HealthCheckOptions() 21 | { 22 | Predicate = _ => true, 23 | ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse 24 | }); 25 | return app; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Framework/WebAPI/Hosting/BaseStartup.cs: -------------------------------------------------------------------------------- 1 | using App.Metrics; 2 | using FluentValidation.AspNetCore; 3 | using Framework.Core.Serializer; 4 | using Framework.WebAPI.Documetation; 5 | using Framework.WebAPI.HealthCheck; 6 | using Framework.WebAPI.Hosting.Cors; 7 | using Framework.WebAPI.Hosting.Formatters; 8 | using Framework.WebAPI.Hosting.JWT; 9 | using Framework.WebAPI.Hosting.Middlewares; 10 | using Framework.WebAPI.Versioning; 11 | using Microsoft.AspNetCore.Builder; 12 | using Microsoft.AspNetCore.Hosting; 13 | using Microsoft.AspNetCore.Mvc; 14 | using Microsoft.AspNetCore.Mvc.ApiExplorer; 15 | using Microsoft.Extensions.Configuration; 16 | using Microsoft.Extensions.DependencyInjection; 17 | using Microsoft.Extensions.Logging; 18 | using Newtonsoft.Json; 19 | using System.IO; 20 | 21 | namespace Framework.WebAPI.Hosting 22 | { 23 | public abstract class BaseStartup 24 | { 25 | public BaseStartup(IConfiguration configuration) 26 | { 27 | Configuration = configuration; 28 | } 29 | 30 | protected IConfiguration Configuration { get; } 31 | 32 | // This method gets called by the runtime. Use this method to add services to the container. 33 | public void ConfigureServices(IServiceCollection services) 34 | { 35 | services.AddSecurity(Configuration); 36 | 37 | services.AddCustomCors(); 38 | 39 | services.AddHealthCheck(); 40 | 41 | services.AddMvc(o => 42 | { 43 | o.InputFormatters.Add(new ImageRawRequestBodyFormatter()); 44 | }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2) 45 | .AddFluentValidation() 46 | .AddJsonOptions(o => o.SerializerSettings.NullValueHandling = NullValueHandling.Ignore); 47 | 48 | services.AddApiVersion(); 49 | services.AddDocumentation(); 50 | 51 | services.AddSingleton(); 52 | services.AddMetrics(); 53 | AfterConfigureServices(services); 54 | } 55 | 56 | public abstract void AfterConfigureServices(IServiceCollection services); 57 | 58 | 59 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 60 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApiVersionDescriptionProvider provider) 61 | { 62 | BeforeConfigureApp(app, env); 63 | 64 | app.UseCustomCors(); 65 | 66 | app.UseHealthCheck(); 67 | 68 | app.UseMiddleware() 69 | .UseMvc(); 70 | 71 | app.UseAuthentication(); 72 | 73 | app.UseDocumentation(provider); 74 | 75 | AfterConfigureApp(app, env); 76 | 77 | } 78 | 79 | public abstract void BeforeConfigureApp(IApplicationBuilder app, IHostingEnvironment env); 80 | 81 | public abstract void AfterConfigureApp(IApplicationBuilder app, IHostingEnvironment env); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Framework/WebAPI/Hosting/Cors/CorsExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Framework.WebAPI.Hosting.Cors 5 | { 6 | public static class CorsExtensions 7 | { 8 | public static IServiceCollection AddCustomCors(this IServiceCollection services) 9 | { 10 | services.AddCors(options => 11 | { 12 | options.AddPolicy("AllowAny", 13 | builder => builder 14 | .AllowAnyOrigin() 15 | .AllowAnyMethod() 16 | .AllowAnyHeader()); 17 | }); 18 | 19 | return services; 20 | } 21 | 22 | public static IApplicationBuilder UseCustomCors(this IApplicationBuilder app) 23 | { 24 | app.UseCors("AllowAny"); 25 | 26 | return app; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Framework/WebAPI/Hosting/Formatters/ImageRawRequestBodyFormatter.cs: -------------------------------------------------------------------------------- 1 |  2 | using Microsoft.AspNetCore.Mvc.Formatters; 3 | using Microsoft.Net.Http.Headers; 4 | using System; 5 | using System.IO; 6 | using System.Threading.Tasks; 7 | 8 | //Example: https://weblog.west-wind.com/posts/2017/Sep/14/Accepting-Raw-Request-Body-Content-in-ASPNET-Core-API-Controllers 9 | namespace Framework.WebAPI.Hosting.Formatters 10 | { 11 | /// 12 | /// Formatter that allows content of type image/*. Allows for a single input parameter 13 | /// in the form of: 14 | /// 15 | /// public string RawString([FromBody] byte[] data) 16 | /// 17 | public class ImageRawRequestBodyFormatter : InputFormatter 18 | { 19 | public ImageRawRequestBodyFormatter() 20 | { 21 | SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/*")); 22 | } 23 | 24 | 25 | /// 26 | /// Allowimage/png to be processed 27 | /// 28 | /// 29 | /// 30 | public override bool CanRead(InputFormatterContext context) 31 | { 32 | if (context == null) throw new ArgumentNullException(nameof(context)); 33 | 34 | var contentType = context.HttpContext.Request.ContentType; 35 | var lenght = context.HttpContext.Request.ContentLength; 36 | 37 | if (!lenght.HasValue) 38 | return false; 39 | 40 | //Validate content-lenght 41 | if (lenght > 2097152) 42 | return false; 43 | 44 | if (contentType.StartsWith("image/")) 45 | return true; 46 | 47 | return false; 48 | } 49 | 50 | /// 51 | /// Handle image/png for byte[] results 52 | /// 53 | /// 54 | /// 55 | public override async Task ReadRequestBodyAsync(InputFormatterContext context) 56 | { 57 | var request = context.HttpContext.Request; 58 | var contentType = context.HttpContext.Request.ContentType; 59 | 60 | if (!contentType.StartsWith("image/")) 61 | return await InputFormatterResult.FailureAsync(); 62 | 63 | using (var reader = new StreamReader(request.Body)) 64 | { 65 | var base64string = await reader.ReadToEndAsync(); 66 | var content = Convert.FromBase64String(base64string); 67 | return await InputFormatterResult.SuccessAsync(content); 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Framework/WebAPI/Hosting/JWT/JWTExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication.JwtBearer; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Options; 6 | using Microsoft.IdentityModel.Tokens; 7 | using System; 8 | 9 | namespace Framework.WebAPI.Hosting.JWT 10 | { 11 | public static class JWTExtensions 12 | { 13 | public static IServiceCollection AddSecurity(this IServiceCollection services, IConfiguration configuration) 14 | { 15 | services.AddCors(options => 16 | { 17 | options.AddPolicy("AllowAny", 18 | builder => builder 19 | .AllowAnyOrigin() 20 | .AllowAnyHeader() 21 | .AllowAnyMethod()); 22 | }); 23 | 24 | var signingConfigurations = new SigningConfigurations(); 25 | services.AddSingleton(signingConfigurations); 26 | 27 | services.Configure(configuration.GetSection(nameof(TokenConfigurations))); 28 | var tokenConfigurations = services.BuildServiceProvider().GetRequiredService>().Value; 29 | // var tokenConfigurations = new TokenConfigurations 30 | // { 31 | // Audience = CommonHelpers.GetValueFromEnv("JWT_AUD"), 32 | // Seconds = CommonHelpers.GetValueFromEnv("JWT_EXPIRATION"), 33 | // Issuer = CommonHelpers.GetValueFromEnv("JWT_ISS") 34 | // }; 35 | 36 | // services.AddSingleton(tokenConfigurations); 37 | 38 | services.AddAuthentication(authOptions => 39 | { 40 | authOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 41 | authOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 42 | }).AddJwtBearer(options => 43 | { 44 | 45 | options.TokenValidationParameters = new TokenValidationParameters 46 | { 47 | // Clock skew compensates for server time drift. 48 | // We recommend 5 minutes or less: 49 | ClockSkew = TimeSpan.FromMinutes(5), 50 | // Specify the key used to sign the token: 51 | IssuerSigningKey = signingConfigurations.Key, 52 | RequireSignedTokens = true, 53 | // Ensure the token hasn't expired: 54 | RequireExpirationTime = true, 55 | ValidateLifetime = true, 56 | 57 | ValidateAudience = true, 58 | ValidAudience = tokenConfigurations.Audience, 59 | 60 | ValidIssuer = tokenConfigurations.Issuer 61 | }; 62 | }); 63 | 64 | // Ativa o uso do token como forma de autorizar o acesso 65 | // a recursos deste projeto 66 | services.AddAuthorization(auth => 67 | { 68 | auth.AddPolicy(JwtBearerDefaults.AuthenticationScheme, new AuthorizationPolicyBuilder() 69 | .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme) 70 | .RequireAuthenticatedUser().Build()); 71 | }); 72 | 73 | return services; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Framework/WebAPI/Hosting/JWT/SigningConfigurations.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.IdentityModel.Tokens; 2 | using System.Security.Cryptography; 3 | 4 | namespace Framework.WebAPI.Hosting.JWT 5 | { 6 | public class SigningConfigurations 7 | { 8 | public SecurityKey Key { get; } 9 | public SigningCredentials SigningCredentials { get; } 10 | 11 | public SigningConfigurations() 12 | { 13 | using (var provider = new RSACryptoServiceProvider(2048)) 14 | { 15 | Key = new RsaSecurityKey(provider.ExportParameters(true)); 16 | } 17 | 18 | SigningCredentials = new SigningCredentials( 19 | Key, SecurityAlgorithms.RsaSha256Signature); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Framework/WebAPI/Hosting/JWT/TokenConfigurations.cs: -------------------------------------------------------------------------------- 1 | namespace Framework.WebAPI.Hosting.JWT 2 | { 3 | public class TokenConfigurations 4 | { 5 | public string Audience { get; set; } 6 | public string Issuer { get; set; } 7 | public int Seconds { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Framework/WebAPI/Hosting/Middlewares/HttpExceptionMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Framework.WebAPI.Responses; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Newtonsoft.Json; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Net; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Framework.WebAPI.Hosting.Middlewares 12 | { 13 | /// 14 | /// Middleware para captura de exceções. 15 | /// 16 | public class HttpExceptionMiddleware 17 | { 18 | private readonly RequestDelegate _next; 19 | private readonly ILogger _logger; 20 | 21 | /// 22 | /// Construtor do middleware. 23 | /// 24 | /// Request Delegate. 25 | /// Fábrica de log. 26 | public HttpExceptionMiddleware(RequestDelegate next, ILoggerFactory loggerFactory) 27 | { 28 | _next = next; 29 | _logger = loggerFactory.CreateLogger(); 30 | } 31 | 32 | /// 33 | /// Execução da captura. 34 | /// 35 | /// Contexto HTTP. 36 | /// Tarefa. 37 | public async Task Invoke(HttpContext context) 38 | { 39 | try 40 | { 41 | await _next.Invoke(context); 42 | } 43 | catch (Exception ex) 44 | { 45 | _logger.LogError(ex, "HttpExceptionMiddleware: Erro inexperado"); 46 | 47 | var result = JsonConvert.SerializeObject(new { message = "Infelizmente ocorreu um erro não tratado, entre em contato com os desenolvedores" }); 48 | context.Response.ContentType = "application/json"; 49 | context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; ; 50 | await context.Response.WriteAsync(result); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Framework/WebAPI/Responses/NotOkDefaultReponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Framework.WebAPI.Responses 6 | { 7 | /// 8 | /// Estrutura padrão para respostas de erro 9 | /// 10 | public sealed class NotOkDefaultReponse 11 | { 12 | public string Message { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Framework/WebAPI/Versioning/ApiVersionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Framework.WebAPI.Versioning 4 | { 5 | public static class ApiVersionExtensions 6 | { 7 | public static IServiceCollection AddApiVersion(this IServiceCollection services) 8 | { 9 | // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service 10 | // note: the specified format code will format the version as "'v'major[.minor][-status]" 11 | services.AddVersionedApiExplorer(o => 12 | { 13 | o.GroupNameFormat = "'v'VVV"; 14 | 15 | // note: this option is only necessary when versioning by url segment. the SubstitutionFormat 16 | // can also be used to control the format of the API version in route templates 17 | o.SubstituteApiVersionInUrl = true; 18 | }); 19 | 20 | services.AddApiVersioning(o => 21 | { 22 | o.ReportApiVersions = true; 23 | o.AssumeDefaultVersionWhenUnspecified = true; 24 | o.DefaultApiVersion = new Microsoft.AspNetCore.Mvc.ApiVersion(1, 0); 25 | }); 26 | 27 | 28 | return services; 29 | } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docker-compose-services.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | 3 | services: 4 | mongodb: 5 | image: mongo 6 | container_name: "mongodb" 7 | ports: 8 | - "27017:27017" 9 | 10 | mysqldb: 11 | image: mysql:5 12 | command: --default-authentication-plugin=mysql_native_password 13 | container_name: "mysqldb" 14 | deploy: 15 | replicas: 1 16 | endpoint_mode: dnsrr 17 | environment: 18 | MYSQL_ROOT_PASSWORD: root 19 | ports: 20 | - "3306:3306" 21 | 22 | redis: 23 | image: redis 24 | container_name: "redis" 25 | deploy: 26 | replicas: 1 27 | endpoint_mode: dnsrr 28 | ports: 29 | - "6379:6379" 30 | 31 | rabbitmq: 32 | image: rabbitmq:3-management 33 | container_name: 'rabbitmq' 34 | deploy: 35 | replicas: 1 36 | endpoint_mode: dnsrr 37 | ports: 38 | - "5672:5672" 39 | - "15672:15672" 40 | environment: 41 | RABBITMQ_DEFAULT_USER: guest 42 | RABBITMQ_DEFAULT_PASS: guest -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | 3 | services: 4 | web: 5 | build: . 6 | image: demo-api 7 | container_name: aspnet-core-webapi 8 | restart: always 9 | deploy: 10 | replicas: 1 11 | environment: 12 | - JWT_ISS=http://localhost:8181/api/signup 13 | - JWT_AUD=demo.api 14 | - JWT_EXPIRATION=1800 15 | - MONGO_URI=mongodb://mongodb:27017/clinfy 16 | - MYSQL_CONNECTION=Server=mysqldb;Database=dbz;User=root;Password=root; 17 | - REDIS_URI=redis,name=Demo.Api,defaultDatabase=1 18 | - RABBITMQ_URI=amqp://guest:guest@rabbitmq 19 | ports: 20 | - "8181:80" 21 | links: 22 | - mongodb 23 | - mysqldb 24 | - rabbitmq 25 | - redis 26 | - healthapp 27 | depends_on: 28 | - mongodb 29 | - mysqldb 30 | - rabbitmq 31 | - redis 32 | 33 | healthapp: 34 | image: xabarilcoding/healthchecksui 35 | container_name: healthapp 36 | restart: always 37 | environment: 38 | HealthChecks-UI__HealthChecks__0__Name: web 39 | HealthChecks-UI__HealthChecks__0__Uri: http://web/healthz 40 | ports: 41 | - "8083:80" 42 | 43 | mongodb: 44 | image: mongo 45 | container_name: "mongodb" 46 | ports: 47 | - "27017:27017" 48 | 49 | mysqldb: 50 | image: mysql:5 51 | command: --default-authentication-plugin=mysql_native_password 52 | container_name: "mysqldb" 53 | deploy: 54 | replicas: 1 55 | endpoint_mode: dnsrr 56 | environment: 57 | MYSQL_ROOT_PASSWORD: root 58 | ports: 59 | - "3306:3306" 60 | 61 | redis: 62 | image: redis 63 | container_name: "redis" 64 | deploy: 65 | replicas: 1 66 | endpoint_mode: dnsrr 67 | ports: 68 | - "6379:6379" 69 | 70 | rabbitmq: 71 | image: rabbitmq:3-management 72 | container_name: 'rabbitmq' 73 | deploy: 74 | replicas: 1 75 | endpoint_mode: dnsrr 76 | ports: 77 | - "5672:5672" 78 | - "15672:15672" 79 | environment: 80 | RABBITMQ_DEFAULT_USER: guest 81 | RABBITMQ_DEFAULT_PASS: guest 82 | 83 | prom: 84 | image: quay.io/prometheus/prometheus:v2.0.0 85 | volumes: 86 | - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml 87 | command: "--config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/prometheus" 88 | ports: 89 | - 9090:9090 90 | depends_on: 91 | - exporter 92 | exporter: 93 | image: prom/node-exporter:latest 94 | ports: 95 | - "9100:9100" 96 | grafana: 97 | image: grafana/grafana 98 | ports: 99 | - "3000:3000" 100 | environment: 101 | - GF_INSTALL_PLUGINS=grafana-piechart-panel 102 | depends_on: 103 | - prom -------------------------------------------------------------------------------- /prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | # my global config 2 | global: 3 | scrape_interval: 15s # By default, scrape targets every 15 seconds. 4 | evaluation_interval: 15s # By default, scrape targets every 15 seconds. 5 | # scrape_timeout is set to the global default (10s). 6 | 7 | # Attach these labels to any time series or alerts when communicating with 8 | # external systems (federation, remote storage, Alertmanager). 9 | external_labels: 10 | monitor: 'codelab-monitor' 11 | 12 | # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. 13 | rule_files: 14 | # - "first.rules" 15 | # - "second.rules" 16 | 17 | # A scrape configuration containing exactly one endpoint to scrape: 18 | # Here it's Prometheus itself. 19 | scrape_configs: 20 | # The job name is added as a label `job=` to any timeseries scraped from this config. 21 | - job_name: 'prometheus' 22 | 23 | # Override the global default and scrape targets from this job every 5 seconds. 24 | scrape_interval: 5s 25 | 26 | # metrics_path defaults to '/metrics' 27 | # scheme defaults to 'http'. 28 | 29 | static_configs: 30 | - targets: ['localhost:9090'] 31 | 32 | - job_name: "node" 33 | scrape_interval: "15s" 34 | static_configs: 35 | - targets: ['exporter:9100'] 36 | 37 | - job_name: "webapi" 38 | scrape_interval: 5s 39 | static_configs: 40 | - targets: ['web'] -------------------------------------------------------------------------------- /sonarqube.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM microsoft/dotnet:2.2-sdk AS build-env 2 | 3 | RUN apt-get update -yqq > /dev/null && \ 4 | apt-get install -yqq default-jre > /dev/null 5 | 6 | RUN dotnet tool install -g dotnet-sonarscanner 7 | 8 | ENV PATH="$PATH:/root/.dotnet/tools" 9 | 10 | WORKDIR /app 11 | COPY . ./ 12 | WORKDIR /app/ 13 | 14 | RUN dotnet restore --no-cache 15 | 16 | RUN dotnet sonarscanner begin /k:"Template" /d:sonar.host.url="http://10.11.4.172:9000" /d:sonar.login="af909261a2f77dcd104c0431238a50c37a2a55e6" /d:sonar.cs.opencover.reportsPaths="./**/coverage.opencover.xml" 17 | RUN dotnet build 18 | RUN dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover --no-build 19 | 20 | RUN dotnet sonarscanner end /d:sonar.login="af909261a2f77dcd104c0431238a50c37a2a55e6" 21 | --------------------------------------------------------------------------------