Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
11 changes: 6 additions & 5 deletions api/BudgetTracker.csproj
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
<OpenApiGenerateDocumentsOnBuild>true</OpenApiGenerateDocumentsOnBuild>
<OpenApiGenerateDocumentsOptions>--openapi-version OpenApi3_0</OpenApiGenerateDocumentsOptions>
</PropertyGroup>

<Choose>
Expand All @@ -28,13 +29,13 @@

<ItemGroup>
<PackageReference Include="AdaptiveCards" Version="3.1.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.19" />
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="9.0.9">
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.11" />
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="10.0.11">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Identity.Web" Version="3.14.1" />
<PackageReference Include="Microsoft.Identity.Web.GraphServiceClient" Version="3.14.1" />
<PackageReference Include="Microsoft.Identity.Web" Version="4.14.2" />
<PackageReference Include="Microsoft.Identity.Web.GraphServiceClient" Version="4.14.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand Down
2 changes: 1 addition & 1 deletion api/BudgetTracker.http
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 14 additions & 16 deletions api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
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;
Expand All @@ -18,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) =>
{
Expand All @@ -28,6 +31,7 @@
if (!string.IsNullOrEmpty(settings.ServerUrl))
{
// Clear localhost entries added automatically
document.Servers ??= [];
document.Servers.Clear();
document.Servers.Add(new()
{
Expand All @@ -44,7 +48,8 @@

// Add the OAuth2 security scheme
document.Components ??= new OpenApiComponents();
document.Components.SecuritySchemes.Add("OAuth2", new()
document.Components.SecuritySchemes ??= new Dictionary<string, IOpenApiSecurityScheme>();
document.Components.SecuritySchemes.Add("OAuth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new()
Expand All @@ -63,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]
},
});

Expand All @@ -89,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<string, IOpenApiExtension>();
operation.Extensions.Add("x-openai-isConsequential", new JsonNodeExtension(false));
}

return Task.CompletedTask;
Expand Down
57 changes: 34 additions & 23 deletions examples/openapi/BudgetTracker.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"openapi": "3.0.1",
"openapi": "3.0.4",
"info": {
"title": "BudgetTracker | v1",
"version": "1.0.0"
Expand Down Expand Up @@ -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": [
Expand All @@ -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": [
Expand All @@ -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": [
Expand All @@ -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": {
Expand Down
4 changes: 3 additions & 1 deletion register-device-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
Loading