Merge branch 'backstage:master' into topic/add-entity-pull-requests

This commit is contained in:
Andre Wanlin
2021-11-17 06:36:49 -06:00
committed by GitHub
214 changed files with 6751 additions and 556 deletions
@@ -22,7 +22,7 @@ import { OAuthHandlers } from './types';
const mockResponseData = {
providerInfo: {
accessToken: 'ACCESS_TOKEN',
idToken: 'ID_TOKEN',
token: 'ID_TOKEN',
expiresInSeconds: 10,
scope: 'email',
},
@@ -216,7 +216,7 @@ describe('OAuthAdapter', () => {
...mockResponseData,
backstageIdentity: {
id: mockResponseData.backstageIdentity.id,
idToken: 'my-id-token',
token: 'my-id-token',
},
});
});
@@ -233,10 +233,12 @@ export class OAuthAdapter implements AuthProviderRouteHandlers {
return;
}
if (!identity.idToken) {
identity.idToken = await this.options.tokenIssuer.issueToken({
if (!(identity.token || identity.idToken)) {
identity.token = await this.options.tokenIssuer.issueToken({
claims: { sub: identity.id },
});
} else if (!identity.token && identity.idToken) {
identity.token = identity.idToken;
}
}
+1
View File
@@ -1290,6 +1290,7 @@ export class NextCatalogBuilder {
locationService: LocationService;
router: Router;
}>;
getDefaultProcessors(): CatalogProcessor[];
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
replaceEntityPolicies(policies: EntityPolicy[]): NextCatalogBuilder;
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
@@ -265,7 +265,8 @@ export class NextCatalogBuilder {
* Sets what entity processors to use. These are responsible for reading,
* parsing, and processing entities before they are persisted in the catalog.
*
* This function replaces the default set of processors; use with care.
* This function replaces the default set of processors, consider using with
* {@link NextCatalogBuilder#getDefaultProcessors}; use with care.
*
* @param processors One or more processors
*/
@@ -275,6 +276,30 @@ export class NextCatalogBuilder {
return this;
}
/**
* Returns the default list of entity processors. These are responsible for reading,
* parsing, and processing entities before they are persisted in the catalog. Changing
* the order of processing can give more control to custom processors.
*
* Consider using with {@link NextCatalogBuilder#replaceProcessors}
*
*/
getDefaultProcessors(): CatalogProcessor[] {
const { config, logger, reader } = this.env;
const integrations = ScmIntegrations.fromConfig(config);
return [
new FileReaderProcessor(),
BitbucketDiscoveryProcessor.fromConfig(config, { logger }),
GithubDiscoveryProcessor.fromConfig(config, { logger }),
GithubOrgReaderProcessor.fromConfig(config, { logger }),
GitLabDiscoveryProcessor.fromConfig(config, { logger }),
new UrlReaderProcessor({ reader, logger }),
CodeOwnersProcessor.fromConfig(config, { logger, reader }),
new AnnotateLocationEntityProcessor({ integrations }),
];
}
/**
* Sets up the catalog to use a custom parser for entity data.
*
@@ -392,7 +417,7 @@ export class NextCatalogBuilder {
}
private buildProcessors(): CatalogProcessor[] {
const { config, logger, reader } = this.env;
const { config, reader } = this.env;
const integrations = ScmIntegrations.fromConfig(config);
this.checkDeprecatedReaderProcessors();
@@ -416,16 +441,7 @@ export class NextCatalogBuilder {
// These are only added unless the user replaced them all
if (!this.processorsReplace) {
processors.push(
new FileReaderProcessor(),
BitbucketDiscoveryProcessor.fromConfig(config, { logger }),
GithubDiscoveryProcessor.fromConfig(config, { logger }),
GithubOrgReaderProcessor.fromConfig(config, { logger }),
GitLabDiscoveryProcessor.fromConfig(config, { logger }),
new UrlReaderProcessor({ reader, logger }),
CodeOwnersProcessor.fromConfig(config, { logger, reader }),
new AnnotateLocationEntityProcessor({ integrations }),
);
processors.push(...this.getDefaultProcessors());
}
// Add the ones (if any) that the user added
@@ -131,29 +131,25 @@ function parseFilter(
db: Knex,
): Knex.QueryBuilder {
if (isEntitiesSearchFilter(filter)) {
return query.where(function filterFunction() {
return query.andWhere(function filterFunction() {
addCondition(this, db, filter);
});
}
if (isOrEntityFilter(filter)) {
let cumulativeQuery = query;
for (const subFilter of filter.anyOf ?? []) {
cumulativeQuery = cumulativeQuery.orWhere(subQuery =>
parseFilter(subFilter, subQuery, db),
);
}
return cumulativeQuery;
return query.andWhere(function filterFunction() {
for (const subFilter of filter.anyOf ?? []) {
this.orWhere(subQuery => parseFilter(subFilter, subQuery, db));
}
});
}
if (isAndEntityFilter(filter)) {
let cumulativeQuery = query;
for (const subFilter of filter.allOf ?? []) {
cumulativeQuery = cumulativeQuery.andWhere(subQuery =>
parseFilter(subFilter, subQuery, db),
);
}
return cumulativeQuery;
return query.andWhere(function filterFunction() {
for (const subFilter of filter.allOf ?? []) {
this.andWhere(subQuery => parseFilter(subFilter, subQuery, db));
}
});
}
return query;
@@ -18,8 +18,6 @@ import {
Entity,
ENTITY_DEFAULT_NAMESPACE,
LOCATION_ANNOTATION,
RELATION_CONSUMES_API,
RELATION_PROVIDES_API,
stringifyEntityRef,
} from '@backstage/catalog-model';
import {
@@ -36,7 +34,6 @@ import {
import {
catalogApiRef,
getEntityMetadataEditUrl,
getEntityRelations,
getEntitySourceLocation,
useEntity,
} from '@backstage/plugin-catalog-react';
@@ -51,7 +48,6 @@ import {
import CachedIcon from '@material-ui/icons/Cached';
import DocsIcon from '@material-ui/icons/Description';
import EditIcon from '@material-ui/icons/Edit';
import ExtensionIcon from '@material-ui/icons/Extension';
import React, { useCallback } from 'react';
import { viewTechDocRouteRef } from '../../routes';
import { AboutContent } from './AboutContent';
@@ -95,16 +91,6 @@ export function AboutCard({ variant }: AboutCardProps) {
scmIntegrationsApi,
);
const entityMetadataEditUrl = getEntityMetadataEditUrl(entity);
const providesApiRelations = getEntityRelations(
entity,
RELATION_PROVIDES_API,
);
const consumesApiRelations = getEntityRelations(
entity,
RELATION_CONSUMES_API,
);
const hasApis =
providesApiRelations.length > 0 || consumesApiRelations.length > 0;
const viewInSource: IconLinkVerticalProps = {
label: 'View Source',
@@ -126,13 +112,6 @@ export function AboutCard({ variant }: AboutCardProps) {
name: entity.metadata.name,
}),
};
const viewApi: IconLinkVerticalProps = {
title: hasApis ? '' : 'No APIs available',
label: 'View API',
disabled: !hasApis,
icon: <ExtensionIcon />,
href: 'api',
};
let cardClass = '';
if (variant === 'gridItem') {
@@ -181,9 +160,7 @@ export function AboutCard({ variant }: AboutCardProps) {
</IconButton>
</>
}
subheader={
<HeaderIconLinkRow links={[viewInSource, viewInTechDocs, viewApi]} />
}
subheader={<HeaderIconLinkRow links={[viewInSource, viewInTechDocs]} />}
/>
<Divider />
<CardContent className={cardContentClass}>
@@ -61,7 +61,7 @@ export function aggregationFor(
const days = DateTime.fromISO(endDate).diff(
DateTime.fromISO(inclusiveStartDateOf(duration, inclusiveEndDate)),
'days',
);
).days;
function nextDelta(): number {
const varianceFromBaseline = 0.15;
+4 -3
View File
@@ -31,10 +31,11 @@ export interface Config {
*/
ssl?:
| {
ca: string[];
ca?: string[];
/** @visibility secret */
key: string;
cert: string;
key?: string;
cert?: string;
rejectUnauthorized?: boolean;
}
| boolean;
/**
@@ -144,6 +144,7 @@ describe('fetch:template', () => {
name: 'test-project',
count: 1234,
itemList: ['first', 'second', 'third'],
showDummyFile: false,
},
});
@@ -163,6 +164,10 @@ describe('fetch:template', () => {
},
'.${{ values.name }}': '${{ values.itemList | dump }}',
'a-binary-file.png': aBinaryFile,
'{% if values.showDummyFile %}dummy-file.txt{% else %}{% endif %}':
'dummy file',
'${{ "dummy-file2.txt" if values.showDummyFile else "" }}':
'some dummy file',
},
});
@@ -181,6 +186,18 @@ describe('fetch:template', () => {
);
});
it('skips empty filename', async () => {
await expect(
fs.pathExists(`${workspacePath}/target/dummy-file.txt`),
).resolves.toEqual(false);
});
it('skips empty filename syntax #2', async () => {
await expect(
fs.pathExists(`${workspacePath}/target/dummy-file2.txt`),
).resolves.toEqual(false);
});
it('copies files with no templating in names or content successfully', async () => {
await expect(
fs.readFile(`${workspacePath}/target/static.txt`, 'utf-8'),
@@ -241,6 +241,11 @@ export function createFetchTemplateAction(options: {
localOutputPath = templater.renderString(localOutputPath, context);
}
const outputPath = resolvePath(outputDir, localOutputPath);
// variables have been expanded to make an empty file name
// this is due to a conditional like if values.my_condition then file-name.txt else empty string so skip
if (outputDir === outputPath) {
continue;
}
if (!renderContents && !extension) {
ctx.logger.info(
+1 -1
View File
@@ -52,7 +52,7 @@
"git-url-parse": "^11.6.0",
"humanize-duration": "^3.25.1",
"immer": "^9.0.1",
"json-schema": "^0.3.0",
"json-schema": "^0.4.0",
"lodash": "^4.17.21",
"luxon": "^2.0.2",
"qs": "^6.9.4",
+8
View File
@@ -121,6 +121,14 @@ export interface Config {
* @visibility backend
*/
s3ForcePathStyle?: boolean;
/**
* (Optional) AWS Server Side Encryption
* Defaults to undefined.
* If not set, encrypted buckets will fail to publish.
* https://docs.aws.amazon.com/AmazonS3/latest/userguide/specifying-s3-encryption.html
*/
sse?: 'aws:kms' | 'AES256';
};
}
| {
@@ -39,7 +39,7 @@ export const DocsCardGrid = ({
'techdocs.legacyUseCaseSensitiveTripletPaths',
)
? (str: string) => str
: (str: string) => str.toLocaleLowerCase();
: (str: string) => str.toLocaleLowerCase('en-US');
if (!entities) return null;
return (
@@ -56,7 +56,7 @@ export const DocsTable = ({
'techdocs.legacyUseCaseSensitiveTripletPaths',
)
? (str: string) => str
: (str: string) => str.toLocaleLowerCase();
: (str: string) => str.toLocaleLowerCase('en-US');
if (!entities) return null;
@@ -76,8 +76,12 @@ const TechDocsReaderContext = createContext<TechDocsReaderValue>(
{} as TechDocsReaderValue,
);
const TechDocsReaderProvider = ({ children }: PropsWithChildren<{}>) => {
const { namespace = '', kind = '', name = '', '*': path } = useParams();
const TechDocsReaderProvider = ({
children,
entityRef,
}: PropsWithChildren<{ entityRef: EntityName }>) => {
const { '*': path } = useParams();
const { kind, namespace, name } = entityRef;
const value = useReaderState(kind, namespace, name, path);
return (
<TechDocsReaderContext.Provider value={value}>
@@ -96,10 +100,10 @@ const TechDocsReaderProvider = ({ children }: PropsWithChildren<{}>) => {
* @internal
*/
export const withTechDocsReaderProvider =
<T extends {}>(Component: ComponentType<T>) =>
<T extends {}>(Component: ComponentType<T>, entityRef: EntityName) =>
(props: T) =>
(
<TechDocsReaderProvider>
<TechDocsReaderProvider entityRef={entityRef}>
<Component {...props} />
</TechDocsReaderProvider>
);
@@ -128,12 +132,12 @@ export const useTechDocsReader = () => useContext(TechDocsReaderContext);
* todo: Make public or stop exporting (see others: "altReaderExperiments")
* @internal
*/
export const useTechDocsReaderDom = (): Element | null => {
export const useTechDocsReaderDom = (entityRef: EntityName): Element | null => {
const navigate = useNavigate();
const theme = useTheme<BackstageTheme>();
const techdocsStorageApi = useApi(techdocsStorageApiRef);
const scmIntegrationsApi = useApi(scmIntegrationsApiRef);
const { namespace = '', kind = '', name = '' } = useParams();
const { namespace = '', kind = '', name = '' } = entityRef;
const { state, path, content: rawPage } = useTechDocsReader();
const [sidebars, setSidebars] = useState<HTMLElement[]>();
@@ -214,6 +218,16 @@ export const useTechDocsReaderDom = (): Element | null => {
.md-typeset .admonition, .md-typeset details {
font-size: 1rem;
}
/* style the checkmarks of the task list */
.md-typeset .task-list-control .task-list-indicator::before {
background-color: ${theme.palette.action.disabledBackground};
}
.md-typeset .task-list-control [type="checkbox"]:checked + .task-list-indicator:before {
background-color: ${theme.palette.success.main};
}
/**/
@media screen and (max-width: 76.1875em) {
.md-nav {
background-color: ${theme.palette.background.default};
@@ -293,8 +307,8 @@ export const useTechDocsReaderDom = (): Element | null => {
--md-details-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8.59 16.58L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42z"/></svg>');
}
:host {
--md-tasklist-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2A10 10 0 002 12a10 10 0 0010 10 10 10 0 0010-10A10 10 0 0012 2z"/></svg>');
--md-tasklist-icon--checked: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2m-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>');
--md-tasklist-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></svg>');
--md-tasklist-icon--checked: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>');
}
`,
}),
@@ -305,9 +319,11 @@ export const useTechDocsReaderDom = (): Element | null => {
namespace,
scmIntegrationsApi,
techdocsStorageApi,
theme.palette.action.disabledBackground,
theme.palette.background.default,
theme.palette.background.paper,
theme.palette.primary.main,
theme.palette.success.main,
theme.palette.text.primary,
theme.typography.fontFamily,
],
@@ -400,7 +416,7 @@ const TheReader = ({
withSearch = true,
}: Props) => {
const classes = useStyles();
const dom = useTechDocsReaderDom();
const dom = useTechDocsReaderDom(entityRef);
const shadowDomRef = useRef<HTMLDivElement>(null);
const onReadyRef = useRef<() => void>(onReady);
@@ -440,7 +456,7 @@ export const Reader = ({
onReady = () => {},
withSearch = true,
}: Props) => (
<TechDocsReaderProvider>
<TechDocsReaderProvider entityRef={entityRef}>
<TheReader
entityRef={entityRef}
onReady={onReady}
+2 -1
View File
@@ -37,10 +37,11 @@ export const ProviderSettingsItem: ({
// @public (undocumented)
export const Router: ({ providerSettings }: Props) => JSX.Element;
// Warning: (ae-forgotten-export) The symbol "SettingsProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "Settings" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const Settings: () => JSX.Element;
export const Settings: (props: SettingsProps) => JSX.Element;
// Warning: (ae-missing-release-tag) "UserSettingsAppearanceCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -18,13 +18,14 @@ import React from 'react';
import SettingsIcon from '@material-ui/icons/Settings';
import { settingsRouteRef } from '../plugin';
import { SidebarItem } from '@backstage/core-components';
import { IconComponent } from '@backstage/core-plugin-api';
export const Settings = () => {
return (
<SidebarItem
text="Settings"
to={settingsRouteRef.path}
icon={SettingsIcon}
/>
);
type SettingsProps = {
icon?: IconComponent;
};
export const Settings = (props: SettingsProps) => {
const Icon = props.icon ? props.icon : SettingsIcon;
return <SidebarItem text="Settings" to={settingsRouteRef.path} icon={Icon} />;
};