GithubHelp home page GithubHelp logo

dentallapp / back-end Goto Github PK

View Code? Open in Web Editor NEW
56.0 2.0 15.0 1.87 MB

A web application that manages appointment scheduling, reminders and cancellation of appointments

License: GNU Affero General Public License v3.0

C# 93.25% HTML 6.40% Dockerfile 0.30% Shell 0.04%
bot-framework csharp dental-office dotnet plugin-architecture plugin-system vertical-slice-architecture webapi

back-end's People

Contributors

guiller1999 avatar mrdave1999 avatar ts-pytham avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

back-end's Issues

IDX10501: Signature validation failed. Unable to match key: Failed to validate the token.

I have deployed this project on a test virtual server and in addition, I created the Azure Bot Service resource to register the bot and attach the DirectLine channel in the resource, however, when interacting with the bot from the WebChat (a component of Bot Framework-WebChat) the following error occurs on the server side:

Jan 12 13:50:15 DentallApp bash[1582]: info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Jan 12 13:50:15 DentallApp bash[1582]:       token: '{"alg":"RS256","kid":"d25T3rS8ZCu8VUIqDV3fV14llFI","x5t":"d25T3rS8ZCu8VUIqDV3fV14llFI","typ":"JWT","cty":"JWT"}.{"serviceurl":"https://directline.botframework.com/","nbf":1673549415,"exp":1673550015,"iss":"https://api.botframework.com","aud":"023e5038-8c60-45ee-87a2-852aa984480c"}'.
Jan 12 13:50:15 DentallApp bash[1582]:       '.
Jan 12 13:50:15 DentallApp bash[1582]:          at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters, BaseConfiguration configuration)
Jan 12 13:50:15 DentallApp bash[1582]:          at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(Byte[] encodedBytes, Byte[] signature, SecurityKey key, String algorithm, SecurityToken securityToken, TokenValidationParameters validationParameters)
Jan 12 13:50:15 DentallApp bash[1582]:          at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForVerifying(SecurityKey key, String algorithm)
Jan 12 13:50:15 DentallApp bash[1582]:          at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForVerifying(SecurityKey key, String algorithm, Boolean cacheProvider)
Jan 12 13:50:15 DentallApp bash[1582]:          at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures, Boolean cacheProvider)
Jan 12 13:50:15 DentallApp bash[1582]:        is not supported. The list of supported algorithms is available here: https://aka.ms/IdentityModel/supported-algorithms
Jan 12 13:50:15 DentallApp bash[1582]:       Algorithm: 'RS256', SecurityKey: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey, KeyId: '', InternalId: 'd1JZdyHpnBVySxTBXp85dMx1k7Hb2R4DAYUctaaQCtE'.'
Jan 12 13:50:15 DentallApp bash[1582]:        'System.NotSupportedException: IDX10634: Unable to create the SignatureProvider.
Jan 12 13:50:15 DentallApp bash[1582]:       Exceptions caught:
Jan 12 13:50:15 DentallApp bash[1582]:       Number of keys in Configuration: '1'.
Jan 12 13:50:15 DentallApp bash[1582]:       Number of keys in TokenValidationParameters: '0'.
Jan 12 13:50:15 DentallApp bash[1582]:       kid: 'd25T3rS8ZCu8VUIqDV3fV14llFI'.
Jan 12 13:50:15 DentallApp bash[1582]:       Bearer was not authenticated. Failure message: IDX10501: Signature validation failed. Unable to match key:
Jan 12 13:50:15 DentallApp bash[1582]: info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[7]
Jan 12 13:50:15 DentallApp bash[1582]:          at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
Jan 12 13:50:15 DentallApp bash[1582]:          at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
Jan 12 13:50:15 DentallApp bash[1582]:          at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, JwtSecurityToken outerToken, TokenValidationParameters validationParameters, SecurityToken& signatureValidatedToken)
Jan 12 13:50:15 DentallApp bash[1582]:       --- End of stack trace from previous location ---
Jan 12 13:50:15 DentallApp bash[1582]:          at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateJWS(String token, TokenValidationParameters validationParameters, BaseConfiguration currentConfiguration, SecurityToken& signatureValidatedToken, ExceptionDispatchInfo& exceptionThrown)
Jan 12 13:50:15 DentallApp bash[1582]:          at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters, BaseConfiguration configuration)
Jan 12 13:50:15 DentallApp bash[1582]:          at Microsoft.IdentityModel.Tokens.InternalValidators.ValidateLifetimeAndIssuerAfterSignatureNotValidatedJwt(SecurityToken securityToken, Nullable`1 notBefore, Nullable`1 expires, String kid, TokenValidationParameters validationParameters, BaseConfiguration configuration, StringBuilder exceptionStrings, Int32 numKeysInConfiguration, Int32 numKeysInTokenValidationParameters)
Jan 12 13:50:15 DentallApp bash[1582]:       token: '{"alg":"RS256","kid":"d25T3rS8ZCu8VUIqDV3fV14llFI","x5t":"d25T3rS8ZCu8VUIqDV3fV14llFI","typ":"JWT","cty":"JWT"}.{"serviceurl":"https://directline.botframework.com/","nbf":1673549415,"exp":1673550015,"iss":"https://api.botframework.com","aud":"023e5038-8c60-45ee-87a2-852aa984480c"}'.
Jan 12 13:50:15 DentallApp bash[1582]:       '.
Jan 12 13:50:15 DentallApp bash[1582]:          at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters, BaseConfiguration configuration)
Jan 12 13:50:15 DentallApp bash[1582]:          at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(Byte[] encodedBytes, Byte[] signature, SecurityKey key, String algorithm, SecurityToken securityToken, TokenValidationParameters validationParameters)
Jan 12 13:50:15 DentallApp bash[1582]:          at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForVerifying(SecurityKey key, String algorithm)
Jan 12 13:50:15 DentallApp bash[1582]:          at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForVerifying(SecurityKey key, String algorithm, Boolean cacheProvider)
Jan 12 13:50:15 DentallApp bash[1582]:          at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures, Boolean cacheProvider)
Jan 12 13:50:15 DentallApp bash[1582]:        is not supported. The list of supported algorithms is available here: https://aka.ms/IdentityModel/supported-algorithms
Jan 12 13:50:15 DentallApp bash[1582]:       Algorithm: 'RS256', SecurityKey: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey, KeyId: '', InternalId: 'd1JZdyHpnBVySxTBXp85dMx1k7Hb2R4DAYUctaaQCtE'.'
Jan 12 13:50:15 DentallApp bash[1582]:        'System.NotSupportedException: IDX10634: Unable to create the SignatureProvider.
Jan 12 13:50:15 DentallApp bash[1582]:       Exceptions caught:
Jan 12 13:50:15 DentallApp bash[1582]:       Number of keys in Configuration: '1'.
Jan 12 13:50:15 DentallApp bash[1582]:       Number of keys in TokenValidationParameters: '0'.
Jan 12 13:50:15 DentallApp bash[1582]:       kid: 'd25T3rS8ZCu8VUIqDV3fV14llFI'.
Jan 12 13:50:15 DentallApp bash[1582]:       Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10501: Signature validation failed. Unable to match key:
Jan 12 13:50:15 DentallApp bash[1582]:       Failed to validate the token.

