Skip to content

Commit 7055811

Browse files
authored
Update to .NET 9 and replace Swashbuckle/Swagger with ASP.NET Core OpenAPI generation (#27)
* Update .gitignore * Update project to .NET 9 * Replaced Swagger/Swashbuckle with MS OpenAPI * Updated TTK example plugin project * Update .NET version in CI workflow
1 parent 29998fb commit 7055811

30 files changed

+781
-860
lines changed

.github/workflows/dotnet.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- name: Setup .NET
1919
uses: actions/setup-dotnet@v4
2020
with:
21-
dotnet-version: 8.x
21+
dotnet-version: 9.x
2222
- name: Restore dependencies
2323
run: dotnet restore
2424
- name: Build

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,11 +397,13 @@ FodyWeavers.xsd
397397
# JetBrains Rider
398398
*.sln.iml
399399

400-
appsettings.Development.json
400+
appsettings.Development*.json
401401
openapi/**/*.yml
402402
openapi/**/*.json
403403
plugin/**/*.json
404404
plugin/**/*.yml
405405
*.zip
406406
.kiota/
407407
BudgetTracker-DevTunnel.http
408+
*.cer
409+
start-tunnel.bat

.vscode/launch.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"request": "launch",
1111
"preLaunchTask": "build",
1212
// If you have changed target frameworks, make sure to update the program path.
13-
"program": "${workspaceFolder}/api/bin/Debug/net8.0/BudgetTracker.dll",
13+
"program": "${workspaceFolder}/api/bin/Debug/net9.0/BudgetTracker.dll",
1414
"args": [],
1515
"cwd": "${workspaceFolder}/api",
1616
"stopAtEntry": false,
@@ -20,7 +20,7 @@
2020
"serverReadyAction": {
2121
"action": "openExternally",
2222
"pattern": "\\bNow listening on:\\s+(https?://\\S+)",
23-
"uriFormat": "%s/swagger"
23+
"uriFormat": "%s/openapi/BudgetTracker.json"
2424
},
2525
"env": {
2626
"ASPNETCORE_ENVIRONMENT": "Development"

.vscode/settings.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
{
22
"cSpell.words": [
33
"authcomplete",
4+
"cellspacing",
45
"devtunnel",
56
"MSRC",
67
"openapi",
78
"seealso",
89
"Swashbuckle",
910
"winget"
11+
],
12+
"cSpell.ignorePaths": [
13+
"package-lock.json",
14+
"node_modules",
15+
"vscode-extension",
16+
".git/{info,lfs,logs,refs,objects}/**",
17+
".git/{index,*refs,*HEAD}",
18+
".vscode",
19+
".vscode-insiders",
20+
".gitignore",
21+
"*.csproj",
22+
"examples/**"
1023
]
1124
}

.vscode/tasks.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@
55
"label": "build",
66
"command": "dotnet",
77
"type": "process",
8+
"options": {
9+
"env": {
10+
"ASPNETCORE_ENVIRONMENT": "Development"
11+
}
12+
},
813
"args": [
914
"build",
15+
"--no-incremental",
1016
"${workspaceFolder}/api/BudgetTracker.csproj",
1117
"/property:GenerateFullPaths=true",
1218
"/consoleloggerparameters:NoSummary;ForceNoAlign"
@@ -36,6 +42,20 @@
3642
"${workspaceFolder}/api/BudgetTracker.csproj"
3743
],
3844
"problemMatcher": "$msCompile"
45+
},
46+
{
47+
"label": "gen-samples",
48+
"detail": "Generate sample OpenAPI with placeholders",
49+
"command": "dotnet",
50+
"type": "process",
51+
"args": [
52+
"build",
53+
"--no-incremental",
54+
"${workspaceFolder}/api/BudgetTracker.csproj",
55+
"/property:GenerateFullPaths=true",
56+
"/consoleloggerparameters:NoSummary;ForceNoAlign"
57+
],
58+
"problemMatcher": "$msCompile"
3959
}
4060
]
4161
}

api/BudgetTracker.csproj

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,44 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>net9.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
8+
<OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
9+
<OpenApiGenerateDocumentsOnBuild>true</OpenApiGenerateDocumentsOnBuild>
810
</PropertyGroup>
911

12+
<Choose>
13+
<When Condition="$(ASPNETCORE_ENVIRONMENT) == Development">
14+
<PropertyGroup>
15+
<OpenApiDocumentsDirectory>../openapi</OpenApiDocumentsDirectory>
16+
</PropertyGroup>
17+
</When>
18+
<When Condition="$(ASPNETCORE_ENVIRONMENT) == ''">
19+
<PropertyGroup>
20+
<OpenApiDocumentsDirectory>../examples/openapi</OpenApiDocumentsDirectory>
21+
</PropertyGroup>
22+
</When>
23+
</Choose>
24+
1025
<ItemGroup>
1126
<AdditionalFiles Include="$(MSBuildThisFileDirectory)stylecop.json" />
1227
</ItemGroup>
1328

