├── .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 |
--------------------------------------------------------------------------------