Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
H
HIAST-Clinics
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
almohanad.hafez
HIAST-Clinics
Commits
041d69cc
Commit
041d69cc
authored
Aug 18, 2024
by
Almouhannad
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
(B) Link validation, implement Create employee case
parent
22134c5c
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
272 additions
and
9 deletions
+272
-9
API.csproj
Clinics.Backend/API/API.csproj
+1
-0
Program.cs
Clinics.Backend/API/Program.cs
+9
-0
Application.csproj
Clinics.Backend/Application/Application.csproj
+1
-0
ValidationPipelineBehavior.cs
...ckend/Application/Behaviors/ValidationPipelineBehavior.cs
+90
-0
CreateEmployeeCommandValidator.cs
...ployees/Commands/Create/CreateEmployeeCommandValidator.cs
+73
-0
ValidationErrorMessages.cs
...idationConstants/ErrorMessages/ValidationErrorMessages.cs
+19
-0
ValidationRegularExpressions.cs
...stants/RegularExpressions/ValidationRegularExpressions.cs
+18
-0
ApiController.cs
...cs.Backend/Presentation/Controllers/Base/ApiController.cs
+53
-0
EmployeesController.cs
...s.Backend/Presentation/Controllers/EmployeesController.cs
+8
-9
No files found.
Clinics.Backend/API/API.csproj
View file @
041d69cc
...
...
@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.2" />
<PackageReference Include="MediatR" Version="12.4.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
...
...
Clinics.Backend/API/Program.cs
View file @
041d69cc
using
API.Options.Database
;
using
API.SeedDatabaseHelper
;
using
Application.Behaviors
;
using
FluentValidation
;
using
MediatR
;
using
Microsoft.EntityFrameworkCore
;
using
Microsoft.Extensions.Options
;
using
Persistence.Context
;
...
...
@@ -49,6 +52,12 @@ builder
#region Add MadiatR
builder
.
Services
.
AddMediatR
(
configuration
=>
configuration
.
RegisterServicesFromAssembly
(
Application
.
AssemblyReference
.
Assembly
));
#region Add validation pipeline
builder
.
Services
.
AddScoped
(
typeof
(
IPipelineBehavior
<,>),
typeof
(
ValidationPipelineBehavior
<,>));
builder
.
Services
.
AddValidatorsFromAssembly
(
Application
.
AssemblyReference
.
Assembly
);
#endregion
#endregion
#region Link controllers with presentation layer
...
...
Clinics.Backend/Application/Application.csproj
View file @
041d69cc
...
...
@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation" Version="11.9.2" />
<PackageReference Include="MediatR" Version="12.4.0" />
</ItemGroup>
...
...
Clinics.Backend/Application/Behaviors/ValidationPipelineBehavior.cs
0 → 100644
View file @
041d69cc
using
Domain.Shared.Validation
;
using
Domain.Shared
;
using
MediatR
;
using
FluentValidation
;
namespace
Application.Behaviors
;
// Using MediatR pipeline to perform validation
// Using Fluent validation (IValidator) to make validators
public
class
ValidationPipelineBehavior
<
TRequest
,
TResponse
>
:
IPipelineBehavior
<
TRequest
,
TResponse
>
// From MediatR
where
TRequest
:
IRequest
<
TResponse
>
// Request sent by the pipeline (enforced by imp. of CQRS)
where
TResponse
:
Result
// Type of response (enforced by imp. of CQRS)
{
#
region
CTOR
DI
for
validators
private
readonly
IEnumerable
<
IValidator
<
TRequest
>>
_validators
;
public
ValidationPipelineBehavior
(
IEnumerable
<
IValidator
<
TRequest
>>
validators
)
{
_validators
=
validators
;
}
#
endregion
#
region
Create
validation
result
private
static
TResult
CreateValidationResult
<
TResult
>(
Error
[]
errors
)
where
TResult
:
Result
{
// Simple case, Result only
if
(
typeof
(
TResult
)
==
typeof
(
Result
))
{
return
(
ValidationResult
.
WithErrors
(
errors
)
as
TResult
)!;
// This won't fail ever
}
// Other complicated case, we have a Result<Type>
// Using reflection
object
validationResult
=
typeof
(
ValidationResult
<>)
.
GetGenericTypeDefinition
()
.
MakeGenericType
(
typeof
(
TResult
).
GenericTypeArguments
[
0
])
.
GetMethod
(
nameof
(
ValidationResult
.
WithErrors
))!
.
Invoke
(
null
,
[
errors
])!;
return
(
TResult
)
validationResult
;
}
#
endregion
#
region
Handle
method
(
pipeline
behavior
)
public
async
Task
<
TResponse
>
Handle
(
TRequest
request
,
RequestHandlerDelegate
<
TResponse
>
next
,
CancellationToken
cancellationToken
)
{
// Validate request
// If any errors, return validation request
// Otherwise,return next() [Pipline ~ Middleware]
// Case of no validators
if
(!
_validators
.
Any
())
{
return
await
next
();
}
// Generate errors array
Error
[]
errors
=
_validators
.
Select
(
validator
=>
validator
.
Validate
(
request
))
// Select the result of validate method
.
SelectMany
(
validationResult
=>
validationResult
.
Errors
)
.
Where
(
validationFailure
=>
validationFailure
is
not
null
)
.
Select
(
failure
=>
new
Error
(
// Project into Error object
failure
.
PropertyName
,
failure
.
ErrorMessage
))
.
Distinct
()
.
ToArray
();
if
(
errors
.
Length
!=
0
)
{
return
CreateValidationResult
<
TResponse
>(
errors
);
// Static method in this class
}
return
await
next
();
}
#
endregion
}
Clinics.Backend/Application/Employees/Commands/Create/CreateEmployeeCommandValidator.cs
0 → 100644
View file @
041d69cc
using
Domain.ValidationConstants.ErrorMessages
;
using
Domain.ValidationConstants.RegularExpressions
;
using
FluentValidation
;
namespace
Application.Employees.Commands.Create
;
public
class
CreateEmployeeCommandValidator
:
AbstractValidator
<
CreateEmployeeCommand
>
{
public
CreateEmployeeCommandValidator
()
{
#
region
First
name
RuleFor
(
c
=>
c
.
FirstName
)
.
NotEmpty
()
.
WithMessage
(
ValidationErrorMessages
.
Required
);
RuleFor
(
c
=>
c
.
FirstName
)
.
MaximumLength
(
50
)
.
WithMessage
(
ValidationErrorMessages
.
FixedLength
);
RuleFor
(
c
=>
c
.
FirstName
)
.
Matches
(
ValidationRegularExpressions
.
ArabicLettersOnly
)
.
WithMessage
(
ValidationErrorMessages
.
ArabicLettersOnly
);
#
endregion
#
region
Middle
name
RuleFor
(
c
=>
c
.
MiddleName
)
.
NotEmpty
()
.
WithMessage
(
ValidationErrorMessages
.
Required
);
RuleFor
(
c
=>
c
.
MiddleName
)
.
MaximumLength
(
50
)
.
WithMessage
(
ValidationErrorMessages
.
FixedLength
);
RuleFor
(
c
=>
c
.
MiddleName
)
.
Matches
(
ValidationRegularExpressions
.
ArabicLettersOnly
)
.
WithMessage
(
ValidationErrorMessages
.
ArabicLettersOnly
);
#
endregion
#
region
Last
name
RuleFor
(
c
=>
c
.
LastName
)
.
NotEmpty
()
.
WithMessage
(
ValidationErrorMessages
.
Required
);
RuleFor
(
c
=>
c
.
LastName
)
.
MaximumLength
(
50
)
.
WithMessage
(
ValidationErrorMessages
.
FixedLength
);
RuleFor
(
c
=>
c
.
LastName
)
.
Matches
(
ValidationRegularExpressions
.
ArabicLettersOnly
)
.
WithMessage
(
ValidationErrorMessages
.
ArabicLettersOnly
);
#
endregion
#
region
Serial
number
RuleFor
(
c
=>
c
.
SerialNumber
)
.
NotEmpty
()
.
WithMessage
(
ValidationErrorMessages
.
Required
);
RuleFor
(
c
=>
c
.
SerialNumber
)
.
Matches
(
ValidationRegularExpressions
.
NumbersOnly
)
.
WithMessage
(
ValidationErrorMessages
.
NumbersOnly
);
RuleFor
(
c
=>
c
.
SerialNumber
)
.
MaximumLength
(
20
)
.
WithMessage
(
ValidationErrorMessages
.
FixedLength
);
#
endregion
#
region
Center
status
RuleFor
(
c
=>
c
.
CenterStatus
)
.
NotEmpty
()
.
WithMessage
(
ValidationErrorMessages
.
Required
);
RuleFor
(
c
=>
c
.
CenterStatus
)
.
Matches
(
ValidationRegularExpressions
.
ArabicLettersOnly
)
.
WithMessage
(
ValidationErrorMessages
.
ArabicLettersOnly
);
RuleFor
(
c
=>
c
.
CenterStatus
)
.
MaximumLength
(
50
)
.
WithMessage
(
ValidationErrorMessages
.
FixedLength
);
#
endregion
// TODO: add validation rules for additional fields
}
}
Clinics.Backend/Domain/ValidationConstants/ErrorMessages/ValidationErrorMessages.cs
0 → 100644
View file @
041d69cc
namespace
Domain.ValidationConstants.ErrorMessages
;
public
static
class
ValidationErrorMessages
{
public
static
string
Required
=>
"هذا الحقل مطلوب"
;
public
static
string
ArabicOrEnglishLettersOnly
=>
"يجب أن يحوي هذا الحقل على أحرف عربية فقط أو أحرف انكليزية فقط ولا يحوي أي رموز أو أرقام"
;
public
static
string
ArabicLettersOnly
=>
"يجب أن يحوي هذا الحقل على أحرف عربية فقط ولا يحوي أي رموز أو أرقام"
;
public
static
string
FixedLength
=>
"هذا الحقل طويل للغاية"
;
public
static
string
NumbersOnly
=>
"يجب أن يحوي هذا الحقل على أرقام فقط"
;
}
Clinics.Backend/Domain/ValidationConstants/RegularExpressions/ValidationRegularExpressions.cs
0 → 100644
View file @
041d69cc
namespace
Domain.ValidationConstants.RegularExpressions
;
public
static
class
ValidationRegularExpressions
{
public
static
string
ArabicOrEnglishLettersOnly
=>
@"^[\u0600-\u06ff\s]+$|^[a-zA-Z\s]+$"
;
public
static
string
ArabicLettersOnly
=>
@"^[א-ء-ي]+$"
;
public
static
string
EmailAddress
=>
@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
;
public
static
string
NumbersOnly
=>
@"^\d+$"
;
}
Clinics.Backend/Presentation/Controllers/Base/ApiController.cs
0 → 100644
View file @
041d69cc
using
Domain.Shared.Validation
;
using
Domain.Shared
;
using
MediatR
;
using
Microsoft.AspNetCore.Http
;
using
Microsoft.AspNetCore.Mvc
;
namespace
Presentation.Controllers.Base
;
[ApiController]
public
abstract
class
ApiController
:
ControllerBase
{
#
region
CTOR
DI
for
MediatR
sendse
protected
readonly
ISender
_sender
;
protected
ApiController
(
ISender
sender
)
{
_sender
=
sender
;
}
#
endregion
protected
IActionResult
HandleFailure
(
Result
result
)
=>
result
switch
{
{
IsSuccess
:
true
}
=>
throw
new
InvalidOperationException
(),
IValidationResult
validationResult
=>
BadRequest
(
CreateProblemDetails
(
"Validation Error"
,
StatusCodes
.
Status400BadRequest
,
result
.
Error
,
validationResult
.
Errors
)),
_
=>
BadRequest
(
CreateProblemDetails
(
"Bad Request"
,
StatusCodes
.
Status400BadRequest
,
result
.
Error
))
};
private
static
ProblemDetails
CreateProblemDetails
(
string
title
,
int
status
,
Error
error
,
Error
[]?
errors
=
null
)
=>
new
()
{
Title
=
title
,
Type
=
error
.
Code
,
Detail
=
error
.
Message
,
Status
=
status
,
Extensions
=
{
{
nameof
(
errors
),
errors
}
}
};
}
\ No newline at end of file
Clinics.Backend/Presentation/Controllers/EmployeesController.cs
View file @
041d69cc
using
Application.Employees.Commands.Create
;
using
MediatR
;
using
Microsoft.AspNetCore.Mvc
;
using
Presentation.Controllers.Base
;
namespace
Presentation.Controllers
;
[Route("api/[controller]
")]
[ApiController]
public
class
EmployeesController
:
ControllerBase
[Route("api/Employees")]
public
class
EmployeesController
:
ApiController
{
#
region
DI
for
MeditR
sender
private
readonly
ISender
_sender
;
public
EmployeesController
(
ISender
sender
)
public
EmployeesController
(
ISender
sender
)
:
base
(
sender
)
{
_sender
=
sender
;
}
#
endregion
...
...
@@ -21,8 +20,8 @@ public class EmployeesController : ControllerBase
public
async
Task
<
IActionResult
>
Create
(
CreateEmployeeCommand
command
)
{
var
result
=
await
_sender
.
Send
(
command
);
if
(
result
.
IsSuccess
)
if
(
result
.
IsFailure
)
return
HandleFailure
(
result
);
return
Created
();
else
return
BadRequest
(
result
.
Error
.
Message
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment