feat(skill): automatically onboard to openapi tooling (#34179)
* feat(skill): automatically onboard to openapi tooling Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com> * test skill against notifications backend Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com> * update URLs to point to main repo and describe multiple client types Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com> * address PR feedback Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com> * revert erroneous changes Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com> * add changeset Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com> * address review feedback: skill doc fixes and entityRef schema dedup Disambiguate generated entityRef/orderField types and correct skill guidance on operationId casing and OpenAPI version support. Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com> * note in skill that handler-side validation should be stripped Reviewers may forget that manual InputError guards and primitive-type checks become dead weight once the OpenAPI validator runs. Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com> --------- Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-notifications-backend': patch
|
||||
---
|
||||
|
||||
Migrated the internal router to be generated from the plugin's OpenAPI specification. The HTTP API is unchanged.
|
||||
@@ -391,6 +391,7 @@ readonly
|
||||
rebase
|
||||
rebasing
|
||||
Recharts
|
||||
recurse
|
||||
redactions
|
||||
Redash
|
||||
redis
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
"name": "plugin-analytics-instrumentation",
|
||||
"description": "Instrument a Backstage frontend plugin with analytics events using the Backstage Analytics API. Use this skill when adding, reviewing, or extending event capture (captureEvent, AnalyticsContext) in plugin components, deciding whether an interaction warrants an event, or writing tests for analytics behavior.",
|
||||
"files": ["SKILL.md"]
|
||||
},
|
||||
{
|
||||
"name": "onboard-to-openapi-server",
|
||||
"description": "Use this skill when the user wants to migrate an existing Backstage backend plugin's hand-written Express router to the typed OpenAPI tooling. Optionally, can also add typed client generation and migrate router tests to the OpenAPI test wrapper.",
|
||||
"files": ["SKILL.md"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
---
|
||||
name: onboard-to-openapi-server
|
||||
description: Use this skill when the user wants to migrate an existing Backstage backend plugin's hand-written Express router to the typed OpenAPI tooling. Optionally, can also add typed client generation and migrate router tests to the OpenAPI test wrapper.
|
||||
---
|
||||
|
||||
# OpenAPI Onboard Plugin
|
||||
|
||||
Onboard an existing Backstage backend plugin to the repo's OpenAPI tooling. The core flow reverse-engineers an `openapi.yaml` from the plugin's existing Express router, generates a server stub via `backstage-repo-tools`, switches the router over to `createOpenApiRouter`, and verifies with `yarn tsc` + the package's tests.
|
||||
|
||||
The OpenAPI spec is the source of truth. The router is the _starting point_: we read it once to derive the spec, then the spec drives generation forward.
|
||||
|
||||
## Up front: confirm scope with the user
|
||||
|
||||
Before writing anything, ask which of the optional steps the user wants. Default to **off** unless the user says yes.
|
||||
|
||||
| Step | Always or optional |
|
||||
| ------------------------------------------------------ | ------------------ |
|
||||
| Inventory router → write `openapi.yaml` | Always |
|
||||
| Generate server stub (`--server`) | Always |
|
||||
| Switch router to `createOpenApiRouter` | Always |
|
||||
| Run `yarn tsc` and the plugin's existing tests | Always |
|
||||
| **Generate a typed client (`--client-package <pkg>`)** | **Optional — ask** |
|
||||
| **Migrate router tests to `wrapServer`** | **Optional — ask** |
|
||||
| Write changesets | Optional — ask |
|
||||
|
||||
When the user picks the minimal flow, skip the corresponding sections below and don't mention them in the final summary.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Run from the monorepo root.
|
||||
- `yarn install` has been run.
|
||||
- The target plugin already has a working Express router (typically `src/service/router.ts` or `src/service/createRouter.ts`) and a passing test for it.
|
||||
- The plugin builds and its tests pass _before_ starting — establish a green baseline first.
|
||||
|
||||
If any prerequisite is missing, stop and tell the user.
|
||||
|
||||
## Inputs
|
||||
|
||||
Ask the user for, or infer:
|
||||
|
||||
- **Target plugin directory** — e.g. `plugins/auth-backend`. Required.
|
||||
- **Plugin id** — used in the spec's `info.title` (e.g. `auth`, `events`). Default to the plugin's `package.json` `backstage.pluginId`.
|
||||
- **Client package directory** — only if the user opted into client generation. Suggest the conventional sibling `*-node` or `*-common` package and confirm.
|
||||
|
||||
## Core workflow
|
||||
|
||||
### Step 1 — Establish a baseline
|
||||
|
||||
1. Confirm a clean working tree with `git status`.
|
||||
2. Run the plugin's existing tests to confirm they pass:
|
||||
```
|
||||
CI=1 yarn test <plugin-dir>/src/service
|
||||
```
|
||||
3. Run `yarn tsc` from the repo root.
|
||||
|
||||
If either fails, stop. Do not start an onboarding on top of a red baseline — fixes will get tangled with the migration.
|
||||
|
||||
### Step 2 — Inventory the router
|
||||
|
||||
Read the router file(s) and produce a complete list of every route. For each route capture:
|
||||
|
||||
- HTTP method and path (including path params).
|
||||
- Path/query/header parameters and which are required.
|
||||
- Request body shape (TypeScript types, validation calls, or JSON schemas — whatever exists).
|
||||
- Response shapes per status code, including error responses.
|
||||
- Auth/permission decorators (`httpAuth.credentials`, `permissions.authorize`, etc.) — these are not in the spec but must be preserved verbatim in the post-migration router.
|
||||
|
||||
Read every helper the router pulls in (request validators, type guards, response builders) so the spec accurately reflects runtime behavior. Do _not_ skip routes mounted via `router.use(subRouter)` — recurse into them.
|
||||
|
||||
Present this inventory to the user before writing any spec. Misreading the router here causes the spec to drift from reality, which then poisons every downstream step.
|
||||
|
||||
### Step 3 — Author `src/schema/openapi.yaml`
|
||||
|
||||
Create `<plugin-dir>/src/schema/openapi.yaml` modeled on existing onboarded plugins:
|
||||
|
||||
- [`plugins/events-backend/src/schema/openapi.yaml`](https://github.com/backstage/backstage/blob/master/plugins/events-backend/src/schema/openapi.yaml) — small, modern reference.
|
||||
- [`plugins/scaffolder-backend/src/schema/openapi.yaml`](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/src/schema/openapi.yaml) — larger, with more parameter and schema reuse.
|
||||
- [`plugins/catalog-backend/src/schema/openapi.yaml`](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/src/schema/openapi.yaml) — largest, good for query-param-heavy endpoints.
|
||||
|
||||
Conventions to follow:
|
||||
|
||||
- `openapi: 3.1.0`.
|
||||
- `info.title` is the plugin id; `info.version: '1'`.
|
||||
- `servers: [{ url: / }]`.
|
||||
- Define a reusable `Error`/`ErrorResponse` under `components.schemas` and `components.responses` and reference it from every error status.
|
||||
- Reuse path parameters via `components.parameters`.
|
||||
- Each operation gets a stable `operationId` (PascalCase verb-noun, e.g. `ListNotifications`, `PostEvent`) — these become the generated client's method names. Match the convention used by the other onboarded Backstage plugins linked above.
|
||||
- `requestBody` content type usually `application/json`; responses likewise.
|
||||
- Required query/path params marked `required: true`.
|
||||
|
||||
Validate the spec is well-formed:
|
||||
|
||||
```
|
||||
yarn backstage-repo-tools repo schema openapi lint <plugin-dir>/src/schema/openapi.yaml
|
||||
```
|
||||
|
||||
Iterate on lint output until clean.
|
||||
|
||||
### Step 4 — Wire up dependencies and the `generate` script
|
||||
|
||||
In `<plugin-dir>/package.json`:
|
||||
|
||||
1. Ensure `@backstage/backend-openapi-utils` is in `dependencies` (required at runtime by the generated `router.ts`).
|
||||
2. Add a `generate` script. The exact form depends on whether the user opted into client generation:
|
||||
- **Server only (default):**
|
||||
```json
|
||||
"generate": "backstage-repo-tools package schema openapi generate --server"
|
||||
```
|
||||
- **Server + client (opt-in):** see the optional client section below.
|
||||
|
||||
### Step 5 — Generate the server stub
|
||||
|
||||
Run from the plugin directory:
|
||||
|
||||
```
|
||||
yarn --cwd <plugin-dir> generate
|
||||
```
|
||||
|
||||
This will:
|
||||
|
||||
- Empty `<plugin-dir>/src/schema/openapi/generated/` and rewrite it.
|
||||
- Produce `apis/Api.server.ts`, `models/*.model.ts`, an `index.ts`, and `router.ts` (which exports `spec` and `createOpenApiRouter`).
|
||||
- Run lint and prettier on the generated output.
|
||||
|
||||
The generator also writes `<plugin-dir>/src/schema/openapi/index.ts` re-exporting `./generated`. Leave this alone.
|
||||
|
||||
If generation fails, the most common causes are: invalid YAML (fix and rerun), unsupported OpenAPI version (`@backstage/backend-openapi-utils` only supports OpenAPI 3.1.x — keep `openapi: 3.1.0` at the top of the spec).
|
||||
|
||||
### Step 6 — Switch the router over to `createOpenApiRouter`
|
||||
|
||||
In the existing router file:
|
||||
|
||||
1. Replace `const router = Router()` (or `express.Router()`) with:
|
||||
```ts
|
||||
import { createOpenApiRouter } from '../schema/openapi';
|
||||
// ...
|
||||
const router = await createOpenApiRouter();
|
||||
```
|
||||
(Adjust the import path to the router's location.) The function is async — propagate the `await` up to where the router is constructed.
|
||||
2. Move route handlers onto the new router unchanged. The OpenAPI router validates incoming requests against the spec at runtime (response shapes are validated separately via `wrapServer` in tests); if a handler reads `req.body.foo` where the spec says it is `req.body.event.foo`, the spec is wrong, not the handler — go fix the spec and regenerate.
|
||||
3. Remove request validation that the spec now covers. Anything the OpenAPI validator enforces — required path/query/body params, primitive types, enums, `format`, `minimum`/`maximum`, `minLength`/`pattern`, etc. — should be deleted from the handler. Typical things to strip out: manual `if (!req.body.x) throw new InputError(...)` guards, `typeof req.query.foo !== 'string'` checks, hand-rolled `enum` membership tests, `Number.parseInt`/`.toString()` coercion on values the spec already types correctly. Keep validation the spec _can't_ express — cross-field invariants, auth/permission checks, business-rule guards, lookups against the database.
|
||||
4. Keep all middleware in the same order (cookie parsers, body parsers, auth, error handlers). Auth/permissions logic is NOT generated and must remain in the router code.
|
||||
5. Preserve the function signature and exports of `createRouter`/`router` exactly so callers (the plugin's `*Plugin.ts`) need no change.
|
||||
|
||||
[`plugins/events-backend/src/service/hub/createEventBusRouter.ts`](https://github.com/backstage/backstage/blob/master/plugins/events-backend/src/service/hub/createEventBusRouter.ts) is the canonical example.
|
||||
|
||||
### Step 7 — Verify
|
||||
|
||||
Run, in order, and fix any failures before moving to the next:
|
||||
|
||||
1. **Type check** — `yarn tsc` from the repo root. Common failures: handler argument types now narrower than the spec allows, missing `await` on `createOpenApiRouter()`. Fix in code, never by widening spec types to `any`.
|
||||
2. **Tests** — `CI=1 yarn test <plugin-dir>/src/service`. The existing tests should still pass against the new typed router without any test-side changes (test migration is a separate, optional step).
|
||||
3. **Lint** — `yarn lint --fix <plugin-dir>`.
|
||||
|
||||
If a test fails because the spec is wrong, edit `openapi.yaml` and rerun `yarn --cwd <plugin-dir> generate`, then re-run the test. Never edit files under `src/schema/openapi/generated/` — they are clobbered on every generate.
|
||||
|
||||
## Optional: generate a typed client
|
||||
|
||||
Only do this if the user explicitly asked for it.
|
||||
|
||||
1. Decide which package will host the client. There are three common shapes — ask the user which one fits:
|
||||
|
||||
- **Backend-only client** (`*-node` package, e.g. `plugins/foo-node`) — when only other backend plugins call this API.
|
||||
- **Frontend-only client** (`*-react` package, or a frontend plugin package like `plugins/foo`) — when only browser code calls this API.
|
||||
- **Shared client** (`*-common` package, e.g. `plugins/foo-common`) — when both backend and frontend need it. The `*-common` package is the safest default when in doubt because it's reachable from either side.
|
||||
|
||||
If the chosen package does not yet exist as a workspace package, scaffold it with `yarn new` first (`backend-plugin-node` for `*-node`, `plugin-common` for `*-common`, etc.). Confirm naming and template with the user before scaffolding.
|
||||
|
||||
2. Update the `generate` script in the plugin's `package.json`:
|
||||
```json
|
||||
"generate": "backstage-repo-tools package schema openapi generate --server --client-package <client-pkg-dir>"
|
||||
```
|
||||
3. Re-run `yarn --cwd <plugin-dir> generate`. The client package's `src/generated/` will be emptied and rewritten with `apis/<Name>Api.client.ts`, `models/`, and `pluginId.ts`.
|
||||
4. Run `yarn build:api-reports` from the repo root, since the client package's public surface changed.
|
||||
5. `yarn tsc` again from the repo root.
|
||||
6. Lint the client package: `yarn lint --fix <client-pkg-dir>`.
|
||||
|
||||
[`plugins/events-node/src/generated/`](https://github.com/backstage/backstage/tree/master/plugins/events-node/src/generated) is a reference for what a client output looks like.
|
||||
|
||||
## Optional: migrate router tests to `wrapServer`
|
||||
|
||||
Only do this if the user explicitly asked for it. The server-side runtime validation in `createOpenApiRouter` already covers the request path; `wrapServer` adds response-side validation in tests, which is useful but not required.
|
||||
|
||||
For each router test file (`router.test.ts`, `createRouter.test.ts`):
|
||||
|
||||
1. Add the import:
|
||||
```ts
|
||||
import { wrapServer } from '@backstage/backend-openapi-utils/testUtils';
|
||||
```
|
||||
2. Wrap every place a `supertest` `app` is constructed:
|
||||
```ts
|
||||
const app = await wrapServer(express().use(router));
|
||||
```
|
||||
`wrapServer` is async, so the surrounding `beforeEach`/`beforeAll` must `await`.
|
||||
3. If the test uses `mockErrorHandler()`, mount it on the inner express app _before_ wrapping:
|
||||
```ts
|
||||
const app = await wrapServer(express().use(router).use(mockErrorHandler()));
|
||||
```
|
||||
4. Do not change assertions. The wrapper validates every request and response against the spec at test time — assertion failures that surface as `Response did not match schema` mean the spec or the handler disagree; fix whichever is wrong.
|
||||
5. Re-run `CI=1 yarn test <plugin-dir>/src/service`.
|
||||
|
||||
[`plugins/scaffolder-backend/src/service/router.test.ts`](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/src/service/router.test.ts) and [`plugins/catalog-backend/src/service/createRouter.test.ts`](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/src/service/createRouter.test.ts) are reference migrations.
|
||||
|
||||
## Optional: changesets
|
||||
|
||||
Only if the user wants to land this as a PR. Create changesets in `.changeset/` per `/CONTRIBUTING.md`:
|
||||
|
||||
- A `minor` (or `patch` for `< 1.0.0` packages) changeset for the backend plugin describing the new typed router.
|
||||
- If a client package was generated, a separate changeset for it.
|
||||
- Tailor each message to its package's audience. Do not reference internal symbols (`createOpenApiRouter`, `EndpointMap`, etc.) — describe the user-facing impact.
|
||||
|
||||
Do not run `yarn changesets version`. Write the changeset markdown files directly.
|
||||
|
||||
## Reference files in the upstream Backstage repo
|
||||
|
||||
These all live in [`backstage/backstage`](https://github.com/backstage/backstage); the user's workspace likely doesn't contain them. Browse via the links below.
|
||||
|
||||
| What you need | Where to read |
|
||||
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Spec example (small) | [`plugins/events-backend/src/schema/openapi.yaml`](https://github.com/backstage/backstage/blob/master/plugins/events-backend/src/schema/openapi.yaml) |
|
||||
| Spec example (large) | [`plugins/catalog-backend/src/schema/openapi.yaml`](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/src/schema/openapi.yaml) |
|
||||
| Router using `createOpenApiRouter` | [`plugins/events-backend/src/service/hub/createEventBusRouter.ts`](https://github.com/backstage/backstage/blob/master/plugins/events-backend/src/service/hub/createEventBusRouter.ts) |
|
||||
| Test using `wrapServer` | [`plugins/scaffolder-backend/src/service/router.test.ts`](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/src/service/router.test.ts) |
|
||||
| Generate command source | [`packages/repo-tools/src/commands/package/schema/openapi/generate/`](https://github.com/backstage/backstage/tree/master/packages/repo-tools/src/commands/package/schema/openapi/generate) |
|
||||
| Spec lint command source | [`packages/repo-tools/src/commands/repo/schema/openapi/lint.ts`](https://github.com/backstage/backstage/blob/master/packages/repo-tools/src/commands/repo/schema/openapi/lint.ts) |
|
||||
| `wrapServer` implementation | [`packages/backend-openapi-utils/src/testUtils.ts`](https://github.com/backstage/backstage/blob/master/packages/backend-openapi-utils/src/testUtils.ts) |
|
||||
| Client output reference | [`plugins/events-node/src/generated/`](https://github.com/backstage/backstage/tree/master/plugins/events-node/src/generated) |
|
||||
|
||||
## Things to refuse / hand back to the user
|
||||
|
||||
- Don't onboard a plugin whose tests are red on the baseline — fix those first in a separate change.
|
||||
- Don't invent the auth/permissions story — preserve whatever the router does today.
|
||||
- Don't use `--no-verify` on commits.
|
||||
- Don't edit generated files. If something needs to change there, change the spec and regenerate.
|
||||
- If the router has dynamic mounted sub-routers that you can't statically inventory (e.g. plugin extension points that register routes at runtime), stop and ask the user how they want those handled — the spec can't capture them faithfully.
|
||||
@@ -35,6 +35,7 @@
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"clean": "backstage-cli package clean",
|
||||
"generate": "backstage-repo-tools package schema openapi generate --server",
|
||||
"lint": "backstage-cli package lint",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
@@ -42,6 +43,7 @@
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-openapi-utils": "workspace:^",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
"@backstage/config": "workspace:^",
|
||||
@@ -64,6 +66,7 @@
|
||||
"@backstage/plugin-auth-backend-module-guest-provider": "workspace:^",
|
||||
"@backstage/plugin-events-backend": "workspace:^",
|
||||
"@backstage/plugin-signals-backend": "workspace:^",
|
||||
"@backstage/repo-tools": "workspace:^",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"supertest": "^7.0.0"
|
||||
|
||||
@@ -0,0 +1,675 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: notifications
|
||||
version: '1'
|
||||
description: The Backstage backend plugin that powers user notifications.
|
||||
license:
|
||||
name: Apache-2.0
|
||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
contact: {}
|
||||
servers:
|
||||
- url: /
|
||||
components:
|
||||
examples: {}
|
||||
headers: {}
|
||||
parameters:
|
||||
notificationId:
|
||||
name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
offset:
|
||||
name: offset
|
||||
in: query
|
||||
required: false
|
||||
allowReserved: true
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 0
|
||||
limit:
|
||||
name: limit
|
||||
in: query
|
||||
required: false
|
||||
allowReserved: true
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 0
|
||||
orderField:
|
||||
name: orderField
|
||||
in: query
|
||||
required: false
|
||||
allowReserved: true
|
||||
description: One or more "field,direction" pairs. Direction is "asc" or "desc". Pass multiple to express compound sorts.
|
||||
schema:
|
||||
$ref: '#/components/schemas/OrderField'
|
||||
topic:
|
||||
name: topic
|
||||
in: query
|
||||
required: false
|
||||
allowReserved: true
|
||||
schema:
|
||||
type: string
|
||||
search:
|
||||
name: search
|
||||
in: query
|
||||
required: false
|
||||
allowReserved: true
|
||||
schema:
|
||||
type: string
|
||||
read:
|
||||
name: read
|
||||
in: query
|
||||
required: false
|
||||
allowReserved: true
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- 'true'
|
||||
- 'false'
|
||||
saved:
|
||||
name: saved
|
||||
in: query
|
||||
required: false
|
||||
allowReserved: true
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- 'true'
|
||||
- 'false'
|
||||
createdAfter:
|
||||
name: createdAfter
|
||||
in: query
|
||||
required: false
|
||||
allowReserved: true
|
||||
description: ISO 8601 date-time string. Validated by the handler.
|
||||
schema:
|
||||
type: string
|
||||
minimumSeverity:
|
||||
name: minimumSeverity
|
||||
in: query
|
||||
required: false
|
||||
allowReserved: true
|
||||
schema:
|
||||
$ref: '#/components/schemas/NotificationSeverity'
|
||||
requestBodies: {}
|
||||
responses:
|
||||
ErrorResponse:
|
||||
description: An error response from the backend.
|
||||
content:
|
||||
application/json; charset=utf-8:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
schemas:
|
||||
NotificationSeverity:
|
||||
type: string
|
||||
enum:
|
||||
- critical
|
||||
- high
|
||||
- normal
|
||||
- low
|
||||
|
||||
NotificationPayload:
|
||||
type: object
|
||||
required:
|
||||
- title
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
description: Notification title
|
||||
description:
|
||||
type: string
|
||||
description: Optional longer description for the notification
|
||||
link:
|
||||
type: string
|
||||
description: Optional link where the notification is pointing to
|
||||
severity:
|
||||
$ref: '#/components/schemas/NotificationSeverity'
|
||||
topic:
|
||||
type: string
|
||||
description: Optional notification topic
|
||||
scope:
|
||||
type: string
|
||||
description: Notification scope, can be used to re-send same notifications in case the scope and origin matches.
|
||||
icon:
|
||||
type: string
|
||||
description: Optional notification icon
|
||||
metadata:
|
||||
type: object
|
||||
description: Optional additional customizable metadata.
|
||||
additionalProperties: true
|
||||
|
||||
NotificationResponsePayload:
|
||||
description: Response variant of NotificationPayload. Optional string fields may be returned as null when unset.
|
||||
type: object
|
||||
required:
|
||||
- title
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
description:
|
||||
type:
|
||||
- string
|
||||
- 'null'
|
||||
link:
|
||||
type:
|
||||
- string
|
||||
- 'null'
|
||||
severity:
|
||||
$ref: '#/components/schemas/NotificationSeverity'
|
||||
topic:
|
||||
type:
|
||||
- string
|
||||
- 'null'
|
||||
scope:
|
||||
type:
|
||||
- string
|
||||
- 'null'
|
||||
icon:
|
||||
type:
|
||||
- string
|
||||
- 'null'
|
||||
metadata:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
|
||||
Notification:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- user
|
||||
- created
|
||||
- origin
|
||||
- payload
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: Unique identifier for the notification.
|
||||
user:
|
||||
type:
|
||||
- string
|
||||
- 'null'
|
||||
description: User entity reference, or null for broadcast notifications.
|
||||
created:
|
||||
type: string
|
||||
format: date-time
|
||||
saved:
|
||||
type:
|
||||
- string
|
||||
- 'null'
|
||||
format: date-time
|
||||
read:
|
||||
type:
|
||||
- string
|
||||
- 'null'
|
||||
format: date-time
|
||||
updated:
|
||||
type:
|
||||
- string
|
||||
- 'null'
|
||||
format: date-time
|
||||
origin:
|
||||
type: string
|
||||
description: Origin of the notification (the sender's reference).
|
||||
payload:
|
||||
$ref: '#/components/schemas/NotificationResponsePayload'
|
||||
|
||||
NotificationStatus:
|
||||
type: object
|
||||
required:
|
||||
- unread
|
||||
- read
|
||||
properties:
|
||||
unread:
|
||||
type: integer
|
||||
read:
|
||||
type: integer
|
||||
|
||||
TopicSetting:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- enabled
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
enabled:
|
||||
type: boolean
|
||||
|
||||
OriginSetting:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- enabled
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
enabled:
|
||||
type: boolean
|
||||
topics:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/TopicSetting'
|
||||
|
||||
ChannelSetting:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- origins
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
enabled:
|
||||
type: boolean
|
||||
origins:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/OriginSetting'
|
||||
|
||||
NotificationSettings:
|
||||
type: object
|
||||
required:
|
||||
- channels
|
||||
properties:
|
||||
channels:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ChannelSetting'
|
||||
|
||||
EntityRef:
|
||||
description: A single entity ref, or a list of entity refs.
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
OrderField:
|
||||
description: One or more "field,direction" pairs, given as a single string or an array of strings.
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
NotificationRecipients:
|
||||
oneOf:
|
||||
- type: object
|
||||
required:
|
||||
- type
|
||||
- entityRef
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- entity
|
||||
entityRef:
|
||||
$ref: '#/components/schemas/EntityRef'
|
||||
excludeEntityRef:
|
||||
$ref: '#/components/schemas/EntityRef'
|
||||
- type: object
|
||||
required:
|
||||
- type
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- broadcast
|
||||
|
||||
NotificationSendOptions:
|
||||
type: object
|
||||
required:
|
||||
- recipients
|
||||
- payload
|
||||
properties:
|
||||
recipients:
|
||||
$ref: '#/components/schemas/NotificationRecipients'
|
||||
payload:
|
||||
$ref: '#/components/schemas/NotificationPayload'
|
||||
|
||||
UpdateNotificationsRequest:
|
||||
type: object
|
||||
required:
|
||||
- ids
|
||||
properties:
|
||||
ids:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
read:
|
||||
type: boolean
|
||||
saved:
|
||||
type: boolean
|
||||
|
||||
ListNotificationsResponse:
|
||||
type: object
|
||||
required:
|
||||
- totalCount
|
||||
- notifications
|
||||
properties:
|
||||
totalCount:
|
||||
type: integer
|
||||
notifications:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Notification'
|
||||
|
||||
GetTopicsResponse:
|
||||
type: object
|
||||
required:
|
||||
- topics
|
||||
properties:
|
||||
topics:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
Error:
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- message
|
||||
request:
|
||||
type: object
|
||||
properties:
|
||||
method:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
- method
|
||||
- url
|
||||
response:
|
||||
type: object
|
||||
properties:
|
||||
statusCode:
|
||||
type: number
|
||||
required:
|
||||
- statusCode
|
||||
required:
|
||||
- error
|
||||
- request
|
||||
- response
|
||||
securitySchemes:
|
||||
JWT:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
operationId: ListNotificationsLegacy
|
||||
deprecated: true
|
||||
description: Deprecated alias for `GET /notifications`.
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/offset'
|
||||
- $ref: '#/components/parameters/limit'
|
||||
- $ref: '#/components/parameters/orderField'
|
||||
- $ref: '#/components/parameters/topic'
|
||||
- $ref: '#/components/parameters/search'
|
||||
- $ref: '#/components/parameters/read'
|
||||
- $ref: '#/components/parameters/saved'
|
||||
- $ref: '#/components/parameters/createdAfter'
|
||||
- $ref: '#/components/parameters/minimumSeverity'
|
||||
responses:
|
||||
'200':
|
||||
description: A page of notifications for the current user.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ListNotificationsResponse'
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
post:
|
||||
operationId: CreateNotificationsLegacy
|
||||
deprecated: true
|
||||
description: Deprecated alias for `POST /notifications`.
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NotificationSendOptions'
|
||||
responses:
|
||||
'200':
|
||||
description: The notifications that were created.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Notification'
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
|
||||
/notifications:
|
||||
get:
|
||||
operationId: ListNotifications
|
||||
description: List notifications for the current user.
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/offset'
|
||||
- $ref: '#/components/parameters/limit'
|
||||
- $ref: '#/components/parameters/orderField'
|
||||
- $ref: '#/components/parameters/topic'
|
||||
- $ref: '#/components/parameters/search'
|
||||
- $ref: '#/components/parameters/read'
|
||||
- $ref: '#/components/parameters/saved'
|
||||
- $ref: '#/components/parameters/createdAfter'
|
||||
- $ref: '#/components/parameters/minimumSeverity'
|
||||
responses:
|
||||
'200':
|
||||
description: A page of notifications for the current user.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ListNotificationsResponse'
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
post:
|
||||
operationId: CreateNotifications
|
||||
description: Send a notification to one or more recipients (service credentials required).
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NotificationSendOptions'
|
||||
responses:
|
||||
'200':
|
||||
description: The notifications that were created.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Notification'
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
|
||||
/update:
|
||||
post:
|
||||
operationId: UpdateNotificationsLegacy
|
||||
deprecated: true
|
||||
description: Deprecated alias for `POST /notifications/update`.
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateNotificationsRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: The notifications that were updated.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Notification'
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
|
||||
/notifications/update:
|
||||
post:
|
||||
operationId: UpdateNotifications
|
||||
description: Mark notifications as read/unread or saved/unsaved.
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateNotificationsRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: The notifications that were updated.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Notification'
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
|
||||
/status:
|
||||
get:
|
||||
operationId: GetStatus
|
||||
description: Get the unread/read counts for the current user.
|
||||
responses:
|
||||
'200':
|
||||
description: The current user's notification status.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NotificationStatus'
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
|
||||
/settings:
|
||||
get:
|
||||
operationId: GetSettings
|
||||
description: Get the current user's notification settings, merged with defaults.
|
||||
responses:
|
||||
'200':
|
||||
description: Effective notification settings for the user.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NotificationSettings'
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
post:
|
||||
operationId: UpdateSettings
|
||||
description: Update the current user's notification settings.
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NotificationSettings'
|
||||
responses:
|
||||
'200':
|
||||
description: Effective notification settings for the user.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NotificationSettings'
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
|
||||
/topics:
|
||||
get:
|
||||
operationId: GetTopics
|
||||
description: List notification topics that the user has received notifications on.
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/search'
|
||||
- $ref: '#/components/parameters/read'
|
||||
- $ref: '#/components/parameters/saved'
|
||||
- $ref: '#/components/parameters/createdAfter'
|
||||
- $ref: '#/components/parameters/minimumSeverity'
|
||||
responses:
|
||||
'200':
|
||||
description: The list of topics.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GetTopicsResponse'
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
|
||||
/{id}:
|
||||
get:
|
||||
operationId: GetNotificationLegacy
|
||||
deprecated: true
|
||||
description: Deprecated alias for `GET /notifications/{id}`.
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/notificationId'
|
||||
responses:
|
||||
'200':
|
||||
description: The requested notification.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Notification'
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
|
||||
/notifications/{id}:
|
||||
get:
|
||||
operationId: GetNotification
|
||||
description: Get a single notification by id for the current user.
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/notificationId'
|
||||
responses:
|
||||
'200':
|
||||
description: The requested notification.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Notification'
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { GetTopicsResponse } from '../models/GetTopicsResponse.model';
|
||||
import { ListNotificationsResponse } from '../models/ListNotificationsResponse.model';
|
||||
import { Notification } from '../models/Notification.model';
|
||||
import { NotificationSendOptions } from '../models/NotificationSendOptions.model';
|
||||
import { NotificationSettings } from '../models/NotificationSettings.model';
|
||||
import { NotificationSeverity } from '../models/NotificationSeverity.model';
|
||||
import { NotificationStatus } from '../models/NotificationStatus.model';
|
||||
import { OrderField } from '../models/OrderField.model';
|
||||
import { UpdateNotificationsRequest } from '../models/UpdateNotificationsRequest.model';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type CreateNotifications = {
|
||||
body: NotificationSendOptions;
|
||||
response: Array<Notification> | Error;
|
||||
};
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type CreateNotificationsLegacy = {
|
||||
body: NotificationSendOptions;
|
||||
response: Array<Notification> | Error;
|
||||
};
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type GetNotification = {
|
||||
path: {
|
||||
id: string;
|
||||
};
|
||||
response: Notification | Error;
|
||||
};
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type GetNotificationLegacy = {
|
||||
path: {
|
||||
id: string;
|
||||
};
|
||||
response: Notification | Error;
|
||||
};
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type GetSettings = {
|
||||
response: NotificationSettings | Error;
|
||||
};
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type GetStatus = {
|
||||
response: NotificationStatus | Error;
|
||||
};
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type GetTopics = {
|
||||
query: {
|
||||
search?: string;
|
||||
read?: 'true' | 'false';
|
||||
saved?: 'true' | 'false';
|
||||
createdAfter?: string;
|
||||
minimumSeverity?: NotificationSeverity;
|
||||
};
|
||||
response: GetTopicsResponse | Error;
|
||||
};
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ListNotifications = {
|
||||
query: {
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
orderField?: OrderField;
|
||||
topic?: string;
|
||||
search?: string;
|
||||
read?: 'true' | 'false';
|
||||
saved?: 'true' | 'false';
|
||||
createdAfter?: string;
|
||||
minimumSeverity?: NotificationSeverity;
|
||||
};
|
||||
response: ListNotificationsResponse | Error;
|
||||
};
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ListNotificationsLegacy = {
|
||||
query: {
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
orderField?: OrderField;
|
||||
topic?: string;
|
||||
search?: string;
|
||||
read?: 'true' | 'false';
|
||||
saved?: 'true' | 'false';
|
||||
createdAfter?: string;
|
||||
minimumSeverity?: NotificationSeverity;
|
||||
};
|
||||
response: ListNotificationsResponse | Error;
|
||||
};
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type UpdateNotifications = {
|
||||
body: UpdateNotificationsRequest;
|
||||
response: Array<Notification> | Error;
|
||||
};
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type UpdateNotificationsLegacy = {
|
||||
body: UpdateNotificationsRequest;
|
||||
response: Array<Notification> | Error;
|
||||
};
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type UpdateSettings = {
|
||||
body: NotificationSettings;
|
||||
response: NotificationSettings | Error;
|
||||
};
|
||||
|
||||
export type EndpointMap = {
|
||||
'#post|/notifications': CreateNotifications;
|
||||
|
||||
'#post|/': CreateNotificationsLegacy;
|
||||
|
||||
'#get|/notifications/{id}': GetNotification;
|
||||
|
||||
'#get|/{id}': GetNotificationLegacy;
|
||||
|
||||
'#get|/settings': GetSettings;
|
||||
|
||||
'#get|/status': GetStatus;
|
||||
|
||||
'#get|/topics': GetTopics;
|
||||
|
||||
'#get|/notifications': ListNotifications;
|
||||
|
||||
'#get|/': ListNotificationsLegacy;
|
||||
|
||||
'#post|/notifications/update': UpdateNotifications;
|
||||
|
||||
'#post|/update': UpdateNotificationsLegacy;
|
||||
|
||||
'#post|/settings': UpdateSettings;
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
export * from './Api.server';
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './apis';
|
||||
export * from './router';
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { OriginSetting } from '../models/OriginSetting.model';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ChannelSetting {
|
||||
id: string;
|
||||
enabled?: boolean;
|
||||
origins: Array<OriginSetting>;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* A single entity ref, or a list of entity refs.
|
||||
* @public
|
||||
*/
|
||||
export type EntityRef = Array<string> | string;
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ErrorError {
|
||||
name: string;
|
||||
message: string;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ErrorRequest {
|
||||
method: string;
|
||||
url: string;
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ErrorResponse {
|
||||
statusCode: number;
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface GetTopicsResponse {
|
||||
topics: Array<string>;
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { Notification } from '../models/Notification.model';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ListNotificationsResponse {
|
||||
totalCount: number;
|
||||
notifications: Array<Notification>;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { ErrorError } from '../models/ErrorError.model';
|
||||
import { ErrorRequest } from '../models/ErrorRequest.model';
|
||||
import { ErrorResponse } from '../models/ErrorResponse.model';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ModelError {
|
||||
error: ErrorError;
|
||||
request: ErrorRequest;
|
||||
response: ErrorResponse;
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { NotificationResponsePayload } from '../models/NotificationResponsePayload.model';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Notification {
|
||||
/**
|
||||
* Unique identifier for the notification.
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* User entity reference, or null for broadcast notifications.
|
||||
*/
|
||||
user: string | null;
|
||||
created: Date;
|
||||
saved?: Date | null;
|
||||
read?: Date | null;
|
||||
updated?: Date | null;
|
||||
/**
|
||||
* Origin of the notification (the sender\'s reference).
|
||||
*/
|
||||
origin: string;
|
||||
payload: NotificationResponsePayload;
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { NotificationSeverity } from '../models/NotificationSeverity.model';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface NotificationPayload {
|
||||
/**
|
||||
* Notification title
|
||||
*/
|
||||
title: string;
|
||||
/**
|
||||
* Optional longer description for the notification
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* Optional link where the notification is pointing to
|
||||
*/
|
||||
link?: string;
|
||||
severity?: NotificationSeverity;
|
||||
/**
|
||||
* Optional notification topic
|
||||
*/
|
||||
topic?: string;
|
||||
/**
|
||||
* Notification scope, can be used to re-send same notifications in case the scope and origin matches.
|
||||
*/
|
||||
scope?: string;
|
||||
/**
|
||||
* Optional notification icon
|
||||
*/
|
||||
icon?: string;
|
||||
/**
|
||||
* Optional additional customizable metadata.
|
||||
*/
|
||||
metadata?: { [key: string]: any };
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { NotificationRecipientsOneOf } from '../models/NotificationRecipientsOneOf.model';
|
||||
import { NotificationRecipientsOneOf1 } from '../models/NotificationRecipientsOneOf1.model';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type NotificationRecipients =
|
||||
| NotificationRecipientsOneOf
|
||||
| NotificationRecipientsOneOf1;
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { EntityRef } from '../models/EntityRef.model';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface NotificationRecipientsOneOf {
|
||||
type: NotificationRecipientsOneOfTypeEnum;
|
||||
entityRef: EntityRef;
|
||||
excludeEntityRef?: EntityRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type NotificationRecipientsOneOfTypeEnum = 'entity';
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface NotificationRecipientsOneOf1 {
|
||||
type: NotificationRecipientsOneOf1TypeEnum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type NotificationRecipientsOneOf1TypeEnum = 'broadcast';
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { NotificationSeverity } from '../models/NotificationSeverity.model';
|
||||
|
||||
/**
|
||||
* Response variant of NotificationPayload. Optional string fields may be returned as null when unset.
|
||||
* @public
|
||||
*/
|
||||
export interface NotificationResponsePayload {
|
||||
title: string;
|
||||
description?: string | null;
|
||||
link?: string | null;
|
||||
severity?: NotificationSeverity;
|
||||
topic?: string | null;
|
||||
scope?: string | null;
|
||||
icon?: string | null;
|
||||
metadata?: { [key: string]: any };
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { NotificationPayload } from '../models/NotificationPayload.model';
|
||||
import { NotificationRecipients } from '../models/NotificationRecipients.model';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface NotificationSendOptions {
|
||||
recipients: NotificationRecipients;
|
||||
payload: NotificationPayload;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { ChannelSetting } from '../models/ChannelSetting.model';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface NotificationSettings {
|
||||
channels: Array<ChannelSetting>;
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type NotificationSeverity = 'critical' | 'high' | 'normal' | 'low';
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface NotificationStatus {
|
||||
unread: number;
|
||||
read: number;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* One or more \"field,direction\" pairs, given as a single string or an array of strings.
|
||||
* @public
|
||||
*/
|
||||
export type OrderField = Array<string> | string;
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { TopicSetting } from '../models/TopicSetting.model';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface OriginSetting {
|
||||
id: string;
|
||||
enabled: boolean;
|
||||
topics?: Array<TopicSetting>;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface TopicSetting {
|
||||
id: string;
|
||||
enabled: boolean;
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface UpdateNotificationsRequest {
|
||||
ids: Array<string>;
|
||||
read?: boolean;
|
||||
saved?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from '../models/ChannelSetting.model';
|
||||
export * from '../models/EntityRef.model';
|
||||
export * from '../models/ErrorError.model';
|
||||
export * from '../models/ErrorRequest.model';
|
||||
export * from '../models/ErrorResponse.model';
|
||||
export * from '../models/GetTopicsResponse.model';
|
||||
export * from '../models/ListNotificationsResponse.model';
|
||||
export * from '../models/ModelError.model';
|
||||
export * from '../models/Notification.model';
|
||||
export * from '../models/NotificationPayload.model';
|
||||
export * from '../models/NotificationRecipients.model';
|
||||
export * from '../models/NotificationRecipientsOneOf.model';
|
||||
export * from '../models/NotificationRecipientsOneOf1.model';
|
||||
export * from '../models/NotificationResponsePayload.model';
|
||||
export * from '../models/NotificationSendOptions.model';
|
||||
export * from '../models/NotificationSettings.model';
|
||||
export * from '../models/NotificationSeverity.model';
|
||||
export * from '../models/NotificationStatus.model';
|
||||
export * from '../models/OrderField.model';
|
||||
export * from '../models/OriginSetting.model';
|
||||
export * from '../models/TopicSetting.model';
|
||||
export * from '../models/UpdateNotificationsRequest.model';
|
||||
@@ -0,0 +1,988 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { createValidatedOpenApiRouterFromGeneratedEndpointMap } from '@backstage/backend-openapi-utils';
|
||||
import { EndpointMap } from './apis';
|
||||
|
||||
export const spec = {
|
||||
openapi: '3.1.0',
|
||||
info: {
|
||||
title: 'notifications',
|
||||
version: '1',
|
||||
description: 'The Backstage backend plugin that powers user notifications.',
|
||||
license: {
|
||||
name: 'Apache-2.0',
|
||||
url: 'http://www.apache.org/licenses/LICENSE-2.0.html',
|
||||
},
|
||||
contact: {},
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: '/',
|
||||
},
|
||||
],
|
||||
components: {
|
||||
examples: {},
|
||||
headers: {},
|
||||
parameters: {
|
||||
notificationId: {
|
||||
name: 'id',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
offset: {
|
||||
name: 'offset',
|
||||
in: 'query',
|
||||
required: false,
|
||||
allowReserved: true,
|
||||
schema: {
|
||||
type: 'integer',
|
||||
minimum: 0,
|
||||
},
|
||||
},
|
||||
limit: {
|
||||
name: 'limit',
|
||||
in: 'query',
|
||||
required: false,
|
||||
allowReserved: true,
|
||||
schema: {
|
||||
type: 'integer',
|
||||
minimum: 0,
|
||||
},
|
||||
},
|
||||
orderField: {
|
||||
name: 'orderField',
|
||||
in: 'query',
|
||||
required: false,
|
||||
allowReserved: true,
|
||||
description:
|
||||
'One or more "field,direction" pairs. Direction is "asc" or "desc". Pass multiple to express compound sorts.',
|
||||
schema: {
|
||||
$ref: '#/components/schemas/OrderField',
|
||||
},
|
||||
},
|
||||
topic: {
|
||||
name: 'topic',
|
||||
in: 'query',
|
||||
required: false,
|
||||
allowReserved: true,
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
search: {
|
||||
name: 'search',
|
||||
in: 'query',
|
||||
required: false,
|
||||
allowReserved: true,
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
read: {
|
||||
name: 'read',
|
||||
in: 'query',
|
||||
required: false,
|
||||
allowReserved: true,
|
||||
schema: {
|
||||
type: 'string',
|
||||
enum: ['true', 'false'],
|
||||
},
|
||||
},
|
||||
saved: {
|
||||
name: 'saved',
|
||||
in: 'query',
|
||||
required: false,
|
||||
allowReserved: true,
|
||||
schema: {
|
||||
type: 'string',
|
||||
enum: ['true', 'false'],
|
||||
},
|
||||
},
|
||||
createdAfter: {
|
||||
name: 'createdAfter',
|
||||
in: 'query',
|
||||
required: false,
|
||||
allowReserved: true,
|
||||
description: 'ISO 8601 date-time string. Validated by the handler.',
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
minimumSeverity: {
|
||||
name: 'minimumSeverity',
|
||||
in: 'query',
|
||||
required: false,
|
||||
allowReserved: true,
|
||||
schema: {
|
||||
$ref: '#/components/schemas/NotificationSeverity',
|
||||
},
|
||||
},
|
||||
},
|
||||
requestBodies: {},
|
||||
responses: {
|
||||
ErrorResponse: {
|
||||
description: 'An error response from the backend.',
|
||||
content: {
|
||||
'application/json; charset=utf-8': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
schemas: {
|
||||
NotificationSeverity: {
|
||||
type: 'string',
|
||||
enum: ['critical', 'high', 'normal', 'low'],
|
||||
},
|
||||
NotificationPayload: {
|
||||
type: 'object',
|
||||
required: ['title'],
|
||||
properties: {
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Notification title',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
description: 'Optional longer description for the notification',
|
||||
},
|
||||
link: {
|
||||
type: 'string',
|
||||
description: 'Optional link where the notification is pointing to',
|
||||
},
|
||||
severity: {
|
||||
$ref: '#/components/schemas/NotificationSeverity',
|
||||
},
|
||||
topic: {
|
||||
type: 'string',
|
||||
description: 'Optional notification topic',
|
||||
},
|
||||
scope: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Notification scope, can be used to re-send same notifications in case the scope and origin matches.',
|
||||
},
|
||||
icon: {
|
||||
type: 'string',
|
||||
description: 'Optional notification icon',
|
||||
},
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Optional additional customizable metadata.',
|
||||
additionalProperties: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
NotificationResponsePayload: {
|
||||
description:
|
||||
'Response variant of NotificationPayload. Optional string fields may be returned as null when unset.',
|
||||
type: 'object',
|
||||
required: ['title'],
|
||||
properties: {
|
||||
title: {
|
||||
type: 'string',
|
||||
},
|
||||
description: {
|
||||
type: ['string', 'null'],
|
||||
},
|
||||
link: {
|
||||
type: ['string', 'null'],
|
||||
},
|
||||
severity: {
|
||||
$ref: '#/components/schemas/NotificationSeverity',
|
||||
},
|
||||
topic: {
|
||||
type: ['string', 'null'],
|
||||
},
|
||||
scope: {
|
||||
type: ['string', 'null'],
|
||||
},
|
||||
icon: {
|
||||
type: ['string', 'null'],
|
||||
},
|
||||
metadata: {
|
||||
type: 'object',
|
||||
additionalProperties: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Notification: {
|
||||
type: 'object',
|
||||
required: ['id', 'user', 'created', 'origin', 'payload'],
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'Unique identifier for the notification.',
|
||||
},
|
||||
user: {
|
||||
type: ['string', 'null'],
|
||||
description:
|
||||
'User entity reference, or null for broadcast notifications.',
|
||||
},
|
||||
created: {
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
},
|
||||
saved: {
|
||||
type: ['string', 'null'],
|
||||
format: 'date-time',
|
||||
},
|
||||
read: {
|
||||
type: ['string', 'null'],
|
||||
format: 'date-time',
|
||||
},
|
||||
updated: {
|
||||
type: ['string', 'null'],
|
||||
format: 'date-time',
|
||||
},
|
||||
origin: {
|
||||
type: 'string',
|
||||
description: "Origin of the notification (the sender's reference).",
|
||||
},
|
||||
payload: {
|
||||
$ref: '#/components/schemas/NotificationResponsePayload',
|
||||
},
|
||||
},
|
||||
},
|
||||
NotificationStatus: {
|
||||
type: 'object',
|
||||
required: ['unread', 'read'],
|
||||
properties: {
|
||||
unread: {
|
||||
type: 'integer',
|
||||
},
|
||||
read: {
|
||||
type: 'integer',
|
||||
},
|
||||
},
|
||||
},
|
||||
TopicSetting: {
|
||||
type: 'object',
|
||||
required: ['id', 'enabled'],
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
},
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
},
|
||||
OriginSetting: {
|
||||
type: 'object',
|
||||
required: ['id', 'enabled'],
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
},
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
},
|
||||
topics: {
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: '#/components/schemas/TopicSetting',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ChannelSetting: {
|
||||
type: 'object',
|
||||
required: ['id', 'origins'],
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
},
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
},
|
||||
origins: {
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: '#/components/schemas/OriginSetting',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
NotificationSettings: {
|
||||
type: 'object',
|
||||
required: ['channels'],
|
||||
properties: {
|
||||
channels: {
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: '#/components/schemas/ChannelSetting',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
EntityRef: {
|
||||
description: 'A single entity ref, or a list of entity refs.',
|
||||
oneOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
OrderField: {
|
||||
description:
|
||||
'One or more "field,direction" pairs, given as a single string or an array of strings.',
|
||||
oneOf: [
|
||||
{
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
NotificationRecipients: {
|
||||
oneOf: [
|
||||
{
|
||||
type: 'object',
|
||||
required: ['type', 'entityRef'],
|
||||
properties: {
|
||||
type: {
|
||||
type: 'string',
|
||||
enum: ['entity'],
|
||||
},
|
||||
entityRef: {
|
||||
$ref: '#/components/schemas/EntityRef',
|
||||
},
|
||||
excludeEntityRef: {
|
||||
$ref: '#/components/schemas/EntityRef',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
required: ['type'],
|
||||
properties: {
|
||||
type: {
|
||||
type: 'string',
|
||||
enum: ['broadcast'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
NotificationSendOptions: {
|
||||
type: 'object',
|
||||
required: ['recipients', 'payload'],
|
||||
properties: {
|
||||
recipients: {
|
||||
$ref: '#/components/schemas/NotificationRecipients',
|
||||
},
|
||||
payload: {
|
||||
$ref: '#/components/schemas/NotificationPayload',
|
||||
},
|
||||
},
|
||||
},
|
||||
UpdateNotificationsRequest: {
|
||||
type: 'object',
|
||||
required: ['ids'],
|
||||
properties: {
|
||||
ids: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
read: {
|
||||
type: 'boolean',
|
||||
},
|
||||
saved: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
},
|
||||
ListNotificationsResponse: {
|
||||
type: 'object',
|
||||
required: ['totalCount', 'notifications'],
|
||||
properties: {
|
||||
totalCount: {
|
||||
type: 'integer',
|
||||
},
|
||||
notifications: {
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: '#/components/schemas/Notification',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
GetTopicsResponse: {
|
||||
type: 'object',
|
||||
required: ['topics'],
|
||||
properties: {
|
||||
topics: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Error: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
error: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['name', 'message'],
|
||||
},
|
||||
request: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
method: {
|
||||
type: 'string',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['method', 'url'],
|
||||
},
|
||||
response: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
statusCode: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
required: ['statusCode'],
|
||||
},
|
||||
},
|
||||
required: ['error', 'request', 'response'],
|
||||
},
|
||||
},
|
||||
securitySchemes: {
|
||||
JWT: {
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'JWT',
|
||||
},
|
||||
},
|
||||
},
|
||||
paths: {
|
||||
'/': {
|
||||
get: {
|
||||
operationId: 'ListNotificationsLegacy',
|
||||
deprecated: true,
|
||||
description: 'Deprecated alias for `GET /notifications`.',
|
||||
parameters: [
|
||||
{
|
||||
$ref: '#/components/parameters/offset',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/limit',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/orderField',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/topic',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/search',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/read',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/saved',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/createdAfter',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/minimumSeverity',
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'A page of notifications for the current user.',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/ListNotificationsResponse',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
post: {
|
||||
operationId: 'CreateNotificationsLegacy',
|
||||
deprecated: true,
|
||||
description: 'Deprecated alias for `POST /notifications`.',
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/NotificationSendOptions',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'The notifications that were created.',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: '#/components/schemas/Notification',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
'/notifications': {
|
||||
get: {
|
||||
operationId: 'ListNotifications',
|
||||
description: 'List notifications for the current user.',
|
||||
parameters: [
|
||||
{
|
||||
$ref: '#/components/parameters/offset',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/limit',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/orderField',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/topic',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/search',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/read',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/saved',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/createdAfter',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/minimumSeverity',
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'A page of notifications for the current user.',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/ListNotificationsResponse',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
post: {
|
||||
operationId: 'CreateNotifications',
|
||||
description:
|
||||
'Send a notification to one or more recipients (service credentials required).',
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/NotificationSendOptions',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'The notifications that were created.',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: '#/components/schemas/Notification',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
'/update': {
|
||||
post: {
|
||||
operationId: 'UpdateNotificationsLegacy',
|
||||
deprecated: true,
|
||||
description: 'Deprecated alias for `POST /notifications/update`.',
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/UpdateNotificationsRequest',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'The notifications that were updated.',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: '#/components/schemas/Notification',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
'/notifications/update': {
|
||||
post: {
|
||||
operationId: 'UpdateNotifications',
|
||||
description: 'Mark notifications as read/unread or saved/unsaved.',
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/UpdateNotificationsRequest',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'The notifications that were updated.',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: '#/components/schemas/Notification',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
'/status': {
|
||||
get: {
|
||||
operationId: 'GetStatus',
|
||||
description: 'Get the unread/read counts for the current user.',
|
||||
responses: {
|
||||
'200': {
|
||||
description: "The current user's notification status.",
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/NotificationStatus',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
'/settings': {
|
||||
get: {
|
||||
operationId: 'GetSettings',
|
||||
description:
|
||||
"Get the current user's notification settings, merged with defaults.",
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'Effective notification settings for the user.',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/NotificationSettings',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
post: {
|
||||
operationId: 'UpdateSettings',
|
||||
description: "Update the current user's notification settings.",
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/NotificationSettings',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'Effective notification settings for the user.',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/NotificationSettings',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
'/topics': {
|
||||
get: {
|
||||
operationId: 'GetTopics',
|
||||
description:
|
||||
'List notification topics that the user has received notifications on.',
|
||||
parameters: [
|
||||
{
|
||||
$ref: '#/components/parameters/search',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/read',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/saved',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/createdAfter',
|
||||
},
|
||||
{
|
||||
$ref: '#/components/parameters/minimumSeverity',
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'The list of topics.',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/GetTopicsResponse',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
'/{id}': {
|
||||
get: {
|
||||
operationId: 'GetNotificationLegacy',
|
||||
deprecated: true,
|
||||
description: 'Deprecated alias for `GET /notifications/{id}`.',
|
||||
parameters: [
|
||||
{
|
||||
$ref: '#/components/parameters/notificationId',
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'The requested notification.',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Notification',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
'/notifications/{id}': {
|
||||
get: {
|
||||
operationId: 'GetNotification',
|
||||
description: 'Get a single notification by id for the current user.',
|
||||
parameters: [
|
||||
{
|
||||
$ref: '#/components/parameters/notificationId',
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'The requested notification.',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Notification',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
export const createOpenApiRouter = async (
|
||||
options?: Parameters<
|
||||
typeof createValidatedOpenApiRouterFromGeneratedEndpointMap
|
||||
>['1'],
|
||||
) =>
|
||||
createValidatedOpenApiRouterFromGeneratedEndpointMap<EndpointMap>(
|
||||
spec,
|
||||
options,
|
||||
);
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './generated';
|
||||
@@ -14,8 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import express, { Request, Response } from 'express';
|
||||
import Router from 'express-promise-router';
|
||||
import express, { Request } from 'express';
|
||||
import { createOpenApiRouter, EndpointMap } from '../schema/openapi';
|
||||
import { internal as openapi } from '@backstage/backend-openapi-utils';
|
||||
import {
|
||||
normalizeSeverity,
|
||||
NotificationGetOptions,
|
||||
@@ -45,7 +46,6 @@ import {
|
||||
NotificationReadSignal,
|
||||
NotificationSettings,
|
||||
notificationSeverities,
|
||||
NotificationStatus,
|
||||
OriginSetting,
|
||||
} from '@backstage/plugin-notifications-common';
|
||||
import { parseEntityOrderFieldParams } from './parseEntityOrderFieldParams';
|
||||
@@ -108,7 +108,7 @@ export async function createRouter(
|
||||
recipientResolver ??
|
||||
new DefaultNotificationRecipientResolver(auth, catalog);
|
||||
|
||||
const getUser = async (req: Request<unknown>) => {
|
||||
const getUser = async (req: Request<any, any, any, any, any>) => {
|
||||
const credentials = await httpAuth.credentials(req, { allow: ['user'] });
|
||||
const info = await userInfo.getUserInfo(credentials);
|
||||
return info.userEntityRef;
|
||||
@@ -454,114 +454,112 @@ export async function createRouter(
|
||||
};
|
||||
|
||||
const appendCommonOptions = (
|
||||
req: Request,
|
||||
req: {
|
||||
query: openapi.StaticQueryParamsSchema<EndpointMap, '/topics', 'get'>;
|
||||
},
|
||||
opts: NotificationGetOptions | TopicGetOptions,
|
||||
) => {
|
||||
if (req.query.search) {
|
||||
opts.search = req.query.search.toString();
|
||||
opts.search = req.query.search;
|
||||
}
|
||||
if (req.query.read === 'true') {
|
||||
opts.read = true;
|
||||
} else if (req.query.read === 'false') {
|
||||
opts.read = false;
|
||||
// or keep undefined
|
||||
}
|
||||
|
||||
if (req.query.saved === 'true') {
|
||||
opts.saved = true;
|
||||
} else if (req.query.saved === 'false') {
|
||||
opts.saved = false;
|
||||
// or keep undefined
|
||||
}
|
||||
if (req.query.createdAfter) {
|
||||
const sinceEpoch = Date.parse(String(req.query.createdAfter));
|
||||
const sinceEpoch = Date.parse(req.query.createdAfter);
|
||||
if (isNaN(sinceEpoch)) {
|
||||
throw new InputError('Unexpected date format');
|
||||
}
|
||||
opts.createdAfter = new Date(sinceEpoch);
|
||||
}
|
||||
if (req.query.minimumSeverity) {
|
||||
opts.minimumSeverity = normalizeSeverity(
|
||||
req.query.minimumSeverity.toString(),
|
||||
);
|
||||
opts.minimumSeverity = normalizeSeverity(req.query.minimumSeverity);
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Move to use OpenAPI router instead
|
||||
const router = Router();
|
||||
router.use(express.json());
|
||||
const router = await createOpenApiRouter();
|
||||
|
||||
const listNotificationsHandler = async (req: Request, res: Response) => {
|
||||
const listNotificationsHandler: openapi.EndpointMapRequestHandler<
|
||||
EndpointMap,
|
||||
'/notifications',
|
||||
'get'
|
||||
> = async (req, res) => {
|
||||
const user = await getUser(req);
|
||||
const opts: NotificationGetOptions = {
|
||||
user: user,
|
||||
};
|
||||
if (req.query.offset) {
|
||||
opts.offset = Number.parseInt(req.query.offset.toString(), 10);
|
||||
const opts: NotificationGetOptions = { user };
|
||||
if (req.query.offset !== undefined) {
|
||||
opts.offset = req.query.offset;
|
||||
}
|
||||
if (req.query.limit) {
|
||||
opts.limit = Number.parseInt(req.query.limit.toString(), 10);
|
||||
if (req.query.limit !== undefined) {
|
||||
opts.limit = req.query.limit;
|
||||
}
|
||||
if (req.query.orderField) {
|
||||
opts.orderField = parseEntityOrderFieldParams(req.query);
|
||||
if (req.query.orderField !== undefined) {
|
||||
opts.orderField = parseEntityOrderFieldParams({
|
||||
orderField: req.query.orderField,
|
||||
});
|
||||
}
|
||||
|
||||
if (req.query.topic) {
|
||||
opts.topic = req.query.topic.toString();
|
||||
opts.topic = req.query.topic;
|
||||
}
|
||||
|
||||
appendCommonOptions(req, opts);
|
||||
|
||||
const [notifications, totalCount] = await Promise.all([
|
||||
store.getNotifications(opts),
|
||||
store.getNotificationsCount(opts),
|
||||
]);
|
||||
res.json({
|
||||
totalCount,
|
||||
notifications,
|
||||
});
|
||||
res.json({ totalCount, notifications });
|
||||
};
|
||||
|
||||
router.get('/', listNotificationsHandler); // Deprecated endpoint
|
||||
router.get('/', listNotificationsHandler); // Deprecated alias of /notifications
|
||||
router.get('/notifications', listNotificationsHandler);
|
||||
|
||||
router.get('/status', async (req: Request<any, NotificationStatus>, res) => {
|
||||
router.get('/status', async (req, res) => {
|
||||
const user = await getUser(req);
|
||||
const status = await store.getStatus({ user });
|
||||
res.json(status);
|
||||
});
|
||||
|
||||
router.get(
|
||||
'/settings',
|
||||
async (req: Request<any, NotificationSettings>, res) => {
|
||||
const user = await getUser(req);
|
||||
const response = await getNotificationSettings(user);
|
||||
res.json(response);
|
||||
},
|
||||
);
|
||||
router.get('/settings', async (req, res) => {
|
||||
const user = await getUser(req);
|
||||
const response = await getNotificationSettings(user);
|
||||
res.json(response);
|
||||
});
|
||||
|
||||
router.post(
|
||||
'/settings',
|
||||
async (
|
||||
req: Request<any, NotificationSettings, NotificationSettings>,
|
||||
res,
|
||||
) => {
|
||||
const user = await getUser(req);
|
||||
const channels = getNotificationChannels();
|
||||
const settings: NotificationSettings = req.body;
|
||||
if (settings.channels.some(c => !channels.includes(c.id))) {
|
||||
throw new InputError('Invalid channel');
|
||||
}
|
||||
await store.saveNotificationSettings({ user, settings });
|
||||
const response = await getNotificationSettings(user);
|
||||
res.json(response);
|
||||
},
|
||||
);
|
||||
router.post('/settings', async (req, res) => {
|
||||
const user = await getUser(req);
|
||||
const channels = getNotificationChannels();
|
||||
const settings: NotificationSettings = req.body;
|
||||
if (settings.channels.some(c => !channels.includes(c.id))) {
|
||||
throw new InputError('Invalid channel');
|
||||
}
|
||||
await store.saveNotificationSettings({ user, settings });
|
||||
const response = await getNotificationSettings(user);
|
||||
res.json(response);
|
||||
});
|
||||
|
||||
const getNotificationHandler = async (req: Request, res: Response) => {
|
||||
router.get('/topics', async (req, res) => {
|
||||
const user = await getUser(req);
|
||||
const opts: TopicGetOptions = { user };
|
||||
appendCommonOptions(req, opts);
|
||||
const topics = await store.getTopics(opts);
|
||||
res.json(topics);
|
||||
});
|
||||
|
||||
const getNotificationHandler: openapi.EndpointMapRequestHandler<
|
||||
EndpointMap,
|
||||
'/notifications/{id}',
|
||||
'get'
|
||||
> = async (req, res) => {
|
||||
const user = await getUser(req);
|
||||
const opts: NotificationGetOptions = {
|
||||
user: user,
|
||||
user,
|
||||
limit: 1,
|
||||
ids: [req.params.id],
|
||||
};
|
||||
@@ -572,31 +570,17 @@ export async function createRouter(
|
||||
res.json(notifications[0]);
|
||||
};
|
||||
|
||||
// Get topics
|
||||
const listTopicsHandler = async (req: Request, res: Response) => {
|
||||
const user = await getUser(req);
|
||||
const opts: TopicGetOptions = {
|
||||
user: user,
|
||||
};
|
||||
|
||||
appendCommonOptions(req, opts);
|
||||
|
||||
const topics = await store.getTopics(opts);
|
||||
res.json(topics);
|
||||
};
|
||||
|
||||
router.get('/topics', listTopicsHandler);
|
||||
|
||||
// Make sure this is the last "GET" handler
|
||||
router.get('/:id', getNotificationHandler); // Deprecated endpoint
|
||||
// Make sure this is the last "GET" handler.
|
||||
router.get('/:id', getNotificationHandler); // Deprecated alias of /notifications/:id
|
||||
router.get('/notifications/:id', getNotificationHandler);
|
||||
|
||||
const updateNotificationsHandler = async (req: Request, res: Response) => {
|
||||
const updateNotificationsHandler: openapi.EndpointMapRequestHandler<
|
||||
EndpointMap,
|
||||
'/notifications/update',
|
||||
'post'
|
||||
> = async (req, res) => {
|
||||
const user = await getUser(req);
|
||||
const { ids, read, saved } = req.body;
|
||||
if (!ids || !Array.isArray(ids)) {
|
||||
throw new InputError();
|
||||
}
|
||||
|
||||
if (read === true) {
|
||||
await store.markRead({ user, ids });
|
||||
@@ -609,7 +593,7 @@ export async function createRouter(
|
||||
});
|
||||
}
|
||||
} else if (read === false) {
|
||||
await store.markUnread({ user: user, ids });
|
||||
await store.markUnread({ user, ids });
|
||||
|
||||
if (signals) {
|
||||
await signals.publish<NotificationReadSignal>({
|
||||
@@ -621,16 +605,16 @@ export async function createRouter(
|
||||
}
|
||||
|
||||
if (saved === true) {
|
||||
await store.markSaved({ user: user, ids });
|
||||
await store.markSaved({ user, ids });
|
||||
} else if (saved === false) {
|
||||
await store.markUnsaved({ user: user, ids });
|
||||
await store.markUnsaved({ user, ids });
|
||||
}
|
||||
|
||||
const notifications = await store.getNotifications({ ids, user: user });
|
||||
const notifications = await store.getNotifications({ ids, user });
|
||||
res.json(notifications);
|
||||
};
|
||||
|
||||
router.post('/update', updateNotificationsHandler); // Deprecated endpoint
|
||||
router.post('/update', updateNotificationsHandler); // Deprecated alias of /notifications/update
|
||||
router.post('/notifications/update', updateNotificationsHandler);
|
||||
|
||||
const sendBroadcastNotification = async (
|
||||
@@ -768,10 +752,11 @@ export async function createRouter(
|
||||
return sent.filter(n => n !== undefined);
|
||||
};
|
||||
|
||||
const createNotificationHandler = async (
|
||||
req: Request<any, Notification[], NotificationSendOptions>,
|
||||
res: Response,
|
||||
) => {
|
||||
const createNotificationsHandler: openapi.EndpointMapRequestHandler<
|
||||
EndpointMap,
|
||||
'/notifications',
|
||||
'post'
|
||||
> = async (req, res) => {
|
||||
const credentials = await httpAuth.credentials(req, {
|
||||
allow: ['service'],
|
||||
});
|
||||
@@ -848,8 +833,8 @@ export async function createRouter(
|
||||
};
|
||||
|
||||
// Add new notification
|
||||
router.post('/', createNotificationHandler);
|
||||
router.post('/notifications', createNotificationHandler);
|
||||
router.post('/', createNotificationsHandler); // Deprecated alias of /notifications
|
||||
router.post('/notifications', createNotificationsHandler);
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -6276,6 +6276,7 @@ __metadata:
|
||||
resolution: "@backstage/plugin-notifications-backend@workspace:plugins/notifications-backend"
|
||||
dependencies:
|
||||
"@backstage/backend-defaults": "workspace:^"
|
||||
"@backstage/backend-openapi-utils": "workspace:^"
|
||||
"@backstage/backend-plugin-api": "workspace:^"
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
"@backstage/catalog-model": "workspace:^"
|
||||
@@ -6290,6 +6291,7 @@ __metadata:
|
||||
"@backstage/plugin-notifications-node": "workspace:^"
|
||||
"@backstage/plugin-signals-backend": "workspace:^"
|
||||
"@backstage/plugin-signals-node": "workspace:^"
|
||||
"@backstage/repo-tools": "workspace:^"
|
||||
"@backstage/types": "workspace:^"
|
||||
"@types/express": "npm:^4.17.6"
|
||||
"@types/supertest": "npm:^2.0.8"
|
||||
|
||||
Reference in New Issue
Block a user