Merge remote-tracking branch 'upstream/master' into airbrake-no-projectid-no-error
# Conflicts: # ADOPTERS.md
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed the `AboutCard` component which has been replaced by `EntityAboutCard`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed `reduceCatalogFilters` and `reduceEntityFilters` due to low external utility value.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
chore(deps): bump `minimatch` from 5.0.0 to 5.0.1
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/core-components': patch
|
||||
'@backstage/plugin-gcp-projects': patch
|
||||
---
|
||||
|
||||
chore(deps): bump `@react-hookz/web` from 12.3.0 to 13.0.0
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed `CatalogResultListItemProps` and `CatalogResultListItem`, replaced by `CatalogSearchResultListItemProps` and `CatalogSearchResultListItem`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed `ScaffolderTaskOutput.entityRef` and `ScaffolderTaskOutput.remoteUrl`, which both have been deprecated for over a year. Please use the `links` output instead.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
**BREAKING**:
|
||||
|
||||
- Removed the previously deprecated `runPeriodically` export. Please use the `@backstage/backend-tasks` package instead, or copy [the actual implementation](https://github.com/backstage/backstage/blob/02875d4d56708c60f86f6b0a5b3da82e24988354/plugins/catalog-backend/src/util/runPeriodically.ts#L29) into your own code if you explicitly do not want coordination of task runs across your worker nodes.
|
||||
- Removed the previously deprecated `CatalogProcessorLocationResult.optional` field. Please set the corresponding `LocationSpec.presence` field to `'optional'` instead.
|
||||
- Related to the previous point, the `processingResult.location` function no longer has a second boolean `optional` argument. Please set the corresponding `LocationSpec.presence` field to `'optional'` instead.
|
||||
- Removed the previously deprecated `StaticLocationProcessor`. It has not been in use for some time; its functionality is covered by `ConfigLocationEntityProvider` instead.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
'@backstage/plugin-rollbar': patch
|
||||
---
|
||||
|
||||
Removed usage of removed hook.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed `useEntityCompoundName`, use `useRouteRefParams(entityRouteRef)` instead.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Updated the template to write the Backstage release version to `backstage.json`, rather than the version of `@backstage/create-app`. This change is applied automatically when running `backstage-cli versions:bump` in the latest version of the Backstage CLI.
|
||||
@@ -1,3 +1,10 @@
|
||||
{
|
||||
"currentReleaseVersion": {}
|
||||
"currentReleaseVersion": {
|
||||
"@backstage/backend-common": "0.12.1",
|
||||
"@backstage/catalog-model": "0.12.1",
|
||||
"@backstage/cli": "0.15.1",
|
||||
"@backstage/plugin-catalog-backend": "0.23.1",
|
||||
"@backstage/plugin-catalog-common": "0.2.1",
|
||||
"@backstage/plugin-catalog-react": "0.8.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Applied the fix from version `0.15.1` of this package, which was part of the `v0.70.1` release of Backstage.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
'@backstage/backend-common': patch
|
||||
'@backstage/catalog-model': patch
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
'@backstage/plugin-catalog-common': patch
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Applied the fix for the `/alpha` entry point resolution that was part of the `v0.70.1` release of Backstage.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed `useEntityListProvider` use `useEntityList` instead.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/catalog-model': minor
|
||||
---
|
||||
|
||||
**BREAKING**: The default validator for `metadata.tags` now permits the colon (`:`) character as well.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
The backend development setup now ignores the `"browser"` and `"module"` entry points in `package.json`, and instead always uses `"main"`.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
'@backstage/plugin-org': patch
|
||||
---
|
||||
|
||||
Fixed EntityOwnerPicker and OwnershipCard url filter issue with more than 21 owners
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/catalog-model': patch
|
||||
---
|
||||
|
||||
**DEPRECATION**:
|
||||
|
||||
- Deprecated `CommonValidatorFunctions.isValidString`, please use `isNonEmptyString` instead which is equivalent but better named.
|
||||
- Deprecated `CommonValidatorFunctions.isValidTag`, with no replacement. Its purpose was too specific and not reusable, so it will be removed.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/backend-common': minor
|
||||
---
|
||||
|
||||
**BREAKING**:
|
||||
|
||||
- Removed the (since way back) deprecated `createDatabase` export, please use `createDatabaseClient` instead.
|
||||
- Removed the (since way back) deprecated `SingleConnectionDatabaseManager` export, please use `DatabaseManager` instead.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Allow passing more repo configuration for `publish:github` action
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/catalog-client': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed `CatalogClient.getLocationByEntity` and `CatalogClient.getOriginLocationByEntity` which had previously been deprecated. Please use `CatalogApi.getLocationByRef` instead. Note that this only affects you if you were using `CatalogClient` (the class) directly, rather than `CatalogApi` (the interface), since it has been removed from the interface in an earlier release.
|
||||
@@ -102,3 +102,4 @@ _If you're using Backstage in your organization, please try to add your company
|
||||
| [Grupo Boticário](https://www.grupoboticario.com.br/) | [@chicoribas](https://github.com/chicoribas), [@fndiaz](https://github.com/fndiaz), [@digogid](https://github.com/digogid), [@manumbs](https://github.com/manumbs), [@haooliveira84](https://github.com/haooliveira84), [@Rhullyam](https://github.com/Rhullyam) | Centralized developer portal with catalog, documentation, and software templates to build a ready to go application, including pipelines (CI/CD) and cloud services provisioning |
|
||||
| [Snyk](https://snyk.io/) | [@robcresswell](https://github.com/robcresswell) | Developer portal for software discovery, API documentation and templating |
|
||||
| [Stilingue](https://www.stilingue.com.br/) | [@stilingue-inteligencia-artificial](https://github.com/Stilingue-IA), [@bbviana](https://github.com/bbviana) | Developer portal, services catalog and centralization of metrics from Grafana, Sentry and GCP. Furthermore, centralization of documentation and infra details like DNS, Network services, SSL and so on. |
|
||||
| [TUI Group](https://www.tuigroup.com/) | [Simon Stamm](https://github.com/simonstamm), [Christian Rudolph](https://github.com/ChrisRu82) | Developer portal for all engineer to provide discoverability to all internal components, APIs, documentation and to scaffold templates with integrations to our internal tools extended by plugins from our community. |
|
||||
|
||||
@@ -49,29 +49,6 @@ yarn add sqlite3
|
||||
From an operational perspective, you only need to install drivers for clients
|
||||
that are actively used.
|
||||
|
||||
### Database Manager
|
||||
|
||||
Existing Backstage instances should be updated to use `DatabaseManager` from
|
||||
`@backstage/backend-common` in your `packages/backend/src/index.ts` file, the
|
||||
`SingleConnectionDatabaseManager` has been deprecated. Import the manager and
|
||||
update the references as shown below if this is not the case:
|
||||
|
||||
```diff
|
||||
import {
|
||||
- SingleConnectionDatabaseManager,
|
||||
+ DatabaseManager,
|
||||
} from '@backstage/backend-common';
|
||||
|
||||
// ...
|
||||
|
||||
function makeCreateEnv(config: Config) {
|
||||
// ...
|
||||
- const databaseManager = SingleConnectionDatabaseManager.fromConfig(config);
|
||||
+ const databaseManager = DatabaseManager.fromConfig(config);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
You should set the base database client and connection information in your
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @backstage/backend-common
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Fixed runtime resolution of the `/alpha` entry point.
|
||||
|
||||
## 0.12.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -170,9 +170,6 @@ export class Contexts {
|
||||
): Context;
|
||||
}
|
||||
|
||||
// @public @deprecated
|
||||
export const createDatabase: typeof createDatabaseClient;
|
||||
|
||||
// @public
|
||||
export function createDatabaseClient(
|
||||
dbConfig: Config,
|
||||
@@ -574,9 +571,6 @@ export type ServiceBuilder = {
|
||||
// @public
|
||||
export function setRootLogger(newLogger: winston.Logger): void;
|
||||
|
||||
// @public @deprecated
|
||||
export const SingleConnectionDatabaseManager: typeof DatabaseManager;
|
||||
|
||||
// @public
|
||||
export class SingleHostDiscovery implements PluginEndpointDiscovery {
|
||||
static fromConfig(
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { createDatabaseClient, ensureDatabaseExists } from './connection';
|
||||
import { SingleConnectionDatabaseManager } from './SingleConnection';
|
||||
|
||||
jest.mock('./connection', () => ({
|
||||
...jest.requireActual('./connection'),
|
||||
createDatabaseClient: jest.fn(),
|
||||
ensureDatabaseExists: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('SingleConnectionDatabaseManager', () => {
|
||||
const defaultConfigOptions = {
|
||||
backend: {
|
||||
database: {
|
||||
client: 'pg',
|
||||
connection: {
|
||||
host: 'localhost',
|
||||
user: 'foo',
|
||||
password: 'bar',
|
||||
database: 'foodb',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const defaultConfig = () => new ConfigReader(defaultConfigOptions);
|
||||
|
||||
// This is similar to the ts-jest `mocked` helper.
|
||||
const mocked = (f: Function) => f as jest.Mock;
|
||||
|
||||
afterEach(() => jest.resetAllMocks());
|
||||
|
||||
describe('SingleConnectionDatabaseManager.fromConfig', () => {
|
||||
it('accesses the backend.database key', () => {
|
||||
const config = defaultConfig();
|
||||
const getConfig = jest.spyOn(config, 'getConfig');
|
||||
|
||||
SingleConnectionDatabaseManager.fromConfig(config);
|
||||
|
||||
expect(getConfig.mock.calls[0][0]).toEqual('backend.database');
|
||||
});
|
||||
});
|
||||
|
||||
describe('SingleConnectionDatabaseManager.forPlugin', () => {
|
||||
const manager = SingleConnectionDatabaseManager.fromConfig(defaultConfig());
|
||||
|
||||
it('connects to a database scoped to the plugin', async () => {
|
||||
const pluginId = 'test1';
|
||||
await manager.forPlugin(pluginId).getClient();
|
||||
|
||||
expect(mocked(createDatabaseClient)).toHaveBeenCalledTimes(1);
|
||||
|
||||
const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1);
|
||||
const callArgs = mockCalls[0];
|
||||
expect(callArgs[1].connection.database).toEqual(
|
||||
`backstage_plugin_${pluginId}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('provides different plugins different databases', async () => {
|
||||
const plugin1Id = 'test1';
|
||||
const plugin2Id = 'test2';
|
||||
await manager.forPlugin(plugin1Id).getClient();
|
||||
await manager.forPlugin(plugin2Id).getClient();
|
||||
|
||||
expect(mocked(createDatabaseClient)).toHaveBeenCalledTimes(2);
|
||||
|
||||
const mockCalls = mocked(createDatabaseClient).mock.calls;
|
||||
const plugin1CallArgs = mockCalls[0];
|
||||
const plugin2CallArgs = mockCalls[1];
|
||||
expect(plugin1CallArgs[1].connection.database).not.toEqual(
|
||||
plugin2CallArgs[1].connection.database,
|
||||
);
|
||||
});
|
||||
|
||||
it('ensure plugin database is created', async () => {
|
||||
await manager.forPlugin('test').getClient();
|
||||
const mockCalls = mocked(ensureDatabaseExists).mock.calls.splice(-1);
|
||||
const [_, database] = mockCalls[0];
|
||||
|
||||
expect(database).toEqual('backstage_plugin_test');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DatabaseManager } from './DatabaseManager';
|
||||
|
||||
/**
|
||||
* Implements a Database Manager which will automatically create new databases
|
||||
* for plugins when requested. All requested databases are created with the
|
||||
* credentials provided; if the database already exists no attempt to create
|
||||
* the database will be made.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use `DatabaseManager` from `@backend-common` instead.
|
||||
*/
|
||||
export const SingleConnectionDatabaseManager = DatabaseManager;
|
||||
@@ -57,14 +57,6 @@ export function createDatabaseClient(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for {@link createDatabaseClient}
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use createDatabaseClient instead
|
||||
*/
|
||||
export const createDatabase = createDatabaseClient;
|
||||
|
||||
/**
|
||||
* Ensures that the given databases all exist, creating them if they do not.
|
||||
*
|
||||
|
||||
@@ -14,18 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './SingleConnection';
|
||||
export * from './DatabaseManager';
|
||||
|
||||
/*
|
||||
* Undocumented API surface from connection is being reduced for future deprecation.
|
||||
* Avoid exporting additional symbols.
|
||||
*/
|
||||
export {
|
||||
createDatabaseClient,
|
||||
createDatabase,
|
||||
ensureDatabaseExists,
|
||||
} from './connection';
|
||||
export { createDatabaseClient, ensureDatabaseExists } from './connection';
|
||||
|
||||
export type { PluginDatabaseManager } from './types';
|
||||
export { isDatabaseConflictError } from './util';
|
||||
|
||||
@@ -107,11 +107,6 @@ export class CatalogClient implements CatalogApi {
|
||||
request: GetEntityFacetsRequest,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<GetEntityFacetsResponse>;
|
||||
// @deprecated (undocumented)
|
||||
getLocationByEntity(
|
||||
entity: Entity,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<Location_2 | undefined>;
|
||||
getLocationById(
|
||||
id: string,
|
||||
options?: CatalogRequestOptions,
|
||||
@@ -120,11 +115,6 @@ export class CatalogClient implements CatalogApi {
|
||||
locationRef: string,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<Location_2 | undefined>;
|
||||
// @deprecated (undocumented)
|
||||
getOriginLocationByEntity(
|
||||
entity: Entity,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<Location_2 | undefined>;
|
||||
refreshEntity(
|
||||
entityRef: string,
|
||||
options?: CatalogRequestOptions,
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
ANNOTATION_LOCATION,
|
||||
ANNOTATION_ORIGIN_LOCATION,
|
||||
Entity,
|
||||
CompoundEntityRef,
|
||||
parseEntityRef,
|
||||
@@ -308,49 +306,6 @@ export class CatalogClient implements CatalogApi {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated please use getLocationByRef instead
|
||||
*/
|
||||
async getOriginLocationByEntity(
|
||||
entity: Entity,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<Location | undefined> {
|
||||
const locationCompound =
|
||||
entity.metadata.annotations?.[ANNOTATION_ORIGIN_LOCATION];
|
||||
if (!locationCompound) {
|
||||
return undefined;
|
||||
}
|
||||
const all: { data: Location }[] = await this.requestRequired(
|
||||
'GET',
|
||||
'/locations',
|
||||
options,
|
||||
);
|
||||
return all
|
||||
.map(r => r.data)
|
||||
.find(l => locationCompound === stringifyLocationRef(l));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated please use getLocationByRef instead
|
||||
*/
|
||||
async getLocationByEntity(
|
||||
entity: Entity,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<Location | undefined> {
|
||||
const locationCompound = entity.metadata.annotations?.[ANNOTATION_LOCATION];
|
||||
if (!locationCompound) {
|
||||
return undefined;
|
||||
}
|
||||
const all: { data: Location }[] = await this.requestRequired(
|
||||
'GET',
|
||||
'/locations',
|
||||
options,
|
||||
);
|
||||
return all
|
||||
.map(r => r.data)
|
||||
.find(l => locationCompound === stringifyLocationRef(l));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc CatalogApi.getLocationByRef}
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @backstage/catalog-model
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Fixed runtime resolution of the `/alpha` entry point.
|
||||
|
||||
## 0.12.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -51,6 +51,7 @@ export const apiEntityV1alpha1Validator: KindValidator;
|
||||
// @public
|
||||
export class CommonValidatorFunctions {
|
||||
static isJsonSafe(value: unknown): boolean;
|
||||
static isNonEmptyString(value: unknown): value is string;
|
||||
static isValidDnsLabel(value: unknown): boolean;
|
||||
static isValidDnsSubdomain(value: unknown): boolean;
|
||||
static isValidPrefixAndOrSuffix(
|
||||
@@ -59,7 +60,9 @@ export class CommonValidatorFunctions {
|
||||
isValidPrefix: (value: string) => boolean,
|
||||
isValidSuffix: (value: string) => boolean,
|
||||
): boolean;
|
||||
// @deprecated
|
||||
static isValidString(value: unknown): boolean;
|
||||
// @deprecated
|
||||
static isValidTag(value: unknown): boolean;
|
||||
static isValidUrl(value: unknown): boolean;
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ export class FieldFormatEntityPolicy implements EntityPolicy {
|
||||
expectation = 'a string that is a valid url';
|
||||
break;
|
||||
case 'isValidString':
|
||||
case 'isNonEmptyString':
|
||||
expectation = 'a non empty string';
|
||||
break;
|
||||
default:
|
||||
@@ -156,7 +157,7 @@ export class FieldFormatEntityPolicy implements EntityPolicy {
|
||||
optional(
|
||||
`links.${i}.title`,
|
||||
links[i]?.title,
|
||||
CommonValidatorFunctions.isValidString,
|
||||
CommonValidatorFunctions.isNonEmptyString,
|
||||
);
|
||||
optional(
|
||||
`links.${i}.icon`,
|
||||
|
||||
@@ -226,4 +226,20 @@ describe('CommonValidatorFunctions', () => {
|
||||
])(`isValidString %p ? %p`, (value, result) => {
|
||||
expect(CommonValidatorFunctions.isValidString(value)).toBe(result);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[null, false],
|
||||
[true, false],
|
||||
[7, false],
|
||||
[{}, false],
|
||||
['', false],
|
||||
[' ', false],
|
||||
[' ', false],
|
||||
['abc', true],
|
||||
[' abc ', true],
|
||||
['abc xyz', true],
|
||||
['abc xyz abc.', true],
|
||||
])(`isNonEmptyString %p ? %p`, (value, result) => {
|
||||
expect(CommonValidatorFunctions.isNonEmptyString(value)).toBe(result);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -98,6 +98,7 @@ export class CommonValidatorFunctions {
|
||||
/**
|
||||
* Checks that the value is a valid tag.
|
||||
*
|
||||
* @deprecated This will be removed in a future release
|
||||
* @param value - The value to check
|
||||
*/
|
||||
static isValidTag(value: unknown): boolean {
|
||||
@@ -110,7 +111,7 @@ export class CommonValidatorFunctions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the value is a valid URL.
|
||||
* Checks that the value is a valid string URL.
|
||||
*
|
||||
* @param value - The value to check
|
||||
*/
|
||||
@@ -131,9 +132,19 @@ export class CommonValidatorFunctions {
|
||||
/**
|
||||
* Checks that the value is a non empty string value.
|
||||
*
|
||||
* @deprecated use isNonEmptyString instead
|
||||
* @param value - The value to check
|
||||
*/
|
||||
static isValidString(value: unknown): boolean {
|
||||
return typeof value === 'string' && value?.trim()?.length >= 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the value is a string value that's not empty.
|
||||
*
|
||||
* @param value - The value to check
|
||||
*/
|
||||
static isNonEmptyString(value: unknown): value is string {
|
||||
return typeof value === 'string' && value?.trim()?.length >= 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CommonValidatorFunctions } from './CommonValidatorFunctions';
|
||||
import { KubernetesValidatorFunctions } from './KubernetesValidatorFunctions';
|
||||
import { Validators } from './types';
|
||||
|
||||
@@ -27,7 +26,15 @@ const defaultValidators: Validators = {
|
||||
isValidLabelValue: KubernetesValidatorFunctions.isValidLabelValue,
|
||||
isValidAnnotationKey: KubernetesValidatorFunctions.isValidAnnotationKey,
|
||||
isValidAnnotationValue: KubernetesValidatorFunctions.isValidAnnotationValue,
|
||||
isValidTag: CommonValidatorFunctions.isValidTag,
|
||||
isValidTag: (value: unknown): boolean => {
|
||||
// NOTE(freben): This one is a bit of an oddball and doesn't fit well anywhere to delegate to, so it's just inlined for now.
|
||||
return (
|
||||
typeof value === 'string' &&
|
||||
value.length >= 1 &&
|
||||
value.length <= 63 &&
|
||||
/^[a-z0-9:+#]+(\-[a-z0-9:+#]+)*$/.test(value)
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @backstage/cli
|
||||
|
||||
## 0.15.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Fixed an issue where the release stage entry point of packages were not resolved correctly.
|
||||
|
||||
## 0.15.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
"json-schema": "^0.4.0",
|
||||
"jest-transform-yaml": "^1.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"minimatch": "5.0.0",
|
||||
"minimatch": "5.0.1",
|
||||
"mini-css-extract-plugin": "^2.4.2",
|
||||
"npm-packlist": "^3.0.0",
|
||||
"node-libs-browser": "^2.2.1",
|
||||
|
||||
@@ -23,6 +23,11 @@ const SKIPPED_KEYS = ['access', 'registry', 'tag', 'alphaTypes', 'betaTypes'];
|
||||
const PKG_PATH = 'package.json';
|
||||
const PKG_BACKUP_PATH = 'package.json-prepack';
|
||||
|
||||
function resolveEntrypoint(pkg: any, name: string) {
|
||||
const targetEntry = pkg.publishConfig[name] || pkg[name];
|
||||
return targetEntry && joinPath('..', targetEntry);
|
||||
}
|
||||
|
||||
// Writes e.g. alpha/package.json
|
||||
async function writeReleaseStageEntrypoint(pkg: any, stage: 'alpha' | 'beta') {
|
||||
await fs.ensureDir(paths.resolveTarget(stage));
|
||||
@@ -31,9 +36,9 @@ async function writeReleaseStageEntrypoint(pkg: any, stage: 'alpha' | 'beta') {
|
||||
{
|
||||
name: pkg.name,
|
||||
version: pkg.version,
|
||||
main: (pkg.publishConfig.main || pkg.main) && '..',
|
||||
module: (pkg.publishConfig.module || pkg.module) && '..',
|
||||
browser: (pkg.publishConfig.browser || pkg.browser) && '..',
|
||||
main: resolveEntrypoint(pkg, 'main'),
|
||||
module: resolveEntrypoint(pkg, 'module'),
|
||||
browser: resolveEntrypoint(pkg, 'browser'),
|
||||
types: joinPath('..', pkg.publishConfig[`${stage}Types`]),
|
||||
},
|
||||
{ encoding: 'utf8', spaces: 2 },
|
||||
|
||||
@@ -272,7 +272,7 @@ export async function createBackendConfig(
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'],
|
||||
mainFields: ['browser', 'module', 'main'],
|
||||
mainFields: ['main'],
|
||||
modules: [paths.rootNodeModules, ...moduleDirs],
|
||||
plugins: [
|
||||
new LinkedPackageResolvePlugin(paths.rootNodeModules, externalPkgs),
|
||||
|
||||
@@ -20,6 +20,11 @@ import { join as joinPath, resolve as resolvePath } from 'path';
|
||||
|
||||
const SKIPPED_KEYS = ['access', 'registry', 'tag', 'alphaTypes', 'betaTypes'];
|
||||
|
||||
function resolveEntrypoint(pkg: any, name: string) {
|
||||
const targetEntry = pkg.publishConfig[name] || pkg[name];
|
||||
return targetEntry && joinPath('..', targetEntry);
|
||||
}
|
||||
|
||||
// Writes e.g. alpha/package.json
|
||||
async function writeReleaseStageEntrypoint(
|
||||
pkg: any,
|
||||
@@ -32,9 +37,9 @@ async function writeReleaseStageEntrypoint(
|
||||
{
|
||||
name: pkg.name,
|
||||
version: pkg.version,
|
||||
main: (pkg.publishConfig.main || pkg.main) && '..',
|
||||
module: (pkg.publishConfig.module || pkg.module) && '..',
|
||||
browser: (pkg.publishConfig.browser || pkg.browser) && '..',
|
||||
main: resolveEntrypoint(pkg, 'main'),
|
||||
module: resolveEntrypoint(pkg, 'module'),
|
||||
browser: resolveEntrypoint(pkg, 'browser'),
|
||||
types: joinPath('..', pkg.publishConfig[`${stage}Types`]),
|
||||
},
|
||||
{ encoding: 'utf8', spaces: 2 },
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"react-syntax-highlighter": "^15.4.5",
|
||||
"react-text-truncate": "^0.18.0",
|
||||
"react-use": "^17.3.2",
|
||||
"@react-hookz/web": "^12.3.0",
|
||||
"@react-hookz/web": "^13.0.0",
|
||||
"react-virtualized-auto-sizer": "^1.0.6",
|
||||
"react-window": "^1.8.6",
|
||||
"remark-gfm": "^3.0.1",
|
||||
|
||||
@@ -59,7 +59,7 @@ describe('command entrypoint', () => {
|
||||
|
||||
test('should call expected tasks with no path option', async () => {
|
||||
const cmd = {} as unknown as Command;
|
||||
await createApp(cmd, '1.0.0');
|
||||
await createApp(cmd);
|
||||
expect(checkAppExistsMock).toHaveBeenCalled();
|
||||
expect(createTemporaryAppFolderMock).toHaveBeenCalled();
|
||||
expect(templatingMock).toHaveBeenCalled();
|
||||
@@ -69,7 +69,7 @@ describe('command entrypoint', () => {
|
||||
|
||||
it('should call expected tasks with path option', async () => {
|
||||
const cmd = { path: 'myDirectory' } as unknown as Command;
|
||||
await createApp(cmd, '1.0.0');
|
||||
await createApp(cmd);
|
||||
expect(checkPathExistsMock).toHaveBeenCalled();
|
||||
expect(templatingMock).toHaveBeenCalled();
|
||||
expect(buildAppMock).toHaveBeenCalled();
|
||||
@@ -77,7 +77,7 @@ describe('command entrypoint', () => {
|
||||
|
||||
it('should not call `buildAppTask` when `skipInstall` is supplied', async () => {
|
||||
const cmd = { skipInstall: true } as unknown as Command;
|
||||
await createApp(cmd, '1.0.0');
|
||||
await createApp(cmd);
|
||||
expect(buildAppMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
templatingTask,
|
||||
} from './lib/tasks';
|
||||
|
||||
export default async (cmd: Command, version: string): Promise<void> => {
|
||||
export default async (cmd: Command): Promise<void> => {
|
||||
/* eslint-disable-next-line no-restricted-syntax */
|
||||
const paths = findPaths(__dirname);
|
||||
|
||||
@@ -80,7 +80,7 @@ export default async (cmd: Command, version: string): Promise<void> => {
|
||||
await checkPathExistsTask(appDir);
|
||||
|
||||
Task.section('Preparing files');
|
||||
await templatingTask(templateDir, cmd.path, answers, version);
|
||||
await templatingTask(templateDir, cmd.path, answers);
|
||||
} else {
|
||||
// Template to temporary location, and then move files
|
||||
|
||||
@@ -91,7 +91,7 @@ export default async (cmd: Command, version: string): Promise<void> => {
|
||||
await createTemporaryAppFolderTask(tempDir);
|
||||
|
||||
Task.section('Preparing files');
|
||||
await templatingTask(templateDir, tempDir, answers, version);
|
||||
await templatingTask(templateDir, tempDir, answers);
|
||||
|
||||
Task.section('Moving to final location');
|
||||
await moveAppTask(tempDir, appDir, answers.name);
|
||||
|
||||
@@ -38,7 +38,7 @@ const main = (argv: string[]) => {
|
||||
'--skip-install',
|
||||
'Skip the install and builds steps after creating the app',
|
||||
)
|
||||
.action(cmd => createApp(cmd, version));
|
||||
.action(cmd => createApp(cmd));
|
||||
|
||||
program.parse(argv);
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@ import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import child_process from 'child_process';
|
||||
import path from 'path';
|
||||
import { version as releaseVersion } from '../../../../package.json';
|
||||
import {
|
||||
buildAppTask,
|
||||
checkAppExistsTask,
|
||||
@@ -195,11 +196,11 @@ describe('templatingTask', () => {
|
||||
name: 'SuperCoolBackstageInstance',
|
||||
dbTypeSqlite: true,
|
||||
};
|
||||
await templatingTask(templateDir, destinationDir, context, '1.0.0');
|
||||
await templatingTask(templateDir, destinationDir, context);
|
||||
expect(fs.existsSync('templatedApp/package.json')).toBe(true);
|
||||
expect(fs.existsSync('templatedApp/.dockerignore')).toBe(true);
|
||||
await expect(fs.readJson('templatedApp/backstage.json')).resolves.toEqual({
|
||||
version: '1.0.0',
|
||||
version: releaseVersion,
|
||||
});
|
||||
// catalog was populated with `context.name`
|
||||
expect(
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BACKSTAGE_JSON } from '@backstage/cli-common';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import handlebars from 'handlebars';
|
||||
@@ -23,7 +22,6 @@ import recursive from 'recursive-readdir';
|
||||
import {
|
||||
basename,
|
||||
dirname,
|
||||
join,
|
||||
resolve as resolvePath,
|
||||
relative as relativePath,
|
||||
} from 'path';
|
||||
@@ -86,7 +84,6 @@ export async function templatingTask(
|
||||
templateDir: string,
|
||||
destinationDir: string,
|
||||
context: any,
|
||||
version: string,
|
||||
) {
|
||||
const files = await recursive(templateDir).catch(error => {
|
||||
throw new Error(`Failed to read template directory: ${error.message}`);
|
||||
@@ -136,12 +133,6 @@ export async function templatingTask(
|
||||
});
|
||||
}
|
||||
}
|
||||
await Task.forItem('creating', BACKSTAGE_JSON, () =>
|
||||
fs.writeFile(
|
||||
join(destinationDir, BACKSTAGE_JSON),
|
||||
`{\n "version": ${JSON.stringify(version)}\n}\n`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,8 @@ Rollup will extract the value of the version field in each package at build time
|
||||
leaving any imports in place.
|
||||
*/
|
||||
|
||||
import { version as root } from '../../../../package.json';
|
||||
|
||||
import { version as appDefaults } from '../../../app-defaults/package.json';
|
||||
import { version as backendCommon } from '../../../backend-common/package.json';
|
||||
import { version as backendTasks } from '../../../backend-tasks/package.json';
|
||||
@@ -75,6 +77,7 @@ import { version as pluginTechdocsBackend } from '../../../../plugins/techdocs-b
|
||||
import { version as pluginUserSettings } from '../../../../plugins/user-settings/package.json';
|
||||
|
||||
export const packageVersions = {
|
||||
root,
|
||||
'@backstage/app-defaults': appDefaults,
|
||||
'@backstage/backend-common': backendCommon,
|
||||
'@backstage/backend-tasks': backendTasks,
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"version": "{{version 'root'}}"
|
||||
}
|
||||
@@ -1,5 +1,16 @@
|
||||
# @backstage/plugin-catalog-backend
|
||||
|
||||
## 0.23.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Marked `GithubMultiOrgReaderProcessor` as stable, as it was moved to `/alpha` by mistake.
|
||||
- Fixed runtime resolution of the `/alpha` entry point.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.12.1
|
||||
- @backstage/catalog-model@0.12.1
|
||||
- @backstage/plugin-catalog-common@0.2.1
|
||||
|
||||
## 0.23.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -333,7 +333,6 @@ export type CatalogProcessorErrorResult = {
|
||||
export type CatalogProcessorLocationResult = {
|
||||
type: 'location';
|
||||
location: LocationSpec;
|
||||
optional?: boolean;
|
||||
};
|
||||
|
||||
// @public
|
||||
@@ -706,7 +705,7 @@ export type GithubMultiOrgConfig = Array<{
|
||||
userNamespace: string | undefined;
|
||||
}>;
|
||||
|
||||
// @alpha
|
||||
// @public
|
||||
export class GithubMultiOrgReaderProcessor implements CatalogProcessor {
|
||||
constructor(options: {
|
||||
integrations: ScmIntegrationRegistry;
|
||||
@@ -812,7 +811,7 @@ function inputError(
|
||||
// @public @deprecated (undocumented)
|
||||
function location_2(
|
||||
newLocation: LocationSpec,
|
||||
optional?: boolean,
|
||||
_optional?: boolean,
|
||||
): CatalogProcessorResult;
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -1007,10 +1006,7 @@ export const processingResult: Readonly<{
|
||||
atLocation: LocationSpec,
|
||||
message: string,
|
||||
) => CatalogProcessorResult;
|
||||
readonly location: (
|
||||
newLocation: LocationSpec,
|
||||
optional?: boolean | undefined,
|
||||
) => CatalogProcessorResult;
|
||||
readonly location: (newLocation: LocationSpec) => CatalogProcessorResult;
|
||||
readonly entity: (
|
||||
atLocation: LocationSpec,
|
||||
newEntity: Entity,
|
||||
@@ -1074,22 +1070,6 @@ export interface RouterOptions {
|
||||
refreshService?: RefreshService;
|
||||
}
|
||||
|
||||
// @public @deprecated
|
||||
export function runPeriodically(fn: () => any, delayMs: number): () => void;
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export class StaticLocationProcessor implements StaticLocationProcessor {
|
||||
constructor(staticLocations: LocationSpec[]);
|
||||
// (undocumented)
|
||||
static fromConfig(config: Config): StaticLocationProcessor;
|
||||
// (undocumented)
|
||||
readLocation(
|
||||
location: LocationSpec,
|
||||
_optional: boolean,
|
||||
emit: CatalogProcessorEmit,
|
||||
): Promise<boolean>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export class UrlReaderProcessor implements CatalogProcessor {
|
||||
constructor(options: { reader: UrlReader; logger: Logger_2 });
|
||||
|
||||
@@ -68,9 +68,9 @@ export function generalError(
|
||||
*/
|
||||
export function location(
|
||||
newLocation: LocationSpec,
|
||||
optional?: boolean,
|
||||
_optional?: boolean,
|
||||
): CatalogProcessorResult {
|
||||
return { type: 'location', location: newLocation, optional };
|
||||
return { type: 'location', location: newLocation };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -54,11 +54,8 @@ export const processingResult = Object.freeze({
|
||||
return { type: 'error', location: atLocation, error: new Error(message) };
|
||||
},
|
||||
|
||||
location(
|
||||
newLocation: LocationSpec,
|
||||
optional?: boolean,
|
||||
): CatalogProcessorResult {
|
||||
return { type: 'location', location: newLocation, optional };
|
||||
location(newLocation: LocationSpec): CatalogProcessorResult {
|
||||
return { type: 'location', location: newLocation };
|
||||
},
|
||||
|
||||
entity(atLocation: LocationSpec, newEntity: Entity): CatalogProcessorResult {
|
||||
|
||||
@@ -147,8 +147,6 @@ export type CatalogProcessorEmit = (generated: CatalogProcessorResult) => void;
|
||||
export type CatalogProcessorLocationResult = {
|
||||
type: 'location';
|
||||
location: LocationSpec;
|
||||
/** @deprecated Set `location.presence = 'optional'` instead */
|
||||
optional?: boolean;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
import {
|
||||
processingResult,
|
||||
CatalogProcessorEmit,
|
||||
LocationSpec,
|
||||
} from '../../api';
|
||||
|
||||
/**
|
||||
* @deprecated no longer in use, replaced by the ConfigLocationEntityProvider.
|
||||
* @public
|
||||
*/
|
||||
export class StaticLocationProcessor implements StaticLocationProcessor {
|
||||
static fromConfig(config: Config): StaticLocationProcessor {
|
||||
const locations: LocationSpec[] = [];
|
||||
|
||||
const lConfigs = config.getOptionalConfigArray('catalog.locations') ?? [];
|
||||
for (const lConfig of lConfigs) {
|
||||
const type = lConfig.getString('type');
|
||||
const target = lConfig.getString('target');
|
||||
locations.push({ type, target });
|
||||
}
|
||||
|
||||
return new StaticLocationProcessor(locations);
|
||||
}
|
||||
|
||||
constructor(private readonly staticLocations: LocationSpec[]) {}
|
||||
|
||||
async readLocation(
|
||||
location: LocationSpec,
|
||||
_optional: boolean,
|
||||
emit: CatalogProcessorEmit,
|
||||
): Promise<boolean> {
|
||||
if (location.type !== 'bootstrap') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const staticLocation of this.staticLocations) {
|
||||
emit(processingResult.location(staticLocation));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,5 @@ export type {
|
||||
PlaceholderResolverRead,
|
||||
PlaceholderResolverResolveUrl,
|
||||
} from './PlaceholderProcessor';
|
||||
export { StaticLocationProcessor } from './StaticLocationProcessor';
|
||||
export { UrlReaderProcessor } from './UrlReaderProcessor';
|
||||
export { parseEntityYaml } from '../util/parse';
|
||||
|
||||
@@ -40,10 +40,11 @@ import {
|
||||
import { buildOrgHierarchy } from '../util/org';
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* Extracts teams and users out of a multiple GitHub orgs namespaced per org.
|
||||
*
|
||||
* Be aware that this processor may not be compatible with future org structures in the catalog.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class GithubMultiOrgReaderProcessor implements CatalogProcessor {
|
||||
private readonly integrations: ScmIntegrationRegistry;
|
||||
|
||||
@@ -106,9 +106,8 @@ export class ProcessorOutputCollector {
|
||||
|
||||
this.deferredEntities.push({ entity, locationKey: location });
|
||||
} else if (i.type === 'location') {
|
||||
const presence = i.optional ? 'optional' : 'required';
|
||||
const entity = locationSpecToLocationEntity(
|
||||
{ presence, ...i.location },
|
||||
i.location,
|
||||
this.parentEntity,
|
||||
);
|
||||
const locationKey = getEntityLocationRef(entity);
|
||||
|
||||
@@ -14,5 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './runPeriodically';
|
||||
export * from './RecursivePartial';
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Runs a function repeatedly, with a fixed wait between invocations.
|
||||
*
|
||||
* Supports async functions, and silently ignores exceptions and rejections.
|
||||
*
|
||||
* @param fn - The function to run. May return a Promise.
|
||||
* @param delayMs - The delay between a completed function invocation and the
|
||||
* next.
|
||||
* @returns A function that, when called, stops the invocation loop.
|
||||
* @deprecated use \@backstage/backend-tasks package instead.
|
||||
* @public
|
||||
*/
|
||||
export function runPeriodically(fn: () => any, delayMs: number): () => void {
|
||||
let cancel: () => void;
|
||||
let cancelled = false;
|
||||
const cancellationPromise = new Promise<void>(resolve => {
|
||||
cancel = () => {
|
||||
resolve();
|
||||
cancelled = true;
|
||||
};
|
||||
});
|
||||
|
||||
const startRefresh = async () => {
|
||||
while (!cancelled) {
|
||||
try {
|
||||
await fn();
|
||||
} catch {
|
||||
// ignore intentionally
|
||||
}
|
||||
|
||||
await Promise.race([
|
||||
new Promise(resolve => setTimeout(resolve, delayMs)),
|
||||
cancellationPromise,
|
||||
]);
|
||||
}
|
||||
};
|
||||
startRefresh();
|
||||
|
||||
return cancel!;
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
# @backstage/plugin-catalog-common
|
||||
|
||||
## 0.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Fixed runtime resolution of the `/alpha` entry point.
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @backstage/plugin-catalog-react
|
||||
|
||||
## 0.8.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Fixed runtime resolution of the `/alpha` entry point.
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@0.12.1
|
||||
|
||||
## 0.8.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -477,16 +477,6 @@ export class MockStarredEntitiesApi implements StarredEntitiesApi {
|
||||
toggleStarred(entityRef: string): Promise<void>;
|
||||
}
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export function reduceCatalogFilters(
|
||||
filters: EntityFilter[],
|
||||
): Record<string, string | symbol | (string | symbol)[]>;
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export function reduceEntityFilters(
|
||||
filters: EntityFilter[],
|
||||
): (entity: Entity) => boolean;
|
||||
|
||||
// @public
|
||||
export interface StarredEntitiesApi {
|
||||
starredEntitie$(): Observable<Set<string>>;
|
||||
@@ -522,13 +512,6 @@ export function useEntity<TEntity extends Entity = Entity>(): {
|
||||
refresh?: VoidFunction;
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export const useEntityCompoundName: () => {
|
||||
kind: string;
|
||||
namespace: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export const useEntityFromUrl: () => EntityLoadingStatus;
|
||||
|
||||
@@ -544,11 +527,6 @@ export function useEntityList<
|
||||
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,
|
||||
>(): EntityListContextProps<EntityFilters>;
|
||||
|
||||
// @public @deprecated
|
||||
export function useEntityListProvider<
|
||||
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,
|
||||
>(): EntityListContextProps<EntityFilters>;
|
||||
|
||||
// @public
|
||||
export function useEntityOwnership(): {
|
||||
loading: boolean;
|
||||
|
||||
@@ -25,11 +25,9 @@ export type {
|
||||
EntityProviderProps,
|
||||
AsyncEntityProviderProps,
|
||||
} from './useEntity';
|
||||
export { useEntityCompoundName } from './useEntityCompoundName';
|
||||
export {
|
||||
EntityListContext,
|
||||
EntityListProvider,
|
||||
useEntityListProvider,
|
||||
useEntityList,
|
||||
} from './useEntityListProvider';
|
||||
export type {
|
||||
|
||||
@@ -14,7 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
errorApiRef,
|
||||
useApi,
|
||||
useRouteRefParams,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import {
|
||||
createVersionedContext,
|
||||
createVersionedValueMap,
|
||||
@@ -24,7 +28,7 @@ import React, { ReactNode, useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import useAsyncRetry from 'react-use/lib/useAsyncRetry';
|
||||
import { catalogApiRef } from '../api';
|
||||
import { useEntityCompoundName } from './useEntityCompoundName';
|
||||
import { entityRouteRef } from '../routes';
|
||||
|
||||
/** @public */
|
||||
export type EntityLoadingStatus<TEntity extends Entity = Entity> = {
|
||||
@@ -104,7 +108,7 @@ export const EntityProvider = ({ entity, children }: EntityProviderProps) => (
|
||||
* @deprecated will be deleted shortly due to low external usage, re-implement if needed.
|
||||
*/
|
||||
export const useEntityFromUrl = (): EntityLoadingStatus => {
|
||||
const { kind, namespace, name } = useEntityCompoundName();
|
||||
const { kind, namespace, name } = useRouteRefParams(entityRouteRef);
|
||||
const navigate = useNavigate();
|
||||
const errorApi = useApi(errorApiRef);
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { entityRouteRef } from '../routes';
|
||||
import { useRouteRefParams } from '@backstage/core-plugin-api';
|
||||
|
||||
/**
|
||||
* Grabs entity kind, namespace, and name from the location
|
||||
* @public
|
||||
* @deprecated use {@link @backstage/core-plugin-api#useRouteRefParams} instead
|
||||
*/
|
||||
export const useEntityCompoundName = () => {
|
||||
const { kind, namespace, name } = useRouteRefParams(entityRouteRef);
|
||||
return { kind, namespace, name };
|
||||
};
|
||||
@@ -195,7 +195,7 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({
|
||||
});
|
||||
const newParams = qs.stringify(
|
||||
{ ...oldParams, filters: queryParams },
|
||||
{ addQueryPrefix: true },
|
||||
{ addQueryPrefix: true, arrayFormat: 'repeat' },
|
||||
);
|
||||
const newUrl = `${window.location.pathname}${newParams}`;
|
||||
// We use direct history manipulation since useSearchParams and
|
||||
@@ -249,20 +249,6 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook for interacting with the entity list context provided by the {@link EntityListProvider}.
|
||||
* @public
|
||||
* @deprecated use {@link useEntityList} instead.
|
||||
*/
|
||||
export function useEntityListProvider<
|
||||
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,
|
||||
>(): EntityListContextProps<EntityFilters> {
|
||||
const context = useContext(EntityListContext);
|
||||
if (!context)
|
||||
throw new Error('useEntityList must be used within EntityListProvider');
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for interacting with the entity list context provided by the {@link EntityListProvider}.
|
||||
* @public
|
||||
|
||||
@@ -30,5 +30,12 @@ export * from './filters';
|
||||
export { entityRouteParams, entityRouteRef } from './routes';
|
||||
export * from './testUtils';
|
||||
export * from './types';
|
||||
export * from './utils';
|
||||
export * from './overridableComponents';
|
||||
export {
|
||||
getEntityMetadataEditUrl,
|
||||
getEntityMetadataViewUrl,
|
||||
getEntityRelations,
|
||||
getEntitySourceLocation,
|
||||
isOwnerOf,
|
||||
} from './utils';
|
||||
export type { EntitySourceLocation } from './utils';
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntityFilter } from '../types';
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated will be made private.
|
||||
*/
|
||||
export function reduceCatalogFilters(
|
||||
filters: EntityFilter[],
|
||||
): Record<string, string | symbol | (string | symbol)[]> {
|
||||
@@ -32,10 +28,6 @@ export function reduceCatalogFilters(
|
||||
}, {} as Record<string, string | symbol | (string | symbol)[]>);
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated will be made private.
|
||||
*/
|
||||
export function reduceEntityFilters(
|
||||
filters: EntityFilter[],
|
||||
): (entity: Entity) => boolean {
|
||||
|
||||
@@ -26,9 +26,6 @@ import { TableProps } from '@backstage/core-components';
|
||||
import { TabProps } from '@material-ui/core';
|
||||
import { UserListFilterKind } from '@backstage/plugin-catalog-react';
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export function AboutCard(props: AboutCardProps): JSX.Element;
|
||||
|
||||
// @public
|
||||
export interface AboutCardProps {
|
||||
// (undocumented)
|
||||
@@ -104,12 +101,6 @@ export const catalogPlugin: BackstagePlugin<
|
||||
}
|
||||
>;
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export const CatalogResultListItem: typeof CatalogSearchResultListItem;
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export type CatalogResultListItemProps = CatalogSearchResultListItemProps;
|
||||
|
||||
// @public (undocumented)
|
||||
export function CatalogSearchResultListItem(
|
||||
props: CatalogSearchResultListItemProps,
|
||||
|
||||
@@ -72,7 +72,7 @@ const useStyles = makeStyles({
|
||||
});
|
||||
|
||||
/**
|
||||
* Props for {@link AboutCard}.
|
||||
* Props for {@link EntityAboutCard}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
@@ -81,8 +81,7 @@ export interface AboutCardProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Please use EntityAboutCard instead
|
||||
* Exported publicly via the EntityAboutCard
|
||||
*/
|
||||
export function AboutCard(props: AboutCardProps) {
|
||||
const { variant } = props;
|
||||
|
||||
-12
@@ -73,15 +73,3 @@ export function CatalogSearchResultListItem(
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated use {@link CatalogSearchResultListItemProps} instead
|
||||
*/
|
||||
export type CatalogResultListItemProps = CatalogSearchResultListItemProps;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated use {@link CatalogSearchResultListItem} instead
|
||||
*/
|
||||
export const CatalogResultListItem = CatalogSearchResultListItem;
|
||||
|
||||
@@ -14,11 +14,5 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
CatalogSearchResultListItem,
|
||||
CatalogResultListItem,
|
||||
} from './CatalogSearchResultListItem';
|
||||
export type {
|
||||
CatalogSearchResultListItemProps,
|
||||
CatalogResultListItemProps,
|
||||
} from './CatalogSearchResultListItem';
|
||||
export { CatalogSearchResultListItem } from './CatalogSearchResultListItem';
|
||||
export type { CatalogSearchResultListItemProps } from './CatalogSearchResultListItem';
|
||||
|
||||
@@ -33,15 +33,16 @@ import {
|
||||
attachComponentData,
|
||||
IconComponent,
|
||||
useElementFilter,
|
||||
useRouteRefParams,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import {
|
||||
EntityRefLinks,
|
||||
entityRouteRef,
|
||||
FavoriteEntity,
|
||||
getEntityRelations,
|
||||
InspectEntityDialog,
|
||||
UnregisterEntityDialog,
|
||||
useAsyncEntity,
|
||||
useEntityCompoundName,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { Box, TabProps } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
@@ -176,7 +177,7 @@ export const EntityLayout = (props: EntityLayoutProps) => {
|
||||
UNSTABLE_contextMenuOptions,
|
||||
children,
|
||||
} = props;
|
||||
const { kind, namespace, name } = useEntityCompoundName();
|
||||
const { kind, namespace, name } = useRouteRefParams(entityRouteRef);
|
||||
const { entity, loading, error } = useAsyncEntity();
|
||||
const location = useLocation();
|
||||
const routes = useElementFilter(
|
||||
|
||||
@@ -22,7 +22,12 @@
|
||||
|
||||
export * from './apis';
|
||||
|
||||
export * from './components/AboutCard';
|
||||
export type {
|
||||
AboutCardProps,
|
||||
AboutContentProps,
|
||||
AboutFieldProps,
|
||||
} from './components/AboutCard';
|
||||
export { AboutContent, AboutField } from './components/AboutCard';
|
||||
export * from './components/CatalogKindHeader';
|
||||
export * from './components/CatalogSearchResultListItem';
|
||||
export * from './components/CatalogTable';
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.57",
|
||||
"react-router-dom": "6.0.0-beta.0",
|
||||
"@react-hookz/web": "^12.3.0"
|
||||
"@react-hookz/web": "^13.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17.0.0"
|
||||
|
||||
@@ -248,7 +248,7 @@ describe('OwnershipCard', () => {
|
||||
|
||||
expect(getByText('OPENAPI').closest('a')).toHaveAttribute(
|
||||
'href',
|
||||
'/create/?filters%5Bkind%5D=API&filters%5Btype%5D=openapi&filters%5Bowners%5D%5B0%5D=my-team&filters%5Buser%5D=all',
|
||||
'/create/?filters%5Bkind%5D=API&filters%5Btype%5D=openapi&filters%5Bowners%5D=my-team&filters%5Buser%5D=all',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -295,7 +295,7 @@ describe('OwnershipCard', () => {
|
||||
|
||||
expect(getByText('OPENAPI').closest('a')).toHaveAttribute(
|
||||
'href',
|
||||
'/create/?filters%5Bkind%5D=API&filters%5Btype%5D=openapi&filters%5Bowners%5D%5B0%5D=user%3Athe-user&filters%5Bowners%5D%5B1%5D=my-team&filters%5Buser%5D=all',
|
||||
'/create/?filters%5Bkind%5D=API&filters%5Btype%5D=openapi&filters%5Bowners%5D=user%3Athe-user&filters%5Bowners%5D=my-team&filters%5Buser%5D=all',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -120,9 +120,14 @@ const getQueryParams = (
|
||||
const user = owner as UserEntity;
|
||||
filters.owners = [...filters.owners, ...user.spec.memberOf];
|
||||
}
|
||||
const queryParams = qs.stringify({
|
||||
filters,
|
||||
});
|
||||
const queryParams = qs.stringify(
|
||||
{
|
||||
filters,
|
||||
},
|
||||
{
|
||||
arrayFormat: 'repeat',
|
||||
},
|
||||
);
|
||||
|
||||
return queryParams;
|
||||
};
|
||||
|
||||
@@ -14,16 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
catalogApiRef,
|
||||
useEntityCompoundName,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { useApi, useRouteRefParams } from '@backstage/core-plugin-api';
|
||||
|
||||
export function useCatalogEntity() {
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
const { namespace, name } = useEntityCompoundName();
|
||||
const { namespace, name } = useRouteRefParams(entityRouteRef);
|
||||
|
||||
const {
|
||||
value: entity,
|
||||
|
||||
@@ -233,6 +233,10 @@ export function createPublishGithubAction(options: {
|
||||
description?: string | undefined;
|
||||
access?: string | undefined;
|
||||
defaultBranch?: string | undefined;
|
||||
deleteBranchOnMerge?: boolean | undefined;
|
||||
allowRebaseMerge?: boolean | undefined;
|
||||
allowSquashMerge?: boolean | undefined;
|
||||
allowMergeCommit?: boolean | undefined;
|
||||
sourcePath?: string | undefined;
|
||||
requireCodeOwnerReviews?: boolean | undefined;
|
||||
repoVisibility?: 'internal' | 'private' | 'public' | undefined;
|
||||
|
||||
@@ -88,6 +88,10 @@ describe('publish:github', () => {
|
||||
name: 'repo',
|
||||
org: 'owner',
|
||||
private: true,
|
||||
delete_branch_on_merge: false,
|
||||
allow_squash_merge: true,
|
||||
allow_merge_commit: true,
|
||||
allow_rebase_merge: true,
|
||||
visibility: 'private',
|
||||
});
|
||||
|
||||
@@ -103,6 +107,10 @@ describe('publish:github', () => {
|
||||
name: 'repo',
|
||||
org: 'owner',
|
||||
private: false,
|
||||
delete_branch_on_merge: false,
|
||||
allow_squash_merge: true,
|
||||
allow_merge_commit: true,
|
||||
allow_rebase_merge: true,
|
||||
visibility: 'public',
|
||||
});
|
||||
});
|
||||
@@ -123,6 +131,10 @@ describe('publish:github', () => {
|
||||
description: 'description',
|
||||
name: 'repo',
|
||||
private: true,
|
||||
delete_branch_on_merge: false,
|
||||
allow_squash_merge: true,
|
||||
allow_merge_commit: true,
|
||||
allow_rebase_merge: true,
|
||||
});
|
||||
|
||||
await action.handler({
|
||||
@@ -138,6 +150,10 @@ describe('publish:github', () => {
|
||||
description: 'description',
|
||||
name: 'repo',
|
||||
private: false,
|
||||
delete_branch_on_merge: false,
|
||||
allow_squash_merge: true,
|
||||
allow_merge_commit: true,
|
||||
allow_rebase_merge: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -46,6 +46,10 @@ export function createPublishGithubAction(options: {
|
||||
description?: string;
|
||||
access?: string;
|
||||
defaultBranch?: string;
|
||||
deleteBranchOnMerge?: boolean;
|
||||
allowRebaseMerge?: boolean;
|
||||
allowSquashMerge?: boolean;
|
||||
allowMergeCommit?: boolean;
|
||||
sourcePath?: string;
|
||||
requireCodeOwnerReviews?: boolean;
|
||||
repoVisibility?: 'private' | 'internal' | 'public';
|
||||
@@ -94,6 +98,26 @@ export function createPublishGithubAction(options: {
|
||||
type: 'string',
|
||||
description: `Sets the default branch on the repository. The default value is 'master'`,
|
||||
},
|
||||
deleteBranchOnMerge: {
|
||||
title: 'Delete Branch On Merge',
|
||||
type: 'boolean',
|
||||
description: `Delete the branch after merging the PR. The default value is 'false'`,
|
||||
},
|
||||
allowMergeCommit: {
|
||||
title: 'Allow Merge Commits',
|
||||
type: 'boolean',
|
||||
description: `Allow merge commits. The default value is 'true'`,
|
||||
},
|
||||
allowSquashMerge: {
|
||||
title: 'Allow Squash Merges',
|
||||
type: 'boolean',
|
||||
description: `Allow squash merges. The default value is 'true'`,
|
||||
},
|
||||
allowRebaseMerge: {
|
||||
title: 'Allow Rebase Merges',
|
||||
type: 'boolean',
|
||||
description: `Allow rebase merges. The default value is 'true'`,
|
||||
},
|
||||
sourcePath: {
|
||||
title: 'Source Path',
|
||||
description:
|
||||
@@ -156,6 +180,10 @@ export function createPublishGithubAction(options: {
|
||||
requireCodeOwnerReviews = false,
|
||||
repoVisibility = 'private',
|
||||
defaultBranch = 'master',
|
||||
deleteBranchOnMerge = false,
|
||||
allowMergeCommit = true,
|
||||
allowSquashMerge = true,
|
||||
allowRebaseMerge = true,
|
||||
collaborators,
|
||||
topics,
|
||||
token: providedToken,
|
||||
@@ -188,11 +216,19 @@ export function createPublishGithubAction(options: {
|
||||
private: repoVisibility === 'private',
|
||||
visibility: repoVisibility,
|
||||
description: description,
|
||||
delete_branch_on_merge: deleteBranchOnMerge,
|
||||
allow_merge_commit: allowMergeCommit,
|
||||
allow_squash_merge: allowSquashMerge,
|
||||
allow_rebase_merge: allowRebaseMerge,
|
||||
})
|
||||
: client.rest.repos.createForAuthenticatedUser({
|
||||
name: repo,
|
||||
private: repoVisibility === 'private',
|
||||
description: description,
|
||||
delete_branch_on_merge: deleteBranchOnMerge,
|
||||
allow_merge_commit: allowMergeCommit,
|
||||
allow_squash_merge: allowSquashMerge,
|
||||
allow_rebase_merge: allowRebaseMerge,
|
||||
});
|
||||
|
||||
const { data: newRepo } = await repoCreationPromise;
|
||||
|
||||
@@ -314,6 +314,14 @@ export interface ScaffolderGetIntegrationsListResponse {
|
||||
}[];
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type ScaffolderOutputLink = {
|
||||
title?: string;
|
||||
icon?: string;
|
||||
url?: string;
|
||||
entityRef?: string;
|
||||
};
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ScaffolderPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
@@ -372,12 +380,8 @@ export type ScaffolderTask = {
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ScaffolderTaskOutput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export type ScaffolderTaskOutput = {
|
||||
entityRef?: string;
|
||||
remoteUrl?: string;
|
||||
links?: ScaffolderOutputLink[];
|
||||
} & {
|
||||
[key: string]: unknown;
|
||||
@@ -451,8 +455,4 @@ export const TemplateTypePicker: () => JSX.Element | null;
|
||||
|
||||
// @public
|
||||
export const useTemplateSecrets: () => ScaffolderUseTemplateSecrets;
|
||||
|
||||
// Warnings were encountered during analysis:
|
||||
//
|
||||
// src/types.d.ts:32:5 - (ae-forgotten-export) The symbol "ScaffolderOutputLink" needs to be exported by the entry point index.d.ts
|
||||
```
|
||||
|
||||
@@ -217,12 +217,8 @@ export const TaskStatusStepper = memo(
|
||||
},
|
||||
);
|
||||
|
||||
const hasLinks = ({
|
||||
entityRef,
|
||||
remoteUrl,
|
||||
links = [],
|
||||
}: ScaffolderTaskOutput): boolean =>
|
||||
!!(entityRef || remoteUrl || links.length > 0);
|
||||
const hasLinks = ({ links = [] }: ScaffolderTaskOutput): boolean =>
|
||||
links.length > 0;
|
||||
|
||||
/**
|
||||
* TaskPageProps for constructing a TaskPage
|
||||
|
||||
@@ -26,43 +26,6 @@ describe('TaskPageLinks', () => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('renders the entityRef link', async () => {
|
||||
const output = { entityRef: 'Component:default/my-app' };
|
||||
const { findByText } = await renderInTestApp(
|
||||
<TaskPageLinks output={output} />,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name/*': entityRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const element = await findByText('Open in catalog');
|
||||
|
||||
expect(element).toBeInTheDocument();
|
||||
expect(element).toHaveAttribute(
|
||||
'href',
|
||||
'/catalog/default/Component/my-app',
|
||||
);
|
||||
});
|
||||
|
||||
it('renders the remoteUrl link', async () => {
|
||||
const output = { remoteUrl: 'https://remote.url' };
|
||||
const { findByText } = await renderInTestApp(
|
||||
<TaskPageLinks output={output} />,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name/*': entityRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const element = await findByText('Repo');
|
||||
|
||||
expect(element).toBeInTheDocument();
|
||||
expect(element).toHaveAttribute('href', 'https://remote.url');
|
||||
});
|
||||
|
||||
it('renders further links', async () => {
|
||||
const output = {
|
||||
links: [
|
||||
|
||||
@@ -28,29 +28,13 @@ type TaskPageLinksProps = {
|
||||
};
|
||||
|
||||
export const TaskPageLinks = ({ output }: TaskPageLinksProps) => {
|
||||
const { entityRef: entityRefOutput, remoteUrl } = output;
|
||||
let { links = [] } = output;
|
||||
const { links = [] } = output;
|
||||
const app = useApp();
|
||||
const entityRoute = useRouteRef(entityRouteRef);
|
||||
|
||||
const iconResolver = (key?: string): IconComponent =>
|
||||
key ? app.getSystemIcon(key) ?? LanguageIcon : LanguageIcon;
|
||||
|
||||
if (remoteUrl) {
|
||||
links = [{ url: remoteUrl, title: 'Repo' }, ...links];
|
||||
}
|
||||
|
||||
if (entityRefOutput) {
|
||||
links = [
|
||||
{
|
||||
entityRef: entityRefOutput,
|
||||
title: 'Open in catalog',
|
||||
icon: 'catalog',
|
||||
},
|
||||
...links,
|
||||
];
|
||||
}
|
||||
|
||||
return (
|
||||
<Box px={3} pb={3}>
|
||||
{links
|
||||
|
||||
@@ -28,6 +28,7 @@ export type {
|
||||
ScaffolderApi,
|
||||
ScaffolderGetIntegrationsListOptions,
|
||||
ScaffolderGetIntegrationsListResponse,
|
||||
ScaffolderOutputLink,
|
||||
ScaffolderScaffoldOptions,
|
||||
ScaffolderScaffoldResponse,
|
||||
ScaffolderStreamLogsOptions,
|
||||
|
||||
@@ -43,18 +43,16 @@ export type ListActionsResponse = Array<{
|
||||
};
|
||||
}>;
|
||||
|
||||
type ScaffolderOutputLink = {
|
||||
/** @public */
|
||||
export type ScaffolderOutputLink = {
|
||||
title?: string;
|
||||
icon?: string;
|
||||
url?: string;
|
||||
entityRef?: string;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type ScaffolderTaskOutput = {
|
||||
/** @deprecated use the `links` property to link out to relevant resources */
|
||||
entityRef?: string;
|
||||
/** @deprecated use the `links` property to link out to relevant resources */
|
||||
remoteUrl?: string;
|
||||
links?: ScaffolderOutputLink[];
|
||||
} & {
|
||||
[key: string]: unknown;
|
||||
|
||||
@@ -4851,6 +4851,13 @@
|
||||
dependencies:
|
||||
"@react-hookz/deep-equal" "^1.0.1"
|
||||
|
||||
"@react-hookz/web@^13.0.0":
|
||||
version "13.0.0"
|
||||
resolved "https://registry.npmjs.org/@react-hookz/web/-/web-13.0.0.tgz#7c4d54fb4c1edf885879914d719e35086964e46e"
|
||||
integrity sha512-sAMTOiOnAvxpJtHea7Vk+2+KJdTC/o77wPII+glnH+astNUSSiduptLaMPeoIXXwqjfJT+IJ9JzWyZPrLpdyFw==
|
||||
dependencies:
|
||||
"@react-hookz/deep-equal" "^1.0.1"
|
||||
|
||||
"@rjsf/core@^3.2.1":
|
||||
version "3.2.1"
|
||||
resolved "https://registry.npmjs.org/@rjsf/core/-/core-3.2.1.tgz#8a7b24c9a6f01f0ecb093fdfc777172c12b1b009"
|
||||
@@ -17906,10 +17913,10 @@ minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4:
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimatch@5.0.0, minimatch@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.0.0.tgz#281d8402aaaeed18a9e8406ad99c46a19206c6ef"
|
||||
integrity sha512-EU+GCVjXD00yOUf1TwAHVP7v3fBD3A8RkkPYsWWKGWesxM/572sL53wJQnHxquHlRhYUV36wHkqrN8cdikKc2g==
|
||||
minimatch@5.0.1, minimatch@^5.0.0:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b"
|
||||
integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==
|
||||
dependencies:
|
||||
brace-expansion "^2.0.1"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user