Merge branch 'backstage:master' into feature/asyncapi3
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Replace default plugin extension and plugin ids to be `app` instead of `root`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-dynamic-feature-service': patch
|
||||
---
|
||||
|
||||
Fix wrong `alpha` support in dynamic plugins support: the `alpha` sub-package should not be required for the dynamic plugins to be loaded under the new backend system.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-github': minor
|
||||
---
|
||||
|
||||
Allow to force the creation of a pull request from a forked repository
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-react': patch
|
||||
---
|
||||
|
||||
fix for: converting circular structure to JSON error
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Simple typo fix in the fetch:template action example on the word 'skeleton'.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-adr': patch
|
||||
---
|
||||
|
||||
Fix alignment of text in `AdrSearchResultListItem`. Update size and font to match other `SearchResultListItem`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-graph': patch
|
||||
---
|
||||
|
||||
use `CatalogClient.getEntitiesByRefs()` to reduce the number of backend requests from plugin `catalog-graph`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-components': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed the `SidebarIntro` component as it was providing instructions for features that do not exist, along with `IntroCard`. If you were relying on this component and want to keep using it you can refer to the original implementations of [`SidebarIntro`](https://github.com/backstage/backstage/blob/80f2413334ed9b221ec3c2b7c22fa737ad8d8885/packages/core-components/src/layout/Sidebar/Intro.tsx#L149) and [`IntroCard`](https://github.com/backstage/backstage/blob/80f2413334ed9b221ec3c2b7c22fa737ad8d8885/packages/core-components/src/layout/Sidebar/Intro.tsx#L100).
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Move the `NODE_OPTIONS` messaging for `--no-node-snapshot` to the `SecureTemplater` in order to get better messaging at runtime
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/plugin-analytics-module-newrelic-browser': minor
|
||||
'@backstage/plugin-analytics-module-ga4': minor
|
||||
'@backstage/plugin-analytics-module-ga': minor
|
||||
'@backstage/core-compat-api': minor
|
||||
---
|
||||
|
||||
Add support to the new analytics api.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Upgrade jest
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/catalog-model': patch
|
||||
'@backstage/test-utils': patch
|
||||
'@backstage/plugin-explore': patch
|
||||
---
|
||||
|
||||
Fix wording in API reference
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/catalog-client': minor
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
---
|
||||
|
||||
Add API to get location by entity
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-github': patch
|
||||
---
|
||||
|
||||
Decreased number of teams fetched by GraphQL Query responsible for fetching Teams and Members in organization, due to timeouts when running against big organizations
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Minor performance improvement for `queryEntities` when the limit is 0.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-app-api': patch
|
||||
---
|
||||
|
||||
Wrap the root element with the analytics context to ensure it always exists for all extensions.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Add line clamping to `CatalogSearchResultListItem`
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-react': patch
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Added `ScaffolderPageContextMenu` to `ActionsPage`, `ListTaskPage`, and `TemplateEditorPage` so that you can more easily navigate between these pages
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/backend-plugin-api': patch
|
||||
'@backstage/backend-common': patch
|
||||
---
|
||||
|
||||
Support `token` in `readTree`, `readUrl` and `search`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes-react': patch
|
||||
---
|
||||
|
||||
Extracted common dialog component.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@techdocs/cli': patch
|
||||
---
|
||||
|
||||
fix: mkdocs parameter casing
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-plugin-api': patch
|
||||
---
|
||||
|
||||
Throw a more specific exception `NotImplementedError` when an API implementation cannot be found.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/catalog-client': patch
|
||||
---
|
||||
|
||||
Internal updates to auto-generated files.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-app-api': patch
|
||||
---
|
||||
|
||||
The default `ComponentsApi` implementation now uses the `ComponentRef` ID as the component key, rather than the reference instance. This fixes a bug where duplicate installations of `@backstage/frontend-plugin-api` would break the app.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
---
|
||||
|
||||
App component extensions are no longer wrapped in an `ExtensionBoundary`, allowing them to inherit the outer context instead.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/integration': minor
|
||||
'@backstage/backend-common': patch
|
||||
---
|
||||
|
||||
Fix rate limit detection by looking for HTTP status code 429 and updating the header `x-ratelimit-remaining` to look for in case of a 403 code is returned
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/repo-tools': patch
|
||||
---
|
||||
|
||||
Fixes an issue where comments would be duplicated in the template. Also, removes a header with the title and version of the OpenAPI spec from generated code.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': minor
|
||||
'@backstage/plugin-scaffolder-node': minor
|
||||
---
|
||||
|
||||
Support providing an overriding token for `fetch:template`, `fetch:plain` and `fetch:file` when interacting with upstream integrations
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs': minor
|
||||
---
|
||||
|
||||
Updated the styling for <code> tags to avoid word break.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-react': minor
|
||||
---
|
||||
|
||||
Use more distinguishable icons for link (`Link`) and text output (`Description`).
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
'@backstage/frontend-app-api': patch
|
||||
'@backstage/core-compat-api': patch
|
||||
---
|
||||
|
||||
Allow external route refs in the new system to have a `defaultTarget` pointing to a route that it'll resolve to by default if no explicit bindings were made by the adopter.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-react': patch
|
||||
---
|
||||
|
||||
Fix issue where `ui:schema` was replaced with an empty object if `dependencies` is defined
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Fixed an issue that would cause an invalid `__backstage-autodetected-plugins__.js` to be written when using experimental module discovery.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-home': patch
|
||||
---
|
||||
|
||||
Added filter support for HomePageVisitedByType in order to enable filtering entities from the list
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
'@backstage/plugin-scaffolder-node': patch
|
||||
---
|
||||
|
||||
Add option to configure nunjucks with the `trimBlocks` and `lstripBlocks` options in the fetch:template action
|
||||
@@ -66,6 +66,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: 'Upload to code-scanning'
|
||||
uses: github/codeql-action/upload-sarif@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1
|
||||
uses: github/codeql-action/upload-sarif@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3.23.2
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -58,6 +58,6 @@ jobs:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
NODE_OPTIONS: --max-old-space-size=7168
|
||||
- name: Upload Snyk report
|
||||
uses: github/codeql-action/upload-sarif@v3.23.1
|
||||
uses: github/codeql-action/upload-sarif@v3.23.2
|
||||
with:
|
||||
sarif_file: snyk.sarif
|
||||
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3.23.1
|
||||
uses: github/codeql-action/init@v3.23.2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3.23.1
|
||||
uses: github/codeql-action/autobuild@v3.23.2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -80,4 +80,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3.23.1
|
||||
uses: github/codeql-action/analyze@v3.23.2
|
||||
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v1.3.1
|
||||
uses: microsoft/setup-msbuild@v1.3.2
|
||||
|
||||
- name: Setup gyp env
|
||||
run: |
|
||||
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
|
||||
- run: yarn build-storybook
|
||||
|
||||
- uses: chromaui/action@5f2cdb26c04c0364f5c8ca3fc41627a83d6ffc8a # v10
|
||||
- uses: chromaui/action@05a82adb1e6919df177f54777e81a2ef3e312323 # v10
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# projectToken intentionally shared to allow collaborators to run Chromatic on forks
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
env:
|
||||
CI: true
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
NODE_OPTIONS: ${{ matrix.node-version == '20.x' && '--max-old-space-size=4096 --no-node-snapshot' || '--max-old-space-size=4096' }}
|
||||
INTEGRATION_TEST_GITHUB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITHUB_TOKEN }}
|
||||
INTEGRATION_TEST_GITLAB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITLAB_TOKEN }}
|
||||
INTEGRATION_TEST_BITBUCKET_TOKEN: ${{ secrets.INTEGRATION_TEST_BITBUCKET_TOKEN }}
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
---
|
||||
title: Auth Architecture Evolution
|
||||
status: provisional
|
||||
authors:
|
||||
- '@Rugvip'
|
||||
owners:
|
||||
- '@backstage/maintainers'
|
||||
project-areas:
|
||||
- core
|
||||
creation-date: 2024-01-28
|
||||
---
|
||||
|
||||
# BEP: Auth Architecture Evolution
|
||||
|
||||
<!-- Before merging the initial BEP PR, create a feature issue and update the below link. You can wait with this step until the BEP is ready to be merged. -->
|
||||
|
||||
[**Discussion Issue**](https://github.com/backstage/backstage/issues/NNNNN)
|
||||
|
||||
- [Summary](#summary)
|
||||
- [Motivation](#motivation)
|
||||
- [Goals](#goals)
|
||||
- [Non-Goals](#non-goals)
|
||||
- [Proposal](#proposal)
|
||||
- [Design Details](#design-details)
|
||||
- [Release Plan](#release-plan)
|
||||
- [Dependencies](#dependencies)
|
||||
- [Alternatives](#alternatives)
|
||||
|
||||
## Summary
|
||||
|
||||
This proposal outlines a new architecture for authenticating users and services in Backstage. It adds built-in access restriction to Backstage instances that protects them from outside access, and enables more granular access control for inter-plugin communication and access from external services.
|
||||
|
||||
It proposes a new `AuthService` interface that handles all user and service authentication and token management available to plugins, as well as a new `HttpAuthService` interface that is a higher level service used to protect endpoints of plugin routers. The `auth-backend` will also be extended to support issuing of user tokens with a reduced scope for cookie-based authentication of requests.
|
||||
|
||||
The changes to the service-to-service auth are aimed to be the minimum needed to get the necessary interfaces in place, and will rely on the existing symmetrical keys for now.
|
||||
|
||||
## Motivation
|
||||
|
||||
This proposal aims to address several of the points in the [Auth Meta issue](https://github.com/backstage/backstage/issues/15999), with the overarching goal being to replace the existing [API request authentication](https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/authenticate-api-requests.md) tutorial in `contrib/` with a more robust and secure built-in solution. The tutorial exists for two purposes: to add authentication of API requests as part of using the permission system in Backstage, and to protect a Backstage instance from external access. It does a fairly good job of the former, although we want to avoid placing user tokens in cookies, but it does a quite poor job of the latter, which we want to fix.
|
||||
|
||||
A secondary goal is to do this work before stabilizing the APIs in the new Backend system, as it will have some impact on how plugin backends are built. This will inevitably also lead to the need to improve the way that service-to-service auth is handled in Backstage, although that is not the primary goal of this work.
|
||||
|
||||
By providing protection of Backstage instances out of the box we drastically reduce both the barrier of entry as well as security risks for Backstage adopters. It will no longer be a requirement to either set up protection of your Backstage instance or not do so and risk exposing your instance to malicious actors. It also drastically reduces the complexity of enabling the permission system of Backstage, since access restrictions are already built-in.
|
||||
|
||||
### Goals
|
||||
|
||||
The following goals are the primary focus of this BEP:
|
||||
|
||||
- Built-in protection of Backstage instances such that it is safe to deploy Backstage directly towards the internet.
|
||||
- Protection of the frontend app bundle from being accessed by unauthenticated users.
|
||||
- Basic rate limiting of non-authenticated requests.
|
||||
- Cookie-based authentication of requests for static assets that protects against CSRF attacks and does not unnecessarily expose user tokens.
|
||||
- Basic improvements to the service-to-service auth service interfaces such that we are confident that we do not need to break them in the near future.
|
||||
- If possible we will keep using the existing symmetrical keys that are used today, but it is likely that we will need to break compatibility of existing tokens.
|
||||
- Encapsulation of user credentials in upstream service requests, avoiding the pattern where backend plugins re-use the user token directly for their outgoing requests.
|
||||
|
||||
### Non-Goals
|
||||
|
||||
- No advanced rate limiting or other protection against DDoS attacks. If this is a concern then adopters should still use other external technologies to protect access to their Backstage instance.
|
||||
- As part of the immediate work we will only add as much support for service-to-service auth as is needed for a stable API, and not necessarily make it very capable from the start.
|
||||
|
||||
## Proposal
|
||||
|
||||
Two new backend service interfaces are introduced to support these new features. The new `AuthService` is a low-level service that encapsulates authentication and creation of bearer tokens for all types of Backstage identities, including user, service, and services making requests on behalf of users. The new `HttpAuthService` is a higher level service that lets your create [express middleware](https://expressjs.com/en/guide/using-middleware.html) to protect endpoints, as well as access the credentials and identity of incoming requests. These new services replace the existing `IdentityService` and `TokenManagerService`
|
||||
|
||||
The proposed design leaves the decision for how different endpoints are protected to the implementation of the plugin backends themselves. This includes whether particular routes should allow anonymous access, access from users authenticated via a cookie, or perhaps only allow access from other plugin backends and external services. This means that integrators do not need to - and do not have the ability to - configure access controls of individual endpoints, except for what the permission system already provides, and what is made available through static configuration or extension points.
|
||||
|
||||
In order to allow for cookie-based authentication of incoming user requests, the `auth` plugin backend is extended to be able to issue user tokens with reduced scope, which in turn integrate with the new `AuthService` and `HttpAuthService`. The ability to use cookie auth for requests is an opt-in per route and is only be permitted for read methods (`GET`, `HEAD`, `OPTIONS`). The actual implementation of cookie-based flows will be up to each plugin, but with significant help from the new auth service interfaces.
|
||||
|
||||
For service-to-service communication we will move away from reusing user tokens in upstream requests. We will instead implement an "On-Behalf-Of" flow where incoming user credentials are encapsulated in a service token for the upstream request. In line with this the new auth service interfaces will aim to make it difficult to directly forward credentials from incoming requests, and instead encourage that plugin backends issue new service credentials for upstream requests.
|
||||
|
||||
## Design Details
|
||||
|
||||
### AuthService (WIP)
|
||||
|
||||
The new `AuthService` interface is defined as follows:
|
||||
|
||||
```ts
|
||||
export type BackstageCredentials = {
|
||||
token: string;
|
||||
|
||||
user?: {
|
||||
userEntityRef: string;
|
||||
ownershipEntityRefs: string[];
|
||||
};
|
||||
|
||||
service?: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
export interface AuthService {
|
||||
authenticate(token: string): Promise<BackstageCredentials>;
|
||||
|
||||
issueToken(credentials: BackstageCredentials): Promise<{ token: string }>;
|
||||
}
|
||||
```
|
||||
|
||||
TODO: add usage patterns for this service
|
||||
|
||||
### HttpAuthService (WIP)
|
||||
|
||||
The new `HttpAuthService` interface is defined as follows:
|
||||
|
||||
```ts
|
||||
type AuthTypes = 'user' | 'user-cookie' | 'service' | 'unauthorized';
|
||||
|
||||
export interface HttpAuthServiceMiddlewareOptions {
|
||||
allow: AuthTypes[];
|
||||
}
|
||||
|
||||
export interface HttpAuthService {
|
||||
createHttpPluginRouterMiddleware(): Handler;
|
||||
|
||||
middleware(options?: HttpAuthServiceMiddlewareOptions): Handler;
|
||||
|
||||
credentials(
|
||||
req: Request,
|
||||
options?: HttpAuthServiceMiddlewareOptions,
|
||||
): BackstageCredentials;
|
||||
|
||||
requestHeaders(credentials: BackstageCredentials): Record<string, string>;
|
||||
|
||||
issueUserCookie(res: Response): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
TODO: add usage patterns for this service
|
||||
|
||||
## Release Plan
|
||||
|
||||
The existing `IdentityService` and `TokenManagerService` will be deprecated and instead implemented in terms of the new `AuthService`.
|
||||
|
||||
The release plan for the `HttpAuthService` is TBD, but is likely to be shipped as a no-op for plugins using the old backend system.
|
||||
|
||||
## Dependencies
|
||||
|
||||
No significant dependencies have been identified for this work, although any future security audits of Backstage are considered dependent on this work.
|
||||
|
||||
## Alternatives
|
||||
|
||||
An alternative to built-in protection from external access would be to keep relying on external mechanisms to protect access to Backstage. We feel that this is a suboptimal solution since it adds complexity to the adoption of Backstage, and increases the risk of misconfiguration and security breaches. Regardless of whether we add built-in protection or not the ability to protect API endpoints needs to be addressed in some way, since it is a requirement for the permission system to work. This means that the extra steps to ensure protection out of the box are fairly minimal when looking at just the delta for protecting API access.
|
||||
@@ -474,6 +474,34 @@ Response type is JSON, on the form
|
||||
]
|
||||
```
|
||||
|
||||
### `GET /locations/{id}`
|
||||
|
||||
Gets a location by it's location ID.
|
||||
|
||||
Response type is JSON, on the form
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "b9784c38-7118-472f-9e22-5638fc73bab0",
|
||||
"target": "https://git.example.com/example-project/example-repository/blob/main/catalog-info.yaml",
|
||||
"type": "url"
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /locations/by-entity/{kind}/{namespace}/{name}`
|
||||
|
||||
Gets a location referring to a given entity.
|
||||
|
||||
Response type is JSON, on the form
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "b9784c38-7118-472f-9e22-5638fc73bab0",
|
||||
"target": "https://git.example.com/example-project/example-repository/blob/main/catalog-info.yaml",
|
||||
"type": "url"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /locations`
|
||||
|
||||
Adds a location to be ingested by the catalog.
|
||||
|
||||
@@ -114,7 +114,7 @@ const extension = createExtension({
|
||||
|
||||
### Extension Data Uniqueness
|
||||
|
||||
Note that the key used in the output map, in this case `element`, is only used internally within the definition of the extension itself. That actual identifier for the data when consumed by other extensions is the ID of the reference, in this case `core.reactElement`. This means that you can not output multiple different values for the same extension data reference, as they would conflict with each other. That in turn makes overly generic extension data references a bad idea, for example a generic "string" type. Instead create separate references for each type of data that you want to share.
|
||||
Note that the key used in the output map, in this case `element`, is only used internally within the definition of the extension itself. That actual identifier for the data when consumed by other extensions is the ID of the reference, in this case [`core.reactElement`](https://github.com/backstage/backstage/blob/916da47e8abdb880877daa18881eb8fdbb33e70a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts#L23). This means that you can not output multiple different values for the same extension data reference, as they would conflict with each other. That in turn makes overly generic extension data references a bad idea, for example a generic "string" type. Instead create separate references for each type of data that you want to share.
|
||||
|
||||
```tsx
|
||||
const extension = createExtension({
|
||||
@@ -160,7 +160,7 @@ const extension = createExtension({
|
||||
|
||||
## Extension Inputs
|
||||
|
||||
The Extension Data can be passed up to other extensions through their extension inputs. Similar to the outputs seen before, let's create an example an extension with a extension input:
|
||||
The Extension Data can be passed up to other extensions through their extension inputs. Similar to the outputs seen before, let's create an example of an extension with a extension input:
|
||||
|
||||
```tsx
|
||||
const navigationExtension = createExtension({
|
||||
@@ -254,7 +254,7 @@ With the `inputs` not only the `output` of an extensions item is passed to the e
|
||||
|
||||
## Extension Configuration
|
||||
|
||||
With the `app-config.yaml` there is already the option to pass configuration to plugins or the app to e.g. define the `baseURL` of your app. For extensions this concept would be limiting as an extension can be independent of the plugin & initiated several times. Therefor we created a possibility to configure each extension individually through config. The extension config schema is created using the [`zod`](https://zod.dev/) library, which in addition to TypeScript type checking also provides runtime validation and coercion. If we continue with the example of the `navigationExtension` and now want it to contain a configurable title, we could make it available like the following:
|
||||
With the `app-config.yaml` there is already the option to pass configuration to plugins or the app to e.g. define the `baseURL` of your app. For extensions this concept would be limiting as an extension can be independent of the plugin & initiated several times. Therefore we created a possibility to configure each extension individually through config. The extension config schema is created using the [`zod`](https://zod.dev/) library, which in addition to TypeScript type checking also provides runtime validation and coercion. If we continue with the example of the `navigationExtension` and now want it to contain a configurable title, we could make it available like the following:
|
||||
|
||||
```tsx
|
||||
const navigationExtension = createExtension({
|
||||
@@ -297,7 +297,7 @@ app:
|
||||
|
||||
With creating an extension by using `createExtension(...)` you have the advantage that the extension can be anything in your Backstage application. We realised that this comes with the trade-off of having to repeat boilerplate code for similar building blocks. Here extension creators come into play for covering common building blocks in Backstage like pages using `createPageExtension`, themes using the `createThemeExtension` or items for the navigation using `createNavItemExtension`.
|
||||
|
||||
If we follow the example from above all items of the navigation have similarities, like they all want to be attached to the same extension with the same input as well as rendering the same navigation item component. Therefor `createExtension` can be abstracted for this use case to `createNavItemExtension` and if we add the extension to the app it will end up in the right place & looks like we expect a navigation item to look.
|
||||
If we follow the example from above all items of the navigation have similarities, like they all want to be attached to the same extension with the same input as well as rendering the same navigation item component. Therefore `createExtension` can be abstracted for this use case to `createNavItemExtension` and if we add the extension to the app it will end up in the right place & looks like we expect a navigation item to look.
|
||||
|
||||
```tsx
|
||||
export const HomeNavIcon = createNavItemExtension({
|
||||
|
||||
@@ -14,7 +14,7 @@ Utility APIs are pieces of standalone functionality, interfaces that can be requ
|
||||
|
||||
A common example of a utility API is a client interface to interact with the backend part of a plugin, such as the catalog client. Any frontend plugin can then request an implementation of that interface to make requests through.
|
||||
|
||||
The following diagram shows a hypothetical application, which depends on two plugins and also provides some extra overrides. Note that both the plugins and the core framework provide utility APIs, and that they depended on each other. The app also chooses to use its overrides mechanism to supply a replacement implementation of one API, which takes precedence over the default one. Thus, all consumers of that API will be sure to get that new implementation provided to them.
|
||||
The following diagram shows a hypothetical application, which depends on two plugins and also provides some extra overrides. Note that both the plugins and the core framework provide utility APIs, and that they depend on each other. The app also chooses to use its overrides mechanism to supply a replacement implementation of one API, which takes precedence over the default one. Thus, all consumers of that API will be sure to get that new implementation provided to them.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ export default createPlugin({
|
||||
});
|
||||
```
|
||||
|
||||
In the example above we associated the `indexRouteRef` with the `catalogIndexPage` extension and provided both the route ref and page via the Catalog plugin. So, When this plugin is installed in the app, the index page will become associated with the newly created `RouteRef`, making it possible to use the route ref to navigate the page extension.
|
||||
In the example above we associated the `indexRouteRef` with the `catalogIndexPage` extension and provided both the route ref and page via the Catalog plugin. So, when this plugin is installed in the app, the index page will become associated with the newly created `RouteRef`, making it possible to use the route ref to navigate the page extension.
|
||||
|
||||
It may seem unclear why we configure the `routes` option when creating a plugin as the route has already been passed to the extension. We do that to make it possible for other plugins to route to our page, which is explained in detail in the [binding routes](#binding-external-route-references) section.
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ describe('Entity details component', () => {
|
||||
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApiMock]]}>
|
||||
<EntityDetails entitRef={entityRef} />
|
||||
<EntityDetails entityRef={entityRef} />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ As part of signing in with an identity resolver, a Backstage Token is issued con
|
||||
|
||||
The token is used to prove the identity of the user within the Backstage system, and is used throughout Backstage plugins to control access. It is important that the ownership resolution logic is consistent across the entire Backstage ecosystem, with no possibility of misinterpreting the ownership information.
|
||||
|
||||
For cross backend communication the Backstage Token is typically forwarded or in strict backend to backend communication without a user party the backend itself issues a service token based on a pre-shared secret which is then validated on the receiving end. There are no unique service identities tied to these tokens at this point, meaning the tokens can be used across all services in a Backstage installation, this is something that we aim to improve in the future.
|
||||
For cross-backend communication, the Backstage Token is typically forwarded or, in strict backend-to-backend communication without a user party, the backend itself issues a service token based on a pre-shared secret which is then validated on the receiving end. There are no unique service identities tied to these tokens at this point, meaning the tokens can be used across all services in a Backstage installation. This is something that we aim to improve in the future.
|
||||
|
||||
Backstage also supports authentication through a proxy where the user identity is read from the incoming request from the proxy, which has been decorated by an authenticating reverse proxy such as [AWS ALB](https://aws.amazon.com/elasticloadbalancing/application-load-balancer/). The following proxy auth providers verify the signature of incoming requests, and are therefore safe to deploy with direct access by users: `awsAlb`, `cfAccess`, and `gcpIap`. Providers like `oauth2Proxy` does not verify the incoming request and can therefore be spoofed by a malicious internal user to supply the `auth` backend with forged identity information. It’s therefore highly recommended to restrict access to the `oauth2Proxy` endpoints, or use a different provider.
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ Contributed by [@davidfestal](https://github.com/davidfestal) in [#18862](https:
|
||||
|
||||
### New Scaffolder action `gitlab:issues:create`
|
||||
|
||||
You can now create GitHub issues in your scaffolder flows! Contributed by [@elaine-mattos](https://github.com/elaine-mattos) in [#21929](https://github.com/backstage/backstage/pull/21929)
|
||||
You can now create GitLab issues in your scaffolder flows! Contributed by [@elaine-mattos](https://github.com/elaine-mattos) in [#21929](https://github.com/backstage/backstage/pull/21929)
|
||||
|
||||
### New Scaffolder action `gitlab:repo:push`
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@ To switch a project to React 18, there are generally three changes that need to
|
||||
// highlight-remove-next-line
|
||||
"@types/react": "^17",
|
||||
// highlight-remove-next-line
|
||||
"@types/react": "^17",
|
||||
"@types/react-dom": "^17",
|
||||
// highlight-add-next-line
|
||||
"@types/react-dom": "^18",
|
||||
"@types/react": "^18",
|
||||
// highlight-add-next-line
|
||||
"@types/react-dom": "^18",
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@ author: dweber019
|
||||
authorUrl: https://github.com/dweber019
|
||||
category: Linting
|
||||
description: API Spectral Linter is a quality assurance tool that checks the compliance of API's specifications Spectral rule sets.
|
||||
documentation: https://github.com/dweber019/backstage-plugin-api-docs-spectral-linter
|
||||
iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugin-api-docs-spectral-linter/main/plugins/api-docs-spectral-linter/docs/pluginIcon.png
|
||||
documentation: https://github.com/dweber019/backstage-plugins/tree/main/plugins/api-docs-spectral-linter
|
||||
iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugins/main/plugins/api-docs-spectral-linter/docs/pluginIcon.png
|
||||
npmPackageName: '@dweber019/backstage-plugin-api-docs-spectral-linter'
|
||||
addedDate: '2023-03-27'
|
||||
|
||||
@@ -4,7 +4,7 @@ author: dweber019
|
||||
authorUrl: https://github.com/dweber019
|
||||
category: Discovery
|
||||
description: An API docs extension to render WSDL / SOAP web services human readable.
|
||||
documentation: https://github.com/dweber019/backstage-plugin-api-docs-module-wsdl
|
||||
iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugin-api-docs-module-wsdl/main/docs/logo.png
|
||||
documentation: https://github.com/dweber019/backstage-plugins/tree/main/plugins/api-docs-module-wsdl
|
||||
iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugins/main/plugins/api-docs-module-wsdl/docs/logo.png
|
||||
npmPackageName: '@dweber019/backstage-plugin-api-docs-module-wsdl'
|
||||
addedDate: '2023-12-22'
|
||||
|
||||
@@ -4,7 +4,7 @@ author: dweber019
|
||||
authorUrl: https://github.com/dweber019
|
||||
category: Quality
|
||||
description: Display end of life data for entities from endoflife.data
|
||||
documentation: https://github.com/dweber019/backstage-plugin-endoflife
|
||||
iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugin-endoflife/main/plugins/endoflife/docs/pluginIcon.png
|
||||
documentation: https://github.com/dweber019/backstage-plugins/tree/main/plugins/endoflife
|
||||
iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugins/main/plugins/endoflife/docs/pluginIcon.png
|
||||
npmPackageName: '@dweber019/backstage-plugin-endoflife'
|
||||
addedDate: '2024-01-18'
|
||||
|
||||
@@ -4,7 +4,7 @@ author: dweber019
|
||||
authorUrl: https://github.com/dweber019
|
||||
category: Discovery
|
||||
description: The tips plugin will show the end user some helpful information for entities.
|
||||
documentation: https://github.com/dweber019/backstage-plugin-tips
|
||||
iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugin-tips/main/plugins/tips/docs/pluginIcon.png
|
||||
documentation: https://github.com/dweber019/backstage-plugins/tree/main/plugins/tips
|
||||
iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugins/main/plugins/tips/docs/pluginIcon.png
|
||||
npmPackageName: '@dweber019/backstage-plugin-tips'
|
||||
addedDate: '2023-10-08'
|
||||
|
||||
+43
-43
@@ -2701,90 +2701,90 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-darwin-arm64@npm:1.3.105":
|
||||
version: 1.3.105
|
||||
resolution: "@swc/core-darwin-arm64@npm:1.3.105"
|
||||
"@swc/core-darwin-arm64@npm:1.3.107":
|
||||
version: 1.3.107
|
||||
resolution: "@swc/core-darwin-arm64@npm:1.3.107"
|
||||
conditions: os=darwin & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-darwin-x64@npm:1.3.105":
|
||||
version: 1.3.105
|
||||
resolution: "@swc/core-darwin-x64@npm:1.3.105"
|
||||
"@swc/core-darwin-x64@npm:1.3.107":
|
||||
version: 1.3.107
|
||||
resolution: "@swc/core-darwin-x64@npm:1.3.107"
|
||||
conditions: os=darwin & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-linux-arm-gnueabihf@npm:1.3.105":
|
||||
version: 1.3.105
|
||||
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.105"
|
||||
"@swc/core-linux-arm-gnueabihf@npm:1.3.107":
|
||||
version: 1.3.107
|
||||
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.107"
|
||||
conditions: os=linux & cpu=arm
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-linux-arm64-gnu@npm:1.3.105":
|
||||
version: 1.3.105
|
||||
resolution: "@swc/core-linux-arm64-gnu@npm:1.3.105"
|
||||
"@swc/core-linux-arm64-gnu@npm:1.3.107":
|
||||
version: 1.3.107
|
||||
resolution: "@swc/core-linux-arm64-gnu@npm:1.3.107"
|
||||
conditions: os=linux & cpu=arm64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-linux-arm64-musl@npm:1.3.105":
|
||||
version: 1.3.105
|
||||
resolution: "@swc/core-linux-arm64-musl@npm:1.3.105"
|
||||
"@swc/core-linux-arm64-musl@npm:1.3.107":
|
||||
version: 1.3.107
|
||||
resolution: "@swc/core-linux-arm64-musl@npm:1.3.107"
|
||||
conditions: os=linux & cpu=arm64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-linux-x64-gnu@npm:1.3.105":
|
||||
version: 1.3.105
|
||||
resolution: "@swc/core-linux-x64-gnu@npm:1.3.105"
|
||||
"@swc/core-linux-x64-gnu@npm:1.3.107":
|
||||
version: 1.3.107
|
||||
resolution: "@swc/core-linux-x64-gnu@npm:1.3.107"
|
||||
conditions: os=linux & cpu=x64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-linux-x64-musl@npm:1.3.105":
|
||||
version: 1.3.105
|
||||
resolution: "@swc/core-linux-x64-musl@npm:1.3.105"
|
||||
"@swc/core-linux-x64-musl@npm:1.3.107":
|
||||
version: 1.3.107
|
||||
resolution: "@swc/core-linux-x64-musl@npm:1.3.107"
|
||||
conditions: os=linux & cpu=x64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-win32-arm64-msvc@npm:1.3.105":
|
||||
version: 1.3.105
|
||||
resolution: "@swc/core-win32-arm64-msvc@npm:1.3.105"
|
||||
"@swc/core-win32-arm64-msvc@npm:1.3.107":
|
||||
version: 1.3.107
|
||||
resolution: "@swc/core-win32-arm64-msvc@npm:1.3.107"
|
||||
conditions: os=win32 & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-win32-ia32-msvc@npm:1.3.105":
|
||||
version: 1.3.105
|
||||
resolution: "@swc/core-win32-ia32-msvc@npm:1.3.105"
|
||||
"@swc/core-win32-ia32-msvc@npm:1.3.107":
|
||||
version: 1.3.107
|
||||
resolution: "@swc/core-win32-ia32-msvc@npm:1.3.107"
|
||||
conditions: os=win32 & cpu=ia32
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-win32-x64-msvc@npm:1.3.105":
|
||||
version: 1.3.105
|
||||
resolution: "@swc/core-win32-x64-msvc@npm:1.3.105"
|
||||
"@swc/core-win32-x64-msvc@npm:1.3.107":
|
||||
version: 1.3.107
|
||||
resolution: "@swc/core-win32-x64-msvc@npm:1.3.107"
|
||||
conditions: os=win32 & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core@npm:^1.3.46":
|
||||
version: 1.3.105
|
||||
resolution: "@swc/core@npm:1.3.105"
|
||||
version: 1.3.107
|
||||
resolution: "@swc/core@npm:1.3.107"
|
||||
dependencies:
|
||||
"@swc/core-darwin-arm64": 1.3.105
|
||||
"@swc/core-darwin-x64": 1.3.105
|
||||
"@swc/core-linux-arm-gnueabihf": 1.3.105
|
||||
"@swc/core-linux-arm64-gnu": 1.3.105
|
||||
"@swc/core-linux-arm64-musl": 1.3.105
|
||||
"@swc/core-linux-x64-gnu": 1.3.105
|
||||
"@swc/core-linux-x64-musl": 1.3.105
|
||||
"@swc/core-win32-arm64-msvc": 1.3.105
|
||||
"@swc/core-win32-ia32-msvc": 1.3.105
|
||||
"@swc/core-win32-x64-msvc": 1.3.105
|
||||
"@swc/core-darwin-arm64": 1.3.107
|
||||
"@swc/core-darwin-x64": 1.3.107
|
||||
"@swc/core-linux-arm-gnueabihf": 1.3.107
|
||||
"@swc/core-linux-arm64-gnu": 1.3.107
|
||||
"@swc/core-linux-arm64-musl": 1.3.107
|
||||
"@swc/core-linux-x64-gnu": 1.3.107
|
||||
"@swc/core-linux-x64-musl": 1.3.107
|
||||
"@swc/core-win32-arm64-msvc": 1.3.107
|
||||
"@swc/core-win32-ia32-msvc": 1.3.107
|
||||
"@swc/core-win32-x64-msvc": 1.3.107
|
||||
"@swc/counter": ^0.1.1
|
||||
"@swc/types": ^0.1.5
|
||||
peerDependencies:
|
||||
@@ -2813,7 +2813,7 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
"@swc/helpers":
|
||||
optional: true
|
||||
checksum: 5baa880bc92748ef4845d9c65eba5d6dd01adaa673854e20a5116f5e267c12180db50e563cf3c34a415772b9742d021176a9d9a91065c190ef6f54fefe85728c
|
||||
checksum: 0dccff50461fb8c0f4af053b70e555c91386cb07aa7657a7328d58e397d15640723587549416d8fa7dcc073ad11b39318146bd50ec4a82345ce2ce39c7ba4c00
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ app:
|
||||
packages: 'all' # ✨
|
||||
routes:
|
||||
bindings:
|
||||
pages.pageX: pages.pageX
|
||||
catalog.viewTechDoc: techdocs.docRoot
|
||||
catalog.createComponent: catalog-import.importPage
|
||||
|
||||
|
||||
@@ -27,7 +27,9 @@ import { Route, Routes } from 'react-router-dom';
|
||||
|
||||
const indexRouteRef = createRouteRef();
|
||||
const page1RouteRef = createRouteRef();
|
||||
export const externalPageXRouteRef = createExternalRouteRef();
|
||||
export const externalPageXRouteRef = createExternalRouteRef({
|
||||
defaultTarget: 'pages.pageX',
|
||||
});
|
||||
export const pageXRouteRef = createRouteRef();
|
||||
// const page2RouteRef = createSubRouteRef({
|
||||
// id: 'page2',
|
||||
|
||||
@@ -46,7 +46,7 @@ const treeResponseFactory = DefaultReadTreeResponseFactory.create({
|
||||
|
||||
const mockCredentialsProvider = {
|
||||
getCredentials: jest.fn().mockResolvedValue({ headers: {} }),
|
||||
} as unknown as GithubCredentialsProvider;
|
||||
} satisfies GithubCredentialsProvider;
|
||||
|
||||
const githubProcessor = new GithubUrlReader(
|
||||
new GithubIntegration(
|
||||
@@ -105,7 +105,7 @@ describe('GithubUrlReader', () => {
|
||||
otherheader: 'something',
|
||||
};
|
||||
|
||||
(mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({
|
||||
mockCredentialsProvider.getCredentials.mockResolvedValue({
|
||||
headers: mockHeaders,
|
||||
});
|
||||
|
||||
@@ -146,7 +146,7 @@ describe('GithubUrlReader', () => {
|
||||
otherheader: 'something',
|
||||
};
|
||||
|
||||
(mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({
|
||||
mockCredentialsProvider.getCredentials.mockResolvedValue({
|
||||
headers: mockHeaders,
|
||||
});
|
||||
|
||||
@@ -182,7 +182,7 @@ describe('GithubUrlReader', () => {
|
||||
otherheader: 'something',
|
||||
};
|
||||
|
||||
(mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({
|
||||
mockCredentialsProvider.getCredentials.mockResolvedValue({
|
||||
headers: mockHeaders,
|
||||
});
|
||||
|
||||
@@ -241,7 +241,7 @@ describe('GithubUrlReader', () => {
|
||||
});
|
||||
|
||||
it('should return etag and last-modified from the response', async () => {
|
||||
(mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({
|
||||
mockCredentialsProvider.getCredentials.mockResolvedValue({
|
||||
headers: {
|
||||
Authorization: 'bearer blah',
|
||||
},
|
||||
@@ -271,6 +271,33 @@ describe('GithubUrlReader', () => {
|
||||
expect(response.etag).toBe('foo');
|
||||
expect(response.lastModifiedAt).toEqual(new Date('2021-01-01T00:00:00Z'));
|
||||
});
|
||||
|
||||
it('should override the token if its provided', async () => {
|
||||
expect.assertions(1);
|
||||
|
||||
mockCredentialsProvider.getCredentials.mockResolvedValue({
|
||||
headers: {
|
||||
Authorization: 'bearer blah',
|
||||
},
|
||||
});
|
||||
|
||||
worker.use(
|
||||
rest.get(
|
||||
'https://ghe.github.com/api/v3/repos/backstage/mock/tree/contents/',
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('authorization')).toBe(
|
||||
'Bearer overridentoken',
|
||||
);
|
||||
return res(ctx.status(200));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await gheProcessor.readUrl(
|
||||
'https://github.com/backstage/mock/tree/blob/main',
|
||||
{ token: 'overridentoken' },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
@@ -431,7 +458,7 @@ describe('GithubUrlReader', () => {
|
||||
otherheader: 'something',
|
||||
};
|
||||
|
||||
(mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({
|
||||
mockCredentialsProvider.getCredentials.mockResolvedValue({
|
||||
headers: mockHeaders,
|
||||
});
|
||||
|
||||
@@ -463,6 +490,44 @@ describe('GithubUrlReader', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should override the token when provided', async () => {
|
||||
expect.assertions(1);
|
||||
|
||||
const mockHeaders = {
|
||||
Authorization: 'bearer blah',
|
||||
};
|
||||
|
||||
mockCredentialsProvider.getCredentials.mockResolvedValue({
|
||||
headers: mockHeaders,
|
||||
});
|
||||
|
||||
worker.use(
|
||||
rest.get(
|
||||
'https://ghe.github.com/api/v3/repos/backstage/mock/tarball/etag123abc',
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('authorization')).toBe(
|
||||
'Bearer overridentoken',
|
||||
);
|
||||
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.set('Content-Type', 'application/x-gzip'),
|
||||
ctx.set(
|
||||
'content-disposition',
|
||||
'attachment; filename=backstage-mock-etag123.tar.gz',
|
||||
),
|
||||
ctx.body(repoBuffer),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await gheProcessor.readTree(
|
||||
'https://ghe.github.com/backstage/mock/tree/main',
|
||||
{ token: 'overridentoken' },
|
||||
);
|
||||
});
|
||||
|
||||
it('includes the subdomain in the github url', async () => {
|
||||
const response = await gheProcessor.readTree(
|
||||
'https://ghe.github.com/backstage/mock/tree/main/docs',
|
||||
@@ -905,6 +970,34 @@ describe('GithubUrlReader', () => {
|
||||
await runTests(gheProcessor, 'https://ghe.github.com');
|
||||
});
|
||||
|
||||
it('passes through a token for the search request', async () => {
|
||||
expect.assertions(1);
|
||||
|
||||
worker.use(
|
||||
rest.get(
|
||||
'https://ghe.github.com/api/v3/repos/backstage/mock/git/trees/etag123abc',
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('authorization')).toBe(
|
||||
'Bearer overridentoken',
|
||||
);
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.set('Content-Type', 'application/json'),
|
||||
ctx.json({
|
||||
truncated: true,
|
||||
tree: [],
|
||||
} as Partial<GhTreeResponse>),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await gheProcessor.search(
|
||||
`https://ghe.github.com/backstage/mock/tree/main/**/*`,
|
||||
{ token: 'overridentoken' },
|
||||
);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('succeeds on ghe when going via readTree', async () => {
|
||||
worker.use(
|
||||
|
||||
@@ -92,13 +92,31 @@ export class GithubUrlReader implements UrlReader {
|
||||
return response.buffer();
|
||||
}
|
||||
|
||||
private getCredentials = async (
|
||||
url: string,
|
||||
options?: { token?: string },
|
||||
): Promise<GithubCredentials> => {
|
||||
if (options?.token) {
|
||||
return {
|
||||
headers: {
|
||||
Authorization: `Bearer ${options.token}`,
|
||||
},
|
||||
type: 'token',
|
||||
token: options.token,
|
||||
};
|
||||
}
|
||||
|
||||
return await this.deps.credentialsProvider.getCredentials({
|
||||
url,
|
||||
});
|
||||
};
|
||||
|
||||
async readUrl(
|
||||
url: string,
|
||||
options?: ReadUrlOptions,
|
||||
): Promise<ReadUrlResponse> {
|
||||
const credentials = await this.deps.credentialsProvider.getCredentials({
|
||||
url,
|
||||
});
|
||||
const credentials = await this.getCredentials(url, options);
|
||||
|
||||
const ghUrl = getGithubFileFetchUrl(
|
||||
url,
|
||||
this.integration.config,
|
||||
@@ -141,9 +159,7 @@ export class GithubUrlReader implements UrlReader {
|
||||
}
|
||||
|
||||
const { filepath } = parseGitUrl(url);
|
||||
const { headers } = await this.deps.credentialsProvider.getCredentials({
|
||||
url,
|
||||
});
|
||||
const { headers } = await this.getCredentials(url, options);
|
||||
|
||||
return this.doReadTree(
|
||||
repoDetails.repo.archive_url,
|
||||
@@ -169,9 +185,7 @@ export class GithubUrlReader implements UrlReader {
|
||||
}
|
||||
|
||||
const { filepath } = parseGitUrl(url);
|
||||
const { headers } = await this.deps.credentialsProvider.getCredentials({
|
||||
url,
|
||||
});
|
||||
const { headers } = await this.getCredentials(url, options);
|
||||
|
||||
const files = await this.doSearch(
|
||||
url,
|
||||
@@ -333,10 +347,7 @@ export class GithubUrlReader implements UrlReader {
|
||||
// GitHub returns a 403 response with a couple of headers indicating rate
|
||||
// limit status. See more in the GitHub docs:
|
||||
// https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting
|
||||
if (
|
||||
response.status === 403 &&
|
||||
response.headers.get('X-RateLimit-Remaining') === '0'
|
||||
) {
|
||||
if (this.integration.parseRateLimitInfo(response).isRateLimited) {
|
||||
message += ' (rate limit exceeded)';
|
||||
}
|
||||
|
||||
|
||||
@@ -484,6 +484,37 @@ Please add '${mockDir.resolve(
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "alpha manifest preferred but skipped because the `alpha` sub-directory doesn't exist",
|
||||
preferAlpha: true,
|
||||
fileSystem: {
|
||||
backstageRoot: {
|
||||
'dist-dynamic': {
|
||||
'test-backend-plugin': {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'test-backend-plugin-dynamic',
|
||||
version: '0.0.0',
|
||||
main: 'dist/index.cjs.js',
|
||||
backstage: { role: 'backend-plugin' },
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedPluginPackages: [
|
||||
{
|
||||
location: url.pathToFileURL(
|
||||
mockDir.resolve('backstageRoot/dist-dynamic/test-backend-plugin'),
|
||||
),
|
||||
manifest: {
|
||||
name: 'test-backend-plugin-dynamic',
|
||||
version: '0.0.0',
|
||||
main: 'dist/index.cjs.js',
|
||||
backstage: { role: 'backend-plugin' },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'invalid alpha package.json',
|
||||
preferAlpha: true,
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import { Config } from '@backstage/config';
|
||||
import { ScannedPluginPackage, ScannedPluginManifest } from './types';
|
||||
import * as fs from 'fs/promises';
|
||||
import { Stats, lstatSync } from 'fs';
|
||||
import { Stats, lstatSync, existsSync } from 'fs';
|
||||
import * as chokidar from 'chokidar';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
@@ -178,22 +178,24 @@ export class PluginScanner {
|
||||
if (platform === 'node') {
|
||||
if (this.preferAlpha) {
|
||||
const pluginHomeAlpha = path.resolve(pluginHome, 'alpha');
|
||||
if ((await fs.lstat(pluginHomeAlpha)).isDirectory()) {
|
||||
const backstage = scannedPlugin.manifest.backstage;
|
||||
try {
|
||||
scannedPlugin = await this.scanDir(pluginHomeAlpha);
|
||||
} catch (e) {
|
||||
this.logger.error(
|
||||
`failed to load dynamic plugin manifest from '${pluginHomeAlpha}'`,
|
||||
e,
|
||||
if (existsSync(pluginHomeAlpha)) {
|
||||
if ((await fs.lstat(pluginHomeAlpha)).isDirectory()) {
|
||||
const backstage = scannedPlugin.manifest.backstage;
|
||||
try {
|
||||
scannedPlugin = await this.scanDir(pluginHomeAlpha);
|
||||
} catch (e) {
|
||||
this.logger.error(
|
||||
`failed to load dynamic plugin manifest from '${pluginHomeAlpha}'`,
|
||||
e,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
scannedPlugin.manifest.backstage = backstage;
|
||||
} else {
|
||||
this.logger.warn(
|
||||
`skipping '${pluginHomeAlpha}' since it is not a directory`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
scannedPlugin.manifest.backstage = backstage;
|
||||
} else {
|
||||
this.logger.warn(
|
||||
`skipping '${pluginHomeAlpha}' since it is not a directory`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,6 +316,7 @@ export type ReadTreeOptions = {
|
||||
): boolean;
|
||||
etag?: string;
|
||||
signal?: AbortSignal;
|
||||
token?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
@@ -343,6 +344,7 @@ export type ReadUrlOptions = {
|
||||
etag?: string;
|
||||
lastModifiedAfter?: Date;
|
||||
signal?: AbortSignal;
|
||||
token?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
@@ -390,6 +392,7 @@ export interface SchedulerService extends PluginTaskScheduler {}
|
||||
export type SearchOptions = {
|
||||
etag?: string;
|
||||
signal?: AbortSignal;
|
||||
token?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
|
||||
@@ -92,6 +92,18 @@ export type ReadUrlOptions = {
|
||||
* Not all reader implementations may take this field into account.
|
||||
*/
|
||||
signal?: AbortSignal;
|
||||
|
||||
/**
|
||||
* An optional token to use for authentication when reading the resources.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* By default all URL Readers will use the integrations config which is supplied
|
||||
* when creating the Readers. Sometimes it might be desireable to use the already
|
||||
* created URLReaders but with a different token, maybe that's supplied by the user
|
||||
* at runtime.
|
||||
*/
|
||||
token?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -179,6 +191,18 @@ export type ReadTreeOptions = {
|
||||
* Not all reader implementations may take this field into account.
|
||||
*/
|
||||
signal?: AbortSignal;
|
||||
|
||||
/**
|
||||
* An optional token to use for authentication when reading the resources.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* By default all URL Readers will use the integrations config which is supplied
|
||||
* when creating the Readers. Sometimes it might be desireable to use the already
|
||||
* created URLReaders but with a different token, maybe that's supplied by the user
|
||||
* at runtime.
|
||||
*/
|
||||
token?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -281,6 +305,18 @@ export type SearchOptions = {
|
||||
* Not all reader implementations may take this field into account.
|
||||
*/
|
||||
signal?: AbortSignal;
|
||||
|
||||
/**
|
||||
* An optional token to use for authentication when reading the resources.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* By default all URL Readers will use the integrations config which is supplied
|
||||
* when creating the Readers. Sometimes it might be desireable to use the already
|
||||
* created URLReaders but with a different token, maybe that's supplied by the user
|
||||
* at runtime.
|
||||
*/
|
||||
token?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,6 +50,10 @@ export interface CatalogApi {
|
||||
request: GetEntityFacetsRequest,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<GetEntityFacetsResponse>;
|
||||
getLocationByEntity(
|
||||
entityRef: string | CompoundEntityRef,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<Location_2 | undefined>;
|
||||
getLocationById(
|
||||
id: string,
|
||||
options?: CatalogRequestOptions,
|
||||
@@ -120,6 +124,10 @@ export class CatalogClient implements CatalogApi {
|
||||
request: GetEntityFacetsRequest,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<GetEntityFacetsResponse>;
|
||||
getLocationByEntity(
|
||||
entityRef: CompoundEntityRef | string,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<Location_2 | undefined>;
|
||||
getLocationById(
|
||||
id: string,
|
||||
options?: CatalogRequestOptions,
|
||||
|
||||
@@ -617,6 +617,71 @@ describe('CatalogClient', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLocationByEntity', () => {
|
||||
const defaultResponse = {
|
||||
data: {
|
||||
kind: 'c',
|
||||
namespace: 'ns',
|
||||
name: 'n',
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
server.use(
|
||||
rest.get(`${mockBaseUrl}/locations/by-entity/c/ns/n`, (_, res, ctx) => {
|
||||
return res(ctx.json(defaultResponse));
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should locations from correct endpoint', async () => {
|
||||
const response = await client.getLocationByEntity(
|
||||
{ kind: 'c', namespace: 'ns', name: 'n' },
|
||||
{ token },
|
||||
);
|
||||
expect(response).toEqual(defaultResponse);
|
||||
});
|
||||
|
||||
it('forwards authorization token', async () => {
|
||||
expect.assertions(1);
|
||||
|
||||
server.use(
|
||||
rest.get(
|
||||
`${mockBaseUrl}/locations/by-entity/c/ns/n`,
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('authorization')).toBe(`Bearer ${token}`);
|
||||
return res(ctx.json(defaultResponse));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await client.getLocationByEntity(
|
||||
{ kind: 'c', namespace: 'ns', name: 'n' },
|
||||
{ token },
|
||||
);
|
||||
});
|
||||
|
||||
it('skips authorization header if token is omitted', async () => {
|
||||
expect.assertions(1);
|
||||
|
||||
server.use(
|
||||
rest.get(
|
||||
`${mockBaseUrl}/locations/by-entity/c/ns/n`,
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('authorization')).toBeNull();
|
||||
return res(ctx.json(defaultResponse));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await client.getLocationByEntity({
|
||||
kind: 'c',
|
||||
namespace: 'ns',
|
||||
name: 'n',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateEntity', () => {
|
||||
it('returns valid false when validation fails', async () => {
|
||||
server.use(
|
||||
|
||||
@@ -88,6 +88,21 @@ export class CatalogClient implements CatalogApi {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc CatalogApi.getLocationByEntity}
|
||||
*/
|
||||
async getLocationByEntity(
|
||||
entityRef: CompoundEntityRef | string,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<Location | undefined> {
|
||||
return await this.requestOptional(
|
||||
await this.apiClient.getLocationByEntity(
|
||||
{ path: parseEntityRef(entityRef) },
|
||||
options,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc CatalogApi.getEntities}
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { DiscoveryApi } from '../types/discovery';
|
||||
import { FetchApi } from '../types/fetch';
|
||||
import crossFetch from 'cross-fetch';
|
||||
@@ -461,6 +464,42 @@ export class DefaultApiClient {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a location for entity.
|
||||
* @param kind
|
||||
* @param namespace
|
||||
* @param name
|
||||
*/
|
||||
public async getLocationByEntity(
|
||||
// @ts-ignore
|
||||
request: {
|
||||
path: {
|
||||
kind: string;
|
||||
namespace: string;
|
||||
name: string;
|
||||
};
|
||||
},
|
||||
options?: RequestOptions,
|
||||
): Promise<TypedResponse<Location>> {
|
||||
const baseUrl = await this.discoveryApi.getBaseUrl(pluginId);
|
||||
|
||||
const uriTemplate = `/locations/by-entity/{kind}/{namespace}/{name}`;
|
||||
|
||||
const uri = parser.parse(uriTemplate).expand({
|
||||
kind: request.path.kind,
|
||||
namespace: request.path.namespace,
|
||||
name: request.path.name,
|
||||
});
|
||||
|
||||
return await this.fetchApi.fetch(`${baseUrl}${uri}`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(options?.token && { Authorization: `Bearer ${options?.token}` }),
|
||||
},
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all locations
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
export interface AnalyzeLocationEntityField {
|
||||
/**
|
||||
* A text to show to the user to inform about the choices made. Like, it could say \"Found a CODEOWNERS file that covers this target, so we suggest leaving this field empty; which would currently make it owned by X\" where X is taken from the codeowners file.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { Entity } from '../models/Entity.model';
|
||||
import { LocationSpec } from '../models/LocationSpec.model';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { AnalyzeLocationEntityField } from '../models/AnalyzeLocationEntityField.model';
|
||||
import { RecursivePartialEntity } from '../models/RecursivePartialEntity.model';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { LocationInput } from '../models/LocationInput.model';
|
||||
|
||||
export interface AnalyzeLocationRequest {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { AnalyzeLocationExistingEntity } from '../models/AnalyzeLocationExistingEntity.model';
|
||||
import { AnalyzeLocationGenerateEntity } from '../models/AnalyzeLocationGenerateEntity.model';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { Entity } from '../models/Entity.model';
|
||||
import { Location } from '../models/Location.model';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
export interface CreateLocationRequest {
|
||||
target: string;
|
||||
type: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { NullableEntity } from '../models/NullableEntity.model';
|
||||
|
||||
export interface EntitiesBatchResponse {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { EntitiesQueryResponsePageInfo } from '../models/EntitiesQueryResponsePageInfo.model';
|
||||
import { Entity } from '../models/Entity.model';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
export interface EntitiesQueryResponsePageInfo {
|
||||
/**
|
||||
* The cursor for the next batch of entities.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { EntityMeta } from '../models/EntityMeta.model';
|
||||
import { EntityRelation } from '../models/EntityRelation.model';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { EntityAncestryResponseItemsInner } from '../models/EntityAncestryResponseItemsInner.model';
|
||||
|
||||
export interface EntityAncestryResponse {
|
||||
|
||||
+4
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { Entity } from '../models/Entity.model';
|
||||
|
||||
export interface EntityAncestryResponseItemsInner {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
export interface EntityFacet {
|
||||
value: string;
|
||||
count: number;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { EntityFacet } from '../models/EntityFacet.model';
|
||||
|
||||
export interface EntityFacetsResponse {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* A link to external information that is related to the entity.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { EntityLink } from '../models/EntityLink.model';
|
||||
|
||||
/**
|
||||
@@ -21,6 +24,7 @@ import { EntityLink } from '../models/EntityLink.model';
|
||||
*/
|
||||
export interface EntityMeta {
|
||||
[key: string]: any;
|
||||
|
||||
/**
|
||||
* A list of external hyperlinks related to the entity.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* A relation of a specific type to another entity in the catalog.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
export interface ErrorError {
|
||||
name: string;
|
||||
message: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
export interface ErrorRequest {
|
||||
method: string;
|
||||
url: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
export interface ErrorResponse {
|
||||
statusCode: number;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
export interface GetEntitiesByRefsRequest {
|
||||
entityRefs: Array<string>;
|
||||
fields?: Array<string>;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { Location } from '../models/Location.model';
|
||||
|
||||
export interface GetLocations200ResponseInner {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* Entity location for a specific entity.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
export interface LocationInput {
|
||||
type: string;
|
||||
target: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* Holds the entity location information.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,12 +14,16 @@
|
||||
* 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';
|
||||
|
||||
export interface ModelError {
|
||||
[key: string]: any;
|
||||
|
||||
error: ErrorError;
|
||||
request?: ErrorRequest;
|
||||
response: ErrorResponse;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { EntityMeta } from '../models/EntityMeta.model';
|
||||
import { EntityRelation } from '../models/EntityRelation.model';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { RecursivePartialEntityMeta } from '../models/RecursivePartialEntityMeta.model';
|
||||
import { RecursivePartialEntityRelation } from '../models/RecursivePartialEntityRelation.model';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { EntityLink } from '../models/EntityLink.model';
|
||||
|
||||
export interface RecursivePartialEntityMeta {
|
||||
|
||||
+4
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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.
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { EntityLink } from '../models/EntityLink.model';
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user