Remove circular dependencies

In reference to issue #5563 this does the initial work to remove
all the circular dependencies that we have encountered while
building backstage using bazel. The next step will be to implement
a method to catch these circular dependencies before they get
merged in

Signed-off-by: jrusso1020 <jrusso@brex.com>
This commit is contained in:
jrusso1020
2021-05-15 10:21:34 -06:00
parent 1cfef4244e
commit 65e6c45410
70 changed files with 384 additions and 267 deletions
+15
View File
@@ -0,0 +1,15 @@
---
'@backstage/backend-common': patch
'@backstage/core': patch
'@backstage/core-api': patch
'@backstage/integration': patch
'@backstage/techdocs-common': patch
'@backstage/plugin-api-docs': patch
'@backstage/plugin-auth-backend': patch
'@backstage/plugin-cloudbuild': patch
'@backstage/plugin-github-actions': patch
'@backstage/plugin-scaffolder-backend': patch
'@backstage/plugin-techdocs': patch
---
Remove circular dependencies
+4 -4
View File
@@ -32,7 +32,7 @@ import { Writable } from 'stream';
// @public (undocumented)
export class AzureUrlReader implements UrlReader {
constructor(integration: AzureIntegration, deps: {
treeResponseFactory: ReadTreeResponseFactory;
treeResponseFactory: IReadTreeResponseFactory;
});
// (undocumented)
static factory: ReaderFactory;
@@ -49,7 +49,7 @@ export class AzureUrlReader implements UrlReader {
// @public
export class BitbucketUrlReader implements UrlReader {
constructor(integration: BitbucketIntegration, deps: {
treeResponseFactory: ReadTreeResponseFactory;
treeResponseFactory: IReadTreeResponseFactory;
});
// (undocumented)
static factory: ReaderFactory;
@@ -214,7 +214,7 @@ export class Git {
// @public
export class GithubUrlReader implements UrlReader {
constructor(integration: GitHubIntegration, deps: {
treeResponseFactory: ReadTreeResponseFactory;
treeResponseFactory: IReadTreeResponseFactory;
credentialsProvider: GithubCredentialsProvider;
});
// (undocumented)
@@ -232,7 +232,7 @@ export class GithubUrlReader implements UrlReader {
// @public (undocumented)
export class GitlabUrlReader implements UrlReader {
constructor(integration: GitLabIntegration, deps: {
treeResponseFactory: ReadTreeResponseFactory;
treeResponseFactory: IReadTreeResponseFactory;
});
// (undocumented)
static factory: ReaderFactory;
@@ -27,9 +27,9 @@ import parseGitUrl from 'git-url-parse';
import { Minimatch } from 'minimatch';
import { Readable } from 'stream';
import { NotFoundError, NotModifiedError } from '@backstage/errors';
import { ReadTreeResponseFactory } from './tree';
import { stripFirstDirectoryFromPath } from './tree/util';
import {
IReadTreeResponseFactory,
ReaderFactory,
ReadTreeOptions,
ReadTreeResponse,
@@ -50,7 +50,7 @@ export class AzureUrlReader implements UrlReader {
constructor(
private readonly integration: AzureIntegration,
private readonly deps: { treeResponseFactory: ReadTreeResponseFactory },
private readonly deps: { treeResponseFactory: IReadTreeResponseFactory },
) {}
async read(url: string): Promise<Buffer> {
@@ -27,9 +27,9 @@ import parseGitUrl from 'git-url-parse';
import { Minimatch } from 'minimatch';
import { Readable } from 'stream';
import { NotFoundError, NotModifiedError } from '@backstage/errors';
import { ReadTreeResponseFactory } from './tree';
import { stripFirstDirectoryFromPath } from './tree/util';
import {
IReadTreeResponseFactory,
ReaderFactory,
ReadTreeOptions,
ReadTreeResponse,
@@ -56,7 +56,7 @@ export class BitbucketUrlReader implements UrlReader {
constructor(
private readonly integration: BitbucketIntegration,
private readonly deps: { treeResponseFactory: ReadTreeResponseFactory },
private readonly deps: { treeResponseFactory: IReadTreeResponseFactory },
) {
const {
host,
@@ -26,8 +26,8 @@ import parseGitUrl from 'git-url-parse';
import { Minimatch } from 'minimatch';
import { Readable } from 'stream';
import { NotFoundError, NotModifiedError } from '@backstage/errors';
import { ReadTreeResponseFactory } from './tree';
import {
IReadTreeResponseFactory,
ReaderFactory,
ReadTreeOptions,
ReadTreeResponse,
@@ -65,7 +65,7 @@ export class GithubUrlReader implements UrlReader {
constructor(
private readonly integration: GitHubIntegration,
private readonly deps: {
treeResponseFactory: ReadTreeResponseFactory;
treeResponseFactory: IReadTreeResponseFactory;
credentialsProvider: GithubCredentialsProvider;
},
) {
@@ -25,9 +25,9 @@ import parseGitUrl from 'git-url-parse';
import { Minimatch } from 'minimatch';
import { Readable } from 'stream';
import { NotFoundError, NotModifiedError } from '@backstage/errors';
import { ReadTreeResponseFactory } from './tree';
import { stripFirstDirectoryFromPath } from './tree/util';
import {
IReadTreeResponseFactory,
ReaderFactory,
ReadTreeOptions,
ReadTreeResponse,
@@ -50,7 +50,7 @@ export class GitlabUrlReader implements UrlReader {
constructor(
private readonly integration: GitLabIntegration,
private readonly deps: { treeResponseFactory: ReadTreeResponseFactory },
private readonly deps: { treeResponseFactory: IReadTreeResponseFactory },
) {}
async read(url: string): Promise<Buffer> {
@@ -15,25 +15,16 @@
*/
import os from 'os';
import { Readable } from 'stream';
import { Config } from '@backstage/config';
import { ReadTreeResponse } from '../types';
import {
ReadTreeResponse,
FromArchiveOptions,
IReadTreeResponseFactory,
} from '../types';
import { TarArchiveResponse } from './TarArchiveResponse';
import { ZipArchiveResponse } from './ZipArchiveResponse';
type FromArchiveOptions = {
// A binary stream of a tar archive.
stream: Readable;
// If unset, the files at the root of the tree will be read.
// subpath must not contain the name of the top level directory.
subpath?: string;
// etag of the blob
etag: string;
// Filter passed on from the ReadTreeOptions
filter?: (path: string) => boolean;
};
export class ReadTreeResponseFactory {
export class ReadTreeResponseFactory implements IReadTreeResponseFactory {
static create(options: { config: Config }): ReadTreeResponseFactory {
return new ReadTreeResponseFactory(
options.config.getOptionalString('backend.workingDirectory') ??
+19 -2
View File
@@ -14,9 +14,9 @@
* limitations under the License.
*/
import { Readable } from 'stream';
import { Logger } from 'winston';
import { Config } from '@backstage/config';
import { ReadTreeResponseFactory } from './tree';
/**
* A generic interface for fetching plain data from URLs.
@@ -39,7 +39,7 @@ export type UrlReaderPredicateTuple = {
export type ReaderFactory = (options: {
config: Config;
logger: Logger;
treeResponseFactory: ReadTreeResponseFactory;
treeResponseFactory: IReadTreeResponseFactory;
}) => UrlReaderPredicateTuple[];
/**
@@ -105,6 +105,23 @@ export type ReadTreeResponseFile = {
content(): Promise<Buffer>;
};
export type FromArchiveOptions = {
// A binary stream of a tar archive.
stream: Readable;
// If unset, the files at the root of the tree will be read.
// subpath must not contain the name of the top level directory.
subpath?: string;
// etag of the blob
etag: string;
// Filter passed on from the ReadTreeOptions
filter?: (path: string) => boolean;
};
export interface IReadTreeResponseFactory {
fromTarArchive(options: FromArchiveOptions): Promise<ReadTreeResponse>;
fromZipArchive(options: FromArchiveOptions): Promise<ReadTreeResponse>;
}
/**
* An options object for search operations.
*/
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { IconComponent } from '../../icons';
import { IconComponent } from '../../icons/types';
import { Observable } from '../../types';
import { ApiRef, createApiRef } from '../system';
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { PublishSubject } from '../../../lib';
import { PublishSubject } from '../../../lib/subjects';
import { Observable } from '../../../types';
import { AlertApi, AlertMessage } from '../../definitions';
@@ -15,7 +15,7 @@
*/
import { AppThemeApi, AppTheme } from '../../definitions';
import { BehaviorSubject } from '../../../lib';
import { BehaviorSubject } from '../../../lib/subjects';
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 { PublishSubject } from '../../../lib';
import { PublishSubject } from '../../../lib/subjects';
import { Observable } from '../../../types';
import { ErrorApi, ErrorContext } from '../../definitions';
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { BehaviorSubject } from '../../../lib';
import { BehaviorSubject } from '../../../lib/subjects';
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/subjects';
import { Observable } from '../../../types';
/**
+3 -4
View File
@@ -15,13 +15,12 @@
*/
import { ComponentType } from 'react';
import { IconComponent, IconComponentMap, IconKey } from '../icons';
import { IconComponent, IconComponentMap, IconKey } from '../icons/types';
import { AnyExternalRoutes, BackstagePlugin } from '../plugin/types';
import { ExternalRouteRef, RouteRef } from '../routing';
import { AnyApiFactory } from '../apis';
import { ExternalRouteRef, RouteRef, SubRouteRef } from '../routing/types';
import { AnyApiFactory } from '../apis/system';
import { AppTheme, ProfileInfo } from '../apis/definitions';
import { AppConfig } from '@backstage/config';
import { SubRouteRef } from '../routing/types';
export type BootErrorPageProps = {
step: 'load-config' | 'load-chunk';
@@ -14,8 +14,8 @@
* limitations under the License.
*/
import { AuthRequester } from '../../apis';
import {
AuthRequester,
OAuthRequestApi,
AuthProvider,
DiscoveryApi,
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { SessionState } from '../../apis';
import { SessionState } from '../../apis/definitions';
import { Observable } from '../../types';
import { BehaviorSubject } from '../subjects';
@@ -15,7 +15,7 @@
*/
import { Observable } from '../../types';
import { SessionState } from '../../apis';
import { SessionState } from '../../apis/definitions';
export type GetSessionOptions = {
optional?: boolean;
+1 -1
View File
@@ -23,7 +23,7 @@ import {
ParamKeys,
OptionalParams,
} from './types';
import { IconComponent } from '../icons';
import { IconComponent } from '../icons/types';
// TODO(Rugvip): Remove this in the next breaking release, it's exported but unused
export type RouteRefConfig<Params extends AnyParams> = {
+1 -1
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { IconComponent } from '../icons';
import { IconComponent } from '../icons/types';
import { getOrCreateGlobalSingleton } from '../lib/globalObject';
export type AnyParams = { [param in string]: string } | undefined;
@@ -15,7 +15,7 @@
*/
import React, { useContext, ReactNode, PropsWithChildren } from 'react';
import { Button, makeStyles } from '@material-ui/core';
import { StepActions } from './SimpleStepperStep';
import { StepActions } from './types';
import { VerticalStepperContext } from './SimpleStepper';
const useStyles = makeStyles(theme => ({
@@ -22,6 +22,7 @@ import {
makeStyles,
} from '@material-ui/core';
import { SimpleStepperFooter } from './SimpleStepperFooter';
import { StepProps } from './types';
const useStyles = makeStyles(theme => ({
end: {
@@ -29,30 +30,6 @@ const useStyles = makeStyles(theme => ({
},
}));
export type StepActions = {
showNext?: boolean;
canNext?: () => boolean;
onNext?: () => void;
nextStep?: (current: number, last: number) => number;
nextText?: string;
showBack?: boolean;
backText?: string;
onBack?: () => void;
showRestart?: boolean;
canRestart?: () => boolean;
onRestart?: () => void;
restartText?: string;
};
export type StepProps = {
title: string;
children: React.ReactElement;
end?: boolean;
actions?: StepActions;
};
export const SimpleStepperStep = ({
title,
children,
@@ -0,0 +1,41 @@
/*
* 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';
export type StepActions = {
showNext?: boolean;
canNext?: () => boolean;
onNext?: () => void;
nextStep?: (current: number, last: number) => number;
nextText?: string;
showBack?: boolean;
backText?: string;
onBack?: () => void;
showRestart?: boolean;
canRestart?: () => boolean;
onRestart?: () => void;
restartText?: string;
};
export type StepProps = {
title: string;
children: React.ReactElement;
end?: boolean;
actions?: StepActions;
};
@@ -16,7 +16,8 @@
import React, { useMemo } from 'react';
import { Helmet } from 'react-helmet';
import { matchRoutes, useNavigate, useParams, useRoutes } from 'react-router';
import { Content, HeaderTabs } from '../../layout';
import { Content } from '../../layout/Content';
import { HeaderTabs } from '../../layout/HeaderTabs';
import { SubRoute } from './types';
export function useSelectedSubRoute(
+2 -5
View File
@@ -20,11 +20,8 @@ import { BitbucketIntegration } from './bitbucket/BitbucketIntegration';
import { GitHubIntegration } from './github/GitHubIntegration';
import { GitLabIntegration } from './gitlab/GitLabIntegration';
import { defaultScmResolveUrl } from './helpers';
import {
ScmIntegration,
ScmIntegrationRegistry,
ScmIntegrationsGroup,
} from './types';
import { ScmIntegration, ScmIntegrationsGroup } from './types';
import { ScmIntegrationRegistry } from './registry';
type IntegrationsByType = {
azure: ScmIntegrationsGroup<AzureIntegration>;
+2 -5
View File
@@ -21,8 +21,5 @@ export * from './gitlab';
export * from './googleGcs';
export { defaultScmResolveUrl } from './helpers';
export { ScmIntegrations } from './ScmIntegrations';
export type {
ScmIntegration,
ScmIntegrationRegistry,
ScmIntegrationsGroup,
} from './types';
export type { ScmIntegration, ScmIntegrationsGroup } from './types';
export type { ScmIntegrationRegistry } from './registry';
+67
View File
@@ -0,0 +1,67 @@
/*
* 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 { ScmIntegration, ScmIntegrationsGroup } from './types';
import { AzureIntegration } from './azure/AzureIntegration';
import { BitbucketIntegration } from './bitbucket/BitbucketIntegration';
import { GitHubIntegration } from './github/GitHubIntegration';
import { GitLabIntegration } from './gitlab/GitLabIntegration';
/**
* Holds all registered SCM integrations, of all types.
*/
export interface ScmIntegrationRegistry
extends ScmIntegrationsGroup<ScmIntegration> {
azure: ScmIntegrationsGroup<AzureIntegration>;
bitbucket: ScmIntegrationsGroup<BitbucketIntegration>;
github: ScmIntegrationsGroup<GitHubIntegration>;
gitlab: ScmIntegrationsGroup<GitLabIntegration>;
/**
* Resolves an absolute or relative URL in relation to a base URL.
*
* This method is adapted for use within SCM systems, so relative URLs are
* within the context of the root of the hierarchy pointed to by the base
* URL.
*
* For example, if the base URL is `<repo root url>/folder/a.yaml`, i.e.
* within the file tree of a certain repo, an absolute path of `/b.yaml` does
* not resolve to `https://hostname/b.yaml` but rather to
* `<repo root url>/b.yaml` inside the file tree of that same repo.
*
* @param options.url The (absolute or relative) URL or path to resolve
* @param options.base The base URL onto which this resolution happens
* @param options.lineNumber The line number in the target file to link to, starting with 1. Only applicable when linking to files.
*/
resolveUrl(options: {
url: string;
base: string;
lineNumber?: number;
}): string;
/**
* Resolves the edit URL for a file within the SCM system.
*
* Most SCM systems have a web interface that allows viewing and editing files
* in the repository. The returned URL directly jumps into the edit mode for
* the file.
* If this is not possible, the integration can fall back to a URL to view
* the file in the web interface.
*
* @param url The absolute URL to the file that should be edited.
*/
resolveEditUrl(url: string): string;
}
-50
View File
@@ -15,10 +15,6 @@
*/
import { Config } from '@backstage/config';
import { AzureIntegration } from './azure/AzureIntegration';
import { BitbucketIntegration } from './bitbucket/BitbucketIntegration';
import { GitHubIntegration } from './github/GitHubIntegration';
import { GitLabIntegration } from './gitlab/GitLabIntegration';
/**
* Encapsulates a single SCM integration.
@@ -95,52 +91,6 @@ export interface ScmIntegrationsGroup<T extends ScmIntegration> {
byHost(host: string): T | undefined;
}
/**
* Holds all registered SCM integrations, of all types.
*/
export interface ScmIntegrationRegistry
extends ScmIntegrationsGroup<ScmIntegration> {
azure: ScmIntegrationsGroup<AzureIntegration>;
bitbucket: ScmIntegrationsGroup<BitbucketIntegration>;
github: ScmIntegrationsGroup<GitHubIntegration>;
gitlab: ScmIntegrationsGroup<GitLabIntegration>;
/**
* Resolves an absolute or relative URL in relation to a base URL.
*
* This method is adapted for use within SCM systems, so relative URLs are
* within the context of the root of the hierarchy pointed to by the base
* URL.
*
* For example, if the base URL is `<repo root url>/folder/a.yaml`, i.e.
* within the file tree of a certain repo, an absolute path of `/b.yaml` does
* not resolve to `https://hostname/b.yaml` but rather to
* `<repo root url>/b.yaml` inside the file tree of that same repo.
*
* @param options.url The (absolute or relative) URL or path to resolve
* @param options.base The base URL onto which this resolution happens
* @param options.lineNumber The line number in the target file to link to, starting with 1. Only applicable when linking to files.
*/
resolveUrl(options: {
url: string;
base: string;
lineNumber?: number;
}): string;
/**
* Resolves the edit URL for a file within the SCM system.
*
* Most SCM systems have a web interface that allows viewing and editing files
* in the repository. The returned URL directly jumps into the edit mode for
* the file.
* If this is not possible, the integration can fall back to a URL to view
* the file in the web interface.
*
* @param url The absolute URL to the file that should be edited.
*/
resolveEditUrl(url: string): string;
}
export type ScmIntegrationsFactory<T extends ScmIntegration> = (options: {
config: Config;
}) => ScmIntegrationsGroup<T>;
@@ -18,7 +18,9 @@ import { Entity } from '@backstage/catalog-model';
import { Config } from '@backstage/config';
import { Logger } from 'winston';
import { parseReferenceAnnotation } from '../../helpers';
import { CommonGitPreparer, DirectoryPreparer, UrlPreparer } from '../prepare';
import { DirectoryPreparer } from './dir';
import { CommonGitPreparer } from './commonGit';
import { UrlPreparer } from './url';
import { PreparerBase, PreparerBuilder, RemoteProtocol } from './types';
type factoryOptions = {
+1 -1
View File
@@ -16,7 +16,7 @@
import { ApiEntity } from '@backstage/catalog-model';
import { createApiRef } from '@backstage/core';
import { ApiDefinitionWidget } from './components';
import { ApiDefinitionWidget } from './components/ApiDefinitionCard/ApiDefinitionWidget';
export const apiDocsConfigRef = createApiRef<ApiDocsConfig>({
id: 'plugin.api-docs.config',
@@ -23,7 +23,7 @@ import {
AuthProviderConfig,
} from '../../providers/types';
import { InputError } from '@backstage/errors';
import { TokenIssuer } from '../../identity';
import { TokenIssuer } from '../../identity/types';
import { verifyNonce } from './helpers';
import { postMessageResponse, ensuresXRequestedWith } from '../flow';
import { OAuthHandlers, OAuthStartRequest, OAuthRefreshRequest } from './types';
@@ -39,7 +39,7 @@ import {
PassportDoneCallback,
} from '../../lib/passport';
import { AuthProviderFactory, RedirectInfo } from '../types';
import { TokenIssuer } from '../../identity';
import { TokenIssuer } from '../../identity/types';
type PrivateInfo = {
refreshToken: string;
@@ -28,7 +28,7 @@ import {
} from '../../lib/passport';
import { AuthProviderRouteHandlers, AuthProviderFactory } from '../types';
import { postMessageResponse } from '../../lib/flow';
import { TokenIssuer } from '../../identity';
import { TokenIssuer } from '../../identity/types';
type SamlInfo = {
fullProfile: any;
+1 -1
View File
@@ -19,7 +19,7 @@ import { CatalogApi } from '@backstage/catalog-client';
import { Config } from '@backstage/config';
import express from 'express';
import { Logger } from 'winston';
import { TokenIssuer } from '../identity';
import { TokenIssuer } from '../identity/types';
export type AuthProviderConfig = {
/**
@@ -14,8 +14,8 @@
* limitations under the License.
*/
import React, { useEffect } from 'react';
import { useWorkflowRuns } from '../useWorkflowRuns';
import { WorkflowRun, WorkflowRunsTable } from '../WorkflowRunsTable';
import { useWorkflowRuns, WorkflowRun } from '../useWorkflowRuns';
import { WorkflowRunsTable } from '../WorkflowRunsTable';
import { Entity } from '@backstage/catalog-model';
import { useEntity } from '@backstage/plugin-catalog-react';
import { WorkflowRunStatus } from '../WorkflowRunStatus';
+1 -1
View File
@@ -17,7 +17,7 @@ import React from 'react';
import { Entity } from '@backstage/catalog-model';
import { useEntity } from '@backstage/plugin-catalog-react';
import { Routes, Route } from 'react-router';
import { rootRouteRef, buildRouteRef } from '../plugin';
import { rootRouteRef, buildRouteRef } from '../routes';
import { WorkflowRunDetails } from './WorkflowRunDetails';
import { WorkflowRunsTable } from './WorkflowRunsTable';
import { CLOUDBUILD_ANNOTATION } from './useProjectName';
@@ -19,26 +19,14 @@ import RetryIcon from '@material-ui/icons/Replay';
import GoogleIcon from '@material-ui/icons/CloudCircle';
import { Link as RouterLink, generatePath } from 'react-router-dom';
import { Table, TableColumn } from '@backstage/core';
import { useWorkflowRuns } from '../useWorkflowRuns';
import { useWorkflowRuns, WorkflowRun } from '../useWorkflowRuns';
import { WorkflowRunStatus } from '../WorkflowRunStatus';
import SyncIcon from '@material-ui/icons/Sync';
import { useProjectName } from '../useProjectName';
import { Entity } from '@backstage/catalog-model';
import { Substitutions } from '../../api/types';
import { buildRouteRef } from '../../plugin';
import { buildRouteRef } from '../../routes';
import moment from 'moment';
export type WorkflowRun = {
id: string;
message: string;
url?: string;
googleUrl?: string;
status: string;
substitutions: Substitutions;
createTime: string;
rerun: () => void;
};
const generatedColumns: TableColumn[] = [
{
title: 'Status',
@@ -14,4 +14,3 @@
* limitations under the License.
*/
export { WorkflowRunsTable, WorkflowRunsTableView } from './WorkflowRunsTable';
export type { WorkflowRun } from './WorkflowRunsTable';
@@ -15,10 +15,23 @@
*/
import { useState } from 'react';
import { useAsyncRetry } from 'react-use';
import { WorkflowRun } from './WorkflowRunsTable/WorkflowRunsTable';
import { cloudbuildApiRef } from '../api/CloudbuildApi';
import { useApi, errorApiRef } from '@backstage/core';
import { ActionsListWorkflowRunsForRepoResponseData } from '../api/types';
import {
ActionsListWorkflowRunsForRepoResponseData,
Substitutions,
} from '../api/types';
export type WorkflowRun = {
id: string;
message: string;
url?: string;
googleUrl?: string;
status: string;
substitutions: Substitutions;
createTime: string;
rerun: () => void;
};
export function useWorkflowRuns({ projectId }: { projectId: string }) {
const api = useApi(cloudbuildApiRef);
+1 -11
View File
@@ -15,23 +15,13 @@
*/
import {
createPlugin,
createRouteRef,
createApiFactory,
googleAuthApiRef,
createRoutableExtension,
createComponentExtension,
} from '@backstage/core';
import { cloudbuildApiRef, CloudbuildClient } from './api';
export const rootRouteRef = createRouteRef({
path: '',
title: 'Google Cloudbuild',
});
export const buildRouteRef = createRouteRef({
path: ':id',
title: 'Cloudbuild Run',
});
import { rootRouteRef } from './routes';
export const cloudbuildPlugin = createPlugin({
id: 'cloudbuild',
+26
View File
@@ -0,0 +1,26 @@
/*
* 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 { createRouteRef } from '@backstage/core';
export const rootRouteRef = createRouteRef({
path: '',
title: 'Google Cloudbuild',
});
export const buildRouteRef = createRouteRef({
path: ':id',
title: 'Cloudbuild Run',
});
@@ -34,8 +34,8 @@ import {
import ExternalLinkIcon from '@material-ui/icons/Launch';
import React, { useEffect } from 'react';
import { GITHUB_ACTIONS_ANNOTATION } from '../useProjectName';
import { useWorkflowRuns } from '../useWorkflowRuns';
import { WorkflowRun, WorkflowRunsTable } from '../WorkflowRunsTable';
import { useWorkflowRuns, WorkflowRun } from '../useWorkflowRuns';
import { WorkflowRunsTable } from '../WorkflowRunsTable';
import { WorkflowRunStatus } from '../WorkflowRunStatus';
const useStyles = makeStyles<Theme>({
@@ -17,7 +17,7 @@ import React from 'react';
import { Entity } from '@backstage/catalog-model';
import { useEntity } from '@backstage/plugin-catalog-react';
import { Routes, Route } from 'react-router';
import { rootRouteRef, buildRouteRef } from '../plugin';
import { rootRouteRef, buildRouteRef } from '../routes';
import { WorkflowRunDetails } from './WorkflowRunDetails';
import { WorkflowRunsTable } from './WorkflowRunsTable';
import { GITHUB_ACTIONS_ANNOTATION } from './useProjectName';
@@ -16,7 +16,7 @@
import { useApi, useRouteRefParams } from '@backstage/core';
import { useAsync } from 'react-use';
import { githubActionsApiRef } from '../../api';
import { buildRouteRef } from '../../plugin';
import { buildRouteRef } from '../../routes';
export const useWorkflowRunsDetails = ({
hostname,
@@ -32,32 +32,14 @@ import {
configApiRef,
useApi,
} from '@backstage/core';
import { useWorkflowRuns } from '../useWorkflowRuns';
import { useWorkflowRuns, WorkflowRun } from '../useWorkflowRuns';
import { WorkflowRunStatus } from '../WorkflowRunStatus';
import SyncIcon from '@material-ui/icons/Sync';
import { buildRouteRef } from '../../plugin';
import { buildRouteRef } from '../../routes';
import { useProjectName } from '../useProjectName';
import { Entity } from '@backstage/catalog-model';
import { readGitHubIntegrationConfigs } from '@backstage/integration';
export type WorkflowRun = {
workflowName: string;
id: string;
message: string;
url?: string;
githubUrl?: string;
source: {
branchName: string;
commit: {
hash: string;
url?: string;
};
};
status: string;
conclusion: string;
onReRunClick: () => void;
};
const generatedColumns: TableColumn[] = [
{
title: 'ID',
@@ -14,4 +14,3 @@
* limitations under the License.
*/
export { WorkflowRunsTable, WorkflowRunsTableView } from './WorkflowRunsTable';
export type { WorkflowRun } from './WorkflowRunsTable';
@@ -15,10 +15,27 @@
*/
import { useState } from 'react';
import { useAsyncRetry } from 'react-use';
import { WorkflowRun } from './WorkflowRunsTable/WorkflowRunsTable';
import { githubActionsApiRef } from '../api/GithubActionsApi';
import { useApi, errorApiRef } from '@backstage/core';
export type WorkflowRun = {
workflowName: string;
id: string;
message: string;
url?: string;
githubUrl?: string;
source: {
branchName: string;
commit: {
hash: string;
url?: string;
};
};
status: string;
conclusion: string;
onReRunClick: () => void;
};
export function useWorkflowRuns({
hostname,
owner,
+1 -13
View File
@@ -17,25 +17,13 @@
import {
configApiRef,
createPlugin,
createRouteRef,
createApiFactory,
githubAuthApiRef,
createRoutableExtension,
createComponentExtension,
} from '@backstage/core';
import { githubActionsApiRef, GithubActionsClient } from './api';
// TODO(freben): This is just a demo route for now
export const rootRouteRef = createRouteRef({
path: '',
title: 'GitHub Actions',
});
export const buildRouteRef = createRouteRef({
path: ':id',
params: ['id'],
title: 'GitHub Actions Workflow Run',
});
import { rootRouteRef } from './routes';
export const githubActionsPlugin = createPlugin({
id: 'github-actions',
+29
View File
@@ -0,0 +1,29 @@
/*
* 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 { createRouteRef } from '@backstage/core';
// TODO(freben): This is just a demo route for now
export const rootRouteRef = createRouteRef({
path: '',
title: 'GitHub Actions',
});
export const buildRouteRef = createRouteRef({
path: ':id',
params: ['id'],
title: 'GitHub Actions Workflow Run',
});
@@ -17,5 +17,3 @@ export * from './prepare';
export * from './publish';
export * from './templater';
export * from './helpers';
export { createLegacyActions } from './legacy';
+1 -1
View File
@@ -23,7 +23,7 @@ import {
rootRouteRef,
rootDocsRouteRef,
rootCatalogDocsRouteRef,
} from './plugin';
} from './routes';
import { TechDocsHome } from './home/components/TechDocsHome';
import { TechDocsPage } from './reader/components/TechDocsPage';
import { EntityPageDocs } from './EntityPageDocs';
@@ -21,7 +21,7 @@ import { Entity } from '@backstage/catalog-model';
import { Button, ItemCardGrid, ItemCardHeader } from '@backstage/core';
import { Card, CardActions, CardContent, CardMedia } from '@material-ui/core';
import { rootDocsRouteRef } from '../../plugin';
import { rootDocsRouteRef } from '../../routes';
export const DocsCardGrid = ({
entities,
@@ -22,7 +22,7 @@ import { IconButton, Tooltip } from '@material-ui/core';
import ShareIcon from '@material-ui/icons/Share';
import { Table, EmptyState, Button, SubvalueCell, Link } from '@backstage/core';
import { Entity } from '@backstage/catalog-model';
import { rootDocsRouteRef } from '../../plugin';
import { rootDocsRouteRef } from '../../routes';
export const DocsTable = ({
entities,
+5 -16
View File
@@ -20,27 +20,16 @@ import {
createComponentExtension,
createPlugin,
createRoutableExtension,
createRouteRef,
discoveryApiRef,
identityApiRef,
} from '@backstage/core';
import { techdocsApiRef, techdocsStorageApiRef } from './api';
import { TechDocsClient, TechDocsStorageClient } from './client';
export const rootRouteRef = createRouteRef({
path: '',
title: 'TechDocs Landing Page',
});
export const rootDocsRouteRef = createRouteRef({
path: ':namespace/:kind/:name/*',
title: 'Docs',
});
export const rootCatalogDocsRouteRef = createRouteRef({
path: '*',
title: 'Docs',
});
import {
rootDocsRouteRef,
rootRouteRef,
rootCatalogDocsRouteRef,
} from './routes';
export const techdocsPlugin = createPlugin({
id: 'techdocs',
@@ -23,7 +23,7 @@ import React, { useEffect, useRef, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useAsync } from 'react-use';
import { techdocsStorageApiRef } from '../../api';
import transformer, {
import {
addBaseUrl,
addGitFeedbackLink,
addLinkClickListener,
@@ -33,6 +33,7 @@ import transformer, {
rewriteDocLinks,
sanitizeDOM,
simplifyMkdocsFooter,
transform as transformer,
} from '../transformers';
import { TechDocsNotFound } from './TechDocsNotFound';
import TechDocsProgressBar from './TechDocsProgressBar';
@@ -18,7 +18,7 @@ import { TechDocsPageHeader } from './TechDocsPageHeader';
import { act } from '@testing-library/react';
import { renderInTestApp } from '@backstage/test-utils';
import { entityRouteRef } from '@backstage/plugin-catalog-react';
import { rootRouteRef } from '../../plugin';
import { rootRouteRef } from '../../routes';
describe('<TechDocsPageHeader />', () => {
it('should render a techdocs page header', async () => {
@@ -24,7 +24,7 @@ import {
import CodeIcon from '@material-ui/icons/Code';
import React from 'react';
import { AsyncState } from 'react-use/lib/useAsync';
import { rootRouteRef } from '../../plugin';
import { rootRouteRef } from '../../routes';
import { TechDocsMetadata } from '../../types';
type TechDocsPageHeaderProps = {
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { EntityName } from '@backstage/catalog-model';
import type { Transformer } from './index';
import type { Transformer } from './transformer';
import { TechDocsStorageApi } from '../../api';
type AddBaseUrlOptions = {
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { Transformer } from './index';
import type { Transformer } from './transformer';
type AddLinkClickListenerOptions = {
baseUrl: string;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import transform, { Transformer } from './index';
import { Transformer, transform } from './transformer';
describe('transform', () => {
it('calls the transformers', () => {
@@ -23,30 +23,4 @@ export * from './simplifyMkdocsFooter';
export * from './onCssReady';
export * from './sanitizeDOM';
export * from './injectCss';
// TODO(freben): move all of this out of index
export type Transformer = (dom: Element) => Element;
function transform(
html: string | Element,
transformers: Transformer[],
): Element {
let dom: Element;
if (typeof html === 'string') {
dom = new DOMParser().parseFromString(html, 'text/html').documentElement;
} else if (html instanceof Element) {
dom = html;
} else {
throw new Error('dom is not a recognized type');
}
transformers.forEach(transformer => {
dom = transformer(dom);
});
return dom;
}
export default transform;
export * from './transformer';
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { Transformer } from './index';
import type { Transformer } from './transformer';
type InjectCssOptions = {
css: string;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { Transformer } from './index';
import type { Transformer } from './transformer';
type OnCssReadyOptions = {
docStorageUrl: Promise<string>;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { Transformer } from './index';
import type { Transformer } from './transformer';
export const removeMkdocsHeader = (): Transformer => {
return dom => {
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { Transformer } from './index';
import type { Transformer } from './transformer';
export const rewriteDocLinks = (): Transformer => {
return dom => {
@@ -16,7 +16,7 @@
// @ts-ignore
import sanitizeHtml from 'sanitize-html';
import type { Transformer } from '../index';
import type { Transformer } from '../transformer';
import { TECHDOCS_ALLOWED_TAGS } from './tags';
import { TECHDOCS_ALLOWED_ATTRIBUTES } from './attributes';
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { Transformer } from './index';
import type { Transformer } from './transformer';
export const simplifyMkdocsFooter = (): Transformer => {
return dom => {
@@ -0,0 +1,38 @@
/*
* 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 type Transformer = (dom: Element) => Element;
export const transform = (
html: string | Element,
transformers: Transformer[],
): Element => {
let dom: Element;
if (typeof html === 'string') {
dom = new DOMParser().parseFromString(html, 'text/html').documentElement;
} else if (html instanceof Element) {
dom = html;
} else {
throw new Error('dom is not a recognized type');
}
transformers.forEach(transformer => {
dom = transformer(dom);
});
return dom;
};
+32
View File
@@ -0,0 +1,32 @@
/*
* 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 { createRouteRef } from '@backstage/core';
export const rootRouteRef = createRouteRef({
path: '',
title: 'TechDocs Landing Page',
});
export const rootDocsRouteRef = createRouteRef({
path: ':namespace/:kind/:name/*',
title: 'Docs',
});
export const rootCatalogDocsRouteRef = createRouteRef({
path: '*',
title: 'Docs',
});
+1 -1
View File
@@ -14,8 +14,8 @@
* limitations under the License.
*/
import transformer from '../reader/transformers';
import type { Transformer } from '../reader/transformers';
import { transform as transformer } from '../reader/transformers';
export type CreateTestShadowDomOptions = {
preTransformers: Transformer[];