This error occurs every time a message is sent to the bot, although the bot still works without any problem.

Migrate to .net 7.0 and EF Core 7.0

These are the packages to be upgraded to v4.18.1:

<PackageReference Include="Microsoft.Bot.Builder.Dialogs" Version="4.18.1" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="4.18.1" />

Now with this change, the project can change from target framework to .net 7.0, previously it was not possible because bot framework worked with .netcoreapp 3.1 (see microsoft/botframework-sdk#6551).

Get the list of dentists for the basic user

The following method gets the list of the basic user's favorite dentists and also includes the dentists that are not preferred by the userId:

public async Task<IEnumerable<DentistGetDto>> GetListOfDentistsAsync(int userId)
=> await (from dentist in Context.Set<Employee>()
join dentistDetails in Context.Set<Person>() on dentist.PersonId equals dentistDetails.Id
join office in Context.Set<Office>() on dentist.OfficeId equals office.Id
join userRole in Context.Set<UserRole>() on
new
{
RoleId = RolesId.Dentist,
dentist.UserId
}
equals
new
{
userRole.RoleId,
userRole.UserId
}
join favoriteDentist in Context.Set<FavoriteDentist>() on
new
{
UserId = userId,
dentist.Id
}
equals
new
{
favoriteDentist.UserId,
Id = favoriteDentist.DentistId
} into favoriteDentists
from favoriteDentist in favoriteDentists.DefaultIfEmpty()
select new DentistGetDto
{
DentistId = dentist.Id,
FullName = dentistDetails.FullName,
PregradeUniversity = dentist.PregradeUniversity,
PostgradeUniversity = dentist.PostgradeUniversity,
OfficeId = dentist.OfficeId,
OfficeName = office.Name,
IsFavorite = favoriteDentist != null
}).ToListAsync();

But the curious line of code is the following:

IsFavorite = favoriteDentist != null

The above line EF Core translates it to:

`f`.`id` IS NOT NULL AS `IsFavorite`

f is a simple alias of the favorite_dentists table.

The strangest thing is that if the code is changed to:

IsFavorite            = favoriteDentist.Id != null 

And the result is the same as in the previous case:

`f`.`id` IS NOT NULL AS `IsFavorite`

In both cases the same sql code is generated and returns the expected result, only that in this expression a warning is thrown that an int cannot be compared with null:

// Generates a warning but the LINQ query continues to work.
favoriteDentist.Id != null 

There is an issue that talks about this: dotnet/efcore#22517

So the question is, can our project rely on this behavior?

PD: The final SQL code generated by EF Core is:

SELECT
`e`.`id` AS `DentistId`, 
CONCAT(CONCAT(COALESCE(`p`.`names`, ''), ' '), COALESCE(`p`.`last_names`, '')) AS `FullName`, 
`e`.`pregrade_university` AS `PregradeUniversity`, 
`e`.`postgrade_university` AS `PostgradeUniversity`, 
`e`.`office_id` AS `OfficeId`, 
`t`.`name` AS `OfficeName`, 
`f`.`id` IS NOT NULL AS `IsFavorite`
FROM `employees` AS `e`
INNER JOIN `persons` AS `p` ON `e`.`person_id` = `p`.`id`
INNER JOIN (
   	SELECT `o`.`id`, `o`.`name`
   	FROM `offices` AS `o`
   	WHERE NOT (`o`.`is_deleted`)
   ) AS `t` ON `e`.`office_id` = `t`.`id`
INNER JOIN `user_roles` AS `u` ON (4 = `u`.`role_id`) AND (`e`.`user_id` = `u`.`user_id`)
LEFT JOIN `favorite_dentists` AS `f` ON (@__userId_0 = `f`.`user_id`) AND (`e`.`id` = `f`.`dentist_id`)
WHERE NOT (`e`.`is_deleted`)

Create a custom endpoint for generate a Direct Line token

It is necessary to create an endpoint that is responsible for calling the DirectLine API to generate a token and this is because if it is done from the client, the secret key would be exposed, this brings as a consequence that any malicious user could access any conversation associated with the bot.

The solution is to create a service on the back-end side in which it is in charge of making a request to the next endpoint:

POST https://directline.botframework.com/v3/directline/tokens/generate
Authorization: Bearer SECRET_KEY

This can be done on the front-end side but it would be insecure.

Reduce code in POST/PUT operations of controllers

SAMPLES

The following codes can be simplified by using the ternary operator:

public async Task<ActionResult<Response<DtoBase>>> Post([FromBody]AppointmentInsertDto appointmentInsertDto)
{
var response = await _appointmentService.CreateAppointmentAsync(appointmentInsertDto);
if (response.Success)
return CreatedAtAction(nameof(Post), response);
return BadRequest(response);
}

public async Task<ActionResult<Response>> Put(int id, [FromBody]AppointmentUpdateDto appointmentUpdateDto)
{
var response = await _appointmentService.UpdateAppointmentAsync(id, User, appointmentUpdateDto);
if (response.Success)
return Ok(response);
return BadRequest(response);
}

Add project documentation

  • Translate comments from Spanish to English.
  • Add XML comments to controller actions to include it in swagger doc.
  • Produce the type of HTTP status code returned by each controller action.

Apply the null-condition operator to format dates

This code:

public static string GetDateAndHourInSpanishFormat(this DateTime? dateTimeNullable)
{
if (dateTimeNullable is null)
return null;
DateTime dt = (DateTime)dateTimeNullable;
return dt.ToString("f", new System.Globalization.CultureInfo("es-ES"));
}

Can be converted to:

 public static string GetDateAndHourInSpanishFormat(this DateTime? dt) 
     => dt?.ToString("f", new System.Globalization.CultureInfo("es-ES")); 

The compiler translates the above code to:

public static string GetDateAndHourInSpanishFormat(Nullable<DateTime> dt)
{
     return dt.HasValue ? dt.GetValueOrDefault().ToString("f", new CultureInfo("es-ES")) : null;
}

Unit tests take more than a minute to run

Something is happening but unit tests are taking a long time to run. I'm not sure if it's the simulation tests that are performed between the bot and the user, as these tests are more of integration, because they depend on the file system.

Add Docker support

The following files should be created:

  • Dockerfile
  • docker-compose.yml: It has three services: webapi, database and InDirectLine.

Must also create the InDirectLine image and upload it in DockerHub to use it from docker-compose.yml.

The client must perform the following steps to deploy the back-end:

  • Clone the repository.
  • Configure the .env file.
  • Execute the command: docker compose up --build -d.

Remove child layer repositories

Child layer repositories such as Kinship, Gender and Appointment Status only add unnecessary complexity, so they can be eliminated as they are very simple queries.

Add a persistence mechanism for jobs

This mechanism is necessary because if the reminders are scheduled to be sent on the first day of each month at 8:00 a.m. but if the server shuts down for several hours before the scheduled time, then the reminder will not be sent when the server starts because it is after 8 a.m.

See Job Stores.

Remove single-line methods at the service layer

For example:

public async Task<IEnumerable<OfficeGetDto>> GetOfficesAsync()
=> await _officeRepository.GetOfficesAsync();
public async Task<IEnumerable<OfficeShowDto>> GetOfficesForEditAsync()
=> await _officeRepository.GetOfficesForEditAsync();
public async Task<IEnumerable<OfficeGetDto>> GetAllOfficesAsync()
=> await _officeRepository.GetAllOfficesAsync();

These methods are useless, they do not provide any benefit, they only make maintenance more difficult.

Remove DbSets from the `AppDbContext` class

In the code the generic Set<T> method is being used, so these properties are unusable and only saturate the context instance:

public DbSet<GeneralTreatment> GeneralTreatments { get; set; }
public DbSet<SpecificTreatment> SpecificTreatments { get; set; }
public DbSet<Person> Persons { get; set; }
public DbSet<Gender> Genders { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserRole> UserRoles { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<Dependent> Dependents { get; set; }
public DbSet<Kinship> Kinships { get; set; }
public DbSet<Employee> Employees { get; set; }
public DbSet<Office> Offices { get; set; }
public DbSet<Appoinment> Appoinments { get; set; }
public DbSet<AppoinmentStatus> AppoinmentsStatus { get; set; }
public DbSet<EmployeeSchedule> EmployeeSchedules { get; set; }
public DbSet<WeekDay> WeekDays { get; set; }
public DbSet<FavoriteDentist> FavoriteDentists { get; set; }
public DbSet<OfficeSchedule> OfficeSchedules { get; set; }

Implement each layer in a separate project

Purpose

The purpose of separating these layers in your own project is to ensure that the interdependence between them is not broken.

For example, the shared layer does not need to depend on any other layer. It is a simple "mediator" that allows communication between the layers, thus avoiding coupling between them.
In case I accidentally add a class from another layer in the shared layer, the compiler will generate a compilation error that the type does not exist or there is no reference to another project. This avoids accidentally coupling one layer with another.

The current diagram represents a part of dentallapp's architecture:

backend-architecture

Images of dental services cannot have white-spaces

On Windows there are no problems but on Linux distributions when a file name has whitespace, it is appended around single quotes. This leads to invalid behavior, because in the database the image name is stored without single quotes but in the file system it is (this should not be the case).

This would cause problems, for example, when you want to edit the image of a dental service, it will not be possible to delete the old image because the image name in the database will not match the image in the file system.

Sample:

File system:

'brackets_0023222.png'

database:

brackets_0023222.png

The classes should not be coupled with data provider

For example, this class is coupled to MySqlConnection and should actually depend on an abstraction to facilitate dependency inversion:

public async Task<ReportGetTotalAppoinmentDto> GetTotalAppoinmentsByDateRangeAsync(ReportPostWithDentistDto reportPostDto)
{
using var connection = new MySqlConnection(_settings.ConnectionString);
var sql = @"
SELECT

Also, in this case, the using global directive could be used to avoid having to import the types in each file:

using Dapper;
using MySqlConnector;

EF Core select all columns in SQL when a DTO mapper is called on Select()

The following LINQ:

=> await Context.Set<User>()
.Include(user => user.Person)
.Where(user => user.UserName == username)
.Select(user => new UserResetPasswordDto()
{
UserId = user.Id,
UserName = user.UserName,
Name = user.Person.Names,
Password = user.Password
})
.FirstOrDefaultAsync();

EF Core translates it to:

SELECT `u`.`id` AS `UserId`, `u`.`username` AS `UserName`, `p`.`names` AS `Name`, `u`.`password` AS `Password`
FROM `users` AS `u`
INNER JOIN `persons` AS `p` ON `u`.`person_id` = `p`.`id`
WHERE `u`.`username` = @__username_0
LIMIT 1

In this case EF Core selects the columns associated to UserResetPasswordDto.

But when the mapper is called in the Select method:

.Select(user => user.MapToUserResetPasswordDto()) 

EF Core translates it to:

SELECT `u`.`id`, `u`.`created_at`, `u`.`password`, `u`.`person_id`, `u`.`refresh_token`, `u`.`refresh_token_expiry`, `u`.`updated_at`, `u`.`username`, `p`.`id`, `p`.`cell_phone`, `p`.`created_at`, `p`.`date_birth`, `p`.`document`, `p`.`email`, `p`.`gender_id`, `p`.`last_names`, `p`.`names`, `p`.`updated_at`
FROM `users` AS `u`
INNER JOIN `persons` AS `p` ON `u`.`person_id` = `p`.`id`
WHERE `u`.`username` = @__username_0
LIMIT 1

EF Core selects ALL columns. The mapper code is:

public static UserResetPasswordDto MapToUserResetPasswordDto(this User user)
=> new()
{
UserId = user.Id,
UserName = user.UserName,
Name = user.Person.Names,
Password = user.Password
};

There is a possible solution in this StackOverflow answer: https://stackoverflow.com/a/62138200 (must be tested)

This problem is also mentioned here: dotnet/efcore#24509

Add the kinship in the choices

When the chatbot asks the user to select a patient for appointment scheduling, it could be displayed like this:
Image
The kinship is displayed next to the patient's name.

Remove abstractions that represent application logic

The abstraction is only used once and has only one implementation associated with it. This only adds unnecessary complexity to the project and complicates maintenance, since changes must be made to both the abstraction and the concrete class when a new method needs to be added.

When using the `dotnet ef` command displays warnings related to global query filters

The CLI displays the following warnings:

warn: Microsoft.EntityFrameworkCore.Model.Validation[10622]
      Entity 'Employee' has a global query filter defined and is the required end of a relationship with the entity 'Appoinment'. This may lead to unexpected results when the required entity is filtered out. Either configure the navigation as optional, or define matching query filters for both entities in the navigation. See https://go.microsoft.com/fwlink/?linkid=2131316 for more information.

warn: Microsoft.EntityFrameworkCore.Model.Validation[10622]
      Entity 'Employee' has a global query filter defined and is the required end of a relationship with the entity 'EmployeeSchedule'. This may lead to unexpected results when the required entity is filtered out. Either configure the navigation as optional, or define matching query filters for both entities in the navigation. See https://go.microsoft.com/fwlink/?linkid=2131316 for more information.

warn: Microsoft.EntityFrameworkCore.Model.Validation[10622]
      Entity 'Employee' has a global query filter defined and is the required end of a relationship with the entity 'FavoriteDentist'. This may lead to unexpected results when the required entity is filtered out. Either configure the navigation as optional, or define matching query filters for both entities in the navigation. See https://go.microsoft.com/fwlink/?linkid=2131316 for more information.

warn: Microsoft.EntityFrameworkCore.Model.Validation[10622]
      Entity 'GeneralTreatment' has a global query filter defined and is the required end of a relationship with the entity 'SpecificTreatment'. This may lead to unexpected results when the required entity is filtered out. Either configure the navigation as optional, or define matching query filters for both entities in the navigation. See https://go.microsoft.com/fwlink/?linkid=2131316 for more information.

Could these warnings impair the application or bring unexpected results?

Prevent the user from not being able to use the chatbot if not authenticated

Currently the endpoint where the bot listens is public, so anyone could communicate with the bot. It would be nice if only authenticated users could access the bot from the website.

In addition, the same security token can be passed to the bot as the one provide by the website when the user logs in.
In that case, on the bot side, the access token would have to be validated to check if the user has authenticated.

If no check is performed, any user can schedule appointments for other users simply by knowing the user ID (this is known as user impersonation).

Avoid coupling classes with `DateTime.Now`

Currently the IDateTimeProvider interface (which is in the Helpers module) has been added for this problem, but some modules still need to be refactored to depend on the interface to facilitate unit testing.

Send requests to the database once a day to remind appointments

Currently, there is a background process that sends an it hourly request to the database in order to get the appointments that are close to the stipulated date and then send the appointment reminders to the patients. Instead of sending it hourly, the request could be sent in the morning at 8:00 a.m. (this could be modified from a .env file).

PD: So with the above change, the HasReminder property of the Appointment model would no longer be needed (this property was used to ensure that the reminder is sent only once to the patient).

AuthenticationScheme: Bearer was not authenticated

When accessing an unprotected endpoint such as http://localhost:3978/api/gender, the following message is displayed:

dbug: DentallApp.Extensions.AuthenticationJwtBearer+CustomJwtBearerHandler[9]
      AuthenticationScheme: Bearer was not authenticated.
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]

This should not be displayed because at no point is the Authorization header included in the HTTP request, i.e. the Bearer does not actually provide any access token.

This is strange behavior, as this does not impair the operation of the application. An important detail is that this message is only displayed in development mode.
If we change ASPNETCORE_ENVIRONMENT to Production, the message disappears.

PD: This behavior arose from PR #136.

Add each feature in a file to represent the vertical slice

Example:

In the Dependents module there would be four files representing the vertical slice:

  • CreateDependent.cs
  • DeleteDependent.cs
  • UpdateDependent.cs
  • GetDependentsByUserId.cs

Each file represents a feature and in it are the related components such as the use case, mapper, validator and the DTOs (request/response).

Load bot credentials from an .env file

The following keys must be set up from an .env:

  • MICROSOFT_APP_TYPE=
  • MICROSOFT_APP_ID=
  • MICROSOFT_APP_PASSWORD=
  • MICROSOFT_APP_TENANT_ID=

The default value of each key must be an empty string.

Replace the `IDbConnector` interface by `IDbConnection`

This connector is unnecessary, since you can use the provider class as MySqlConnection:

using MySqlConnector;
namespace DentallApp.DataAccess.DbConnectors;
public class MariaDbConnector : IDbConnector
{
private readonly string _connectionString;
public MariaDbConnector(string connectionString)
=> _connectionString = connectionString;
public IDbConnection CreateConnection()
=> new MySqlConnection(_connectionString);
}

That abstraction layer is not needed, because IDbConnection can be registered as a service, for example:

builder.Services.AddScoped<IDbConnection>(
                 serviceProvider => new MySqlConnection("MY_CONNECTION_STRING"));

Another advantage of this approach is that it is no longer necessary to use the using statement to release the resource, because the DI container takes care of that responsibility (behind the scenes it invokes the Dispose method).

Avoid injecting an unnecessary dependency in the `Reports` module

Sample code:

public ReportQuery(AppDbContext context, IDbConnector dbConnector)
{
_context = context;
_dbConnector = dbConnector;
}

This class uses both an ORM and raw sql. The problem lies when an operation is executed using raw sql together with Dapper, so the DI container injects an instance of AppDbContext in the constructor of the ReportQuery class, this leads to a waste of memory (because the AppDbContext is never used).
In this case, it would be useful to use the MediatR library and create a per-request handler so that each one uses an AppDbContext or raw sql with Dapper in isolation.

One important detail is that AppDbContext is registered as scoped, so an instance is created in each HTTP request, while IDbConnector is registered as singleton, so it is the same instance in each request.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.