From a47a53b343271ab6f1294cf0134af82b986ce5da Mon Sep 17 00:00:00 2001 From: Szeregowy Date: Wed, 25 Mar 2026 19:03:13 +0100 Subject: [PATCH 1/2] =?UTF-8?q?docs:=20fix=20mkdocs.yml=20=E2=80=94=20usun?= =?UTF-8?q?i=C4=99cie=20nieprawid=C5=82owego=20alternate=20selector,=20j?= =?UTF-8?q?=C4=99zyk:=20pl=20zgodnie=20z=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mkdocs.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 2567a57..c9e1e94 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -119,13 +119,6 @@ extra: social: - icon: fontawesome/brands/github link: https://github.com/tailored-apps/SharedComponents - alternate: - - name: Polski - link: / - lang: pl - - name: English - link: /en/ - lang: en # Extensions markdown_extensions: From e0a834801887dcdd6fa57407e00f509ae619a15c Mon Sep 17 00:00:00 2001 From: Szeregowy Date: Wed, 25 Mar 2026 19:14:17 +0100 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20podzia=C5=82=20PL/EN=20=E2=80=94=20?= =?UTF-8?q?osobne=20katalogi=20docs/=20i=20docs-en/=20dla=20dw=C3=B3ch=20l?= =?UTF-8?q?anguage=20sites?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 44 +++- docs-en/Libraries/DateTime/index.md | 168 +++++++++++++ docs-en/Libraries/Email/Models.md | 91 +++++++ docs-en/Libraries/Email/Office365.md | 151 ++++++++++++ docs-en/Libraries/Email/index.md | 208 ++++++++++++++++ .../EntityFramework/UnitOfWork.WebApiCore.md | 135 +++++++++++ docs-en/Libraries/EntityFramework/index.md | 193 +++++++++++++++ docs-en/Libraries/ExceptionHandling/index.md | 169 +++++++++++++ docs-en/Libraries/MediatR/Caching.md | 90 +++++++ docs-en/Libraries/MediatR/Email.md | 151 ++++++++++++ docs-en/Libraries/MediatR/ML.md | 162 +++++++++++++ docs-en/Libraries/MediatR/PagedRequest.md | 177 ++++++++++++++ docs-en/Libraries/MediatR/index.md | 174 ++++++++++++++ docs-en/Libraries/Payments/Providers/Adyen.md | 106 +++++++++ .../Libraries/Payments/Providers/CashBill.md | 82 +++++++ .../Libraries/Payments/Providers/HotPay.md | 80 +++++++ .../Libraries/Payments/Providers/PayNow.md | 86 +++++++ docs-en/Libraries/Payments/Providers/PayU.md | 111 +++++++++ .../Payments/Providers/Przelewy24.md | 90 +++++++ .../Libraries/Payments/Providers/Revolut.md | 84 +++++++ .../Libraries/Payments/Providers/Stripe.md | 119 ++++++++++ docs-en/Libraries/Payments/Providers/Tpay.md | 96 ++++++++ docs-en/Libraries/Payments/index.md | 223 ++++++++++++++++++ docs-en/Libraries/Querying/index.md | 176 ++++++++++++++ docs-en/Libraries/Readme.md | 0 docs-en/contributing.md | 132 +++++++++++ docs-en/index.md | 57 +++++ docs/Libraries/DateTime/index.md | 8 +- docs/Libraries/Email/Models.md | 6 +- docs/Libraries/Email/Office365.md | 11 +- docs/Libraries/Email/index.md | 10 +- .../EntityFramework/UnitOfWork.WebApiCore.md | 12 +- docs/Libraries/EntityFramework/index.md | 12 +- docs/Libraries/ExceptionHandling/index.md | 13 +- docs/Libraries/MediatR/Caching.md | 8 +- docs/Libraries/MediatR/Email.md | 8 +- docs/Libraries/MediatR/ML.md | 8 +- docs/Libraries/MediatR/PagedRequest.md | 8 +- docs/Libraries/MediatR/index.md | 10 +- docs/Libraries/Payments/index.md | 15 +- docs/Libraries/Querying/index.md | 12 +- mkdocs-en.yml | 158 +++++++++++++ mkdocs.yml | 14 ++ 43 files changed, 3529 insertions(+), 139 deletions(-) create mode 100644 docs-en/Libraries/DateTime/index.md create mode 100644 docs-en/Libraries/Email/Models.md create mode 100644 docs-en/Libraries/Email/Office365.md create mode 100644 docs-en/Libraries/Email/index.md create mode 100644 docs-en/Libraries/EntityFramework/UnitOfWork.WebApiCore.md create mode 100644 docs-en/Libraries/EntityFramework/index.md create mode 100644 docs-en/Libraries/ExceptionHandling/index.md create mode 100644 docs-en/Libraries/MediatR/Caching.md create mode 100644 docs-en/Libraries/MediatR/Email.md create mode 100644 docs-en/Libraries/MediatR/ML.md create mode 100644 docs-en/Libraries/MediatR/PagedRequest.md create mode 100644 docs-en/Libraries/MediatR/index.md create mode 100644 docs-en/Libraries/Payments/Providers/Adyen.md create mode 100644 docs-en/Libraries/Payments/Providers/CashBill.md create mode 100644 docs-en/Libraries/Payments/Providers/HotPay.md create mode 100644 docs-en/Libraries/Payments/Providers/PayNow.md create mode 100644 docs-en/Libraries/Payments/Providers/PayU.md create mode 100644 docs-en/Libraries/Payments/Providers/Przelewy24.md create mode 100644 docs-en/Libraries/Payments/Providers/Revolut.md create mode 100644 docs-en/Libraries/Payments/Providers/Stripe.md create mode 100644 docs-en/Libraries/Payments/Providers/Tpay.md create mode 100644 docs-en/Libraries/Payments/index.md create mode 100644 docs-en/Libraries/Querying/index.md create mode 100644 docs-en/Libraries/Readme.md create mode 100644 docs-en/contributing.md create mode 100644 docs-en/index.md create mode 100644 mkdocs-en.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2118c06..67b253d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,26 +1,33 @@ -name: 'Publish to Github pages' +name: 'Publish to Github Pages' + on: push: branches: - master + permissions: contents: write + jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: 3.x + - uses: actions/setup-dotnet@v4 with: dotnet-version: 10.0.x + - name: Restore Workloads run: dotnet workload restore + - name: Test run: dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat="opencover" - + - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v5 env: @@ -30,13 +37,26 @@ jobs: with: key: ${{ github.ref }} path: .cache - - name: Install DocXml - run: dotnet tool install xmldocmd -g - - name: generate docs - working-directory: src/TailoredApps.Shared.DateTime/bin/Debug/netstandard2.0 - run: xmldocmd ./TailoredApps.Shared.DateTime.dll ../../../../../docs/Libraries/ - - run: pip install mkdocs-material - - name: Publish - run: mkdocs gh-deploy --force - - \ No newline at end of file + + - name: Install MkDocs Material + run: pip install mkdocs-material + + # ── Docs build ──────────────────────────────────────────────────────── + + - name: Build Polish site (root) + run: mkdocs build --config-file mkdocs.yml --site-dir site + + - name: Build English site (/en/) + run: mkdocs build --config-file mkdocs-en.yml --site-dir site/en + + # ── Deploy ─────────────────────────────────────────────────────────── + + - name: Deploy to GitHub Pages + run: | + cd site + git init + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add . + git commit -m "docs: deploy PL + EN [skip ci]" + git push --force "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" HEAD:gh-pages diff --git a/docs-en/Libraries/DateTime/index.md b/docs-en/Libraries/DateTime/index.md new file mode 100644 index 0000000..66586a7 --- /dev/null +++ b/docs-en/Libraries/DateTime/index.md @@ -0,0 +1,168 @@ +# TailoredApps.Shared.DateTime + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.DateTime)](https://www.nuget.org/packages/TailoredApps.Shared.DateTime/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +--- + +## Description + +This library solves one of the fundamental testability problems in .NET — direct use of `System.DateTime.Now` in production code, which prevents writing deterministic unit tests. + +`TailoredApps.Shared.DateTime` provides the `IDateTimeProvider` interface and its default implementation `DateTimeProvider`. Instead of calling `DateTime.Now` directly, you inject `IDateTimeProvider` via DI and call `provider.Now`. In tests you swap the implementation for a mock that returns any point in time — making tests repeatable and independent of the system clock. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.DateTime +``` + +--- + +## Rejestracja w DI + +```csharp +// Program.cs +using TailoredApps.Shared.DateTime; + +builder.Services.AddSingleton(); +``` + +--- + +## Przykład użycia + +### Kod produkcyjny + +```csharp +public class OrderService +{ + private readonly IDateTimeProvider _dateTime; + + public OrderService(IDateTimeProvider dateTime) + { + _dateTime = dateTime; + } + + public Order CreateOrder(string customerId, decimal amount) + { + return new Order + { + Id = Guid.NewGuid(), + CustomerId = customerId, + Amount = amount, + CreatedAt = _dateTime.UtcNow, // zamiast DateTime.UtcNow + ExpiresAt = _dateTime.UtcNow.AddDays(30) + }; + } + + public bool IsOrderExpired(Order order) + { + return order.ExpiresAt < _dateTime.UtcNow; + } +} +``` + +### Test jednostkowy (Moq) + +```csharp +using Moq; +using TailoredApps.Shared.DateTime; +using Xunit; + +public class OrderServiceTests +{ + [Fact] + public void CreateOrder_ShouldSetCreatedAtToCurrentUtcTime() + { + // Arrange + var fixedTime = new DateTime(2024, 6, 1, 12, 0, 0, DateTimeKind.Utc); + var dateTimeMock = new Mock(); + dateTimeMock.Setup(d => d.UtcNow).Returns(fixedTime); + + var service = new OrderService(dateTimeMock.Object); + + // Act + var order = service.CreateOrder("customer-1", 99.99m); + + // Assert + Assert.Equal(fixedTime, order.CreatedAt); + Assert.Equal(fixedTime.AddDays(30), order.ExpiresAt); + } + + [Fact] + public void IsOrderExpired_WhenExpiresInPast_ReturnsTrue() + { + // Arrange + var now = new DateTime(2024, 6, 1, DateTimeKind.Utc); + var dateTimeMock = new Mock(); + dateTimeMock.Setup(d => d.UtcNow).Returns(now); + + var service = new OrderService(dateTimeMock.Object); + var expiredOrder = new Order { ExpiresAt = now.AddDays(-1) }; + + // Act & Assert + Assert.True(service.IsOrderExpired(expiredOrder)); + } +} +``` + +--- + +## API Reference + +| Typ | Rodzaj | Opis | +|-----|--------|------| +| `IDateTimeProvider` | Interfejs | Główny kontrakt — wszystkie właściwości do pobierania czasu | +| `DateTimeProvider` | Klasa | Implementacja produkcyjna — deleguje do `System.DateTime` | +| `IDateTimeProvider.Now` | Właściwość | Aktualny czas lokalny (`DateTime.Now`) | +| `IDateTimeProvider.UtcNow` | Właściwość | Aktualny czas UTC (`DateTime.UtcNow`) | +| `IDateTimeProvider.Today` | Właściwość | Aktualna data lokalna (`DateTime.Today`) | +| `IDateTimeProvider.UtcToday` | Właściwość | Aktualna data UTC (`DateTime.UtcNow.Date`) | +| `IDateTimeProvider.TimeOfDay` | Właściwość | Pora dnia (lokalnie) jako `TimeSpan` | +| `IDateTimeProvider.UtcTimeOfDaty` | Właściwość | Pora dnia UTC jako `TimeSpan` | + +--- + +## 🤖 AI Agent Prompt + +```markdown +## TailoredApps.Shared.DateTime — Instrukcja dla agenta AI + +Używasz biblioteki TailoredApps.Shared.DateTime w projekcie .NET. + +### Rejestracja +```csharp +// Program.cs +builder.Services.AddSingleton(); +``` + +### Użycie +- Nigdy nie używaj `DateTime.Now` ani `DateTime.UtcNow` bezpośrednio w kodzie produkcyjnym +- Wstrzykuj `IDateTimeProvider` przez konstruktor +- Używaj `provider.UtcNow` dla timestampów w bazie danych +- Używaj `provider.Now` tylko gdy potrzebujesz czasu lokalnego (np. do wyświetlania) + +```csharp +// ✅ Poprawnie +public class MyService +{ + private readonly IDateTimeProvider _dateTime; + public MyService(IDateTimeProvider dateTime) => _dateTime = dateTime; + public DateTime GetExpiry() => _dateTime.UtcNow.AddHours(1); +} + +// ❌ Niepoprawnie +public class MyService +{ + public DateTime GetExpiry() => DateTime.UtcNow.AddHours(1); // nie testowalny! +} +``` + +### Zasady +- Zawsze używaj `IDateTimeProvider` zamiast `System.DateTime` bezpośrednio +- W testach mockuj interfejs, aby zwracał stały punkt w czasie +- Preferuj `UtcNow`/`UtcToday` dla wartości zapisywanych w bazie danych +``` diff --git a/docs-en/Libraries/Email/Models.md b/docs-en/Libraries/Email/Models.md new file mode 100644 index 0000000..62c8fe8 --- /dev/null +++ b/docs-en/Libraries/Email/Models.md @@ -0,0 +1,91 @@ +# TailoredApps.Shared.Email.Models + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.Email.Models)](https://www.nuget.org/packages/TailoredApps.Shared.Email.Models/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +--- + +## Description + +A lightweight package containing only the `MailMessage` data model — a representation of an email message. Separating the model into its own package allows other libraries (e.g. `TailoredApps.Shared.Email.Office365`) to depend only on the model without pulling in the full SMTP implementation. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.Email.Models +``` + +--- + +## Przykład użycia + +```csharp +using TailoredApps.Shared.Email.Models; + +// Przykład: wyświetlenie listy odebranych wiadomości +ICollection messages = await emailProvider.GetMail( + folder: "Inbox", + sender: "boss@company.com", + fromLast: TimeSpan.FromDays(7) +); + +foreach (var msg in messages) +{ + Console.WriteLine($"[{msg.Date:yyyy-MM-dd}] Od: {msg.Sender}"); + Console.WriteLine($" Temat: {msg.Topic}"); + Console.WriteLine($" Do: {msg.Recipent}"); + + if (!string.IsNullOrEmpty(msg.HtmlBody)) + Console.WriteLine($" (HTML body, {msg.HtmlBody.Length} znaków)"); + + if (msg.Attachements?.Count > 0) + Console.WriteLine($" Załączniki: {string.Join(", ", msg.Attachements.Keys)}"); +} +``` + +--- + +## API Reference + +### Klasa `MailMessage` + +| Właściwość | Typ | Opis | +|------------|-----|------| +| `Topic` | `string` | Temat wiadomości | +| `Sender` | `string` | Adres nadawcy | +| `Recipent` | `string` | Adres odbiorcy | +| `Copy` | `string` | Adres CC (kopia) | +| `Body` | `string` | Treść tekstowa (plain-text) | +| `HtmlBody` | `string` | Treść HTML | +| `Attachements` | `Dictionary` | Załączniki: nazwa pliku → zawartość Base64 | +| `Date` | `DateTimeOffset` | Data i czas wysłania wiadomości | + +--- + +## 🤖 AI Agent Prompt + +```markdown +## TailoredApps.Shared.Email.Models — Instrukcja dla agenta AI + +Używasz modelu `MailMessage` z biblioteki TailoredApps.Shared.Email.Models. + +### Model MailMessage +```csharp +// Właściwości: +msg.Topic // temat +msg.Sender // nadawca +msg.Recipent // odbiorca +msg.Copy // CC +msg.Body // treść plain-text +msg.HtmlBody // treść HTML +msg.Attachements // Dictionary — Base64 załączniki +msg.Date // DateTimeOffset — data wysłania +``` + +### Zasady +- Model jest używany jako zwracana wartość przez IEmailProvider.GetMail() +- Załączniki przechowywane jako Base64 — dekoduj przez Convert.FromBase64String() gdy potrzebujesz byte[] +- Właściwość Recipent (nie Recipient) — literówka w API, nie zmieniaj +``` diff --git a/docs-en/Libraries/Email/Office365.md b/docs-en/Libraries/Email/Office365.md new file mode 100644 index 0000000..d90af5e --- /dev/null +++ b/docs-en/Libraries/Email/Office365.md @@ -0,0 +1,151 @@ +# TailoredApps.Shared.Email.Office365 + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.Email.Office365)](https://www.nuget.org/packages/TailoredApps.Shared.Email.Office365/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +--- + +## Description + +An `IEmailProvider` implementation that reads email from Microsoft 365 using IMAP with OAuth2 authentication (client credentials flow). The library authenticates with Azure AD as a confidential client application — supporting both client secret and certificate authentication. + +This allows you to receive messages from an Office 365 mailbox without storing a user password. The library automatically handles token acquisition and caching via Microsoft Identity. + +!!! warning "SendMail — not implemented" + The `SendMail` method throws `NotImplementedException`. This library supports **reading only** via IMAP. For sending, use `SmtpEmailProvider` or Microsoft Graph integration. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.Email.Office365 +``` + +--- + +## Rejestracja w DI + +```csharp +// Program.cs +using TailoredApps.Shared.Email.Office365; + +builder.Services.RegisterOffice365Provider(); +``` + +### Konfiguracja `appsettings.json` + +```json +{ + "Mail": { + "Providers": { + "Office365": { + "Instance": "https://login.microsoftonline.com/{0}", + "ApiUrl": "https://graph.microsoft.com/", + "Tenant": "your-tenant-id-or-domain.onmicrosoft.com", + "ClientId": "your-application-client-id", + "MailBox": "shared-mailbox@yourdomain.com", + "ClientSecret": "your-client-secret" + } + } + } +} +``` + +### Konfiguracja Azure AD + +Aplikacja w Azure AD potrzebuje uprawnień **aplikacyjnych** (nie delegowanych): + +- `IMAP.AccessAsApp` — dostęp IMAP jako aplikacja +- Administracja: `New-ServicePrincipalAllowedToUseApp` dla konkretnej skrzynki + +--- + +## Przykład użycia + +```csharp +public class MailboxMonitorService +{ + private readonly IEmailProvider _emailProvider; + + public MailboxMonitorService(IEmailProvider emailProvider) + { + _emailProvider = emailProvider; + } + + public async Task> GetNewOrdersAsync() + { + // Pobierz wiadomości z ostatnich 24 godzin od konkretnego nadawcy + var messages = await _emailProvider.GetMail( + folder: "Orders", + sender: "orders@partner.com", + fromLast: TimeSpan.FromHours(24) + ); + + return messages; + } + + public async Task> GetUnreadFromInboxAsync() + { + // Pobierz wszystkie wiadomości ze skrzynki odbiorczej + return await _emailProvider.GetMail(); + } +} +``` + +--- + +## API Reference + +| Typ | Rodzaj | Opis | +|-----|--------|------| +| `Office365EmailProvider` | Klasa | Implementacja `IEmailProvider` przez IMAP + OAuth2 | +| `AuthenticationConfig` | Klasa | Konfiguracja Azure AD (tenant, clientId, secret/cert) | +| `AuthenticationConfig.Instance` | Właściwość | URL instancji AAD (domyślnie Azure Public) | +| `AuthenticationConfig.Tenant` | Właściwość | Tenant ID lub domena | +| `AuthenticationConfig.ClientId` | Właściwość | Application ID w Azure AD | +| `AuthenticationConfig.MailBox` | Właściwość | Adres skrzynki do obsługi | +| `AuthenticationConfig.ClientSecret` | Właściwość | Sekret klienta (alternatywa dla certyfikatu) | +| `AuthenticationConfig.Certificate` | Właściwość | Certyfikat (`CertificateDescription`) | +| `Office365EmailProviderExtensions.RegisterOffice365Provider` | Metoda ext. | Rejestruje provider w DI | + +--- + +## 🤖 AI Agent Prompt + +```markdown +## TailoredApps.Shared.Email.Office365 — Instrukcja dla agenta AI + +Używasz biblioteki TailoredApps.Shared.Email.Office365 w projekcie .NET. + +### Rejestracja +```csharp +builder.Services.RegisterOffice365Provider(); +``` + +### appsettings.json +```json +"Mail": { "Providers": { "Office365": { + "Tenant": "tenant-id", + "ClientId": "app-client-id", + "MailBox": "mailbox@domain.com", + "ClientSecret": "secret" +}}} +``` + +### Użycie +```csharp +// Tylko odczyt — SendMail rzuca NotImplementedException! +var messages = await _emailProvider.GetMail( + folder: "Inbox", + sender: "filter@domain.com", + fromLast: TimeSpan.FromHours(24) +); +``` + +### Zasady +- SendMail() nie jest zaimplementowane — użyj SmtpEmailProvider do wysyłki +- Wymaga uprawnień aplikacyjnych IMAP.AccessAsApp w Azure AD +- Tokeny OAuth2 są automatycznie cache'owane przez MSAL +- Sekcja konfiguracji: "Mail:Providers:Office365" +``` diff --git a/docs-en/Libraries/Email/index.md b/docs-en/Libraries/Email/index.md new file mode 100644 index 0000000..e0cbf4b --- /dev/null +++ b/docs-en/Libraries/Email/index.md @@ -0,0 +1,208 @@ +# TailoredApps.Shared.Email + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.Email)](https://www.nuget.org/packages/TailoredApps.Shared.Email/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +--- + +## Description + +This library provides a complete abstraction for sending email messages in .NET applications. It is built around the `IEmailProvider` interface, which can be swapped depending on the environment — in production use `SmtpEmailProvider` (sends via SMTP), in local development use `EmailServiceToConsoleWriter` (prints to console without actual delivery). + +The library also includes a template-based email body building system (`IMailMessageBuilder`), supporting simple token substitution (`DefaultMessageBuilder`) or file-system templates with `{{token}}` placeholders (`TokenReplacingMailMessageBuilder`). + +Built-in safeguard against accidental spam in non-production environments: when `IsProd = false`, all emails are redirected to the `CatchAll` address instead of real recipients. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.Email +``` + +--- + +## Rejestracja w DI + +=== "SMTP (produkcja)" + + ```csharp + // Program.cs + using TailoredApps.Shared.Email; + + // Rejestracja SMTP provider + builder.Services.RegisterSmtpProvider(); + + // Opcjonalnie: rejestracja buildera szablonów + builder.Services.AddTransient(); + builder.Services.Configure(options => + { + options.Location = Path.Combine(builder.Environment.ContentRootPath, "EmailTemplates"); + options.FileExtension = "html"; + }); + ``` + +=== "Konsola (development)" + + ```csharp + // Program.cs + builder.Services.RegisterConsoleProvider(); + ``` + +### Konfiguracja `appsettings.json` + +```json +{ + "Mail": { + "Providers": { + "Smtp": { + "Host": "smtp.example.com", + "Port": 587, + "UserName": "noreply@example.com", + "Password": "secret", + "From": "noreply@example.com", + "EnableSsl": true, + "IsProd": true, + "CatchAll": "dev@example.com" + } + } + } +} +``` + +--- + +## Przykład użycia + +```csharp +public class NotificationService +{ + private readonly IEmailProvider _emailProvider; + private readonly IMailMessageBuilder _messageBuilder; + + public NotificationService( + IEmailProvider emailProvider, + IMailMessageBuilder messageBuilder) + { + _emailProvider = emailProvider; + _messageBuilder = messageBuilder; + } + + public async Task SendWelcomeEmailAsync(string recipientEmail, string userName) + { + var body = _messageBuilder.Build( + templateKey: "welcome.html", + variables: new Dictionary + { + { "UserName", userName }, + { "AppUrl", "https://myapp.example.com" } + }, + templates: null // załaduje z pliku, jeśli skonfigurowano Location + ); + + var messageId = await _emailProvider.SendMail( + recipnet: recipientEmail, + topic: "Witaj w MyApp!", + messageBody: body, + attachments: null + ); + + Console.WriteLine($"Email sent, MessageId: {messageId}"); + } + + public async Task SendInvoiceAsync( + string recipientEmail, + string subject, + string htmlBody, + byte[] pdfBytes) + { + await _emailProvider.SendMail( + recipnet: recipientEmail, + topic: subject, + messageBody: htmlBody, + attachments: new Dictionary + { + { "faktura.pdf", pdfBytes } + } + ); + } +} +``` + +### Szablon e-mail (welcome.html) + +```html + + + +

