Merge branch 'community-page' of github.com:suuus/backstage into community-page
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Corrected replacements for depreciated FilteredEntityLayout items
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-search-backend': minor
|
||||
---
|
||||
|
||||
**BREAKING**: The `authorization` property is no longer returned on search results when queried. Note: this will only result in a breaking change if you have custom code in your frontend that relies on the `authorization.resourceRef` property on documents.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-user-settings': patch
|
||||
---
|
||||
|
||||
Wired up the OneLogin provider to be visible in the Settings UI when configured correctly.
|
||||
|
||||
Previously it wasn't visible at all.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-auth-node': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed the deprecated `id` and `entity` fields from `BackstageSignInResult`.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-search-common': patch
|
||||
---
|
||||
|
||||
- Introduce `SearchDocument` type. This type contains the subset of `IndexableDocument` properties relevant to the frontend, and is intended to be used for documents returned to the frontend from the search API.
|
||||
- `SearchResultSet` is now a wrapper for documents of type `SearchDocument`, and is intended to be used in the frontend. This isn't a breaking change, since `IndexableDocument`s are valid `SearchDocument`s, so the old and new types are compatible.
|
||||
- Introduce `IndexableResultSet` type, which wraps `IndexableDocument` instances in the same way as `SearchResultSet`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-auth-backend': minor
|
||||
---
|
||||
|
||||
**BREAKING**: All sign-in resolvers must now return a `token` in their sign-in result. Returning an `id` is no longer supported.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/plugin-search-backend': patch
|
||||
'@backstage/plugin-search-backend-node': patch
|
||||
'@backstage/plugin-search-backend-module-elasticsearch': patch
|
||||
'@backstage/plugin-search-backend-module-pg': patch
|
||||
---
|
||||
|
||||
Use new `IndexableResultSet` type as return type of query method in `SearchEngine` implementation.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-search': patch
|
||||
---
|
||||
|
||||
Switch to `SearchDocument` type in `DefaultResultListItem` props
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-auth-backend': patch
|
||||
---
|
||||
|
||||
**DEPRECATION**: The `tokenIssuer` option for `OAuthAdapter` is no longer needed and has been deprecated.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Add support for string refs to the `EntityRefLinks` component
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-components': patch
|
||||
---
|
||||
|
||||
fix support config ref to use backstage/backstage
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes': patch
|
||||
---
|
||||
|
||||
Fix division by zero in currentToDeclaredResourceToPerc when pod resources weren't set
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-common': patch
|
||||
---
|
||||
|
||||
Updated the visibility of database connection fields in config to be secret
|
||||
@@ -106,3 +106,4 @@ _If you're using Backstage in your organization, please try to add your company
|
||||
| [Alliander](https://www.alliander.com/) | [@leon-vg](https://github.com/leon-vg), [@gieljl](https://github.com/gieljl), [@niekteg](https://github.com/niekteg) | Developer portal - software catalog, technical documentation, software templates, tech radar and exploration of used tools/services |
|
||||
| [VIA](https://www.via.com.br) | [@vagnerguedes](https://github.com/vagnerguedes) | Centralized Developer Experience portal - Software catalog and documentation platform, software templates, techdocs, scaffolding, self-service infrastructure |
|
||||
| [Surevine](https://www.surevine.com/) | [@DJDANNY123](https://github.com/djdanny123) | Developer portal for software catalog, discovery and a view of the technologies we are using across the organisation, we are looking to explore how we can enrich our entities in Backstage by integrating a software bill of materials. |
|
||||
| [Bonial International GmbH](https://www.bonial.com/) | [@pjungermann](https://github.com/pjungermann) | Centralized developer portal with software catalog, tech docs, templates, and more. |
|
||||
|
||||
@@ -74,7 +74,7 @@ spec:
|
||||
backend:
|
||||
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
|
||||
service:
|
||||
name: {{ include "frontend.serviceName" . }}
|
||||
name: {{ include "backend.serviceName" . }}
|
||||
port:
|
||||
number: 80
|
||||
{{- else -}}
|
||||
|
||||
@@ -364,7 +364,7 @@ spec:
|
||||
secretsKey: USER_OAUTH_TOKEN
|
||||
additionalScopes:
|
||||
github:
|
||||
- workflow:write
|
||||
- workflow
|
||||
allowedHosts:
|
||||
- github.com
|
||||
...
|
||||
|
||||
@@ -45,7 +45,7 @@ In terms of maturity and stability, the road ahead for Backstage is bright and b
|
||||
You can read more about Backstage’s roadmap [here](https://backstage.io/docs/overview/roadmap), but in terms of maturity and stability we would like to highlight the following:
|
||||
|
||||
- CNCF incubation: The Backstage project [recently moved from Sandbox to Incubation](https://www.cncf.io/blog/2022/03/15/backstage-project-joins-the-cncf-incubator/) phase within the [CNCF maturity levels](https://www.cncf.io/projects/#:~:text=Maturity%20levels,should%20be%20adopting%20different%20projects.).
|
||||
- Security auditing and strategy: With the CNCF’s support,, the project maintainers are working on a security audit initiative to make the platform more secure and will share more details soon. The audit is part of an overall security strategy being developed this quarter.
|
||||
- Security auditing and strategy: With the CNCF’s support, the project maintainers are working on a security audit initiative to make the platform more secure and will share more details soon. The audit is part of an overall security strategy being developed this quarter.
|
||||
- Paid plugins for advanced and enterprise use cases: Last year, [Spotify announced its work on paid plugins](https://backstage.spotify.com/blog/paid-plugins-announcement/) built on top of the Backstage open source platform. This is another example of the platform’s maturity and Spotify’s continued commitment to the project, with a focus on customers and use cases requiring enterprise-focused support and plugins developed and proven by Spotify.
|
||||
|
||||
# Some links to peruse
|
||||
|
||||
@@ -60,10 +60,10 @@
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.57",
|
||||
"@octokit/rest": "^18.5.3",
|
||||
"@roadiehq/backstage-plugin-buildkite": "^1.4.0",
|
||||
"@roadiehq/backstage-plugin-github-insights": "^1.6.0",
|
||||
"@roadiehq/backstage-plugin-github-pull-requests": "^1.5.0",
|
||||
"@roadiehq/backstage-plugin-travis-ci": "^1.4.0",
|
||||
"@roadiehq/backstage-plugin-buildkite": "^2.0.0",
|
||||
"@roadiehq/backstage-plugin-github-insights": "^2.0.0",
|
||||
"@roadiehq/backstage-plugin-github-pull-requests": "^2.0.0",
|
||||
"@roadiehq/backstage-plugin-travis-ci": "^2.0.0",
|
||||
"history": "^5.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^17.0.2",
|
||||
|
||||
@@ -180,7 +180,7 @@ export const cicdContent = (
|
||||
<EntityJenkinsContent />
|
||||
</EntitySwitch.Case>
|
||||
|
||||
<EntitySwitch.Case if={isBuildkiteAvailable as (e: Entity) => boolean}>
|
||||
<EntitySwitch.Case if={isBuildkiteAvailable}>
|
||||
<EntityBuildkiteContent />
|
||||
</EntitySwitch.Case>
|
||||
|
||||
@@ -192,7 +192,7 @@ export const cicdContent = (
|
||||
<EntityCloudbuildContent />
|
||||
</EntitySwitch.Case>
|
||||
|
||||
<EntitySwitch.Case if={isTravisciAvailable as (e: Entity) => boolean}>
|
||||
<EntitySwitch.Case if={isTravisciAvailable}>
|
||||
<EntityTravisCIContent />
|
||||
</EntitySwitch.Case>
|
||||
|
||||
@@ -327,9 +327,7 @@ const overviewContent = (
|
||||
{cicdCard}
|
||||
|
||||
<EntitySwitch>
|
||||
<EntitySwitch.Case
|
||||
if={isGithubInsightsAvailable as (e: Entity) => boolean}
|
||||
>
|
||||
<EntitySwitch.Case if={isGithubInsightsAvailable}>
|
||||
<Grid item md={6}>
|
||||
<EntityGithubInsightsLanguagesCard />
|
||||
<EntityGithubInsightsReleasesCard />
|
||||
@@ -349,9 +347,7 @@ const overviewContent = (
|
||||
</EntitySwitch>
|
||||
|
||||
<EntitySwitch>
|
||||
<EntitySwitch.Case
|
||||
if={isGithubPullRequestsAvailable as (e: Entity) => boolean}
|
||||
>
|
||||
<EntitySwitch.Case if={isGithubPullRequestsAvailable}>
|
||||
<Grid item sm={4}>
|
||||
<EntityGithubPullRequestsOverviewCard />
|
||||
</Grid>
|
||||
|
||||
Vendored
+4
-4
@@ -73,7 +73,7 @@ export interface Config {
|
||||
client: 'better-sqlite3' | 'sqlite3' | 'pg';
|
||||
/**
|
||||
* Base database connection string or Knex object
|
||||
* @secret
|
||||
* @visibility secret
|
||||
*/
|
||||
connection: string | object;
|
||||
/** Database name prefix override */
|
||||
@@ -109,7 +109,7 @@ export interface Config {
|
||||
client?: 'better-sqlite3' | 'sqlite3' | 'pg';
|
||||
/**
|
||||
* Database connection string or Knex object override
|
||||
* @secret
|
||||
* @visibility secret
|
||||
*/
|
||||
connection?: string | object;
|
||||
/**
|
||||
@@ -138,7 +138,7 @@ export interface Config {
|
||||
store: 'redis';
|
||||
/**
|
||||
* A redis connection string in the form `redis://user:pass@host:port`.
|
||||
* @secret
|
||||
* @visibility secret
|
||||
*/
|
||||
connection: string;
|
||||
}
|
||||
@@ -146,7 +146,7 @@ export interface Config {
|
||||
store: 'memcache';
|
||||
/**
|
||||
* A memcache connection string in the form `user:pass@host:port`.
|
||||
* @secret
|
||||
* @visibility secret
|
||||
*/
|
||||
connection: string;
|
||||
};
|
||||
|
||||
@@ -42,7 +42,7 @@ const DEFAULT_SUPPORT_CONFIG: SupportConfig = {
|
||||
{
|
||||
// TODO: Update to dedicated support page on backstage.io/docs
|
||||
title: 'Add `app.support` config key',
|
||||
url: 'https://github.com/andrewthauer/backstage/blob/master/app-config.yaml',
|
||||
url: 'https://github.com/backstage/backstage/blob/master/app-config.yaml',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -153,11 +153,11 @@ describe('ScmAuth', () => {
|
||||
githubAuth.getCredentials({
|
||||
url: 'http://example.com',
|
||||
additionalScope: {
|
||||
customScopes: { github: ['org:read', 'workflow:write'] },
|
||||
customScopes: { github: ['org:read', 'workflow'] },
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
token: 'repo read:org read:user org:read workflow:write',
|
||||
token: 'repo read:org read:user org:read workflow',
|
||||
});
|
||||
|
||||
const gitlabAuth = ScmAuth.forGitlab(mockAuthApi);
|
||||
|
||||
@@ -50,7 +50,6 @@ describe('oauth helpers', () => {
|
||||
email: 'foo@bar.com',
|
||||
},
|
||||
backstageIdentity: {
|
||||
id: 'a',
|
||||
token: 'a.b.c',
|
||||
identity: {
|
||||
type: 'user',
|
||||
@@ -110,7 +109,6 @@ describe('oauth helpers', () => {
|
||||
email: 'foo@bar.com',
|
||||
},
|
||||
backstageIdentity: {
|
||||
id: 'a',
|
||||
token: 'a.b.c',
|
||||
identity: {
|
||||
type: 'user',
|
||||
@@ -157,7 +155,6 @@ describe('oauth helpers', () => {
|
||||
displayName: "Adam l'Hôpital",
|
||||
},
|
||||
backstageIdentity: {
|
||||
id: 'a',
|
||||
token: 'a.b.c',
|
||||
identity: {
|
||||
type: 'user',
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import express from 'express';
|
||||
import { THOUSAND_DAYS_MS, TEN_MINUTES_MS, OAuthAdapter } from './OAuthAdapter';
|
||||
import { encodeState } from './helpers';
|
||||
import { OAuthHandlers, OAuthResponse, OAuthState } from './types';
|
||||
import { OAuthHandlers, OAuthState } from './types';
|
||||
|
||||
const mockResponseData = {
|
||||
providerInfo: {
|
||||
@@ -30,18 +30,11 @@ const mockResponseData = {
|
||||
email: 'foo@bar.com',
|
||||
},
|
||||
backstageIdentity: {
|
||||
id: 'foo',
|
||||
token:
|
||||
'eyblob.eyJzdWIiOiJqaW1teW1hcmt1bSIsImVudCI6WyJ1c2VyOmRlZmF1bHQvamltbXltYXJrdW0iXX0=.eyblob',
|
||||
'eyblob.eyJzdWIiOiJ1c2VyOmRlZmF1bHQvamltbXltYXJrdW0iLCJlbnQiOlsidXNlcjpkZWZhdWx0L2ppbW15bWFya3VtIl19.eyblob',
|
||||
},
|
||||
};
|
||||
|
||||
function mkTokenBody(payload: unknown): string {
|
||||
return Buffer.from(JSON.stringify(payload), 'utf8')
|
||||
.toString('base64')
|
||||
.replace(/=/g, '');
|
||||
}
|
||||
|
||||
describe('OAuthAdapter', () => {
|
||||
class MyAuthProvider implements OAuthHandlers {
|
||||
async start() {
|
||||
@@ -324,13 +317,11 @@ describe('OAuthAdapter', () => {
|
||||
expect(mockResponse.json).toHaveBeenCalledWith({
|
||||
...mockResponseData,
|
||||
backstageIdentity: {
|
||||
id: mockResponseData.backstageIdentity.id,
|
||||
token: mockResponseData.backstageIdentity.token,
|
||||
idToken: mockResponseData.backstageIdentity.token,
|
||||
identity: {
|
||||
ownershipEntityRefs: ['user:default/jimmymarkum'],
|
||||
type: 'user',
|
||||
userEntityRef: 'user:default/jimmymarkum',
|
||||
ownershipEntityRefs: ['user:default/jimmymarkum'],
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -356,95 +347,6 @@ describe('OAuthAdapter', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('correctly populates incomplete identities', async () => {
|
||||
const mockRefresh = jest.fn<
|
||||
Promise<{ response: OAuthResponse }>,
|
||||
[express.Request]
|
||||
>();
|
||||
|
||||
const oauthProvider = new OAuthAdapter(
|
||||
{
|
||||
refresh: mockRefresh,
|
||||
start: jest.fn(),
|
||||
handler: jest.fn(),
|
||||
} as OAuthHandlers,
|
||||
{
|
||||
...oAuthProviderOptions,
|
||||
tokenIssuer: {
|
||||
issueToken: async ({ claims }) => `a.${mkTokenBody(claims)}.a`,
|
||||
listPublicKeys: async () => ({ keys: [] }),
|
||||
},
|
||||
disableRefresh: false,
|
||||
isOriginAllowed: () => false,
|
||||
},
|
||||
);
|
||||
|
||||
const mockRequest = {
|
||||
header: () => 'XMLHttpRequest',
|
||||
cookies: {
|
||||
'test-provider-refresh-token': 'token',
|
||||
},
|
||||
query: {},
|
||||
} as unknown as express.Request;
|
||||
|
||||
const mockResponse = {
|
||||
json: jest.fn().mockReturnThis(),
|
||||
status: jest.fn().mockReturnThis(),
|
||||
} as unknown as express.Response;
|
||||
|
||||
// Without a token
|
||||
mockRefresh.mockResolvedValueOnce({
|
||||
response: {
|
||||
...mockResponseData,
|
||||
backstageIdentity: {
|
||||
id: 'foo',
|
||||
token: '',
|
||||
},
|
||||
},
|
||||
});
|
||||
await oauthProvider.refresh(mockRequest, mockResponse);
|
||||
expect(mockResponse.json).toHaveBeenCalledTimes(1);
|
||||
expect(mockResponse.json).toHaveBeenLastCalledWith({
|
||||
...mockResponseData,
|
||||
backstageIdentity: {
|
||||
id: 'foo',
|
||||
token: `a.${mkTokenBody({ sub: 'user:default/foo' })}.a`,
|
||||
idToken: `a.${mkTokenBody({ sub: 'user:default/foo' })}.a`,
|
||||
identity: {
|
||||
type: 'user',
|
||||
userEntityRef: 'user:default/foo',
|
||||
ownershipEntityRefs: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// With a token
|
||||
mockRefresh.mockResolvedValueOnce({
|
||||
response: {
|
||||
...mockResponseData,
|
||||
backstageIdentity: {
|
||||
id: 'foo',
|
||||
token: `z.${mkTokenBody({ sub: 'user:my-ns/foo' })}.z`,
|
||||
},
|
||||
},
|
||||
});
|
||||
await oauthProvider.refresh(mockRequest, mockResponse);
|
||||
expect(mockResponse.json).toHaveBeenCalledTimes(2);
|
||||
expect(mockResponse.json).toHaveBeenLastCalledWith({
|
||||
...mockResponseData,
|
||||
backstageIdentity: {
|
||||
id: 'foo',
|
||||
token: `z.${mkTokenBody({ sub: 'user:my-ns/foo' })}.z`,
|
||||
idToken: `z.${mkTokenBody({ sub: 'user:my-ns/foo' })}.z`,
|
||||
identity: {
|
||||
type: 'user',
|
||||
userEntityRef: 'user:my-ns/foo',
|
||||
ownershipEntityRefs: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('sets the correct cookie configuration using a callbackUrl', async () => {
|
||||
const config = {
|
||||
baseUrl: 'http://domain.org/auth',
|
||||
|
||||
@@ -17,11 +17,6 @@
|
||||
import express, { CookieOptions } from 'express';
|
||||
import crypto from 'crypto';
|
||||
import { URL } from 'url';
|
||||
import {
|
||||
DEFAULT_NAMESPACE,
|
||||
parseEntityRef,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import {
|
||||
BackstageIdentityResponse,
|
||||
BackstageSignInResult,
|
||||
@@ -58,7 +53,8 @@ export type Options = {
|
||||
cookieDomain: string;
|
||||
cookiePath: string;
|
||||
appOrigin: string;
|
||||
tokenIssuer: TokenIssuer;
|
||||
/** @deprecated This option is no longer needed */
|
||||
tokenIssuer?: TokenIssuer;
|
||||
isOriginAllowed: (origin: string) => boolean;
|
||||
callbackUrl: string;
|
||||
};
|
||||
@@ -263,22 +259,11 @@ export class OAuthAdapter implements AuthProviderRouteHandlers {
|
||||
if (!identity) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (identity.token) {
|
||||
return prepareBackstageIdentityResponse(identity);
|
||||
if (!identity.token) {
|
||||
throw new InputError(`Identity response must return a token`);
|
||||
}
|
||||
|
||||
const userEntityRef = stringifyEntityRef(
|
||||
parseEntityRef(identity.id, {
|
||||
defaultKind: 'user',
|
||||
defaultNamespace: DEFAULT_NAMESPACE,
|
||||
}),
|
||||
);
|
||||
const token = await this.options.tokenIssuer.issueToken({
|
||||
claims: { sub: userEntityRef },
|
||||
});
|
||||
|
||||
return prepareBackstageIdentityResponse({ ...identity, token });
|
||||
return prepareBackstageIdentityResponse(identity);
|
||||
}
|
||||
|
||||
private setNonceCookie = (res: express.Response, nonce: string) => {
|
||||
|
||||
@@ -123,9 +123,8 @@ describe('AwsAlbAuthProvider', () => {
|
||||
}),
|
||||
signInResolver: async () => {
|
||||
return {
|
||||
id: 'user.name',
|
||||
token:
|
||||
'eyblob.eyJzdWIiOiJqaW1teW1hcmt1bSIsImVudCI6WyJ1c2VyOmRlZmF1bHQvamltbXltYXJrdW0iXX0=.eyblob',
|
||||
'eyblob.eyJzdWIiOiJ1c2VyOmRlZmF1bHQvamltbXltYXJrdW0iLCJlbnQiOlsidXNlcjpkZWZhdWx0L2ppbW15bWFya3VtIl19.eyblob',
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -136,11 +135,8 @@ describe('AwsAlbAuthProvider', () => {
|
||||
|
||||
expect(mockResponse.json).toHaveBeenCalledWith({
|
||||
backstageIdentity: {
|
||||
id: 'user.name',
|
||||
token:
|
||||
'eyblob.eyJzdWIiOiJqaW1teW1hcmt1bSIsImVudCI6WyJ1c2VyOmRlZmF1bHQvamltbXltYXJrdW0iXX0=.eyblob',
|
||||
idToken:
|
||||
'eyblob.eyJzdWIiOiJqaW1teW1hcmt1bSIsImVudCI6WyJ1c2VyOmRlZmF1bHQvamltbXltYXJrdW0iXX0=.eyblob',
|
||||
'eyblob.eyJzdWIiOiJ1c2VyOmRlZmF1bHQvamltbXltYXJrdW0iLCJlbnQiOlsidXNlcjpkZWZhdWx0L2ppbW15bWFya3VtIl19.eyblob',
|
||||
identity: {
|
||||
ownershipEntityRefs: ['user:default/jimmymarkum'],
|
||||
type: 'user',
|
||||
|
||||
@@ -45,7 +45,7 @@ describe('GcpIapProvider', () => {
|
||||
const iapToken = { sub: 's', email: 'e@mail.com' };
|
||||
|
||||
authHandler.mockResolvedValueOnce({ email: 'e@mail.com' });
|
||||
signInResolver.mockResolvedValueOnce({ id: 'i', token: backstageToken });
|
||||
signInResolver.mockResolvedValueOnce({ token: backstageToken });
|
||||
tokenValidator.mockResolvedValueOnce(iapToken);
|
||||
|
||||
const app = express();
|
||||
@@ -61,8 +61,6 @@ describe('GcpIapProvider', () => {
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
backstageIdentity: {
|
||||
id: 'i',
|
||||
idToken: backstageToken,
|
||||
token: backstageToken,
|
||||
identity: {
|
||||
type: 'user',
|
||||
|
||||
@@ -43,7 +43,7 @@ import {
|
||||
|
||||
describe('Oauth2ProxyAuthProvider', () => {
|
||||
const mockToken =
|
||||
'eyblob.eyJzdWIiOiJqaW1teW1hcmt1bSIsImVudCI6WyJ1c2VyOmRlZmF1bHQvamltbXltYXJrdW0iXX0=.eyblob';
|
||||
'eyblob.eyJzdWIiOiJ1c2VyOmRlZmF1bHQvamltbXltYXJrdW0iLCJlbnQiOlsidXNlcjpkZWZhdWx0L2ppbW15bWFya3VtIl19.eyblob';
|
||||
|
||||
let provider: Oauth2ProxyAuthProvider<any>;
|
||||
let logger: jest.Mocked<Logger>;
|
||||
@@ -122,7 +122,6 @@ describe('Oauth2ProxyAuthProvider', () => {
|
||||
profile: {},
|
||||
});
|
||||
signInResolver.mockResolvedValue({
|
||||
id: 'some-id',
|
||||
token: mockToken,
|
||||
});
|
||||
|
||||
@@ -142,7 +141,6 @@ describe('Oauth2ProxyAuthProvider', () => {
|
||||
const profile = { displayName: 'some value' };
|
||||
mockRequest.header.mockReturnValue(`Bearer token`);
|
||||
signInResolver.mockResolvedValue({
|
||||
id: 'some-id',
|
||||
token: mockToken,
|
||||
});
|
||||
authHandler.mockResolvedValue({ profile: profile });
|
||||
@@ -162,12 +160,10 @@ describe('Oauth2ProxyAuthProvider', () => {
|
||||
);
|
||||
expect(mockResponse.json).toHaveBeenCalledWith({
|
||||
backstageIdentity: {
|
||||
id: 'some-id',
|
||||
idToken: mockToken,
|
||||
identity: {
|
||||
ownershipEntityRefs: ['user:default/jimmymarkum'],
|
||||
type: 'user',
|
||||
userEntityRef: 'user:default/jimmymarkum',
|
||||
ownershipEntityRefs: ['user:default/jimmymarkum'],
|
||||
},
|
||||
token: mockToken,
|
||||
},
|
||||
@@ -186,7 +182,6 @@ describe('Oauth2ProxyAuthProvider', () => {
|
||||
profile: {},
|
||||
});
|
||||
signInResolver.mockResolvedValue({
|
||||
id: 'some-id',
|
||||
token: mockToken,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,13 +27,10 @@ describe('prepareBackstageIdentityResponse', () => {
|
||||
const token = mkToken({ sub: 'k:ns/n', ent: ['k:ns/o'] });
|
||||
expect(
|
||||
prepareBackstageIdentityResponse({
|
||||
id: 'x',
|
||||
token,
|
||||
}),
|
||||
).toEqual({
|
||||
id: 'x',
|
||||
token,
|
||||
idToken: token,
|
||||
identity: {
|
||||
type: 'user',
|
||||
userEntityRef: 'k:ns/n',
|
||||
@@ -41,67 +38,4 @@ describe('prepareBackstageIdentityResponse', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('populates incomplete identities', () => {
|
||||
expect(
|
||||
prepareBackstageIdentityResponse({
|
||||
id: 'x',
|
||||
token: mkToken({ sub: 'n' }),
|
||||
}),
|
||||
).toEqual({
|
||||
id: 'x',
|
||||
token: expect.any(String),
|
||||
idToken: expect.any(String),
|
||||
identity: {
|
||||
type: 'user',
|
||||
userEntityRef: 'user:default/n',
|
||||
ownershipEntityRefs: [],
|
||||
},
|
||||
});
|
||||
expect(
|
||||
prepareBackstageIdentityResponse({
|
||||
id: 'x',
|
||||
token: mkToken({ sub: 'k:n' }),
|
||||
}),
|
||||
).toEqual({
|
||||
id: 'x',
|
||||
token: expect.any(String),
|
||||
idToken: expect.any(String),
|
||||
identity: {
|
||||
type: 'user',
|
||||
userEntityRef: 'k:default/n',
|
||||
ownershipEntityRefs: [],
|
||||
},
|
||||
});
|
||||
expect(
|
||||
prepareBackstageIdentityResponse({
|
||||
id: 'x',
|
||||
token: mkToken({ sub: 'ns/n' }),
|
||||
}),
|
||||
).toEqual({
|
||||
id: 'x',
|
||||
token: expect.any(String),
|
||||
idToken: expect.any(String),
|
||||
identity: {
|
||||
type: 'user',
|
||||
userEntityRef: 'user:ns/n',
|
||||
ownershipEntityRefs: [],
|
||||
},
|
||||
});
|
||||
expect(
|
||||
prepareBackstageIdentityResponse({
|
||||
id: 'x',
|
||||
token: mkToken({ sub: 'n', ent: ['k:ns/o'] }),
|
||||
}),
|
||||
).toEqual({
|
||||
id: 'x',
|
||||
token: expect.any(String),
|
||||
idToken: expect.any(String),
|
||||
identity: {
|
||||
type: 'user',
|
||||
userEntityRef: 'user:default/n',
|
||||
ownershipEntityRefs: ['k:ns/o'],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,11 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
DEFAULT_NAMESPACE,
|
||||
parseEntityRef,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import {
|
||||
BackstageIdentityResponse,
|
||||
BackstageSignInResult,
|
||||
@@ -41,21 +36,11 @@ export function prepareBackstageIdentityResponse(
|
||||
): BackstageIdentityResponse {
|
||||
const { sub, ent } = parseJwtPayload(result.token);
|
||||
|
||||
const userEntityRef = stringifyEntityRef(
|
||||
parseEntityRef(sub, {
|
||||
defaultKind: 'user',
|
||||
defaultNamespace: DEFAULT_NAMESPACE,
|
||||
}),
|
||||
);
|
||||
return {
|
||||
...{
|
||||
// TODO: idToken is for backwards compatibility and can be removed in the future
|
||||
idToken: result.token,
|
||||
...result,
|
||||
},
|
||||
...result,
|
||||
identity: {
|
||||
type: 'user',
|
||||
userEntityRef,
|
||||
userEntityRef: sub,
|
||||
ownershipEntityRefs: ent ?? [],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
|
||||
// @public
|
||||
@@ -13,10 +12,6 @@ export interface BackstageIdentityResponse extends BackstageSignInResult {
|
||||
|
||||
// @public
|
||||
export interface BackstageSignInResult {
|
||||
// @deprecated
|
||||
entity?: Entity;
|
||||
// @deprecated
|
||||
id: string;
|
||||
token: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.13.1",
|
||||
"@backstage/catalog-model": "^1.0.0",
|
||||
"@backstage/config": "^1.0.0",
|
||||
"@backstage/errors": "^1.0.0",
|
||||
"jose": "^1.27.1",
|
||||
|
||||
@@ -135,12 +135,11 @@ describe('IdentityClient', () => {
|
||||
const token = await factory.issueToken({ claims: { sub: 'foo' } });
|
||||
const response = await client.authenticate(token);
|
||||
expect(response).toEqual({
|
||||
id: 'foo',
|
||||
token: token,
|
||||
identity: {
|
||||
ownershipEntityRefs: [],
|
||||
type: 'user',
|
||||
userEntityRef: 'foo',
|
||||
ownershipEntityRefs: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -202,12 +201,11 @@ describe('IdentityClient', () => {
|
||||
const token = await factory.issueToken({ claims: { sub: 'foo' } });
|
||||
const response = await client.authenticate(token);
|
||||
expect(response).toEqual({
|
||||
id: 'foo',
|
||||
token: token,
|
||||
identity: {
|
||||
ownershipEntityRefs: [],
|
||||
type: 'user',
|
||||
userEntityRef: 'foo',
|
||||
ownershipEntityRefs: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -87,7 +87,6 @@ export class IdentityClient {
|
||||
}
|
||||
|
||||
const user: BackstageIdentityResponse = {
|
||||
id: decoded.sub,
|
||||
token,
|
||||
identity: {
|
||||
type: 'user',
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
|
||||
/**
|
||||
* A representation of a successful Backstage sign-in.
|
||||
*
|
||||
@@ -25,25 +23,6 @@ import { Entity } from '@backstage/catalog-model';
|
||||
* @public
|
||||
*/
|
||||
export interface BackstageSignInResult {
|
||||
/**
|
||||
* An opaque ID that uniquely identifies the user within Backstage.
|
||||
*
|
||||
* This is typically the same as the user entity `metadata.name`.
|
||||
*
|
||||
* @deprecated Use the `identity` field instead
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The entity that the user is represented by within Backstage.
|
||||
*
|
||||
* This entity may or may not exist within the Catalog, and it can be used
|
||||
* to read and store additional metadata about the user.
|
||||
*
|
||||
* @deprecated Use the `identity` field instead.
|
||||
*/
|
||||
entity?: Entity;
|
||||
|
||||
/**
|
||||
* The token used to authenticate the user within Backstage.
|
||||
*/
|
||||
|
||||
@@ -261,15 +261,11 @@ export type EntityRefLinkProps = {
|
||||
} & Omit<LinkProps, 'to'>;
|
||||
|
||||
// @public
|
||||
export const EntityRefLinks: ({
|
||||
entityRefs,
|
||||
defaultKind,
|
||||
...linkProps
|
||||
}: EntityRefLinksProps) => JSX.Element;
|
||||
export function EntityRefLinks(props: EntityRefLinksProps): JSX.Element;
|
||||
|
||||
// @public
|
||||
export type EntityRefLinksProps = {
|
||||
entityRefs: (Entity | CompoundEntityRef)[];
|
||||
entityRefs: (string | Entity | CompoundEntityRef)[];
|
||||
defaultKind?: string;
|
||||
} & Omit<LinkProps, 'to'>;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import { LinkProps } from '@backstage/core-components';
|
||||
* @public
|
||||
*/
|
||||
export type EntityRefLinksProps = {
|
||||
entityRefs: (Entity | CompoundEntityRef)[];
|
||||
entityRefs: (string | Entity | CompoundEntityRef)[];
|
||||
defaultKind?: string;
|
||||
} & Omit<LinkProps, 'to'>;
|
||||
|
||||
@@ -34,17 +34,20 @@ export type EntityRefLinksProps = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const EntityRefLinks = ({
|
||||
entityRefs,
|
||||
defaultKind,
|
||||
...linkProps
|
||||
}: EntityRefLinksProps) => (
|
||||
<>
|
||||
{entityRefs.map((r, i) => (
|
||||
<React.Fragment key={i}>
|
||||
{i > 0 && ', '}
|
||||
<EntityRefLink {...linkProps} entityRef={r} defaultKind={defaultKind} />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
export function EntityRefLinks(props: EntityRefLinksProps) {
|
||||
const { entityRefs, defaultKind, ...linkProps } = props;
|
||||
return (
|
||||
<>
|
||||
{entityRefs.map((r, i) => (
|
||||
<React.Fragment key={i}>
|
||||
{i > 0 && ', '}
|
||||
<EntityRefLink
|
||||
{...linkProps}
|
||||
entityRef={r}
|
||||
defaultKind={defaultKind}
|
||||
/>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { CatalogFilterLayout } from '@backstage/plugin-catalog-react';
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use `FilteredCatalogLayout` from `@backstage/plugin-catalog-react` instead.
|
||||
* @deprecated Use `CatalogFilterLayout` from `@backstage/plugin-catalog-react` instead.
|
||||
*/
|
||||
export const FilteredEntityLayout = CatalogFilterLayout as (props: {
|
||||
children: React.ReactNode;
|
||||
@@ -26,12 +26,12 @@ export const FilteredEntityLayout = CatalogFilterLayout as (props: {
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use `FilteredCatalogLayout.Filters` from `@backstage/plugin-catalog-react` instead.
|
||||
* @deprecated Use `CatalogFilterLayout.Filters` from `@backstage/plugin-catalog-react` instead.
|
||||
*/
|
||||
export const FilterContainer = CatalogFilterLayout.Filters;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use `FilteredCatalogLayout.Content` from `@backstage/plugin-catalog-react` instead.
|
||||
* @deprecated Use `CatalogFilterLayout.Content` from `@backstage/plugin-catalog-react` instead.
|
||||
*/
|
||||
export const EntityListContainer = CatalogFilterLayout.Content;
|
||||
|
||||
@@ -118,6 +118,8 @@ export const currentToDeclaredResourceToPerc = (
|
||||
current: number | string,
|
||||
resource: number | string,
|
||||
): string => {
|
||||
if (Number(resource) === 0) return `0%`;
|
||||
|
||||
if (typeof current === 'number' && typeof resource === 'number') {
|
||||
return `${Math.round((current / resource) * 100)}%`;
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ describe('RepoUrlPicker', () => {
|
||||
'ui:options': {
|
||||
requestUserCredentials: {
|
||||
secretsKey: 'testKey',
|
||||
additionalScopes: { github: ['workflow:write'] },
|
||||
additionalScopes: { github: ['workflow'] },
|
||||
},
|
||||
},
|
||||
}}
|
||||
@@ -164,7 +164,7 @@ describe('RepoUrlPicker', () => {
|
||||
additionalScope: {
|
||||
repoWrite: true,
|
||||
customScopes: {
|
||||
github: ['workflow:write'],
|
||||
github: ['workflow'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -10,10 +10,10 @@ import { Client } from '@elastic/elasticsearch';
|
||||
import { Config } from '@backstage/config';
|
||||
import type { ConnectionOptions } from 'tls';
|
||||
import { IndexableDocument } from '@backstage/plugin-search-common';
|
||||
import { IndexableResultSet } from '@backstage/plugin-search-common';
|
||||
import { Logger } from 'winston';
|
||||
import { SearchEngine } from '@backstage/plugin-search-common';
|
||||
import { SearchQuery } from '@backstage/plugin-search-common';
|
||||
import { SearchResultSet } from '@backstage/plugin-search-common';
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ElasticSearchClientOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/plugin-search-backend-module-elasticsearch" does not have an export "ElasticSearchEngine"
|
||||
@@ -119,7 +119,7 @@ export class ElasticSearchSearchEngine implements SearchEngine {
|
||||
getIndexer(type: string): Promise<ElasticSearchSearchEngineIndexer>;
|
||||
newClient<T>(create: (options: ElasticSearchClientOptions) => T): T;
|
||||
// (undocumented)
|
||||
query(query: SearchQuery): Promise<SearchResultSet>;
|
||||
query(query: SearchQuery): Promise<IndexableResultSet>;
|
||||
// Warning: (ae-forgotten-export) The symbol "ElasticSearchQueryTranslator" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
|
||||
+2
-2
@@ -21,9 +21,9 @@ import {
|
||||
import { Config } from '@backstage/config';
|
||||
import {
|
||||
IndexableDocument,
|
||||
IndexableResultSet,
|
||||
SearchEngine,
|
||||
SearchQuery,
|
||||
SearchResultSet,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { Client } from '@elastic/elasticsearch';
|
||||
import esb from 'elastic-builder';
|
||||
@@ -192,7 +192,7 @@ export class ElasticSearchSearchEngine implements SearchEngine {
|
||||
return indexer;
|
||||
}
|
||||
|
||||
async query(query: SearchQuery): Promise<SearchResultSet> {
|
||||
async query(query: SearchQuery): Promise<IndexableResultSet> {
|
||||
const { elasticSearchQuery, documentTypes, pageSize } =
|
||||
this.translator(query);
|
||||
const queryIndices = documentTypes
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
```ts
|
||||
import { BatchSearchEngineIndexer } from '@backstage/plugin-search-backend-node';
|
||||
import { IndexableDocument } from '@backstage/plugin-search-common';
|
||||
import { IndexableResultSet } from '@backstage/plugin-search-common';
|
||||
import { Knex } from 'knex';
|
||||
import { PluginDatabaseManager } from '@backstage/backend-common';
|
||||
import { SearchEngine } from '@backstage/plugin-search-backend-node';
|
||||
import { SearchQuery } from '@backstage/plugin-search-common';
|
||||
import { SearchResultSet } from '@backstage/plugin-search-common';
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ConcretePgSearchQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
@@ -88,7 +88,7 @@ export class PgSearchEngine implements SearchEngine {
|
||||
// (undocumented)
|
||||
getIndexer(type: string): Promise<PgSearchEngineIndexer>;
|
||||
// (undocumented)
|
||||
query(query: SearchQuery): Promise<SearchResultSet>;
|
||||
query(query: SearchQuery): Promise<IndexableResultSet>;
|
||||
// (undocumented)
|
||||
setTranslator(
|
||||
translator: (query: SearchQuery) => ConcretePgSearchQuery,
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
import { PluginDatabaseManager } from '@backstage/backend-common';
|
||||
import { SearchEngine } from '@backstage/plugin-search-backend-node';
|
||||
import { SearchQuery, SearchResultSet } from '@backstage/plugin-search-common';
|
||||
import {
|
||||
SearchQuery,
|
||||
IndexableResultSet,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { PgSearchEngineIndexer } from './PgSearchEngineIndexer';
|
||||
import {
|
||||
DatabaseDocumentStore,
|
||||
@@ -81,7 +84,7 @@ export class PgSearchEngine implements SearchEngine {
|
||||
});
|
||||
}
|
||||
|
||||
async query(query: SearchQuery): Promise<SearchResultSet> {
|
||||
async query(query: SearchQuery): Promise<IndexableResultSet> {
|
||||
const { pgQuery, pageSize } = this.translator(query);
|
||||
|
||||
const rows = await this.databaseStore.transaction(async tx =>
|
||||
|
||||
@@ -9,13 +9,13 @@ import { DocumentCollatorFactory } from '@backstage/plugin-search-common';
|
||||
import { DocumentDecoratorFactory } from '@backstage/plugin-search-common';
|
||||
import { DocumentTypeInfo } from '@backstage/plugin-search-common';
|
||||
import { IndexableDocument } from '@backstage/plugin-search-common';
|
||||
import { IndexableResultSet } from '@backstage/plugin-search-common';
|
||||
import { Logger } from 'winston';
|
||||
import { default as lunr_2 } from 'lunr';
|
||||
import { QueryTranslator } from '@backstage/plugin-search-common';
|
||||
import { Readable } from 'stream';
|
||||
import { SearchEngine } from '@backstage/plugin-search-common';
|
||||
import { SearchQuery } from '@backstage/plugin-search-common';
|
||||
import { SearchResultSet } from '@backstage/plugin-search-common';
|
||||
import { Transform } from 'stream';
|
||||
import { Writable } from 'stream';
|
||||
|
||||
@@ -87,7 +87,7 @@ export class LunrSearchEngine implements SearchEngine {
|
||||
// (undocumented)
|
||||
protected lunrIndices: Record<string, lunr_2.Index>;
|
||||
// (undocumented)
|
||||
query(query: SearchQuery): Promise<SearchResultSet>;
|
||||
query(query: SearchQuery): Promise<IndexableResultSet>;
|
||||
// (undocumented)
|
||||
setTranslator(translator: LunrQueryTranslator): void;
|
||||
// (undocumented)
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
import {
|
||||
IndexableDocument,
|
||||
IndexableResultSet,
|
||||
SearchQuery,
|
||||
SearchResultSet,
|
||||
QueryTranslator,
|
||||
SearchEngine,
|
||||
} from '@backstage/plugin-search-common';
|
||||
@@ -147,7 +147,7 @@ export class LunrSearchEngine implements SearchEngine {
|
||||
return indexer;
|
||||
}
|
||||
|
||||
async query(query: SearchQuery): Promise<SearchResultSet> {
|
||||
async query(query: SearchQuery): Promise<IndexableResultSet> {
|
||||
const { lunrQueryBuilder, documentTypes, pageSize } = this.translator(
|
||||
query,
|
||||
) as ConcreteLunrQuery;
|
||||
@@ -196,8 +196,8 @@ export class LunrSearchEngine implements SearchEngine {
|
||||
? encodePageCursor({ page: page - 1 })
|
||||
: undefined;
|
||||
|
||||
// Translate results into SearchResultSet
|
||||
const realResultSet: SearchResultSet = {
|
||||
// Translate results into IndexableResultSet
|
||||
const realResultSet: IndexableResultSet = {
|
||||
results: results.slice(offset, offset + pageSize).map(d => {
|
||||
return { type: d.type, document: this.docStore[d.result.ref] };
|
||||
}),
|
||||
|
||||
@@ -25,12 +25,12 @@ import {
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
DocumentTypeInfo,
|
||||
IndexableResult,
|
||||
IndexableResultSet,
|
||||
QueryRequestOptions,
|
||||
QueryTranslator,
|
||||
SearchEngine,
|
||||
SearchQuery,
|
||||
SearchResult,
|
||||
SearchResultSet,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { Config } from '@backstage/config';
|
||||
import { InputError } from '@backstage/errors';
|
||||
@@ -85,7 +85,7 @@ export class AuthorizedSearchEngine implements SearchEngine {
|
||||
async query(
|
||||
query: SearchQuery,
|
||||
options: QueryRequestOptions,
|
||||
): Promise<SearchResultSet> {
|
||||
): Promise<IndexableResultSet> {
|
||||
const queryStartTime = Date.now();
|
||||
|
||||
const authorizer = new DataLoader(
|
||||
@@ -144,7 +144,7 @@ export class AuthorizedSearchEngine implements SearchEngine {
|
||||
const { page } = decodePageCursor(query.pageCursor);
|
||||
const targetResults = (page + 1) * this.pageSize;
|
||||
|
||||
let filteredResults: SearchResult[] = [];
|
||||
let filteredResults: IndexableResult[] = [];
|
||||
let nextPageCursor: string | undefined;
|
||||
let latencyBudgetExhausted = false;
|
||||
|
||||
@@ -183,7 +183,7 @@ export class AuthorizedSearchEngine implements SearchEngine {
|
||||
}
|
||||
|
||||
private async filterResults(
|
||||
results: SearchResult[],
|
||||
results: IndexableResult[],
|
||||
typeDecisions: Record<string, AuthorizeDecision>,
|
||||
authorizer: DataLoader<AuthorizeQuery, AuthorizeDecision>,
|
||||
) {
|
||||
|
||||
@@ -19,7 +19,6 @@ import { ConfigReader } from '@backstage/config';
|
||||
import { PermissionAuthorizer } from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
IndexBuilder,
|
||||
LunrSearchEngine,
|
||||
SearchEngine,
|
||||
} from '@backstage/plugin-search-backend-node';
|
||||
import express from 'express';
|
||||
@@ -39,8 +38,19 @@ describe('createRouter', () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
const logger = getVoidLogger();
|
||||
const searchEngine = new LunrSearchEngine({ logger });
|
||||
const indexBuilder = new IndexBuilder({ logger, searchEngine });
|
||||
mockSearchEngine = {
|
||||
getIndexer: jest.fn(),
|
||||
setTranslator: jest.fn(),
|
||||
query: jest.fn().mockResolvedValue({
|
||||
results: [],
|
||||
nextPageCursor: '',
|
||||
previousPageCursor: '',
|
||||
}),
|
||||
};
|
||||
const indexBuilder = new IndexBuilder({
|
||||
logger,
|
||||
searchEngine: mockSearchEngine,
|
||||
});
|
||||
|
||||
const router = await createRouter({
|
||||
engine: indexBuilder.getSearchEngine(),
|
||||
@@ -56,7 +66,7 @@ describe('createRouter', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('GET /query', () => {
|
||||
@@ -101,6 +111,42 @@ describe('createRouter', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('removes backend-only properties from search documents', async () => {
|
||||
mockSearchEngine.query.mockResolvedValue({
|
||||
results: [
|
||||
{
|
||||
type: 'software-catalog',
|
||||
document: {
|
||||
text: 'foo',
|
||||
title: 'bar baz',
|
||||
location: '/catalog/default/component/example',
|
||||
authorization: {
|
||||
resourceRef: 'component:default/example',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
nextPageCursor: '',
|
||||
previousPageCursor: '',
|
||||
});
|
||||
|
||||
const response = await request(app).get('/query');
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toMatchObject({
|
||||
results: [
|
||||
{
|
||||
type: 'software-catalog',
|
||||
document: {
|
||||
text: 'foo',
|
||||
title: 'bar baz',
|
||||
location: '/catalog/default/component/example',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('search result filtering', () => {
|
||||
beforeAll(async () => {
|
||||
const logger = getVoidLogger();
|
||||
|
||||
@@ -26,6 +26,7 @@ import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-no
|
||||
import { PermissionAuthorizer } from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
DocumentTypeInfo,
|
||||
IndexableResultSet,
|
||||
SearchResultSet,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { SearchEngine } from '@backstage/plugin-search-backend-node';
|
||||
@@ -89,6 +90,17 @@ export async function createRouter(
|
||||
}),
|
||||
});
|
||||
|
||||
const toSearchResults = (resultSet: IndexableResultSet): SearchResultSet => ({
|
||||
...resultSet,
|
||||
results: resultSet.results.map(result => ({
|
||||
...result,
|
||||
document: {
|
||||
...result.document,
|
||||
authorization: undefined,
|
||||
},
|
||||
})),
|
||||
});
|
||||
|
||||
const router = Router();
|
||||
router.get(
|
||||
'/query',
|
||||
@@ -116,7 +128,7 @@ export async function createRouter(
|
||||
try {
|
||||
const resultSet = await engine?.query(query, { token });
|
||||
|
||||
res.send(filterResultSet(resultSet));
|
||||
res.send(filterResultSet(toSearchResults(resultSet)));
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`There was a problem performing the search query. ${err}`,
|
||||
|
||||
@@ -30,14 +30,17 @@ export type DocumentTypeInfo = {
|
||||
};
|
||||
|
||||
// @beta
|
||||
export interface IndexableDocument {
|
||||
export type IndexableDocument = SearchDocument & {
|
||||
authorization?: {
|
||||
resourceRef: string;
|
||||
};
|
||||
location: string;
|
||||
text: string;
|
||||
title: string;
|
||||
}
|
||||
};
|
||||
|
||||
// @beta (undocumented)
|
||||
export type IndexableResult = Result<IndexableDocument>;
|
||||
|
||||
// @beta (undocumented)
|
||||
export type IndexableResultSet = ResultSet<IndexableDocument>;
|
||||
|
||||
// @beta
|
||||
export type QueryRequestOptions = {
|
||||
@@ -47,13 +50,38 @@ export type QueryRequestOptions = {
|
||||
// @beta
|
||||
export type QueryTranslator = (query: SearchQuery) => unknown;
|
||||
|
||||
// @beta (undocumented)
|
||||
export interface Result<TDocument extends SearchDocument> {
|
||||
// (undocumented)
|
||||
document: TDocument;
|
||||
// (undocumented)
|
||||
type: string;
|
||||
}
|
||||
|
||||
// @beta (undocumented)
|
||||
export interface ResultSet<TDocument extends SearchDocument> {
|
||||
// (undocumented)
|
||||
nextPageCursor?: string;
|
||||
// (undocumented)
|
||||
previousPageCursor?: string;
|
||||
// (undocumented)
|
||||
results: Result<TDocument>[];
|
||||
}
|
||||
|
||||
// @beta
|
||||
export interface SearchDocument {
|
||||
location: string;
|
||||
text: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
// @beta
|
||||
export interface SearchEngine {
|
||||
getIndexer(type: string): Promise<Writable>;
|
||||
query(
|
||||
query: SearchQuery,
|
||||
options?: QueryRequestOptions,
|
||||
): Promise<SearchResultSet>;
|
||||
): Promise<IndexableResultSet>;
|
||||
setTranslator(translator: QueryTranslator): void;
|
||||
}
|
||||
|
||||
@@ -70,20 +98,8 @@ export interface SearchQuery {
|
||||
}
|
||||
|
||||
// @beta (undocumented)
|
||||
export interface SearchResult {
|
||||
// (undocumented)
|
||||
document: IndexableDocument;
|
||||
// (undocumented)
|
||||
type: string;
|
||||
}
|
||||
export type SearchResult = Result<SearchDocument>;
|
||||
|
||||
// @beta (undocumented)
|
||||
export interface SearchResultSet {
|
||||
// (undocumented)
|
||||
nextPageCursor?: string;
|
||||
// (undocumented)
|
||||
previousPageCursor?: string;
|
||||
// (undocumented)
|
||||
results: SearchResult[];
|
||||
}
|
||||
export type SearchResultSet = ResultSet<SearchDocument>;
|
||||
```
|
||||
|
||||
@@ -31,26 +31,45 @@ export interface SearchQuery {
|
||||
/**
|
||||
* @beta
|
||||
*/
|
||||
export interface SearchResult {
|
||||
export interface Result<TDocument extends SearchDocument> {
|
||||
type: string;
|
||||
document: IndexableDocument;
|
||||
document: TDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta
|
||||
*/
|
||||
export interface SearchResultSet {
|
||||
results: SearchResult[];
|
||||
export interface ResultSet<TDocument extends SearchDocument> {
|
||||
results: Result<TDocument>[];
|
||||
nextPageCursor?: string;
|
||||
previousPageCursor?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base properties that all indexed documents must include, as well as some
|
||||
* common properties that documents are encouraged to use where appropriate.
|
||||
* @beta
|
||||
*/
|
||||
export interface IndexableDocument {
|
||||
export type SearchResult = Result<SearchDocument>;
|
||||
|
||||
/**
|
||||
* @beta
|
||||
*/
|
||||
export type SearchResultSet = ResultSet<SearchDocument>;
|
||||
|
||||
/**
|
||||
* @beta
|
||||
*/
|
||||
export type IndexableResult = Result<IndexableDocument>;
|
||||
|
||||
/**
|
||||
* @beta
|
||||
*/
|
||||
export type IndexableResultSet = ResultSet<IndexableDocument>;
|
||||
|
||||
/**
|
||||
* Base properties that all search documents must include.
|
||||
* @beta
|
||||
*/
|
||||
export interface SearchDocument {
|
||||
/**
|
||||
* The primary name of the document (e.g. name, title, identifier, etc).
|
||||
*/
|
||||
@@ -66,7 +85,16 @@ export interface IndexableDocument {
|
||||
* is clicked).
|
||||
*/
|
||||
location: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties related to indexing of documents. This type is only useful for
|
||||
* backends working directly with documents being inserted or retrieved from
|
||||
* search indexes. When dealing with documents in the frontend, use
|
||||
* {@link SearchDocument}.
|
||||
* @beta
|
||||
*/
|
||||
export type IndexableDocument = SearchDocument & {
|
||||
/**
|
||||
* Optional authorization information to be used when determining whether this
|
||||
* search result should be visible to a given user.
|
||||
@@ -77,7 +105,7 @@ export interface IndexableDocument {
|
||||
*/
|
||||
resourceRef: string;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Information about a specific document type. Intended to be used in the
|
||||
@@ -178,5 +206,5 @@ export interface SearchEngine {
|
||||
query(
|
||||
query: SearchQuery,
|
||||
options?: QueryRequestOptions,
|
||||
): Promise<SearchResultSet>;
|
||||
): Promise<IndexableResultSet>;
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ import { ApiRef } from '@backstage/core-plugin-api';
|
||||
import { AsyncState } from 'react-use/lib/useAsync';
|
||||
import { BackstagePlugin } from '@backstage/core-plugin-api';
|
||||
import { IconComponent } from '@backstage/core-plugin-api';
|
||||
import { IndexableDocument } from '@backstage/plugin-search-common';
|
||||
import { InputBaseProps } from '@material-ui/core';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { default as React_2 } from 'react';
|
||||
import { ReactElement } from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RouteRef } from '@backstage/core-plugin-api';
|
||||
import { SearchDocument } from '@backstage/plugin-search-common';
|
||||
import { SearchQuery } from '@backstage/plugin-search-common';
|
||||
import { SearchResult as SearchResult_2 } from '@backstage/plugin-search-common';
|
||||
import { SearchResultSet } from '@backstage/plugin-search-common';
|
||||
@@ -31,7 +31,7 @@ export const DefaultResultListItem: ({
|
||||
}: {
|
||||
icon?: ReactNode;
|
||||
secondaryAction?: ReactNode;
|
||||
result: IndexableDocument;
|
||||
result: SearchDocument;
|
||||
lineClamp?: number | undefined;
|
||||
}) => JSX.Element;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
import { IndexableDocument } from '@backstage/plugin-search-common';
|
||||
import { SearchDocument } from '@backstage/plugin-search-common';
|
||||
import {
|
||||
ListItem,
|
||||
ListItemIcon,
|
||||
@@ -29,7 +29,7 @@ import TextTruncate from 'react-text-truncate';
|
||||
type Props = {
|
||||
icon?: ReactNode;
|
||||
secondaryAction?: ReactNode;
|
||||
result: IndexableDocument;
|
||||
result: SearchDocument;
|
||||
lineClamp?: number;
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
microsoftAuthApiRef,
|
||||
bitbucketAuthApiRef,
|
||||
atlassianAuthApiRef,
|
||||
oneloginAuthApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
|
||||
type Props = {
|
||||
@@ -80,6 +81,14 @@ export const DefaultProviderSettings = ({ configuredProviders }: Props) => (
|
||||
icon={Star}
|
||||
/>
|
||||
)}
|
||||
{configuredProviders.includes('onelogin') && (
|
||||
<ProviderSettingsItem
|
||||
title="OneLogin"
|
||||
description="Provides authentication towards OneLogin APIs"
|
||||
apiRef={oneloginAuthApiRef}
|
||||
icon={Star}
|
||||
/>
|
||||
)}
|
||||
{configuredProviders.includes('atlassian') && (
|
||||
<ProviderSettingsItem
|
||||
title="Atlassian"
|
||||
|
||||
@@ -1358,123 +1358,6 @@
|
||||
"@babel/helper-validator-identifier" "^7.16.7"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@backstage/catalog-client@^0.9.0":
|
||||
version "0.9.0"
|
||||
resolved "https://registry.npmjs.org/@backstage/catalog-client/-/catalog-client-0.9.0.tgz#3e1024fab13fd8e2000d33833d2463ea9be5df9d"
|
||||
integrity sha512-mr2wrb+7uAPczOOYt07C4AbXIeX0mu+Iw6RPDNg67GvPvQVcbKCrjth9VEj8rScFGmJAn66ZcronbcZSS0MJww==
|
||||
dependencies:
|
||||
"@backstage/catalog-model" "^0.13.0"
|
||||
"@backstage/errors" "^0.2.2"
|
||||
cross-fetch "^3.1.5"
|
||||
|
||||
"@backstage/catalog-model@^0.13.0":
|
||||
version "0.13.0"
|
||||
resolved "https://registry.npmjs.org/@backstage/catalog-model/-/catalog-model-0.13.0.tgz#abeb91522ac7ef7907907ad5bc889803131db209"
|
||||
integrity sha512-Q+0C0LJ8niNQ4TXduUNTycZ2PLMbhKmfgWywunJLO34DZAoFnTWJEAdEYF536YyLciHhLO0dHRUaHY29Sdr+Xg==
|
||||
dependencies:
|
||||
"@backstage/config" "^0.1.15"
|
||||
"@backstage/errors" "^0.2.2"
|
||||
"@backstage/types" "^0.1.3"
|
||||
ajv "^7.0.3"
|
||||
json-schema "^0.4.0"
|
||||
lodash "^4.17.21"
|
||||
uuid "^8.0.0"
|
||||
|
||||
"@backstage/config@^0.1.15":
|
||||
version "0.1.15"
|
||||
resolved "https://registry.npmjs.org/@backstage/config/-/config-0.1.15.tgz#4bad122ad861be5bd61a60639f92d2494fa245c5"
|
||||
integrity sha512-eNJEYYSEu9MkrkBYiMpUBWEc3Bu64YgB9pZZGCMW7/9350tV2wbylEdoBJHslilJlJhiUyTXBckn8Ua7DOH7rw==
|
||||
dependencies:
|
||||
"@backstage/types" "^0.1.3"
|
||||
lodash "^4.17.21"
|
||||
|
||||
"@backstage/core-plugin-api@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.npmjs.org/@backstage/core-plugin-api/-/core-plugin-api-0.8.0.tgz#e2096bff679183168a7f9b47ed27c50a01970e32"
|
||||
integrity sha512-hHTrh0lui7TAUnLdqyvNznBuoh3PNEwHnlWCcnp2wfvLc7syG5bjnzorATknCixIiWsPfod5qtSSNeO4hQoFVQ==
|
||||
dependencies:
|
||||
"@backstage/config" "^0.1.15"
|
||||
"@backstage/types" "^0.1.3"
|
||||
"@backstage/version-bridge" "^0.1.2"
|
||||
history "^5.0.0"
|
||||
prop-types "^15.7.2"
|
||||
react-router-dom "6.0.0-beta.0"
|
||||
zen-observable "^0.8.15"
|
||||
|
||||
"@backstage/errors@^0.2.2":
|
||||
version "0.2.2"
|
||||
resolved "https://registry.npmjs.org/@backstage/errors/-/errors-0.2.2.tgz#2113e0bc859e645b8b59bfcb435f7535739b02f8"
|
||||
integrity sha512-s6Ru3NL4oFiHwNsMY1uiwqNKuOu9KQWkpse3adWZsDu1fCQKOw2omQ2qBHZ4Uj1xJ6x9Ye0yNTJ4vfHfdwiaEg==
|
||||
dependencies:
|
||||
"@backstage/types" "^0.1.3"
|
||||
cross-fetch "^3.1.5"
|
||||
serialize-error "^8.0.1"
|
||||
|
||||
"@backstage/integration-react@^0.1.10":
|
||||
version "0.1.25"
|
||||
resolved "https://registry.npmjs.org/@backstage/integration-react/-/integration-react-0.1.25.tgz#ebbdd30d66e1d210b7cd33a682ad2be0d5ea5fc0"
|
||||
integrity sha512-WI0UuXcX3m1OotfvqCVtQgyish9Gk+YkLVyrwWbzHFZ0isbT2I2i6VD/iau1X55oZpaubxLZ5CzYzA4QcAdm4w==
|
||||
dependencies:
|
||||
"@backstage/config" "^0.1.15"
|
||||
"@backstage/core-components" "^0.9.1"
|
||||
"@backstage/core-plugin-api" "^0.8.0"
|
||||
"@backstage/integration" "^0.8.0"
|
||||
"@backstage/theme" "^0.2.15"
|
||||
"@material-ui/core" "^4.12.2"
|
||||
"@material-ui/icons" "^4.9.1"
|
||||
"@material-ui/lab" "4.0.0-alpha.57"
|
||||
react-use "^17.2.4"
|
||||
|
||||
"@backstage/integration@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.npmjs.org/@backstage/integration/-/integration-0.8.0.tgz#d74131ad347272b4935973aa4bd098fad9548ce6"
|
||||
integrity sha512-GfNJRdcU7NMFjVqUSJOFRRrqEjKRGmgsL+s3TDtqmqqji7XPttTKD43+Ou50hRf0dkIETe5m1XHr1Fd5ksMt9A==
|
||||
dependencies:
|
||||
"@backstage/config" "^0.1.15"
|
||||
"@octokit/auth-app" "^3.4.0"
|
||||
"@octokit/rest" "^18.5.3"
|
||||
cross-fetch "^3.1.5"
|
||||
git-url-parse "^11.6.0"
|
||||
lodash "^4.17.21"
|
||||
luxon "^2.0.2"
|
||||
|
||||
"@backstage/plugin-catalog-react@^0.9.0":
|
||||
version "0.9.0"
|
||||
resolved "https://registry.npmjs.org/@backstage/plugin-catalog-react/-/plugin-catalog-react-0.9.0.tgz#ff8c09ec455655fadb2c2fafd578908138b858bc"
|
||||
integrity sha512-qSiz/q7B/IlkHkx3Tcrbokm+jIOPMGjSzxdnKWv5c6KcqF+XLiVr6TavLWcAjaxk81skF8N3RCDG68tA1lL0HA==
|
||||
dependencies:
|
||||
"@backstage/catalog-client" "^0.9.0"
|
||||
"@backstage/catalog-model" "^0.13.0"
|
||||
"@backstage/core-components" "^0.9.1"
|
||||
"@backstage/core-plugin-api" "^0.8.0"
|
||||
"@backstage/errors" "^0.2.2"
|
||||
"@backstage/integration" "^0.8.0"
|
||||
"@backstage/plugin-permission-common" "^0.5.2"
|
||||
"@backstage/plugin-permission-react" "^0.3.3"
|
||||
"@backstage/types" "^0.1.3"
|
||||
"@backstage/version-bridge" "^0.1.2"
|
||||
"@material-ui/core" "^4.12.2"
|
||||
"@material-ui/icons" "^4.9.1"
|
||||
"@material-ui/lab" "4.0.0-alpha.57"
|
||||
classnames "^2.2.6"
|
||||
jwt-decode "^3.1.0"
|
||||
lodash "^4.17.21"
|
||||
qs "^6.9.4"
|
||||
react-router "6.0.0-beta.0"
|
||||
react-use "^17.2.4"
|
||||
yaml "^1.10.0"
|
||||
zen-observable "^0.8.15"
|
||||
|
||||
"@backstage/types@^0.1.3":
|
||||
version "0.1.3"
|
||||
resolved "https://registry.npmjs.org/@backstage/types/-/types-0.1.3.tgz#6613d8cbdf97d42d31cd1e66a833df533e7ccf14"
|
||||
integrity sha512-fJVi4oVrlO+G3PRv1fYSll9/X4pE11HLnkI//Geare9sP6wSfp/2zXpLYfKVsG0e24jOl7Swkc8lwLkQ90zMaQ==
|
||||
|
||||
"@backstage/version-bridge@^0.1.2":
|
||||
version "0.1.2"
|
||||
resolved "https://registry.npmjs.org/@backstage/version-bridge/-/version-bridge-0.1.2.tgz#a24f42e0f383d497576f8c9d43851c6538345c03"
|
||||
integrity sha512-hwThlwfVCXaHva6IBakbHlWt3c1ev3KIwukpBlU3tDgi6tTvG8tPDe2aRwz7zzPHZRtgEw6JPvjA9gn9vSPbzQ==
|
||||
|
||||
"@bcoe/v8-coverage@^0.2.3":
|
||||
version "0.2.3"
|
||||
resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
@@ -2111,9 +1994,9 @@
|
||||
xcase "^2.0.1"
|
||||
|
||||
"@google-cloud/common@^3.7.0":
|
||||
version "3.7.0"
|
||||
resolved "https://registry.npmjs.org/@google-cloud/common/-/common-3.7.0.tgz#ee3fba75aeaa614978aebf8740380670026592aa"
|
||||
integrity sha512-oFgpKLjH9JTOAyQd3kB36iSuH8wNSpDKb1TywlB6zcsG0xmJFxLutmfPhz03KUxRMNQOZ1K1Gc9BYvJifVnGVA==
|
||||
version "3.10.0"
|
||||
resolved "https://registry.npmjs.org/@google-cloud/common/-/common-3.10.0.tgz#454d1155bb512109cd83c6183aabbd39f9aabda7"
|
||||
integrity sha512-XMbJYMh/ZSaZnbnrrOFfR/oQrb0SxG4qh6hDisWCoEbFcBHV0qHQo4uXfeMCzolx2Mfkh6VDaOGg+hyJsmxrlw==
|
||||
dependencies:
|
||||
"@google-cloud/projectify" "^2.0.0"
|
||||
"@google-cloud/promisify" "^2.0.0"
|
||||
@@ -2121,7 +2004,7 @@
|
||||
duplexify "^4.1.1"
|
||||
ent "^2.2.0"
|
||||
extend "^3.0.2"
|
||||
google-auth-library "^7.0.2"
|
||||
google-auth-library "^7.14.0"
|
||||
retry-request "^4.2.2"
|
||||
teeny-request "^7.0.0"
|
||||
|
||||
@@ -4972,15 +4855,15 @@
|
||||
resolved "https://registry.npmjs.org/@rjsf/material-ui/-/material-ui-3.2.1.tgz#84fbf322485aee3a84101e189161f0687779ec8d"
|
||||
integrity sha512-8UiDeDbjCImFSfOegGu13otQ7OdP9FOYpcLjeouppnhs+MPeIEAtYS+jCcBKmi3reyTagC15/KVSRhde1wS1vg==
|
||||
|
||||
"@roadiehq/backstage-plugin-buildkite@^1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-buildkite/-/backstage-plugin-buildkite-1.4.0.tgz#f268ba2b6f43f68158af379d55fc7e19842c1b43"
|
||||
integrity sha512-bG16kyn5e/QzceGy7PXs/wb09/p7W1O+kev7V/PGECHlxgj/Gg575maFsyZU/bIGTQM3cUavwvj0pzfdXjFD3Q==
|
||||
"@roadiehq/backstage-plugin-buildkite@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-buildkite/-/backstage-plugin-buildkite-2.0.0.tgz#e5da049a2e15c533228e26da1af537b3783fe5de"
|
||||
integrity sha512-qurSWt+n505BXFBTzoTSXn9BPmhtLjt+SPiVYjD3KSqJ7hwOoJg3rr1wuCWigUDj1R0Kx9TgaxNscETGBzUUgQ==
|
||||
dependencies:
|
||||
"@backstage/catalog-model" "^0.13.0"
|
||||
"@backstage/catalog-model" "^1.0.0"
|
||||
"@backstage/core-components" "^0.9.0"
|
||||
"@backstage/core-plugin-api" "^0.8.0"
|
||||
"@backstage/plugin-catalog-react" "^0.9.0"
|
||||
"@backstage/core-plugin-api" "^1.0.0"
|
||||
"@backstage/plugin-catalog-react" "^1.0.0"
|
||||
"@backstage/theme" "^0.2.6"
|
||||
"@material-ui/core" "^4.12.1"
|
||||
"@material-ui/icons" "^4.11.2"
|
||||
@@ -4991,16 +4874,16 @@
|
||||
react-router-dom "6.0.0-beta.0"
|
||||
react-use "^17.2.4"
|
||||
|
||||
"@roadiehq/backstage-plugin-github-insights@^1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-1.6.0.tgz#0ccd63ff08f720f09d0076ba3210ef2373bb62b2"
|
||||
integrity sha512-TV7hrxxhJy0yN+jERVKALYW36nwvNWCpCOcBGgoBepbFCOhiUgc3g9EjRp0b0z+uUSywFkEPUYH1B1CXGzDLdw==
|
||||
"@roadiehq/backstage-plugin-github-insights@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-2.0.0.tgz#1137f4f3ad5744559bf095ee868371f5fd8c60ec"
|
||||
integrity sha512-6rLMWq8F6hYNAkuF+gOKyAZ/A2MGVHuNJArBJCUVhw6b/tmSxQcj78y62GaXB1Zz9KERhSk8OZf94m0+EE8C5g==
|
||||
dependencies:
|
||||
"@backstage/catalog-model" "^0.13.0"
|
||||
"@backstage/catalog-model" "^1.0.0"
|
||||
"@backstage/core-components" "^0.9.0"
|
||||
"@backstage/core-plugin-api" "^0.8.0"
|
||||
"@backstage/integration-react" "^0.1.10"
|
||||
"@backstage/plugin-catalog-react" "^0.9.0"
|
||||
"@backstage/core-plugin-api" "^1.0.0"
|
||||
"@backstage/integration-react" "^1.0.0"
|
||||
"@backstage/plugin-catalog-react" "^1.0.0"
|
||||
"@backstage/theme" "^0.2.7"
|
||||
"@date-io/core" "2.10.7"
|
||||
"@material-ui/core" "^4.11.0"
|
||||
@@ -5015,15 +4898,15 @@
|
||||
react-use "^17.2.4"
|
||||
zustand "3.6.9"
|
||||
|
||||
"@roadiehq/backstage-plugin-github-pull-requests@^1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-pull-requests/-/backstage-plugin-github-pull-requests-1.5.0.tgz#30266e037831c11842cd8e1456008fede2c6b580"
|
||||
integrity sha512-jlNYV/RC0HVwkp8lCmPAhps6KpRP4OuTrsrIXt557EHWb+kuwEAV5jKaSkINvUOjIPAnOB7SA83xXWZW1Y9lig==
|
||||
"@roadiehq/backstage-plugin-github-pull-requests@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-pull-requests/-/backstage-plugin-github-pull-requests-2.0.0.tgz#99ba670e1bd9bdc954b43901883aaf9832ae8942"
|
||||
integrity sha512-3R9MmWGqmd7cVEZtiUShr10M8j+aM0SQUaZG1EuW+AzlErA2Zo1pcSBwKuIOcotWbyQzVsruKEPI2BrvJkJikQ==
|
||||
dependencies:
|
||||
"@backstage/catalog-model" "^0.13.0"
|
||||
"@backstage/catalog-model" "^1.0.0"
|
||||
"@backstage/core-components" "^0.9.0"
|
||||
"@backstage/core-plugin-api" "^0.8.0"
|
||||
"@backstage/plugin-catalog-react" "^0.9.0"
|
||||
"@backstage/core-plugin-api" "^1.0.0"
|
||||
"@backstage/plugin-catalog-react" "^1.0.0"
|
||||
"@material-ui/core" "^4.11.0"
|
||||
"@material-ui/icons" "^4.9.1"
|
||||
"@octokit/rest" "^18.5.3"
|
||||
@@ -5036,15 +4919,15 @@
|
||||
react-router "6.0.0-beta.0"
|
||||
react-use "^17.2.4"
|
||||
|
||||
"@roadiehq/backstage-plugin-travis-ci@^1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-1.4.0.tgz#165b401b4b4eaae93ebb1148854c8343392f9950"
|
||||
integrity sha512-tYNpfybmKiOULYoL7YV2DJ1l0e8UcbZsc3B0pSUbchPedoWlvgpXoG5BmttTZcCW8MI0e1kK2FWqwnVkvygRhQ==
|
||||
"@roadiehq/backstage-plugin-travis-ci@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-2.0.0.tgz#d86eba878bbaa6ada77e9eda932ff25bd9121021"
|
||||
integrity sha512-nrqWysSLBq4t9jVkQb5rz1pfNt2Ojlj+s0004FSNzPa8PPD+5XUhFZiS9Mi4kJVUC9BLW5SVdJzWtWEGbxyLPw==
|
||||
dependencies:
|
||||
"@backstage/catalog-model" "^0.13.0"
|
||||
"@backstage/catalog-model" "^1.0.0"
|
||||
"@backstage/core-components" "^0.9.0"
|
||||
"@backstage/core-plugin-api" "^0.8.0"
|
||||
"@backstage/plugin-catalog-react" "^0.9.0"
|
||||
"@backstage/core-plugin-api" "^1.0.0"
|
||||
"@backstage/plugin-catalog-react" "^1.0.0"
|
||||
"@backstage/theme" "^0.2.9"
|
||||
"@material-ui/core" "^4.11.3"
|
||||
"@material-ui/icons" "^4.11.2"
|
||||
@@ -12106,10 +11989,10 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
|
||||
"@material-ui/icons" "^4.9.1"
|
||||
"@material-ui/lab" "4.0.0-alpha.57"
|
||||
"@octokit/rest" "^18.5.3"
|
||||
"@roadiehq/backstage-plugin-buildkite" "^1.4.0"
|
||||
"@roadiehq/backstage-plugin-github-insights" "^1.6.0"
|
||||
"@roadiehq/backstage-plugin-github-pull-requests" "^1.5.0"
|
||||
"@roadiehq/backstage-plugin-travis-ci" "^1.4.0"
|
||||
"@roadiehq/backstage-plugin-buildkite" "^2.0.0"
|
||||
"@roadiehq/backstage-plugin-github-insights" "^2.0.0"
|
||||
"@roadiehq/backstage-plugin-github-pull-requests" "^2.0.0"
|
||||
"@roadiehq/backstage-plugin-travis-ci" "^2.0.0"
|
||||
history "^5.0.0"
|
||||
prop-types "^15.7.2"
|
||||
react "^17.0.2"
|
||||
@@ -13357,10 +13240,10 @@ globby@^7.1.1:
|
||||
pify "^3.0.0"
|
||||
slash "^1.0.0"
|
||||
|
||||
google-auth-library@^7.0.0, google-auth-library@^7.0.2, google-auth-library@^7.6.1:
|
||||
version "7.12.0"
|
||||
resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.12.0.tgz#7965db6bc20cb31f2df05a08a296bbed6af69426"
|
||||
integrity sha512-RS/whvFPMoF1hQNxnoVET3DWKPBt1Xgqe2rY0k+Jn7TNhoHlwdnSe7Rlcbo2Nub3Mt2lUVz26X65aDQrWp6x8w==
|
||||
google-auth-library@^7.0.0, google-auth-library@^7.14.0, google-auth-library@^7.6.1:
|
||||
version "7.14.0"
|
||||
resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.0.tgz#9d6a20592f7b4d4c463cd3e93934c4b1711d5dc6"
|
||||
integrity sha512-or8r7qUqGVI3W8lVSdPh0ZpeFyQHeE73g5c0p+bLNTTUFXJ+GSeDQmZRZ2p4H8cF/RJYa4PNvi/A1ar1uVNLFA==
|
||||
dependencies:
|
||||
arrify "^2.0.0"
|
||||
base64-js "^1.3.0"
|
||||
|
||||
Reference in New Issue
Block a user