Skip to content

Commit 179268c

Browse files
committed
Merge remote-tracking branch 'origin/dev' into next
2 parents 7fea229 + c971ea1 commit 179268c

File tree

10 files changed

+224
-13
lines changed

10 files changed

+224
-13
lines changed

.devcontainer/devcontainer.json

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
2+
// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet
3+
{
4+
"name": "C# (.NET)",
5+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6+
"image": "mcr.microsoft.com/devcontainers/dotnet:1-8.0-bookworm",
7+
"features": {
8+
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
9+
"ghcr.io/devcontainers/features/github-cli:1": {
10+
"version": "2"
11+
},
12+
"ghcr.io/devcontainers/features/powershell:1": {
13+
"version": "latest"
14+
},
15+
"ghcr.io/azure/azure-dev/azd:0": {
16+
"version": "latest"
17+
},
18+
"ghcr.io/devcontainers/features/common-utils:2": {},
19+
"ghcr.io/devcontainers/features/dotnet:2": {
20+
"version": "8.0",
21+
"dotnetRuntimeVersions": "8.0",
22+
"aspNetCoreRuntimeVersions": "8.0"
23+
},
24+
"ghcr.io/devcontainers/features/node:1.3.1": {
25+
"version": "20"
26+
}
27+
},
28+
"postCreateCommand": "dotnet restore && cd ./src && cd coalesce-vue && npm ci && cd ../coalesce-vue-vuetify2 && npm ci && cd ../coalesce-vue-vuetify3 && npm ci",
29+
30+
"customizations": {
31+
"vscode": {
32+
"extensions": [
33+
"ms-vscode.vscode-node-azure-pack",
34+
"GitHub.vscode-github-actions",
35+
"ms-dotnettools.vscode-dotnet-runtime",
36+
"ms-dotnettools.csdevkit",
37+
"ms-dotnetools.csharp",
38+
"streetsidesoftware.code-spell-checker",
39+
"dbaeumer.vscode-eslint",
40+
"vue.volar",
41+
"vue.vscode-typescript-vue-plugin",
42+
"esbenp.prettier-vscode",
43+
"antfu.goto-alias"
44+
]
45+
}
46+
}
47+
}

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
[Documentation](https://intellitect.github.io/Coalesce) · [Get Started](#Get-Started) · [Builds](#Builds)
44

5+
Check out [The Coalesce Podcast](https://www.youtube.com/playlist?list=PLRjft3wXvK_srWUHS4w_lVrIfB4uNqfSD) for some step-by-step tutorials about Coalesce features.
6+
57
Coalesce is a framework for rapid-development of ASP.NET Core + Vue.js web applications. It works from the Entity Framework Core data model that you design, automating the creation of the glue - DTOs, API Controllers, and TypeScript - that sit between your data and the UI of your application.
68

79
## Fundamentals

docs/.vitepress/linkcheck-skip-file.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ github.com/intellitect/coalesce/edit
44
https://www.npmjs.com/package
55
https://github.com/IntelliTect/Coalesce
66
https://intellitect.com
7+
https://www.intellitect.com

docs/cspell.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
"IntelliTect",
1919
"mkdir",
2020
"nameof",
21+
"overridable",
22+
"discoverability",
2123
"netcoreapp",
2224
"netstandard",
2325
"overridable",
@@ -37,7 +39,9 @@
3739
"Vitest",
3840
"Vuetify",
3941
"webp",
40-
"wwwroot"
42+
"wwwroot",
43+
"Swashbuckle",
44+
"swashbuckle"
4145
],
4246
"ignorePaths": [
4347
"node_modules"

docs/modeling/model-components/attributes/many-to-many.md

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ ViewModel.
1111
The named specified in the attribute will be used as the name of a collection of the objects on the other side of the relationship in the generated [TypeScript ViewModels](/stacks/vue/layers/viewmodels.md#model-data-properties).
1212

1313
## Example Usage
14-
14+
In this example, we have a Person entity and an Appointment entity that share a many-to-many relationship. The PersonAppointment entity serves as the required middle table.
1515
``` c#
1616
public class Person
1717
{
@@ -22,6 +22,26 @@ public class Person
2222
[ManyToMany("Appointments")]
2323
public ICollection<PersonAppointment> PersonAppointments { get; set; }
2424
}
25+
26+
public class Appointment
27+
{
28+
public int AppointmentId { get; set; }
29+
public DateTime AppointmentDate { get; set; }
30+
31+
[ManyToMany("People")]
32+
public ICollection<PersonAppointment> PersonAppointments { get; set; }
33+
}
34+
35+
public class PersonAppointment
36+
{
37+
public int PersonAppointmentId { get; set; }
38+
39+
public int PersonId { get; set; }
40+
public Person Person { get; set; }
41+
42+
public int AppointmentId { get; set; }
43+
public Appointment Appointment { get; set; }
44+
}
2545
```
2646

2747
## Properties
@@ -33,4 +53,32 @@ The name of the collection that will contain the set of objects on the other sid
3353

3454
<Prop def="public string FarNavigationProperty { get; set; }" />
3555

36-
The name of the navigation property on the middle entity that points at the far side of the many-to-many relationship. Use this to resolve ambiguities when the middle table of the many-to-many relationship has more than two reference navigation properties on it.
56+
The name of the navigation property on the middle entity that points at the far side of the many-to-many relationship. Use this to resolve ambiguities when the middle table of the many-to-many relationship has more than two reference navigation properties on it.
57+
58+
``` c#
59+
public class Person
60+
{
61+
...
62+
63+
[ManyToMany("Appointments", FarNavigationProperty = nameof(PersonAppointment.Appointment))]
64+
public ICollection<PersonAppointment> PersonAppointments { get; set; }
65+
}
66+
67+
public class Appointment
68+
{
69+
...
70+
71+
[ManyToMany("People", FarNavigationProperty = nameof(PersonAppointment.Person))]
72+
public ICollection<PersonAppointment> PersonAppointments { get; set; }
73+
}
74+
75+
public class PersonAppointment
76+
{
77+
...
78+
79+
// Adding a third reference navigation property in the middle table requires
80+
// the use of FarNavigationProperty in order to resolve ambiguity.
81+
public int WaiverId { get; set; }
82+
public Waiver Waiver { get; set; }
83+
}
84+
```

docs/modeling/model-types/services.md

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
In a Coalesce application, you are likely to end up with a need for some API endpoints that aren't closely tied with your regular data model. While you could stick [Static Methods](/modeling/model-components/methods.md#static-methods) on one of your entities, to do so is detrimental to the organization of your code.
55

6-
Instead, Coalesce allows you to generate API Controllers and a TypeScript client from a service. A service, in this case, is nothing more than a C# class or an interface with methods on it, annotated with `[Coalesce, Service]`. An implementation of this class or interface must be injectable from your application's service container, so a registration in Startup.cs is needed.
6+
Instead, Coalesce allows you to generate API Controllers and a TypeScript client from a service. A service, in this case, is nothing more than a C# class or an interface with methods on it, annotated with `[Coalesce, Service]`. An implementation of this class or interface must be injectable from your application's service container, so a registration in Program.cs is needed.
77

88
The instance methods of these services work just like other custom [Methods](/modeling/model-components/methods.md) in Coalesce, with one notable distinction: Instance methods don't operate on an instance of a model, but instead on a dependency injected instance of the service.
99

@@ -44,21 +44,44 @@ public class WeatherService : IWeatherService
4444
return response.Body.SerializeTo<WeatherData>();
4545
}
4646

47-
public void MethodThatIsNotExposedBecauseItIsNotOnTheExposedInterface() { }
47+
// This method is not exposed because it is not defined on the interface
48+
public void MethodThatIsNotExposed() { }
4849
}
4950
```
5051

5152
And a registration:
5253

5354
``` c#
54-
public class Startup
55+
// In Program.cs
56+
builder.Services.AddCoalesce<AppDbContext>();
57+
builder.Services.AddScoped<IWeatherService, WeatherService>();
58+
```
59+
60+
## Using Interfaces With Services
61+
Interfaces annotated with `[Coalesce, Service]` will automatically expose all methods on that interface. Your interfaces should precisely define the service you intend to expose through Coalesce. Any members you do not want to expose should not be included in the interface.
62+
63+
Although it is not required to use an interface (you can generate endpoints directly from the implementation), it is highly recommended. Interfaces improve testability and reduce the risk of inadvertently changing the signature of a published API.
64+
65+
If you choose to generate directly from the implementation, annotate the class itself with `[Coalesce, Service]` rather than the interface. Unlike interfaces, each method you want to expose on the class must be explicitly annotated with the `[Coalesce]` attribute.
66+
67+
``` c#
68+
[Coalesce, Service]
69+
public class WeatherService
5570
{
56-
public void ConfigureServices(IServiceCollection services)
71+
public WeatherService(AppDbContext db)
5772
{
58-
services.AddCoalesce<AppDbContext>();
59-
services.AddScoped<IWeatherService, WeatherService>();
73+
this.db = db;
6074
}
61-
}
62-
```
6375

64-
While it isn't required that an interface for your service exist (you can generate directly from the implementation), it is highly recommended that an interface be used. Interfaces increase testability and reduce risk of accidentally changing the signature of a published API, among other benefits.
76+
[Coalesce]
77+
public WeatherData GetWeather(string zipCode)
78+
{
79+
// Assuming some magic HttpGet method that works as follows...
80+
var response = HttpGet("http://www.example.com/api/weather/" + zipCode);
81+
return response.Body.SerializeTo<WeatherData>();
82+
}
83+
84+
// This method is not exposed because it lacks the [Coalesce] attribute
85+
private void MethodThatIsNotExposed() { }
86+
}
87+
```

docs/topics/audit-logging.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Add a reference to the Nuget package `IntelliTect.Coalesce.AuditLogging` to your
1414

1515
``` xml:no-line-numbers{3}
1616
<ItemGroup>
17-
<PackageReference Include="IntelliTect.Coalesce.Vue" Version="$(CoalesceVersion)" />
17+
<PackageReference Include="IntelliTect.Coalesce" Version="$(CoalesceVersion)" />
1818
<PackageReference Include="IntelliTect.Coalesce.AuditLogging" Version="$(CoalesceVersion)" />
1919
</ItemGroup>
2020
```
147 KB
Loading
106 KB
Loading

docs/topics/coalesce-swashbuckle.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# OpenAPI/Swagger
2+
3+
When using Coalesce to generate API endpoints, the default OpenAPI generation _(sometimes referred to as its pre-2015 name "Swagger")_ can sometimes result in verbose and confusing API definitions, especially when dealing with DataSources and Behaviors. To address these issues, the `IntelliTect.Coalesce.Swashbuckle` package offers enhancements for OpenAPI definitions, making your Coalesce-generated APIs clearer and more manageable.
4+
5+
## Setup
6+
7+
In this setup process, we're going to add an additional Coalesce NuGet package, configure OpenAPI in your ASP.NET Core application, and specify a Coalesce-specific config property to improve the OpenAPI documentation for Coalesce-generated APIs.
8+
9+
### 1. Add the NuGet Package
10+
11+
Add a reference to the `IntelliTect.Coalesce.Swashbuckle` NuGet package to your web project:
12+
13+
```xml:no-line-numbers{3}
14+
<ItemGroup>
15+
<PackageReference Include="IntelliTect.Coalesce.Vue" Version="$(CoalesceVersion)" />
16+
<PackageReference Include="IntelliTect.Coalesce.Swashbuckle" Version="$(CoalesceVersion)" />
17+
</ItemGroup>
18+
```
19+
20+
### Configure OpenAPI in Program.cs
21+
22+
Update your Program.cs file to configure OpenAPI and include Coalesce-specific enhancements. This involves [setting up OpenAPI as usual](https://learn.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-8.0&tabs=visual-studio) and then applying the Coalesce configuration (Note: You do not need to install the `Swashbuckle.AspNetCore` package if you are using the Coalesce one).
23+
24+
```c#:no-line-numbers
25+
builder.Services.AddSwaggerGen(config =>
26+
{
27+
config.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
28+
config.AddCoalesce(); // Add coalesce specific configuration
29+
});
30+
```
31+
32+
```c#
33+
// Configure the HTTP request pipeline.
34+
if (app.Environment.IsDevelopment())
35+
{
36+
app.UseSwagger();
37+
app.UseSwaggerUI();
38+
}
39+
```
40+
41+
## Improvements
42+
43+
### Default OpenAPI Generation
44+
45+
By default, OpenAPI in ASP.NET Core offers a simple way to document APIs. It generates API documentation based on the structure of your controller actions and data models. While this default setup is functional for many scenarios, it may fall short in representing more complex cases, especially when dealing with Coalesce-generated endpoints that include DataSources and Behaviors. These scenarios can lead to verbose and sometimes confusing OpenAPI documentation.
46+
47+
### Coalesce Enhancements
48+
49+
The `IntelliTect.Coalesce.Swashbuckle` package addresses the limitations of the default OpenAPI generation by providing custom OpenAPI filters. These filters enhance the readability and usability of your OpenAPI documentation for Coalesce-generated APIs.
50+
51+
The primary effect is an adjustment of parameter definitions to account for Coalesce's custom model binders that create instances of Data Sources and Behaviors on each request. These parameters will be updated in the OpenAPI document to account for data source parameters, filter parameters, and other model-specific customizations.
52+
53+
## Visual Comparison
54+
55+
To illustrate the impact of the `IntelliTect.Coalesce.Swashbuckle` package, let's examine the Patient model and its representation in OpenAPI.
56+
57+
```c#
58+
public class Patient
59+
{
60+
public int PatientId { get; init; }
61+
public DateTime NextAppointment { get; set; }
62+
// Additional properties
63+
64+
[DefaultDataSource]
65+
public class PatientDataSource(CrudContext<AppDbContext> context) : StandardDataSource<Patient, AppDbContext>(context)
66+
{
67+
// ...
68+
}
69+
70+
public class PatientsWithUpcomingAppointmentsDataSource(CrudContext<AppDbContext> context) : StandardDataSource<Patient, AppDbContext>(context)
71+
{
72+
[Coalesce]
73+
public int MonthsOut { get; set; }
74+
75+
// ...
76+
}
77+
}
78+
```
79+
80+
#### Without `IntelliTect.Coalesce.Swashbuckle`
81+
In the default OpenAPI configuration, DataSource and Behavior parameters are represented as generic objects. DataSource names are also shown as plain strings, hindering the discoverability of available data sources.
82+
![](./coalesce-swashbuckle-without.jpg)
83+
84+
#### With `IntelliTect.Coalesce.Swashbuckle`
85+
With the `IntelliTect.Coalesce.Swashbuckle` package, OpenAPI can interpret the DataSource as a dropdown menu and provides individual fields for each DataSource property. Additionally, it eliminates unnecessary behavior parameters.
86+
![](./coalesce-swashbuckle-with.jpg)

0 commit comments

Comments
 (0)