Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ The public endpoint surface is generated from gateway DTOs at compile time. The
| `database` | 22 | Collection CRUD (`FindAsync`, `FindOneAsync`, `FindOwnAsync`, insert / update / replace / delete one or many), count, distinct, aggregate, saved aggregate execution (`ExecuteAggregateAsync`), `ChangeResponsibilityAsync`, schema reads, taxonomy and term reads (`FindTermsAsync`, `FindTermsChildrenAsync`, `FindTaxonomyTreeAsync`, `FindTermTreeAsync`, `FindMergedTermTreeAsync`). |
| `echo` | 1 | Smoke-test echo endpoint. |
| `files` | 13 | List, info, signed URL, upload URL + commit, download, delete one / many, public links, and a file by its stored id (`GetFileByIdAsync`). |
| `logs` | 1 | `WriteLogsAsync` sends your app's own log entries to the project's logs: 1–100 `WriteLogEntry` per call (`Title` up to 200 characters; optional `Level` `information` / `warning` / `error`, `Message`, `EventCode` stored as `app:<code>`, `CorrelationId`, `Timestamp`, `Meta` up to 20 keys). It answers `Accepted` at once — the entries are queued. Over the project's per-minute quota it throws `NorbixException` with `HttpStatus` 429 and `CM-ERRORS-LOGGING-003` (wait a minute and retry); `CM-ERRORS-LOGGING-002` means the Logs module is off. |
| `membership` | 18 | User CRUD, registration, preferences, roles, and permissions. |
| `payments` | 4 | Payment orders and checkout: `CreatePaymentOrderAsync`, `GetPaymentOrderAsync`, `GetPaymentOrdersAsync`, `StartPaymentCheckoutAsync`. See [Payment orders and checkout](#payment-orders-and-checkout). |
| `public` | 2 | The project's public config and legal documents, read by the Admin Portal before sign-in (`GetPublicProjectConfigAsync`, `GetPublicProjectLegalAsync`). See [docs/hub/project.md](./docs/hub/project.md#public-config-and-legal-no-sign-in). |
Expand All @@ -335,7 +336,7 @@ The public endpoint surface is generated from gateway DTOs at compile time. The
| `email` | 2 | Public e-mail link endpoints: `OneClickUnsubscribeAsync` and `GetEmailPreferencesByLinkAsync` (reads the marketing preferences behind a signed unsubscribe link; pass the link's `Token`). Both are public links: they work on a client with no API key (the key is sent only when there is one), like the three `Preview*NotificationAsync` methods. |
| `files` | 23 | File storage integrations, triggers, public links, a file by its stored id (`GetFileByIdAsync`), and module settings. |
| `internal` | 1 | Internal type-generation endpoint. |
| `logs` | 9 | Logging integrations and module settings. |
| `logs` | 15 | Module on / off, the log list (`GetLogsAsync`), one entry by id (`GetLogAsync`), one flow by correlation id (`GetLogsByCorrelationIdAsync`), `CleanLogsAsync`, log settings, and logging integrations (MongoDB, Datadog, AWS CloudWatch, …). After saving an integration call `TestLoggingIntegrationAsync`: nothing is delivered to an integration until its last test passed. |
| `membership` | 25 | Roles, policies, users, preferences, integrations, triggers. |
| `notifications` | 123 | Email, push and SMS templates, integrations, campaigns, devices, settings. Push: [docs/hub/push.md](./docs/hub/push.md). SMS: [docs/hub/sms.md](./docs/hub/sms.md). |
| `oauth` | 5 | The gateway's OAuth server routes (register, authorize, token, revoke). |
Expand Down
57 changes: 57 additions & 0 deletions src/Norbix.Sdk.Types/Generated/Api.dtos.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1338,6 +1338,63 @@ public partial class UseFileRequest
public virtual bool? ReassignOwner { get; set; }
}

public partial class WriteLogEntry
{
///<summary>
///information (default), warning or error.
///</summary>
public virtual string? Level { get; set; }

///<summary>
///A short line, 1–200 characters — what the Logs list shows.
///</summary>
public virtual string Title { get; set; }

///<summary>
///Details, at most 8 000 characters.
///</summary>
public virtual string? Message { get; set; }

///<summary>
///Your own code for this kind of line, e.g. order:paid — lower case letters, digits and _ . : -, at most 64. Stored as app:<code>; app:log when empty.
///</summary>
public virtual string? EventCode { get; set; }

///<summary>
///Groups your lines into one flow in the Logs details view: at most 64 letters, digits, _ or -. Empty = the id of this request.
///</summary>
public virtual string? CorrelationId { get; set; }

///<summary>
///When it happened (UTC). Empty = when Norbix received it. At most 7 days old and at most 5 minutes ahead.
///</summary>
public virtual DateTime? Timestamp { get; set; }

///<summary>
///Extra fields, at most 20; key at most 64, value at most 2 000 characters. Values under secret-looking keys (password, token, apiKey …) are masked.
///</summary>
public virtual Dictionary<string, string>? Meta { get; set; }
}

///<summary>
///Send your app's own log entries to the project's logs.
///</summary>
[NorbixRoute("/{version}/logs", "POST")]
public partial class WriteLogs
: CodeMashRequestBase, INorbixRequest<WriteLogsResponse>
{
///<summary>
///1–100 entries.
///</summary>
public virtual List<WriteLogEntry> Entries { get; set; } = [];
}

public partial class WriteLogsResponse
: ResponseBase
{
public virtual int Accepted { get; set; }
}

///<summary>
///Membership · Passkey
///</summary>
Expand Down
55 changes: 49 additions & 6 deletions src/Norbix.Sdk.Types/Generated/Hub.dtos.cs
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,8 @@ public partial class InternalsTypeGen
public virtual FakeCodeIntegrationRequest? Typegen_282_FakeCodeIntegrationRequest { get; set; }
public virtual CodeFunctionSchedulerTaskRequest? Typegen_283_CodeFunctionSchedulerTaskRequest { get; set; }
public virtual MarketplaceFunctionSchedulerTaskRequest? Typegen_284_MarketplaceFunctionSchedulerTaskRequest { get; set; }
public virtual AwsCloudWatchLoggingIntegrationRequest? Typegen_285_AwsCloudWatchLoggingIntegrationRequest { get; set; }
public virtual AwsCloudWatchLoggingIntegrationDto? Typegen_286_AwsCloudWatchLoggingIntegrationDto { get; set; }
}

