packages/core: move api dir into separate core-api package

This commit is contained in:
Patrik Oldsberg
2020-05-27 02:00:18 +02:00
parent ccf7ff095b
commit e17ff776c1
97 changed files with 376 additions and 84 deletions
+8
View File
@@ -0,0 +1,8 @@
module.exports = {
extends: [require.resolve('@backstage/cli/config/eslint')],
rules: {
// TODO: add prop types to JS and remove
'react/prop-types': 0,
'jest/expect-expect': 0,
},
};
+22
View File
@@ -0,0 +1,22 @@
# @backstage/core-api
This package provides the core API used by Backstage plugins and apps.
## Installation
Do not install this package directly, it is reexported by `@backstage/core`. Install that instead:
```sh
$ npm install --save @backstage/core
```
or
```sh
$ yarn add @backstage/core
```
## Documentation
- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md)
- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md)
+71
View File
@@ -0,0 +1,71 @@
{
"name": "@backstage/core-api",
"description": "Core API used by Backstage plugins and apps",
"version": "0.1.1-alpha.6",
"private": false,
"publishConfig": {
"access": "public"
},
"homepage": "https://backstage.io",
"repository": {
"type": "git",
"url": "https://github.com/spotify/backstage",
"directory": "packages/core"
},
"keywords": [
"backstage"
],
"license": "Apache-2.0",
"main": "dist/index.esm.js",
"main:src": "src/index.ts",
"types": "src/index.ts",
"scripts": {
"build": "backstage-cli plugin:build",
"lint": "backstage-cli lint",
"test": "backstage-cli test",
"prepack": "backstage-cli prepack",
"postpack": "backstage-cli postpack",
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/theme": "^0.1.1-alpha.6",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
"@types/classnames": "^2.2.9",
"@types/google-protobuf": "^3.7.2",
"@types/jest": "^25.2.2",
"@types/node": "^12.0.0",
"@types/react-helmet": "^5.0.15",
"@types/react-sparklines": "^1.7.0",
"@types/zen-observable": "^0.8.0",
"classnames": "^2.2.6",
"clsx": "^1.1.0",
"lodash": "^4.17.15",
"material-table": "^1.58.0",
"prop-types": "^15.7.2",
"rc-progress": "^3.0.0",
"react": "^16.12.0",
"react-addons-text-content": "0.0.4",
"react-dom": "^16.12.0",
"react-helmet": "5.2.1",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-sparklines": "^1.7.0",
"react-syntax-highlighter": "^12.2.1",
"react-use": "^14.2.0",
"zen-observable": "^0.8.15"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.6",
"@backstage/test-utils": "^0.1.1-alpha.6",
"@backstage/test-utils-core": "^0.1.1-alpha.6",
"@testing-library/jest-dom": "^5.7.0",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^10.2.4",
"jest-fetch-mock": "^3.0.3"
},
"files": [
"dist/**/*.{js,d.ts}"
]
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { IconComponent } from '../../../icons';
import { IconComponent } from '../../icons';
import { Observable } from '../../types';
import { createApiRef } from '../ApiRef';
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AlertApi, AlertMessage } from '../../../..';
import { PublishSubject } from '../lib';
import { AlertApi, AlertMessage } from '../..';
import { PublishSubject } from '../../../lib';
import { Observable } from '../../../types';
/**
@@ -15,7 +15,7 @@
*/
import { AppThemeApi, AppTheme } from '../../definitions';
import { BehaviorSubject } from '../lib';
import { BehaviorSubject } from '../../../lib';
import { Observable } from '../../../types';
const STORAGE_KEY = 'theme';
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ErrorApi, ErrorContext, AlertApi } from '../../../..';
import { ErrorApi, ErrorContext, AlertApi } from '../..';
/**
* Decorates an ErrorApi by also forwarding error messages
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ErrorApi, ErrorContext } from '../../../..';
import { PublishSubject } from '../lib';
import { ErrorApi, ErrorContext } from '../..';
import { PublishSubject } from '../../../lib';
import { Observable } from '../../../types';
/**
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { BehaviorSubject } from '../lib';
import { BehaviorSubject } from '../../../lib';
import { Observable } from '../../../types';
type RequestQueueEntry<ResultType> = {
@@ -21,7 +21,7 @@ import {
AuthRequesterOptions,
} from '../../definitions';
import { OAuthPendingRequests, PendingRequest } from './OAuthPendingRequests';
import { BehaviorSubject } from '../lib';
import { BehaviorSubject } from '../../../lib';
import { Observable } from '../../../types';
/**
@@ -15,7 +15,7 @@
*/
import GoogleIcon from '@material-ui/icons/AcUnit';
import { DefaultAuthConnector } from '../../lib/AuthConnector';
import { DefaultAuthConnector } from '../../../../lib/AuthConnector';
import { GoogleSession } from './types';
import {
OAuthApi,
@@ -23,8 +23,8 @@ import {
IdTokenOptions,
} from '../../../definitions/auth';
import { OAuthRequestApi, AuthProvider } from '../../../definitions';
import { SessionManager } from '../../lib/AuthSessionManager/types';
import { RefreshingAuthSessionManager } from '../../lib/AuthSessionManager';
import { SessionManager } from '../../../../lib/AuthSessionManager/types';
import { RefreshingAuthSessionManager } from '../../../../lib/AuthSessionManager';
type CreateOptions = {
// TODO(Rugvip): These two should be grabbed from global config when available, they're not unique to GoogleAuth
@@ -17,19 +17,13 @@
import React, { ComponentType, FC } from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import { AppContextProvider } from './AppContext';
import { BackstageApp, AppOptions, AppComponents } from './types';
import { BackstageApp, AppComponents } from './types';
import { BackstagePlugin } from '../plugin';
import { FeatureFlagsRegistryItem } from './FeatureFlags';
import { featureFlagsApiRef } from '../apis/definitions';
import ErrorPage from '../../layout/ErrorPage';
import { AppThemeProvider } from './AppThemeProvider';
import {
IconComponent,
SystemIcons,
SystemIconKey,
defaultSystemIcons,
} from '../../icons';
import { IconComponent, SystemIcons, SystemIconKey } from '../icons';
import {
ApiHolder,
ApiProvider,
@@ -38,7 +32,6 @@ import {
AppThemeSelector,
appThemeApiRef,
} from '../apis';
import { lightTheme, darkTheme } from '@backstage/theme';
import { ApiAggregator } from '../apis/ApiAggregator';
type FullAppOptions = {
@@ -49,7 +42,7 @@ type FullAppOptions = {
themes: AppTheme[];
};
class AppImpl implements BackstageApp {
export class PrivateAppImpl implements BackstageApp {
private readonly apis: ApiHolder;
private readonly icons: SystemIcons;
private readonly plugins: BackstagePlugin[];
@@ -175,40 +168,3 @@ class AppImpl implements BackstageApp {
}
}
}
/**
* Creates a new Backstage App.
*/
export function createApp(options?: AppOptions) {
const DefaultNotFoundPage = () => (
<ErrorPage status="404" statusMessage="PAGE NOT FOUND" />
);
const apis = options?.apis ?? ApiRegistry.from([]);
const icons = { ...defaultSystemIcons, ...options?.icons };
const plugins = options?.plugins ?? [];
const components = {
NotFoundErrorPage: DefaultNotFoundPage,
...options?.components,
};
const themes = options?.themes ?? [
{
id: 'light',
title: 'Light Theme',
variant: 'light',
theme: lightTheme,
},
{
id: 'dark',
title: 'Dark Theme',
variant: 'dark',
theme: darkTheme,
},
];
const app = new AppImpl({ apis, icons, plugins, components, themes });
app.verify();
return app;
}
@@ -14,7 +14,6 @@
* limitations under the License.
*/
export { createApp } from './App';
export { FeatureFlags } from './FeatureFlags';
export { useApp } from './AppContext';
export * from './types';
@@ -15,7 +15,7 @@
*/
import { ComponentType } from 'react';
import { IconComponent, SystemIconKey, SystemIcons } from '../../icons';
import { IconComponent, SystemIconKey, SystemIcons } from '../icons';
import { BackstagePlugin } from '../plugin';
import { ApiHolder } from '../apis';
import { AppTheme } from '../apis/definitions';
+39
View File
@@ -0,0 +1,39 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { SvgIconProps } from '@material-ui/core';
import PeopleIcon from '@material-ui/icons/People';
import PersonIcon from '@material-ui/icons/Person';
import React, { FC } from 'react';
import { useApp } from '../app/AppContext';
import { IconComponent, SystemIconKey, SystemIcons } from './types';
export const defaultSystemIcons: SystemIcons = {
user: PersonIcon,
group: PeopleIcon,
};
const overridableSystemIcon = (key: SystemIconKey): IconComponent => {
const Component: FC<SvgIconProps> = props => {
const app = useApp();
const Icon = app.getSystemIcon(key);
return <Icon {...props} />;
};
return Component;
};
export const UserIcon = overridableSystemIcon('user');
export const GroupIcon = overridableSystemIcon('group');
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 2020 Spotify AB
*
* 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.
*/
export * from './icons';
export * from './types';
+22
View File
@@ -0,0 +1,22 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { ComponentType } from 'react';
import { SvgIconProps } from '@material-ui/core';
export type IconComponent = ComponentType<SvgIconProps>;
export type SystemIconKey = 'user' | 'group';
export type SystemIcons = { [key in SystemIconKey]: IconComponent };
+19
View File
@@ -0,0 +1,19 @@
/*
* Copyright 2020 Spotify AB
*
* 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.
*/
export * from './public';
import * as privateExports from './private';
export default privateExports;
@@ -16,7 +16,7 @@
import ProviderIcon from '@material-ui/icons/AcUnit';
import { DefaultAuthConnector } from './DefaultAuthConnector';
import MockOAuthApi from '../../OAuthRequestApi/MockOAuthApi';
import MockOAuthApi from '../../apis/implementations/OAuthRequestApi/MockOAuthApi';
import * as loginPopup from '../loginPopup';
const anyFetch = fetch as any;
@@ -14,8 +14,8 @@
* limitations under the License.
*/
import { AuthRequester } from '../../..';
import { OAuthRequestApi, AuthProvider } from '../../../definitions';
import { AuthRequester } from '../../apis';
import { OAuthRequestApi, AuthProvider } from '../../apis/definitions';
import { showLoginPopup } from '../loginPopup';
import { AuthConnector } from './types';
+20
View File
@@ -0,0 +1,20 @@
/*
* Copyright 2020 Spotify AB
*
* 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.
*/
export * from './subjects';
export * from './loginPopup';
export * from './AuthConnector';
export * from './AuthSessionManager';
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { Observable } from '../../../types';
import { Observable } from '../types';
import ObservableImpl from 'zen-observable';
// TODO(Rugvip): These are stopgap and probably incomplete implementations of subjects.
@@ -14,4 +14,4 @@
* limitations under the License.
*/
export * from './subjects';
export { PrivateAppImpl } from './app/App';
+22
View File
@@ -0,0 +1,22 @@
/*
* Copyright 2020 Spotify AB
*
* 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.
*/
export * from './apis';
export * from './app';
export * from './icons';
export * from './plugin';
export * from './routing';
export * from './types';
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { IconComponent } from '../../icons';
import { IconComponent } from '../icons';
export type RouteRef = {
path: string;
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 2020 Spotify AB
*
* 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 '@testing-library/jest-dom';
require('jest-fetch-mock').enableMocks();
+1
View File
@@ -28,6 +28,7 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/core-api": "^0.1.1-alpha.6",
"@backstage/theme": "^0.1.1-alpha.6",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
+69
View File
@@ -0,0 +1,69 @@
/*
* Copyright 2020 Spotify AB
*
* 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 React from 'react';
import privateExports, {
AppOptions,
ApiRegistry,
defaultSystemIcons,
} from '@backstage/core-api';
import ErrorPage from '../layout/ErrorPage';
import { lightTheme, darkTheme } from '@backstage/theme';
const { PrivateAppImpl } = privateExports;
// createApp is defined in core, and not core-api, since we need access
// to the components inside core to provide defaults.
// The actual implementation of the app class still lives in core-api,
// as it needs to be used by dev- and test-utils.
/**
* Creates a new Backstage App.
*/
export function createApp(options?: AppOptions) {
const DefaultNotFoundPage = () => (
<ErrorPage status="404" statusMessage="PAGE NOT FOUND" />
);
const apis = options?.apis ?? ApiRegistry.from([]);
const icons = { ...defaultSystemIcons, ...options?.icons };
const plugins = options?.plugins ?? [];
const components = {
NotFoundErrorPage: DefaultNotFoundPage,
...options?.components,
};
const themes = options?.themes ?? [
{
id: 'light',
title: 'Light Theme',
variant: 'light',
theme: lightTheme,
},
{
id: 'dark',
title: 'Dark Theme',
variant: 'dark',
theme: darkTheme,
},
];
const app = new PrivateAppImpl({ apis, icons, plugins, components, themes });
app.verify();
return app;
}
+1 -5
View File
@@ -14,8 +14,4 @@
* limitations under the License.
*/
export * from './apis';
export * from './app';
export * from './routing';
export * from './plugin';
export * from './types';
export { createApp } from './createApp';
@@ -18,7 +18,7 @@ import React, { FC, useEffect, useState } from 'react';
import { Snackbar, IconButton } from '@material-ui/core';
import CloseIcon from '@material-ui/icons/Close';
import { Alert } from '@material-ui/lab';
import { AlertMessage, useApi, alertApiRef } from '../../api';
import { AlertMessage, useApi, alertApiRef } from '@backstage/core-api';
type Props = {};
@@ -16,7 +16,12 @@
import React from 'react';
import CopyTextButton from '.';
import { ApiProvider, errorApiRef, ApiRegistry, ErrorApi } from '../../api';
import {
ApiProvider,
errorApiRef,
ApiRegistry,
ErrorApi,
} from '@backstage/core-api';
export default {
title: 'CopyTextButton',
@@ -18,7 +18,12 @@ import React from 'react';
import { render } from '@testing-library/react';
import { wrapInThemedTestApp } from '@backstage/test-utils';
import CopyTextButton from './CopyTextButton';
import { ApiRegistry, errorApiRef, ApiProvider, ErrorApi } from '../../api';
import {
ApiRegistry,
errorApiRef,
ApiProvider,
ErrorApi,
} from '@backstage/core-api';
jest.mock('popper.js', () => {
const PopperJS = jest.requireActual('popper.js');
@@ -19,9 +19,9 @@ import { IconButton, makeStyles, Tooltip } from '@material-ui/core';
import PropTypes from 'prop-types';
import CopyIcon from '@material-ui/icons/FileCopy';
import { BackstageTheme } from '@backstage/theme';
import { errorApiRef, useApi } from '../../api';
import { errorApiRef, useApi } from '@backstage/core-api';
const useStyles = makeStyles<BackstageTheme>((theme) => ({
const useStyles = makeStyles<BackstageTheme>(theme => ({
button: {
'&:hover': {
backgroundColor: theme.palette.highlight,
@@ -56,7 +56,7 @@ const defaultProps = {
tooltipText: 'Text copied to clipboard',
};
const CopyTextButton: FC<Props> = (props) => {
const CopyTextButton: FC<Props> = props => {
const { text, tooltipDelay, tooltipText } = {
...defaultProps,
...props,
@@ -66,7 +66,7 @@ const CopyTextButton: FC<Props> = (props) => {
const inputRef = useRef<HTMLInputElement>(null);
const [open, setOpen] = useState(false);
const handleCopyClick: MouseEventHandler = (e) => {
const handleCopyClick: MouseEventHandler = e => {
e.stopPropagation();
setOpen(true);
@@ -23,7 +23,7 @@ import {
Theme,
} from '@material-ui/core';
import React, { FC, useState } from 'react';
import { PendingAuthRequest } from '../../api';
import { PendingAuthRequest } from '@backstage/core-api';
const useItemStyles = makeStyles<Theme>(theme => ({
root: {
@@ -27,7 +27,7 @@ import {
import React, { FC, useMemo, useState } from 'react';
import { useObservable } from 'react-use';
import LoginRequestListItem from './LoginRequestListItem';
import { useApi, oauthRequestApiRef } from '../../api';
import { useApi, oauthRequestApiRef } from '@backstage/core-api';
const useStyles = makeStyles<Theme>(theme => ({
dialog: {
+2 -2
View File
@@ -18,7 +18,7 @@ import { SvgIconProps } from '@material-ui/core';
import PeopleIcon from '@material-ui/icons/People';
import PersonIcon from '@material-ui/icons/Person';
import React, { FC } from 'react';
import { useApp } from '../api/app/AppContext';
import { useApp } from '@backstage/core-api';
import { IconComponent, SystemIconKey, SystemIcons } from './types';
export const defaultSystemIcons: SystemIcons = {
@@ -27,7 +27,7 @@ export const defaultSystemIcons: SystemIcons = {
};
const overridableSystemIcon = (key: SystemIconKey): IconComponent => {
const Component: FC<SvgIconProps> = (props) => {
const Component: FC<SvgIconProps> = props => {
const app = useApp();
const Icon = app.getSystemIcon(key);
return <Icon {...props} />;
+2
View File
@@ -14,6 +14,8 @@
* limitations under the License.
*/
export * from '@backstage/core-api';
export * from './api';
export { default as Page } from './layout/Page';
export { gradients, pageTheme } from './layout/Page';
@@ -19,7 +19,7 @@ import { useObservable } from 'react-use';
import LightIcon from '@material-ui/icons/WbSunny';
import DarkIcon from '@material-ui/icons/Brightness2';
import AutoIcon from '@material-ui/icons/BrightnessAuto';
import { appThemeApiRef, useApi } from '../../api';
import { appThemeApiRef, useApi } from '@backstage/core-api';
import { SidebarItem } from './Items';
export const SidebarThemeToggle: FC<{}> = () => {