From 5dd4e0a2ac95ee55a453f7ec952b21f7fbb23f61 Mon Sep 17 00:00:00 2001 From: Jason Johnston Date: Wed, 14 Jan 2026 10:35:25 -0500 Subject: [PATCH 1/2] Update project to .NET 10 --- .github/workflows/dotnet.yml | 2 +- .vscode/launch.json | 2 +- api/BudgetTracker.csproj | 10 +++++----- api/Program.cs | 3 +++ 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 917ba79..b230bcb 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -18,7 +18,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: 9.x + dotnet-version: 10.x - name: Restore dependencies run: dotnet restore - name: Build diff --git a/.vscode/launch.json b/.vscode/launch.json index 2ec1c1e..06f33fc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/api/bin/Debug/net9.0/BudgetTracker.dll", + "program": "${workspaceFolder}/api/bin/Debug/net10.0/BudgetTracker.dll", "args": [], "cwd": "${workspaceFolder}/api", "stopAtEntry": false, diff --git a/api/BudgetTracker.csproj b/api/BudgetTracker.csproj index 93ec6a2..04db855 100644 --- a/api/BudgetTracker.csproj +++ b/api/BudgetTracker.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 enable enable true @@ -28,13 +28,13 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/api/Program.cs b/api/Program.cs index 41aa0be..cfd3dde 100644 --- a/api/Program.cs +++ b/api/Program.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +/* cSpell:ignore appsettings */ + using System.Text; using BudgetTracker.Endpoints; using BudgetTracker.Models; @@ -28,6 +30,7 @@ if (!string.IsNullOrEmpty(settings.ServerUrl)) { // Clear localhost entries added automatically + document.Servers ??= []; document.Servers.Clear(); document.Servers.Add(new() { From dad5ea894a0590733f914f7861ced6fdeccf2628 Mon Sep 17 00:00:00 2001 From: Jason Johnston Date: Tue, 18 Aug 2026 13:42:17 -0400 Subject: [PATCH 2/2] Code fixes due to updated dependencies --- README.md | 2 +- api/BudgetTracker.csproj | 9 +++-- api/BudgetTracker.http | 2 +- api/Program.cs | 31 +++++++--------- examples/openapi/BudgetTracker.json | 57 +++++++++++++++++------------ register-device-code.md | 4 +- 6 files changed, 57 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index b13148f..ead9825 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ The Budget Tracker API is a simple example of a REST API that can be used as the ## Prerequisites -- [.NET SDK](https://dotnet.microsoft.com/) 8.x or later +- [.NET SDK](https://dotnet.microsoft.com/) 10.x or later - [devtunnel CLI](https://learn.microsoft.com/azure/developer/dev-tunnels) for debugging sample locally - A Microsoft work or school account with an Exchange Online mailbox. If you don't have a Microsoft 365 tenant, you might qualify for one through the [Microsoft 365 Developer Program](https://developer.microsoft.com/microsoft-365/dev-program); for details, see the [FAQ](/office/developer-program/microsoft-365-developer-program-faq#who-qualifies-for-a-microsoft-365-e5-developer-subscription-). Alternatively, you can [sign up for a 1-month free trial or purchase a Microsoft 365 plan](https://www.microsoft.com/microsoft-365/try). diff --git a/api/BudgetTracker.csproj b/api/BudgetTracker.csproj index 04db855..6aa2263 100644 --- a/api/BudgetTracker.csproj +++ b/api/BudgetTracker.csproj @@ -7,6 +7,7 @@ true true true + --openapi-version OpenApi3_0 @@ -28,13 +29,13 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/api/BudgetTracker.http b/api/BudgetTracker.http index fd65108..f60fdb6 100644 --- a/api/BudgetTracker.http +++ b/api/BudgetTracker.http @@ -3,7 +3,7 @@ # @name GetBudgets GET {{BudgetTracker_HostAddress}}/budgets Accept: application/json -Authorization: Bearer {{$aadV2Token scopes:api://API_CLIENT_ID/access_as_user tenantId:TENANT_ID clientId:API_CLIENT_ID}} +Authorization: Bearer {{$aadV2Token scopes:api://API_CLIENT_ID/access_as_user tenantId:TENANT_ID clientId:TEST_CLIENT_ID}} ### # @name GetBudgetByName diff --git a/api/Program.cs b/api/Program.cs index cfd3dde..ce7d154 100644 --- a/api/Program.cs +++ b/api/Program.cs @@ -1,15 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -/* cSpell:ignore appsettings */ - using System.Text; using BudgetTracker.Endpoints; using BudgetTracker.Models; using BudgetTracker.Services; using Microsoft.Identity.Web; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; + +/* cSpell:ignore appsettings */ // To enable emoji's in logger output to the terminal Console.OutputEncoding = Encoding.UTF8; @@ -20,6 +19,8 @@ // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddOpenApi((options) => { + options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0; + // Add document transform to add additional info options.AddDocumentTransformer((document, context, cancellationToken) => { @@ -47,7 +48,8 @@ // Add the OAuth2 security scheme document.Components ??= new OpenApiComponents(); - document.Components.SecuritySchemes.Add("OAuth2", new() + document.Components.SecuritySchemes ??= new Dictionary(); + document.Components.SecuritySchemes.Add("OAuth2", new OpenApiSecurityScheme { Type = SecuritySchemeType.OAuth2, Flows = new() @@ -66,20 +68,12 @@ }); // Add security requirement to all endpoints - document.SecurityRequirements.Add(new() + document.Security ??= []; + document.Security.Add(new OpenApiSecurityRequirement { { - new OpenApiSecurityScheme - { - Reference = new() - { - Type = ReferenceType.SecurityScheme, - Id = "OAuth2", - }, - }, - [ - apiScope - ] + new OpenApiSecuritySchemeReference("OAuth2", document), + [apiScope] }, }); @@ -92,7 +86,8 @@ { if (string.Compare(operation.OperationId, "SendTransactionReport", StringComparison.Ordinal) == 0) { - operation.Extensions.Add("x-openai-isConsequential", new OpenApiBoolean(false)); + operation.Extensions ??= new Dictionary(); + operation.Extensions.Add("x-openai-isConsequential", new JsonNodeExtension(false)); } return Task.CompletedTask; diff --git a/examples/openapi/BudgetTracker.json b/examples/openapi/BudgetTracker.json index ea03960..46a6c54 100644 --- a/examples/openapi/BudgetTracker.json +++ b/examples/openapi/BudgetTracker.json @@ -1,5 +1,5 @@ { - "openapi": "3.0.1", + "openapi": "3.0.4", "info": { "title": "BudgetTracker | v1", "version": "1.0.0" @@ -283,9 +283,11 @@ "properties": { "message": { "type": "string", - "description": "The response message" + "description": "Gets the response message.", + "example": "1000 has been deducted from Contoso Copilot plugin project. The budget has 49000 remaining in available funds." } - } + }, + "description": "Represents an API response." }, "Budget": { "required": [ @@ -296,15 +298,18 @@ "properties": { "name": { "type": "string", - "description": "The name of the budget", - "nullable": true + "description": "Gets or sets the name of the budget.", + "nullable": true, + "example": "Contoso Copilot plugin project" }, "availableFunds": { - "type": "number", - "description": "The available funds in the budget", - "format": "double" + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "description": "Gets or sets the available funds in the budget.", + "format": "double", + "example": 50000 } - } + }, + "description": "Represents a project budget." }, "Transaction": { "required": [ @@ -316,30 +321,35 @@ "properties": { "budgetName": { "type": "string", - "description": "The name of the budget to adjust", - "nullable": true + "description": "Gets or sets the name of the budget to adjust.", + "nullable": true, + "example": "Contoso Copilot plugin project" }, "amount": { - "type": "number", - "description": "The amount of the adjustment", - "format": "double" + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "description": "Gets or sets the amount of the adjustment.", + "format": "double", + "example": -5000 }, "description": { "type": "string", - "description": "The description for the change", - "nullable": true + "description": "Gets or sets a description for the change.", + "nullable": true, + "example": "Purchase new laptops for team" }, "expenseCategory": { "type": "string", - "description": "The expense category for charges against the budget", - "nullable": true + "description": "Gets or sets the expense category for charges against the budget.", + "nullable": true, + "example": "hardware" }, "displayTemplate": { "type": "string", - "description": "The JSONPath to the Adaptive Card template to use for rendering this transaction in an API plugin", + "description": "Gets the JSONPath to the Adaptive Card template to use for rendering this transaction in an API plugin.", "nullable": true } - } + }, + "description": "Represents a change to a budget's available funds." }, "TransactionListResponse": { "required": [ @@ -352,13 +362,14 @@ "items": { "$ref": "#/components/schemas/Transaction" }, - "description": "The list of transactions in the API response" + "description": "Gets the API response payload." }, "templates": { "type": "object", - "description": "Adaptive Card templates" + "description": "Gets the Adaptive Card templates." } - } + }, + "description": "Represents an API response payload that includes a list of transactions and a list of Adaptive Card templates." } }, "securitySchemes": { diff --git a/register-device-code.md b/register-device-code.md index 88a022d..dfcdd03 100644 --- a/register-device-code.md +++ b/register-device-code.md @@ -20,7 +20,9 @@ For testing, you can register an application that supports user authentication u 1. Open [BudgetTracker.http](./api/BudgetTracker.http) in Visual Studio Code. -1. Replace all instances of `API_CLIENT_ID` with the **Application (client) ID** you generated in the previous steps. +1. Replace all instances of `API_CLIENT_ID` with the **Application (client) ID** from the **Budget Tracker Service**. + +1. Replace all instances of `TEST_CLIENT_ID` with the **Application (client) ID** you generated in the previous steps. 1. Replace all instances of `TENANT_ID` with the **Application (client) ID** you generated in the previous steps.