[DataContract]
Expand Down Expand Up @@ -6167,6 +6169,9 @@ public partial class TenantLogEntryDto
[DataMember]
public virtual string Env { get; set; }

[DataMember]
public virtual string Origin { get; set; }

[DataMember]
public virtual IReadOnlyDictionary<string, string>? Meta { get; set; }
}
Expand Down Expand Up @@ -6194,6 +6199,14 @@ public partial class AmqpLoggingIntegrationDto
public virtual string RoutingKey { get; set; }
}

public partial class AwsCloudWatchLoggingIntegrationDto
: LoggingIntegrationDto
{
public virtual string Region { get; set; }
public virtual string LogGroup { get; set; }
public virtual string LogStream { get; set; }
}

public partial class AwsKinesisLoggingIntegrationDto
: LoggingIntegrationDto
{
Expand Down Expand Up @@ -16874,6 +16887,25 @@ public partial class CleanLogsResponse
{
}

///<summary>
///Fetch one tenant log entry by id
///</summary>
[NorbixRoute("/{version}/logs/{Id}", "GET")]
public partial class GetLog
: CodeMashRequestBase, INorbixRequest<GetLogResponse>
{
///<summary>
///The log entry id (24 hex characters), from a get_logs row.
///</summary>
public virtual string Id { get; set; }
}

public partial class GetLogResponse
: ResponseBase
{
public virtual TenantLogEntryDto? Item { get; set; }
}

///<summary>
///Fetch a filtered, cursor-paged list of tenant log entries
///</summary>
Expand Down Expand Up @@ -16978,19 +17010,19 @@ public partial class SaveLogSettings
: CodeMashRequestBase, INorbixRequest<SaveLogSettingsResponse>
{
///<summary>
///Drop log entries from requests originating from the Norbix studio (cloud dashboard).
///Drop log entries from requests originating from the Norbix studio (cloud dashboard). Not sent = unchanged.
///</summary>
public virtual bool SkipCloudDashboardLogs { get; set; }
public virtual bool? SkipCloudDashboardLogs { get; set; }

///<summary>
///Strip request/response body meta off http:request / http:response log entries.
///Strip request/response body meta off http:request / http:response log entries. Not sent = unchanged.
///</summary>
public virtual bool SkipHttpBodyMeta { get; set; }
public virtual bool? SkipHttpBodyMeta { get; set; }

///<summary>
///Turn on tenant-visible log entries for AI chat turns. Default false.
///Turn on tenant-visible log entries for AI chat turns. Default false. Not sent = unchanged.
///</summary>
public virtual bool AiChatLoggingEnabled { get; set; }
public virtual bool? AiChatLoggingEnabled { get; set; }
}

public partial class SaveLogSettingsResponse
Expand All @@ -17011,6 +17043,17 @@ public partial class AmqpLoggingIntegrationRequest
public virtual string Password { get; set; }
}