1429
<ItemGroup>
1530
<PackageReference Include="AdaptiveCards" Version="3.1.0" />
16-
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.11" />
31+
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
32+
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="9.0.0">
33+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
34+
<PrivateAssets>all</PrivateAssets>
35+
</PackageReference>
1736
<PackageReference Include="Microsoft.Identity.Web" Version="3.5.0" />
1837
<PackageReference Include="Microsoft.Identity.Web.GraphServiceClient" Version="3.5.0" />
1938
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
2039
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2140
<PrivateAssets>all</PrivateAssets>
2241
</PackageReference>
23-
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" />
2442
</ItemGroup>
2543

2644
</Project>

api/Endpoints/BudgetsEndpoint.cs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
using System.ComponentModel;
45
using BudgetTracker.Models;
56
using BudgetTracker.Services;
67
using Microsoft.AspNetCore.Authorization;
78
using Microsoft.AspNetCore.Http.HttpResults;
89
using Microsoft.AspNetCore.Mvc;
910
using Microsoft.Identity.Web.Resource;
10-
using Microsoft.OpenApi.Any;
1111

1212
namespace BudgetTracker.Endpoints;
1313

@@ -29,12 +29,6 @@ public static void Map(WebApplication app)
2929
.WithName("GetBudgets")
3030
.WithSummary("Get budgets based on budget name")
3131
.WithDescription("Returns details including name and available funds of budgets, optionally filtered by budget name")
32-
.WithOpenApi(operation =>
33-
{
34-
operation.Parameters[0].Description = "The name of the budget to retrieve";
35-
operation.Parameters[0].Required = false;
36-
return operation;
37-
})
3832
.RequireAuthorization();
3933

4034
app.MapPost(Endpoint, CreateBudget)
@@ -46,21 +40,19 @@ public static void Map(WebApplication app)
4640
app.MapPost($"{Endpoint}/charge", ChargeBudget)
4741
.WithName("ChargeBudget")
4842
.WithSummary("Charge an amount to a budget")
49-
.WithDescription("Charge an amount to a budget with a specified name. This removes the specified amount from the budget's available funds")
50-
.WithOpenApi()
43+
.WithDescription("Charge an amount to a budget with a specified name, removing the amount from available funds")
5144
.RequireAuthorization();
5245

5346
app.MapPost($"{Endpoint}/extend", ExtendBudget)
5447
.WithName("ExtendBudget")
5548
.WithSummary("Add an amount to a budget")
56-
.WithDescription("Add an amount to a budget with a specified name. This adds the specified amount to the budget's available funds")
57-
.WithOpenApi()
49+
.WithDescription("Add an amount to a budget with a specified name, adding the amount to available funds")
5850
.RequireAuthorization();
5951
}
6052

6153
private static Results<Ok<List<Budget>>, BadRequest<ApiResponse>> GetBudgets(
6254
HttpContext context,
63-
[FromQuery] string? budgetName,
55+
[FromQuery][Description("The name of the budget to retrieve")] string? budgetName,
6456
[FromServices] BudgetService budgetService,
6557
[FromServices] ILogger<Program> logger)
6658
{

api/Endpoints/TransactionsEndpoint.cs

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
using System.Text.Json;
4+
using System.ComponentModel;
55
using BudgetTracker.Models;
66
using BudgetTracker.Services;
77
using Microsoft.AspNetCore.Http.HttpResults;
88
using Microsoft.AspNetCore.Mvc;
99
using Microsoft.Graph;
1010
using Microsoft.Identity.Web.Resource;
11-
using Microsoft.OpenApi.Any;
1211

1312
namespace BudgetTracker.Endpoints;
1413

@@ -28,37 +27,20 @@ public static void Map(WebApplication app)
2827
app.MapGet(Endpoint, GetTransactions)
2928
.WithName("GetTransactions")
3029
.WithSummary("Get transactions based on budget name or category")
31-
.WithDescription("Returns details of transactions identified from filters like budget name or category. Multiple filters can be used in combination to refine the list of transactions returned")
32-
.WithOpenApi(operation =>
33-
{
34-
operation.Parameters[0].Description = "The name of the budget to filter results on";
35-
operation.Parameters[0].Required = false;
36-
operation.Parameters[1].Description = "The name of the category to filter results on";
37-
operation.Parameters[1].Required = false;
38-
return operation;
39-
})
30+
.WithDescription("Returns details of transactions identified from filters like budget name or category")
4031
.RequireAuthorization();
4132

4233
app.MapPost($"{Endpoint}/send", SendTransactionReport)
4334
.WithName("SendTransactionReport")
4435
.WithSummary("Send a transaction report to my email")
45-
.WithDescription("Sends a transaction report via email, optionally filtered by budget.")
46-
.WithOpenApi(operation =>
47-
{
48-
// Set this to false so Copilot will show an "always allow" option
49-
operation.Extensions.Add("x-openai-isConsequential", new OpenApiBoolean(false));
50-
51-
operation.Parameters[0].Description = "The name of the budget to filter report on";
52-
operation.Parameters[0].Required = false;
53-
return operation;
54-
})
36+
.WithDescription("Sends a transaction report via email, optionally filtered by budget")
5537
.RequireAuthorization();
5638
}
5739

