Merge branch 'master' into read-remote-config

This commit is contained in:
Muhammad Hamadto
2021-10-29 15:26:03 +11:00
committed by GitHub
291 changed files with 3246 additions and 1961 deletions
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-kubernetes': patch
---
The Kubernetes plugin will now re-fetch the kubernetes objects every ten seconds (not current configurable), this allows users to track the progress of deployments without refreshing the browser.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-sentry': patch
---
feature: sentry-plugin allows passing search query for listing issues
-9
View File
@@ -1,9 +0,0 @@
---
'@backstage/test-utils': patch
'@backstage/test-utils-core': patch
---
Migrates all utility methods from `test-utils-core` into `test-utils` and delete exports from the old package.
This should have no impact since this package is considered internal and have no usages outside core packages.
Notable changes are that the testing tool `msw.setupDefaultHandlers()` have been deprecated in favour of `setupRequestMockHandlers()`.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-techdocs': patch
---
Reader will now scroll to the top of the page when navigating between pages
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-scaffolder': patch
---
Surfacing all components of the ScaffolderPage outside of the plugin so you can customize the page
-10
View File
@@ -1,10 +0,0 @@
---
'@backstage/plugin-azure-devops': patch
---
Azure DevOps frontend refactoring items from issue #7641
- Remove backend setup documentation and linked to the Azure DevOps backend plugin for these instructions
- Improved documentation to be easier to expand with new features in the future
- Removed Router based on feedback from maintainers
- Added tests for `getBuildResultComponent` and `getBuildStateComponent` from the BuildTable
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-scaffolder': patch
---
Scaffolder: Enable back Template action buttons if template fails to execute
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-azure-devops-backend': patch
---
Added duration (startTime and finishTime) and identity (uniqueName) to the RepoBuild results. Also did a bit of refactoring to help finish up the backend items in issue #7641
-26
View File
@@ -1,26 +0,0 @@
---
'@backstage/backend-common': patch
'@backstage/catalog-model': patch
'@backstage/cli': patch
'@backstage/config-loader': patch
'@backstage/core-app-api': patch
'@backstage/errors': patch
'@backstage/search-common': patch
'@backstage/test-utils': patch
'@backstage/plugin-app-backend': patch
'@backstage/plugin-catalog-backend': patch
'@backstage/plugin-catalog-backend-module-ldap': patch
'@backstage/plugin-catalog-graphql': patch
'@backstage/plugin-catalog-react': patch
'@backstage/plugin-config-schema': patch
'@backstage/plugin-scaffolder': patch
'@backstage/plugin-scaffolder-backend': patch
'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch
'@backstage/plugin-scaffolder-backend-module-rails': patch
'@backstage/plugin-scaffolder-backend-module-yeoman': patch
'@backstage/plugin-scaffolder-common': patch
'@backstage/plugin-search': patch
'@backstage/plugin-shortcuts': patch
---
Switch to use the json and observable types from `@backstage/types`
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/techdocs-common': patch
---
Support optional bucketRootPath configuration parameter in S3 and GCS publishers
-8
View File
@@ -1,8 +0,0 @@
---
'@backstage/config': patch
'@backstage/core-app-api': patch
'@backstage/core-plugin-api': patch
'@backstage/plugin-scaffolder-backend': patch
---
Start using the new `@backstage/types` package. Initially, this means using the `Observable` and `Json*` types from there. The types also remain in their old places but deprecated, and will be removed in a future release.
-7
View File
@@ -1,7 +0,0 @@
---
'@backstage/config': patch
'@backstage/core-app-api': patch
'@backstage/core-plugin-api': patch
---
Replace usage of test-utils-core with test-utils
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-search-backend-node': patch
---
Handle special case when filter array has single value optimizing Lunr search behaviour.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-sentry': patch
---
fix: sentry-plugin correction for allowed period values
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/core-components': patch
---
Deprecated `DismissbleBannerClassKey` and fixed the typo to make `DismissableBannerClassKey`
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/errors': patch
---
Add `stringifyError` that is useful for logging e.g. `Something went wrong, ${stringifyError(e)}`
+4 -2
View File
@@ -7,7 +7,7 @@ on:
paths:
- '.github/workflows/e2e-win.yml'
- 'packages/cli/**'
- 'packages/e2e/**'
- 'packages/e2e-test/**'
- 'packages/create-app/**'
jobs:
@@ -21,7 +21,7 @@ jobs:
env:
CI: true
NODE_OPTIONS: --max-old-space-size=4096
NODE_OPTIONS: --max-old-space-size=8192
name: Node ${{ matrix.node-version }} on ${{ matrix.os }}
steps:
@@ -48,3 +48,5 @@ jobs:
run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli
- name: run E2E test
run: yarn e2e-test run
env:
DEBUG: zombie
+3
View File
@@ -59,3 +59,6 @@
| [Box](https://www.box.com) | [@kielosz](https://github.com/kielosz), [@jluk-box](https://github.com/jluk-box), [@ptychu](https://github.com/ptychu), [@alexrybch](https://github.com/alexrybch), [@szubster](https://github.com/szubster) | Developer portal for service catalog, integration with internal systems, new service onboarding. |
| [Bazaarvoice](https://www.bazaarvoice.com) | [@niallmccullagh](https://github.com/niallmccullagh) | Developer portal for service catalog and scaffolds, publishing Github docs and API documentation, visualising our internal tech radar and our product engineering org structure. |
| [Krateo PlatformOps](https://www.krateo.io) | [@projectkerberus](https://github.com/projectkerberus) | A multi-cloud control plane to create, manage and deploy any kind of resource easily and centrally via a Developer Portal that centralizes via a self-service catalog the templating and ownership of services, the available documentation, the overview of the components that compose an entire domain and all the data of the service lifecycle. |
| [Adevinta](https://www.adevinta.com) | [Ray Sinnema](https://github.com/RemonSinnema) | Showcase shared services to our internal customers. |
| [Splunk](https://www.splunk.com) | [@tonytamsf](https://github.com/tonytamsf) | Developer portal as a centralized place to find people, services, documentation, escalation policies and give bravos. This portal is also being used as a centralized search engine for engineering specific documentation.|
| [SoundCloud](https://www.soundcloud.com) | [Julio Zynger](https://github.com/julioz) | Developer portal as a [humane registry](https://martinfowler.com/bliki/HumaneRegistry.html) for the organization: catalog of people, services, documentation, feature toggles, escalation policies, etc.|
+2
View File
@@ -2,6 +2,8 @@
# [Backstage](https://backstage.io)
> 🏖 All of the maintainers will be taking a wellness break Nov. 15. The repo and Discord may be quieter than usual, but not to worry. Well have coverage plans in place and be back in full force, rested and restored, on Nov. 8. 🏖
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![CNCF Status](https://img.shields.io/badge/cncf%20status-sandbox-blue.svg)](https://www.cncf.io/projects)
[![Main CI Build](https://github.com/backstage/backstage/workflows/Main%20Master%20Build/badge.svg)](https://github.com/backstage/backstage/actions?query=workflow%3A%22Main+Master+Build%22)
@@ -156,4 +156,16 @@ return await createRouter({
});
```
### List of custom action packages
Here is a list of Open Source custom actions that you can add to your Backstage
scaffolder backend:
| Name | Package | Owner |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| Yeoman | [plugin-scaffolder-backend-module-yeoman](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend-module-yeoman) | [Backstage](https://backstage.io) |
| Cookiecutter | [plugin-scaffolder-backend-module-cookiecutter](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend-module-cookiecutter) | [Backstage](https://backstage.io) |
| Rails | [plugin-scaffolder-backend-module-rails](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend-module-rails) | [Backstage](https://backstage.io) |
| HTTP requests | [scaffolder-backend-module-http-request](https://www.npmjs.com/package/@roadiehq/scaffolder-backend-module-http-request) | [Roadie](https://roadie.io) |
Have fun! 🚀
@@ -27,20 +27,27 @@ You can create your own Field Extension by using the
```tsx
//packages/app/scaffolder/MyCustomExtension/MyCustomExtension.tsx
import React from 'react';
import { FieldProps, FieldValidation } from '@rjsf/core';
import FormControl from '@material-ui/core/FormControl';
import { KubernetesValidatorFunctions } from '@backstage/catalog-model';
/*
This is the actual component that will get rendered in the form
*/
export const MyCustomExtension = ({ onChange, required }: FieldProps<string>) => {
export const MyCustomExtension = ({
onChange,
rawErrors,
required,
formData,
}: FieldProps<string>) => {
return (
<FormControl
<FormControl
margin="normal"
required={required}
error={rawErrors?.length > 0 && !formData}
onChange={onChange}
>
)
/>
);
};
/*
@@ -63,10 +70,10 @@ export const myCustomValidation = (
```tsx
// packages/app/scaffolder/MyCustomExtension/extensions.ts
/*
/*
This is where the magic happens and creates the custom field extension.
Note that if you're writing extensions part of a separate plugin,
Note that if you're writing extensions part of a separate plugin,
then please use `plugin.provide` from there instead and export it part of your `plugin.ts` rather than re-using the `scaffolder.plugin`.
*/
-1
View File
@@ -45,7 +45,6 @@
},
"resolutions": {
"**/@graphql-codegen/cli/**/ws": "^7.4.6",
"**/@roadiehq/**/@backstage/core": "*",
"**/@roadiehq/**/@backstage/plugin-catalog": "*",
"**/@roadiehq/**/@backstage/catalog-model": "*",
"graphql-language-service-interface": "2.8.2",
+14
View File
@@ -1,5 +1,19 @@
# @backstage/backend-common
## 0.9.8
### Patch Changes
- 96cfa561eb: Adjusted some API exports
- 10615525f3: Switch to use the json and observable types from `@backstage/types`
- 1be8d2abdb: Any set configurations which have been tagged with a visibility 'secret', are now redacted from log lines.
- Updated dependencies
- @backstage/config@0.1.11
- @backstage/cli-common@0.1.5
- @backstage/errors@0.1.4
- @backstage/integration@0.6.9
- @backstage/config-loader@0.7.1
## 0.9.7
### Patch Changes
+9 -5
View File
@@ -211,6 +211,12 @@ export type ErrorHandlerOptions = {
logClientErrors?: boolean;
};
// @public
export type FromReadableArrayOptions = Array<{
data: Readable;
path: string;
}>;
// @public (undocumented)
export function getRootLogger(): winston.Logger;
@@ -419,15 +425,13 @@ export type ReadTreeResponse = {
etag: string;
};
// @public (undocumented)
// @public
export type ReadTreeResponseDirOptions = {
targetDir?: string;
};
// @public (undocumented)
export interface ReadTreeResponseFactory {
// Warning: (ae-forgotten-export) The symbol "FromReadableArrayOptions" needs to be exported by the entry point index.d.ts
//
// (undocumented)
fromReadableArray(
options: FromReadableArrayOptions,
@@ -442,7 +446,7 @@ export interface ReadTreeResponseFactory {
): Promise<ReadTreeResponse>;
}
// @public (undocumented)
// @public
export type ReadTreeResponseFactoryOptions = {
stream: Readable;
subpath?: string;
@@ -580,7 +584,7 @@ export type UrlReader = {
search(url: string, options?: SearchOptions): Promise<SearchResponse>;
};
// @public (undocumented)
// @public
export type UrlReaderPredicateTuple = {
predicate: (url: URL) => boolean;
reader: UrlReader;
+10 -9
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-common",
"description": "Common functionality library for Backstage backends",
"version": "0.9.7",
"version": "0.9.8",
"main": "src/index.ts",
"types": "src/index.ts",
"private": false,
@@ -29,13 +29,14 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/cli-common": "^0.1.4",
"@backstage/config": "^0.1.10",
"@backstage/config-loader": "^0.7.0",
"@backstage/errors": "^0.1.3",
"@backstage/integration": "^0.6.7",
"@backstage/cli-common": "^0.1.5",
"@backstage/config": "^0.1.11",
"@backstage/config-loader": "^0.7.1",
"@backstage/errors": "^0.1.4",
"@backstage/integration": "^0.6.9",
"@backstage/types": "^0.1.1",
"@google-cloud/storage": "^5.8.0",
"@lerna/project": "^4.0.0",
"@octokit/rest": "^18.5.3",
"@types/cors": "^2.8.6",
"@types/dockerode": "^3.2.1",
@@ -78,8 +79,8 @@
}
},
"devDependencies": {
"@backstage/cli": "^0.8.0",
"@backstage/test-utils": "^0.1.18",
"@backstage/cli": "^0.8.1",
"@backstage/test-utils": "^0.1.20",
"@types/archiver": "^5.1.0",
"@types/compression": "^1.7.0",
"@types/concat-stream": "^1.6.0",
@@ -99,7 +100,7 @@
"http-errors": "^1.7.3",
"jest": "^26.0.1",
"mock-fs": "^5.1.0",
"msw": "^0.29.0",
"msw": "^0.35.0",
"mysql2": "^2.2.5",
"recursive-readdir": "^2.2.2",
"supertest": "^6.1.3"
+48 -4
View File
@@ -18,11 +18,42 @@ import { resolve as resolvePath } from 'path';
import parseArgs from 'minimist';
import { Logger } from 'winston';
import { findPaths } from '@backstage/cli-common';
import { Config, ConfigReader } from '@backstage/config';
import {
loadConfigSchema,
loadConfig,
ConfigSchema,
ConfigTarget
} from '@backstage/config-loader';
import { AppConfig, Config, ConfigReader } from '@backstage/config';
import { JsonValue } from '@backstage/types';
import { ConfigTarget, loadConfig } from '@backstage/config-loader';
import { isValidUrl } from './urls';
import { setRootLoggerRedactionList } from './logging/rootLogger';
// Fetch the schema and get all the secrets to pass to the rootLogger for redaction
const updateRedactionList = (
schema: ConfigSchema,
configs: AppConfig[],
logger: Logger,
) => {
const secretAppConfigs = schema.process(configs, { visibility: ['secret'] });
const secretConfig = ConfigReader.fromConfigs(secretAppConfigs);
const values = new Set<string>();
const data = secretConfig.get();
JSON.parse(
JSON.stringify(data),
(_, v) => typeof v === 'string' && values.add(v),
);
logger.info(
`${values.size} secrets found in the config which will be redacted`,
);
setRootLoggerRedactionList(Array.from(values));
};
export class ObservableConfigProxy implements Config {
private config: Config = new ConfigReader({});
@@ -155,11 +186,20 @@ export async function loadBackendConfig(options: {
.flat()
.map(arg => (isValidUrl(arg) ? { url: arg } : { path: resolvePath(arg) }));
const config = new ObservableConfigProxy(options.logger);
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
// TODO(hhogg): This is fetching _all_ of the packages of the monorepo
// in order to find the secrets for redactions, however we only care about
// the backend ones, we need to find a way to exclude the frontend packages.
const { Project } = require('@lerna/project');
const project = new Project(paths.targetDir);
const packages = await project.getPackages();
const schema = await loadConfigSchema({
dependencies: packages.map((p: any) => p.name),
});
const config = new ObservableConfigProxy(options.logger);
const configs = await loadConfig({
configRoot: paths.targetRoot,
configPaths: [],
@@ -192,5 +232,9 @@ export async function loadBackendConfig(options: {
config.setConfig(ConfigReader.fromConfigs(configs));
// Subscribe to config changes and update the redaction list for logging
updateRedactionList(schema, configs, options.logger);
config.subscribe(() => updateRedactionList(schema, configs, options.logger));
return config;
}
+1 -1
View File
@@ -15,5 +15,5 @@
*/
export * from './formats';
export * from './rootLogger';
export { createRootLogger, getRootLogger, setRootLogger } from './rootLogger';
export * from './voidLogger';
@@ -15,7 +15,12 @@
*/
import * as winston from 'winston';
import { createRootLogger, getRootLogger, setRootLogger } from './rootLogger';
import {
createRootLogger,
getRootLogger,
setRootLogger,
setRootLoggerRedactionList,
} from './rootLogger';
describe('rootLogger', () => {
it('can replace the default logger', () => {
@@ -30,7 +35,20 @@ describe('rootLogger', () => {
);
});
describe('createRootLoger', () => {
it('redacts given secrets', () => {
const logger = createRootLogger();
jest.spyOn(logger, 'write');
setRootLoggerRedactionList(['SECRET-1', 'SECRET_2', 'SECRET.3']);
logger.info('Logging SECRET-1 and SECRET_2 and SECRET.3');
expect(logger.write).toHaveBeenCalledWith(
expect.objectContaining({
message: 'Logging [REDACTED] and [REDACTED] and [REDACTED]',
}),
);
});
describe('createRootLogger', () => {
it('creates a new logger', () => {
const oldLogger = getRootLogger();
const newLogger = createRootLogger();
@@ -18,8 +18,10 @@ import { merge } from 'lodash';
import * as winston from 'winston';
import { LoggerOptions } from 'winston';
import { coloredFormat } from './formats';
import { escapeRegExp } from '../util/escapeRegExp';
let rootLogger: winston.Logger;
let redactionRegExp: RegExp;
/** @public */
export function getRootLogger(): winston.Logger {
@@ -31,6 +33,30 @@ export function setRootLogger(newLogger: winston.Logger) {
rootLogger = newLogger;
}
export function setRootLoggerRedactionList(redactionList: string[]) {
if (redactionList.length) {
redactionRegExp = new RegExp(
`(${redactionList.map(escapeRegExp).join('|')})`,
'g',
);
}
}
/**
* A winston formatting function that finds occurrences of filteredKeys
* and replaces them with the corresponding identifier.
*/
function redactLogLine(info: winston.Logform.TransformableInfo) {
// TODO(hhogg): The logger is created before the config is loaded,
// because the logger is needed in the config loader. There is a risk of
// a secret being logged out during the config loading stage.
if (redactionRegExp) {
info.message = info.message.replace(redactionRegExp, '[REDACTED]');
}
return info;
}
/** @public */
export function createRootLogger(
options: winston.LoggerOptions = {},
@@ -41,6 +67,7 @@ export function createRootLogger(
{
level: env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format(redactLogLine)(),
env.NODE_ENV === 'production' ? winston.format.json() : coloredFormat,
),
defaultMeta: {
@@ -19,7 +19,7 @@ import {
AzureIntegration,
readAzureIntegrationConfig,
} from '@backstage/integration';
import { msw } from '@backstage/test-utils';
import { setupRequestMockHandlers } from '@backstage/test-utils';
import fs from 'fs-extra';
import mockFs from 'mock-fs';
import { rest } from 'msw';
@@ -51,7 +51,7 @@ describe('AzureUrlReader', () => {
});
const worker = setupServer();
msw.setupDefaultHandlers(worker);
setupRequestMockHandlers(worker);
describe('read', () => {
beforeEach(() => {
@@ -19,7 +19,7 @@ import {
BitbucketIntegration,
readBitbucketIntegrationConfig,
} from '@backstage/integration';
import { msw } from '@backstage/test-utils';
import { setupRequestMockHandlers } from '@backstage/test-utils';
import fs from 'fs-extra';
import mockFs from 'mock-fs';
import { rest } from 'msw';
@@ -72,7 +72,7 @@ describe('BitbucketUrlReader', () => {
});
const worker = setupServer();
msw.setupDefaultHandlers(worker);
setupRequestMockHandlers(worker);
describe('readUrl', () => {
worker.use(
@@ -16,7 +16,7 @@
import { ConfigReader } from '@backstage/config';
import { NotFoundError, NotModifiedError } from '@backstage/errors';
import { msw } from '@backstage/test-utils';
import { setupRequestMockHandlers } from '@backstage/test-utils';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { getVoidLogger } from '../logging';
@@ -28,7 +28,7 @@ const fetchUrlReader = new FetchUrlReader();
describe('FetchUrlReader', () => {
const worker = setupServer();
msw.setupDefaultHandlers(worker);
setupRequestMockHandlers(worker);
beforeEach(() => {
jest.clearAllMocks();
@@ -20,7 +20,7 @@ import {
GitHubIntegration,
readGitHubIntegrationConfig,
} from '@backstage/integration';
import { msw } from '@backstage/test-utils';
import { setupRequestMockHandlers } from '@backstage/test-utils';
import fs from 'fs-extra';
import mockFs from 'mock-fs';
import { rest } from 'msw';
@@ -73,7 +73,7 @@ const tmpDir = os.platform() === 'win32' ? 'C:\\tmp' : '/tmp';
describe('GithubUrlReader', () => {
const worker = setupServer();
msw.setupDefaultHandlers(worker);
setupRequestMockHandlers(worker);
beforeEach(() => {
mockFs({
@@ -15,7 +15,7 @@
*/
import { ConfigReader } from '@backstage/config';
import { msw } from '@backstage/test-utils';
import { setupRequestMockHandlers } from '@backstage/test-utils';
import fs from 'fs-extra';
import mockFs from 'mock-fs';
import { rest } from 'msw';
@@ -77,7 +77,7 @@ describe('GitlabUrlReader', () => {
});
const worker = setupServer();
msw.setupDefaultHandlers(worker);
setupRequestMockHandlers(worker);
describe('read', () => {
beforeEach(() => {
@@ -20,6 +20,7 @@ export { GithubUrlReader } from './GithubUrlReader';
export { GitlabUrlReader } from './GitlabUrlReader';
export { AwsS3UrlReader } from './AwsS3UrlReader';
export type {
FromReadableArrayOptions,
ReaderFactory,
ReadTreeOptions,
ReadTreeResponse,
+106 -37
View File
@@ -24,24 +24,41 @@ import { Config } from '@backstage/config';
* @public
*/
export type UrlReader = {
/* Used to read a single file and return its content. */
/**
* Reads a single file and return its content.
*/
read(url: string): Promise<Buffer>;
/**
* A replacement for the read method that supports options and complex responses.
* Reads a single file and return its content.
*
* Use this whenever it is available, as the read method will be deprecated and
* eventually removed in the future.
* @remarks
*
* This is a replacement for the read method that supports options and
* complex responses.
*
* Use this whenever it is available, as the read method will be
* deprecated and eventually removed in a future release.
*/
readUrl?(url: string, options?: ReadUrlOptions): Promise<ReadUrlResponse>;
/* Used to read a file tree and download as a directory. */
/**
* Reads a full or partial file tree.
*/
readTree(url: string, options?: ReadTreeOptions): Promise<ReadTreeResponse>;
/* Used to search a file in a tree using a glob pattern. */
/**
* Searches for a file in a tree using a glob pattern.
*/
search(url: string, options?: SearchOptions): Promise<SearchResponse>;
};
/** @public */
/**
* A predicate that decides whether a specific {@link UrlReader} can handle a
* given URL.
*
* @public
*/
export type UrlReaderPredicateTuple = {
predicate: (url: URL) => boolean;
reader: UrlReader;
@@ -49,7 +66,7 @@ export type UrlReaderPredicateTuple = {
/**
* A factory function that can read config to construct zero or more
* UrlReaders along with a predicate for when it should be used.
* {@link UrlReader}s along with a predicate for when it should be used.
*
* @public
*/
@@ -66,21 +83,28 @@ export type ReaderFactory = (options: {
*/
export type ReadUrlOptions = {
/**
* An etag can be provided to check whether readUrl's response has changed from a previous execution.
* An ETag which can be provided to check whether a
* {@link UrlReader.readUrl} response has changed from a previous execution.
*
* In the readUrl() response, an etag is returned along with the data. The etag is a unique identifer
* of the data, usually the commit SHA or etag from the target.
* @remarks
*
* When an etag is given in ReadUrlOptions, readUrl will first compare the etag against the etag
* on the target. If they match, readUrl will throw a NotModifiedError indicating that the readUrl
* response will not differ from the previous response which included this particular etag. If they
* do not match, readUrl will return the rest of ReadUrlResponse along with a new etag.
* In the {@link UrlReader.readUrl} response, an ETag is returned along with
* the data. The ETag is a unique identifier of the data, usually the commit
* SHA or ETag from the target.
*
* When an ETag is given in ReadUrlOptions, {@link UrlReader.readUrl} will
* first compare the ETag against the ETag of the target. If they match,
* {@link UrlReader.readUrl} will throw a
* {@link @backstage/errors#NotModifiedError} indicating that the response
* will not differ from the previous response which included this particular
* ETag. If they do not match, {@link UrlReader.readUrl} will return the rest
* of the response along with a new ETag.
*/
etag?: string;
};
/**
* A response object for readUrl operations.
* A response object for {@link UrlReader.readUrl} operations.
*
* @public
*/
@@ -92,13 +116,16 @@ export type ReadUrlResponse = {
/**
* Etag returned by content provider.
*
* @remarks
*
* Can be used to compare and cache responses when doing subsequent calls.
*/
etag?: string;
};
/**
* An options object for readTree operations.
* An options object for {@link UrlReader.readTree} operations.
*
* @public
*/
@@ -106,6 +133,8 @@ export type ReadTreeOptions = {
/**
* A filter that can be used to select which files should be included.
*
* @remarks
*
* The path passed to the filter function is the relative path from the URL
* that the file tree is fetched from, without any leading '/'.
*
@@ -113,56 +142,82 @@ export type ReadTreeOptions = {
* at https://github.com/my/repo/blob/master/my-dir/my-subdir/my-file.txt will
* be represented as my-subdir/my-file.txt
*
* If no filter is provided all files are extracted.
* If no filter is provided, all files are extracted.
*/
filter?(path: string, info?: { size: number }): boolean;
/**
* An etag can be provided to check whether readTree's response has changed from a previous execution.
* An ETag which can be provided to check whether a
* {@link UrlReader.readTree} response has changed from a previous execution.
*
* In the readTree() response, an etag is returned along with the tree blob. The etag is a unique identifer
* of the tree blob, usually the commit SHA or etag from the target.
* @remarks
*
* When an etag is given in ReadTreeOptions, readTree will first compare the etag against the etag
* on the target branch. If they match, readTree will throw a NotModifiedError indicating that the readTree
* response will not differ from the previous response which included this particular etag. If they
* do not match, readTree will return the rest of ReadTreeResponse along with a new etag.
* In the {@link UrlReader.readTree} response, an ETag is returned along with
* the tree blob. The ETag is a unique identifier of the tree blob, usually
* the commit SHA or ETag from the target.
*
* When an ETag is given as a request option, {@link UrlReader.readTree} will
* first compare the ETag against the ETag on the target branch. If they
* match, {@link UrlReader.readTree} will throw a
* {@link @backstage/errors#NotModifiedError} indicating that the response
* will not differ from the previous response which included this particular
* ETag. If they do not match, {@link UrlReader.readTree} will return the
* rest of the response along with a new ETag.
*/
etag?: string;
};
/** @public */
/**
* Options that control {@link ReadTreeResponse.dir} execution.
*
* @public
*/
export type ReadTreeResponseDirOptions = {
/** The directory to write files to. Defaults to the OS tmpdir or `backend.workingDirectory` if set in config */
/**
* The directory to write files to.
*
* @remarks
*
* Defaults to the OS tmpdir, or `backend.workingDirectory` if set in config.
*/
targetDir?: string;
};
/**
* A response object for readTree operations.
* A response object for {@link UrlReader.readTree} operations.
*
* @public
*/
export type ReadTreeResponse = {
/**
* files() returns an array of all the files inside the tree and corresponding functions to read their content.
* Returns an array of all the files inside the tree, and corresponding
* functions to read their content.
*/
files(): Promise<ReadTreeResponseFile[]>;
/**
* Returns the tree contents as a binary archive, using a stream.
*/
archive(): Promise<NodeJS.ReadableStream>;
/**
* dir() extracts the tree response into a directory and returns the path of the directory.
* Extracts the tree response into a directory and returns the path of the
* directory.
*/
dir(options?: ReadTreeResponseDirOptions): Promise<string>;
/**
* Etag returned by content provider.
*
* @remarks
*
* Can be used to compare and cache responses when doing subsequent calls.
*/
etag: string;
};
/**
* Represents a single file in a readTree response.
* Represents a single file in a {@link UrlReader.readTree} response.
*
* @public
*/
@@ -171,7 +226,11 @@ export type ReadTreeResponseFile = {
content(): Promise<Buffer>;
};
/** @public */
/**
* Options that control execution of {@link ReadTreeResponseFactory} methods.
*
* @public
*/
export type ReadTreeResponseFactoryOptions = {
// A binary stream of a tar archive.
stream: Readable;
@@ -183,11 +242,21 @@ export type ReadTreeResponseFactoryOptions = {
// Filter passed on from the ReadTreeOptions
filter?: (path: string, info?: { size: number }) => boolean;
};
/** @public */
/**
* Options that control {@link ReadTreeResponseFactory.fromReadableArray}
* execution.
*
* @public
*/
export type FromReadableArrayOptions = Array<{
// Data in the form of a readable
/**
* The raw data itself.
*/
data: Readable;
// A string containing the filepath of the data
/**
* The filepath of the data.
*/
path: string;
}>;
@@ -213,7 +282,7 @@ export type SearchOptions = {
/**
* An etag can be provided to check whether the search response has changed from a previous execution.
*
* In the search() response, an etag is returned along with the files. The etag is a unique identifer
* In the search() response, an etag is returned along with the files. The etag is a unique identifier
* of the current tree, usually the commit SHA or etag from the target.
*
* When an etag is given in SearchOptions, search will first compare the etag against the etag
@@ -236,7 +305,7 @@ export type SearchResponse = {
files: SearchResponseFile[];
/**
* A unique identifer of the current remote tree, usually the commit SHA or etag from the target.
* A unique identifier of the current remote tree, usually the commit SHA or etag from the target.
*/
etag: string;
};
@@ -0,0 +1,84 @@
/*
* Copyright 2021 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 { escapeRegExp } from './escapeRegExp';
describe('escapeRegExp', () => {
test('does not escape non-regex characters', () => {
expect(escapeRegExp('Backstage Backstage')).toBe('Backstage Backstage');
});
test('all the characters', () => {
expect(escapeRegExp('^$\\.*+?()[]{}|')).toBe(
'\\^\\$\\\\\\.\\*\\+\\?\\(\\)\\[\\]\\{\\}\\|',
);
});
test('character: ^', () => {
expect(escapeRegExp('^')).toBe('\\^');
});
test('character: $', () => {
expect(escapeRegExp('$')).toBe('\\$');
});
test('character: \\', () => {
expect(escapeRegExp('\\')).toBe('\\\\');
});
test('character: .', () => {
expect(escapeRegExp('.')).toBe('\\.');
});
test('character: *', () => {
expect(escapeRegExp('*')).toBe('\\*');
});
test('character: +', () => {
expect(escapeRegExp('+')).toBe('\\+');
});
test('character: ?', () => {
expect(escapeRegExp('?')).toBe('\\?');
});
test('character: (', () => {
expect(escapeRegExp('(')).toBe('\\(');
});
test('character: )', () => {
expect(escapeRegExp(')')).toBe('\\)');
});
test('character: [', () => {
expect(escapeRegExp('[')).toBe('\\[');
});
test('character: ]', () => {
expect(escapeRegExp(']')).toBe('\\]');
});
test('character: {', () => {
expect(escapeRegExp('{')).toBe('\\{');
});
test('character: }', () => {
expect(escapeRegExp('}')).toBe('\\}');
});
test('character: |', () => {
expect(escapeRegExp('|')).toBe('\\|');
});
});
@@ -0,0 +1,24 @@
/*
* Copyright 2021 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.
*/
/**
* Escapes a given string to be used inside a RegExp.
*
* Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
*/
export const escapeRegExp = (text: string) => {
return text.replace(/[.*+?^${}(\)|[\]\\]/g, '\\$&');
};
+1 -1
View File
@@ -37,7 +37,7 @@
"devDependencies": {
"@backstage/cli": "^0.8.0",
"@types/jest": "^26.0.7",
"msw": "^0.29.0"
"msw": "^0.35.0"
},
"files": [
"dist"
+9
View File
@@ -1,5 +1,14 @@
# @backstage/catalog-model
## 0.9.6
### Patch Changes
- 10615525f3: Switch to use the json and observable types from `@backstage/types`
- Updated dependencies
- @backstage/config@0.1.11
- @backstage/errors@0.1.4
## 0.9.5
### Patch Changes
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/catalog-model",
"description": "Types and validators that help describe the model of a Backstage Catalog",
"version": "0.9.5",
"version": "0.9.6",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -30,8 +30,8 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/config": "^0.1.10",
"@backstage/errors": "^0.1.3",
"@backstage/config": "^0.1.11",
"@backstage/errors": "^0.1.4",
"@backstage/types": "^0.1.1",
"@types/json-schema": "^7.0.5",
"@types/yup": "^0.29.13",
@@ -42,7 +42,7 @@
"yup": "^0.32.9"
},
"devDependencies": {
"@backstage/cli": "^0.8.0",
"@backstage/cli": "^0.8.1",
"@types/jest": "^26.0.7",
"@types/lodash": "^4.14.151",
"yaml": "^1.9.2"
+6
View File
@@ -1,5 +1,11 @@
# @backstage/cli-common
## 0.1.5
### Patch Changes
- c2d50a0073: Add docs to the last export of cli-common
## 0.1.4
### Patch Changes
+1 -1
View File
@@ -21,6 +21,6 @@ export type Paths = {
resolveTargetRoot: ResolveFunc;
};
// @public (undocumented)
// @public
export type ResolveFunc = (...paths: string[]) => string;
```
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/cli-common",
"description": "Common functionality used by cli, backend, and create-app",
"version": "0.1.4",
"version": "0.1.5",
"private": false,
"main": "src/index.ts",
"types": "src/index.ts",
+6 -1
View File
@@ -17,7 +17,12 @@
import fs from 'fs';
import { dirname, resolve as resolvePath } from 'path';
/** @public */
/**
* A function that takes a set of path fragments and resolves them into a
* single complete path, relative to some root.
*
* @public
*/
export type ResolveFunc = (...paths: string[]) => string;
/**
+14
View File
@@ -1,5 +1,19 @@
# @backstage/cli
## 0.8.1
### Patch Changes
- f1e96dc5b1: Update usage of msw in default plugin template
- b0dc1fd241: bump `@spotify/eslint-config-base` from 9.0.2 to 12.0.0
- c5bb1df55d: Bump `msw` to `v0.35.0` to resolve [CVE-2021-32796](https://github.com/advisories/GHSA-5fg8-2547-mr8q).
- 10615525f3: Switch to use the json and observable types from `@backstage/types`
- Updated dependencies
- @backstage/config@0.1.11
- @backstage/cli-common@0.1.5
- @backstage/errors@0.1.4
- @backstage/config-loader@0.7.1
## 0.8.0
### Minor Changes
+14 -14
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/cli",
"description": "CLI for developing Backstage plugins and apps",
"version": "0.8.0",
"version": "0.8.1",
"private": false,
"publishConfig": {
"access": "public"
@@ -28,10 +28,10 @@
"backstage-cli": "bin/backstage-cli"
},
"dependencies": {
"@backstage/cli-common": "^0.1.4",
"@backstage/config": "^0.1.10",
"@backstage/config-loader": "^0.7.0",
"@backstage/errors": "^0.1.3",
"@backstage/cli-common": "^0.1.5",
"@backstage/config": "^0.1.11",
"@backstage/config-loader": "^0.7.1",
"@backstage/errors": "^0.1.4",
"@backstage/types": "^0.1.1",
"@hot-loader/react-dom": "^16.13.0",
"@lerna/package-graph": "^4.0.0",
@@ -41,7 +41,7 @@
"@rollup/plugin-json": "^4.0.2",
"@rollup/plugin-node-resolve": "^13.0.0",
"@rollup/plugin-yaml": "^3.0.0",
"@spotify/eslint-config-base": "^9.0.0",
"@spotify/eslint-config-base": "^12.0.0",
"@spotify/eslint-config-react": "^10.0.0",
"@spotify/eslint-config-typescript": "^10.0.0",
"@sucrase/jest-plugin": "^2.1.1",
@@ -110,21 +110,21 @@
"typescript": "^4.0.3",
"util": "^0.12.3",
"webpack": "^5.48.0",
"webpack-dev-server": "4.0.0-rc.0",
"webpack-dev-server": "4.3.1",
"webpack-node-externals": "^3.0.0",
"yaml": "^1.10.0",
"yml-loader": "^2.1.0",
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/backend-common": "^0.9.7",
"@backstage/config": "^0.1.10",
"@backstage/core-components": "^0.7.1",
"@backstage/core-plugin-api": "^0.1.11",
"@backstage/core-app-api": "^0.1.18",
"@backstage/backend-common": "^0.9.8",
"@backstage/config": "^0.1.11",
"@backstage/core-components": "^0.7.2",
"@backstage/core-plugin-api": "^0.1.12",
"@backstage/core-app-api": "^0.1.19",
"@backstage/dev-utils": "^0.2.12",
"@backstage/test-utils": "^0.1.19",
"@backstage/theme": "^0.2.11",
"@backstage/test-utils": "^0.1.20",
"@backstage/theme": "^0.2.12",
"@types/diff": "^5.0.0",
"@types/express": "^4.17.6",
"@types/fs-extra": "^9.0.1",
@@ -36,7 +36,7 @@
"@backstage/cli": "{{versionQuery '@backstage/cli'}}",
"@types/supertest": "{{versionQuery '@types/supertest' '2.0.8'}}",
"supertest": "{{versionQuery 'supertest' '4.0.2'}}",
"msw": "{{versionQuery 'msw' '0.29.0'}}"
"msw": "{{versionQuery 'msw' '0.35.0'}}"
},
"files": [
"dist"
@@ -44,7 +44,7 @@
"@testing-library/user-event": "{{versionQuery '@testing-library/user-event' '13.1.8'}}",
"@types/jest": "{{versionQuery '@types/jest' '26.0.7'}}",
"@types/node": "{{versionQuery '@types/node' '14.14.32'}}",
"msw": "{{versionQuery 'msw' '0.29.0'}}",
"msw": "{{versionQuery 'msw' '0.35.0'}}",
"cross-fetch": "{{versionQuery 'cross-fetch' '3.0.6'}}"
},
"files": [
@@ -4,12 +4,15 @@ import { ThemeProvider } from '@material-ui/core';
import { lightTheme } from '@backstage/theme';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { msw, renderInTestApp } from '@backstage/test-utils';
import {
setupRequestMockHandlers,
renderInTestApp,
} from "@backstage/test-utils";
describe('ExampleComponent', () => {
const server = setupServer();
// Enable sane handlers for network requests
msw.setupDefaultHandlers(server);
setupRequestMockHandlers(server);
// setup mock response
beforeEach(() => {
@@ -3,12 +3,12 @@ import { render } from '@testing-library/react';
import { ExampleFetchComponent } from './ExampleFetchComponent';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { msw } from '@backstage/test-utils';
import { setupRequestMockHandlers } from '@backstage/test-utils';
describe('ExampleFetchComponent', () => {
const server = setupServer();
// Enable sane handlers for network requests
msw.setupDefaultHandlers(server);
setupRequestMockHandlers(server);
// setup mock response
beforeEach(() => {
+10
View File
@@ -1,5 +1,15 @@
# @backstage/codemods
## 0.1.20
### Patch Changes
- Updated dependencies
- @backstage/cli-common@0.1.5
- @backstage/core-components@0.7.2
- @backstage/core-app-api@0.1.19
- @backstage/core-plugin-api@0.1.12
## 0.1.19
### Patch Changes
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/codemods",
"description": "A collection of codemods for Backstage projects",
"version": "0.1.19",
"version": "0.1.20",
"private": false,
"publishConfig": {
"access": "public",
@@ -31,7 +31,7 @@
"backstage-codemods": "bin/backstage-codemods"
},
"dependencies": {
"@backstage/cli-common": "0.1.4",
"@backstage/cli-common": "0.1.5",
"@backstage/core-app-api": "*",
"@backstage/core-components": "*",
"@backstage/core-plugin-api": "*",
+11
View File
@@ -1,5 +1,16 @@
# @backstage/config-loader
## 0.7.1
### Patch Changes
- 10615525f3: Switch to use the json and observable types from `@backstage/types`
- ea21f7f567: bump `typescript-json-schema` from 0.50.1 to 0.51.0
- Updated dependencies
- @backstage/config@0.1.11
- @backstage/cli-common@0.1.5
- @backstage/errors@0.1.4
## 0.7.0
### Minor Changes
+5 -5
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/config-loader",
"description": "Config loading functionality used by Backstage backend, and CLI",
"version": "0.7.0",
"version": "0.7.1",
"private": false,
"publishConfig": {
"access": "public",
@@ -30,9 +30,9 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/cli-common": "^0.1.4",
"@backstage/config": "^0.1.9",
"@backstage/errors": "^0.1.3",
"@backstage/cli-common": "^0.1.5",
"@backstage/config": "^0.1.11",
"@backstage/errors": "^0.1.4",
"@backstage/types": "^0.1.1",
"@types/json-schema": "^7.0.6",
"ajv": "^7.0.3",
@@ -41,7 +41,7 @@
"json-schema": "^0.3.0",
"json-schema-merge-allof": "^0.8.1",
"json-schema-traverse": "^1.0.0",
"typescript-json-schema": "^0.50.1",
"typescript-json-schema": "^0.51.0",
"yaml": "^1.9.2",
"yup": "^0.32.9",
"node-fetch": "2.6.5"
+8
View File
@@ -1,5 +1,13 @@
# @backstage/config
## 0.1.11
### Patch Changes
- 10d267a1b7: Minor exports cleanup
- 41c49884d2: Start using the new `@backstage/types` package. Initially, this means using the `Observable` and `Json*` types from there. The types also remain in their old places but deprecated, and will be removed in a future release.
- 925a967f36: Replace usage of test-utils-core with test-utils
## 0.1.10
### Patch Changes
-17
View File
@@ -46,39 +46,22 @@ export class ConfigReader implements Config {
fallback?: ConfigReader | undefined,
prefix?: string,
);
// (undocumented)
static fromConfigs(configs: AppConfig[]): ConfigReader;
// (undocumented)
get<T = JsonValue_2>(key?: string): T;
// (undocumented)
getBoolean(key: string): boolean;
// (undocumented)
getConfig(key: string): ConfigReader;
// (undocumented)
getConfigArray(key: string): ConfigReader[];
// (undocumented)
getNumber(key: string): number;
// (undocumented)
getOptional<T = JsonValue_2>(key?: string): T | undefined;
// (undocumented)
getOptionalBoolean(key: string): boolean | undefined;
// (undocumented)
getOptionalConfig(key: string): ConfigReader | undefined;
// (undocumented)
getOptionalConfigArray(key: string): ConfigReader[] | undefined;
// (undocumented)
getOptionalNumber(key: string): number | undefined;
// (undocumented)
getOptionalString(key: string): string | undefined;
// (undocumented)
getOptionalStringArray(key: string): string[] | undefined;
// (undocumented)
getString(key: string): string;
// (undocumented)
getStringArray(key: string): string[];
// (undocumented)
has(key: string): boolean;
// (undocumented)
keys(): string[];
}
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/config",
"description": "Config API used by Backstage core, backend, and CLI",
"version": "0.1.10",
"version": "0.1.11",
"private": false,
"publishConfig": {
"access": "public",
@@ -34,7 +34,7 @@
"lodash": "^4.17.21"
},
"devDependencies": {
"@backstage/test-utils": "^0.1.19",
"@backstage/test-utils": "^0.1.20",
"@types/jest": "^26.0.7",
"@types/node": "^14.14.32"
},
+19
View File
@@ -72,6 +72,9 @@ export class ConfigReader implements Config {
private filteredKeys?: string[];
private notifiedFilteredKeys = new Set<string>();
/**
* Instantiates the config reader from a list of application config objects.
*/
static fromConfigs(configs: AppConfig[]): ConfigReader {
if (configs.length === 0) {
return new ConfigReader(undefined);
@@ -96,6 +99,7 @@ export class ConfigReader implements Config {
private readonly prefix: string = '',
) {}
/** {@inheritdoc Config.has} */
has(key: string): boolean {
const value = this.readValue(key);
if (value !== undefined) {
@@ -104,12 +108,14 @@ export class ConfigReader implements Config {
return this.fallback?.has(key) ?? false;
}
/** {@inheritdoc Config.keys} */
keys(): string[] {
const localKeys = this.data ? Object.keys(this.data) : [];
const fallbackKeys = this.fallback?.keys() ?? [];
return [...new Set([...localKeys, ...fallbackKeys])];
}
/** {@inheritdoc Config.get} */
get<T = JsonValue>(key?: string): T {
const value = this.getOptional(key);
if (value === undefined) {
@@ -118,6 +124,7 @@ export class ConfigReader implements Config {
return value as T;
}
/** {@inheritdoc Config.getOptional} */
getOptional<T = JsonValue>(key?: string): T | undefined {
const value = this.readValue(key);
const fallbackValue = this.fallback?.getOptional<T>(key);
@@ -154,6 +161,7 @@ export class ConfigReader implements Config {
).value as T;
}
/** {@inheritdoc Config.getConfig} */
getConfig(key: string): ConfigReader {
const value = this.getOptionalConfig(key);
if (value === undefined) {
@@ -162,6 +170,7 @@ export class ConfigReader implements Config {
return value;
}
/** {@inheritdoc Config.getOptionalConfig} */
getOptionalConfig(key: string): ConfigReader | undefined {
const value = this.readValue(key);
const fallbackConfig = this.fallback?.getOptionalConfig(key);
@@ -177,6 +186,7 @@ export class ConfigReader implements Config {
return fallbackConfig;
}
/** {@inheritdoc Config.getConfigArray} */
getConfigArray(key: string): ConfigReader[] {
const value = this.getOptionalConfigArray(key);
if (value === undefined) {
@@ -185,6 +195,7 @@ export class ConfigReader implements Config {
return value;
}
/** {@inheritdoc Config.getOptionalConfigArray} */
getOptionalConfigArray(key: string): ConfigReader[] | undefined {
const configs = this.readConfigValue<JsonObject[]>(key, values => {
if (!Array.isArray(values)) {
@@ -220,6 +231,7 @@ export class ConfigReader implements Config {
return configs.map((obj, index) => this.copy(obj, `${key}[${index}]`));
}
/** {@inheritdoc Config.getNumber} */
getNumber(key: string): number {
const value = this.getOptionalNumber(key);
if (value === undefined) {
@@ -228,6 +240,7 @@ export class ConfigReader implements Config {
return value;
}
/** {@inheritdoc Config.getOptionalNumber} */
getOptionalNumber(key: string): number | undefined {
const value = this.readConfigValue<string | number>(
key,
@@ -247,6 +260,7 @@ export class ConfigReader implements Config {
return number;
}
/** {@inheritdoc Config.getBoolean} */
getBoolean(key: string): boolean {
const value = this.getOptionalBoolean(key);
if (value === undefined) {
@@ -255,6 +269,7 @@ export class ConfigReader implements Config {
return value;
}
/** {@inheritdoc Config.getOptionalBoolean} */
getOptionalBoolean(key: string): boolean | undefined {
return this.readConfigValue(
key,
@@ -262,6 +277,7 @@ export class ConfigReader implements Config {
);
}
/** {@inheritdoc Config.getString} */
getString(key: string): string {
const value = this.getOptionalString(key);
if (value === undefined) {
@@ -270,6 +286,7 @@ export class ConfigReader implements Config {
return value;
}
/** {@inheritdoc Config.getOptionalString} */
getOptionalString(key: string): string | undefined {
return this.readConfigValue(
key,
@@ -278,6 +295,7 @@ export class ConfigReader implements Config {
);
}
/** {@inheritdoc Config.getStringArray} */
getStringArray(key: string): string[] {
const value = this.getOptionalStringArray(key);
if (value === undefined) {
@@ -286,6 +304,7 @@ export class ConfigReader implements Config {
return value;
}
/** {@inheritdoc Config.getOptionalStringArray} */
getOptionalStringArray(key: string): string[] | undefined {
return this.readConfigValue(key, values => {
if (!Array.isArray(values)) {
+14
View File
@@ -1,5 +1,19 @@
# @backstage/core-app-api
## 0.1.19
### Patch Changes
- 10615525f3: Switch to use the json and observable types from `@backstage/types`
- 41c49884d2: Start using the new `@backstage/types` package. Initially, this means using the `Observable` and `Json*` types from there. The types also remain in their old places but deprecated, and will be removed in a future release.
- 925a967f36: Replace usage of test-utils-core with test-utils
- 6b615e92c8: Api cleanup, adding `@public` where necessary and tweaking some comments
- Updated dependencies
- @backstage/config@0.1.11
- @backstage/theme@0.2.12
- @backstage/core-components@0.7.2
- @backstage/core-plugin-api@0.1.12
## 0.1.18
### Patch Changes
+55 -138
View File
@@ -58,8 +58,6 @@ import { StorageApi } from '@backstage/core-plugin-api';
import { StorageValueChange } from '@backstage/core-plugin-api';
import { SubRouteRef } from '@backstage/core-plugin-api';
// Warning: (ae-missing-release-tag) "AlertApiForwarder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export class AlertApiForwarder implements AlertApi {
// (undocumented)
@@ -68,8 +66,6 @@ export class AlertApiForwarder implements AlertApi {
post(alert: AlertMessage): void;
}
// Warning: (ae-missing-release-tag) "ApiFactoryHolder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type ApiFactoryHolder = {
get<T>(api: ApiRef<T>):
@@ -83,8 +79,6 @@ export type ApiFactoryHolder = {
| undefined;
};
// Warning: (ae-missing-release-tag) "ApiFactoryRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export class ApiFactoryRegistry implements ApiFactoryHolder {
// (undocumented)
@@ -109,11 +103,9 @@ export class ApiFactoryRegistry implements ApiFactoryHolder {
>(scope: ApiFactoryScope, factory: ApiFactory<Api, Impl, Deps>): boolean;
}
// Warning: (ae-missing-release-tag) "ApiProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export const ApiProvider: {
({ apis, children }: PropsWithChildren<ApiProviderProps>): JSX.Element;
(props: PropsWithChildren<ApiProviderProps>): JSX.Element;
propTypes: {
apis: PropTypes.Validator<
PropTypes.InferProps<{
@@ -124,9 +116,7 @@ export const ApiProvider: {
};
};
// Warning: (ae-missing-release-tag) "ApiRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class ApiRegistry implements ApiHolder {
constructor(apis: Map<string, unknown>);
// Warning: (ae-forgotten-export) The symbol "ApiRegistryBuilder" needs to be exported by the entry point index.d.ts
@@ -134,22 +124,14 @@ export class ApiRegistry implements ApiHolder {
// (undocumented)
static builder(): ApiRegistryBuilder;
// Warning: (ae-forgotten-export) The symbol "ApiImpl" needs to be exported by the entry point index.d.ts
//
// (undocumented)
static from(apis: ApiImpl[]): ApiRegistry;
// (undocumented)
get<T>(api: ApiRef<T>): T | undefined;
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
static with<T>(api: ApiRef<T>, impl: T): ApiRegistry;
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
with<T>(api: ApiRef<T>, impl: T): ApiRegistry;
}
// Warning: (ae-missing-release-tag) "ApiResolver" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class ApiResolver implements ApiHolder {
constructor(factories: ApiFactoryHolder);
// (undocumented)
@@ -160,9 +142,7 @@ export class ApiResolver implements ApiHolder {
): void;
}
// Warning: (ae-missing-release-tag) "AppComponents" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type AppComponents = {
NotFoundErrorPage: ComponentType<{}>;
BootErrorPage: ComponentType<BootErrorPageProps>;
@@ -173,23 +153,17 @@ export type AppComponents = {
SignInPage?: ComponentType<SignInPageProps>;
};
// Warning: (ae-missing-release-tag) "AppConfigLoader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export type AppConfigLoader = () => Promise<AppConfig[]>;
// Warning: (ae-missing-release-tag) "AppContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type AppContext = {
getPlugins(): BackstagePlugin<any, any>[];
getSystemIcon(key: string): IconComponent | undefined;
getComponents(): AppComponents;
};
// Warning: (ae-missing-release-tag) "AppOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type AppOptions = {
apis?: Iterable<AnyApiFactory>;
icons?: Partial<AppIcons> & {
@@ -205,9 +179,8 @@ export type AppOptions = {
// Warning: (ae-forgotten-export) The symbol "PartialKeys" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "TargetRouteMap" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "KeysWithType" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "AppRouteBinder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type AppRouteBinder = <
ExternalRoutes extends {
[name: string]: ExternalRouteRef;
@@ -220,9 +193,7 @@ export type AppRouteBinder = <
>,
) => void;
// Warning: (ae-missing-release-tag) "AppThemeSelector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class AppThemeSelector implements AppThemeApi {
constructor(themes: AppTheme[]);
// (undocumented)
@@ -237,9 +208,7 @@ export class AppThemeSelector implements AppThemeApi {
setActiveThemeId(themeId?: string): void;
}
// Warning: (ae-missing-release-tag) "AtlassianAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class AtlassianAuth {
// Warning: (ae-forgotten-export) The symbol "OAuthApiCreateOptions" needs to be exported by the entry point index.d.ts
//
@@ -252,9 +221,7 @@ export class AtlassianAuth {
}: OAuthApiCreateOptions): typeof atlassianAuthApiRef.T;
}
// Warning: (ae-missing-release-tag) "Auth0Auth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class Auth0Auth {
// (undocumented)
static create({
@@ -266,9 +233,7 @@ export class Auth0Auth {
}: OAuthApiCreateOptions): typeof auth0AuthApiRef.T;
}
// Warning: (ae-missing-release-tag) "BackstageApp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type BackstageApp = {
getPlugins(): BackstagePlugin<any, any>[];
getSystemIcon(key: string): IconComponent | undefined;
@@ -276,19 +241,20 @@ export type BackstageApp = {
getRouter(): ComponentType<{}>;
};
// Warning: (ae-missing-release-tag) "BackstagePluginWithAnyOutput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type BackstagePluginWithAnyOutput = Omit<
BackstagePlugin<any, any>,
'output'
> & {
output(): (PluginOutput | UnknownPluginOutput)[];
output(): (
| PluginOutput
| {
type: string;
}
)[];
};
// Warning: (ae-missing-release-tag) "BitbucketAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class BitbucketAuth {
// (undocumented)
static create({
@@ -300,9 +266,7 @@ export class BitbucketAuth {
}: OAuthApiCreateOptions): typeof bitbucketAuthApiRef.T;
}
// Warning: (ae-missing-release-tag) "BitbucketSession" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type BitbucketSession = {
providerInfo: {
accessToken: string;
@@ -313,9 +277,7 @@ export type BitbucketSession = {
backstageIdentity: BackstageIdentity;
};
// Warning: (ae-missing-release-tag) "BootErrorPageProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type BootErrorPageProps = {
step: 'load-config' | 'load-chunk';
error: Error;
@@ -324,18 +286,13 @@ export type BootErrorPageProps = {
export { ConfigReader };
// Warning: (ae-forgotten-export) The symbol "PrivateAppImpl" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "createApp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export function createApp(options?: AppOptions): PrivateAppImpl;
// Warning: (ae-missing-release-tag) "defaultConfigLoader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export const defaultConfigLoader: AppConfigLoader;
// Warning: (ae-missing-release-tag) "ErrorAlerter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export class ErrorAlerter implements ErrorApi {
constructor(alertApi: AlertApi, errorApi: ErrorApi);
@@ -352,8 +309,6 @@ export class ErrorAlerter implements ErrorApi {
post(error: Error, context?: ErrorContext): void;
}
// Warning: (ae-missing-release-tag) "ErrorApiForwarder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export class ErrorApiForwarder implements ErrorApi {
// (undocumented)
@@ -365,23 +320,17 @@ export class ErrorApiForwarder implements ErrorApi {
post(error: Error, context?: ErrorContext): void;
}
// Warning: (ae-missing-release-tag) "ErrorBoundaryFallbackProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type ErrorBoundaryFallbackProps = {
plugin?: BackstagePlugin;
error: Error;
resetError: () => void;
};
// Warning: (ae-missing-release-tag) "FeatureFlagged" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export const FeatureFlagged: (props: FeatureFlaggedProps) => JSX.Element;
// Warning: (ae-missing-release-tag) "FeatureFlaggedProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type FeatureFlaggedProps = {
children: ReactNode;
} & (
@@ -393,15 +342,15 @@ export type FeatureFlaggedProps = {
}
);
// Warning: (ae-forgotten-export) The symbol "FlatRoutesProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "FlatRoutes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export const FlatRoutes: (props: FlatRoutesProps) => JSX.Element | null;
// Warning: (ae-missing-release-tag) "GithubAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type FlatRoutesProps = {
children: ReactNode;
};
// @public
export class GithubAuth implements OAuthApi, SessionApi {
// Warning: (ae-forgotten-export) The symbol "SessionManager" needs to be exported by the entry point index.d.ts
constructor(sessionManager: SessionManager<GithubSession>);
@@ -431,9 +380,7 @@ export class GithubAuth implements OAuthApi, SessionApi {
signOut(): Promise<void>;
}
// Warning: (ae-missing-release-tag) "GithubSession" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type GithubSession = {
providerInfo: {
accessToken: string;
@@ -444,9 +391,7 @@ export type GithubSession = {
backstageIdentity: BackstageIdentity;
};
// Warning: (ae-missing-release-tag) "GitlabAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class GitlabAuth {
// (undocumented)
static create({
@@ -458,9 +403,7 @@ export class GitlabAuth {
}: OAuthApiCreateOptions): typeof gitlabAuthApiRef.T;
}
// Warning: (ae-missing-release-tag) "GoogleAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class GoogleAuth {
// (undocumented)
static create({
@@ -472,8 +415,6 @@ export class GoogleAuth {
}: OAuthApiCreateOptions): typeof googleAuthApiRef.T;
}
// Warning: (ae-missing-release-tag) "LocalStorageFeatureFlags" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export class LocalStorageFeatureFlags implements FeatureFlagsApi {
// (undocumented)
@@ -486,9 +427,7 @@ export class LocalStorageFeatureFlags implements FeatureFlagsApi {
save(options: FeatureFlagsSaveOptions): void;
}
// Warning: (ae-missing-release-tag) "MicrosoftAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class MicrosoftAuth {
// (undocumented)
static create({
@@ -500,17 +439,13 @@ export class MicrosoftAuth {
}: OAuthApiCreateOptions): typeof microsoftAuthApiRef.T;
}
// Warning: (ae-missing-release-tag) "NoOpAnalyticsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export class NoOpAnalyticsApi implements AnalyticsApi {
// (undocumented)
captureEvent(_event: AnalyticsEvent): void;
}
// Warning: (ae-missing-release-tag) "OAuth2" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class OAuth2
implements
OAuthApi,
@@ -519,8 +454,10 @@ export class OAuth2
BackstageIdentityApi,
SessionApi
{
// Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts
constructor(options: Options);
constructor(options: {
sessionManager: SessionManager<OAuth2Session>;
scopeTransform: (scopes: string[]) => string[];
});
// Warning: (ae-forgotten-export) The symbol "CreateOptions" needs to be exported by the entry point index.d.ts
//
// (undocumented)
@@ -553,9 +490,7 @@ export class OAuth2
signOut(): Promise<void>;
}
// Warning: (ae-missing-release-tag) "OAuth2Session" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type OAuth2Session = {
providerInfo: {
idToken: string;
@@ -567,8 +502,6 @@ export type OAuth2Session = {
backstageIdentity: BackstageIdentity;
};
// Warning: (ae-missing-release-tag) "OAuthRequestManager" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export class OAuthRequestManager implements OAuthRequestApi {
// (undocumented)
@@ -577,9 +510,7 @@ export class OAuthRequestManager implements OAuthRequestApi {
createAuthRequester<T>(options: AuthRequesterOptions<T>): AuthRequester<T>;
}
// Warning: (ae-missing-release-tag) "OktaAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class OktaAuth {
// (undocumented)
static create({
@@ -591,9 +522,7 @@ export class OktaAuth {
}: OAuthApiCreateOptions): typeof oktaAuthApiRef.T;
}
// Warning: (ae-missing-release-tag) "OneLoginAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class OneLoginAuth {
// Warning: (ae-forgotten-export) The symbol "CreateOptions" needs to be exported by the entry point index.d.ts
//
@@ -606,9 +535,7 @@ export class OneLoginAuth {
}: CreateOptions_2): typeof oneloginAuthApiRef.T;
}
// Warning: (ae-missing-release-tag) "SamlAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class SamlAuth
implements ProfileInfoApi, BackstageIdentityApi, SessionApi
{
@@ -636,16 +563,12 @@ export class SamlAuth
signOut(): Promise<void>;
}
// Warning: (ae-missing-release-tag) "SignInPageProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type SignInPageProps = {
onResult(result: SignInResult): void;
};
// Warning: (ae-missing-release-tag) "SignInResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type SignInResult = {
userId: string;
profile: ProfileInfo;
@@ -653,15 +576,11 @@ export type SignInResult = {
signOut?: () => Promise<void>;
};
// Warning: (ae-missing-release-tag) "UnhandledErrorForwarder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class UnhandledErrorForwarder {
static forward(errorApi: ErrorApi, errorContext: ErrorContext): void;
}
// Warning: (ae-missing-release-tag) "UrlPatternDiscovery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export class UrlPatternDiscovery implements DiscoveryApi {
static compile(pattern: string): UrlPatternDiscovery;
@@ -669,15 +588,14 @@ export class UrlPatternDiscovery implements DiscoveryApi {
getBaseUrl(pluginId: string): Promise<string>;
}
// Warning: (ae-missing-release-tag) "WebStorage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class WebStorage implements StorageApi {
constructor(namespace: string, errorApi: ErrorApi);
// Warning: (ae-forgotten-export) The symbol "CreateStorageApiOptions" needs to be exported by the entry point index.d.ts
//
// (undocumented)
static create(options: CreateStorageApiOptions): WebStorage;
static create(options: {
errorApi: ErrorApi;
namespace?: string;
}): WebStorage;
// (undocumented)
forBucket(name: string): WebStorage;
// (undocumented)
@@ -692,7 +610,6 @@ export class WebStorage implements StorageApi {
// Warnings were encountered during analysis:
//
// src/apis/system/ApiProvider.d.ts:9:5 - (ae-forgotten-export) The symbol "ApiProviderProps" needs to be exported by the entry point index.d.ts
// src/app/types.d.ts:89:5 - (ae-forgotten-export) The symbol "UnknownPluginOutput" needs to be exported by the entry point index.d.ts
// src/app/types.d.ts:100:5 - (ae-forgotten-export) The symbol "AppIcons" needs to be exported by the entry point index.d.ts
// src/apis/system/ApiProvider.d.ts:15:5 - (ae-forgotten-export) The symbol "ApiProviderProps" needs to be exported by the entry point index.d.ts
// src/app/types.d.ts:152:5 - (ae-forgotten-export) The symbol "AppIcons" needs to be exported by the entry point index.d.ts
```
+8 -8
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/core-app-api",
"description": "Core app API used by Backstage apps",
"version": "0.1.18",
"version": "0.1.19",
"private": false,
"publishConfig": {
"access": "public",
@@ -29,10 +29,10 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/core-components": "^0.7.1",
"@backstage/config": "^0.1.10",
"@backstage/core-plugin-api": "^0.1.11",
"@backstage/theme": "^0.2.11",
"@backstage/core-components": "^0.7.2",
"@backstage/config": "^0.1.11",
"@backstage/core-plugin-api": "^0.1.12",
"@backstage/theme": "^0.2.12",
"@backstage/types": "^0.1.1",
"@backstage/version-bridge": "^0.1.0",
"@material-ui/core": "^4.12.2",
@@ -46,8 +46,8 @@
"zen-observable": "^0.8.15"
},
"devDependencies": {
"@backstage/cli": "^0.8.0",
"@backstage/test-utils": "^0.1.19",
"@backstage/cli": "^0.8.1",
"@backstage/test-utils": "^0.1.20",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
"@testing-library/react-hooks": "^7.0.2",
@@ -56,7 +56,7 @@
"@types/node": "^14.14.32",
"@types/zen-observable": "^0.8.0",
"cross-fetch": "^3.0.6",
"msw": "^0.29.0"
"msw": "^0.35.0"
},
"files": [
"dist",
@@ -20,6 +20,8 @@ import { PublishSubject } from '../../../lib/subjects';
/**
* Base implementation for the AlertApi that simply forwards alerts to consumers.
*
* @public
*/
export class AlertApiForwarder implements AlertApi {
private readonly subject = new PublishSubject<AlertMessage>();
@@ -17,6 +17,8 @@ import { AnalyticsApi, AnalyticsEvent } from '@backstage/core-plugin-api';
/**
* Base implementation for the AnalyticsApi that does nothing.
*
* @public
*/
export class NoOpAnalyticsApi implements AnalyticsApi {
captureEvent(_event: AnalyticsEvent): void {}
@@ -20,6 +20,12 @@ import { BehaviorSubject } from '../../../lib/subjects';
const STORAGE_KEY = 'theme';
/**
* Exposes the themes installed in the app, and permits switching the currently
* active theme.
*
* @public
*/
export class AppThemeSelector implements AppThemeApi {
static createWithStorage(themes: AppTheme[]) {
const selector = new AppThemeSelector(themes);
@@ -21,6 +21,8 @@ const ERROR_PREFIX = 'Invalid discovery URL pattern,';
/**
* UrlPatternDiscovery is a lightweight DiscoveryApi implementation.
* It uses a single template string to construct URLs for each plugin.
*
* @public
*/
export class UrlPatternDiscovery implements DiscoveryApi {
/**
@@ -18,6 +18,8 @@ import { ErrorApi, ErrorContext, AlertApi } from '@backstage/core-plugin-api';
/**
* Decorates an ErrorApi by also forwarding error messages
* to the alertApi with an 'error' severity.
*
* @public
*/
export class ErrorAlerter implements ErrorApi {
constructor(
@@ -20,6 +20,8 @@ import { PublishSubject } from '../../../lib/subjects';
/**
* Base implementation for the ErrorApi that simply forwards errors to consumers.
*
* @public
*/
export class ErrorApiForwarder implements ErrorApi {
private readonly subject = new PublishSubject<{
@@ -16,6 +16,11 @@ import { ErrorApi, ErrorContext } from '@backstage/core-plugin-api';
* limitations under the License.
*/
/**
* Utility class that helps with error forwarding.
*
* @public
*/
export class UnhandledErrorForwarder {
/**
* Add event listener, such that unhandled errors can be forwarded using an given `ErrorApi` instance
@@ -43,7 +43,10 @@ export function validateFlagName(name: string): void {
}
/**
* Create the FeatureFlags implementation based on the API.
* A feature flags implementation that stores the flags in the browser's local
* storage.
*
* @public
*/
export class LocalStorageFeatureFlags implements FeatureFlagsApi {
private registeredFeatureFlags: FeatureFlag[] = [];
@@ -30,6 +30,8 @@ import { BehaviorSubject } from '../../../lib/subjects';
* The purpose of this class and the API is to read a stream of incoming requests
* of OAuth access tokens from different providers with varying scope, and funnel
* them all together into a single request for each OAuth provider.
*
* @public
*/
export class OAuthRequestManager implements OAuthRequestApi {
private readonly subject = new BehaviorSubject<PendingAuthRequest[]>([]);
@@ -13,13 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CreateStorageApiOptions, WebStorage } from './WebStorage';
import { StorageApi } from '@backstage/core-plugin-api';
import { WebStorage } from './WebStorage';
import { ErrorApi, StorageApi } from '@backstage/core-plugin-api';
describe('WebStorage Storage API', () => {
const mockErrorApi = { post: jest.fn(), error$: jest.fn() };
const createWebStorage = (
args?: Partial<CreateStorageApiOptions>,
args?: Partial<{
errorApi: ErrorApi;
namespace?: string;
}>,
): StorageApi => {
return WebStorage.create({
errorApi: mockErrorApi,
@@ -24,18 +24,21 @@ import ObservableImpl from 'zen-observable';
const buckets = new Map<string, WebStorage>();
export type CreateStorageApiOptions = {
errorApi: ErrorApi;
namespace?: string;
};
/**
* An implementation of the storage API, that uses the browser's local storage.
*
* @public
*/
export class WebStorage implements StorageApi {
constructor(
private readonly namespace: string,
private readonly errorApi: ErrorApi,
) {}
static create(options: CreateStorageApiOptions): WebStorage {
static create(options: {
errorApi: ErrorApi;
namespace?: string;
}): WebStorage {
return new WebStorage(options.namespace ?? '', options.errorApi);
}
@@ -25,7 +25,12 @@ const DEFAULT_PROVIDER = {
icon: AtlassianIcon,
};
class AtlassianAuth {
/**
* Implements the OAuth flow to Atlassian products.
*
* @public
*/
export default class AtlassianAuth {
static create({
discoveryApi,
environment = 'development',
@@ -40,5 +45,3 @@ class AtlassianAuth {
});
}
}
export default AtlassianAuth;
@@ -25,7 +25,12 @@ const DEFAULT_PROVIDER = {
icon: Auth0Icon,
};
class Auth0Auth {
/**
* Implements the OAuth flow to Auth0 products.
*
* @public
*/
export default class Auth0Auth {
static create({
discoveryApi,
environment = 'development',
@@ -42,5 +47,3 @@ class Auth0Auth {
});
}
}
export default Auth0Auth;
@@ -40,7 +40,12 @@ const DEFAULT_PROVIDER = {
icon: BitbucketIcon,
};
class BitbucketAuth {
/**
* Implements the OAuth flow to Bitbucket products.
*
* @public
*/
export default class BitbucketAuth {
static create({
discoveryApi,
environment = 'development',
@@ -57,5 +62,3 @@ class BitbucketAuth {
});
}
}
export default BitbucketAuth;
@@ -16,6 +16,11 @@
import { ProfileInfo, BackstageIdentity } from '@backstage/core-plugin-api';
/**
* Session information for Bitbucket auth.
*
* @public
*/
export type BitbucketSession = {
providerInfo: {
accessToken: string;
@@ -51,7 +51,12 @@ const DEFAULT_PROVIDER = {
icon: GithubIcon,
};
class GithubAuth implements OAuthApi, SessionApi {
/**
* Implements the OAuth flow to GitHub products.
*
* @public
*/
export default class GithubAuth implements OAuthApi, SessionApi {
static create({
discoveryApi,
environment = 'development',
@@ -158,4 +163,3 @@ class GithubAuth implements OAuthApi, SessionApi {
return new Set(scopeList);
}
}
export default GithubAuth;
@@ -16,6 +16,11 @@
import { ProfileInfo, BackstageIdentity } from '@backstage/core-plugin-api';
/**
* Session information for GitHub auth.
*
* @public
*/
export type GithubSession = {
providerInfo: {
accessToken: string;
@@ -25,7 +25,12 @@ const DEFAULT_PROVIDER = {
icon: GitlabIcon,
};
class GitlabAuth {
/**
* Implements the OAuth flow to GitLab products.
*
* @public
*/
export default class GitlabAuth {
static create({
discoveryApi,
environment = 'development',
@@ -42,4 +47,3 @@ class GitlabAuth {
});
}
}
export default GitlabAuth;
@@ -27,7 +27,12 @@ const DEFAULT_PROVIDER = {
const SCOPE_PREFIX = 'https://www.googleapis.com/auth/';
class GoogleAuth {
/**
* Implements the OAuth flow to Google products.
*
* @public
*/
export default class GoogleAuth {
static create({
discoveryApi,
oauthRequestApi,
@@ -65,4 +70,3 @@ class GoogleAuth {
});
}
}
export default GoogleAuth;
@@ -25,7 +25,12 @@ const DEFAULT_PROVIDER = {
icon: MicrosoftIcon,
};
class MicrosoftAuth {
/**
* Implements the OAuth flow to Microsoft products.
*
* @public
*/
export default class MicrosoftAuth {
static create({
environment = 'development',
provider = DEFAULT_PROVIDER,
@@ -48,5 +53,3 @@ class MicrosoftAuth {
});
}
}
export default MicrosoftAuth;
@@ -33,11 +33,6 @@ import { Observable } from '@backstage/types';
import { OAuth2Session } from './types';
import { OAuthApiCreateOptions } from '../types';
type Options = {
sessionManager: SessionManager<OAuth2Session>;
scopeTransform: (scopes: string[]) => string[];
};
type CreateOptions = OAuthApiCreateOptions & {
scopeTransform?: (scopes: string[]) => string[];
};
@@ -59,7 +54,12 @@ const DEFAULT_PROVIDER = {
icon: OAuth2Icon,
};
class OAuth2
/**
* Implements a generic OAuth2 flow for auth.
*
* @public
*/
export default class OAuth2
implements
OAuthApi,
OpenIdConnectApi,
@@ -115,7 +115,10 @@ class OAuth2
private readonly sessionManager: SessionManager<OAuth2Session>;
private readonly scopeTransform: (scopes: string[]) => string[];
constructor(options: Options) {
constructor(options: {
sessionManager: SessionManager<OAuth2Session>;
scopeTransform: (scopes: string[]) => string[];
}) {
this.sessionManager = options.sessionManager;
this.scopeTransform = options.scopeTransform;
}
@@ -176,5 +179,3 @@ class OAuth2
return new Set(scopeTransform(scopeList));
}
}
export default OAuth2;
@@ -16,6 +16,11 @@
import { ProfileInfo, BackstageIdentity } from '@backstage/core-plugin-api';
/**
* Session information for generic OAuth2 auth.
*
* @public
*/
export type OAuth2Session = {
providerInfo: {
idToken: string;
@@ -37,7 +37,12 @@ const OKTA_OIDC_SCOPES: Set<String> = new Set([
const OKTA_SCOPE_PREFIX: string = 'okta.';
class OktaAuth {
/**
* Implements the OAuth flow to Okta products.
*
* @public
*/
export default class OktaAuth {
static create({
discoveryApi,
environment = 'development',
@@ -67,5 +72,3 @@ class OktaAuth {
});
}
}
export default OktaAuth;
@@ -26,7 +26,6 @@ import { OAuth2 } from '../oauth2';
type CreateOptions = {
discoveryApi: DiscoveryApi;
oauthRequestApi: OAuthRequestApi;
environment?: string;
provider?: AuthProvider & { id: string };
};
@@ -49,7 +48,12 @@ const OIDC_SCOPES: Set<String> = new Set([
const SCOPE_PREFIX: string = 'onelogin.';
class OneLoginAuth {
/**
* Implements a OneLogin OAuth flow.
*
* @public
*/
export default class OneLoginAuth {
static create({
discoveryApi,
environment = 'development',
@@ -78,5 +82,3 @@ class OneLoginAuth {
});
}
}
export default OneLoginAuth;
@@ -45,7 +45,14 @@ const DEFAULT_PROVIDER = {
icon: SamlIcon,
};
class SamlAuth implements ProfileInfoApi, BackstageIdentityApi, SessionApi {
/**
* Implements a general SAML based auth flow.
*
* @public
*/
export default class SamlAuth
implements ProfileInfoApi, BackstageIdentityApi, SessionApi
{
static create({
discoveryApi,
environment = 'development',
@@ -94,5 +101,3 @@ class SamlAuth implements ProfileInfoApi, BackstageIdentityApi, SessionApi {
return session?.profile;
}
}
export default SamlAuth;
@@ -15,6 +15,11 @@
*/
import { ProfileInfo, BackstageIdentity } from '@backstage/core-plugin-api';
/**
* Session information for SAML auth.
*
* @public
*/
export type SamlSession = {
userId: string;
profile: ProfileInfo;
@@ -44,6 +44,8 @@ type FactoryTuple = {
*
* Each scope has an assigned priority, where factories registered with
* higher priority scopes override ones with lower priority.
*
* @public
*/
export class ApiFactoryRegistry implements ApiFactoryHolder {
private readonly factories = new Map<string, FactoryTuple>();
@@ -30,10 +30,14 @@ type ApiProviderProps = {
const ApiContext = createVersionedContext<{ 1: ApiHolder }>('api-context');
export const ApiProvider = ({
apis,
children,
}: PropsWithChildren<ApiProviderProps>) => {
/**
* Provides an {@link @backstage/core-plugin-api#ApiHolder} for consumption in
* the React tree.
*
* @public
*/
export const ApiProvider = (props: PropsWithChildren<ApiProviderProps>) => {
const { apis, children } = props;
const parentHolder = useContext(ApiContext)?.atVersion(1);
const holder = parentHolder ? new ApiAggregator(apis, parentHolder) : apis;
@@ -32,11 +32,21 @@ class ApiRegistryBuilder {
}
}
/**
* A registry for utility APIs.
*
* @public
*/
export class ApiRegistry implements ApiHolder {
static builder() {
return new ApiRegistryBuilder();
}
/**
* Creates a new ApiRegistry with a list of API implementations.
*
* @param apis - A list of pairs mapping an ApiRef to its respective implementation
*/
static from(apis: ApiImpl[]) {
return new ApiRegistry(new Map(apis.map(([api, impl]) => [api.id, impl])));
}
@@ -44,8 +54,8 @@ export class ApiRegistry implements ApiHolder {
/**
* Creates a new ApiRegistry with a single API implementation.
*
* @param api ApiRef for the API to add
* @param impl Implementation of the API to add
* @param api - ApiRef for the API to add
* @param impl - Implementation of the API to add
*/
static with<T>(api: ApiRef<T>, impl: T): ApiRegistry {
return new ApiRegistry(new Map([[api.id, impl]]));
@@ -56,8 +66,8 @@ export class ApiRegistry implements ApiHolder {
/**
* Returns a new ApiRegistry with the provided API added to the existing ones.
*
* @param api ApiRef for the API to add
* @param impl Implementation of the API to add
* @param api - ApiRef for the API to add
* @param impl - Implementation of the API to add
*/
with<T>(api: ApiRef<T>, impl: T): ApiRegistry {
return new ApiRegistry(new Map([...this.apis, [api.id, impl]]));
@@ -22,6 +22,12 @@ import {
} from '@backstage/core-plugin-api';
import { ApiFactoryHolder } from './types';
/**
* Handles the actual on-demand instantiation and memoization of APIs out of
* an {@link ApiFactoryHolder}.
*
* @public
*/
export class ApiResolver implements ApiHolder {
/**
* Validate factories by making sure that each of the apis can be created
@@ -16,6 +16,9 @@
import { ApiFactory, ApiRef } from '@backstage/core-plugin-api';
/**
* @public
*/
export type ApiFactoryHolder = {
get<T>(
api: ApiRef<T>,
@@ -48,6 +48,8 @@ import { BackstagePlugin } from '@backstage/core-plugin-api';
* which can be rewritten at runtime to contain an additional JSON config object.
* If runtime config is present, it will be placed first in the config array, overriding
* other config values.
*
* @public
*/
export const defaultConfigLoader: AppConfigLoader = async (
// This string may be replaced at runtime to provide additional config.
@@ -100,6 +102,8 @@ export function OptionallyWrapInRouter({ children }: PropsWithChildren<{}>) {
/**
* Creates a new Backstage App.
*
* @public
*/
export function createApp(options?: AppOptions) {
const DefaultNotFoundPage = () => (
+65 -9
View File
@@ -29,11 +29,21 @@ import {
import { AppConfig } from '@backstage/config';
import { AppIcons } from './icons';
/**
* Props for the `BootErrorPage` component of {@link AppComponents}.
*
* @public
*/
export type BootErrorPageProps = {
step: 'load-config' | 'load-chunk';
error: Error;
};
/**
* The outcome of signing in on the sign-in page.
*
* @public
*/
export type SignInResult = {
/**
* User ID that will be returned by the IdentityApi
@@ -53,6 +63,11 @@ export type SignInResult = {
signOut?: () => Promise<void>;
};
/**
* Props for the `SignInPage` component of {@link AppComponents}.
*
* @public
*/
export type SignInPageProps = {
/**
* Set the sign-in result for the app. This should only be called once.
@@ -60,12 +75,22 @@ export type SignInPageProps = {
onResult(result: SignInResult): void;
};
/**
* Props for the fallback error boundary.
*
* @public
*/
export type ErrorBoundaryFallbackProps = {
plugin?: BackstagePlugin;
error: Error;
resetError: () => void;
};
/**
* A set of replaceable core components that are part of every Backstage app.
*
* @public
*/
export type AppComponents = {
NotFoundErrorPage: ComponentType<{}>;
BootErrorPage: ComponentType<BootErrorPageProps>;
@@ -91,6 +116,8 @@ export type AppComponents = {
*
* If multiple config objects are returned in the array, values in the earlier configs
* will override later ones.
*
* @public
*/
export type AppConfigLoader = () => Promise<AppConfig[]>;
@@ -123,6 +150,12 @@ type TargetRouteMap<
: never;
};
/**
* A function that can bind from external routes of a given plugin, to concrete
* routes of other plugins. See {@link createApp}.
*
* @public
*/
export type AppRouteBinder = <
ExternalRoutes extends { [name: string]: ExternalRouteRef },
>(
@@ -133,21 +166,33 @@ export type AppRouteBinder = <
>,
) => void;
// Output from newer or older plugin API versions that might not be supported by
// this version of the app API, but we don't want to break at the type checking level.
// We only use this more permissive type for the `createApp` options, as we otherwise
// want to stick to using the type for the outputs that we know about in this version
// of the app api.
type UnknownPluginOutput = {
type: string;
};
/**
* Internal helper type that represents a plugin with any type of output.
*
* @public
* @remarks
*
* The `type: string` type is there to handle output from newer or older plugin
* API versions that might not be supported by this version of the app API, but
* we don't want to break at the type checking level. We only use this more
* permissive type for the `createApp` options, as we otherwise want to stick
* to using the type for the outputs that we know about in this version of the
* app api.
*
* TODO(freben): This should be marked internal but that's not supported by the api report generation tools yet
*/
export type BackstagePluginWithAnyOutput = Omit<
BackstagePlugin<any, any>,
'output'
> & {
output(): (PluginOutput | UnknownPluginOutput)[];
output(): (PluginOutput | { type: string })[];
};
/**
* The options accepted by {@link createApp}.
*
* @public
*/
export type AppOptions = {
/**
* A collection of ApiFactories to register in the application to either
@@ -226,6 +271,11 @@ export type AppOptions = {
bindRoutes?(context: { bind: AppRouteBinder }): void;
};
/**
* The public API of the output of {@link createApp}.
*
* @public
*/
export type BackstageApp = {
/**
* Returns all plugins registered for the app.
@@ -250,6 +300,12 @@ export type BackstageApp = {
getRouter(): ComponentType<{}>;
};
/**
* The central context providing runtime app specific state that plugin views
* want to consume.
*
* @public
*/
export type AppContext = {
/**
* Get a list of all plugins that are installed in the app.
@@ -19,7 +19,7 @@ import { DefaultAuthConnector } from './DefaultAuthConnector';
import MockOAuthApi from '../../apis/implementations/OAuthRequestApi/MockOAuthApi';
import * as loginPopup from '../loginPopup';
import { UrlPatternDiscovery } from '../../apis';
import { msw } from '@backstage/test-utils';
import { setupRequestMockHandlers } from '@backstage/test-utils';
import { setupServer } from 'msw/node';
import { rest } from 'msw';
@@ -41,7 +41,7 @@ const defaultOptions = {
describe('DefaultAuthConnector', () => {
const server = setupServer();
msw.setupDefaultHandlers(server);
setupRequestMockHandlers(server);
afterEach(() => {
jest.resetAllMocks();
@@ -21,11 +21,22 @@ import {
} from '@backstage/core-plugin-api';
import React, { ReactNode } from 'react';
/**
* Props for the {@link FeatureFlagged} component.
*
* @public
*/
export type FeatureFlaggedProps = { children: ReactNode } & (
| { with: string }
| { without: string }
);
/**
* Enables or disables rendering of its children based on the state of a given
* feature flag.
*
* @public
*/
export const FeatureFlagged = (props: FeatureFlaggedProps) => {
const { children } = props;
const featureFlagApi = useApi(featureFlagsApiRef);
@@ -24,10 +24,26 @@ type RouteObject = {
children?: RouteObject[];
};
type FlatRoutesProps = {
/**
* Props for the {@link FlatRoutes} component.
*
* @public
*/
export type FlatRoutesProps = {
children: ReactNode;
};
/**
* A wrapper around a set of routes.
*
* @remarks
*
* The root of the routing hierarchy in your app should use this component,
* instead of the one from `react-router-dom`. This ensures that all of the
* plugin route and utility API wiring happens under the hood.
*
* @public
*/
export const FlatRoutes = (props: FlatRoutesProps): JSX.Element | null => {
const app = useApp();
const { NotFoundErrorPage } = app.getComponents();
@@ -15,5 +15,6 @@
*/
export { FlatRoutes } from './FlatRoutes';
export type { FlatRoutesProps } from './FlatRoutes';
export { FeatureFlagged } from './FeatureFlagged';
export type { FeatureFlaggedProps } from './FeatureFlagged';
+12
View File
@@ -1,5 +1,17 @@
# @backstage/core-components
## 0.7.2
### Patch Changes
- afd5c82ce1: Updates the d3 dependencies
- c6c2942daa: Deprecated `DismissbleBannerClassKey` and fixed the typo to make `DismissableBannerClassKey`
- Updated dependencies
- @backstage/config@0.1.11
- @backstage/theme@0.2.12
- @backstage/errors@0.1.4
- @backstage/core-plugin-api@0.1.12
## 0.7.1
### Patch Changes

Some files were not shown because too many files have changed in this diff Show More