public partial class AwsCloudWatchLoggingIntegrationRequest
: LoggingIntegrationRequest
{
public virtual LoggingProvider Provider { get; set; }
public virtual string Region { get; set; }
public virtual string LogGroup { get; set; }
public virtual string LogStream { get; set; }
public virtual string AccessKey { get; set; }
public virtual string SecretKey { get; set; }
}

public partial class AwsKinesisLoggingIntegrationRequest
: LoggingIntegrationRequest
{
Expand Down
62 changes: 62 additions & 0 deletions tests/Norbix.Hub.Tests/HubLogsEndpointTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using Norbix.Sdk.Tests.Helpers;
using Norbix.Sdk.Types.Hub;
using NUnit.Framework;
using VerifyNUnit;

namespace Norbix.Hub.Tests;

/// <summary>
/// The Logs endpoints of the Hub that a reader cares about beyond the coverage
/// snapshot (<c>EndpointCoverageTests.Hub.Logs</c>): one log entry by its id,
/// and the logging integration test — nothing is delivered to an integration
/// until its last test passed.
/// </summary>
[TestFixture]
public sealed class HubLogsEndpointTests
{
private const string LogId = "6716f2a0c3b1e4d5f6a7b8c9";
private const string IntegrationId = "33333333-3333-3333-3333-333333333333";

[Test]
public async Task GetLog_puts_the_id_in_the_path_and_reads_the_entry()
{
using var fixture = NorbixTestFixture.Create();
fixture.Respond(
"/logs/" + LogId,
new
{
item = new
{
id = LogId,
timestamp = "2026-10-21T08:00:00Z",
module = "app",
level = "information",
eventCode = "app:order:paid",
title = "Order paid",
message = "Order ord_1042 paid by card",
correlationId = "checkout-1042",
env = "PROD",
origin = "app",
meta = new Dictionary<string, string> { ["orderId"] = "ord_1042" },
},
});

var response = await fixture.Client.Logs.GetLogAsync(new GetLog { Id = LogId });

await Verifier.Verify(
new { Sent = fixture.LastRequest, Entry = response?.Item },
VerifyConfig.VerifySettings);
}

[Test]
public async Task TestLoggingIntegration_posts_the_integration_id()
{
using var fixture = NorbixTestFixture.Create();
fixture.Respond("/logs/integrations/test", new { });

await fixture.Client.Logs.TestLoggingIntegrationAsync(
new TestLoggingIntegration { IntegrationId = IntegrationId });

await Verifier.Verify(fixture.LastRequest, VerifyConfig.VerifySettings);
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
Target: Hub,
Group: logs,
Count: 14,
Count: 15,
Endpoints: [
{
Target: Hub,
Expand Down Expand Up @@ -91,6 +91,45 @@
}
}
},
{
Target: Hub,
Group: logs,
MethodName: GetLogAsync,
RequestType: GetLog,
HttpMethod: GET,
Path: /{version}/logs/{Id},
PathParams: [
Id
],
IsAccountScoped: false,
IsUnauthenticated: false,
Sent: {
Method: GET,
Path: /v2/logs/{guid},
Query: ?env=test-env,
Headers: {
Authorization: Bearer test-bearer,
norbix-account-id: test-account,
norbix-project-id: test-project
}
},
ResponseType: GetLogResponse,
Response: {
Item: {
Id: Guid_1,
Timestamp: DateTime_1,
Module: test-module,
Level: test-level,
EventCode: test-eventCode,
Title: test-title,
Message: test-message,
CorrelationId: Guid_1
},
ResponseStatus: {
IsSuccess: true
}
}
},
{
Target: Hub,
Group: logs,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
Sent: {
Method: GET,
Path: /v2/logs/6716f2a0c3b1e4d5f6a7b8c9,
Headers: {
Authorization: Bearer test-api-key,
norbix-project-id: test-project
}
},
Entry: {
Id: 6716f2a0c3b1e4d5f6a7b8c9,
Timestamp: DateTime_1,
Module: app,
Level: information,
EventCode: app:order:paid,
Title: Order paid,
Message: Order ord_1042 paid by card,
CorrelationId: checkout-1042,
Env: PROD,
Origin: app,
Meta: {
orderId: ord_1042
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
Method: POST,
Path: /v2/logs/integrations/test,
Headers: {
Authorization: Bearer test-api-key,
Content-Type: application/json; charset=utf-8,
norbix-project-id: test-project
},
Body: {
integrationId: Guid_1
}
}
Loading
Loading