Skip to content

Commit aaf24a4

Browse files
committed
feat: zendesk generated client
1 parent 995a365 commit aaf24a4

421 files changed

Lines changed: 77731 additions & 12801 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.config/dotnet-tools.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"version": 1,
3+
"isRoot": true,
4+
"tools": {
5+
"microsoft.openapi.kiota": {
6+
"version": "1.32.5",
7+
"commands": [
8+
"kiota"
9+
]
10+
}
11+
}
12+
}

.github/workflows/pipeline.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,15 @@ jobs:
7171
- '*.slnx'
7272
- '*.props'
7373
- 'src/**'
74+
- '.config/dotnet-tools.json'
7475
build:
7576
- '*.sln'
7677
- '*.slnx'
7778
- '*.props'
7879
- 'src/**'
7980
- 'tests/**'
8081
- 'playground/**'
82+
- '.config/dotnet-tools.json'
8183
8284
- name: evaluate - build
8385
id: evaluate_build
@@ -131,6 +133,12 @@ jobs:
131133
- name: dotnet - restore
132134
run: dotnet restore
133135

136+
- name: dotnet - tools - restore
137+
# The ES.FX.Zendesk build regenerates its git-ignored Kiota clients via the pinned `kiota`
138+
# local tool (see eng/zendesk/README.md); restore it before build. pwsh is preinstalled on
139+
# ubuntu-latest runners.
140+
run: dotnet tool restore
141+
134142
- name: dotnet - build
135143
run: dotnet build --no-restore --configuration ${{ env.build_configuration }} /p:Version=${{ env.gitVersion_SemVer }} /p:AssemblyVersion=${{env.gitVersion_AssemblySemFileVer}} /p:NuGetVersion=${{env.gitVersion_SemVer}}
136144

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ project.lock.json
6262
project.fragment.lock.json
6363
artifacts/
6464