5840
private static Results<Ok<TransactionListResponse>, BadRequest<ApiResponse>> GetTransactions(
5941
HttpContext context,
60-
[FromQuery] string? budgetName,
61-
[FromQuery] string? category,
42+
[FromQuery][Description("The name of the budget to filter results on")] string? budgetName,
43+
[FromQuery][Description("The name of the category to filter results on")] string? category,
6244
[FromServices] BudgetService budgetService,
6345
[FromServices] ILogger<Program> logger)
6446
{
@@ -80,7 +62,7 @@ private static Results<Ok<TransactionListResponse>, BadRequest<ApiResponse>> Get
8062

8163
private static async Task<Results<Accepted<ApiResponse>, BadRequest<ApiResponse>>> SendTransactionReport(
8264
HttpContext context,
83-
[FromQuery] string? budgetName,
65+
[FromQuery][Description("The name of the budget to filter report on")] string? budgetName,
8466
[FromServices] BudgetService budgetService,
8567
[FromServices] GraphServiceClient graphClient,
8668
[FromServices] ILogger<Program> logger)

api/Extensions/OpenApiExtensions.cs

Lines changed: 0 additions & 30 deletions
This file was deleted.

api/Models/ApiResponse.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
using System.ComponentModel;
5+
46
namespace BudgetTracker.Models;
57

68
/// <summary>
@@ -12,6 +14,6 @@ public class ApiResponse(string message)
1214
/// Gets the response message.
1315
/// </summary>
1416
/// <example>1000 has been deducted from Contoso Copilot plugin project. The budget has 49000 remaining in available funds.</example>
15-
/// <example>1000 has been added to Contoso Copilot plugin project. The budget has 51000 remaining in available funds.</example>
17+
[Description("The response message")]
1618
public string Message => message;
1719
}

api/Models/Budget.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
using System.ComponentModel;
45
using System.ComponentModel.DataAnnotations;
56

67
namespace BudgetTracker.Models;
@@ -15,12 +16,14 @@ public class Budget
1516
/// </summary>
1617
/// <example>Contoso Copilot plugin project</example>
1718
[Required]
19+
[Description("The name of the budget")]
1820
public string? Name { get; set; }
1921

2022
/// <summary>
2123
/// Gets or sets the available funds in the budget.
2224
/// </summary>
2325
/// <example>50000</example>
2426
[Required]
27+
[Description("The available funds in the budget")]
2528
public decimal AvailableFunds { get; set; }
2629
}

api/Models/Transaction.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
using System.ComponentModel;
45
using System.ComponentModel.DataAnnotations;
56

67
namespace BudgetTracker.Models;
@@ -15,31 +16,36 @@ public class Transaction
1516
/// </summary>
1617
/// <example>Contoso Copilot plugin project</example>
1718
[Required]
19+
[Description("The name of the budget to adjust")]
1820
public string? BudgetName { get; set; }
1921

2022
/// <summary>
2123
/// Gets or sets the amount of the adjustment.
2224
/// </summary>
2325
/// <example>-5000</example>
2426
[Required]
27+
[Description("The amount of the adjustment")]
2528
public decimal Amount { get; set; }
2629

2730
/// <summary>
2831
/// Gets or sets a description for the change.
2932
/// </summary>
3033
/// <example>Purchase new laptops for team</example>
3134
[Required]
35+
[Description("The description for the change")]
3236
public string? Description { get; set; }
3337

3438
/// <summary>
3539
/// Gets or sets the expense category for charges against the budget.
3640
/// </summary>
3741
/// <example>hardware</example>
42+
[Description("The expense category for charges against the budget")]
3843
public string? ExpenseCategory { get; set; }
3944

4045
/// <summary>
4146
/// Gets the JSONPath to the Adaptive Card template to use for rendering this transaction in an API plugin.
4247
/// </summary>
48+
[Description("The JSONPath to the Adaptive Card template to use for rendering this transaction in an API plugin")]
4349
public string DisplayTemplate
4450
{
4551
get

0 commit comments

Comments
 (0)