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

Feature / Data Source Recipe Beta #188

Merged
merged 9 commits into from
Oct 30, 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
12 changes: 12 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-reportgenerator-globaltool": {
"version": "5.3.11",
"commands": [
"reportgenerator"
]
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name: Docs - Regular Checks
name: Regular Checks - Docs
on:
pull_request:
branches:
- main
- 'epic/**'
paths:
- 'docs/**'
- 'docs/**/*'
jobs:
generate:
name: Generate
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
name: Project - Regular Checks
name: Regular Checks - Project
on:
pull_request:
branches:
- main
- 'epic/**'
paths:
- 'src/**'
- 'test/**'
- 'src/**/*'
- 'test/**/*'
- '.editorconfig'
- 'Directory.Build.props'
- 'Directory.Packages.props'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name: Samples - Regular Checks
name: Regular Checks - Samples
on:
pull_request:
branches:
- main
- 'epic/**'
paths:
- "samples/**"
- "samples/**/*"
jobs:
prepare:
name: Prepare
Expand Down
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ build:
@ dotnet build
test:
@ dotnet test
coverage:
@ \
rm -rdf .coverage ; \
dotnet test -c Release --collect:"XPlat Code Coverage" --logger trx --results-directory .coverage --settings test/runsettings.xml ; \
dotnet reportgenerator -reports:.coverage/*/coverage.cobertura.xml -targetdir:.coverage/html ; \
open .coverage/html/index.html
run:
@ \
echo "(1) Recipe.Service (Development)" ; \
Expand All @@ -24,6 +30,3 @@ run:
cd ./docs ; \
make run ; \
fi

# dotnet test -c Release --collect:"XPlat Code Coverage" --logger trx --results-directory .coverage --settings test/runsettings.xml
# reportgenerator -reports:.coverage\0d84daea-0041-4f8d-a93c-51d3d348fa69\coverage.cobertura.xml;.coverage\d606db4f-8ea5-4e9f-a304-f37b22a1f34b\coverage.cobertura.xml -targetdir:.coverage/report
14 changes: 11 additions & 3 deletions docs/features/exception-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@ This feature allows adding custom _Exception Handlers_.
app.Features.AddExceptionHandling(...);
```

## Default
## Problem Details

Adds default exception handler as well as middleware to log exceptions.
Adds an exception handler implementation that returns errors in problem details
format. It also adds a middleware that logs exceptions.

```csharp
c => c.Default(typeUrlFormat: "https://my-service.com/errors/{0}")
c => c.ProblemDetails(typeUrlFormat: "https://my-service.com/errors/{0}")
```

> [!NOTE]
>
> More to find at [RFC 7807][rfc] and [Handle errors in ASP.NET Core][dotnet].

[rfc]: https://www.rfc-editor.org/rfc/rfc7807.html
[dotnet]: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-8.0&source=recommendations#problem-details
86 changes: 86 additions & 0 deletions docs/recipes/data-source.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Data Source

Data source recipe includes enough layers and feature implementations for a
backend application that is expected to provide data from configured data source
using report queries in `.sql` files.

To create an application from this recipe, use `DataSource()` extension of
`Bake` class directly in `Program.cs`.

```csharp
Bake.New
.DataSource(
business: c => c.DomainAssemblies([...])
)
.Run();
```

## Layers

| Name | Run | Test |
| -------------------- | ------------------ | ------------------ |
| Code Generation | :white_check_mark: | :white_check_mark: |
| Data Access | :white_check_mark: | :white_check_mark: |
| Domain | :white_check_mark: | :white_check_mark: |
| HTTP Server | :white_check_mark: | :no_entry: |
| Rest API | :white_check_mark: | :no_entry: |
| Runtime | :white_check_mark: | :white_check_mark: |
| Testing | :no_entry: | :white_check_mark: |

## Features

| Name | Run | Test |
| ------------------ | ---------------------------------- | ---------------------------------- |
| Business | :white_check_mark: (No Default) | :white_check_mark: |
| Caching | :white_check_mark: Scoped Memory | :white_check_mark: |
| Coding Style(s) | :white_check_mark: | :white_check_mark: |
| | Add/Remove Child | |
| | Command Pattern | |
| | Records are DTOs | |
| | Remaining Services are Singleton | |
| | Scoped by Suffix | |
| | Use Built-in Types | |
| | Use Nullable Types | |
| | With Method | |
| Core | :white_check_mark: Dotnet | :white_check_mark: Mock |
| Database | :white_check_mark: Sqlite | :white_check_mark: In Memory |
| Exception Handling | :white_check_mark: Problem Details | :white_check_mark: |
| Greeting | :white_check_mark: Swagger | :no_entry: |
| Lifetime(s) | :white_check_mark: | :white_check_mark: |
| | Singleton | |
| | Scoped | |
| | Transient | |
| Logging | :white_check_mark: Request | :no_entry: |
| Mocking Overrider | :no_entry: | :white_check_mark: First Interface |
| Reporting | :white_check_mark: NativeSql | :white_check_mark: Mock |

> [!NOTE]
>
> When _Test_ column have :white_check_mark: without a note, this means it
> inherits whatever _Run_ column denotes.

## Phase Execution Order

```mermaid
flowchart TD
CB(CreateBuilder)
BC(BuildConfiguration)
AD(AddDomainTypes)
BD(BuildDomainModel)
GC(GenerateCode)
C(Compile)
AS(AddServices)
B(Build)
PB(PostBuild)
R(Run)

CB -->|ConfigurationManager\nWebApplicationBuilder| BC
BC --> AD
AD -->|IDomainTypeCollection| BD
BD -->|DomainModel| GC
GC -->|IGeneratedAssemblyCollection| C
C -->|GeneratedAssemblyProvider| AS
AS -->|IServiceCollection| B
B -->|IServiceProvider\nWebApplication|PB
PB --> R
```
70 changes: 35 additions & 35 deletions docs/recipes/service.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,41 +29,41 @@ Bake.New

## Features

| Name | Run | Test |
| ------------------ | -------------------------------- | ---------------------------------- |
| Authentication(s) | :white_check_mark: | :no_entry: |
| | Fixed Bearer Token | |
| Authorization | :white_check_mark: Claim Based | :no_entry: |
| Business | :white_check_mark: (No Default) | :white_check_mark: |
| Caching | :white_check_mark: Scoped Memory | :white_check_mark: |
| Coding Style(s) | :white_check_mark: | :white_check_mark: |
| | Add/Remove Child | |
| | Command Pattern | |
| | Entity Extension via Composition | |
| | Entity Subclass via Composition | |
| | Object as JSON | |
| | Records are DTOs | |
| | Remaining Services are Singleton | |
| | Rich Entity | |
| | Scoped by Suffix | |
| | Single by Unique | |
| | `Uri` Return is Redirect | |
| | Use Built-in Types | |
| | Use Nullable Types | |
| | With Method | |
| Communication | :white_check_mark: HTTP | :white_check_mark: Mock |
| Core | :white_check_mark: Dotnet | :white_check_mark: Mock |
| Cors | :white_check_mark: Disabled | :no_entry: |
| Database | :white_check_mark: Sqlite | :white_check_mark: In Memory |
| Exception Handling | :white_check_mark: Default | :white_check_mark: |
| Greeting | :white_check_mark: Swagger | :no_entry: |
| Lifetime(s) | :white_check_mark: | :white_check_mark: |
| | Singleton | |
| | Scoped | |
| | Transient | |
| Logging | :white_check_mark: Request | :no_entry: |
| Mocking Overrider | :no_entry: | :white_check_mark: First Interface |
| ORM | :white_check_mark: Auto Map | :white_check_mark: |
| Name | Run | Test |
| ------------------ | ---------------------------------- | ---------------------------------- |
| Authentication(s) | :white_check_mark: | :no_entry: |
| | Fixed Bearer Token | |
| Authorization | :white_check_mark: Claim Based | :no_entry: |
| Business | :white_check_mark: (No Default) | :white_check_mark: |
| Caching | :white_check_mark: Scoped Memory | :white_check_mark: |
| Coding Style(s) | :white_check_mark: | :white_check_mark: |
| | Add/Remove Child | |
| | Command Pattern | |
| | Entity Extension via Composition | |
| | Entity Subclass via Composition | |
| | Object as JSON | |
| | Records are DTOs | |
| | Remaining Services are Singleton | |
| | Rich Entity | |
| | Scoped by Suffix | |
| | Single by Unique | |
| | `Uri` Return is Redirect | |
| | Use Built-in Types | |
| | Use Nullable Types | |
| | With Method | |
| Communication | :white_check_mark: HTTP | :white_check_mark: Mock |
| Core | :white_check_mark: Dotnet | :white_check_mark: Mock |
| Cors | :white_check_mark: Disabled | :no_entry: |
| Database | :white_check_mark: Sqlite | :white_check_mark: In Memory |
| Exception Handling | :white_check_mark: Problem Details | :white_check_mark: |
| Greeting | :white_check_mark: Swagger | :no_entry: |
| Lifetime(s) | :white_check_mark: | :white_check_mark: |
| | Singleton | |
| | Scoped | |
| | Transient | |
| Logging | :white_check_mark: Request | :no_entry: |
| Mocking Overrider | :no_entry: | :white_check_mark: First Interface |
| ORM | :white_check_mark: Auto Map | :white_check_mark: |

> [!NOTE]
>
Expand Down
61 changes: 60 additions & 1 deletion src/recipe/Baked.Recipe.Service.Application/BakeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Baked.Greeting;
using Baked.Logging;
using Baked.Orm;
using Baked.Reporting;

namespace Baked;

Expand Down Expand Up @@ -39,7 +40,7 @@ public static Application Service(this Bake bake,
core ??= c => c.Dotnet();
cors ??= c => c.Disabled();
database ??= c => c.Sqlite();
exceptionHandling ??= c => c.Default();
exceptionHandling ??= c => c.ProblemDetails();
greeting ??= c => c.Swagger();
logging ??= c => c.Request();
orm ??= c => c.AutoMap();
Expand Down Expand Up @@ -92,4 +93,62 @@ public static Application Service(this Bake bake,
configure(app);
});
}

public static Application DataSource(this Bake bake,
Func<BusinessConfigurator, IFeature<BusinessConfigurator>> business,
Func<CachingConfigurator, IFeature<CachingConfigurator>>? caching = default,
Func<CoreConfigurator, IFeature<CoreConfigurator>>? core = default,
Func<DatabaseConfigurator, IFeature<DatabaseConfigurator>>? database = default,
Func<ExceptionHandlingConfigurator, IFeature<ExceptionHandlingConfigurator>>? exceptionHandling = default,
Func<GreetingConfigurator, IFeature<GreetingConfigurator>>? greeting = default,
Func<LoggingConfigurator, IFeature<LoggingConfigurator>>? logging = default,
Func<ReportingConfigurator, IFeature<ReportingConfigurator>>? reporting = default,
Action<ApplicationDescriptor>? configure = default
)
{
caching ??= c => c.ScopedMemory();
core ??= c => c.Dotnet();
database ??= c => c.Sqlite();
exceptionHandling ??= c => c.ProblemDetails();
greeting ??= c => c.Swagger();
logging ??= c => c.Request();
reporting ??= c => c.NativeSql();
configure ??= _ => { };

return bake.Application(app =>
{
app.Layers.AddCodeGeneration();
app.Layers.AddDataAccess();
app.Layers.AddDomain();
app.Layers.AddHttpServer();
app.Layers.AddRestApi();
app.Layers.AddRuntime();

app.Features.AddBusiness(business);
app.Features.AddCaching(caching);
app.Features.AddCodingStyles([
c => c.AddRemoveChild(),
c => c.CommandPattern(),
c => c.RecordsAreDtos(),
c => c.RemainingServicesAreSingleton(),
c => c.ScopedBySuffix(),
c => c.UseBuiltInTypes(),
c => c.UseNullableTypes(),
c => c.WithMethod()
]);
app.Features.AddCore(core);
app.Features.AddDatabase(database);
app.Features.AddExceptionHandling(exceptionHandling);
app.Features.AddGreeting(greeting);
app.Features.AddLifetimes([
c => c.Scoped(),
c => c.Singleton(),
c => c.Transient()
]);
app.Features.AddLogging(logging);
app.Features.AddReporting(reporting);

configure(app);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public void Configure(LayerConfigurator configurator)
configurator.ConfigureServiceCollection(services =>
{
services.AddSingleton(TimeProvider.System);
services.AddSingleton<ITextTransformer, HumanizerTextTransformer>();

services.AddFileProvider(new EmbeddedFileProvider(_entryAssembly, _baseNamespace(_entryAssembly)));
services.AddFileProvider(new PhysicalFileProvider(Path.GetDirectoryName(_entryAssembly.Location) ??
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Humanizer;

namespace Baked.Core;

public class HumanizerTextTransformer : ITextTransformer
{
public string Camelize(string text) =>
text.Camelize();

public string Kebaberize(string text) =>
text.Kebaberize();

public string Pascalize(string text) =>
text.Pascalize();

public string Pluralize(string text) =>
text.Pluralize();

public string Singularize(string text) =>
text.Singularize();

public string Snakerize(string text) =>
text.Underscore();

public string Titleize(string text) =>
text.Titleize();
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public void Configure(LayerConfigurator configurator)
configurator.ConfigureServiceCollection(services =>
{
services.AddSingleton<TimeProvider, ResettableFakeTimeProvider>();
services.AddSingleton<ITextTransformer, HumanizerTextTransformer>();
services.AddSingleton<FakeSettings>();
});

Expand Down
3 changes: 3 additions & 0 deletions src/recipe/Baked.Recipe.Service.Application/DataSourceNfr.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Baked.Testing;

public abstract class DataSourceNfr : WebApplicationNfr;
Loading