Merge branch 'master' of https://github.com/backstage/backstage into marley/7678-pull-request-dashboard

This commit is contained in:
Marley Powell
2021-11-08 09:32:09 +00:00
575 changed files with 11666 additions and 3404 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Switched to dynamically determining the packages that are unsafe to repack when executing the CLI within the Backstage main repo.
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/core-components': patch
'@backstage/theme': patch
---
Added a warning variant to `DismissableBanner` component. If you are using a
custom theme, you will need to add the optional `palette.banner.warning` color,
otherwise this variant will fall back to the `palette.banner.error` color.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-common': patch
---
AWSS3UrlReader now throws a `NotModifiedError` (exported from @backstage/backend-common) when s3 returns a 304 response.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-kafka': patch
---
Use IdentityApi to provide Auth Token for KafkaBackendClient Api calls
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/test-utils': patch
---
Update Keyboard deprecation with a link to the recommended successor
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend': patch
---
Expose some classes and interfaces public so TaskWorkers can run externally from the scaffolder API.
+42
View File
@@ -0,0 +1,42 @@
---
'@backstage/plugin-scaffolder': patch
'@backstage/plugin-scaffolder-backend': patch
---
Adds a new endpoint for consuming logs from the Scaffolder that uses long polling instead of Server Sent Events.
This is useful if Backstage is accessed from an environment that doesn't support SSE correctly, which happens in combination with certain enterprise HTTP Proxy servers.
It is intended to switch the endpoint globally for the whole instance.
If you want to use it, you can provide a reconfigured API to the `scaffolderApiRef`:
```tsx
// packages/app/src/apis.ts
// ...
import {
scaffolderApiRef,
ScaffolderClient,
} from '@backstage/plugin-scaffolder';
export const apis: AnyApiFactory[] = [
// ...
createApiFactory({
api: scaffolderApiRef,
deps: {
discoveryApi: discoveryApiRef,
identityApi: identityApiRef,
scmIntegrationsApi: scmIntegrationsApiRef,
},
factory: ({ discoveryApi, identityApi, scmIntegrationsApi }) =>
new ScaffolderClient({
discoveryApi,
identityApi,
scmIntegrationsApi,
// use long polling instead of an eventsource
useLongPollingLogs: true,
}),
}),
];
```
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-app-api': patch
---
I have added default icons for the catalog, scaffolder, techdocs, and search.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/techdocs-common': patch
---
Default TechDocs container used at docs generation-time is now [v0.3.5](https://github.com/backstage/techdocs-container/releases/tag/v0.3.5).
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/techdocs-common': patch
---
Updated to properly join URL segments under any OS for both AWS S3 and GCP
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/errors': patch
---
Add `stringifyError` that is useful for logging e.g. `Something went wrong, ${stringifyError(e)}`
+6 -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:
@@ -40,6 +40,8 @@ jobs:
node-version: ${{ matrix.node-version }}
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.0.2
- name: setup chrome
uses: browser-actions/setup-chrome@latest
- name: yarn install
run: yarn install --frozen-lockfile
@@ -48,3 +50,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
+2 -5
View File
@@ -53,6 +53,8 @@ jobs:
id: yarn-cache
if: steps.cache-modules.outputs.cache-hit != 'true'
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: setup chrome
uses: browser-actions/setup-chrome@latest
- name: cache global yarn cache
uses: actions/cache@v2
if: steps.cache-modules.outputs.cache-hit != 'true'
@@ -72,8 +74,3 @@ jobs:
run: |
sudo sysctl fs.inotify.max_user_watches=524288
yarn e2e-test run
env:
POSTGRES_HOST: localhost
POSTGRES_PORT: ${{ job.services.postgres.ports[5432] }}
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
+4
View File
@@ -59,3 +59,7 @@
| [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.|
| [Volvofinans Bank](https://www.volvofinans.se) | [Johan Hammar](https://github.com/johanhammar) | Developer portal enabling engineers to manage and explore software and documentation. |
+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)
Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

@@ -502,6 +502,8 @@ spec:
lifecycle: production
owner: artist-relations-team
system: artist-engagement-portal
dependsOn:
- resource:default/artists-db
providesApis:
- artist-api
```
@@ -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`.
*/
+8
View File
@@ -63,6 +63,10 @@ techdocs:
# (Required) Cloud Storage Bucket Name
bucketName: 'techdocs-storage'
# (Optional) Location in storage bucket to save files
# If not set, the default location will be the root of the storage bucket
bucketRootPath: '/'
# (Optional) An API key is required to write to a storage bucket.
# If missing, GOOGLE_APPLICATION_CREDENTIALS environment variable will be used.
# https://cloud.google.com/docs/authentication/production
@@ -75,6 +79,10 @@ techdocs:
# (Required) AWS S3 Bucket Name
bucketName: 'techdocs-storage'
# (Optional) Location in storage bucket to save files
# If not set, the default location will be the root of the storage bucket
bucketRootPath: '/'
# (Optional) An API key is required to write to a storage bucket.
# If not set, environment variables or aws config file will be used to authenticate.
# https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/loading-node-credentials-environment.html
+81 -31
View File
@@ -4,49 +4,99 @@ title: Getting Started
description: Documentation on How to get started with Backstage
---
There are two different ways to get started with Backstage:
For most Backstage installations, installing the standalone app will bring you
the best and most streamlined experience. In this guide you will:
- **Recommended:** Create a standalone app
- **Contributors:** Clone the Backstage repository
- Deploy Backstage Standalone with npm packages
- Run Backstage Standalone with a SQLite in-memory database and demo content
Creating a standalone app makes it simpler to customize the application for your
needs and stay up to date with the project. You will depend on `@backstage`
packages from npm, making your app much smaller. This is the recommended
approach for most installations.
This guide assumes a basic understanding of working on a Linux based operating
system using tools like apt-get, npm, yarn, curl. Docker knowledge is also
helpful for making the best use of your Backstage installation.
If you want to contribute plugins or to the project in general, it's easier to
fork and clone the repository. The `@backstage` packages will be included in the
clone. That will let you stay up to date with the latest changes, and give you
an easier path to make Pull Requests.
If you are planning to contribute plugins or the project in general, we advise
you to use the
[Getting Started for Contributors](https://backstage.io/docs/getting-started/running-backstage-locally)
guide to do a repository-based installation.
### Prerequisites
- Access to a Linux-based operating system, such as Linux, MacOS or
[Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/)
- An account with elevated rights
- `curl` or `wget` installed
- Node.js Active LTS Release installed (currently v14) using one of these
methods:
- Using `nvm` (recommended)
- [Installing nvm](https://github.com/nvm-sh/nvm#install--update-script)
- [Install and change Node version with nvm](https://nodejs.org/en/download/package-manager/#nvm)
- [Binary Download](https://nodejs.org/en/download/)
- [Package manager](https://nodejs.org/en/download/package-manager/)
- [Using NodeSource packages](https://github.com/nodesource/distributions/blob/master/README.md)
- `yarn` [Installation](https://classic.yarnpkg.com/en/docs/install)
- `docker` [installation](https://docs.docker.com/engine/install/)
- `git` [installation](https://github.com/git-guides/install-git)
- If the system is not directly accessible over your network, the following
ports need to be opened: 3000, 7000
### Create your Backstage App
Backstage provides the `@backstage/create-app` package to scaffold standalone
instances of Backstage. You will need to have
[Node.js](https://nodejs.org/en/download/) Active LTS Release installed
(currently v14) and [Yarn](https://classic.yarnpkg.com/en/docs/install). You
will also need to have [Docker](https://docs.docker.com/engine/install/)
installed to use some features like Software Templates and TechDocs.
Using `npx` you can then run the following to create an app in a chosen
subdirectory of your current working directory:
To install the Backstage Standalone app, we make use of `npx`, a tool to run
Node executables straight from the registry. Running the command below will
install Backstage. The wizard will create a subdirectory inside your current
working directory.
```bash
npx @backstage/create-app
```
You will be taken through a wizard to create your app. You can read more about
this process in [Create an app](./create-an-app.md).
The wizard will ask you
### Contributing to Backstage
- The name of the app, which will also be the name of the directory
- The database type to use for the backend. For this guide, you'll be using the
SQLite option.
If you intend to make changes to the core project's packages, certain plugins,
or project documentation, then you can fork and clone
[https://github.com/backstage/backstage](https://github.com/backstage/backstage).
<p align='center'>
<img src='../assets/getting-started/wizard.png' alt='Screenshot of the wizard asking for a name for the app, and a selection menu for the database.'>
</p>
This will let you run the latest code off of the `master` branch, fix bugs or
contribute new features, run test suites, etc.
### Run the Backstage app
You can read more in our
[CONTRIBUTING](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md)
guide, which can help you get setup with a Backstage development environment.
When the installation is complete you can go to the application directory and
start the app. The `yarn dev` command will run both the frontend and backend as
separate processes (named `[0]` and `[1]`) in the same window.
```bash
cd my-backstage-app
yarn dev
```
<p align='center'>
<img src='../assets/getting-started/startup.png' alt='Screenshot of the command output, with the message web pack compiled successfully.'>
</p>
It might take a little while, but as soon as the message
`[0] webpack compiled successfully` appears, you can open a browser and directly
navigate to your freshly installed Backstage portal at `http://localhost:3000`.
You can start exploring the demo immediately.
<p align='center'>
<img src='../assets/getting-started/portal.png' alt='Screenshot of the Backstage portal.'>
</p>
Congratulations! That should be it. Let us know how it went:
[on discord](https://discord.gg/EBHEGzX), file issues for any
[feature](https://github.com/backstage/backstage/issues/new?labels=help+wanted&template=feature_template.md)
or
[plugin suggestions](https://github.com/backstage/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME),
or
[bugs](https://github.com/backstage/backstage/issues/new?labels=bug&template=bug_template.md)
you have, and feel free to
[contribute](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md)!
The most common next steps are to configure Backstage, add a plugin and moving
to a more persistent database:
- [Setting up Authentication](https://backstage.io/docs/auth/)
- [Switching from SQLite to PostgresQL](https://backstage.io/docs/tutorials/switching-sqlite-postgres)
- [Adding a plugin](https://backstage.io/docs/getting-started/configure-app-with-plugins)
-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",
+1 -1
View File
@@ -83,7 +83,7 @@
window,
document,
'script',
'https://www.datadoghq-browser-agent.com/datadog-rum.js',
'https://www.datadoghq-browser-agent.com/datadog-rum-v3.js',
'DD_RUM',
);
DD_RUM.onReady(function () {
+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
+10 -6
View File
@@ -18,7 +18,7 @@ import { GithubCredentialsProvider } from '@backstage/integration';
import { GitHubIntegration } from '@backstage/integration';
import { GitLabIntegration } from '@backstage/integration';
import { isChildPath } from '@backstage/cli-common';
import { JsonValue } from '@backstage/config';
import { JsonValue } from '@backstage/types';
import { Knex } from 'knex';
import { Logger as Logger_2 } from 'winston';
import { MergeResult } from 'isomorphic-git';
@@ -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;
+11 -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,12 +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",
@@ -77,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",
@@ -98,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"
+1 -1
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { JsonValue } from '@backstage/config';
import { JsonValue } from '@backstage/types';
import { createHash } from 'crypto';
import Keyv from 'keyv';
+47 -4
View File
@@ -18,8 +18,38 @@ import { resolve as resolvePath } from 'path';
import parseArgs from 'minimist';
import { Logger } from 'winston';
import { findPaths } from '@backstage/cli-common';
import { Config, ConfigReader, JsonValue } from '@backstage/config';
import { loadConfig } from '@backstage/config-loader';
import {
loadConfigSchema,
loadConfig,
ConfigSchema,
} from '@backstage/config-loader';
import { AppConfig, Config, ConfigReader } from '@backstage/config';
import { JsonValue } from '@backstage/types';
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({});
@@ -150,11 +180,20 @@ export async function loadBackendConfig(options: {
const args = parseArgs(options.argv);
const configPaths: string[] = [args.config ?? []].flat();
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: configPaths.map(opt => resolvePath(opt)),
@@ -186,5 +225,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;
}
@@ -15,7 +15,8 @@
*/
import { Knex } from 'knex';
import { omit } from 'lodash';
import { Config, ConfigReader, JsonObject } from '@backstage/config';
import { Config, ConfigReader } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import {
createDatabaseClient,
ensureDatabaseExists,
@@ -14,7 +14,8 @@
* limitations under the License.
*/
import { Config, JsonObject } from '@backstage/config';
import { Config } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import { InputError } from '@backstage/errors';
import knexFactory, { Knex } from 'knex';
import { mergeDatabaseConfig } from './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: {
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ConfigReader, JsonObject } from '@backstage/config';
import { ConfigReader } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import { getVoidLogger } from '../logging';
import { DefaultReadTreeResponseFactory } from './tree';
import { AwsS3UrlReader } from './AwsS3UrlReader';
@@ -25,6 +27,7 @@ import { UrlReaderPredicateTuple } from './types';
import AWSMock from 'aws-sdk-mock';
import aws from 'aws-sdk';
import path from 'path';
import { NotModifiedError } from '@backstage/errors';
const treeResponseFactory = DefaultReadTreeResponseFactory.create({
config: new ConfigReader({}),
@@ -129,29 +132,37 @@ describe('AwsS3UrlReader', () => {
});
describe('read', () => {
AWSMock.setSDKInstance(aws);
AWSMock.mock(
'S3',
'getObject',
Buffer.from(
require('fs').readFileSync(
path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'),
let awsS3UrlReader: AwsS3UrlReader;
beforeAll(() => {
AWSMock.setSDKInstance(aws);
AWSMock.mock(
'S3',
'getObject',
Buffer.from(
require('fs').readFileSync(
path.resolve(
__dirname,
'__fixtures__/awsS3/awsS3-mock-object.yaml',
),
),
),
),
);
const s3 = new aws.S3();
const awsS3UrlReader = new AwsS3UrlReader(
new AwsS3Integration(
readAwsS3IntegrationConfig(
new ConfigReader({
host: 'amazonaws.com',
accessKeyId: 'fake-access-key',
secretAccessKey: 'fake-secret-key',
}),
);
const s3 = new aws.S3();
awsS3UrlReader = new AwsS3UrlReader(
new AwsS3Integration(
readAwsS3IntegrationConfig(
new ConfigReader({
host: 'amazonaws.com',
accessKeyId: 'fake-access-key',
secretAccessKey: 'fake-secret-key',
}),
),
),
),
{ s3, treeResponseFactory },
);
{ s3, treeResponseFactory },
);
});
it('returns contents of an object in a bucket', async () => {
const response = await awsS3UrlReader.read(
@@ -174,32 +185,39 @@ describe('AwsS3UrlReader', () => {
});
describe('readUrl', () => {
AWSMock.setSDKInstance(aws);
let awsS3UrlReader: AwsS3UrlReader;
AWSMock.mock(
'S3',
'getObject',
Buffer.from(
require('fs').readFileSync(
path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'),
beforeAll(() => {
AWSMock.setSDKInstance(aws);
AWSMock.mock(
'S3',
'getObject',
Buffer.from(
require('fs').readFileSync(
path.resolve(
__dirname,
'__fixtures__/awsS3/awsS3-mock-object.yaml',
),
),
),
),
);
);
const s3 = new aws.S3();
const s3 = new aws.S3();
const awsS3UrlReader = new AwsS3UrlReader(
new AwsS3Integration(
readAwsS3IntegrationConfig(
new ConfigReader({
host: 'amazonaws.com',
accessKeyId: 'fake-access-key',
secretAccessKey: 'fake-secret-key',
}),
awsS3UrlReader = new AwsS3UrlReader(
new AwsS3Integration(
readAwsS3IntegrationConfig(
new ConfigReader({
host: 'amazonaws.com',
accessKeyId: 'fake-access-key',
secretAccessKey: 'fake-secret-key',
}),
),
),
),
{ s3, treeResponseFactory },
);
{ s3, treeResponseFactory },
);
});
it('returns contents of an object in a bucket', async () => {
const response = await awsS3UrlReader.readUrl(
@@ -221,40 +239,89 @@ describe('AwsS3UrlReader', () => {
);
});
});
describe('readUrl with etag', () => {
let awsS3UrlReader: AwsS3UrlReader;
beforeAll(() => {
AWSMock.setSDKInstance(aws);
AWSMock.mock('S3', 'getObject', (_, callback) => {
callback({ statusCode: 304 }, null);
});
const s3 = new aws.S3();
awsS3UrlReader = new AwsS3UrlReader(
new AwsS3Integration(
readAwsS3IntegrationConfig(
new ConfigReader({
host: 'amazonaws.com',
accessKeyId: 'fake-access-key',
secretAccessKey: 'fake-secret-key',
}),
),
),
{ s3, treeResponseFactory },
);
});
it('returns contents of an object in a bucket', async () => {
await expect(
awsS3UrlReader.readUrl(
'https://test-bucket.s3.us-east-2.amazonaws.com/awsS3-mock-object.yaml',
{
etag: 'abc123',
},
),
).rejects.toThrow(NotModifiedError);
});
});
describe('readTree', () => {
const object: aws.S3.Types.Object = {
Key: 'awsS3-mock-object.yaml',
};
const objectList: aws.S3.ObjectList = [object];
const output: aws.S3.Types.ListObjectsV2Output = {
Contents: objectList,
};
AWSMock.setSDKInstance(aws);
AWSMock.mock('S3', 'listObjectsV2', output);
let awsS3UrlReader: AwsS3UrlReader;
AWSMock.mock(
'S3',
'getObject',
Buffer.from(
require('fs').readFileSync(
path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'),
),
),
);
beforeAll(() => {
const object: aws.S3.Types.Object = {
Key: 'awsS3-mock-object.yaml',
};
const s3 = new aws.S3();
const awsS3UrlReader = new AwsS3UrlReader(
new AwsS3Integration(
readAwsS3IntegrationConfig(
new ConfigReader({
host: '.amazonaws.com',
accessKeyId: 'fake-access-key',
secretAccessKey: 'fake-secret-key',
}),
const objectList: aws.S3.ObjectList = [object];
const output: aws.S3.Types.ListObjectsV2Output = {
Contents: objectList,
};
AWSMock.setSDKInstance(aws);
AWSMock.mock('S3', 'listObjectsV2', output);
AWSMock.mock(
'S3',
'getObject',
Buffer.from(
require('fs').readFileSync(
path.resolve(
__dirname,
'__fixtures__/awsS3/awsS3-mock-object.yaml',
),
),
),
),
{ s3, treeResponseFactory },
);
);
const s3 = new aws.S3();
awsS3UrlReader = new AwsS3UrlReader(
new AwsS3Integration(
readAwsS3IntegrationConfig(
new ConfigReader({
host: '.amazonaws.com',
accessKeyId: 'fake-access-key',
secretAccessKey: 'fake-secret-key',
}),
),
),
{ s3, treeResponseFactory },
);
});
it('returns contents of an object in a bucket', async () => {
const response = await awsS3UrlReader.readTree(
'https://test.s3.us-east-2.amazonaws.com',
@@ -27,7 +27,7 @@ import {
} from './types';
import getRawBody from 'raw-body';
import { AwsS3Integration, ScmIntegrations } from '@backstage/integration';
import { ForwardedError } from '@backstage/errors';
import { ForwardedError, NotModifiedError } from '@backstage/errors';
import { ListObjectsV2Output, ObjectList } from 'aws-sdk/clients/s3';
const parseURL = (
@@ -163,6 +163,10 @@ export class AwsS3UrlReader implements UrlReader {
etag: etag,
};
} catch (e) {
if (e.statusCode === 304) {
throw new NotModifiedError();
}
throw new ForwardedError('Could not retrieve file from S3', e);
}
}
@@ -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(() => {
@@ -14,7 +14,8 @@
* limitations under the License.
*/
import { ConfigReader, JsonObject } from '@backstage/config';
import { ConfigReader } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import { getVoidLogger } from '../logging';
import { DefaultReadTreeResponseFactory } from './tree';
import { GoogleGcsUrlReader } from './GoogleGcsUrlReader';
@@ -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, '\\$&');
};
+3
View File
@@ -48,6 +48,9 @@
"@backstage/plugin-search-backend-module-elasticsearch": "^0.0.4",
"@backstage/plugin-search-backend-module-pg": "^0.2.1",
"@backstage/plugin-techdocs-backend": "^0.10.5",
"@backstage/plugin-tech-insights-backend": "^0.1.0",
"@backstage/plugin-tech-insights-node": "^0.1.0",
"@backstage/plugin-tech-insights-backend-module-jsonfc": "^0.1.0",
"@backstage/plugin-todo-backend": "^0.1.13",
"@gitbeaker/node": "^30.2.0",
"@octokit/rest": "^18.5.3",
+5
View File
@@ -53,6 +53,7 @@ import graphql from './plugins/graphql';
import app from './plugins/app';
import badges from './plugins/badges';
import jenkins from './plugins/jenkins';
import techInsights from './plugins/techInsights';
import { PluginEnvironment } from './types';
function makeCreateEnv(config: Config) {
@@ -107,12 +108,16 @@ async function main() {
const appEnv = useHotMemoize(module, () => createEnv('app'));
const badgesEnv = useHotMemoize(module, () => createEnv('badges'));
const jenkinsEnv = useHotMemoize(module, () => createEnv('jenkins'));
const techInsightsEnv = useHotMemoize(module, () =>
createEnv('tech-insights'),
);
const apiRouter = Router();
apiRouter.use('/catalog', await catalog(catalogEnv));
apiRouter.use('/code-coverage', await codeCoverage(codeCoverageEnv));
apiRouter.use('/rollbar', await rollbar(rollbarEnv));
apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv));
apiRouter.use('/tech-insights', await techInsights(techInsightsEnv));
apiRouter.use('/auth', await auth(authEnv));
apiRouter.use('/azure-devops', await azureDevOps(azureDevOpsEnv));
apiRouter.use('/search', await search(searchEnv));
@@ -0,0 +1,107 @@
/*
* 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 {
createRouter,
buildTechInsightsContext,
createFactRetrieverRegistration,
} from '@backstage/plugin-tech-insights-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import { CatalogClient } from '@backstage/catalog-client';
import {
JsonRulesEngineFactCheckerFactory,
JSON_RULE_ENGINE_CHECK_TYPE,
} from '@backstage/plugin-tech-insights-backend-module-jsonfc';
export default async function createPlugin({
logger,
config,
discovery,
database,
}: PluginEnvironment): Promise<Router> {
const techInsightsContext = await buildTechInsightsContext({
logger,
config,
database,
discovery,
factRetrievers: [
createFactRetrieverRegistration('5 4 * * 6', {
// Example cron, At 04:05 on Saturday.
id: 'testRetriever',
version: '1.1.2',
entityFilter: [{ kind: 'component' }], // EntityFilter to be used in the future (creating checks, graphs etc.) to figure out which entities this fact retrieves data for.
schema: {
examplenumberfact: {
type: 'integer',
description: 'Example fact returning a number',
},
},
handler: async _ctx => {
const catalogClient = new CatalogClient({
discoveryApi: discovery,
});
const entities = await catalogClient.getEntities({
filter: [{ kind: 'component' }],
});
return Promise.resolve(
entities.items.map(it => {
return {
entity: {
namespace: it.metadata.namespace!!,
kind: it.kind,
name: it.metadata.name,
},
facts: {
examplenumberfact: 2,
},
};
}),
);
},
}),
],
factCheckerFactory: new JsonRulesEngineFactCheckerFactory({
checks: [
{
id: 'simpleTestCheck',
type: JSON_RULE_ENGINE_CHECK_TYPE,
name: 'simpleTestCheck',
description: 'Simple Check For Testing',
factIds: ['testRetriever'],
rule: {
conditions: {
all: [
{
fact: 'examplenumberfact',
operator: 'lessThan',
value: 5,
},
],
},
},
},
],
logger,
}),
});
return await createRouter({
...techInsightsContext,
logger,
config,
});
}
+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
+2 -2
View File
@@ -3,9 +3,9 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { JsonObject } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import { JSONSchema7 } from 'json-schema';
import { JsonValue } from '@backstage/config';
import { JsonValue } from '@backstage/types';
import { SerializedError } from '@backstage/errors';
import * as yup from 'yup';
+5 -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,9 @@
"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",
"ajv": "^7.0.3",
@@ -41,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"
+1 -1
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { JsonObject } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import { EntityName } from '../types';
import { UNSTABLE_EntityStatus } from './EntityStatus';
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { JsonObject } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import type { Entity } from '../entity/Entity';
import schema from '../schema/kinds/Template.v1beta2.schema.json';
import { ajvCompiledJsonSchemaValidator } from './util';
+1 -1
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { JsonValue } from '@backstage/config';
import { JsonValue } from '@backstage/types';
import { JSONSchema7 } from 'json-schema';
/**
+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
+15 -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,11 @@
"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",
"@lerna/project": "^4.0.0",
@@ -40,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",
@@ -109,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",
+1 -1
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { JsonObject } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import { mergeConfigSchemas } from '@backstage/config-loader';
import { Command } from 'commander';
import { JSONSchema7 as JSONSchema } from 'json-schema';
+1 -1
View File
@@ -18,7 +18,7 @@ import { Command } from 'commander';
import { JSONSchema7 as JSONSchema } from 'json-schema';
import { stringify as stringifyYaml } from 'yaml';
import { loadCliConfig } from '../../lib/config';
import { JsonObject } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import { mergeConfigSchemas } from '@backstage/config-loader';
export default async (cmd: Command) => {
+1 -1
View File
@@ -15,7 +15,7 @@
*/
import { YarnInfoInspectData } from '../../lib/versioning';
import { JsonObject } from '@backstage/config';
import { JsonObject } from '@backstage/types';
/**
* TODO: possible types
+6 -4
View File
@@ -24,14 +24,16 @@ import { tmpdir } from 'os';
import tar, { CreateOptions } from 'tar';
import { paths } from '../paths';
import { run } from '../run';
import { packageVersions } from '../version';
import { ParallelOption } from '../parallel';
import {
dependencies as cliDependencies,
devDependencies as cliDevDependencies,
} from '../../../package.json';
// These packages aren't safe to pack in parallel since the CLI depends on them
const UNSAFE_PACKAGES = [
...Object.keys(packageVersions),
'@backstage/cli-common',
'@backstage/config-loader',
...Object.keys(cliDependencies),
...Object.keys(cliDevDependencies),
];
type LernaPackage = {
@@ -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
+1 -1
View File
@@ -4,7 +4,7 @@
```ts
import { AppConfig } from '@backstage/config';
import { JsonObject } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import { JSONSchema7 } from 'json-schema';
// @public
+6 -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,10 @@
"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",
"chokidar": "^3.5.2",
@@ -40,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"
},
+2 -1
View File
@@ -14,7 +14,8 @@
* limitations under the License.
*/
import { AppConfig, JsonObject } from '@backstage/config';
import { AppConfig } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import { assertError } from '@backstage/errors';
const ENV_PREFIX = 'APP_CONFIG_';
@@ -23,7 +23,7 @@ import {
} from 'path';
import { ConfigSchemaPackageEntry } from './types';
import { getProgramFromFiles, generateSchema } from 'typescript-json-schema';
import { JsonObject } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import { assertError } from '@backstage/errors';
type Item = {
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { JsonObject } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import { ConfigVisibility } from './types';
import { filterByVisibility, filterErrorsByVisibility } from './filtering';
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { JsonObject, JsonValue } from '@backstage/config';
import { JsonObject, JsonValue } from '@backstage/types';
import {
ConfigVisibility,
DEFAULT_CONFIG_VISIBILITY,
@@ -14,7 +14,8 @@
* limitations under the License.
*/
import { AppConfig, JsonObject } from '@backstage/config';
import { AppConfig } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import { compileConfigSchemas } from './compile';
import { collectConfigSchemas } from './collect';
import { filterByVisibility, filterErrorsByVisibility } from './filtering';
@@ -14,7 +14,8 @@
* limitations under the License.
*/
import { AppConfig, JsonObject } from '@backstage/config';
import { AppConfig } from '@backstage/config';
import { JsonObject } from '@backstage/types';
/**
* An sub-set of configuration schema.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { JsonObject, JsonValue } from '@backstage/config';
import { JsonObject, JsonValue } from '@backstage/types';
import { assertError } from '@backstage/errors';
import { TransformFunc } from './types';
import { isObject } from './utils';
@@ -16,7 +16,7 @@
import yaml from 'yaml';
import { extname, dirname, resolve as resolvePath } from 'path';
import { JsonObject, JsonValue } from '@backstage/config';
import { JsonObject, JsonValue } from '@backstage/types';
import { isObject } from './utils';
import { TransformFunc, EnvFunc, ReadFileFunc } from './types';
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { JsonValue } from '@backstage/config';
import { JsonValue } from '@backstage/types';
import { TransformFunc, EnvFunc } from './types';
/**
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { JsonValue } from '@backstage/config';
import { JsonValue } from '@backstage/types';
export type EnvFunc = (name: string) => Promise<string | undefined>;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { JsonValue, JsonObject } from '@backstage/config';
import { JsonValue, JsonObject } from '@backstage/types';
export function isObject(obj: JsonValue | undefined): obj is JsonObject {
if (typeof obj !== 'object') {
+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
+19 -33
View File
@@ -3,10 +3,15 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import type { JsonArray as JsonArray_2 } from '@backstage/types';
import { JsonObject as JsonObject_2 } from '@backstage/types';
import type { JsonPrimitive as JsonPrimitive_2 } from '@backstage/types';
import { JsonValue as JsonValue_2 } from '@backstage/types';
// @public
export type AppConfig = {
context: string;
data: JsonObject;
data: JsonObject_2;
filteredKeys?: string[];
};
@@ -17,8 +22,8 @@ export type Config = {
};
has(key: string): boolean;
keys(): string[];
get<T = JsonValue>(key?: string): T;
getOptional<T = JsonValue>(key?: string): T | undefined;
get<T = JsonValue_2>(key?: string): T;
getOptional<T = JsonValue_2>(key?: string): T | undefined;
getConfig(key: string): Config;
getOptionalConfig(key: string): Config | undefined;
getConfigArray(key: string): Config[];
@@ -36,58 +41,39 @@ export type Config = {
// @public
export class ConfigReader implements Config {
constructor(
data: JsonObject | undefined,
data: JsonObject_2 | undefined,
context?: string,
fallback?: ConfigReader | undefined,
prefix?: string,
);
// (undocumented)
static fromConfigs(configs: AppConfig[]): ConfigReader;
// (undocumented)
get<T = JsonValue>(key?: string): T;
// (undocumented)
get<T = JsonValue_2>(key?: string): T;
getBoolean(key: string): boolean;
// (undocumented)
getConfig(key: string): ConfigReader;
// (undocumented)
getConfigArray(key: string): ConfigReader[];
// (undocumented)
getNumber(key: string): number;
// (undocumented)
getOptional<T = JsonValue>(key?: string): T | undefined;
// (undocumented)
getOptional<T = JsonValue_2>(key?: string): T | undefined;
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[];
}
// @public
export interface JsonArray extends Array<JsonValue> {}
// @public @deprecated
export type JsonArray = JsonArray_2;
// @public
export type JsonObject = {
[key in string]?: JsonValue;
};
// @public @deprecated
export type JsonObject = JsonObject_2;
// @public
export type JsonPrimitive = number | string | boolean | null;
// @public @deprecated
export type JsonPrimitive = JsonPrimitive_2;
// @public
export type JsonValue = JsonObject | JsonArray | JsonPrimitive;
// @public @deprecated
export type JsonValue = JsonValue_2;
```
+3 -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",
@@ -30,10 +30,11 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/types": "^0.1.1",
"lodash": "^4.17.21"
},
"devDependencies": {
"@backstage/test-utils-core": "^0.1.2",
"@backstage/test-utils": "^0.1.20",
"@types/jest": "^26.0.7",
"@types/node": "^14.14.32"
},
+55
View File
@@ -0,0 +1,55 @@
/*
* 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.
*/
// Temporarily re-export the JSON types from @backstage/types
import type {
JsonArray as CoreJsonArray,
JsonObject as CoreJsonObject,
JsonPrimitive as CoreJsonPrimitive,
JsonValue as CoreJsonValue,
} from '@backstage/types';
/**
* A type representing all allowed JSON primitive values.
*
* @public
* @deprecated Please use the same type from `@backstage/types` instead
*/
export type JsonPrimitive = CoreJsonPrimitive;
/**
* A type representing all allowed JSON object values.
*
* @public
* @deprecated Please use the same type from `@backstage/types` instead
*/
export type JsonObject = CoreJsonObject;
/**
* A type representing all allowed JSON array values.
*
* @public
* @deprecated Please use the same type from `@backstage/types` instead
*/
export type JsonArray = CoreJsonArray;
/**
* A type representing all allowed JSON values.
*
* @public
* @deprecated Please use the same type from `@backstage/types` instead
*/
export type JsonValue = CoreJsonValue;
+3 -4
View File
@@ -20,12 +20,11 @@
* @packageDocumentation
*/
export { ConfigReader } from './reader';
export type {
AppConfig,
Config,
JsonArray,
JsonObject,
JsonPrimitive,
JsonValue,
} from './types';
} from './deprecatedTypes';
export { ConfigReader } from './reader';
export type { AppConfig, Config } from './types';
+1 -1
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { withLogCollector } from '@backstage/test-utils-core';
import { withLogCollector } from '@backstage/test-utils';
import { ConfigReader } from './reader';
const DATA = {
+21 -1
View File
@@ -14,7 +14,8 @@
* limitations under the License.
*/
import { AppConfig, Config, JsonValue, JsonObject } from './types';
import { JsonValue, JsonObject } from '@backstage/types';
import { AppConfig, Config } from './types';
import cloneDeep from 'lodash/cloneDeep';
import mergeWith from 'lodash/mergeWith';
@@ -71,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);
@@ -95,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) {
@@ -103,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) {
@@ -117,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);
@@ -153,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) {
@@ -161,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);
@@ -176,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) {
@@ -184,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)) {
@@ -219,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) {
@@ -227,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,
@@ -246,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) {
@@ -254,6 +269,7 @@ export class ConfigReader implements Config {
return value;
}
/** {@inheritdoc Config.getOptionalBoolean} */
getOptionalBoolean(key: string): boolean | undefined {
return this.readConfigValue(
key,
@@ -261,6 +277,7 @@ export class ConfigReader implements Config {
);
}
/** {@inheritdoc Config.getString} */
getString(key: string): string {
const value = this.getOptionalString(key);
if (value === undefined) {
@@ -269,6 +286,7 @@ export class ConfigReader implements Config {
return value;
}
/** {@inheritdoc Config.getOptionalString} */
getOptionalString(key: string): string | undefined {
return this.readConfigValue(
key,
@@ -277,6 +295,7 @@ export class ConfigReader implements Config {
);
}
/** {@inheritdoc Config.getStringArray} */
getStringArray(key: string): string[] {
const value = this.getOptionalStringArray(key);
if (value === undefined) {
@@ -285,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)) {
+1 -27
View File
@@ -14,33 +14,7 @@
* limitations under the License.
*/
/**
* A type representing all allowed JSON primitive values.
*
* @public
*/
export type JsonPrimitive = number | string | boolean | null;
/**
* A type representing all allowed JSON object values.
*
* @public
*/
export type JsonObject = { [key in string]?: JsonValue };
/**
* A type representing all allowed JSON array values.
*
* @public
*/
export interface JsonArray extends Array<JsonValue> {}
/**
* A type representing all allowed JSON values.
*
* @public
*/
export type JsonValue = JsonObject | JsonArray | JsonPrimitive;
import { JsonObject, JsonValue } from '@backstage/types';
/**
* A serialized form of configuration data that carries additional context.
+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
+56 -139
View File
@@ -40,7 +40,7 @@ import { IconComponent } from '@backstage/core-plugin-api';
import { microsoftAuthApiRef } from '@backstage/core-plugin-api';
import { OAuthApi } from '@backstage/core-plugin-api';
import { OAuthRequestApi } from '@backstage/core-plugin-api';
import { Observable } from '@backstage/core-plugin-api';
import { Observable } from '@backstage/types';
import { oktaAuthApiRef } from '@backstage/core-plugin-api';
import { oneloginAuthApiRef } from '@backstage/core-plugin-api';
import { OpenIdConnectApi } from '@backstage/core-plugin-api';
@@ -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
```
+9 -9
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,11 @@
"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",
"@material-ui/icons": "^4.9.1",
@@ -45,9 +46,8 @@
"zen-observable": "^0.8.15"
},
"devDependencies": {
"@backstage/cli": "^0.8.0",
"@backstage/test-utils": "^0.1.19",
"@backstage/test-utils-core": "^0.1.3",
"@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",
@@ -13,11 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AlertApi, AlertMessage, Observable } from '@backstage/core-plugin-api';
import { AlertApi, AlertMessage } from '@backstage/core-plugin-api';
import { Observable } from '@backstage/types';
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 {}

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