65+
# Kiota-generated Zendesk API clients — produced at build time (see src/ES.FX.Zendesk/OpenApi/README.md), not committed.
66+
# The .editorconfig is kept: it silences analyzers for the generated code and is not itself generated.
67+
/src/ES.FX.Zendesk/Generated/*
68+
!/src/ES.FX.Zendesk/Generated/.editorconfig
69+
6570
# ASP.NET Scaffolding
6671
ScaffoldingReadMe.txt
6772

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ registration, health checks, OpenTelemetry) and follows a fixed shape — study
8484
A newer Zendesk vertical spans the layers: `ES.FX.Zendesk` (typed Zendesk API client,
8585
[`docs/libraries/zendesk-client.md`](docs/libraries/zendesk-client.md)), `ES.FX.Ignite.Zendesk` (its Spark,
8686
[`docs/ignite/sparks/zendesk.md`](docs/ignite/sparks/zendesk.md)), and `ES.FX.Zendesk.MCP.Host` (the
87-
deployable MCP server app exposing the client as 168 read/write MCP tools,
87+
deployable MCP server app exposing the client as 172 read/write MCP tools,
8888
[`docs/libraries/zendesk-mcp-server.md`](docs/libraries/zendesk-mcp-server.md); its MCP wiring is
8989
host-inline — there is no MCP Spark package).
9090

Directory.Packages.props

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="10.0.9" />
4949
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.9" />
5050
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="10.7.0" />
51+
<PackageVersion Include="Microsoft.Kiota.Abstractions" Version="2.0.0" />
52+
<PackageVersion Include="Microsoft.Kiota.Bundle" Version="2.0.0" />
5153
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.7.0" />
5254
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
5355
<PackageVersion Include="Microting.EntityFrameworkCore.MySql" Version="10.0.9" />

docs/ignite/sparks/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ Every Spark, grouped by function.
9595
| Spark | Purpose |
9696
| --- | --- |
9797
| [Hermes Agent](./hermes-agent.md) | Registers the [ES.FX.NousResearch.HermesAgent](../../libraries/hermes-agent-client.md) typed `IHermesAgentClient` (static bearer key) with a live health check and tracing. |
98-
| [Zendesk](./zendesk.md) | Registers the [ES.FX.Zendesk](../../libraries/zendesk-client.md) typed `IZendeskClient` (OAuth `client_credentials`) with a live health check and tracing. |
98+
| [Zendesk](./zendesk.md) | Registers the [ES.FX.Zendesk](../../libraries/zendesk-client.md) Kiota-generated `ZendeskSupportApiClient` and `ZendeskHelpCenterApiClient` (OAuth `client_credentials`) with a live health check and tracing. |
9999

100100
### API & documentation
101101

docs/ignite/sparks/zendesk.md

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,34 @@
11
---
22
title: Zendesk client integration
3-
description: Register the ES.FX.Zendesk typed client with Ignite, including OAuth configuration binding, a live health check, and OpenTelemetry tracing.
3+
description: Register the Kiota-generated ES.FX.Zendesk Support and Help Center clients with Ignite, including OAuth configuration binding, a live health check, and OpenTelemetry tracing.
44
---
55

66
## Overview
77

8-
The Zendesk Spark registers the [ES.FX.Zendesk typed client](../../libraries/zendesk-client.md)
9-
(`IZendeskClient`, OAuth `client_credentials`) into dependency injection, with configuration binding,
10-
startup validation, a live health check, and OpenTelemetry tracing already wired up. Call
11-
`builder.IgniteZendeskClient()` once and inject `IZendeskClient` anywhere in your app.
8+
The Zendesk Spark registers the [ES.FX.Zendesk clients](../../libraries/zendesk-client.md) — the
9+
Kiota-generated `ZendeskSupportApiClient` and `ZendeskHelpCenterApiClient` (OAuth
10+
`client_credentials`) — into dependency injection, with configuration binding, startup validation, a
11+
live health check, and OpenTelemetry tracing already wired up. Call `builder.IgniteZendeskClient()`
12+
once and inject the clients anywhere in your app.
1213

1314
Under the hood the Spark:
1415

1516
- Binds `ZendeskClientOptions` (subdomain + OAuth credentials) from the `Ignite:Zendesk` configuration
1617
section with `ValidateOnStart()`, so a bad configuration fails at startup instead of on first use.
1718
- Binds a `ZendeskClientSparkSettings` (observability toggles) from the `Ignite:Zendesk:Settings`
1819
sub-section.
19-
- Registers the client via the library's `AddZendeskClient()` (typed `HttpClient`, keyed-capable, OAuth
20-
token provider with cached, single-flight refresh).
21-
- Adds a **live** health check that calls `GET /api/v2/users/me.json` with the configured credentials.
22-
- Adds the client's `ES.FX.Zendesk` `ActivitySource` to the Ignite OpenTelemetry pipeline.
20+
- Registers the clients via the library's `AddZendeskClient()` — the generated Support and Help Center
21+
clients, the shared Kiota `IRequestAdapter`, and the `ZendeskAttachmentContentFetcher`, all on a
22+
typed `HttpClient` with a keyed-capable OAuth token provider (cached, single-flight refresh).
23+
- Adds a **live** health check that calls `GET /api/v2/users/me` (via the generated Support client)
24+
with the configured credentials.
25+
- Adds **both** Zendesk tracing sources (`ES.FX.Zendesk` and the Kiota adapter's
26+
`Microsoft.Kiota.Http.HttpClientLibrary`) to the Ignite OpenTelemetry pipeline.
2327

2428
> [!NOTE]
25-
> The client surface, OAuth behavior, error model (`ZendeskApiException`, `RetryAfter`), and the
26-
> resource-area operations are documented on the [Zendesk API client](../../libraries/zendesk-client.md)
27-
> page. This page covers the Ignite wiring.
29+
> The client surface, OAuth behavior, error model (`ZendeskApiException`, Kiota `ApiException`), and
30+
> the raw-JSON escape hatches are documented on the
31+
> [Zendesk API client](../../libraries/zendesk-client.md) page. This page covers the Ignite wiring.
2832
2933
## Install the client
3034

@@ -75,19 +79,26 @@ It returns the underlying `IHttpClientBuilder`, for further customization of the
7579

7680
| Service | Lifetime | Notes |
7781
| --- | --- | --- |
78-
| `IZendeskClient` | Transient | The typed client. Keyed when `serviceKey` is set. |
82+
| `ZendeskSupportApiClient` | Transient | The generated Support API client. Keyed when `serviceKey` is set. |
83+
| `ZendeskHelpCenterApiClient` | Transient | The generated Help Center API client. Keyed when `serviceKey` is set. |
84+
| `IRequestAdapter` | Transient (keyed) | The Kiota adapter behind both clients — the escape hatch for raw/wire-true requests. |
85+
| `ZendeskAttachmentContentFetcher` | Transient (keyed) | Authenticated attachment-content downloads. |
7986
| `IZendeskAccessTokenProvider` | Singleton (keyed) | The OAuth token cache for the instance. |
8087
| `ZendeskClientSparkSettings` | Singleton (keyed) | The resolved observability settings. |
8188
| Health check `ZendeskClient` || Live credential check. See [Health checks](#health-checks). |
82-
| OpenTelemetry `ActivitySource` || `ES.FX.Zendesk`. See [Tracing](#tracing). |
89+
| OpenTelemetry `ActivitySource`s || `ES.FX.Zendesk` + `Microsoft.Kiota.Http.HttpClientLibrary`. See [Tracing](#tracing). |
8390

8491
### Consume the client
8592

8693
```csharp
87-
public sealed class TicketLookup(IZendeskClient zendesk)
94+
public sealed class TicketLookup(ZendeskSupportApiClient zendesk)
8895
{
89-
public Task<ZendeskTicket> GetAsync(long id, CancellationToken cancellationToken) =>
90-
zendesk.Tickets.GetByIdAsync(id, cancellationToken);
96+
public async Task<TicketObject?> GetAsync(long id, CancellationToken cancellationToken)
97+
{
98+
// GET /api/v2/tickets/{ticket_id}
99+
var response = await zendesk.Api.V2.Tickets[id].GetAsync(cancellationToken: cancellationToken);
100+
return response?.Ticket;
101+
}
91102
}
92103
```
93104

@@ -108,9 +119,10 @@ builder.IgniteZendeskClient(name: "sales", serviceKey: "sales");
108119

109120
- **`name`** selects the configuration sub-section: `name: "support"` reads from
110121
`Ignite:Zendesk:support` instead of `Ignite:Zendesk`. It does not affect DI.
111-
- **`serviceKey`** registers `IZendeskClient` as a **keyed** service. Resolve it with
112-
`[FromKeyedServices("…")]`. When `serviceKey` is `null`, the client is the default (unkeyed)
113-
registration.
122+
- **`serviceKey`** registers the clients (`ZendeskSupportApiClient`, `ZendeskHelpCenterApiClient`,
123+
`IRequestAdapter`, `ZendeskAttachmentContentFetcher`) as **keyed** services. Resolve them with
124+
`[FromKeyedServices("…")]`. When `serviceKey` is `null`, the clients are the default (unkeyed)
125+
registrations.
114126

115127
```json
116128
{
@@ -156,7 +168,7 @@ clear message.
156168
| `HealthChecks.Timeout` | `TimeSpan?` | none | Timeout applied to the health check. |
157169
| `HealthChecks.FailureStatus` | `HealthStatus?` | `Unhealthy` | Reported status when the check fails. |
158170
| `HealthChecks.Tags` | `string[]` | `[]` | Extra tags added alongside the built-in `Zendesk` tag. |
159-
| `Tracing.Enabled` | `bool` | `true` | Adds the `ES.FX.Zendesk` tracing source. |
171+
| `Tracing.Enabled` | `bool` | `true` | Adds both Zendesk tracing sources (curated + Kiota adapter). |
160172

161173
> [!NOTE]
162174
> The Zendesk Spark exposes no `Metrics` setting — HTTP-level metrics come from .NET's built-in
@@ -215,9 +227,10 @@ The final `configurationSectionPath` parameter overrides the root section. It de
215227

216228
The Spark registers a health check named **`ZendeskClient`** by default (`HealthChecks.Enabled` is
217229
`true`). For a keyed registration the name carries the key suffix — e.g. `ZendeskClient[support]`. The
218-
check is **live**: it calls `GET /api/v2/users/me.json`, which verifies DNS, TLS, the OAuth token flow,
219-
and the credentials in one probe. A healthy result reports the authenticated user id and role — never
220-
the email, since health output can surface on an unauthenticated `/health` endpoint.
230+
check is **live**: it calls `GET /api/v2/users/me` through the generated Support client, which verifies
231+
DNS, TLS, the OAuth token flow, and the credentials in one probe. A healthy result reports the
232+
authenticated user id and role — never the email, since health output can surface on an
233+
unauthenticated `/health` endpoint.
221234

222235
The check is tagged `Zendesk`, plus any tags you add via `HealthChecks.Tags`. It surfaces at the health
223236
endpoints mapped by `app.Ignite()`.
@@ -251,10 +264,14 @@ builder.IgniteZendeskClient(configureSettings: settings =>
251264

252265
### Tracing
253266

254-
Tracing is enabled by default (`Tracing.Enabled` is `true`). The Spark adds the client's
255-
`ES.FX.Zendesk` `ActivitySource` to the Ignite OpenTelemetry pipeline, so every Zendesk operation
256-
appears as a `Zendesk.{Area}.{Operation}` client span — with the underlying HTTP span from Ignite's
257-
`HttpClient` instrumentation as its child.
267+
Tracing is enabled by default (`Tracing.Enabled` is `true`). The Spark subscribes **both** Zendesk
268+
`ActivitySource`s to the Ignite OpenTelemetry pipeline:
269+
270+
- `ES.FX.Zendesk` (`ZendeskClientInstrumentation.ActivitySourceName`) — the client's own source, for
271+
spans from the curated rim.
272+
- `Microsoft.Kiota.Http.HttpClientLibrary` (`ZendeskClientInstrumentation.KiotaActivitySourceName`) —
273+
the Kiota request adapter's fixed source, which emits a span per generated-client request, with the
274+
underlying HTTP span from Ignite's `HttpClient` instrumentation as its child.
258275

259276
Disable it via configuration (`Ignite:Zendesk:Settings:Tracing:Enabled = false`) or the
260277
`configureSettings` delegate.
@@ -263,18 +280,21 @@ Disable it via configuration (`Ignite:Zendesk:Settings:Tracing:Enabled = false`)
263280

264281
Ignite applies the standard resilience handler to **every** `HttpClient` by default
265282
(`Ignite:Settings:HttpClient:StandardResilienceHandlerEnabled`), including this one — transient failures and
266-
`429 Too Many Requests` are retried honoring `Retry-After`, with no Spark-specific wiring. When
267-
retries are exhausted, the thrown `ZendeskApiException` still carries the last `RetryAfter` hint.
283+
`429 Too Many Requests` are retried honoring `Retry-After`, with no Spark-specific wiring.
284+
Non-retryable 4xx failures throw the typed `ZendeskApiException`; when retries on `408`/`429`/`5xx`
285+
are exhausted, the failure surfaces as Kiota's `ApiException` with the response headers (including
286+
`Retry-After`) preserved — see
287+
[error handling](../../libraries/zendesk-client.md#error-handling).
268288

269289
### Logging
270290

271291
The client logs through the app's configured logging pipeline — including
272-
[Serilog](./serilog.md) when you enable it — with no extra wiring: `Debug` on success, `Warning` with
273-
the status code on failure. Response bodies are never logged.
292+
[Serilog](./serilog.md) when you enable it — with no extra wiring (for example token acquisition at
293+
`Debug`). Response bodies are never logged.
274294

275295
## See also
276296

277-
- [Zendesk API client](../../libraries/zendesk-client.md) — the client surface, OAuth model, and error handling.
297+
- [Zendesk API client](../../libraries/zendesk-client.md) — the generated clients, OAuth model, and error handling.
278298
- [Zendesk MCP server](../../libraries/zendesk-mcp-server.md) — a deployable MCP host built on this Spark.
279299
- [Ignite overview](../index.md)
280300
- [Sparks catalog](./index.md)

docs/libraries/index.md

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ into Ignite when you want it to.
1414
| --- | --- | --- |
1515
| [**Transactional Outbox**](./transactional-outbox.md) | `ES.FX.TransactionalOutbox` (+ `.EntityFrameworkCore`, provider packages, `.MassTransit`) | Reliable message dispatch tied to your EF Core transaction — enqueue a message in the same `SaveChanges` that writes your data, then a hosted service delivers it. |
1616
| [**Migrations**](./migrations.md) | `ES.FX.Migrations` (+ `ES.FX.Ignite.Migrations`) | A DI-driven migration runner: implement `IMigrationsTask`, register it, and a hosted service applies every task at startup. |
17-
| [**Zendesk API client**](./zendesk-client.md) | `ES.FX.Zendesk` (+ `ES.FX.Ignite.Zendesk` Spark) | A typed, OAuth-authenticated client for the Zendesk Support REST APIresource-grouped operations, typed errors with `Retry-After`, and OpenTelemetry tracing. |
17+
| [**Zendesk API client**](./zendesk-client.md) | `ES.FX.Zendesk` (+ `ES.FX.Ignite.Zendesk` Spark) | Kiota-generated clients covering the complete Zendesk Support and Help Center REST APIstyped request builders, OAuth `client_credentials`, typed errors with `Retry-After`, and OpenTelemetry tracing. |
1818
| [**Hermes Agent API client**](./hermes-agent-client.md) | `ES.FX.NousResearch.HermesAgent` (+ `ES.FX.Ignite.NousResearch.HermesAgent` Spark) | A typed client for the Nous Research Hermes Agent API server — chat completions, Responses API, asynchronous runs, scheduled jobs, sessions and discovery, with `await foreach` streaming, typed errors, and OpenTelemetry tracing. |
1919

2020
Each library is independently consumable and has its own page below with the full end-to-end walkthrough.
@@ -57,24 +57,27 @@ runner from the Ignite Spark angle.
5757

5858
## Zendesk API client
5959

60-
The [Zendesk API client](./zendesk-client.md) is a typed client for the Zendesk Support REST API and
61-
Help Center, built on `IHttpClientFactory`. Register it with `AddZendeskClient()` and inject
62-
`IZendeskClient` — seventeen resource-grouped areas (`Tickets`, `Users`, `Organizations`, `Groups`,
63-
`Search`, `Views`, `Articles`, `TicketFields`, `Macros`, `Forms`, `Brands`, `CustomStatuses`,
64-
`JobStatuses`, `Tags`, `SuspendedTickets`, `Uploads`, `Attachments`) covering reads **and** writes that
65-
mirror the Zendesk API, with offset **and** cursor pagination, OAuth `client_credentials`
66-
authentication (cached, single-flight token refresh), a typed `ZendeskApiException` (status, body,
67-
`Retry-After`), and OpenTelemetry tracing.
60+
The [Zendesk API client](./zendesk-client.md) ships two
61+
[Kiota](https://learn.microsoft.com/en-us/openapi/kiota/)-generated clients covering the complete
62+
Zendesk REST API, built on `IHttpClientFactory`: `ZendeskSupportApiClient` (the full Support API — 614
63+
operations) and `ZendeskHelpCenterApiClient` (the full Help Center API — 177 operations). Register them
64+
with `AddZendeskClient()` and navigate the typed request builders, which mirror the API's URL structure
65+
(`zendesk.Api.V2.Tickets[id].GetAsync(…)`). A curated rim adds what the generator cannot: OAuth
66+
`client_credentials` authentication (cached, single-flight token refresh), a typed
67+
`ZendeskApiException` (status, body, `Retry-After`), authenticated attachment-content downloads, and
68+
OpenTelemetry tracing.
6869

6970
See the [Zendesk API client](./zendesk-client.md) page for the full walkthrough: registration and keyed
70-
multi-tenant instances, the resource areas, configuration and secret hygiene, the OAuth model, error
71-
handling and rate limits, and observability. The [Zendesk Spark](../ignite/sparks/zendesk.md) page
72-
covers the Ignite integration (config binding, startup validation, live health check, tracing).
71+
multi-tenant instances, request-builder usage, the raw-JSON escape hatch and the re-serialization
72+
hazard, configuration and secret hygiene, the OAuth model, error handling and rate limits, and
73+
observability. The [Zendesk Spark](../ignite/sparks/zendesk.md) page covers the Ignite integration
74+
(config binding, startup validation, live health check, tracing).
7375

7476
For exposing Zendesk to an AI agent, the [Zendesk MCP server](./zendesk-mcp-server.md) is a deployable
75-
[Model Context Protocol](https://modelcontextprotocol.io) host — built on this client and Ignite — that
76-
publishes the full client surface as 168 read and write MCP tools, with execution-mode gating and Origin
77-
validation. It is an application, not a package.
77+
[Model Context Protocol](https://modelcontextprotocol.io) host — built on the generated clients and
78+
Ignite — that publishes 172 read and write MCP tools with lean-first responses (summary rows by
79+
default, full records on request), execution-mode gating, and Origin validation. It is an application,
80+
not a package.
7881

7982
---
8083

0 commit comments

Comments
 (0)