Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: refactor analyzer configs and add formatting to ci #304

Merged
merged 2 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,087 changes: 699 additions & 388 deletions .editorconfig

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions .github/actions/build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ runs:
# restore root solution
run: dotnet restore

# npm install, runs `prepare` script automatically in the initialize step
- name: Install NPM Dependencies
shell: bash
if: success()
run: npm install

- name: Format Service
shell: bash
if: ${{ success()}}
run: |
npm run ci-format

- name: Build Service
shell: bash
if: ${{ success()}}
Expand Down
4 changes: 3 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
<PropertyGroup>
<RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<AnalysisMode>All</AnalysisMode>
<CodeAnalysisTreatWarningsAsErrors>false</CodeAnalysisTreatWarningsAsErrors>
<AnalysisLevel>latest-Recommended</AnalysisLevel>
<AnalysisMode>Recommended</AnalysisMode>
</PropertyGroup>

</Project>
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
- [Structure of Project](#structure-of-project)
- [Development Setup](#development-setup)
- [Dotnet Tools Packages](#dotnet-tools-packages)
- [Husky](#husky)
- [Upgrade Nuget Packages](#upgrade-nuget-packages)
- [How to Run](#how-to-run)
- [Config Certificate](#config-certificate)
- [Docker Compose](#docker-compose)
Expand All @@ -47,7 +49,7 @@
- :sparkle: Using `Postgres` for `write side` of some microservices.
- :sparkle: Using `MongoDB` for `read side` of some microservices.
- :sparkle: Using `Event Store` for `write side` of Booking-Microservice to store all `historical state` of aggregate.
- :sparkle: Using `Inbox Pattern` for ensuring message idempotency for receiver and `Exactly once Delivery`.
- :sparkle: Using `Inbox Pattern` for ensuring message idempotency for receiver and `Exactly once Delivery`.
- :sparkle: Using `Outbox Pattern` for ensuring no message is lost and there is at `At Least One Delivery`.
- :sparkle: Using `Unit Testing` for testing small units and mocking our dependencies with `Nsubstitute`.
- :sparkle: Using `End-To-End Testing` and `Integration Testing` for testing `features` with all dependencies using `testcontainers`.
Expand Down Expand Up @@ -159,7 +161,7 @@ Using the CQRS pattern, we cut each business functionality into vertical slices,
## Development Setup

### Dotnet Tools Packages
For installing our requirement package with .NET cli tools, we need to install `dotnet tool manifest`.
For installing our requirement packages with .NET cli tools, we need to install `dotnet tool manifest`.
```bash
dotnet new tool-manifest
```
Expand All @@ -168,6 +170,24 @@ And after that we can restore our dotnet tools packages with .NET cli tools from
dotnet tool restore
```

### Husky
Here we use `husky` to handel some pre commit rules and we used `conventional commits` rules and `formatting` as pre commit rules, here in [package.json](./package.json). of course, we can add more rules for pre commit in future. (find more about husky in the [documentation](https://typicode.github.io/husky/get-started.html))
We need to install `husky` package for `manage` `pre commits hooks` and also I add two packages `@commitlint/cli` and `@commitlint/config-conventional` for handling conventional commits rules in [package.json](./package.json).
Run the command bellow in the root of project to install all npm dependencies related to husky:

```bash
npm install
```

> Note: In the root of project we have `.husky` folder and it has `commit-msg` file for handling conventional commits rules with provide user friendly message and `pre-commit` file that we can run our `scripts` as a `pre-commit` hooks. that here we call `format` script from [package.json](./package.json) for formatting purpose.

### Upgrade Nuget Packages
For upgrading our nuget packages to last version, we use the great package [dotnet-outdated](https://github.com/dotnet-outdated/dotnet-outdated).
Run the command below in the root of project to upgrade all of packages to last version:
```bash
dotnet outdated -u
```

## How to Run

> ### Config Certificate
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"scripts": {
"prepare": "husky && dotnet tool restore",
"format": "dotnet format booking-microservices-sample.sln --severity error --verbosity detailed",
"ci-format": "dotnet format booking-microservices-sample.sln --verify-no-changes --severity error --verbosity detailed",
"upgrade-packages": "dotnet outdated --upgrade"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions src/BuildingBlocks/BuildingBlocks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<PackageReference Include="Grpc.Core.Testing" Version="2.46.6" />
<PackageReference Include="EasyCaching.Core" Version="1.9.2" />
<PackageReference Include="EasyCaching.InMemory" Version="1.9.2" />
<PackageReference Include="EasyNetQ.Management.Client" Version="2.0.0" />
<PackageReference Include="EasyNetQ.Management.Client" Version="3.0.0" />
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
<PackageReference Include="Figgle" Version="0.5.1" />
<PackageReference Include="FluentValidation" Version="11.10.0" />
Expand Down Expand Up @@ -51,7 +51,7 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="OpenTelemetry.Contrib.Instrumentation.MassTransit" Version="1.0.0-beta2" />
<PackageReference Include="Scrutor" Version="4.2.2" />
<PackageReference Include="Sentry.Serilog" Version="4.9.0" />
<PackageReference Include="Sentry.Serilog" Version="4.10.2" />
<PackageReference Include="Serilog" Version="4.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Enrichers.Span" Version="3.1.0" />
Expand Down
10 changes: 5 additions & 5 deletions src/BuildingBlocks/Core/EventDispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
}


public async Task SendAsync<T>(IReadOnlyList<T> events, Type type = null,

Check warning on line 34 in src/BuildingBlocks/Core/EventDispatcher.cs

View workflow job for this annotation

GitHub Actions / ci

Cannot convert null literal to non-nullable reference type.
CancellationToken cancellationToken = default)
where T : IEvent
{
Expand All @@ -54,13 +54,13 @@
switch (events)
{
case IReadOnlyList<IDomainEvent> domainEvents:
{
var integrationEvents = await MapDomainEventToIntegrationEventAsync(domainEvents)
{
var integrationEvents = await MapDomainEventToIntegrationEventAsync(domainEvents)
.ConfigureAwait(false);

await PublishIntegrationEvent(integrationEvents);
break;
}
await PublishIntegrationEvent(integrationEvents);
break;
}

case IReadOnlyList<IIntegrationEvent> integrationEvents:
await PublishIntegrationEvent(integrationEvents);
Expand All @@ -80,7 +80,7 @@
}
}

public async Task SendAsync<T>(T @event, Type type = null,

Check warning on line 83 in src/BuildingBlocks/Core/EventDispatcher.cs

View workflow job for this annotation

GitHub Actions / ci

Cannot convert null literal to non-nullable reference type.
CancellationToken cancellationToken = default)
where T : IEvent =>
await SendAsync(new[] { @event }, type, cancellationToken);
Expand Down
51 changes: 30 additions & 21 deletions src/BuildingBlocks/EFCore/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,40 @@ namespace BuildingBlocks.EFCore;

public static class Extensions
{
public static IServiceCollection AddCustomDbContext<TContext>(
this IServiceCollection services)
where TContext : DbContext, IDbContext
public static IServiceCollection AddCustomDbContext<TContext>(this IServiceCollection services)
where TContext : DbContext, IDbContext
{
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);

services.AddValidateOptions<PostgresOptions>();

services.AddDbContext<TContext>((sp, options) =>
{
var postgresOptions = sp.GetRequiredService<PostgresOptions>();
services.AddDbContext<TContext>(
(sp, options) =>
{
var postgresOptions = sp.GetRequiredService<PostgresOptions>();

Guard.Against.Null(options, nameof(postgresOptions));
Guard.Against.Null(options, nameof(postgresOptions));

options.UseNpgsql(postgresOptions?.ConnectionString,
dbOptions =>
{
dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name);
})
// https://github.com/efcore/EFCore.NamingConventions
.UseSnakeCaseNamingConvention();
});
options.UseNpgsql(
postgresOptions?.ConnectionString,
dbOptions =>
{
dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name);
})
// https://github.com/efcore/EFCore.NamingConventions
.UseSnakeCaseNamingConvention();
});

services.AddScoped<IDbContext>(provider => provider.GetService<TContext>());

return services;
}

public static IApplicationBuilder UseMigration<TContext>(this IApplicationBuilder app, IWebHostEnvironment env)
where TContext : DbContext, IDbContext
public static IApplicationBuilder UseMigration<TContext>(
this IApplicationBuilder app,
IWebHostEnvironment env
)
where TContext : DbContext, IDbContext
{
MigrateDatabaseAsync<TContext>(app.ApplicationServices).GetAwaiter().GetResult();

Expand All @@ -62,22 +66,24 @@ public static IApplicationBuilder UseMigration<TContext>(this IApplicationBuilde
public static void FilterSoftDeletedProperties(this ModelBuilder modelBuilder)
{
Expression<Func<IAggregate, bool>> filterExpr = e => !e.IsDeleted;

foreach (var mutableEntityType in modelBuilder.Model.GetEntityTypes()
.Where(m => m.ClrType.IsAssignableTo(typeof(IEntity))))
{
// modify expression to handle correct child type
var parameter = Expression.Parameter(mutableEntityType.ClrType);

var body = ReplacingExpressionVisitor
.Replace(filterExpr.Parameters.First(), parameter, filterExpr.Body);

var lambdaExpression = Expression.Lambda(body, parameter);

// set filter
mutableEntityType.SetQueryFilter(lambdaExpression);
}
}


//ref: https://andrewlock.net/customising-asp-net-core-identity-ef-core-naming-conventions-for-postgresql/
// ref: https://andrewlock.net/customising-asp-net-core-identity-ef-core-naming-conventions-for-postgresql/
public static void ToSnakeCaseTables(this ModelBuilder modelBuilder)
{
foreach (var entity in modelBuilder.Model.GetEntityTypes())
Expand All @@ -86,7 +92,9 @@ public static void ToSnakeCaseTables(this ModelBuilder modelBuilder)
entity.SetTableName(entity.GetTableName()?.Underscore());

var tableObjectIdentifier =
StoreObjectIdentifier.Table(entity.GetTableName()?.Underscore()!, entity.GetSchema());
StoreObjectIdentifier.Table(
entity.GetTableName()?.Underscore()!,
entity.GetSchema());

// Replace column names
foreach (var property in entity.GetProperties())
Expand All @@ -107,7 +115,7 @@ public static void ToSnakeCaseTables(this ModelBuilder modelBuilder)
}

private static async Task MigrateDatabaseAsync<TContext>(IServiceProvider serviceProvider)
where TContext : DbContext, IDbContext
where TContext : DbContext, IDbContext
{
using var scope = serviceProvider.CreateScope();

Expand All @@ -119,6 +127,7 @@ private static async Task SeedDataAsync(IServiceProvider serviceProvider)
{
using var scope = serviceProvider.CreateScope();
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();

foreach (var seeder in seeders)
{
await seeder.SeedAllAsync();
Expand Down
4 changes: 2 additions & 2 deletions src/BuildingBlocks/EventStoreDB/Events/EventTypeMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static void AddCustomMap(Type eventType, string mappedEventTypeName)

public static string ToName(Type eventType) => Instance.typeNameMap.GetOrAdd(eventType, _ =>
{
var eventTypeName = eventType.FullName!.Replace(".", "_");
var eventTypeName = eventType.FullName!.Replace(".", "_", StringComparison.CurrentCulture);

Instance.typeMap.AddOrUpdate(eventTypeName, eventType, (_, _) => eventType);

Expand All @@ -31,7 +31,7 @@ public static string ToName(Type eventType) => Instance.typeNameMap.GetOrAdd(eve

public static Type? ToType(string eventTypeName) => Instance.typeMap.GetOrAdd(eventTypeName, _ =>
{
var type = TypeProvider.GetFirstMatchingTypeFromCurrentDomainAssembly(eventTypeName.Replace("_", "."));
var type = TypeProvider.GetFirstMatchingTypeFromCurrentDomainAssembly(eventTypeName.Replace("_", ".", StringComparison.CurrentCulture));

if (type == null)
return null;
Expand Down
2 changes: 1 addition & 1 deletion src/BuildingBlocks/Jwt/AuthHeaderHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage reques
{
var token = (_httpContext?.HttpContext?.Request.Headers["Authorization"])?.ToString();

request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", ""));
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", "", StringComparison.CurrentCulture));

return base.SendAsync(request, cancellationToken);
}
Expand Down
3 changes: 2 additions & 1 deletion src/BuildingBlocks/Logging/Extensions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Globalization;
using System.Text;
using BuildingBlocks.Web;
using Microsoft.AspNetCore.Builder;
Expand Down Expand Up @@ -44,7 +45,7 @@ public static WebApplicationBuilder AddCustomSerilog(this WebApplicationBuilder
new ElasticsearchSinkOptions(new Uri(logOptions.Elastic.ElasticServiceUrl))
{
AutoRegisterTemplate = true,
IndexFormat = $"{appOptions.Name}-{environment?.ToLower()}"
IndexFormat = $"{appOptions.Name}-{environment?.ToLower(CultureInfo.CurrentCulture)}"
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/BuildingBlocks/Mongo/ImmutablePocoConvention.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ private static List<PropertyInfo> GetMatchingProperties(

private static bool ParameterMatchProperty(ParameterInfo parameter, PropertyInfo property)
{
return string.Equals(property.Name, parameter.Name, System.StringComparison.InvariantCultureIgnoreCase)
return string.Equals(property.Name, parameter.Name, StringComparison.OrdinalIgnoreCase)
&& parameter.ParameterType == property.PropertyType;
}

Expand Down
3 changes: 2 additions & 1 deletion src/BuildingBlocks/Mongo/MongoDbContext.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Globalization;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Conventions;
Expand Down Expand Up @@ -42,7 +43,7 @@ private static void RegisterConventions()

public IMongoCollection<T> GetCollection<T>(string? name = null)
{
return Database.GetCollection<T>(name ?? typeof(T).Name.ToLower());
return Database.GetCollection<T>(name ?? typeof(T).Name.ToLower(CultureInfo.CurrentCulture));
}

public void Dispose()
Expand Down
Loading
Loading