Witaj, {{UserName}}!

+

Twoje konto zostało założone. Kliknij tutaj by się zalogować.

+ + +``` + +--- + +## API Reference + +| Typ | Rodzaj | Opis | +|-----|--------|------| +| `IEmailProvider` | Interfejs | Główny kontrakt: `SendMail`, `GetMail` | +| `SmtpEmailProvider` | Klasa | Wysyłka przez SMTP; opcje z `SmtpEmailServiceOptions` | +| `EmailServiceToConsoleWriter` | Klasa | Wypisuje dane emaila do konsoli (dev/test) | +| `SmtpEmailServiceOptions` | Klasa | Konfiguracja SMTP: Host, Port, UserName, Password, From, IsProd, CatchAll | +| `IMailMessageBuilder` | Interfejs | Kontrakt: `Build(templateKey, variables, templates)` | +| `DefaultMessageBuilder` | Klasa | Podstawia tokeny w słowniku szablonów | +| `TokenReplacingMailMessageBuilder` | Klasa | Ładuje szablony z systemu plików; placeholdery `{{token}}` | +| `TokenReplacingMailMessageBuilderOptions` | Klasa | `Location` (ścieżka do katalogu szablonów), `FileExtension` | +| `SmtpEmailProviderExtensions.RegisterSmtpProvider` | Metoda ext. | Rejestruje `SmtpEmailProvider` w DI | +| `SmtpEmailProviderExtensions.RegisterConsoleProvider` | Metoda ext. | Rejestruje `EmailServiceToConsoleWriter` w DI | + +--- + +## 🤖 AI Agent Prompt + +```markdown +## TailoredApps.Shared.Email — Instrukcja dla agenta AI + +Używasz biblioteki TailoredApps.Shared.Email w projekcie .NET. + +### Rejestracja +```csharp +// Produkcja (SMTP): +builder.Services.RegisterSmtpProvider(); + +// Development (konsola): +builder.Services.RegisterConsoleProvider(); + +// Builder szablonów (opcjonalnie): +builder.Services.AddTransient(); +builder.Services.Configure(o => { + o.Location = "EmailTemplates/"; + o.FileExtension = "html"; +}); +``` + +### appsettings.json +```json +"Mail": { "Providers": { "Smtp": { + "Host": "smtp.host.com", "Port": 587, "UserName": "user", + "Password": "pass", "From": "no-reply@app.com", + "EnableSsl": true, "IsProd": true, "CatchAll": "dev@app.com" +}}} +``` + +### Użycie +```csharp +// Wstrzyknij IEmailProvider + IMailMessageBuilder +var body = _builder.Build("template.html", variables, null); +await _emailProvider.SendMail(email, subject, body, attachments); +``` + +### Zasady +- Gdy IsProd=false, wszystkie emaile trafiają na CatchAll — nigdy do prawdziwych odbiorców +- Do testów wstrzyknij IEmailProvider jako mock lub użyj RegisterConsoleProvider +- Placeholdery w szablonach TokenReplacing: {{NazwaTokena}} +- Załączniki: słownik fileName → byte[] +``` diff --git a/docs-en/Libraries/EntityFramework/UnitOfWork.WebApiCore.md b/docs-en/Libraries/EntityFramework/UnitOfWork.WebApiCore.md new file mode 100644 index 0000000..068a402 --- /dev/null +++ b/docs-en/Libraries/EntityFramework/UnitOfWork.WebApiCore.md @@ -0,0 +1,135 @@ +# TailoredApps.Shared.EntityFramework.UnitOfWork.WebApiCore + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.EntityFramework.UnitOfWork.WebApiCore)](https://www.nuget.org/packages/TailoredApps.Shared.EntityFramework.UnitOfWork.WebApiCore/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +--- + +## Description + +ASP.NET Core Web API integration for Unit of Work — eliminates the boilerplate of manually committing and rolling back transactions in every controller. The library provides `TransactionFilterAttribute` — a global ASP.NET Core action filter that automatically: + +- **Opens a transaction** before the controller action executes +- **Commits** on successful completion +- **Rolls back** on exception + +Optionally decorate an action with `[TransactionIsolationLevel(IsolationLevel.Serializable)]` to set a specific isolation level for that endpoint. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.EntityFramework.UnitOfWork.WebApiCore +``` + +--- + +## Rejestracja w DI + +```csharp +// Program.cs +using TailoredApps.Shared.EntityFramework.UnitOfWork.WebApiCore; + +// 1. Rejestracja UoW + TransactionFilterAttribute +builder.Services + .AddUnitOfWorkForWebApi(); + +// 2. Rejestracja jako globalny filter +builder.Services.AddControllers(options => +{ + options.Filters.AddUnitOfWorkTransactionAttribute(); +}); +``` + +--- + +## Przykład użycia + +### Kontroler — transakcja zarządzana automatycznie + +```csharp +[ApiController] +[Route("api/[controller]")] +public class OrdersController : ControllerBase +{ + private readonly IUnitOfWork _uow; + + public OrdersController(IUnitOfWork uow) + { + _uow = uow; + } + + [HttpPost] + public async Task CreateOrder([FromBody] CreateOrderDto dto) + { + // Nie musisz ręcznie commitować — TransactionFilter zrobi to po zakończeniu akcji + var order = new Order { CustomerId = dto.CustomerId, Amount = dto.Amount }; + _uow.DataProvider.Orders.Add(order); + await _uow.SaveChangesAsync(); + + return CreatedAtAction(nameof(GetOrder), new { id = order.Id }, order); + } + + // Endpoint z wyższym poziomem izolacji — zapobiega phantom reads + [HttpPost("transfer")] + [TransactionIsolationLevel(System.Data.IsolationLevel.Serializable)] + public async Task TransferFunds([FromBody] TransferDto dto) + { + var from = await _uow.DataProvider.Accounts.FindAsync(dto.FromId); + var to = await _uow.DataProvider.Accounts.FindAsync(dto.ToId); + + from.Balance -= dto.Amount; + to.Balance += dto.Amount; + + await _uow.SaveChangesAsync(); + return Ok(); + } +} +``` + +Jeśli akcja rzuci wyjątek, `TransactionFilterAttribute` automatycznie wywoła `RollbackTransaction()`. + +--- + +## API Reference + +| Typ | Rodzaj | Opis | +|-----|--------|------| +| `TransactionFilterAttribute` | Action Filter | Automatyczne commit/rollback transakcji dla każdej akcji | +| `TransactionIsolationLevelAttribute` | Atrybut | Ustawia poziom izolacji dla konkretnej akcji lub kontrolera | +| `UnitOfWorkConfiguration.AddUnitOfWorkForWebApi` | Metoda ext. | Rejestruje UoW + filter w DI | +| `UnitOfWorkConfiguration.AddUnitOfWorkTransactionAttribute` | Metoda ext. | Dodaje `TransactionFilterAttribute` jako globalny filter | + +--- + +## 🤖 AI Agent Prompt + +```markdown +## TailoredApps.Shared.EntityFramework.UnitOfWork.WebApiCore — Instrukcja dla agenta AI + +Używasz automatycznego zarządzania transakcjami przez TransactionFilterAttribute w ASP.NET Core. + +### Rejestracja +```csharp +// Program.cs +builder.Services.AddUnitOfWorkForWebApi(); +builder.Services.AddControllers(o => o.Filters.AddUnitOfWorkTransactionAttribute()); +``` + +### Zachowanie +- Każda akcja kontrolera jest automatycznie opakowana w transakcję +- Sukces → CommitTransaction() +- Wyjątek → RollbackTransaction() + +### Poziom izolacji per akcja +```csharp +[TransactionIsolationLevel(IsolationLevel.Serializable)] +public async Task CriticalOperation() { ... } +``` + +### Zasady +- Nie wywołuj ręcznie CommitTransaction/RollbackTransaction w kontrolerach — filter to robi +- TransactionIsolationLevelAttribute można stosować na klasie kontrolera lub na metodzie +- Domyślny poziom izolacji pochodzi z konfiguracji UoW +``` diff --git a/docs-en/Libraries/EntityFramework/index.md b/docs-en/Libraries/EntityFramework/index.md new file mode 100644 index 0000000..f1ac12b --- /dev/null +++ b/docs-en/Libraries/EntityFramework/index.md @@ -0,0 +1,193 @@ +# TailoredApps.Shared.EntityFramework + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.EntityFramework)](https://www.nuget.org/packages/TailoredApps.Shared.EntityFramework/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +--- + +## Description + +This library provides a complete implementation of the **Unit of Work** pattern on top of Entity Framework Core. It solves the problem of uncontrolled transaction management in multi-layer applications — instead of calling `SaveChanges()` directly in each repository, you have a single control point (`IUnitOfWork`) managing the transaction lifecycle. + +Key capabilities: +- **Transactions** with configurable isolation levels +- **Auditing** — automatic entity change tracking (who changed what) +- **Hooks** — `IHook` to execute code before/after `SaveChanges` or transaction commit/rollback +- **InMemory** provider support for testing + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.EntityFramework +``` + +--- + +## Rejestracja w DI + +```csharp +// Program.cs +using TailoredApps.Shared.EntityFramework; + +// Podstawowa rejestracja +builder.Services.AddUnitOfWork() + .WithAudit(options => + { + options.IgnoreProperty("RowVersion"); + }); +``` + +Gdzie `IApplicationDbContext` to interfejs Twojego DbContext, a `ApplicationDbContext` — implementacja dziedzicząca po `DbContext`. + +--- + +## Przykład użycia + +### Definicja kontekstu + +```csharp +public interface IApplicationDbContext +{ + DbSet Orders { get; } + DbSet Customers { get; } +} + +public class ApplicationDbContext : DbContext, IApplicationDbContext +{ + public DbSet Orders { get; set; } + public DbSet Customers { get; set; } + + public ApplicationDbContext(DbContextOptions options) + : base(options) { } +} +``` + +### Użycie w serwisie + +```csharp +public class OrderService +{ + private readonly IUnitOfWork _uow; + + public OrderService(IUnitOfWork uow) + { + _uow = uow; + } + + public async Task CreateOrderAsync(CreateOrderRequest request) + { + var order = new Order + { + CustomerId = request.CustomerId, + TotalAmount = request.TotalAmount, + Status = OrderStatus.Pending + }; + + _uow.DataProvider.Orders.Add(order); + await _uow.SaveChangesAsync(); + + return order; + } + + public async Task TransferFundsAsync(int fromId, int toId, decimal amount) + { + // Ręczna kontrola transakcji z poziomem izolacji + _uow.SetIsolationLevel(System.Data.IsolationLevel.Serializable); + + try + { + var from = await _uow.DataProvider.Accounts.FindAsync(fromId); + var to = await _uow.DataProvider.Accounts.FindAsync(toId); + + from.Balance -= amount; + to.Balance += amount; + + await _uow.SaveChangesAsync(); + _uow.CommitTransaction(); + } + catch + { + _uow.RollbackTransaction(); + throw; + } + } +} +``` + +### Hook — przykład logowania po zapisie + +```csharp +public class AuditLogHook : IHook +{ + private readonly ILogger _logger; + + public AuditLogHook(ILogger logger) => _logger = logger; + + public Task PostSaveChangesAsync(IEnumerable changes, CancellationToken ct) + { + foreach (var change in changes) + _logger.LogInformation("Entity {Type} {Id}: {State}", + change.EntityType, change.EntityId, change.State); + return Task.CompletedTask; + } +} +``` + +--- + +## API Reference + +| Typ | Rodzaj | Opis | +|-----|--------|------| +| `IUnitOfWork` | Interfejs | Zarządzanie transakcją: `SaveChanges`, `CommitTransaction`, `RollbackTransaction` | +| `IUnitOfWork` | Interfejs | Rozszerza `IUnitOfWork` o `DataProvider` (dostęp do DbContext) | +| `IUnitOfWorkContext` | Interfejs | Niskopoziomowe operacje: `BeginTransaction`, `SaveChanges`, `DiscardChanges` | +| `UnitOfWorkContext` | Klasa | Implementacja EF Core `IUnitOfWorkContext` | +| `ITransaction` | Interfejs | Transakcja: `Commit()`, `Rollback()`, `Dispose()` | +| `IHook` | Interfejs | Marker interface dla hooków cyklu życia UoW | +| `IHooksManager` | Interfejs | Zarządza kolekcją hooków i ich wykonywaniem | +| `IAuditSettings` | Interfejs | Konfiguracja audytingu (ignorowane właściwości itp.) | +| `IEntityChangesAuditor` | Interfejs | Zbiera i zapisuje zmiany encji | +| `EntityChange` | Klasa | Opis zmiany: typ encji, ID, stary/nowy stan | +| `AuditEntityState` | Enum | Added, Modified, Deleted | + +--- + +## 🤖 AI Agent Prompt + +```markdown +## TailoredApps.Shared.EntityFramework — Instrukcja dla agenta AI + +Używasz biblioteki TailoredApps.Shared.EntityFramework (Unit of Work pattern na EF Core). + +### Rejestracja +```csharp +builder.Services.AddUnitOfWork(); +``` + +### Użycie +```csharp +// Wstrzyknij IUnitOfWork +_uow.DataProvider.Orders.Add(order); +await _uow.SaveChangesAsync(); + +// Transakcja manualna +_uow.SetIsolationLevel(IsolationLevel.ReadCommitted); +try { + // operacje... + await _uow.SaveChangesAsync(); + _uow.CommitTransaction(); +} catch { + _uow.RollbackTransaction(); + throw; +} +``` + +### Zasady +- Nigdy nie wywołuj DbContext.SaveChanges() bezpośrednio — używaj _uow.SaveChangesAsync() +- Domyślnie transakcja jest otwierana przy pierwszym SaveChanges i commitowana automatycznie przez TransactionFilter (jeśli używasz WebApiCore) +- Dla testów: użyj InMemory provider — UnitOfWorkContext automatycznie to obsługuje +- HasOpenTransaction sprawdza, czy jest otwarta transakcja +``` diff --git a/docs-en/Libraries/ExceptionHandling/index.md b/docs-en/Libraries/ExceptionHandling/index.md new file mode 100644 index 0000000..9df1392 --- /dev/null +++ b/docs-en/Libraries/ExceptionHandling/index.md @@ -0,0 +1,169 @@ +# TailoredApps.Shared.ExceptionHandling + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.ExceptionHandling)](https://www.nuget.org/packages/TailoredApps.Shared.ExceptionHandling/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +--- + +## Description + +This library standardizes exception handling in ASP.NET Core Web API applications. It solves the problem of inconsistent error responses — instead of raw stack traces or random JSON formats, every error is converted to a unified `ExceptionOrValidationError` structure. + +Provides two mechanisms: + +- **Middleware** (`ConfigureExceptionHandler`) — global handler intercepting exceptions for the entire application +- **Action Filter** (`HandleExceptionAttribute`) — decorative approach at controller/action level + +You can define your own `IExceptionHandlingProvider` that maps specific exception types to HTTP codes and error messages. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.ExceptionHandling +``` + +--- + +## Rejestracja w DI + +```csharp +// Program.cs +using TailoredApps.Shared.ExceptionHandling.WebApiCore; + +// Rejestracja serwisu + własnego handlera +builder.Services + .AddExceptionHandlingForWebApi(); + +// Opcja A: Globalny filter MVC +builder.Services.AddControllers(options => +{ + options.Filters.AddExceptionHAndlingFilterAttribute(); +}); + +// Opcja B: Middleware (preferowane dla globalnej obsługi) +var app = builder.Build(); +app.ConfigureExceptionHandler(); +``` + +--- + +## Przykład użycia + +### Własny provider mapujący wyjątki + +```csharp +using TailoredApps.Shared.ExceptionHandling.Interfaces; +using TailoredApps.Shared.ExceptionHandling.Model; + +public class MyExceptionHandlingProvider : IExceptionHandlingProvider +{ + public ExceptionHandlingResponse Response(Exception exception) + { + return exception switch + { + ValidationException validationEx => new ExceptionHandlingResponse + { + ErrorCode = 422, + Errors = validationEx.Errors + .Select(e => new ExceptionOrValidationError(e.PropertyName, e.ErrorMessage)) + .ToList() + }, + + NotFoundException notFoundEx => new ExceptionHandlingResponse + { + ErrorCode = 404, + Errors = new[] { new ExceptionOrValidationError("", notFoundEx.Message) } + }, + + UnauthorizedException => new ExceptionHandlingResponse + { + ErrorCode = 401, + Errors = new[] { new ExceptionOrValidationError("", "Unauthorized") } + }, + + _ => new ExceptionHandlingResponse + { + ErrorCode = 500, + Errors = new[] { new ExceptionOrValidationError("", "Internal server error") } + } + }; + } +} +``` + +### Wynikowy format JSON odpowiedzi błędu + +```json +{ + "errors": [ + { + "field": "Email", + "message": "Email address is required" + }, + { + "message": "Name must not be empty" + } + ] +} +``` + +Właściwość `field` jest pomijana (serializacja `WhenWritingNull`) gdy błąd nie dotyczy konkretnego pola. + +--- + +## API Reference + +| Typ | Rodzaj | Opis | +|-----|--------|------| +| `ExceptionOrValidationError` | Klasa | Model błędu: `Field` (nullable) + `Message` | +| `IExceptionHandlingProvider` | Interfejs | Mapuje `Exception` na `ExceptionHandlingResponse` | +| `IExceptionHandlingService` | Interfejs | Wyższy poziom — wywołuje provider i zwraca response | +| `ExceptionHandlingConfiguration.AddExceptionHandlingForWebApi` | Metoda ext. | Rejestruje handler + filter w DI | +| `ExceptionMiddlewareExtensions.ConfigureExceptionHandler` | Metoda ext. | Dodaje middleware do pipeline | +| `HandleExceptionAttribute` | Action Filter | Dekoracyjna obsługa wyjątku na poziomie akcji | +| `ExceptionHandlingResponse` | Klasa | Wynikowy obiekt: `ErrorCode` (HTTP) + `Errors` (lista) | + +--- + +## 🤖 AI Agent Prompt + +```markdown +## TailoredApps.Shared.ExceptionHandling — Instrukcja dla agenta AI + +Używasz biblioteki TailoredApps.Shared.ExceptionHandling do standaryzacji błędów API. + +### Rejestracja +```csharp +builder.Services.AddExceptionHandlingForWebApi(); +// Opcja A - middleware (globalnie): +app.ConfigureExceptionHandler(); +// Opcja B - filter MVC: +builder.Services.AddControllers(o => o.Filters.AddExceptionHAndlingFilterAttribute()); +``` + +### Implementacja własnego providera +```csharp +public class MyProvider : IExceptionHandlingProvider +{ + public ExceptionHandlingResponse Response(Exception ex) => ex switch + { + NotFoundException => new() { ErrorCode = 404, Errors = [new("", ex.Message)] }, + ValidationException ve => new() { ErrorCode = 422, Errors = ve.Errors + .Select(e => new ExceptionOrValidationError(e.PropertyName, e.ErrorMessage)).ToList() }, + _ => new() { ErrorCode = 500, Errors = [new("", "Internal server error")] } + }; +} +``` + +### Format odpowiedzi +```json +{ "errors": [{ "field": "Email", "message": "Required" }, { "message": "Global error" }] } +``` + +### Zasady +- ExceptionOrValidationError z pustym field (string.Empty) → pole Field = null w JSON (pomijane) +- Zawsze implementuj własny IExceptionHandlingProvider mapujący domeny wyjątki +- Middleware ConfigureExceptionHandler obsługuje WSZYSTKIE wyjątki — filter tylko opakowane +``` diff --git a/docs-en/Libraries/MediatR/Caching.md b/docs-en/Libraries/MediatR/Caching.md new file mode 100644 index 0000000..885fdd6 --- /dev/null +++ b/docs-en/Libraries/MediatR/Caching.md @@ -0,0 +1,90 @@ +# TailoredApps.Shared.MediatR.Caching + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.MediatR.Caching)](https://www.nuget.org/packages/TailoredApps.Shared.MediatR.Caching/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +--- + +## Description + +A lightweight package defining `ICachableRequest` — a marker interface for MediatR requests whose results should be cached. A request implementing this interface provides a `GetCacheKey()` method that generates a unique cache key for that particular query instance. + +This is an alternative caching approach compared to `ICachePolicy` from `TailoredApps.Shared.MediatR` — simpler when cache key logic is straightforward and can live in the request itself. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.MediatR.Caching +``` + +--- + +## Przykład użycia + +```csharp +using TailoredApps.Shared.MediatR.Caching; +using MediatR; + +// Request z wbudowaną logiką cache key +public class GetUserProfileQuery : ICachableRequest +{ + public int UserId { get; set; } + public string Language { get; set; } = "pl"; + + // Unikalny klucz uwzględniający parametry zapytania + public string GetCacheKey() => $"user-profile:{UserId}:{Language}"; +} + +// Handler — standardowy MediatR +public class GetUserProfileQueryHandler : IRequestHandler +{ + private readonly IUserRepository _repo; + + public GetUserProfileQueryHandler(IUserRepository repo) => _repo = repo; + + public async Task Handle(GetUserProfileQuery request, CancellationToken ct) + { + var user = await _repo.GetByIdAsync(request.UserId, ct); + return user.ToProfileDto(request.Language); + } +} +``` + +!!! note "Integracja z CachingBehavior" + Aby cache'owanie działało, potrzebujesz `CachingBehavior` z pakietu `TailoredApps.Shared.MediatR` w pipeline. `ICachableRequest` to marker interface — sam w sobie nie uruchamia cache'owania. + +--- + +## API Reference + +| Typ | Rodzaj | Opis | +|-----|--------|------| +| `ICachableRequest` | Interfejs | Rozszerza `IRequest`; wymaga `GetCacheKey()` | +| `ICachableRequest.GetCacheKey()` | Metoda | Zwraca unikalny klucz cache dla tej instancji requestu | + +--- + +## 🤖 AI Agent Prompt + +```markdown +## TailoredApps.Shared.MediatR.Caching — Instrukcja dla agenta AI + +Używasz ICachableRequest do oznaczania requestów MediatR, których wyniki mają być cache'owane. + +### Użycie +```csharp +public class GetProductQuery : ICachableRequest +{ + public int Id { get; set; } + public string GetCacheKey() => $"product:{Id}"; +} +``` + +### Zasady +- ICachableRequest rozszerza IRequest +- GetCacheKey() musi zwracać unikalny klucz dla tej kombinacji parametrów +- Sam interfejs nie cache'uje — potrzebny CachingBehavior z TailoredApps.Shared.MediatR +- Alternatywa: ICachePolicy — klucz w osobnej klasie policy +``` diff --git a/docs-en/Libraries/MediatR/Email.md b/docs-en/Libraries/MediatR/Email.md new file mode 100644 index 0000000..a85667b --- /dev/null +++ b/docs-en/Libraries/MediatR/Email.md @@ -0,0 +1,151 @@ +# TailoredApps.Shared.MediatR.Email + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.MediatR.Email)](https://www.nuget.org/packages/TailoredApps.Shared.MediatR.Email/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +--- + +## Description + +MediatR pipeline integration for email sending. The library provides the `SendMail` command and its `SendMailCommandHandler`, making email sending a natural part of CQRS architecture — you can send an email via `_mediator.Send(new SendMail { ... })` without a direct dependency on `IEmailProvider`. + +This approach makes it easy to enrich the sending process with logging, retry, and auditing from the pipeline behaviors level, without modifying the handler. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.MediatR.Email +``` + +--- + +## Rejestracja w DI + +```csharp +// Program.cs +using TailoredApps.Shared.Email; +using TailoredApps.Shared.MediatR.Email.Handlers; +using TailoredApps.Shared.Email.MailMessageBuilder; + +// 1. Zarejestruj email provider (SMTP lub konsola) +builder.Services.RegisterSmtpProvider(); + +// 2. Zarejestruj mail message builder +builder.Services.AddTransient(); +builder.Services.Configure(o => +{ + o.Location = "EmailTemplates/"; + o.FileExtension = "html"; +}); + +// 3. Zarejestruj handler +builder.Services.AddTransient(); + +// 4. MediatR (jeśli jeszcze nie zarejestrowany) +builder.Services.AddMediatR(cfg => + cfg.RegisterServicesFromAssembly(typeof(Program).Assembly)); +``` + +--- + +## Przykład użycia + +```csharp +public class UserRegistrationService +{ + private readonly IMediator _mediator; + + public UserRegistrationService(IMediator mediator) => _mediator = mediator; + + public async Task RegisterUserAsync(string email, string userName) + { + // Logika rejestracji... + + // Wyślij email powitalny przez MediatR pipeline + var result = await _mediator.Send(new SendMail + { + Recipent = email, + Subject = "Witaj w naszym serwisie!", + Template = "welcome.html", + TemplateVariables = new Dictionary + { + { "UserName", userName }, + { "ActivationUrl", $"https://app.example.com/activate/{Guid.NewGuid()}" } + }, + Attachments = null + }); + + Console.WriteLine($"Email wysłany, MessageId: {result.MessageId}"); + } + + public async Task SendInvoiceAsync( + string email, + string invoiceNumber, + byte[] pdfContent) + { + await _mediator.Send(new SendMail + { + Recipent = email, + Subject = $"Faktura #{invoiceNumber}", + Template = "invoice.html", + TemplateVariables = new Dictionary + { + { "InvoiceNumber", invoiceNumber } + }, + Attachments = new Dictionary + { + { $"faktura_{invoiceNumber}.pdf", pdfContent } + } + }); + } +} +``` + +--- + +## API Reference + +| Typ | Rodzaj | Opis | +|-----|--------|------| +| `SendMail` | Command (`IRequest`) | Dane emaila: `Recipent`, `Subject`, `Template`, `TemplateVariables`, `Templates`, `Attachments` | +| `SendMailResponse` | Klasa | Wynik wysyłki: `MessageId` (provider-specific identifier) | +| `SendMailCommandHandler` | Handler | Buduje treść z szablonu i wysyła przez `IEmailProvider` | +| `ISendMailCommandHandler` | Interfejs | Kontrakt handlera | + +--- + +## 🤖 AI Agent Prompt + +```markdown +## TailoredApps.Shared.MediatR.Email — Instrukcja dla agenta AI + +Używasz TailoredApps.Shared.MediatR.Email do wysyłania emaili przez MediatR pipeline. + +### Rejestracja +```csharp +builder.Services.RegisterSmtpProvider(); // lub RegisterConsoleProvider() +builder.Services.AddTransient(); +builder.Services.AddTransient(); +``` + +### Wysyłanie emaila +```csharp +var result = await _mediator.Send(new SendMail +{ + Recipent = "user@example.com", + Subject = "Temat", + Template = "template.html", // nazwa pliku szablonu + TemplateVariables = new() { { "Name", "Jan" } }, + Attachments = null // lub Dictionary +}); +// result.MessageId — ID przypisany przez provider +``` + +### Zasady +- Template to klucz szablonu przekazywany do IMailMessageBuilder.Build() +- TemplateVariables zastępują {{token}} w szablonie +- Podaj Templates (Dictionary) gdy szablony są inline, nie z pliku +- SendMail rzuci jeśli template nie zostanie znaleziony w IMailMessageBuilder +``` diff --git a/docs-en/Libraries/MediatR/ML.md b/docs-en/Libraries/MediatR/ML.md new file mode 100644 index 0000000..a1023e2 --- /dev/null +++ b/docs-en/Libraries/MediatR/ML.md @@ -0,0 +1,162 @@ +# TailoredApps.Shared.MediatR.ML + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.MediatR.ML)](https://www.nuget.org/packages/TailoredApps.Shared.MediatR.ML/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +--- + +## Description + +This library integrates **ML.NET** image classification with the MediatR pipeline. It provides ready-made commands (`ClassifyImage`, `TrainImageClassificationModel`) and their handlers, making image classification a first-class citizen in CQRS architecture. + +Under the hood, `ImageClassificationService` uses ML.NET with a `PredictionEnginePool` for efficient concurrent inference. The library also supports in-app model training — you can train a new model by providing a labeled image dataset. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.MediatR.ML +``` + +--- + +## Rejestracja w DI + +```csharp +// Program.cs +using TailoredApps.Shared.MediatR.ImageClassification.Infrastructure; + +builder.Services.AddPredictionEngine(config => +{ + config.AddImageClassificationModel(modelBuilder => + { + modelBuilder.AddFromFile("Models/image-classifier.zip"); + }); +}); + +// MediatR +builder.Services.AddMediatR(cfg => + cfg.RegisterServicesFromAssembly(typeof(Program).Assembly)); +``` + +### Konfiguracja `appsettings.json` + +```json +{ + "ML": { + "ImageClassification": { + "ModelPath": "Models/image-classifier.zip", + "LabelsPath": "Models/labels.txt" + } + } +} +``` + +--- + +## Przykład użycia + +### Klasyfikacja obrazu + +```csharp +public class ImageAnalysisController : ControllerBase +{ + private readonly IMediator _mediator; + + public ImageAnalysisController(IMediator mediator) => _mediator = mediator; + + [HttpPost("classify")] + public async Task ClassifyImage(IFormFile imageFile) + { + using var ms = new MemoryStream(); + await imageFile.CopyToAsync(ms); + + var result = await _mediator.Send(new ClassifyImage + { + FileByteArray = ms.ToArray(), + FileName = imageFile.FileName + }); + + return Ok(new + { + result.FileName, + result.PredictedLabel, + result.PredictedScore, + Confidence = $"{result.PredictedScore:P2}" + }); + } +} +``` + +### Trening modelu + +```csharp +var trainingResult = await _mediator.Send(new TrainImageClassificationModel +{ + TrainingSetFolder = "/data/training-images", // katalog z podfolderami per klasa + ModelDestinationPath = "Models/new-model.zip" +}); + +Console.WriteLine($"Trained! Labels: {string.Join(", ", trainingResult.Labels)}"); +Console.WriteLine(trainingResult.EvaluationInfo); +``` + +--- + +## API Reference + +| Typ | Rodzaj | Opis | +|-----|--------|------| +| `ClassifyImage` | Command | Dane obrazu do klasyfikacji: `FileByteArray`, `FileName` | +| `ClassifyImageResponse` | Klasa | Wynik: `FileName`, `PredictedLabel`, `PredictedScore` | +| `TrainImageClassificationModel` | Command | Parametry treningu: `TrainingSetFolder`, `ModelDestinationPath` | +| `TrainImageClassificationModelResponse` | Klasa | Wynik treningu: `Labels[]`, `EvaluationInfo` | +| `IImageClassificationService` | Interfejs | `Predict(byte[], fileName)`, `Train(images, folder, dest)`, `GetModelInfo()` | +| `IPredictionEnginePoolAdapter` | Interfejs | Abstrakcja nad ML.NET `PredictionEnginePool` | +| `ModelInfo` | Klasa | Metadane modelu: nazwa, checksum, wersja, etykiety | +| `ImagePrediction` | Klasa | Wynik predykcji: etykieta, score, nazwa pliku | +| `InMemoryImageData` | Klasa | Dane wejściowe do modelu: obraz jako `byte[]` | +| `AddPredictionEngineExtension.AddPredictionEngine` | Metoda ext. | Rejestruje cały stack ML w DI | + +--- + +## 🤖 AI Agent Prompt + +```markdown +## TailoredApps.Shared.MediatR.ML — Instrukcja dla agenta AI + +Używasz TailoredApps.Shared.MediatR.ML do klasyfikacji obrazów przez ML.NET i MediatR. + +### Rejestracja +```csharp +builder.Services.AddPredictionEngine(config => + config.AddImageClassificationModel(b => b.AddFromFile("Models/model.zip"))); +``` + +### Klasyfikacja obrazu +```csharp +var result = await _mediator.Send(new ClassifyImage +{ + FileByteArray = imageBytes, + FileName = "photo.jpg" +}); +// result.PredictedLabel — przewidywana klasa +// result.PredictedScore — pewność (0-1) +``` + +### Trening modelu +```csharp +var result = await _mediator.Send(new TrainImageClassificationModel +{ + TrainingSetFolder = "/data/images", // podfoldery = nazwy klas + ModelDestinationPath = "Models/new.zip" +}); +``` + +### Zasady +- Model musi być plikiem ZIP (ML.NET format) +- Folder treningowy: każdy podfolder = jedna klasa, nazwa folderu = etykieta +- PredictionEnginePool jest thread-safe — bezpieczne współbieżne użycie +- PredictedScore ∈ [0,1] — im bliżej 1, tym wyższe zaufanie modelu +``` diff --git a/docs-en/Libraries/MediatR/PagedRequest.md b/docs-en/Libraries/MediatR/PagedRequest.md new file mode 100644 index 0000000..ac9a759 --- /dev/null +++ b/docs-en/Libraries/MediatR/PagedRequest.md @@ -0,0 +1,177 @@ +# TailoredApps.Shared.MediatR.PagedRequest + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.MediatR.PagedRequest)](https://www.nuget.org/packages/TailoredApps.Shared.MediatR.PagedRequest/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +--- + +## Description + +This library provides a base `PagedAndSortedRequest` class for MediatR requests that require pagination and sorting. It standardizes paging (`Page`, `Count`) and sorting (`SortField`, `SortDir`) parameters across all list queries in the application. + +The class is tightly integrated with `TailoredApps.Shared.Querying` — requires `TQuery` to inherit from `QueryBase` and `TResponse` to implement `IPagedResult`. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.MediatR.PagedRequest +``` + +--- + +## Przykład użycia + +### Definicja query filter, response i request + +```csharp +using TailoredApps.Shared.Querying; +using TailoredApps.Shared.MediatR.PagedRequest; + +// 1. Filter dziedziczy po QueryBase +public class ProductFilter : QueryBase +{ + public string NameContains { get; set; } + public decimal? MinPrice { get; set; } + public decimal? MaxPrice { get; set; } + public bool? InStock { get; set; } +} + +// 2. Response implementuje IPagedResult +public class ProductListResponse : IPagedResult +{ + public ICollection Results { get; set; } + public int Count { get; set; } +} + +// 3. Request dziedziczy PagedAndSortedRequest +public class GetProductsQuery + : PagedAndSortedRequest +{ + // Wszystkie parametry paginacji/sortowania są odziedziczone + // Można dodać własne właściwości: + public bool IncludeArchived { get; set; } = false; +} +``` + +### Handler + +```csharp +public class GetProductsQueryHandler + : IRequestHandler +{ + private readonly IProductRepository _repo; + + public GetProductsQueryHandler(IProductRepository repo) => _repo = repo; + + public async Task Handle( + GetProductsQuery request, + CancellationToken ct) + { + var query = _repo.AsQueryable(); + + // Aplikacja filtrów + if (!string.IsNullOrWhiteSpace(request.Filter?.NameContains)) + query = query.Where(p => p.Name.Contains(request.Filter.NameContains)); + + if (request.Filter?.MinPrice.HasValue == true) + query = query.Where(p => p.Price >= request.Filter.MinPrice.Value); + + // Sortowanie + if (request.IsSortingSpecified) + { + query = request.SortDir == SortDirection.Asc + ? query.OrderBy(request.SortField) + : query.OrderByDescending(request.SortField); + } + + var totalCount = await query.CountAsync(ct); + + // Paginacja + if (request.IsPagingSpecified) + query = query.Skip((request.Page!.Value - 1) * request.Count!.Value) + .Take(request.Count.Value); + + var items = await query.Select(p => p.ToDto()).ToListAsync(ct); + + return new ProductListResponse + { + Results = items, + Count = totalCount + }; + } +} +``` + +### Wywołanie z kontrolera + +```csharp +[HttpGet] +public async Task GetProducts([FromQuery] GetProductsQuery query) +{ + // GET /api/products?page=1&count=20&sortField=Price&sortDir=Asc&filter.nameContains=shirt + var result = await _mediator.Send(query); + return Ok(result); +} +``` + +--- + +## API Reference + +| Typ | Rodzaj | Opis | +|-----|--------|------| +| `PagedAndSortedRequest` | Klasa bazowa | Bazowy request MediatR z paginacją i sortowaniem | +| `Page` | Właściwość `int?` | Numer strony (1-based) | +| `Count` | Właściwość `int?` | Liczba elementów na stronie | +| `IsPagingSpecified` | Właściwość `bool` | `true` gdy Page i Count mają wartość | +| `SortField` | Właściwość `string` | Nazwa pola do sortowania | +| `SortDir` | Właściwość `SortDirection?` | Kierunek sortowania (Asc/Desc) | +| `IsSortingSpecified` | Właściwość `bool` | `true` gdy SortField i SortDir są ustawione | +| `Filter` | Właściwość `TQuery` | Obiekt filtra dziedziczący po `QueryBase` | +| `IsSortBy(string)` | Metoda | Sprawdza czy sortowanie jest po danym polu (case-insensitive) | + +--- + +## 🤖 AI Agent Prompt + +```markdown +## TailoredApps.Shared.MediatR.PagedRequest — Instrukcja dla agenta AI + +Używasz PagedAndSortedRequest jako bazowej klasy dla stronicowanych requestów MediatR. + +### Definicja +```csharp +// Filter +public class MyFilter : QueryBase { public string Name { get; set; } } + +// Response +public class MyListResponse : IPagedResult +{ + public ICollection Results { get; set; } + public int Count { get; set; } +} + +// Request +public class GetMyItemsQuery : PagedAndSortedRequest { } +``` + +### Parametry URL (ASP.NET Core binding) +?page=1&count=20&sortField=Name&sortDir=Asc&filter.name=test + +### W handlerze +```csharp +if (request.IsPagingSpecified) + query = query.Skip((request.Page!.Value - 1) * request.Count!.Value).Take(request.Count.Value); + +if (request.IsSortingSpecified) + query = request.SortDir == SortDirection.Asc ? query.OrderBy(...) : query.OrderByDescending(...); +``` + +### Zasady +- TQuery musi dziedziczyć po QueryBase +- TResponse musi implementować IPagedResult +- IsPagingSpecified = Page i Count mają wartość — sprawdzaj przed Skip/Take +- IsSortBy("Name") — sprawdza case-insensitive +``` diff --git a/docs-en/Libraries/MediatR/index.md b/docs-en/Libraries/MediatR/index.md new file mode 100644 index 0000000..4103f6d --- /dev/null +++ b/docs-en/Libraries/MediatR/index.md @@ -0,0 +1,174 @@ +# TailoredApps.Shared.MediatR + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.MediatR)](https://www.nuget.org/packages/TailoredApps.Shared.MediatR/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +--- + +## Description + +This library provides a ready-made set of **pipeline behaviors** for MediatR that cover the most common enterprise application needs: logging, validation, caching, fallback, and retry. Instead of manually implementing these cross-cutting concerns in every handler, register them once via `PipelineRegistration` and they apply to all requests. + +Behaviors execute in order: **Logging → Validation → Caching → Fallback → Retry → Handler**. + +The library supports auto-discovery (via Scrutor) — cache policies, fallback handlers, and retry configurations are automatically scanned and registered from the specified assembly. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.MediatR +``` + +--- + +## Rejestracja w DI + +```csharp +// Program.cs +using TailoredApps.Shared.MediatR.DI; + +// Rejestracja MediatR +builder.Services.AddMediatR(cfg => + cfg.RegisterServicesFromAssembly(typeof(Program).Assembly)); + +// Rejestracja pipeline behaviors +var pipeline = new PipelineRegistration(builder.Services); +pipeline.RegisterPipelineBehaviors(); + +// Opcjonalnie: auto-discovery cache policies, fallback, retry z assembly +pipeline.RegisterPipelineBehaviors(typeof(Program).Assembly); +``` + +--- + +## Przykład użycia + +### Request + Handler + +```csharp +// Request +public class GetProductQuery : IRequest +{ + public int ProductId { get; set; } +} + +// Validator (automatycznie przechwycony przez ValidationBehavior) +public class GetProductQueryValidator : AbstractValidator +{ + public GetProductQueryValidator() + { + RuleFor(x => x.ProductId).GreaterThan(0); + } +} + +// Handler +public class GetProductQueryHandler : IRequestHandler +{ + private readonly IProductRepository _repo; + + public GetProductQueryHandler(IProductRepository repo) => _repo = repo; + + public async Task Handle(GetProductQuery request, CancellationToken ct) + { + var product = await _repo.GetByIdAsync(request.ProductId, ct); + return product?.ToDto() ?? throw new NotFoundException($"Product {request.ProductId} not found"); + } +} +``` + +### Cache Policy dla requestu + +```csharp +public class GetProductQueryCachePolicy : ICachePolicy +{ + public string GetCacheKey(GetProductQuery request) + => $"product:{request.ProductId}"; + + public TimeSpan? SlidingExpiration => TimeSpan.FromMinutes(5); + public TimeSpan? AbsoluteExpiration => null; + public TimeSpan? AbsoluteExpirationRelativeToNow => TimeSpan.FromHours(1); +} +``` + +### Fallback Handler + +```csharp +public class GetProductQueryFallback : IFallbackHandler +{ + public Task HandleFallbackAsync( + GetProductQuery request, + Exception exception, + CancellationToken ct) + { + // Zwróć cached/default wartość gdy handler rzuci + return Task.FromResult(ProductDto.Empty); + } +} +``` + +--- + +## API Reference + +| Typ | Rodzaj | Opis | +|-----|--------|------| +| `LoggingBehavior` | Pipeline Behavior | Loguje czas wykonania i wyjątki; correlation ID per request | +| `ValidationBehavior` | Pipeline Behavior | Wykonuje wszystkie `IValidator` (FluentValidation) | +| `CachingBehavior` | Pipeline Behavior | Cache'uje odpowiedź zgodnie z `ICachePolicy` | +| `FallbackBehavior` | Pipeline Behavior | Przy wyjątku wywołuje `IFallbackHandler` | +| `RetryBehavior` | Pipeline Behavior | Ponawia request zgodnie z `IRetryableRequest` | +| `PipelineRegistration` | Klasa | Rejestruje wszystkie behaviors + auto-discovery z assembly | +| `IPipelineRegistration` | Interfejs | Kontrakt PipelineRegistration | +| `ICachePolicy` | Interfejs | Konfiguracja cache: klucz, TTL, sliding/absolute expiration | +| `IFallbackHandler` | Interfejs | Handler fallbacku przy wyjątku | +| `IRetryableRequest` | Interfejs | Konfiguracja retry dla requestu | +| `ICache` | Interfejs | Abstrakcja cache (wstrzykiwana do CachingBehavior) | + +--- + +## 🤖 AI Agent Prompt + +```markdown +## TailoredApps.Shared.MediatR — Instrukcja dla agenta AI + +Używasz biblioteki TailoredApps.Shared.MediatR z pipeline behaviors w projekcie .NET. + +### Rejestracja +```csharp +builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly)); +var pipeline = new PipelineRegistration(builder.Services); +pipeline.RegisterPipelineBehaviors(); +pipeline.RegisterPipelineBehaviors(typeof(Program).Assembly); // auto-discovery +``` + +### Kolejność behaviors +Logging → Validation → Caching → Fallback → Retry → Handler + +### Walidacja (automatyczna) +```csharp +// Validator automatycznie przechwycony — rzuca ValidationException gdy błąd +public class MyQueryValidator : AbstractValidator +{ + public MyQueryValidator() { RuleFor(x => x.Id).GreaterThan(0); } +} +``` + +### Cache Policy +```csharp +public class MyCachePolicy : ICachePolicy +{ + public string GetCacheKey(MyQuery r) => $"my:{r.Id}"; + public TimeSpan? SlidingExpiration => TimeSpan.FromMinutes(5); + public TimeSpan? AbsoluteExpiration => null; + public TimeSpan? AbsoluteExpirationRelativeToNow => TimeSpan.FromHours(1); +} +``` + +### Zasady +- Wszystkie FluentValidation validators są automatycznie wykrywane przez DI +- Aby cache działał, zaimplementuj ICachePolicy i zarejestruj (auto-discovery) +- LoggingBehavior loguje na poziomie DEBUG — włącz odpowiedni log level +- Każdy request ma unikalne correlation ID w logach +``` diff --git a/docs-en/Libraries/Payments/Providers/Adyen.md b/docs-en/Libraries/Payments/Providers/Adyen.md new file mode 100644 index 0000000..38705ff --- /dev/null +++ b/docs-en/Libraries/Payments/Providers/Adyen.md @@ -0,0 +1,106 @@ +# TailoredApps.Shared.Payments.Provider.Adyen + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.Payments.Provider.Adyen)](https://www.nuget.org/packages/TailoredApps.Shared.Payments.Provider.Adyen/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +Integracja z **Adyen Checkout API**. Obsługuje płatności kartą, BLIK (PLN) i inne metody dostępne w Adyen, z pełną obsługą webhooków i weryfikacją HMAC. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.Payments.Provider.Adyen +``` + +--- + +## Rejestracja w DI + +```csharp +using TailoredApps.Shared.Payments.Provider.Adyen; + +builder.Services + .AddPayments() + .RegisterAdyenProvider(); +``` + +--- + +## Konfiguracja `appsettings.json` + +```json +{ + "Payments": { + "Providers": { + "Adyen": { + "ApiKey": "AQEyhmf...", + "MerchantAccount": "MyCompanyECOM", + "ClientKey": "test_...", + "ReturnUrl": "https://myapp.com/payment/return", + "NotificationHmacKey": "446a32...hex...", + "Environment": "test" + } + } + } +} +``` + +| Opcja | Opis | +|-------|------| +| `ApiKey` | Klucz API Adyen (header `X-API-Key`) | +| `MerchantAccount` | Identyfikator konta merchant | +| `ClientKey` | Klucz klienta Drop-in/Components (opcjonalny) | +| `ReturnUrl` | URL powrotu po płatności | +| `NotificationHmacKey` | Klucz HMAC (hex) do weryfikacji webhooków | +| `Environment` | `"test"` lub `"live"` | +| `CheckoutUrl` | Nadpisuje domyślny URL API (opcjonalne) | + +--- + +## Obsługiwane kanały płatności + +| Waluta | Kanały | +|--------|--------| +| PLN | `card` (Visa, Mastercard), `blik` | +| EUR | `card`, `sepa` | +| Inne | `card` | + +--- + +## Webhook + +Adyen wysyła powiadomienia na endpoint HTTP. Weryfikacja odbywa się przez HMAC SHA-256 na podstawie `NotificationHmacKey`. + +```csharp +[HttpPost("webhooks/adyen")] +public async Task AdyenWebhook([FromBody] JsonElement body) +{ + var result = await _payments.HandleWebhookAsync("Adyen", new PaymentWebhookRequest + { + Body = body.GetRawText(), + Headers = Request.Headers.ToDictionary(h => h.Key, h => h.Value.ToString()) + }); + return result == PaymentWebhookResult.Success ? Ok("[accepted]") : BadRequest(); +} +``` + +--- + +## 🤖 AI Agent Prompt + +```markdown +## Adyen Provider — Instrukcja dla agenta AI + +Provider key: "Adyen" + +Sekcja konfiguracji: "Payments:Providers:Adyen" + +Wymagane pola: ApiKey, MerchantAccount, ReturnUrl + +Webhook: HMAC-SHA256, klucz NotificationHmacKey + +Rejestracja: builder.Services.AddPayments().RegisterAdyenProvider(); + +Środowisko testowe: Environment = "test" (checkout-test.adyen.com) +``` diff --git a/docs-en/Libraries/Payments/Providers/CashBill.md b/docs-en/Libraries/Payments/Providers/CashBill.md new file mode 100644 index 0000000..8539243 --- /dev/null +++ b/docs-en/Libraries/Payments/Providers/CashBill.md @@ -0,0 +1,82 @@ +# TailoredApps.Shared.Payments.Provider.CashBill + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.Payments.Provider.CashBill)](https://www.nuget.org/packages/TailoredApps.Shared.Payments.Provider.CashBill/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +Integracja z **CashBill** — polskim operatorem płatności online obsługującym przelewy, karty i BLIK. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.Payments.Provider.CashBill +``` + +--- + +## Rejestracja w DI + +```csharp +using TailoredApps.Shared.Payments.Provider.CashBill; + +builder.Services + .AddPayments() + .RegisterCashBillProvider(); +``` + +--- + +## Konfiguracja `appsettings.json` + +```json +{ + "Payments": { + "Providers": { + "Cashbill": { + "ShopId": "MY_SHOP_ID", + "ShopSecretPhrase": "my_secret_phrase", + "ServiceUrl": "https://pay.cashbill.eu", + "ReturnUrl": "https://myapp.com/payment/return", + "NegativeReturnUrl": "https://myapp.com/payment/failed" + } + } + } +} +``` + +| Opcja | Opis | +|-------|------| +| `ShopId` | Identyfikator sklepu w CashBill | +| `ShopSecretPhrase` | Fraza sekretna do podpisywania żądań | +| `ServiceUrl` | URL API CashBill (domyślnie: `https://pay.cashbill.eu`) | +| `ReturnUrl` | URL powrotu po udanej płatności | +| `NegativeReturnUrl` | URL powrotu po nieudanej/anulowanej płatności | + +--- + +## Obsługiwane kanały + +CashBill zwraca dynamiczną listę kanałów pobraną z API dla danej waluty (PLN, EUR, USD itd.). Kanały obejmują przelew bankowy, karty, BLIK, e-portfele. + +--- + +## Webhook + +CashBill wysyła powiadomienia GET/POST na `NotifyUrl`. Podpis weryfikowany przez SHA-1 + sekret. + +--- + +## 🤖 AI Agent Prompt + +```markdown +## CashBill Provider — Instrukcja dla agenta AI + +Provider key: "CashBill" + +Sekcja konfiguracji: "Payments:Providers:Cashbill" (małe "b" w Cashbill!) + +Wymagane pola: ShopId, ShopSecretPhrase, ServiceUrl, ReturnUrl, NegativeReturnUrl + +Rejestracja: builder.Services.AddPayments().RegisterCashBillProvider(); +``` diff --git a/docs-en/Libraries/Payments/Providers/HotPay.md b/docs-en/Libraries/Payments/Providers/HotPay.md new file mode 100644 index 0000000..cba593d --- /dev/null +++ b/docs-en/Libraries/Payments/Providers/HotPay.md @@ -0,0 +1,80 @@ +# TailoredApps.Shared.Payments.Provider.HotPay + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.Payments.Provider.HotPay)](https://www.nuget.org/packages/TailoredApps.Shared.Payments.Provider.HotPay/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +Integracja z **HotPay** — polskim mikropłatnościowym operatorem płatności online. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.Payments.Provider.HotPay +``` + +--- + +## Rejestracja w DI + +```csharp +using TailoredApps.Shared.Payments.Provider.HotPay; + +builder.Services + .AddPayments() + .RegisterHotPayProvider(); +``` + +--- + +## Konfiguracja `appsettings.json` + +```json +{ + "Payments": { + "Providers": { + "HotPay": { + "SecretHash": "my_secret_hash", + "ServiceUrl": "https://platnosci.hotpay.pl", + "ReturnUrl": "https://myapp.com/payment/return", + "NotifyUrl": "https://myapp.com/webhooks/hotpay" + } + } + } +} +``` + +| Opcja | Opis | +|-------|------| +| `SecretHash` | Sekret do podpisywania żądań (SEKRET) | +| `ServiceUrl` | URL API HotPay | +| `ReturnUrl` | URL powrotu po płatności | +| `NotifyUrl` | URL do powiadomień o zmianie statusu | + +--- + +## Obsługiwane kanały + +HotPay obsługuje płatności online (PLN). Kanał domyślny: `hotpay`. + +--- + +## Webhook + +HotPay wysyła powiadomienie POST na `NotifyUrl`. Podpis weryfikowany przez SHA-256 z `SecretHash`. + +--- + +## 🤖 AI Agent Prompt + +```markdown +## HotPay Provider — Instrukcja dla agenta AI + +Provider key: "HotPay" + +Sekcja konfiguracji: "Payments:Providers:HotPay" + +Wymagane pola: SecretHash, ReturnUrl, NotifyUrl + +Rejestracja: builder.Services.AddPayments().RegisterHotPayProvider(); +``` diff --git a/docs-en/Libraries/Payments/Providers/PayNow.md b/docs-en/Libraries/Payments/Providers/PayNow.md new file mode 100644 index 0000000..b41eaaf --- /dev/null +++ b/docs-en/Libraries/Payments/Providers/PayNow.md @@ -0,0 +1,86 @@ +# TailoredApps.Shared.Payments.Provider.PayNow + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.Payments.Provider.PayNow)](https://www.nuget.org/packages/TailoredApps.Shared.Payments.Provider.PayNow/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +Integracja z **PayNow** — bramką płatności mBanku obsługującą BLIK, szybkie przelewy i karty płatnicze. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.Payments.Provider.PayNow +``` + +--- + +## Rejestracja w DI + +```csharp +using TailoredApps.Shared.Payments.Provider.PayNow; + +builder.Services + .AddPayments() + .RegisterPayNowProvider(); +``` + +--- + +## Konfiguracja `appsettings.json` + +```json +{ + "Payments": { + "Providers": { + "PayNow": { + "ApiKey": "my-api-key", + "SignatureKey": "my-signature-key", + "ServiceUrl": "https://api.paynow.pl", + "ReturnUrl": "https://myapp.com/payment/return", + "ContinueUrl": "https://myapp.com/payment/continue" + } + } + } +} +``` + +| Opcja | Opis | +|-------|------| +| `ApiKey` | Klucz API do autoryzacji żądań (header `Api-Key`) | +| `SignatureKey` | Klucz do podpisywania żądań i weryfikacji webhooków | +| `ServiceUrl` | URL API PayNow (sandbox: `https://api.sandbox.paynow.pl`) | +| `ReturnUrl` | URL powrotu po płatności | +| `ContinueUrl` | URL kontynuacji po powrocie ze strony PayNow | + +--- + +## Obsługiwane kanały + +PayNow obsługuje: BLIK, szybkie przelewy bankowe, karty płatnicze (PLN). Lista kanałów pobierana dynamicznie z API. + +--- + +## Webhook + +PayNow wysyła powiadomienia POST. Podpis weryfikowany przez SHA-256 HMAC z `SignatureKey`, przekazywany w nagłówku `Signature`. + +--- + +## 🤖 AI Agent Prompt + +```markdown +## PayNow Provider — Instrukcja dla agenta AI + +Provider key: "PayNow" + +Sekcja konfiguracji: "Payments:Providers:PayNow" + +Wymagane pola: ApiKey, SignatureKey, ReturnUrl + +Kwota w API: grosze (int), np. 1999 = 19.99 PLN — biblioteka konwertuje automatycznie + +Sandbox URL: https://api.sandbox.paynow.pl + +Rejestracja: builder.Services.AddPayments().RegisterPayNowProvider(); +``` diff --git a/docs-en/Libraries/Payments/Providers/PayU.md b/docs-en/Libraries/Payments/Providers/PayU.md new file mode 100644 index 0000000..b96e737 --- /dev/null +++ b/docs-en/Libraries/Payments/Providers/PayU.md @@ -0,0 +1,111 @@ +# TailoredApps.Shared.Payments.Provider.PayU + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.Payments.Provider.PayU)](https://www.nuget.org/packages/TailoredApps.Shared.Payments.Provider.PayU/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +Integracja z **PayU REST API v2.1** — wiodącym polskim operatorem płatności obsługującym BLIK, szybkie przelewy, karty i inne metody. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.Payments.Provider.PayU +``` + +--- + +## Rejestracja w DI + +```csharp +using TailoredApps.Shared.Payments.Provider.PayU; + +builder.Services + .AddPayments() + .RegisterPayUProvider(); +``` + +--- + +## Konfiguracja `appsettings.json` + +```json +{ + "Payments": { + "Providers": { + "PayU": { + "ClientId": "your-client-id", + "ClientSecret": "your-client-secret", + "PosId": "your-pos-id", + "SignatureKey": "your-signature-key", + "ServiceUrl": "https://secure.snd.payu.com", + "NotifyUrl": "https://myapp.com/webhooks/payu", + "ContinueUrl": "https://myapp.com/payment/return" + } + } + } +} +``` + +| Opcja | Opis | +|-------|------| +| `ClientId` | Identyfikator klienta OAuth | +| `ClientSecret` | Sekret klienta OAuth | +| `PosId` | Identyfikator punktu sprzedaży (merchantPosId) | +| `SignatureKey` | Klucz do podpisu powiadomień (second key) | +| `ServiceUrl` | URL API PayU (sandbox: `https://secure.snd.payu.com`) | +| `NotifyUrl` | URL do powiadomień o transakcjach (webhook) | +| `ContinueUrl` | URL powrotu po płatności | + +--- + +## Obsługiwane kanały + +PayU oferuje szeroką gamę kanałów dla PLN i EUR: BLIK, przelewy bankowe, karty, PayPal, Google Pay, Apple Pay. Lista pobierana dynamicznie z API PayU. + +--- + +## Autoryzacja + +Provider automatycznie pobiera token OAuth2 (`client_credentials`) przed każdym żądaniem i cache'uje go do wygaśnięcia. + +--- + +## Webhook + +PayU wysyła powiadomienia POST z nagłówkiem `OpenPayU-Signature`. Podpis weryfikowany przez MD5 z `SignatureKey`. + +```csharp +[HttpPost("webhooks/payu")] +public async Task PayUWebhook([FromBody] JsonElement body) +{ + var result = await _payments.HandleWebhookAsync("PayU", new PaymentWebhookRequest + { + Body = body.GetRawText(), + Headers = Request.Headers.ToDictionary(h => h.Key, h => h.Value.ToString()) + }); + return Ok(); // PayU oczekuje 200 OK nawet przy błędzie weryfikacji +} +``` + +--- + +## 🤖 AI Agent Prompt + +```markdown +## PayU Provider — Instrukcja dla agenta AI + +Provider key: "PayU" + +Sekcja konfiguracji: "Payments:Providers:PayU" + +Wymagane pola: ClientId, ClientSecret, PosId, SignatureKey, NotifyUrl, ContinueUrl + +Autoryzacja: OAuth2 client_credentials — automatyczna, nie wymaga interwencji + +Kwota w API: grosze (int) — biblioteka konwertuje automatycznie z decimal + +Sandbox: ServiceUrl = "https://secure.snd.payu.com" + +Rejestracja: builder.Services.AddPayments().RegisterPayUProvider(); +``` diff --git a/docs-en/Libraries/Payments/Providers/Przelewy24.md b/docs-en/Libraries/Payments/Providers/Przelewy24.md new file mode 100644 index 0000000..5b5540c --- /dev/null +++ b/docs-en/Libraries/Payments/Providers/Przelewy24.md @@ -0,0 +1,90 @@ +# TailoredApps.Shared.Payments.Provider.Przelewy24 + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.Payments.Provider.Przelewy24)](https://www.nuget.org/packages/TailoredApps.Shared.Payments.Provider.Przelewy24/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +Integracja z **Przelewy24** — jednym z najpopularniejszych polskich operatorów płatności online obsługującym ponad 170 banków i BLIK. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.Payments.Provider.Przelewy24 +``` + +--- + +## Rejestracja w DI + +```csharp +using TailoredApps.Shared.Payments.Provider.Przelewy24; + +builder.Services + .AddPayments() + .RegisterPrzelewy24Provider(); +``` + +--- + +## Konfiguracja `appsettings.json` + +```json +{ + "Payments": { + "Providers": { + "Przelewy24": { + "MerchantId": 12345, + "PosId": 12345, + "ApiKey": "your-api-key", + "CrcKey": "your-crc-key", + "ServiceUrl": "https://secure.przelewy24.pl", + "ReturnUrl": "https://myapp.com/payment/return", + "NotifyUrl": "https://myapp.com/webhooks/przelewy24" + } + } + } +} +``` + +| Opcja | Opis | +|-------|------| +| `MerchantId` | Identyfikator sprzedawcy w Przelewy24 | +| `PosId` | Identyfikator punktu sprzedaży (zazwyczaj = MerchantId) | +| `ApiKey` | Klucz API do autoryzacji żądań | +| `CrcKey` | Klucz CRC do generowania podpisów transakcji | +| `ServiceUrl` | URL API (sandbox: `https://sandbox.przelewy24.pl`) | +| `ReturnUrl` | URL powrotu po płatności | +| `NotifyUrl` | URL do powiadomień o transakcjach (webhook) | + +--- + +## Obsługiwane kanały + +Przelewy24 obsługuje PLN i EUR przez ponad 170 kanałów płatności (banki, BLIK, karty). Lista kanałów pobierana z API Przelewy24. + +--- + +## Webhook + +Przelewy24 weryfikuje płatność przez endpoint `/transaction/verify`. Podpis transakcji oparty na SHA-384 z kluczem CRC. + +--- + +## 🤖 AI Agent Prompt + +```markdown +## Przelewy24 Provider — Instrukcja dla agenta AI + +Provider key: "Przelewy24" + +Sekcja konfiguracji: "Payments:Providers:Przelewy24" + +Wymagane pola: MerchantId, PosId, ApiKey, CrcKey, ReturnUrl, NotifyUrl + +Kwota: grosze (int) — biblioteka konwertuje automatycznie + +Sandbox: ServiceUrl = "https://sandbox.przelewy24.pl" + +Rejestracja: builder.Services.AddPayments().RegisterPrzelewy24Provider(); +``` diff --git a/docs-en/Libraries/Payments/Providers/Revolut.md b/docs-en/Libraries/Payments/Providers/Revolut.md new file mode 100644 index 0000000..fc8844e --- /dev/null +++ b/docs-en/Libraries/Payments/Providers/Revolut.md @@ -0,0 +1,84 @@ +# TailoredApps.Shared.Payments.Provider.Revolut + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.Payments.Provider.Revolut)](https://www.nuget.org/packages/TailoredApps.Shared.Payments.Provider.Revolut/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +Integracja z **Revolut Merchant API** — obsługa płatności kartą i Revolut Pay z weryfikacją webhooków przez podpis. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.Payments.Provider.Revolut +``` + +--- + +## Rejestracja w DI + +```csharp +using TailoredApps.Shared.Payments.Provider.Revolut; + +builder.Services + .AddPayments() + .RegisterRevolutProvider(); +``` + +--- + +## Konfiguracja `appsettings.json` + +```json +{ + "Payments": { + "Providers": { + "Revolut": { + "ApiKey": "sk_test_...", + "ApiUrl": "https://merchant.revolut.com/api", + "ReturnUrl": "https://myapp.com/payment/return", + "WebhookSecret": "whsec_..." + } + } + } +} +``` + +| Opcja | Opis | +|-------|------| +| `ApiKey` | Klucz API Revolut Merchant (`sk_live_...` lub `sk_test_...`) | +| `ApiUrl` | URL API Revolut Merchant | +| `ReturnUrl` | URL powrotu po płatności | +| `WebhookSecret` | Sekret do weryfikacji podpisu webhooków (`whsec_...`) | + +--- + +## Obsługiwane kanały + +Revolut obsługuje płatności kartą (Visa, Mastercard) i Revolut Pay dla wielu walut (PLN, EUR, GBP, USD i inne). + +--- + +## Webhook + +Revolut podpisuje powiadomienia podpisem `Revolut-Signature` w nagłówku HTTP. Weryfikacja przez HMAC-SHA256 z `WebhookSecret`. + +--- + +## 🤖 AI Agent Prompt + +```markdown +## Revolut Provider — Instrukcja dla agenta AI + +Provider key: "Revolut" + +Sekcja konfiguracji: "Payments:Providers:Revolut" + +Wymagane pola: ApiKey, ReturnUrl, WebhookSecret + +Kwota: grosze/centy (int) — biblioteka konwertuje automatycznie + +Test API key: sk_test_... + +Rejestracja: builder.Services.AddPayments().RegisterRevolutProvider(); +``` diff --git a/docs-en/Libraries/Payments/Providers/Stripe.md b/docs-en/Libraries/Payments/Providers/Stripe.md new file mode 100644 index 0000000..84915c8 --- /dev/null +++ b/docs-en/Libraries/Payments/Providers/Stripe.md @@ -0,0 +1,119 @@ +# TailoredApps.Shared.Payments.Provider.Stripe + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.Payments.Provider.Stripe)](https://www.nuget.org/packages/TailoredApps.Shared.Payments.Provider.Stripe/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +Integracja z **Stripe Checkout** — globalnym operatorem płatności kartą, BLIK (PLN) i Przelewy24. Provider używa Stripe Checkout Session (hosted page). + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.Payments.Provider.Stripe +``` + +--- + +## Rejestracja w DI + +```csharp +using TailoredApps.Shared.Payments.Provider.Stripe; + +builder.Services + .AddPayments() + .RegisterStripeProvider(); +``` + +--- + +## Konfiguracja `appsettings.json` + +```json +{ + "Payments": { + "Providers": { + "Stripe": { + "SecretKey": "sk_test_...", + "WebhookSecret": "whsec_...", + "ReturnUrl": "https://myapp.com/payment/return", + "IsTest": true + } + } + } +} +``` + +| Opcja | Opis | +|-------|------| +| `SecretKey` | Klucz API Stripe (`sk_live_...` lub `sk_test_...`) | +| `WebhookSecret` | Sekret endpointu webhooka (`whsec_...`) | +| `ReturnUrl` | URL powrotu po zakończeniu Checkout Session | +| `IsTest` | `true` = tryb testowy (domyślnie) | + +--- + +## Obsługiwane kanały + +| Waluta | Kanały | +|--------|--------| +| PLN | `card` (Visa, Mastercard, Amex), `blik`, `p24` (Przelewy24) | +| EUR | `card`, `sepa_debit` | +| Inne | `card` | + +--- + +## Przepływ płatności + +1. `RegisterPayment` → tworzy Stripe Checkout Session +2. Użytkownik jest przekierowany na `RedirectUrl` (hosted Stripe page) +3. Po płatności Stripe wywołuje webhook z eventami `checkout.session.completed` / `payment_intent.payment_failed` +4. Provider przetwarza webhook i zwraca `PaymentWebhookResult` + +--- + +## Webhook + +Stripe weryfikuje webhook przez `Stripe-Signature` w nagłówku. Niezbędny jest dostęp do **raw body** żądania HTTP. + +```csharp +[HttpPost("webhooks/stripe")] +public async Task StripeWebhook() +{ + using var reader = new StreamReader(Request.Body); + var rawBody = await reader.ReadToEndAsync(); + + var result = await _payments.HandleWebhookAsync("Stripe", new PaymentWebhookRequest + { + Body = rawBody, + Headers = Request.Headers.ToDictionary(h => h.Key, h => h.Value.ToString()) + }); + + return result == PaymentWebhookResult.Fail ? BadRequest() : Ok(); +} +``` + +!!! warning "Raw body" + Do weryfikacji podpisu Stripe wymagany jest surowy body żądania. Nie używaj `[FromBody]` z automatyczną deserializacją JSON — zamiast tego czytaj `Request.Body` bezpośrednio. + +--- + +## 🤖 AI Agent Prompt + +```markdown +## Stripe Provider — Instrukcja dla agenta AI + +Provider key: "Stripe" + +Sekcja konfiguracji: "Payments:Providers:Stripe" + +Wymagane pola: SecretKey, WebhookSecret, ReturnUrl + +Przepływ: RegisterPayment → Checkout Session → redirect → webhook + +Webhook: wymaga raw body (nie [FromBody]) + nagłówek Stripe-Signature + +Sandbox: IsTest = true, SecretKey = "sk_test_..." + +Rejestracja: builder.Services.AddPayments().RegisterStripeProvider(); +``` diff --git a/docs-en/Libraries/Payments/Providers/Tpay.md b/docs-en/Libraries/Payments/Providers/Tpay.md new file mode 100644 index 0000000..873c5bd --- /dev/null +++ b/docs-en/Libraries/Payments/Providers/Tpay.md @@ -0,0 +1,96 @@ +# TailoredApps.Shared.Payments.Provider.Tpay + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.Payments.Provider.Tpay)](https://www.nuget.org/packages/TailoredApps.Shared.Payments.Provider.Tpay/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +Integracja z **Tpay** — polskim operatorem płatności online obsługującym szybkie przelewy, BLIK, karty i inne metody. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.Payments.Provider.Tpay +``` + +--- + +## Rejestracja w DI + +```csharp +using TailoredApps.Shared.Payments.Provider.Tpay; + +builder.Services + .AddPayments() + .RegisterTpayProvider(); +``` + +--- + +## Konfiguracja `appsettings.json` + +```json +{ + "Payments": { + "Providers": { + "Tpay": { + "ClientId": "your-client-id", + "ClientSecret": "your-client-secret", + "MerchantId": "your-merchant-id", + "ServiceUrl": "https://api.tpay.com", + "ReturnUrl": "https://myapp.com/payment/return", + "NotifyUrl": "https://myapp.com/webhooks/tpay", + "SecurityCode": "your-security-code" + } + } + } +} +``` + +| Opcja | Opis | +|-------|------| +| `ClientId` | Identyfikator klienta OAuth2 | +| `ClientSecret` | Sekret klienta OAuth2 | +| `MerchantId` | Identyfikator sprzedawcy | +| `ServiceUrl` | URL API Tpay (sandbox: `https://openapi.sandbox.tpay.com`) | +| `ReturnUrl` | URL powrotu po płatności | +| `NotifyUrl` | URL do powiadomień (webhook) | +| `SecurityCode` | Kod bezpieczeństwa do weryfikacji powiadomień | + +--- + +## Obsługiwane kanały + +Tpay obsługuje PLN przez: szybkie przelewy bankowe, BLIK, karty, Google Pay, Apple Pay. Lista kanałów pobierana z API. + +--- + +## Autoryzacja + +Provider automatycznie pobiera token OAuth2 przez endpoint `/oauth/auth` i odświeża go po wygaśnięciu. + +--- + +## Webhook + +Tpay wysyła powiadomienia POST z podpisem weryfikowanym przez MD5 z `SecurityCode`. + +--- + +## 🤖 AI Agent Prompt + +```markdown +## Tpay Provider — Instrukcja dla agenta AI + +Provider key: "Tpay" + +Sekcja konfiguracji: "Payments:Providers:Tpay" + +Wymagane pola: ClientId, ClientSecret, MerchantId, ReturnUrl, NotifyUrl, SecurityCode + +Autoryzacja: OAuth2 — automatyczna, token cache'owany + +Sandbox: ServiceUrl = "https://openapi.sandbox.tpay.com" + +Rejestracja: builder.Services.AddPayments().RegisterTpayProvider(); +``` diff --git a/docs-en/Libraries/Payments/index.md b/docs-en/Libraries/Payments/index.md new file mode 100644 index 0000000..8eed02e --- /dev/null +++ b/docs-en/Libraries/Payments/index.md @@ -0,0 +1,223 @@ +# TailoredApps.Shared.Payments + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.Payments)](https://www.nuget.org/packages/TailoredApps.Shared.Payments/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +--- + +## Description + +This library provides a unified abstraction for payment gateway integration in .NET applications. Instead of writing separate logic for each payment operator, you program against the common `IPaymentService` interface, which automatically routes requests to the correct provider based on its key (`"Stripe"`, `"PayU"`, `"CashBill"`, etc.). + +Key architectural elements: + +- **`IPaymentProvider`** — contract for each provider (channels, payment initiation, status, webhook) +- **`IWebhookPaymentProvider`** — extension for providers supporting webhooks with signature verification +- **`IPaymentService`** — facade aggregating all registered providers +- **`PaymentOptionsBuilder`** — fluent API for registering providers in DI + +`TailoredApps.Shared.Payments` is the core — concrete provider implementations are in separate `TailoredApps.Shared.Payments.Provider.*` packages. + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.Payments + +# Dodaj wybrane providery: +dotnet add package TailoredApps.Shared.Payments.Provider.Stripe +dotnet add package TailoredApps.Shared.Payments.Provider.PayU +dotnet add package TailoredApps.Shared.Payments.Provider.CashBill +``` + +--- + +## Rejestracja w DI + +```csharp +// Program.cs +using TailoredApps.Shared.Payments; +using TailoredApps.Shared.Payments.Provider.Stripe; +using TailoredApps.Shared.Payments.Provider.PayU; + +builder.Services + .AddPayments() + .RegisterPaymentProvider() + .RegisterPaymentProvider(); +``` + +--- + +## Przykład użycia + +### Inicjowanie płatności + +```csharp +public class CheckoutService +{ + private readonly IPaymentService _payments; + + public CheckoutService(IPaymentService payments) => _payments = payments; + + public async Task CreatePaymentAsync(CartDto cart, string email) + { + var response = await _payments.RegisterPayment(new PaymentRequest + { + PaymentProvider = "Stripe", // lub "PayU", "CashBill" itp. + PaymentChannel = "card", + PaymentModel = PaymentModel.OneTime, + Title = $"Zamówienie #{cart.OrderId}", + Description = "Zakup w sklepie MyShop", + Currency = "PLN", + Amount = cart.TotalAmount, + Email = email, + FirstName = cart.CustomerFirstName, + Surname = cart.CustomerLastName + }); + + // Przekieruj użytkownika na stronę płatności + return response.RedirectUrl; + } + + public async Task CheckStatusAsync(string providerId, string paymentId) + { + var response = await _payments.GetStatus(providerId, paymentId); + return response.PaymentStatus; + } +} +``` + +### Obsługa webhooków + +```csharp +[ApiController] +[Route("api/webhooks/payments")] +public class PaymentWebhookController : ControllerBase +{ + private readonly IPaymentService _payments; + + public PaymentWebhookController(IPaymentService payments) => _payments = payments; + + [HttpPost("{providerKey}")] + public async Task HandleWebhook( + string providerKey, + [FromBody] JsonElement body) + { + var request = new PaymentWebhookRequest + { + Body = body.GetRawText(), + Headers = Request.Headers.ToDictionary(h => h.Key, h => h.Value.ToString()), + QueryParams = Request.Query.ToDictionary(q => q.Key, q => q.Value.ToString()) + }; + + var result = await _payments.HandleWebhookAsync(providerKey, request); + + return result switch + { + PaymentWebhookResult.Success => Ok(), + PaymentWebhookResult.Ignored => NoContent(), + _ => BadRequest() + }; + } +} +``` + +### Pobieranie dostępnych kanałów płatności + +```csharp +// Wyświetl dostępne metody płatności dla waluty PLN +var providers = await _payments.GetProviders(); + +foreach (var provider in providers) +{ + var channels = await _payments.GetChannels(provider.Key, "PLN"); + Console.WriteLine($"{provider.Name}: {string.Join(", ", channels.Select(c => c.Name))}"); +} +``` + +--- + +## API Reference + +### Core Interfaces + +| Typ | Rodzaj | Opis | +|-----|--------|------| +| `IPaymentService` | Interfejs | Fasada: `GetProviders`, `GetChannels`, `RegisterPayment`, `GetStatus`, `HandleWebhookAsync` | +| `IPaymentProvider` | Interfejs | Kontrakt providera: `Key`, `Name`, `GetPaymentChannels`, `RequestPayment`, `GetStatus`, `TransactionStatusChange` | +| `IWebhookPaymentProvider` | Interfejs | Rozszerzenie: `HandleWebhookAsync(PaymentWebhookRequest)` | +| `IPaymentOptionsBuilder` | Interfejs | Fluent builder: `RegisterPaymentProvider()` | + +### Models + +| Typ | Rodzaj | Opis | +|-----|--------|------| +| `PaymentRequest` | Klasa | Dane płatności: provider, kanał, kwota, waluta, dane płatnika | +| `PaymentResponse` | Klasa | Wynik: `RedirectUrl`, `PaymentUniqueId`, `PaymentStatus` | +| `PaymentStatusEnum` | Enum | `Created`, `Pending`, `Completed`, `Failed`, `Cancelled` | +| `PaymentModel` | Enum | `OneTime`, `Subscription` | +| `PaymentChannel` | Klasa | Kanał płatności: `Id`, `Name`, `Description`, `PaymentModel` | +| `PaymentProvider` | Klasa | Metadane providera: `Key`, `Name`, `Description`, `Url` | +| `PaymentWebhookRequest` | Klasa | Dane HTTP żądania webhook: `Body`, `Headers`, `QueryParams` | +| `PaymentWebhookResult` | Enum | `Success`, `Ignored`, `Fail` | + +### DI Registration + +| Metoda | Opis | +|--------|------| +| `services.AddPayments()` | Rejestruje `IPaymentService`/`PaymentService` | +| `builder.RegisterPaymentProvider()` | Dodaje implementację `IPaymentProvider` | + +--- + +## 🤖 AI Agent Prompt + +```markdown +## TailoredApps.Shared.Payments — Instrukcja dla agenta AI + +Używasz TailoredApps.Shared.Payments do integracji z bramkami płatności. + +### Rejestracja +```csharp +builder.Services.AddPayments() + .RegisterPaymentProvider() + .RegisterPaymentProvider(); +``` + +### Inicjowanie płatności +```csharp +var response = await _payments.RegisterPayment(new PaymentRequest +{ + PaymentProvider = "Stripe", // klucz providera + PaymentChannel = "card", + Currency = "PLN", + Amount = 99.99m, + Email = "user@example.com", + Title = "Zamówienie #123" +}); +string redirectUrl = response.RedirectUrl; // → przeglądarka +``` + +### Sprawdzanie statusu +```csharp +var status = await _payments.GetStatus("Stripe", paymentId); +// status.PaymentStatus: Created/Pending/Completed/Failed/Cancelled +``` + +### Obsługa webhooków +```csharp +var result = await _payments.HandleWebhookAsync(providerKey, new PaymentWebhookRequest +{ + Body = rawBody, + Headers = httpHeaders, + QueryParams = queryParams +}); +``` + +### Zasady +- Każdy provider ma unikalny Key (np. "Stripe", "PayU", "CashBill") +- Zawsze sprawdź PaymentWebhookResult — Ignored to OK, Fail to błąd podpisu/konfiguracji +- Kwota Amount w walucie bazowej (nie w groszach — chyba że provider wymaga inaczej) +- Dla webhooków wymagany jest endpoint HTTP POST z raw body +``` diff --git a/docs-en/Libraries/Querying/index.md b/docs-en/Libraries/Querying/index.md new file mode 100644 index 0000000..0bab829 --- /dev/null +++ b/docs-en/Libraries/Querying/index.md @@ -0,0 +1,176 @@ +# TailoredApps.Shared.Querying + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.Querying)](https://www.nuget.org/packages/TailoredApps.Shared.Querying/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +--- + +## Description + +This library provides a set of base classes and interfaces for building paged and sorted queries in .NET applications. It standardizes the structure of list queries across the entire application — instead of passing `page`, `pageSize`, `sortField` as separate parameters everywhere, you have one consistent contract. + +Key types: +- **`QueryBase`** — abstract base class for query filter objects +- **`PagedAndSortedQuery`** — base class combining filtering, pagination, and sorting +- **`IPagedResult`** — result contract with pagination: collection + total count +- **`SortDirection`** — `Asc`/`Desc`/`Undefined` enum + +--- + +## Instalacja + +```bash +dotnet add package TailoredApps.Shared.Querying +``` + +--- + +## Przykład użycia + +### Definicja filtru i zapytania + +```csharp +using TailoredApps.Shared.Querying; + +// 1. Filter — konkretne kryteria wyszukiwania +public class CustomerFilter : QueryBase +{ + public string NameContains { get; set; } + public string Email { get; set; } + public bool? IsActive { get; set; } + public DateTime? RegisteredAfter { get; set; } +} + +// 2. Zapytanie stronicowane + sortowane +public class GetCustomersQuery : PagedAndSortedQuery +{ + // Wszystkie parametry są odziedziczone: + // Page, Count, SortField, SortDir, Filter, IsPagingSpecified, IsSortingSpecified +} + +// 3. Wynik stronicowany +public class CustomerListResult : IPagedResult +{ + public ICollection Results { get; set; } + public int Count { get; set; } // łączna liczba (bez stronicowania) +} +``` + +### Implementacja w repozytorium / serwisie + +```csharp +public async Task GetCustomersAsync( + GetCustomersQuery query, + CancellationToken ct = default) +{ + var dbQuery = _context.Customers.AsQueryable(); + + // Filtry + if (!string.IsNullOrWhiteSpace(query.Filter?.NameContains)) + dbQuery = dbQuery.Where(c => c.Name.Contains(query.Filter.NameContains)); + + if (!string.IsNullOrWhiteSpace(query.Filter?.Email)) + dbQuery = dbQuery.Where(c => c.Email == query.Filter.Email); + + if (query.Filter?.IsActive.HasValue == true) + dbQuery = dbQuery.Where(c => c.IsActive == query.Filter.IsActive.Value); + + // Sortowanie + if (query.IsSortingSpecified) + { + // Przykład z IsSortBy() + if (query.IsSortBy("Name")) + dbQuery = query.SortDir == SortDirection.Asc + ? dbQuery.OrderBy(c => c.Name) + : dbQuery.OrderByDescending(c => c.Name); + else if (query.IsSortBy("RegisteredAt")) + dbQuery = query.SortDir == SortDirection.Asc + ? dbQuery.OrderBy(c => c.RegisteredAt) + : dbQuery.OrderByDescending(c => c.RegisteredAt); + } + + var totalCount = await dbQuery.CountAsync(ct); + + // Paginacja + if (query.IsPagingSpecified) + { + var skip = (query.Page!.Value - 1) * query.Count!.Value; + dbQuery = dbQuery.Skip(skip).Take(query.Count.Value); + } + + var items = await dbQuery + .Select(c => new CustomerDto { Id = c.Id, Name = c.Name, Email = c.Email }) + .ToListAsync(ct); + + return new CustomerListResult + { + Results = items, + Count = totalCount + }; +} +``` + +--- + +## API Reference + +| Typ | Rodzaj | Opis | +|-----|--------|------| +| `QueryBase` | Klasa abstrakcyjna | Klasa bazowa dla wszystkich obiektów filtrów zapytań | +| `PagedAndSortedQuery` | Klasa abstrakcyjna | Bazowe zapytanie: paginacja + sortowanie + filtr | +| `IPagedAndSortedQuery` | Interfejs | Kontrakt `PagedAndSortedQuery` | +| `IPagedResult` | Interfejs | Wynik stronicowany: `Results` + `Count` | +| `SortDirection` | Enum | `Undefined = 0`, `Asc = 1`, `Desc = 2` | +| `IPagingParameters` | Interfejs | `Page`, `Count`, `IsPagingSpecified` | +| `ISortingParameters` | Interfejs | `SortField`, `SortDir`, `IsSortingSpecified` | +| `IQueryParameters` | Interfejs | Łączy `IPagingParameters` + `ISortingParameters` | +| `IQuery` | Interfejs | Zapytanie z obiektem filtru: `Filter` | +| `IPagedAndSortedRequest` | Interfejs | Kontrakt MediatR request z paginacją (używany przez MediatR.PagedRequest) | +| `IsSortBy(string)` | Metoda | Case-insensitive porównanie z `SortField` | + +--- + +## 🤖 AI Agent Prompt + +```markdown +## TailoredApps.Shared.Querying — Instrukcja dla agenta AI + +Używasz TailoredApps.Shared.Querying do standaryzacji stronicowanych zapytań. + +### Definicja typów +```csharp +// Filter dziedziczy QueryBase +public class MyFilter : QueryBase { public string Name { get; set; } } + +// Zapytanie dziedziczy PagedAndSortedQuery +public class GetItemsQuery : PagedAndSortedQuery { } + +// Wynik implementuje IPagedResult +public class ItemsResult : IPagedResult +{ + public ICollection Results { get; set; } + public int Count { get; set; } +} +``` + +### Parametry URL → automatyczny binding w ASP.NET Core +?page=1&count=20&sortField=Name&sortDir=Asc&filter.name=test + +### W serwisie/repozytorium +```csharp +if (query.IsPagingSpecified) + dbQuery = dbQuery.Skip((query.Page!.Value - 1) * query.Count!.Value).Take(query.Count.Value); + +if (query.IsSortingSpecified) +{ + if (query.IsSortBy("Name")) + dbQuery = query.SortDir == SortDirection.Asc ? dbQuery.OrderBy(x => x.Name) : dbQuery.OrderByDescending(x => x.Name); +} +``` + +### Zasady +- Count w IPagedResult = łączna liczba bez stronicowania (do kalkulacji stron w UI) +- SortDirection.Undefined = brak sortowania (domyślne) +- IsSortBy() sprawdza case-insensitive — używaj zawsze zamiast string.Equals ręcznie +- IsPagingSpecified = OBIE wartości Page i Count muszą być != null +``` diff --git a/docs-en/Libraries/Readme.md b/docs-en/Libraries/Readme.md new file mode 100644 index 0000000..e69de29 diff --git a/docs-en/contributing.md b/docs-en/contributing.md new file mode 100644 index 0000000..92ee0da --- /dev/null +++ b/docs-en/contributing.md @@ -0,0 +1,132 @@ +# Contributing + +Thank you for your interest in contributing to **TailoredApps Shared Components**! This document describes the contribution process and **mandatory documentation requirements**. + +--- + +## General Rules + +1. **Fork → Feature Branch → PR** — do not commit directly to `master` +2. Branch naming: `feature/` or `fix/` +3. One PR = one substantive change +4. All tests must pass before opening a PR +5. Code must have XML docs (summary) for public types and methods + +--- + +## Local Setup + +```bash +git clone https://github.com/tailored-apps/SharedComponents.git +cd SharedComponents + +# .NET +export DOTNET_ROOT=/opt/homebrew/opt/dotnet/libexec +export PATH="$PATH:/opt/homebrew/opt/dotnet/bin" +dotnet restore + +# Documentation +pip install mkdocs-material +mkdocs serve # http://127.0.0.1:8000 +``` + +--- + +## 🔴 IRON RULE OF DOCUMENTATION + +> **Every new library in this repo MUST have a documentation page in `docs/Libraries/`.** +> +> **PR without documentation = PR rejected.** + +### Requirements for each documentation page + +Each `docs/Libraries//index.md` page must contain: + +1. **Header + badges** — library name, NuGet and license badges +2. **Description** — in **Polish** 🇵🇱 and in **English** 🇬🇧 +3. **Installation** — `dotnet add package ...` +4. **DI Registration** — example with `Program.cs` +5. **Usage example** — real, complete C# code (not a toy example) +6. **API Reference** — table/list of main interfaces and classes +7. **🤖 AI Agent Prompt** — ready-made prompt to paste into an AI agent context + +### Navigation update + +After adding a library page, update the `nav:` section in `mkdocs.yml` and the library table on `docs/index.md`. + +### Library page template + +```markdown +# TailoredApps.Shared.XXXXX + +[![NuGet](https://img.shields.io/nuget/v/TailoredApps.Shared.XXXXX)](https://www.nuget.org/packages/TailoredApps.Shared.XXXXX/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) + +## 🇵🇱 Opis + +[Full description in Polish...] + +## 🇬🇧 Description + +[Full description in English...] + +## Instalacja + +\`\`\`bash +dotnet add package TailoredApps.Shared.XXXXX +\`\`\` + +## Rejestracja w DI + +\`\`\`csharp +// Program.cs +builder.Services.AddXxx(); +\`\`\` + +## Przykład użycia + +\`\`\`csharp +// ... +\`\`\` + +## API Reference + +| Type | Description | +|------|-------------| +| \`IXxx\` | ... | + +## 🤖 AI Agent Prompt + +\`\`\`markdown +## TailoredApps.Shared.XXXXX — AI Agent Instructions + +You are using the TailoredApps.Shared.XXXXX library in a .NET project. + +### Registration +... + +### Usage +... + +### Rules +- ... +\`\`\` +``` + +--- + +## Pre-PR Checklist + +- [ ] Code compiles without errors (`dotnet build`) +- [ ] Tests pass (`dotnet test`) +- [ ] XML docs added to public types +- [ ] Documentation page in `docs/Libraries/` +- [ ] `mkdocs.yml` nav updated +- [ ] Table on `docs/index.md` updated +- [ ] `mkdocs build --strict` passes without errors + +--- + +## Questions + +Open an [Issue on GitHub](https://github.com/tailored-apps/SharedComponents/issues) or contact the project maintainers. diff --git a/docs-en/index.md b/docs-en/index.md new file mode 100644 index 0000000..18cf3e7 --- /dev/null +++ b/docs-en/index.md @@ -0,0 +1,57 @@ +# TailoredApps .NET Shared Components + +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tailored-apps/SharedComponents/blob/master/LICENSE) +[![GitHub](https://img.shields.io/badge/GitHub-SharedComponents-181717?logo=github)](https://github.com/tailored-apps/SharedComponents) + +Welcome to the documentation of **TailoredApps Shared Components** — a set of reusable .NET libraries that accelerate the development of professional web applications. Each library solves one specific problem and is designed for testability, extensibility, and integration with the ASP.NET Core + MediatR ecosystem. + +--- + +## Libraries + +| Library | NuGet | Description | +|---|---|---| +| [DateTime](Libraries/DateTime/index.md) | `TailoredApps.Shared.DateTime` | `IDateTimeProvider` abstraction for mocking time in tests | +| [Email](Libraries/Email/index.md) | `TailoredApps.Shared.Email` | SMTP provider, email template builder, console mode | +| [Email.Models](Libraries/Email/Models.md) | `TailoredApps.Shared.Email.Models` | `MailMessage` data model | +| [Email.Office365](Libraries/Email/Office365.md) | `TailoredApps.Shared.Email.Office365` | Sending via Microsoft Graph API (IMAP OAuth2) | +| [EntityFramework](Libraries/EntityFramework/index.md) | `TailoredApps.Shared.EntityFramework` | Unit of Work pattern on EF Core with auditing and hooks | +| [EntityFramework.UnitOfWork.WebApiCore](Libraries/EntityFramework/UnitOfWork.WebApiCore.md) | `TailoredApps.Shared.EntityFramework.UnitOfWork.WebApiCore` | Automatic transactions via ASP.NET Core filter | +| [ExceptionHandling](Libraries/ExceptionHandling/index.md) | `TailoredApps.Shared.ExceptionHandling` | Middleware and filter for exception handling in Web API | +| [MediatR](Libraries/MediatR/index.md) | `TailoredApps.Shared.MediatR` | Pipeline behaviors: Logging, Validation, Caching, Fallback, Retry | +| [MediatR.Caching](Libraries/MediatR/Caching.md) | `TailoredApps.Shared.MediatR.Caching` | `ICachableRequest` marker interface for caching requests | +| [MediatR.Email](Libraries/MediatR/Email.md) | `TailoredApps.Shared.MediatR.Email` | `SendMail` command + handler — email via MediatR pipeline | +| [MediatR.ML](Libraries/MediatR/ML.md) | `TailoredApps.Shared.MediatR.ML` | Image classification via ML.NET in MediatR pipelines | +| [MediatR.PagedRequest](Libraries/MediatR/PagedRequest.md) | `TailoredApps.Shared.MediatR.PagedRequest` | Base MediatR request with pagination and sorting | +| [Payments](Libraries/Payments/index.md) | `TailoredApps.Shared.Payments` | Payment gateway abstraction — IPaymentService + IPaymentProvider | +| [Payments.Provider.Adyen](Libraries/Payments/Providers/Adyen.md) | `TailoredApps.Shared.Payments.Provider.Adyen` | Adyen integration | +| [Payments.Provider.CashBill](Libraries/Payments/Providers/CashBill.md) | `TailoredApps.Shared.Payments.Provider.CashBill` | CashBill integration | +| [Payments.Provider.HotPay](Libraries/Payments/Providers/HotPay.md) | `TailoredApps.Shared.Payments.Provider.HotPay` | HotPay integration | +| [Payments.Provider.PayNow](Libraries/Payments/Providers/PayNow.md) | `TailoredApps.Shared.Payments.Provider.PayNow` | PayNow (mBank) integration | +| [Payments.Provider.PayU](Libraries/Payments/Providers/PayU.md) | `TailoredApps.Shared.Payments.Provider.PayU` | PayU integration | +| [Payments.Provider.Przelewy24](Libraries/Payments/Providers/Przelewy24.md) | `TailoredApps.Shared.Payments.Provider.Przelewy24` | Przelewy24 integration | +| [Payments.Provider.Revolut](Libraries/Payments/Providers/Revolut.md) | `TailoredApps.Shared.Payments.Provider.Revolut` | Revolut Pay integration | +| [Payments.Provider.Stripe](Libraries/Payments/Providers/Stripe.md) | `TailoredApps.Shared.Payments.Provider.Stripe` | Stripe Checkout integration | +| [Payments.Provider.Tpay](Libraries/Payments/Providers/Tpay.md) | `TailoredApps.Shared.Payments.Provider.Tpay` | Tpay integration | +| [Querying](Libraries/Querying/index.md) | `TailoredApps.Shared.Querying` | Base query classes: `QueryBase`, `PagedAndSortedQuery`, `IPagedResult` | + +--- + +## Quick Start + +```bash +# Install the chosen library +dotnet add package TailoredApps.Shared.MediatR +dotnet add package TailoredApps.Shared.EntityFramework +dotnet add package TailoredApps.Shared.Payments +``` + +Full documentation for each library — including code examples, DI registration, and ready-made AI agent prompts — is available in the **Libraries** section in the side menu. + +--- + +## Contributing + +Before adding a new library, read the [contributing guidelines](contributing.md) and [DOCUMENTATION_RULE](https://github.com/tailored-apps/SharedComponents/blob/master/DOCUMENTATION_RULE.md). + +**Every new library must have a documentation page — PRs without one will be rejected.** diff --git a/docs/Libraries/DateTime/index.md b/docs/Libraries/DateTime/index.md index 1efe2ef..3284190 100644 --- a/docs/Libraries/DateTime/index.md +++ b/docs/Libraries/DateTime/index.md @@ -5,18 +5,12 @@ --- -## 🇵🇱 Opis +## Opis Biblioteka rozwiązuje jeden z fundamentalnych problemów testowalności aplikacji .NET — bezpośrednie użycie `System.DateTime.Now` w kodzie produkcyjnym, które uniemożliwia pisanie deterministycznych testów jednostkowych. `TailoredApps.Shared.DateTime` dostarcza interfejs `IDateTimeProvider` i jego domyślną implementację `DateTimeProvider`. Zamiast wywoływać `DateTime.Now` wprost, wstrzykujesz `IDateTimeProvider` przez DI i wywołujesz `provider.Now`. W testach wymieniasz implementację na mock zwracający dowolny punkt w czasie — dzięki temu testy są powtarzalne i niezależne od zegara systemowego. -## 🇬🇧 Description - -This library solves one of the fundamental testability problems in .NET — direct use of `System.DateTime.Now` in production code, which prevents writing deterministic unit tests. - -`TailoredApps.Shared.DateTime` provides the `IDateTimeProvider` interface and its default implementation `DateTimeProvider`. Instead of calling `DateTime.Now` directly, you inject `IDateTimeProvider` via DI and call `provider.Now`. In tests you swap the implementation for a mock that returns any point in time — making tests repeatable and independent of the system clock. - --- ## Instalacja diff --git a/docs/Libraries/Email/Models.md b/docs/Libraries/Email/Models.md index 5a5584b..6c34b92 100644 --- a/docs/Libraries/Email/Models.md +++ b/docs/Libraries/Email/Models.md @@ -5,14 +5,10 @@ --- -## 🇵🇱 Opis +## Opis Lekki pakiet zawierający wyłącznie model danych `MailMessage` — reprezentację wiadomości e-mail. Wydzielenie modelu do osobnego pakietu pozwala innym bibliotekom (np. `TailoredApps.Shared.Email.Office365`) zależeć tylko od modelu, bez ciągnięcia za sobą całej implementacji SMTP. -## 🇬🇧 Description - -A lightweight package containing only the `MailMessage` data model — a representation of an email message. Separating the model into its own package allows other libraries (e.g. `TailoredApps.Shared.Email.Office365`) to depend only on the model without pulling in the full SMTP implementation. - --- ## Instalacja diff --git a/docs/Libraries/Email/Office365.md b/docs/Libraries/Email/Office365.md index c858eac..2e5a74a 100644 --- a/docs/Libraries/Email/Office365.md +++ b/docs/Libraries/Email/Office365.md @@ -5,7 +5,7 @@ --- -## 🇵🇱 Opis +## Opis Implementacja `IEmailProvider` wysyłająca wiadomości e-mail przez Microsoft 365 z wykorzystaniem protokołu IMAP i uwierzytelniania OAuth2 (client credentials flow). Biblioteka uwierzytelnia się w Azure AD jako confidential client application — obsługuje zarówno client secret, jak i certyfikat. @@ -14,15 +14,6 @@ Dzięki temu możesz odbierać wiadomości ze skrzynki Office 365 bez przechowyw !!! warning "SendMail — niezaimplementowane" Metoda `SendMail` rzuca `NotImplementedException`. Ta biblioteka obsługuje **tylko odbiór** wiadomości przez IMAP. Do wysyłki używaj `SmtpEmailProvider` lub integracji przez Microsoft Graph. -## 🇬🇧 Description - -An `IEmailProvider` implementation that reads email from Microsoft 365 using IMAP with OAuth2 authentication (client credentials flow). The library authenticates with Azure AD as a confidential client application — supporting both client secret and certificate authentication. - -This allows you to receive messages from an Office 365 mailbox without storing a user password. The library automatically handles token acquisition and caching via Microsoft Identity. - -!!! warning "SendMail — not implemented" - The `SendMail` method throws `NotImplementedException`. This library supports **reading only** via IMAP. For sending, use `SmtpEmailProvider` or Microsoft Graph integration. - --- ## Instalacja diff --git a/docs/Libraries/Email/index.md b/docs/Libraries/Email/index.md index d86b3c6..c642504 100644 --- a/docs/Libraries/Email/index.md +++ b/docs/Libraries/Email/index.md @@ -5,7 +5,7 @@ --- -## 🇵🇱 Opis +## Opis Biblioteka dostarcza kompletną abstrakcję do wysyłania wiadomości e-mail w aplikacjach .NET. Opiera się na interfejsie `IEmailProvider`, który możesz wymieniać w zależności od środowiska — w produkcji używasz `SmtpEmailProvider` (wysyłka przez SMTP), w lokalnym środowisku deweloperskim `EmailServiceToConsoleWriter` (wypisuje do konsoli bez wysyłki). @@ -13,14 +13,6 @@ Biblioteka zawiera też system budowania treści emaili oparty na szablonach (`I Wbudowane zabezpieczenie przed przypadkowym spamem w środowiskach nieprodukcyjnych: kiedy `IsProd = false`, wszystkie emaile trafiają na adres `CatchAll` zamiast do prawdziwych odbiorców. -## 🇬🇧 Description - -This library provides a complete abstraction for sending email messages in .NET applications. It is built around the `IEmailProvider` interface, which can be swapped depending on the environment — in production use `SmtpEmailProvider` (sends via SMTP), in local development use `EmailServiceToConsoleWriter` (prints to console without actual delivery). - -The library also includes a template-based email body building system (`IMailMessageBuilder`), supporting simple token substitution (`DefaultMessageBuilder`) or file-system templates with `{{token}}` placeholders (`TokenReplacingMailMessageBuilder`). - -Built-in safeguard against accidental spam in non-production environments: when `IsProd = false`, all emails are redirected to the `CatchAll` address instead of real recipients. - --- ## Instalacja diff --git a/docs/Libraries/EntityFramework/UnitOfWork.WebApiCore.md b/docs/Libraries/EntityFramework/UnitOfWork.WebApiCore.md index adbb71a..176c19c 100644 --- a/docs/Libraries/EntityFramework/UnitOfWork.WebApiCore.md +++ b/docs/Libraries/EntityFramework/UnitOfWork.WebApiCore.md @@ -5,7 +5,7 @@ --- -## 🇵🇱 Opis +## Opis Integracja Unit of Work z ASP.NET Core Web API — eliminuje boilerplate polegający na ręcznym commicie i rollbacku transakcji w każdym kontrolerze. Biblioteka dostarcza `TransactionFilterAttribute` — globalny ASP.NET Core action filter, który automatycznie: @@ -15,16 +15,6 @@ Integracja Unit of Work z ASP.NET Core Web API — eliminuje boilerplate polegaj Opcjonalnie możesz udekorować akcję atrybutem `[TransactionIsolationLevel(IsolationLevel.Serializable)]`, aby ustawić konkretny poziom izolacji dla danego endpointu. -## 🇬🇧 Description - -ASP.NET Core Web API integration for Unit of Work — eliminates the boilerplate of manually committing and rolling back transactions in every controller. The library provides `TransactionFilterAttribute` — a global ASP.NET Core action filter that automatically: - -- **Opens a transaction** before the controller action executes -- **Commits** on successful completion -- **Rolls back** on exception - -Optionally decorate an action with `[TransactionIsolationLevel(IsolationLevel.Serializable)]` to set a specific isolation level for that endpoint. - --- ## Instalacja diff --git a/docs/Libraries/EntityFramework/index.md b/docs/Libraries/EntityFramework/index.md index c89c167..adf8dd5 100644 --- a/docs/Libraries/EntityFramework/index.md +++ b/docs/Libraries/EntityFramework/index.md @@ -5,7 +5,7 @@ --- -## 🇵🇱 Opis +## Opis Biblioteka dostarcza kompletną implementację wzorca **Unit of Work** na bazie Entity Framework Core. Rozwiązuje problem niekontrolowanego zarządzania transakcjami w aplikacjach wielowarstwowych — zamiast bezpośrednio wywoływać `SaveChanges()` w każdym repozytorium, masz jeden punkt kontroli (`IUnitOfWork`), który zarządza cyklem życia transakcji. @@ -15,16 +15,6 @@ Kluczowe możliwości: - **Hooks** — `IHook` do wykonywania kodu przed/po `SaveChanges` lub commit/rollback transakcji - Obsługa **InMemory** provider na potrzeby testów -## 🇬🇧 Description - -This library provides a complete implementation of the **Unit of Work** pattern on top of Entity Framework Core. It solves the problem of uncontrolled transaction management in multi-layer applications — instead of calling `SaveChanges()` directly in each repository, you have a single control point (`IUnitOfWork`) managing the transaction lifecycle. - -Key capabilities: -- **Transactions** with configurable isolation levels -- **Auditing** — automatic entity change tracking (who changed what) -- **Hooks** — `IHook` to execute code before/after `SaveChanges` or transaction commit/rollback -- **InMemory** provider support for testing - --- ## Instalacja diff --git a/docs/Libraries/ExceptionHandling/index.md b/docs/Libraries/ExceptionHandling/index.md index 0d5e311..3e289e3 100644 --- a/docs/Libraries/ExceptionHandling/index.md +++ b/docs/Libraries/ExceptionHandling/index.md @@ -5,7 +5,7 @@ --- -## 🇵🇱 Opis +## Opis Biblioteka standaryzuje obsługę wyjątków w aplikacjach ASP.NET Core Web API. Rozwiązuje problem niespójnych odpowiedzi błędów — zamiast nieobrobionych stack traces lub przypadkowych formatów JSON, każdy błąd jest konwertowany na ujednoliconą strukturę `ExceptionOrValidationError`. @@ -16,17 +16,6 @@ Dostarcza dwa mechanizmy: Możesz zdefiniować własny `IExceptionHandlingProvider`, który mapuje konkretne typy wyjątków na kody HTTP i komunikaty błędów. -## 🇬🇧 Description - -This library standardizes exception handling in ASP.NET Core Web API applications. It solves the problem of inconsistent error responses — instead of raw stack traces or random JSON formats, every error is converted to a unified `ExceptionOrValidationError` structure. - -Provides two mechanisms: - -- **Middleware** (`ConfigureExceptionHandler`) — global handler intercepting exceptions for the entire application -- **Action Filter** (`HandleExceptionAttribute`) — decorative approach at controller/action level - -You can define your own `IExceptionHandlingProvider` that maps specific exception types to HTTP codes and error messages. - --- ## Instalacja diff --git a/docs/Libraries/MediatR/Caching.md b/docs/Libraries/MediatR/Caching.md index f632010..935ce4d 100644 --- a/docs/Libraries/MediatR/Caching.md +++ b/docs/Libraries/MediatR/Caching.md @@ -5,18 +5,12 @@ --- -## 🇵🇱 Opis +## Opis Lekki pakiet definiujący `ICachableRequest` — marker interface dla requestów MediatR, których wyniki mają być cache'owane. Request implementujący ten interfejs dostarcza metodę `GetCacheKey()`, która generuje unikalny klucz cache dla danej instancji zapytania. Jest to alternatywne podejście do cache'owania w porównaniu z `ICachePolicy` z pakietu `TailoredApps.Shared.MediatR` — prostsze, gdy logika klucza cache jest prosta i może żyć w samym requeście. -## 🇬🇧 Description - -A lightweight package defining `ICachableRequest` — a marker interface for MediatR requests whose results should be cached. A request implementing this interface provides a `GetCacheKey()` method that generates a unique cache key for that particular query instance. - -This is an alternative caching approach compared to `ICachePolicy` from `TailoredApps.Shared.MediatR` — simpler when cache key logic is straightforward and can live in the request itself. - --- ## Instalacja diff --git a/docs/Libraries/MediatR/Email.md b/docs/Libraries/MediatR/Email.md index 9c5e89a..c57d21e 100644 --- a/docs/Libraries/MediatR/Email.md +++ b/docs/Libraries/MediatR/Email.md @@ -5,18 +5,12 @@ --- -## 🇵🇱 Opis +## Opis Integracja wysyłania emaili z pipeline MediatR. Biblioteka dostarcza command `SendMail` oraz jego handler `SendMailCommandHandler`, dzięki czemu wysyłka emaila staje się naturalną częścią architektury CQRS — możesz wysłać email przez `_mediator.Send(new SendMail { ... })` bez bezpośredniej zależności od `IEmailProvider`. To podejście umożliwia łatwe wzbogacenie procesu wysyłki o logowanie, retry i auditing z poziomu pipeline behaviors, bez modyfikowania handlera. -## 🇬🇧 Description - -MediatR pipeline integration for email sending. The library provides the `SendMail` command and its `SendMailCommandHandler`, making email sending a natural part of CQRS architecture — you can send an email via `_mediator.Send(new SendMail { ... })` without a direct dependency on `IEmailProvider`. - -This approach makes it easy to enrich the sending process with logging, retry, and auditing from the pipeline behaviors level, without modifying the handler. - --- ## Instalacja diff --git a/docs/Libraries/MediatR/ML.md b/docs/Libraries/MediatR/ML.md index d410278..2a9387f 100644 --- a/docs/Libraries/MediatR/ML.md +++ b/docs/Libraries/MediatR/ML.md @@ -5,18 +5,12 @@ --- -## 🇵🇱 Opis +## Opis Biblioteka integruje klasyfikację obrazów przez **ML.NET** z pipeline MediatR. Dostarcza gotowe komendy (`ClassifyImage`, `TrainImageClassificationModel`) i ich handlery, dzięki czemu klasyfikacja obrazów staje się pełnoprawnym elementem architektury CQRS. Pod spodem działa `ImageClassificationService` oparty na ML.NET z poolem silników predykcji (`PredictionEnginePool`) dla wydajnej współbieżnej inferencji. Biblioteka obsługuje też trening modelu z poziomu aplikacji — możesz wytrenować nowy model podając zestaw oznaczonych obrazów. -## 🇬🇧 Description - -This library integrates **ML.NET** image classification with the MediatR pipeline. It provides ready-made commands (`ClassifyImage`, `TrainImageClassificationModel`) and their handlers, making image classification a first-class citizen in CQRS architecture. - -Under the hood, `ImageClassificationService` uses ML.NET with a `PredictionEnginePool` for efficient concurrent inference. The library also supports in-app model training — you can train a new model by providing a labeled image dataset. - --- ## Instalacja diff --git a/docs/Libraries/MediatR/PagedRequest.md b/docs/Libraries/MediatR/PagedRequest.md index d5ae9bc..5623452 100644 --- a/docs/Libraries/MediatR/PagedRequest.md +++ b/docs/Libraries/MediatR/PagedRequest.md @@ -5,18 +5,12 @@ --- -## 🇵🇱 Opis +## Opis Biblioteka dostarcza bazową klasę `PagedAndSortedRequest` dla requestów MediatR, które wymagają paginacji i sortowania. Standaryzuje parametry stronicowania (`Page`, `Count`) i sortowania (`SortField`, `SortDir`) we wszystkich zapytaniach listowych aplikacji. Klasa jest ściśle zintegrowana z biblioteką `TailoredApps.Shared.Querying` — wymaga, aby `TQuery` dziedziczyło po `QueryBase`, a `TResponse` implementowało `IPagedResult`. -## 🇬🇧 Description - -This library provides a base `PagedAndSortedRequest` class for MediatR requests that require pagination and sorting. It standardizes paging (`Page`, `Count`) and sorting (`SortField`, `SortDir`) parameters across all list queries in the application. - -The class is tightly integrated with `TailoredApps.Shared.Querying` — requires `TQuery` to inherit from `QueryBase` and `TResponse` to implement `IPagedResult`. - --- ## Instalacja diff --git a/docs/Libraries/MediatR/index.md b/docs/Libraries/MediatR/index.md index d5ba97c..3892745 100644 --- a/docs/Libraries/MediatR/index.md +++ b/docs/Libraries/MediatR/index.md @@ -5,7 +5,7 @@ --- -## 🇵🇱 Opis +## Opis Biblioteka dostarcza gotowy zestaw **pipeline behaviors** dla MediatR, które pokrywają najczęstsze potrzeby aplikacji enterprise: logowanie, walidację, cache'owanie, fallback i retry. Zamiast ręcznie implementować te cross-cutting concerns w każdym handlerze, rejestrujesz je raz przez `PipelineRegistration` i masz je dla wszystkich requestów. @@ -13,14 +13,6 @@ Behaviory działają w kolejności: **Logging → Validation → Caching → Fal Biblioteka wspiera mechanizm auto-discovery (przez Scrutor) — cache policies, fallback handlers i retry konfiguracje są skanowane i rejestrowane automatycznie ze wskazanego assembly. -## 🇬🇧 Description - -This library provides a ready-made set of **pipeline behaviors** for MediatR that cover the most common enterprise application needs: logging, validation, caching, fallback, and retry. Instead of manually implementing these cross-cutting concerns in every handler, register them once via `PipelineRegistration` and they apply to all requests. - -Behaviors execute in order: **Logging → Validation → Caching → Fallback → Retry → Handler**. - -The library supports auto-discovery (via Scrutor) — cache policies, fallback handlers, and retry configurations are automatically scanned and registered from the specified assembly. - --- ## Instalacja diff --git a/docs/Libraries/Payments/index.md b/docs/Libraries/Payments/index.md index a246f00..ce65f66 100644 --- a/docs/Libraries/Payments/index.md +++ b/docs/Libraries/Payments/index.md @@ -5,7 +5,7 @@ --- -## 🇵🇱 Opis +## Opis Biblioteka dostarcza zunifikowaną abstrakcję do integracji z bramkami płatności w aplikacjach .NET. Zamiast pisać osobną logikę dla każdego operatora płatności, programujesz do wspólnego interfejsu `IPaymentService`, który automatycznie kieruje żądania do właściwego providera na podstawie klucza (`"Stripe"`, `"PayU"`, `"CashBill"` itd.). @@ -18,19 +18,6 @@ Kluczowe elementy architektury: Biblioteka `TailoredApps.Shared.Payments` to samo serce — konkretne implementacje providerów są w osobnych pakietach `TailoredApps.Shared.Payments.Provider.*`. -## 🇬🇧 Description - -This library provides a unified abstraction for payment gateway integration in .NET applications. Instead of writing separate logic for each payment operator, you program against the common `IPaymentService` interface, which automatically routes requests to the correct provider based on its key (`"Stripe"`, `"PayU"`, `"CashBill"`, etc.). - -Key architectural elements: - -- **`IPaymentProvider`** — contract for each provider (channels, payment initiation, status, webhook) -- **`IWebhookPaymentProvider`** — extension for providers supporting webhooks with signature verification -- **`IPaymentService`** — facade aggregating all registered providers -- **`PaymentOptionsBuilder`** — fluent API for registering providers in DI - -`TailoredApps.Shared.Payments` is the core — concrete provider implementations are in separate `TailoredApps.Shared.Payments.Provider.*` packages. - --- ## Instalacja diff --git a/docs/Libraries/Querying/index.md b/docs/Libraries/Querying/index.md index b418327..66d2b32 100644 --- a/docs/Libraries/Querying/index.md +++ b/docs/Libraries/Querying/index.md @@ -5,7 +5,7 @@ --- -## 🇵🇱 Opis +## Opis Biblioteka dostarcza zestaw bazowych klas i interfejsów do budowania stronicowanych i sortowanych zapytań w aplikacjach .NET. Standaryzuje strukturę zapytań listowych w całej aplikacji — zamiast przekazywać `page`, `pageSize`, `sortField` jako oddzielne parametry w każdym miejscu, masz jeden spójny kontrakt. @@ -15,16 +15,6 @@ Kluczowe typy: - **`IPagedResult`** — kontrakt wynikowy ze stronicowaniem: kolekcja + łączna liczba - **`SortDirection`** — enum `Asc`/`Desc`/`Undefined` -## 🇬🇧 Description - -This library provides a set of base classes and interfaces for building paged and sorted queries in .NET applications. It standardizes the structure of list queries across the entire application — instead of passing `page`, `pageSize`, `sortField` as separate parameters everywhere, you have one consistent contract. - -Key types: -- **`QueryBase`** — abstract base class for query filter objects -- **`PagedAndSortedQuery`** — base class combining filtering, pagination, and sorting -- **`IPagedResult`** — result contract with pagination: collection + total count -- **`SortDirection`** — `Asc`/`Desc`/`Undefined` enum - --- ## Instalacja diff --git a/mkdocs-en.yml b/mkdocs-en.yml new file mode 100644 index 0000000..395579d --- /dev/null +++ b/mkdocs-en.yml @@ -0,0 +1,158 @@ +# Project information +site_name: "Tailored Apps. .NET Shared Components" +site_description: "Reusable components for webapps" +site_author: "Tailored Apps." +site_url: "https://shared.tailoredapps.pl/en/" + +# Copyright +copyright: "© 2022 Tailored Apps" + +# Repository +repo_name: "SharedComponents" +repo_url: "https://github.com/tailored-apps/SharedComponents" +edit_uri: "https://github.com/tailored-apps/SharedComponents/edit/master/docs-en" + +# Directories +docs_dir: "docs-en" +site_dir: "site/en" +extra_css: + - stylesheets/extra.css + +# Navigation +nav: + - Home: index.md + - Libraries: + - DateTime: Libraries/DateTime/index.md + - Email: + - Overview: Libraries/Email/index.md + - Models: Libraries/Email/Models.md + - Office365: Libraries/Email/Office365.md + - EntityFramework: + - Overview: Libraries/EntityFramework/index.md + - UnitOfWork.WebApiCore: Libraries/EntityFramework/UnitOfWork.WebApiCore.md + - ExceptionHandling: Libraries/ExceptionHandling/index.md + - MediatR: + - Overview: Libraries/MediatR/index.md + - Caching: Libraries/MediatR/Caching.md + - Email: Libraries/MediatR/Email.md + - ML: Libraries/MediatR/ML.md + - PagedRequest: Libraries/MediatR/PagedRequest.md + - Payments: + - Overview: Libraries/Payments/index.md + - Providers: + - Adyen: Libraries/Payments/Providers/Adyen.md + - CashBill: Libraries/Payments/Providers/CashBill.md + - HotPay: Libraries/Payments/Providers/HotPay.md + - PayNow: Libraries/Payments/Providers/PayNow.md + - PayU: Libraries/Payments/Providers/PayU.md + - Przelewy24: Libraries/Payments/Providers/Przelewy24.md + - Revolut: Libraries/Payments/Providers/Revolut.md + - Stripe: Libraries/Payments/Providers/Stripe.md + - Tpay: Libraries/Payments/Providers/Tpay.md + - Querying: Libraries/Querying/index.md + - Contributing: contributing.md + +# Configuration +theme: + name: material + language: en + features: + - announce.dismiss + - content.action.edit + - content.action.view + - content.code.annotate + - content.code.copy + - content.tooltips + - navigation.footer + - navigation.indexes + - navigation.sections + - navigation.tabs + - navigation.top + - navigation.tracking + - search.highlight + - search.share + - search.suggest + - toc.follow + palette: + - scheme: default + primary: indigo + accent: indigo + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - scheme: slate + primary: indigo + accent: indigo + toggle: + icon: material/brightness-4 + name: Switch to light mode + font: + text: Roboto + code: Roboto Mono + favicon: assets/favicon.png + icon: + logo: logo + +# Plugins +plugins: + - search: + separator: '[\s\-,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])' + +# Customization +extra: + annotate: + json: [.s2] + analytics: + provider: google + property: GOOGLE_ANALYTICS_KEY + social: + - icon: fontawesome/brands/github + link: https://github.com/tailored-apps/SharedComponents + alternate: + - name: Polski + link: / + lang: pl + - name: English + link: /en/ + lang: en + +markdown_extensions: + - abbr + - admonition + - attr_list + - def_list + - footnotes + - md_in_html + - toc: + permalink: true + - pymdownx.arithmatex: + generic: true + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret + - pymdownx.details + - pymdownx.emoji: + emoji_generator: !!python/name:material.extensions.emoji.to_svg + emoji_index: !!python/name:material.extensions.emoji.twemoji + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.keys + - pymdownx.magiclink: + repo_url_shorthand: true + user: squidfunk + repo: mkdocs-material + - pymdownx.mark + - pymdownx.smartsymbols + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: + alternate_style: true + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde diff --git a/mkdocs.yml b/mkdocs.yml index c9e1e94..365ad66 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -119,6 +119,20 @@ extra: social: - icon: fontawesome/brands/github link: https://github.com/tailored-apps/SharedComponents + alternate: + - name: Polski + link: / + lang: pl + - name: English + link: /en/ + lang: en + alternate: + - name: Polski + link: / + lang: pl + - name: English + link: /en/ + lang: en # Extensions markdown_extensions: