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
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,29 @@ The module enable / disable hooks were always mutations, so nothing changes in y

The gateway still answers the old `GET` on these paths for now (it logs a deprecation warning) and will drop it after gateway 0.2, so upgrade the core SDK before then. The scheduler switch moved to `PUT` earlier (core SDK 4.6.0). The code module (`hub.code`) has no hooks in this package yet; call `useNorbix().hub.code.enableCode(...)` directly.

### Logs (core SDK 4.31.0)

Every `hub.logs` method has a hook, and `api.logs.writeLogs` writes your app's own lines.

| Hook | SDK method | Cache |
| ----------------------------------- | --------------------------------- | ------------------------------ |
| `useGetLogsQuery` | `hub.logs.getLogs` | provides `Logs` |
| `useGetLogQuery` | `hub.logs.getLog` (`{ id }`) | provides `Logs` |
| `useGetLogsByCorrelationIdQuery` | `hub.logs.getLogsByCorrelationId` | provides `Logs` |
| `useCleanLogsMutation` | `hub.logs.cleanLogs` | invalidates `Logs` |
| `useGetLogSettingsQuery` | `hub.logs.getLogSettings` | provides `LogsSettings` |
| `useSaveLogSettingsMutation` | `hub.logs.saveLogSettings` | invalidates `LogsSettings` |
| `useTestLoggingIntegrationMutation` | `hub.logs.testLoggingIntegration` | invalidates `LogsIntegrations` |
| `useWriteLogsMutation` | `api.logs.writeLogs` | invalidates `Logs` |

`useSaveLogSettingsMutation` changes only the settings you send; a field left out keeps its value. After
`useSaveLoggingIntegrationMutation`, call `useTestLoggingIntegrationMutation` with the integration id —
nothing is delivered to a logging integration until its last test passed. `useWriteLogsMutation` takes
`{ entries }` (1–100 lines: `title` required, `level`, `message`, `eventCode`, `correlationId`,
`timestamp`, `meta`); on a 429 `CM-ERRORS-LOGGING-003` its `error.retryAfterSeconds` says how many seconds
to wait before sending again. `useEndUserOwnRecordsReportMutation` (`hub.database.endUserOwnRecordsReport`,
new in the same core SDK) lists every schema's end-user-own-records switch, or turns it on with `dryRun: false`.

### Bulk record writes (core SDK 4.11.0)

