MERSELSDK.Maestro.Console.PlatformSdk
0.0.11
dotnet add package MERSELSDK.Maestro.Console.PlatformSdk --version 0.0.11
NuGet\Install-Package MERSELSDK.Maestro.Console.PlatformSdk -Version 0.0.11
<PackageReference Include="MERSELSDK.Maestro.Console.PlatformSdk" Version="0.0.11" />
<PackageVersion Include="MERSELSDK.Maestro.Console.PlatformSdk" Version="0.0.11" />
<PackageReference Include="MERSELSDK.Maestro.Console.PlatformSdk" />
paket add MERSELSDK.Maestro.Console.PlatformSdk --version 0.0.11
#r "nuget: MERSELSDK.Maestro.Console.PlatformSdk, 0.0.11"
#:package MERSELSDK.Maestro.Console.PlatformSdk@0.0.11
#addin nuget:?package=MERSELSDK.Maestro.Console.PlatformSdk&version=0.0.11
#tool nuget:?package=MERSELSDK.Maestro.Console.PlatformSdk&version=0.0.11
MERSELSDK.Maestro.Console.PlatformSdk
Mersel Maestro Platform API için resmi .NET SDK'sı. Platform backend'inizden Maestro'ya HMAC imzalı çağrılar yapar, tarayıcı için hazır uçlar açar ve gelen webhook'ları doğrular. Destek talepleri (inline görsel + dosya eki dahil) ve duyurular için uçtan uca entegrasyon sağlar.
Mimari
Tarayıcı (widget/SPA) --(kendi auth'unuz)--> Platform backend (bu SDK) --(HMAC)--> Maestro PlatformApi
Maestro --(HMAC imzalı webhook)--> Platform backend (bu SDK)
Gizli anahtar (SecretKey) yalnızca backend'de kalır; tarayıcıya hiçbir sır inmez.
Kurulum
dotnet add package MERSELSDK.Maestro.Console.PlatformSdk
Hızlı başlangıç
appsettings.json:
{
"Maestro": {
"BaseUrl": "https://platform-api.maestro.cloud",
"AccessKey": "pk_...",
"SecretKey": "sk_...",
"EndpointPrefix": "/api/maestro",
"Webhook": { "Enabled": true, "Path": "/webhooks/maestro", "Secret": "wh_..." },
"Polling": { "Enabled": false }
}
}
Program.cs:
builder.Services.AddMaestroIntegration(options =>
builder.Configuration.GetSection("Maestro").Bind(options));
// Platform kullanıcısını kendi auth'unuzdan çözen tek zorunlu soyutlama:
builder.Services.AddScoped<IMaestroUserContext, MyUserContext>();
var app = builder.Build();
// /api/maestro/tickets*, /api/maestro/announcements*, webhook alıcısı vb. açılır:
app.MapMaestroEndpoints();
IMaestroUserContext uygulaması:
public sealed class MyUserContext(IHttpContextAccessor http) : IMaestroUserContext
{
public Task<MaestroUserInfo?> GetCurrentUserAsync(CancellationToken ct)
{
var u = http.HttpContext?.User;
if (u?.Identity?.IsAuthenticated != true) return Task.FromResult<MaestroUserInfo?>(null);
return Task.FromResult<MaestroUserInfo?>(new MaestroUserInfo
{
UserId = u.FindFirst("sub")!.Value,
Email = u.FindFirst("email")?.Value,
UserName = u.Identity!.Name,
FirstName = u.FindFirst("given_name")?.Value,
LastName = u.FindFirst("family_name")?.Value,
AccountId = u.FindFirst("account_id")?.Value,
AccountName = u.FindFirst("account_name")?.Value,
// Yalnızca hesap yöneticisi/sahibi ise tüm hesabın taleplerini görebilir (scope=account).
// Tarayıcı bu değeri ASLA belirleyemez; sunucu tarafında platform karar verir.
CanViewAccountTickets = u.IsInRole("AccountAdmin"),
});
}
}
Açılan uçlar (MapMaestroEndpoints)
EndpointPrefix (varsayılan /api/maestro) altında:
| Metod | Yol | Açıklama |
|---|---|---|
GET |
/tickets?pageNumber=&pageSize=&status=&since= |
Sayfalı liste (delta polling için since). |
GET |
/tickets/{id} |
Ticket detayı (yorumlar + ekler + fieldValues dinamik form snapshot'ı). |
POST |
/tickets |
Ticket oluşturur, oluşturulan detayı döner. Gövde opsiyonel fieldValues (kategori dinamik formu) taşır. |
POST |
/tickets/{id}/comments |
Yorum ekler, güncellenmiş detayı döner. |
POST |
/tickets/attachments |
Multipart yükleme; SDK base64 JSON'a çevirip HMAC ile iletir. |
GET |
/tickets/attachments/{token} |
Eki ikili olarak tarayıcıya akıtır. |
GET |
/categories |
Seçilebilir kategoriler; her biri opsiyonel DynamicFormSchema ile (talep oluşturma ekranı için). |
GET |
/announcements |
Aktif duyurular (kullanıcı bazlı isSeen/isDismissed ile; dismiss'lenenler gizli). |
GET |
/announcements/{id} |
Duyuru detayı (isAcknowledged/isSeen/isDismissed + CSAT rating). |
GET |
/announcements/{id}/engagement |
Platform-admin: kendi kitlesi için toplu gördü/onay/dismiss + CSAT özeti. Host bu rotayı admin'e kısıtlamalı. |
POST |
/announcements/{id}/seen |
Görüldü (pasif okuma) sinyali — hesap+kullanıcı bazlı. |
POST |
/announcements/{id}/acknowledge |
Duyuru onaylandı işaretleme. |
POST |
/announcements/{id}/dismiss |
Kapat / bir daha gösterme (kullanıcı bazlı kalıcı). |
POST |
/announcements/{id}/rating |
CSAT (1-5 puan + opsiyonel yorum); güncellenmiş detayı döner. |
GET |
/feature-flags |
Kaba snapshot: { etag, flags: { key: { isEnabled, value, requiresContext } } }. (FeatureFlags.Enabled) |
POST |
/feature-flags/evaluate |
Context-bazlı değerlendirme (geçerli kullanıcının hesabına göre hedefleme); gövde { keys?, attributes? }. |
GET |
/feature-flags/public |
Anonim allow-list'li snapshot (pre-auth ekranlar). Yalnız FeatureFlags.PublicKeys yansıtılır. |
GET |
/health |
SDK + Maestro bağlantı sağlığı. |
POST |
{Webhook.Path} |
İmza doğrulamalı webhook alıcısı. |
Bu uçlar @mersel/maestro-tickets-widget ile birebir uyumludur.
Webhook işleme
public sealed class MyWebhookHandler : IMaestroWebhookHandler
{
public Task OnTicketCommentAddedAsync(TicketCommentEvent e, CancellationToken ct) { /* ... */ }
public Task OnTicketStatusChangedAsync(TicketStatusEvent e, CancellationToken ct) { /* ... */ }
public Task OnTicketCreatedAsync(TicketCreatedEvent e, CancellationToken ct) { /* ... */ }
public Task OnAnnouncementPublishedAsync(AnnouncementEvent e, CancellationToken ct) { /* ... */ }
}
builder.Services.AddScoped<IMaestroWebhookHandler, MyWebhookHandler>();
İmza (X-Maestro-Signature + X-Maestro-Timestamp) Webhook.Secret ile doğrulanır; tolerans dışı zaman damgaları reddedilir.
Gerçek-zamanlı (SignalR)
AddMaestroRealtime() ile tarayıcıya gerçek-zamanlı ticket ve duyuru güncellemeleri eklenir. Gelen webhook olayları (yanıt eklendi, durum değişti, duyuru yayınlandı) ilgili tarayıcılara iletilir; widget veriyi yeniden çeker ve bildirim gösterir. Yerleşik relay, kendi webhook handler'ınızla birlikte çalışır (dispatcher tüm handler'ları çağırır).
Duyuru sinyalleri hedefleme-farkındadır: geniş hedefli duyurular (All/Organization/Platform) tüm bağlı tarayıcılara (mu:ann:all) yayınlanır; partner-hedefli duyurular yalnızca ilgili partner gruplarına (mu:ann:partner:{id}) gider. Bağlantı kurulurken hub, kullanıcının hesabına ait partner alt-ağacını sunucu-sunucu announcements/audience-scope ucundan çözer ve doğru gruplara katılır. Sinyalde içerik taşınmaz; tarayıcı yetkili/filtrelenmiş listeyi yeniden çeker ve toast'ı istemci-diff'inden üretir.
builder.Services.AddMaestroIntegration(/* ... */);
builder.Services.AddMaestroRealtime(); // AddMaestroIntegration'dan SONRA çağrılır
// ...
authed.MapMaestroEndpoints(); // her iki hub'ı yetkili grup altında otomatik mapler
Ticket ve duyuru AYRI hub'lardır (ayrı endpoint → ayrı bağlantı → domain sınırı temiz ve ileride ayrı servise taşımaya açık). İki yol da MapMaestroEndpoints'in çağrıldığı yetki grubunun auth'unu miras alır; kullanıcı IMaestroUserContext'ten çözülür:
- Ticket hub'ı
{EndpointPrefix}/hub/tickets(varsayılan; örn./api/maestro/hub/tickets). Bağlantımu:user:{userId}ve (account-scope görüntüleyiciler için)mu:acct:{accountId}gruplarına eklenir. - Duyuru hub'ı
{EndpointPrefix}/hub/announcements(varsayılan; örn./api/maestro/hub/announcements). Bağlantımu:ann:allve hesabın partner alt-ağacına göremu:ann:partner:{id}gruplarına eklenir.
Yollar Realtime:HubPath ve Realtime:AnnouncementHubPath ile özelleştirilebilir. Payload yalnızca tanımlayıcı taşır; gerçek içerik daima yetki-zorunlu yeniden okuma ile gelir. Auth köprüsü/ingress kuralının {EndpointPrefix}/hub önekini (yalnız /hub/tickets değil) kapsadığından emin olun; böylece iki hub da tek kuralla kapsanır.
CORS — cross-origin widget için ZORUNLU
Widget, bu backend'den farklı bir origin'de servis ediliyorsa (tipik kurulum), hub yolunun tarayıcı isteklerine CORS izni vermesi şarttır. İstemcinin varsayılan transport'u Long Polling'tir; bu GET ile çalışır ve preflight OPTIONS gerektirir. En sık hata yalnızca POST'a izin verip GET/OPTIONS'ı unutmaktır: bu durumda long-poll GET'leri CORS'a takılır, istemci sürekli yeniden bağlanır ve istek seli oluşur.
const string MaestroCors = "MaestroWidget";
builder.Services.AddCors(o => o.AddPolicy(MaestroCors, p => p
.WithOrigins("https://<widget-origin>") // örn. ÖnMuhasebe önyüzü origin'i
.AllowAnyHeader()
.WithMethods("GET", "POST", "OPTIONS", "DELETE")
.AllowCredentials())); // widget withCredentials=true ise gerekli
app.UseCors(MaestroCors); // MapMaestroEndpoints'ten ÖNCE
Transport: Long Polling (varsayılan) → WebSockets (opsiyonel)
Varsayılan Long Polling, host'ta ek ayar gerektirmez ve doğru CORS ile seli durdurur — ama yine de SSE/WS'e göre daha çok istek üretir. İstek hacmini neredeyse sıfıra indirmek için istemci SDK'sına transport: 'auto' verin (WebSockets → SSE → LongPolling fallback). Bunun çalışması için host'ta iki şart vardır:
- Yukarıdaki CORS (GET/OPTIONS) hazır olmalı.
- WS/SSE tarayıcıda
Authorizationheader'ı gönderemez; SignalR token'ı?access_token=...query string ile yollar (SDKaccessTokenFactoryile zaten ekler). Host JWT doğrulaması bunu hub yolunda kabul etmelidir:
.AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = ctx =>
{
var token = ctx.Request.Query["access_token"];
if (!string.IsNullOrEmpty(token) &&
ctx.HttpContext.Request.Path.StartsWithSegments("/api/maestro/hub"))
{
ctx.Token = token;
}
return Task.CompletedTask;
}
};
});
Çalışan referans için samples/SamplePlatform/Program.cs dosyasına bakın.
Doğrudan istemci
Otomatik uçların kapsamadığı çağrılar için IMaestroClient enjekte edip kullanın:
var result = await client.SendAsync<TicketDetail>(HttpMethod.Get, $"tickets/{id}", ct: ct);
var file = await client.DownloadAsync($"tickets/attachments/{token}", ct: ct);
Polling (webhook yoksa)
Polling.Enabled = true ise duyuru ve ticket durum senkronizasyonu için arka plan servisleri (AnnouncementSyncService, TicketStatusSyncService) çalışır ve IMaestroWebhookHandler'ınızı delta'larla besler.
Yapılandırma anahtarları
BaseUrl, AccessKey, SecretKey (zorunlu), EndpointPrefix, HttpClientName, Webhook.{Enabled,Path,Secret,TimestampToleranceSeconds}, Retry.{MaxAttempts,BaseDelayMs,MaxDelayMs}, Polling.{Enabled,AnnouncementIntervalMinutes,TicketStatusIntervalMinutes}, Realtime.{Enabled,HubPath,AnnouncementHubPath} (AddMaestroRealtime() ile açılır).
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.