`useUpdateManyRecordsMutation` / `useDeleteManyRecordsMutation` (Hub) and `useUpdateManyMutation` / `useDeleteManyMutation` (Api) take a new optional `allRecords` argument. An empty filter (`{}`, or no filter on update) matches the whole collection and the gateway refuses it with `CM-ERRORS-DATABASE-037` unless you pass `allRecords: true`:
Expand Down Expand Up @@ -1161,7 +1184,7 @@ norbixApi.injectEndpoints({
prefix: 'Logging',
tag: 'LogsIntegrations',
namespace: (n) => n.hub.logs,
include: { test: false },
include: { test: true },
}),
}),
overrideExisting: false,
Expand All @@ -1179,7 +1202,7 @@ This produces all hooks automatically — `useGetEmailIntegrationsQuery`, `useSa
| Payments | `Payments` | `n.hub.payments` | `PaymentIntegrations` | has `test` + `confirmHumanDelivery` |
| Code | `Code` | `n.hub.code` | `CodeIntegrations` | has `test` |
| Membership | `Membership` | `n.hub.membership` | `MembershipIntegrations` | no `test` |
| Logs | `Logging` _(uses `Logging`, not `Logs`)_ | `n.hub.logs` | `LogsIntegrations` | no `test` |
| Logs | `Logging` _(uses `Logging`, not `Logs`)_ | `n.hub.logs` | `LogsIntegrations` | has `test` (deliveries start after a passed test) |
| Database | `Database` | `n.hub.database` | `DatabaseIntegrations` | already wired in package |

**Trade-off.** The helper is terse but loses static request/response typing — generated hooks return `any` data, because endpoint keys are computed at runtime. If you need full types on a specific integration surface (autocompletion in your IDE), define those endpoints manually instead, following the canonical shape used by `hub.database` integrations in `src/hooks/hub/database.ts`.
Expand Down
172 changes: 86 additions & 86 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"prepublishOnly": "npm run build"
},
"peerDependencies": {
"@norbix.ai/ts": ">=4.29.0",
"@norbix.ai/ts": ">=4.31.0",
"@reduxjs/toolkit": ">=2.0.0",
"react": ">=18.0.0",
"react-redux": ">=9.0.0"
Expand All @@ -68,7 +68,7 @@
"@commitlint/cli": "^21.2.2",
"@commitlint/config-conventional": "^21.2.2",
"@eslint/js": "^10.0.1",
"@norbix.ai/ts": "^4.29.0",
"@norbix.ai/ts": "^4.31.0",
"@reduxjs/toolkit": "^2.12.0",
"@semantic-release/commit-analyzer": "^13.0.1",
"@semantic-release/github": "^12.0.9",
Expand Down
1 change: 1 addition & 0 deletions src/createNorbixApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export function createNorbixApi(
'Scheduler',
'Triggers',
'Logs',
'LogsSettings',
'LogsIntegrations',
] satisfies AllTags[],
endpoints: (builder) => buildEndpoints(builder),
Expand Down
9 changes: 9 additions & 0 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ export interface SerializedNorbixError {
errors: SerializedNorbixErrorItem[];
/** Originating URL when known, useful for debugging. */
url?: string;
/**
* Seconds to wait before sending again, from the answer's `Retry-After`
* header (e.g. a 429 from `writeLogs`). Only when the gateway sent one.
*/
retryAfterSeconds?: number;
}

/**
Expand All @@ -57,6 +62,7 @@ export function serializeNorbixError(err: unknown): SerializedNorbixError {
fieldErrors?: SerializedNorbixErrorItem[];
errors?: SerializedNorbixErrorItem[];
url?: string;
retryAfterSeconds?: number;
};
const status =
typeof e.status === 'number' ? e.status : typeof e.httpStatus === 'number' ? e.httpStatus : 0;
Expand All @@ -75,6 +81,9 @@ export function serializeNorbixError(err: unknown): SerializedNorbixError {
fieldErrors: list,
errors: list,
url: e.url,
...(typeof e.retryAfterSeconds === 'number'
? { retryAfterSeconds: e.retryAfterSeconds }
: {}),
};
}
const message = typeof err === 'string' ? err : 'Unknown Norbix error';
Expand Down
23 changes: 23 additions & 0 deletions src/hooks/api/logs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { Norbix } from '@norbix.ai/ts';

import type { Builder } from '../index.js';
import type { Arg, Result } from '../types.js';

type WriteLogs = Norbix['api']['logs']['writeLogs'];

/**
* `api.logs` — your app's own log lines (API host), 1:1 with the core SDK.
*
* `writeLogs({ entries })` sends 1–100 lines; the answer `{ accepted }` comes
* at once (the lines are queued). A 429 `CM-ERRORS-LOGGING-003` means the
* project's per-minute quota is used up: the hook's `error` carries
* `retryAfterSeconds` (the `Retry-After` header).
*
* Tags: invalidates `Logs`, so an open log list refetches.
*/
export const apiLogs = (b: Builder) => ({
writeLogs: b.mutation<Result<WriteLogs>, Arg<WriteLogs>>({
query: (args) => (norbix) => norbix.api.logs.writeLogs(args),
invalidatesTags: ['Logs'],
}),
});
13 changes: 12 additions & 1 deletion src/hooks/hub/database.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// AUTO-GENERATED — full coverage of `norbix.hub.database` (73 endpoints).
// AUTO-GENERATED — full coverage of `norbix.hub.database` (74 endpoints).
// Synced from the norbix core SDK surface. Re-run the hook sync to refresh.
import type { Norbix } from '@norbix.ai/ts';

Expand Down Expand Up @@ -34,6 +34,7 @@ type RenameDatabaseSchema = Norbix['hub']['database']['renameDatabaseSchema'];
type SaveDatabaseSchema = Norbix['hub']['database']['saveDatabaseSchema'];
type UpdateDatabaseSchemaDraft = Norbix['hub']['database']['updateDatabaseSchemaDraft'];
type UpdateDatabaseSchemaSettings = Norbix['hub']['database']['updateDatabaseSchemaSettings'];
type EndUserOwnRecordsReport = Norbix['hub']['database']['endUserOwnRecordsReport'];
type DeleteDatabaseIntegration = Norbix['hub']['database']['deleteDatabaseIntegration'];
type DisableDatabaseIntegration = Norbix['hub']['database']['disableDatabaseIntegration'];
type EnableDatabaseIntegration = Norbix['hub']['database']['enableDatabaseIntegration'];
Expand Down Expand Up @@ -563,4 +564,14 @@ export const hubDatabase = (b: Builder) => ({
requestImportUploadUrl: b.mutation<Result<RequestImportUploadUrl>, Arg<RequestImportUploadUrl>>({
query: (args) => (norbix) => norbix.hub.database.requestImportUploadUrl(args),
}),

// Lists every schema's endUserOwnRecords switch; with dryRun: false it turns
// the switch on for the schemas named in `enable`, so it is a mutation.
endUserOwnRecordsReport: b.mutation<
Result<EndUserOwnRecordsReport>,
Arg<EndUserOwnRecordsReport>
>({
query: (args) => (norbix) => norbix.hub.database.endUserOwnRecordsReport(args),
invalidatesTags: ['DatabaseSchemas'],
}),
});
62 changes: 56 additions & 6 deletions src/hooks/hub/logs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// AUTO-GENERATED — full coverage of `norbix.hub.logs` (10 endpoints).
// Synced from the norbix core SDK surface. Re-run the hook sync to refresh.
// Full coverage of `norbix.hub.logs` (15 endpoints), 1:1 with the core SDK.
// Written by hand: this repo has no hook sync script (the header used to say
// "re-run the hook sync"; there is none — add a row here for a new SDK method).
import type { Norbix } from '@norbix.ai/ts';

import type { Builder } from '../index.js';
Expand All @@ -15,9 +16,21 @@ type GetLoggingIntegrations = Norbix['hub']['logs']['getLoggingIntegrations'];
type SaveLoggingIntegration = Norbix['hub']['logs']['saveLoggingIntegration'];
type TestLoggingIntegration = Norbix['hub']['logs']['testLoggingIntegration'];
type GetLogsByCorrelationId = Norbix['hub']['logs']['getLogsByCorrelationId'];
type GetLogs = Norbix['hub']['logs']['getLogs'];
type GetLog = Norbix['hub']['logs']['getLog'];
type CleanLogs = Norbix['hub']['logs']['cleanLogs'];
type GetLogSettings = Norbix['hub']['logs']['getLogSettings'];
type SaveLogSettings = Norbix['hub']['logs']['saveLogSettings'];

/**
* `hub.logs` — 10 endpoints, 1:1 with the core SDK.
* `hub.logs` — 15 endpoints, 1:1 with the core SDK.
*
* Tags:
* - `Logs` — log entries and the module switch.
* - `LogsSettings` — the project's log settings.
* - `LogsIntegrations` — logging integrations. Nothing is delivered to an
* integration until its last test passed: call `testLoggingIntegration`
* after every `saveLoggingIntegration`.
*/
export const hubLogs = (b: Builder) => ({
disableLogging: b.mutation<Result<DisableLogging>, Arg<DisableLogging>>({
Expand All @@ -30,17 +43,26 @@ export const hubLogs = (b: Builder) => ({
invalidatesTags: ['Logs'],
}),

deleteLoggingIntegration: b.mutation<Result<DeleteLoggingIntegration>, Arg<DeleteLoggingIntegration>>({
deleteLoggingIntegration: b.mutation<
Result<DeleteLoggingIntegration>,
Arg<DeleteLoggingIntegration>
>({
query: (args) => (norbix) => norbix.hub.logs.deleteLoggingIntegration(args),
invalidatesTags: ['LogsIntegrations'],
}),

disableLoggingIntegration: b.mutation<Result<DisableLoggingIntegration>, Arg<DisableLoggingIntegration>>({
disableLoggingIntegration: b.mutation<
Result<DisableLoggingIntegration>,
Arg<DisableLoggingIntegration>
>({
query: (args) => (norbix) => norbix.hub.logs.disableLoggingIntegration(args),
invalidatesTags: ['LogsIntegrations'],
}),

enableLoggingIntegration: b.mutation<Result<EnableLoggingIntegration>, Arg<EnableLoggingIntegration>>({
enableLoggingIntegration: b.mutation<
Result<EnableLoggingIntegration>,
Arg<EnableLoggingIntegration>
>({
query: (args) => (norbix) => norbix.hub.logs.enableLoggingIntegration(args),
invalidatesTags: ['LogsIntegrations'],
}),
Expand Down Expand Up @@ -69,4 +91,32 @@ export const hubLogs = (b: Builder) => ({
query: (args) => (norbix) => norbix.hub.logs.getLogsByCorrelationId(args),
providesTags: ['Logs'],
}),

getLogs: b.query<Result<GetLogs>, Arg<GetLogs>>({
query: (args) => (norbix) => norbix.hub.logs.getLogs(args),
providesTags: ['Logs'],
}),

/** Arg: `{ id }` — the id of a `getLogs` row. */
getLog: b.query<Result<GetLog>, Arg<GetLog>>({
query: (args) => (norbix) => norbix.hub.logs.getLog(args),
providesTags: ['Logs'],
}),

/** Deletes every entry stored in the project's Norbix Logging. */
cleanLogs: b.mutation<Result<CleanLogs>, Arg<CleanLogs>>({
query: (args) => (norbix) => norbix.hub.logs.cleanLogs(args),
invalidatesTags: ['Logs'],
}),

getLogSettings: b.query<Result<GetLogSettings>, Arg<GetLogSettings>>({
query: (args) => (norbix) => norbix.hub.logs.getLogSettings(args),
providesTags: ['LogsSettings'],
}),

/** Only the settings you send change; a field left out keeps its value. */
saveLogSettings: b.mutation<Result<SaveLogSettings>, Arg<SaveLogSettings>>({
query: (args) => (norbix) => norbix.hub.logs.saveLogSettings(args),
invalidatesTags: ['LogsSettings'],
}),
});
3 changes: 3 additions & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { apiApikeys } from './api/apikeys.js';
import { apiAuth } from './api/auth.js';
import { apiDatabase } from './api/database.js';
import { apiFiles } from './api/files.js';
import { apiLogs } from './api/logs.js';
import { apiMembership } from './api/membership.js';
import { apiPayments } from './api/payments.js';
import { apiPublic } from './api/public.js';
Expand Down Expand Up @@ -116,6 +117,7 @@ export type AllTags =
| 'Scheduler'
| 'Triggers'
| 'Logs'
| 'LogsSettings'
| 'LogsIntegrations';

/** Builder type the per-module endpoint factories accept. */
Expand Down Expand Up @@ -145,6 +147,7 @@ export function buildEndpoints(builder: Builder) {
...apiDatabase(builder),
...apiApikeys(builder),
...apiFiles(builder),
...apiLogs(builder),
...apiPayments(builder),
...apiPublic(builder),
...apiPush(builder),
Expand Down
6 changes: 3 additions & 3 deletions tests/database.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ const RECORD_WRITES = [
describe('hubDatabase', () => {
const endpoints = hubDatabase(fakeBuilder()) as unknown as Record<string, Def>;

it('has one hook per hub.database method of @norbix.ai/ts, same name (74)', () => {
it('has one hook per hub.database method of @norbix.ai/ts, same name (75)', () => {
const methods = sdkMethods('hub');
expect(methods).toHaveLength(74);
expect(methods).toHaveLength(75);
expect(Object.keys(endpoints).sort()).toEqual(methods);
});

Expand Down Expand Up @@ -262,7 +262,7 @@ describe('database hooks in the flat endpoint map', () => {
await all[key].query({})(viaFlatMap.client);
}
expect(viaFlatMap.calls).toEqual(viaFactory.calls);
expect(viaFlatMap.calls.filter((c) => c.method.startsWith('hub.'))).toHaveLength(74);
expect(viaFlatMap.calls.filter((c) => c.method.startsWith('hub.'))).toHaveLength(75);
});
});

Expand Down
Loading