Merge pull request #6756 from backstage/dependabot/npm_and_yarn/prettier-2.3.2
chore(deps-dev): bump prettier from 2.2.1 to 2.3.2
This commit is contained in:
@@ -30,19 +30,19 @@ import openapiApiEntity from './openapi-example-api.yaml';
|
||||
import otherApiEntity from './other-example-api.yaml';
|
||||
import { Content, Header, Page } from '@backstage/core-components';
|
||||
|
||||
const mockEntities = ([
|
||||
const mockEntities = [
|
||||
openapiApiEntity,
|
||||
asyncapiApiEntity,
|
||||
graphqlApiEntity,
|
||||
otherApiEntity,
|
||||
] as unknown) as Entity[];
|
||||
] as unknown as Entity[];
|
||||
|
||||
createDevApp()
|
||||
.registerApi({
|
||||
api: catalogApiRef,
|
||||
deps: {},
|
||||
factory: () =>
|
||||
(({
|
||||
({
|
||||
async getEntities() {
|
||||
return {
|
||||
items: mockEntities.slice(),
|
||||
@@ -51,7 +51,7 @@ createDevApp()
|
||||
async getEntityByName(name: string) {
|
||||
return mockEntities.find(e => e.metadata.name === name);
|
||||
},
|
||||
} as unknown) as typeof catalogApiRef.T),
|
||||
} as unknown as typeof catalogApiRef.T),
|
||||
})
|
||||
.registerApi({
|
||||
api: apiDocsConfigRef,
|
||||
@@ -72,7 +72,7 @@ createDevApp()
|
||||
<Page themeId="home">
|
||||
<Header title="OpenAPI" />
|
||||
<Content>
|
||||
<EntityProvider entity={(openapiApiEntity as any) as Entity}>
|
||||
<EntityProvider entity={openapiApiEntity as any as Entity}>
|
||||
<EntityApiDefinitionCard />
|
||||
</EntityProvider>
|
||||
</Content>
|
||||
@@ -85,7 +85,7 @@ createDevApp()
|
||||
<Page themeId="home">
|
||||
<Header title="AsyncAPI" />
|
||||
<Content>
|
||||
<EntityProvider entity={(asyncapiApiEntity as any) as Entity}>
|
||||
<EntityProvider entity={asyncapiApiEntity as any as Entity}>
|
||||
<EntityApiDefinitionCard />
|
||||
</EntityProvider>
|
||||
</Content>
|
||||
@@ -98,7 +98,7 @@ createDevApp()
|
||||
<Page themeId="home">
|
||||
<Header title="GraphQL" />
|
||||
<Content>
|
||||
<EntityProvider entity={(graphqlApiEntity as any) as Entity}>
|
||||
<EntityProvider entity={graphqlApiEntity as any as Entity}>
|
||||
<EntityApiDefinitionCard />
|
||||
</EntityProvider>
|
||||
</Content>
|
||||
@@ -111,7 +111,7 @@ createDevApp()
|
||||
<Page themeId="home">
|
||||
<Header title="Other" />
|
||||
<Content>
|
||||
<EntityProvider entity={(otherApiEntity as any) as Entity}>
|
||||
<EntityProvider entity={otherApiEntity as any as Entity}>
|
||||
<EntityApiDefinitionCard />
|
||||
</EntityProvider>
|
||||
</Content>
|
||||
|
||||
+14
-11
@@ -88,13 +88,15 @@ const useStyles = makeStyles(theme => ({
|
||||
'& .asyncapi__enum': {
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
'& .asyncapi__info, .asyncapi__channel, .asyncapi__channels > div, .asyncapi__schema, .asyncapi__channel-operations-list .asyncapi__messages-list-item .asyncapi__message, .asyncapi__message, .asyncapi__server, .asyncapi__servers > div, .asyncapi__messages > div, .asyncapi__schemas > div': {
|
||||
'background-color': 'inherit',
|
||||
},
|
||||
'& .asyncapi__channel-parameters-header, .asyncapi__channel-operations-header, .asyncapi__channel-operation-oneOf-subscribe-header, .asyncapi__channel-operation-oneOf-publish-header, .asyncapi__channel-operation-message-header, .asyncapi__message-header, .asyncapi__message-header-title, .asyncapi__message-header-title > h3, .asyncapi__bindings, .asyncapi__bindings-header, .asyncapi__bindings-header > h4': {
|
||||
'background-color': 'inherit',
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
'& .asyncapi__info, .asyncapi__channel, .asyncapi__channels > div, .asyncapi__schema, .asyncapi__channel-operations-list .asyncapi__messages-list-item .asyncapi__message, .asyncapi__message, .asyncapi__server, .asyncapi__servers > div, .asyncapi__messages > div, .asyncapi__schemas > div':
|
||||
{
|
||||
'background-color': 'inherit',
|
||||
},
|
||||
'& .asyncapi__channel-parameters-header, .asyncapi__channel-operations-header, .asyncapi__channel-operation-oneOf-subscribe-header, .asyncapi__channel-operation-oneOf-publish-header, .asyncapi__channel-operation-message-header, .asyncapi__message-header, .asyncapi__message-header-title, .asyncapi__message-header-title > h3, .asyncapi__bindings, .asyncapi__bindings-header, .asyncapi__bindings-header > h4':
|
||||
{
|
||||
'background-color': 'inherit',
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
'& .asyncapi__additional-properties-notice': {
|
||||
color: theme.palette.text.hint,
|
||||
},
|
||||
@@ -104,10 +106,11 @@ const useStyles = makeStyles(theme => ({
|
||||
'& .asyncapi__schema-example-header-title': {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
'& .asyncapi__message-headers-header, .asyncapi__message-payload-header, .asyncapi__server-variables-header, .asyncapi__server-security-header': {
|
||||
'background-color': 'inherit',
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
'& .asyncapi__message-headers-header, .asyncapi__message-payload-header, .asyncapi__server-variables-header, .asyncapi__server-security-header':
|
||||
{
|
||||
'background-color': 'inherit',
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
'& .asyncapi__table-header': {
|
||||
background: theme.palette.background.default,
|
||||
},
|
||||
|
||||
+21
-16
@@ -30,22 +30,26 @@ const useStyles = makeStyles(theme => ({
|
||||
'& .scheme-container': {
|
||||
'background-color': theme.palette.background.default,
|
||||
},
|
||||
'& .opblock-tag, .opblock-tag small, table thead tr td, table thead tr th': {
|
||||
color: theme.palette.text.primary,
|
||||
'border-color': theme.palette.divider,
|
||||
},
|
||||
'& .opblock-tag, .opblock-tag small, table thead tr td, table thead tr th':
|
||||
{
|
||||
color: theme.palette.text.primary,
|
||||
'border-color': theme.palette.divider,
|
||||
},
|
||||
'& section.models, section.models.is-open h4': {
|
||||
'border-color': theme.palette.divider,
|
||||
},
|
||||
'& .opblock .opblock-summary-description, .parameter__type, table.headers td, .model-title, .model .property.primitive, section h3': {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
'& .opblock .opblock-summary-operation-id, .opblock .opblock-summary-path, .opblock .opblock-summary-path__deprecated, .opblock .opblock-section-header h4, .parameter__name, .response-col_status, .response-col_links, .responses-inner h4, .swagger-ui .responses-inner h5, .opblock-section-header .btn, .tab li, .info li, .info p, .info table, section.models h4, .info .title, table.model tr.description, .property-row': {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
'& .opblock .opblock-section-header, .model-box, section.models .model-container': {
|
||||
background: theme.palette.background.default,
|
||||
},
|
||||
'& .opblock .opblock-summary-description, .parameter__type, table.headers td, .model-title, .model .property.primitive, section h3':
|
||||
{
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
'& .opblock .opblock-summary-operation-id, .opblock .opblock-summary-path, .opblock .opblock-summary-path__deprecated, .opblock .opblock-section-header h4, .parameter__name, .response-col_status, .response-col_links, .responses-inner h4, .swagger-ui .responses-inner h5, .opblock-section-header .btn, .tab li, .info li, .info p, .info table, section.models h4, .info .title, table.model tr.description, .property-row':
|
||||
{
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
'& .opblock .opblock-section-header, .model-box, section.models .model-container':
|
||||
{
|
||||
background: theme.palette.background.default,
|
||||
},
|
||||
'& .prop-format, .parameter__in': {
|
||||
color: theme.palette.text.disabled,
|
||||
},
|
||||
@@ -53,9 +57,10 @@ const useStyles = makeStyles(theme => ({
|
||||
color: theme.palette.text.primary,
|
||||
'border-color': theme.palette.divider,
|
||||
},
|
||||
'& .opblock-description-wrapper p, .opblock-external-docs-wrapper p, .opblock-title_normal p, .response-control-media-type__accept-message, .opblock .opblock-section-header>label, .scheme-container .schemes>label, .info .base-url, .model': {
|
||||
color: theme.palette.text.hint,
|
||||
},
|
||||
'& .opblock-description-wrapper p, .opblock-external-docs-wrapper p, .opblock-title_normal p, .response-control-media-type__accept-message, .opblock .opblock-section-header>label, .scheme-container .schemes>label, .info .base-url, .model':
|
||||
{
|
||||
color: theme.palette.text.hint,
|
||||
},
|
||||
'& .parameter__name.required:after': {
|
||||
color: theme.palette.warning.dark,
|
||||
},
|
||||
|
||||
@@ -22,7 +22,7 @@ import { injectConfig } from './config';
|
||||
jest.mock('fs-extra');
|
||||
|
||||
const fsMock = fs as jest.Mocked<typeof fs>;
|
||||
const readFileMock = (fsMock.readFile as unknown) as jest.MockedFunction<
|
||||
const readFileMock = fsMock.readFile as unknown as jest.MockedFunction<
|
||||
(name: string) => Promise<string>
|
||||
>;
|
||||
|
||||
|
||||
@@ -104,8 +104,7 @@ export async function createRouter(
|
||||
// The Cache-Control header instructs the browser to not cache html files since it might
|
||||
// link to static assets from recently deployed versions.
|
||||
if (
|
||||
((express.static.mime as unknown) as Mime).lookup(path) ===
|
||||
'text/html'
|
||||
(express.static.mime as unknown as Mime).lookup(path) === 'text/html'
|
||||
) {
|
||||
res.setHeader('Cache-Control', 'no-store, max-age=0');
|
||||
}
|
||||
|
||||
@@ -223,9 +223,7 @@ export class OAuthEnvironmentHandler implements AuthProviderRouteHandlers {
|
||||
export interface OAuthHandlers {
|
||||
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
|
||||
// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}'
|
||||
handler(
|
||||
req: express.Request,
|
||||
): Promise<{
|
||||
handler(req: express.Request): Promise<{
|
||||
response: AuthResponse<OAuthProviderInfo>;
|
||||
refreshToken?: string;
|
||||
}>;
|
||||
|
||||
@@ -150,7 +150,7 @@ export class TokenFactory implements TokenIssuer {
|
||||
// the new one. This also needs to be implemented cross-service though, meaning new services
|
||||
// that boot up need to be able to grab an existing key to use for signing.
|
||||
this.logger.info(`Created new signing key ${key.kid}`);
|
||||
await this.keyStore.addKey((key.toJWK(false) as unknown) as AnyJWK);
|
||||
await this.keyStore.addKey(key.toJWK(false) as unknown as AnyJWK);
|
||||
|
||||
// At this point we are allowed to start using the new key
|
||||
return key as JSONWebKey;
|
||||
|
||||
@@ -32,10 +32,10 @@ describe('oauth helpers', () => {
|
||||
describe('postMessageResponse', () => {
|
||||
const appOrigin = 'http://localhost:3000';
|
||||
it('should post a message back with payload success', () => {
|
||||
const mockResponse = ({
|
||||
const mockResponse = {
|
||||
end: jest.fn().mockReturnThis(),
|
||||
setHeader: jest.fn().mockReturnThis(),
|
||||
} as unknown) as express.Response;
|
||||
} as unknown as express.Response;
|
||||
|
||||
const data: WebMessageResponse = {
|
||||
type: 'authorization_response',
|
||||
@@ -64,10 +64,10 @@ describe('oauth helpers', () => {
|
||||
});
|
||||
|
||||
it('should post a message back with payload error', () => {
|
||||
const mockResponse = ({
|
||||
const mockResponse = {
|
||||
end: jest.fn().mockReturnThis(),
|
||||
setHeader: jest.fn().mockReturnThis(),
|
||||
} as unknown) as express.Response;
|
||||
} as unknown as express.Response;
|
||||
|
||||
const data: WebMessageResponse = {
|
||||
type: 'authorization_response',
|
||||
@@ -84,13 +84,13 @@ describe('oauth helpers', () => {
|
||||
it('should call postMessage twice but only one of them with target *', () => {
|
||||
let responseBody = '';
|
||||
|
||||
const mockResponse = ({
|
||||
const mockResponse = {
|
||||
end: jest.fn(body => {
|
||||
responseBody = body;
|
||||
return this;
|
||||
}),
|
||||
setHeader: jest.fn().mockReturnThis(),
|
||||
} as unknown) as express.Response;
|
||||
} as unknown as express.Response;
|
||||
|
||||
const data: WebMessageResponse = {
|
||||
type: 'authorization_response',
|
||||
@@ -128,10 +128,10 @@ describe('oauth helpers', () => {
|
||||
});
|
||||
|
||||
it('handles single quotes and unicode chars safely', () => {
|
||||
const mockResponse = ({
|
||||
const mockResponse = {
|
||||
end: jest.fn().mockReturnThis(),
|
||||
setHeader: jest.fn().mockReturnThis(),
|
||||
} as unknown) as express.Response;
|
||||
} as unknown as express.Response;
|
||||
|
||||
const data: WebMessageResponse = {
|
||||
type: 'authorization_response',
|
||||
@@ -164,23 +164,23 @@ describe('oauth helpers', () => {
|
||||
|
||||
describe('ensuresXRequestedWith', () => {
|
||||
it('should return false if no header present', () => {
|
||||
const mockRequest = ({
|
||||
const mockRequest = {
|
||||
header: () => jest.fn(),
|
||||
} as unknown) as express.Request;
|
||||
} as unknown as express.Request;
|
||||
expect(ensuresXRequestedWith(mockRequest)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false if header present with incorrect value', () => {
|
||||
const mockRequest = ({
|
||||
const mockRequest = {
|
||||
header: () => 'INVALID',
|
||||
} as unknown) as express.Request;
|
||||
} as unknown as express.Request;
|
||||
expect(ensuresXRequestedWith(mockRequest)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true if header present with correct value', () => {
|
||||
const mockRequest = ({
|
||||
const mockRequest = {
|
||||
header: () => 'XMLHttpRequest',
|
||||
} as unknown) as express.Request;
|
||||
} as unknown as express.Request;
|
||||
expect(ensuresXRequestedWith(mockRequest)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -71,19 +71,19 @@ describe('OAuthAdapter', () => {
|
||||
providerInstance,
|
||||
oAuthProviderOptions,
|
||||
);
|
||||
const mockRequest = ({
|
||||
const mockRequest = {
|
||||
query: {
|
||||
scope: 'user',
|
||||
env: 'development',
|
||||
},
|
||||
} as unknown) as express.Request;
|
||||
} as unknown as express.Request;
|
||||
|
||||
const mockResponse = ({
|
||||
const mockResponse = {
|
||||
cookie: jest.fn().mockReturnThis(),
|
||||
end: jest.fn().mockReturnThis(),
|
||||
setHeader: jest.fn().mockReturnThis(),
|
||||
statusCode: jest.fn().mockReturnThis(),
|
||||
} as unknown) as express.Response;
|
||||
} as unknown as express.Response;
|
||||
|
||||
await oauthProvider.start(mockRequest, mockResponse);
|
||||
// nonce cookie checks
|
||||
@@ -108,20 +108,20 @@ describe('OAuthAdapter', () => {
|
||||
});
|
||||
|
||||
const state = { nonce: 'nonce', env: 'development' };
|
||||
const mockRequest = ({
|
||||
const mockRequest = {
|
||||
cookies: {
|
||||
'test-provider-nonce': 'nonce',
|
||||
},
|
||||
query: {
|
||||
state: encodeState(state),
|
||||
},
|
||||
} as unknown) as express.Request;
|
||||
} as unknown as express.Request;
|
||||
|
||||
const mockResponse = ({
|
||||
const mockResponse = {
|
||||
cookie: jest.fn().mockReturnThis(),
|
||||
setHeader: jest.fn().mockReturnThis(),
|
||||
end: jest.fn().mockReturnThis(),
|
||||
} as unknown) as express.Response;
|
||||
} as unknown as express.Response;
|
||||
|
||||
await oauthProvider.frameHandler(mockRequest, mockResponse);
|
||||
expect(mockResponse.cookie).toHaveBeenCalledTimes(1);
|
||||
@@ -141,20 +141,20 @@ describe('OAuthAdapter', () => {
|
||||
disableRefresh: true,
|
||||
});
|
||||
|
||||
const mockRequest = ({
|
||||
const mockRequest = {
|
||||
cookies: {
|
||||
'test-provider-nonce': 'nonce',
|
||||
},
|
||||
query: {
|
||||
state: 'nonce',
|
||||
},
|
||||
} as unknown) as express.Request;
|
||||
} as unknown as express.Request;
|
||||
|
||||
const mockResponse = ({
|
||||
const mockResponse = {
|
||||
cookie: jest.fn().mockReturnThis(),
|
||||
setHeader: jest.fn().mockReturnThis(),
|
||||
end: jest.fn().mockReturnThis(),
|
||||
} as unknown) as express.Response;
|
||||
} as unknown as express.Response;
|
||||
|
||||
await oauthProvider.frameHandler(mockRequest, mockResponse);
|
||||
expect(mockResponse.cookie).toHaveBeenCalledTimes(0);
|
||||
@@ -166,15 +166,15 @@ describe('OAuthAdapter', () => {
|
||||
disableRefresh: false,
|
||||
});
|
||||
|
||||
const mockRequest = ({
|
||||
const mockRequest = {
|
||||
header: () => 'XMLHttpRequest',
|
||||
} as unknown) as express.Request;
|
||||
} as unknown as express.Request;
|
||||
|
||||
const mockResponse = ({
|
||||
const mockResponse = {
|
||||
cookie: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
status: jest.fn().mockReturnThis(),
|
||||
} as unknown) as express.Response;
|
||||
} as unknown as express.Response;
|
||||
|
||||
await oauthProvider.logout(mockRequest, mockResponse);
|
||||
expect(mockResponse.cookie).toHaveBeenCalledTimes(1);
|
||||
@@ -192,18 +192,18 @@ describe('OAuthAdapter', () => {
|
||||
disableRefresh: false,
|
||||
});
|
||||
|
||||
const mockRequest = ({
|
||||
const mockRequest = {
|
||||
header: () => 'XMLHttpRequest',
|
||||
cookies: {
|
||||
'test-provider-refresh-token': 'token',
|
||||
},
|
||||
query: {},
|
||||
} as unknown) as express.Request;
|
||||
} as unknown as express.Request;
|
||||
|
||||
const mockResponse = ({
|
||||
const mockResponse = {
|
||||
json: jest.fn().mockReturnThis(),
|
||||
status: jest.fn().mockReturnThis(),
|
||||
} as unknown) as express.Response;
|
||||
} as unknown as express.Response;
|
||||
|
||||
await oauthProvider.refresh(mockRequest, mockResponse);
|
||||
expect(mockResponse.json).toHaveBeenCalledTimes(1);
|
||||
@@ -222,18 +222,18 @@ describe('OAuthAdapter', () => {
|
||||
disableRefresh: true,
|
||||
});
|
||||
|
||||
const mockRequest = ({
|
||||
const mockRequest = {
|
||||
header: () => 'XMLHttpRequest',
|
||||
cookies: {
|
||||
'test-provider-refresh-token': 'token',
|
||||
},
|
||||
query: {},
|
||||
} as unknown) as express.Request;
|
||||
} as unknown as express.Request;
|
||||
|
||||
const mockResponse = ({
|
||||
const mockResponse = {
|
||||
send: jest.fn().mockReturnThis(),
|
||||
status: jest.fn().mockReturnThis(),
|
||||
} as unknown) as express.Response;
|
||||
} as unknown as express.Response;
|
||||
|
||||
await oauthProvider.refresh(mockRequest, mockResponse);
|
||||
expect(mockResponse.send).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -21,24 +21,24 @@ describe('OAuthProvider Utils', () => {
|
||||
describe('verifyNonce', () => {
|
||||
it('should throw error if cookie nonce missing', () => {
|
||||
const state = { nonce: 'NONCE', env: 'development' };
|
||||
const mockRequest = ({
|
||||
const mockRequest = {
|
||||
cookies: {},
|
||||
query: {
|
||||
state: encodeState(state),
|
||||
},
|
||||
} as unknown) as express.Request;
|
||||
} as unknown as express.Request;
|
||||
expect(() => {
|
||||
verifyNonce(mockRequest, 'providera');
|
||||
}).toThrowError('Auth response is missing cookie nonce');
|
||||
});
|
||||
|
||||
it('should throw error if state nonce missing', () => {
|
||||
const mockRequest = ({
|
||||
const mockRequest = {
|
||||
cookies: {
|
||||
'providera-nonce': 'NONCE',
|
||||
},
|
||||
query: {},
|
||||
} as unknown) as express.Request;
|
||||
} as unknown as express.Request;
|
||||
expect(() => {
|
||||
verifyNonce(mockRequest, 'providera');
|
||||
}).toThrowError('Invalid state passed via request');
|
||||
@@ -46,14 +46,14 @@ describe('OAuthProvider Utils', () => {
|
||||
|
||||
it('should throw error if nonce mismatch', () => {
|
||||
const state = { nonce: 'NONCEB', env: 'development' };
|
||||
const mockRequest = ({
|
||||
const mockRequest = {
|
||||
cookies: {
|
||||
'providera-nonce': 'NONCEA',
|
||||
},
|
||||
query: {
|
||||
state: encodeState(state),
|
||||
},
|
||||
} as unknown) as express.Request;
|
||||
} as unknown as express.Request;
|
||||
expect(() => {
|
||||
verifyNonce(mockRequest, 'providera');
|
||||
}).toThrowError('Invalid nonce');
|
||||
@@ -61,14 +61,14 @@ describe('OAuthProvider Utils', () => {
|
||||
|
||||
it('should not throw any error if nonce matches', () => {
|
||||
const state = { nonce: 'NONCE', env: 'development' };
|
||||
const mockRequest = ({
|
||||
const mockRequest = {
|
||||
cookies: {
|
||||
'providera-nonce': 'NONCE',
|
||||
},
|
||||
query: {
|
||||
state: encodeState(state),
|
||||
},
|
||||
} as unknown) as express.Request;
|
||||
} as unknown as express.Request;
|
||||
expect(() => {
|
||||
verifyNonce(mockRequest, 'providera');
|
||||
}).not.toThrow();
|
||||
|
||||
@@ -106,9 +106,7 @@ export interface OAuthHandlers {
|
||||
* Handles the redirect from the auth provider when the user has signed in.
|
||||
* @param {express.Request} req
|
||||
*/
|
||||
handler(
|
||||
req: express.Request,
|
||||
): Promise<{
|
||||
handler(req: express.Request): Promise<{
|
||||
response: AuthResponse<OAuthProviderInfo>;
|
||||
refreshToken?: string;
|
||||
}>;
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
executeRefreshTokenStrategy,
|
||||
} from './PassportStrategyHelper';
|
||||
|
||||
const mockRequest = ({} as unknown) as express.Request;
|
||||
const mockRequest = {} as unknown as express.Request;
|
||||
|
||||
describe('PassportStrategyHelper', () => {
|
||||
class MyCustomRedirectStrategy extends passport.Strategy {
|
||||
|
||||
@@ -195,7 +195,7 @@ export const executeFetchUserProfileStrategy = async (
|
||||
accessToken: string,
|
||||
): Promise<passport.Profile> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const anyStrategy = (providerStrategy as unknown) as ProviderStrategy;
|
||||
const anyStrategy = providerStrategy as unknown as ProviderStrategy;
|
||||
anyStrategy.userProfile(
|
||||
accessToken,
|
||||
(error: Error, rawProfile: passport.Profile) => {
|
||||
|
||||
@@ -79,22 +79,22 @@ describe('AwsALBAuthProvider', () => {
|
||||
getEntityByName: jest.fn(),
|
||||
};
|
||||
|
||||
const mockRequest = ({
|
||||
const mockRequest = {
|
||||
header: jest.fn(() => {
|
||||
return 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyIsImlzcyI6ImZvbyJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.T2BNS4G-6RoiFnXc8Q8TiwdWzTpNitY8jcsGM3N3-Yo';
|
||||
}),
|
||||
} as unknown) as express.Request;
|
||||
const mockRequestWithoutJwt = ({
|
||||
} as unknown as express.Request;
|
||||
const mockRequestWithoutJwt = {
|
||||
header: jest.fn(() => {
|
||||
return undefined;
|
||||
}),
|
||||
} as unknown) as express.Request;
|
||||
const mockResponse = ({
|
||||
} as unknown as express.Request;
|
||||
const mockResponse = {
|
||||
end: jest.fn(),
|
||||
header: () => jest.fn(),
|
||||
json: jest.fn().mockReturnThis(),
|
||||
status: jest.fn(),
|
||||
} as unknown) as express.Response;
|
||||
} as unknown as express.Response;
|
||||
|
||||
describe('should transform to type OAuthResponse', () => {
|
||||
it('when JWT is valid and identity is resolved successfully', async () => {
|
||||
|
||||
@@ -19,10 +19,10 @@ import { GithubAuthProvider } from './provider';
|
||||
import * as helpers from '../../lib/passport/PassportStrategyHelper';
|
||||
import { OAuthResult } from '../../lib/oauth';
|
||||
|
||||
const mockFrameHandler = (jest.spyOn(
|
||||
const mockFrameHandler = jest.spyOn(
|
||||
helpers,
|
||||
'executeFrameHandlerStrategy',
|
||||
) as unknown) as jest.MockedFunction<
|
||||
) as unknown as jest.MockedFunction<
|
||||
() => Promise<{
|
||||
result: Omit<OAuthResult, 'params'> & { params: { scope: string } };
|
||||
}>
|
||||
@@ -87,7 +87,7 @@ describe('GithubAuthProvider', () => {
|
||||
|
||||
it('when "email" is missing, it should be able to create the profile without it', async () => {
|
||||
const accessToken = '19xasczxcm9n7gacn9jdgm19me';
|
||||
const fullProfile = ({
|
||||
const fullProfile = {
|
||||
id: 'uid-123',
|
||||
username: 'jimmymarkum',
|
||||
provider: 'github',
|
||||
@@ -99,7 +99,7 @@ describe('GithubAuthProvider', () => {
|
||||
'https://a1cf74336522e87f135f-2f21ace9a6cf0052456644b80fa06d4f.ssl.cf2.rackcdn.com/images/characters_opt/p-mystic-river-sean-penn.jpg',
|
||||
},
|
||||
],
|
||||
} as unknown) as PassportProfile;
|
||||
} as unknown as PassportProfile;
|
||||
|
||||
const params = {
|
||||
scope: 'read:scope',
|
||||
@@ -131,7 +131,7 @@ describe('GithubAuthProvider', () => {
|
||||
|
||||
it('when "displayName" is missing, it should be able to create the profile and map "displayName" with "username"', async () => {
|
||||
const accessToken = '19xasczxcm9n7gacn9jdgm19me';
|
||||
const fullProfile = ({
|
||||
const fullProfile = {
|
||||
id: 'uid-123',
|
||||
username: 'jimmymarkum',
|
||||
provider: 'github',
|
||||
@@ -143,7 +143,7 @@ describe('GithubAuthProvider', () => {
|
||||
'https://a1cf74336522e87f135f-2f21ace9a6cf0052456644b80fa06d4f.ssl.cf2.rackcdn.com/images/characters_opt/p-mystic-river-sean-penn.jpg',
|
||||
},
|
||||
],
|
||||
} as unknown) as PassportProfile;
|
||||
} as unknown as PassportProfile;
|
||||
|
||||
const params = {
|
||||
scope: 'read:scope',
|
||||
|
||||
@@ -18,10 +18,10 @@ import { GitlabAuthProvider } from './provider';
|
||||
import * as helpers from '../../lib/passport/PassportStrategyHelper';
|
||||
import { OAuthResult } from '../../lib/oauth';
|
||||
|
||||
const mockFrameHandler = (jest.spyOn(
|
||||
const mockFrameHandler = jest.spyOn(
|
||||
helpers,
|
||||
'executeFrameHandlerStrategy',
|
||||
) as unknown) as jest.MockedFunction<() => Promise<{ result: OAuthResult }>>;
|
||||
) as unknown as jest.MockedFunction<() => Promise<{ result: OAuthResult }>>;
|
||||
|
||||
describe('GitlabAuthProvider', () => {
|
||||
it('should transform to type OAuthResponse', async () => {
|
||||
|
||||
@@ -21,10 +21,10 @@ import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { TokenIssuer } from '../../identity/types';
|
||||
import { CatalogIdentityClient } from '../../lib/catalog';
|
||||
|
||||
const mockFrameHandler = (jest.spyOn(
|
||||
const mockFrameHandler = jest.spyOn(
|
||||
helpers,
|
||||
'executeFrameHandlerStrategy',
|
||||
) as unknown) as jest.MockedFunction<
|
||||
) as unknown as jest.MockedFunction<
|
||||
() => Promise<{ result: OAuthResult; privateInfo: any }>
|
||||
>;
|
||||
|
||||
@@ -40,8 +40,9 @@ describe('createGoogleProvider', () => {
|
||||
|
||||
const provider = new GoogleAuthProvider({
|
||||
logger: getVoidLogger(),
|
||||
catalogIdentityClient: (catalogIdentityClient as unknown) as CatalogIdentityClient,
|
||||
tokenIssuer: (tokenIssuer as unknown) as TokenIssuer,
|
||||
catalogIdentityClient:
|
||||
catalogIdentityClient as unknown as CatalogIdentityClient,
|
||||
tokenIssuer: tokenIssuer as unknown as TokenIssuer,
|
||||
authHandler: async ({ fullProfile }) => ({
|
||||
profile: {
|
||||
email: fullProfile.emails![0]!.value,
|
||||
|
||||
@@ -21,10 +21,10 @@ import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { TokenIssuer } from '../../identity/types';
|
||||
import { CatalogIdentityClient } from '../../lib/catalog';
|
||||
|
||||
const mockFrameHandler = (jest.spyOn(
|
||||
const mockFrameHandler = jest.spyOn(
|
||||
helpers,
|
||||
'executeFrameHandlerStrategy',
|
||||
) as unknown) as jest.MockedFunction<
|
||||
) as unknown as jest.MockedFunction<
|
||||
() => Promise<{ result: OAuthResult; privateInfo: any }>
|
||||
>;
|
||||
|
||||
@@ -40,8 +40,9 @@ describe('createMicrosoftProvider', () => {
|
||||
|
||||
const provider = new MicrosoftAuthProvider({
|
||||
logger: getVoidLogger(),
|
||||
catalogIdentityClient: (catalogIdentityClient as unknown) as CatalogIdentityClient,
|
||||
tokenIssuer: (tokenIssuer as unknown) as TokenIssuer,
|
||||
catalogIdentityClient:
|
||||
catalogIdentityClient as unknown as CatalogIdentityClient,
|
||||
tokenIssuer: tokenIssuer as unknown as TokenIssuer,
|
||||
authHandler: async ({ fullProfile }) => ({
|
||||
profile: {
|
||||
email: fullProfile.emails![0]!.value,
|
||||
|
||||
@@ -220,24 +220,22 @@ export const microsoftEmailSignInResolver: SignInResolver<OAuthResult> = async (
|
||||
return { id: entity.metadata.name, entity, token };
|
||||
};
|
||||
|
||||
export const microsoftDefaultSignInResolver: SignInResolver<OAuthResult> = async (
|
||||
info,
|
||||
ctx,
|
||||
) => {
|
||||
const { profile } = info;
|
||||
export const microsoftDefaultSignInResolver: SignInResolver<OAuthResult> =
|
||||
async (info, ctx) => {
|
||||
const { profile } = info;
|
||||
|
||||
if (!profile.email) {
|
||||
throw new Error('Profile contained no email');
|
||||
}
|
||||
if (!profile.email) {
|
||||
throw new Error('Profile contained no email');
|
||||
}
|
||||
|
||||
const userId = profile.email.split('@')[0];
|
||||
const userId = profile.email.split('@')[0];
|
||||
|
||||
const token = await ctx.tokenIssuer.issueToken({
|
||||
claims: { sub: userId, ent: [`user:default/${userId}`] },
|
||||
});
|
||||
const token = await ctx.tokenIssuer.issueToken({
|
||||
claims: { sub: userId, ent: [`user:default/${userId}`] },
|
||||
});
|
||||
|
||||
return { id: userId, token };
|
||||
};
|
||||
return { id: userId, token };
|
||||
};
|
||||
|
||||
export type MicrosoftProviderOptions = {
|
||||
/**
|
||||
|
||||
@@ -70,7 +70,7 @@ describe('OidcAuthProvider', () => {
|
||||
rest.get('https://oidc.test/.well-known/openid-configuration', handler),
|
||||
);
|
||||
const provider = new OidcAuthProvider(clientMetadata);
|
||||
const { strategy } = ((await (provider as any).implementation) as any) as {
|
||||
const { strategy } = (await (provider as any).implementation) as any as {
|
||||
strategy: {
|
||||
_client: ClientMetadata;
|
||||
_issuer: IssuerMetadata;
|
||||
@@ -138,7 +138,7 @@ describe('OidcAuthProvider', () => {
|
||||
const req = {
|
||||
method: 'GET',
|
||||
url: 'https://oidc.test/?code=test2',
|
||||
session: ({ 'oidc:oidc.test': 'test' } as any) as Session,
|
||||
session: { 'oidc:oidc.test': 'test' } as any as Session,
|
||||
} as express.Request;
|
||||
await provider.handler(req);
|
||||
expect(requestSequence).toEqual([0, 1, 2].map(i => requests[i].url));
|
||||
|
||||
@@ -42,7 +42,7 @@ describe('DefaultBadgeBuilder', () => {
|
||||
createBadge: () => badge,
|
||||
},
|
||||
failbadge: {
|
||||
createBadge: () => (undefined as unknown) as Badge, // force a bad return value..
|
||||
createBadge: () => undefined as unknown as Badge, // force a bad return value..
|
||||
},
|
||||
invalidbadge: {
|
||||
createBadge: () => ({ style: 'wrong' as BadgeStyle, ...badge }),
|
||||
|
||||
@@ -48,7 +48,11 @@ export const EntityBadgesDialog = ({ open, onClose }: Props) => {
|
||||
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
const badgesApi = useApi(badgesApiRef);
|
||||
|
||||
const { value: badges, loading, error } = useAsync(async () => {
|
||||
const {
|
||||
value: badges,
|
||||
loading,
|
||||
error,
|
||||
} = useAsync(async () => {
|
||||
if (open && entity) {
|
||||
return await badgesApi.getEntityBadgeSpecs(entity);
|
||||
}
|
||||
|
||||
@@ -140,21 +140,7 @@ describe('readLdapUsers', () => {
|
||||
distinguishedName: ['dn-value'],
|
||||
objectGUID: [
|
||||
Buffer.from([
|
||||
68,
|
||||
2,
|
||||
125,
|
||||
190,
|
||||
209,
|
||||
0,
|
||||
94,
|
||||
73,
|
||||
133,
|
||||
33,
|
||||
230,
|
||||
174,
|
||||
234,
|
||||
195,
|
||||
160,
|
||||
68, 2, 125, 190, 209, 0, 94, 73, 133, 33, 230, 174, 234, 195, 160,
|
||||
152,
|
||||
]),
|
||||
],
|
||||
@@ -286,21 +272,7 @@ describe('readLdapGroups', () => {
|
||||
distinguishedName: ['dn-value'],
|
||||
objectGUID: [
|
||||
Buffer.from([
|
||||
68,
|
||||
2,
|
||||
125,
|
||||
190,
|
||||
209,
|
||||
0,
|
||||
94,
|
||||
73,
|
||||
133,
|
||||
33,
|
||||
230,
|
||||
174,
|
||||
234,
|
||||
195,
|
||||
160,
|
||||
68, 2, 125, 190, 209, 0, 94, 73, 133, 33, 230, 174, 234, 195, 160,
|
||||
152,
|
||||
]),
|
||||
],
|
||||
|
||||
@@ -89,9 +89,11 @@ export async function myGroupTransformer(
|
||||
groupPhoto?: string,
|
||||
): Promise<GroupEntity | undefined> {
|
||||
if (
|
||||
((group as unknown) as {
|
||||
creationOptions: string[];
|
||||
}).creationOptions.includes('ProvisionGroupHomepage')
|
||||
(
|
||||
group as unknown as {
|
||||
creationOptions: string[];
|
||||
}
|
||||
).creationOptions.includes('ProvisionGroupHomepage')
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -21,9 +21,10 @@ import { setupServer } from 'msw/node';
|
||||
import { MicrosoftGraphClient } from './client';
|
||||
|
||||
describe('MicrosoftGraphClient', () => {
|
||||
const confidentialClientApplication: jest.Mocked<msal.ConfidentialClientApplication> = {
|
||||
acquireTokenByClientCredential: jest.fn(),
|
||||
} as any;
|
||||
const confidentialClientApplication: jest.Mocked<msal.ConfidentialClientApplication> =
|
||||
{
|
||||
acquireTokenByClientCredential: jest.fn(),
|
||||
} as any;
|
||||
let client: MicrosoftGraphClient;
|
||||
const worker = setupServer();
|
||||
|
||||
|
||||
@@ -202,14 +202,10 @@ describe('read microsoft graph', () => {
|
||||
'data:image/jpeg;base64,...',
|
||||
);
|
||||
|
||||
const {
|
||||
groups,
|
||||
groupMember,
|
||||
groupMemberOf,
|
||||
rootGroup,
|
||||
} = await readMicrosoftGraphGroups(client, 'tenantid', {
|
||||
groupFilter: 'securityEnabled eq false',
|
||||
});
|
||||
const { groups, groupMember, groupMemberOf, rootGroup } =
|
||||
await readMicrosoftGraphGroups(client, 'tenantid', {
|
||||
groupFilter: 'securityEnabled eq false',
|
||||
});
|
||||
|
||||
const expectedRootGroup = group({
|
||||
metadata: {
|
||||
|
||||
@@ -318,14 +318,12 @@ export function resolveRelations(
|
||||
|
||||
// Make sure every group (except root) has at least one parent. If the parent is missing, add the root.
|
||||
if (rootGroup) {
|
||||
const tenantId = rootGroup.metadata.annotations![
|
||||
MICROSOFT_GRAPH_TENANT_ID_ANNOTATION
|
||||
];
|
||||
const tenantId =
|
||||
rootGroup.metadata.annotations![MICROSOFT_GRAPH_TENANT_ID_ANNOTATION];
|
||||
|
||||
groups.forEach(group => {
|
||||
const groupId = group.metadata.annotations![
|
||||
MICROSOFT_GRAPH_GROUP_ID_ANNOTATION
|
||||
];
|
||||
const groupId =
|
||||
group.metadata.annotations![MICROSOFT_GRAPH_GROUP_ID_ANNOTATION];
|
||||
|
||||
if (!groupId) {
|
||||
return;
|
||||
@@ -392,15 +390,11 @@ export async function readMicrosoftGraphOrg(
|
||||
userFilter: options.userFilter,
|
||||
logger: options.logger,
|
||||
});
|
||||
const {
|
||||
groups,
|
||||
rootGroup,
|
||||
groupMember,
|
||||
groupMemberOf,
|
||||
} = await readMicrosoftGraphGroups(client, tenantId, {
|
||||
groupFilter: options?.groupFilter,
|
||||
transformer: options?.groupTransformer,
|
||||
});
|
||||
const { groups, rootGroup, groupMember, groupMemberOf } =
|
||||
await readMicrosoftGraphGroups(client, tenantId, {
|
||||
groupFilter: options?.groupFilter,
|
||||
transformer: options?.groupTransformer,
|
||||
});
|
||||
|
||||
resolveRelations(rootGroup, groups, users, groupMember, groupMemberOf);
|
||||
users.sort((a, b) => a.metadata.name.localeCompare(b.metadata.name));
|
||||
|
||||
@@ -442,13 +442,11 @@
|
||||
and lets the other processors take care of further processing.
|
||||
|
||||
```typescript
|
||||
const customRepositoryParser: BitbucketRepositoryParser = async function* customRepositoryParser({
|
||||
client,
|
||||
repository,
|
||||
}) {
|
||||
// Custom logic for interpret the matching repository.
|
||||
// See defaultRepositoryParser for an example
|
||||
};
|
||||
const customRepositoryParser: BitbucketRepositoryParser =
|
||||
async function* customRepositoryParser({ client, repository }) {
|
||||
// Custom logic for interpret the matching repository.
|
||||
// See defaultRepositoryParser for an example
|
||||
};
|
||||
|
||||
const processor = BitbucketDiscoveryProcessor.fromConfig(env.config, {
|
||||
parser: customRepositoryParser,
|
||||
@@ -920,7 +918,6 @@
|
||||
spec:
|
||||
type: website
|
||||
---
|
||||
|
||||
```
|
||||
|
||||
This behaves now the same way as Kubernetes handles multiple documents in a single YAML file.
|
||||
|
||||
@@ -88,9 +88,7 @@ export class AwsOrganizationCloudAccountProcessor implements CatalogProcessor {
|
||||
logger: Logger_2;
|
||||
});
|
||||
// (undocumented)
|
||||
extractInformationFromArn(
|
||||
arn: string,
|
||||
): {
|
||||
extractInformationFromArn(arn: string): {
|
||||
accountId: string;
|
||||
organizationId: string;
|
||||
};
|
||||
@@ -596,7 +594,8 @@ export class DefaultCatalogCollator implements DocumentCollator {
|
||||
//
|
||||
// @public (undocumented)
|
||||
export class DefaultCatalogProcessingOrchestrator
|
||||
implements CatalogProcessingOrchestrator {
|
||||
implements CatalogProcessingOrchestrator
|
||||
{
|
||||
constructor(options: {
|
||||
processors: CatalogProcessor[];
|
||||
integrations: ScmIntegrationRegistry;
|
||||
|
||||
@@ -52,12 +52,8 @@ export class DatabaseLocationsCatalog implements LocationsCatalog {
|
||||
}
|
||||
|
||||
async location(id: string): Promise<LocationResponse> {
|
||||
const {
|
||||
message,
|
||||
status,
|
||||
timestamp,
|
||||
...data
|
||||
} = await this.database.location(id);
|
||||
const { message, status, timestamp, ...data } =
|
||||
await this.database.location(id);
|
||||
return {
|
||||
currentStatus: {
|
||||
message,
|
||||
|
||||
@@ -556,9 +556,10 @@ export class CommonDatabase implements Database {
|
||||
}
|
||||
}
|
||||
|
||||
function parsePagination(
|
||||
input?: EntityPagination,
|
||||
): { limit?: number; offset?: number } {
|
||||
function parsePagination(input?: EntityPagination): {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
} {
|
||||
if (!input) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -71,9 +71,8 @@ export class AnnotateLocationEntityProcessor implements CatalogProcessor {
|
||||
annotations: pickBy(
|
||||
{
|
||||
[LOCATION_ANNOTATION]: stringifyLocationReference(location),
|
||||
[ORIGIN_LOCATION_ANNOTATION]: stringifyLocationReference(
|
||||
originLocation,
|
||||
),
|
||||
[ORIGIN_LOCATION_ANNOTATION]:
|
||||
stringifyLocationReference(originLocation),
|
||||
[VIEW_URL_ANNOTATION]: viewUrl,
|
||||
[EDIT_URL_ANNOTATION]: editUrl,
|
||||
[SOURCE_LOCATION_ANNOTATION]: sourceLocation,
|
||||
|
||||
+3
-6
@@ -37,8 +37,7 @@ describe('AwsOrganizationCloudAccountProcessor', () => {
|
||||
return {
|
||||
Accounts: [
|
||||
{
|
||||
Arn:
|
||||
'arn:aws:organizations::192594491037:account/o-1vl18kc5a3/957140518395',
|
||||
Arn: 'arn:aws:organizations::192594491037:account/o-1vl18kc5a3/957140518395',
|
||||
Name: 'testaccount',
|
||||
},
|
||||
],
|
||||
@@ -83,13 +82,11 @@ describe('AwsOrganizationCloudAccountProcessor', () => {
|
||||
return {
|
||||
Accounts: [
|
||||
{
|
||||
Arn:
|
||||
'arn:aws:organizations::192594491037:account/o-1vl18kc5a3/957140518395',
|
||||
Arn: 'arn:aws:organizations::192594491037:account/o-1vl18kc5a3/957140518395',
|
||||
Name: 'testaccount',
|
||||
},
|
||||
{
|
||||
Arn:
|
||||
'arn:aws:organizations::192594491037:account/o-zzzzzzzzz/957140518395',
|
||||
Arn: 'arn:aws:organizations::192594491037:account/o-zzzzzzzzz/957140518395',
|
||||
Name: 'testaccount2',
|
||||
},
|
||||
],
|
||||
|
||||
+4
-3
@@ -89,9 +89,10 @@ export class AwsOrganizationCloudAccountProcessor implements CatalogProcessor {
|
||||
.replace(/[^a-zA-Z0-9\-]/g, '-');
|
||||
}
|
||||
|
||||
extractInformationFromArn(
|
||||
arn: string,
|
||||
): { accountId: string; organizationId: string } {
|
||||
extractInformationFromArn(arn: string): {
|
||||
accountId: string;
|
||||
organizationId: string;
|
||||
} {
|
||||
const parts = arn.split('/');
|
||||
|
||||
return {
|
||||
|
||||
+10
-9
@@ -256,15 +256,16 @@ describe('BitbucketDiscoveryProcessor', () => {
|
||||
});
|
||||
|
||||
describe('Custom repository parser', () => {
|
||||
const customRepositoryParser: BitbucketRepositoryParser = async function* customRepositoryParser({}) {
|
||||
yield results.location(
|
||||
{
|
||||
type: 'custom-location-type',
|
||||
target: 'custom-target',
|
||||
},
|
||||
true,
|
||||
);
|
||||
};
|
||||
const customRepositoryParser: BitbucketRepositoryParser =
|
||||
async function* customRepositoryParser({}) {
|
||||
yield results.location(
|
||||
{
|
||||
type: 'custom-location-type',
|
||||
target: 'custom-target',
|
||||
},
|
||||
true,
|
||||
);
|
||||
};
|
||||
|
||||
const processor = BitbucketDiscoveryProcessor.fromConfig(
|
||||
new ConfigReader({
|
||||
|
||||
@@ -136,9 +136,11 @@ export async function readBitbucketOrg(
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseUrl(
|
||||
urlString: string,
|
||||
): { projectSearchPath: RegExp; repoSearchPath: RegExp; catalogPath: string } {
|
||||
function parseUrl(urlString: string): {
|
||||
projectSearchPath: RegExp;
|
||||
repoSearchPath: RegExp;
|
||||
catalogPath: string;
|
||||
} {
|
||||
const url = new URL(urlString);
|
||||
const path = url.pathname.substr(1).split('/');
|
||||
|
||||
|
||||
@@ -21,9 +21,10 @@ import { getOrganizationRepositories } from './github';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
|
||||
jest.mock('./github');
|
||||
const mockGetOrganizationRepositories = getOrganizationRepositories as jest.MockedFunction<
|
||||
typeof getOrganizationRepositories
|
||||
>;
|
||||
const mockGetOrganizationRepositories =
|
||||
getOrganizationRepositories as jest.MockedFunction<
|
||||
typeof getOrganizationRepositories
|
||||
>;
|
||||
|
||||
describe('GithubDiscoveryProcessor', () => {
|
||||
describe('parseUrl', () => {
|
||||
|
||||
@@ -56,8 +56,9 @@ export class GithubDiscoveryProcessor implements CatalogProcessor {
|
||||
return false;
|
||||
}
|
||||
|
||||
const gitHubConfig = this.integrations.github.byUrl(location.target)
|
||||
?.config;
|
||||
const gitHubConfig = this.integrations.github.byUrl(
|
||||
location.target,
|
||||
)?.config;
|
||||
if (!gitHubConfig) {
|
||||
throw new Error(
|
||||
`There is no GitHub integration that matches ${location.target}. Please add a configuration entry for it under integrations.github`,
|
||||
@@ -110,9 +111,11 @@ export class GithubDiscoveryProcessor implements CatalogProcessor {
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
export function parseUrl(
|
||||
urlString: string,
|
||||
): { org: string; repoSearchPath: RegExp; catalogPath: string } {
|
||||
export function parseUrl(urlString: string): {
|
||||
org: string;
|
||||
repoSearchPath: RegExp;
|
||||
catalogPath: string;
|
||||
} {
|
||||
const url = new URL(urlString);
|
||||
const path = url.pathname.substr(1).split('/');
|
||||
|
||||
|
||||
@@ -75,8 +75,9 @@ export class GithubMultiOrgReaderProcessor implements CatalogProcessor {
|
||||
return false;
|
||||
}
|
||||
|
||||
const gitHubConfig = this.integrations.github.byUrl(location.target)
|
||||
?.config;
|
||||
const gitHubConfig = this.integrations.github.byUrl(
|
||||
location.target,
|
||||
)?.config;
|
||||
if (!gitHubConfig) {
|
||||
throw new Error(
|
||||
`There is no GitHub integration that matches ${location.target}. Please add a configuration entry for it under integrations.github`,
|
||||
@@ -93,12 +94,10 @@ export class GithubMultiOrgReaderProcessor implements CatalogProcessor {
|
||||
|
||||
for (const orgConfig of orgsToProcess) {
|
||||
try {
|
||||
const {
|
||||
headers,
|
||||
type: tokenType,
|
||||
} = await credentialsProvider.getCredentials({
|
||||
url: `${baseUrl}/${orgConfig.name}`,
|
||||
});
|
||||
const { headers, type: tokenType } =
|
||||
await credentialsProvider.getCredentials({
|
||||
url: `${baseUrl}/${orgConfig.name}`,
|
||||
});
|
||||
const client = graphql.defaults({
|
||||
baseUrl: gitHubConfig.apiBaseUrl,
|
||||
headers,
|
||||
|
||||
@@ -113,12 +113,10 @@ export class GithubOrgReaderProcessor implements CatalogProcessor {
|
||||
}
|
||||
|
||||
const credentialsProvider = GithubCredentialsProvider.create(gitHubConfig);
|
||||
const {
|
||||
headers,
|
||||
type: tokenType,
|
||||
} = await credentialsProvider.getCredentials({
|
||||
url: orgUrl,
|
||||
});
|
||||
const { headers, type: tokenType } =
|
||||
await credentialsProvider.getCredentials({
|
||||
url: orgUrl,
|
||||
});
|
||||
|
||||
const client = graphql.defaults({
|
||||
baseUrl: gitHubConfig.apiBaseUrl,
|
||||
|
||||
@@ -26,7 +26,7 @@ import { toAbsoluteUrl } from './LocationEntityProcessor';
|
||||
describe('LocationEntityProcessor', () => {
|
||||
describe('toAbsoluteUrl', () => {
|
||||
it('handles files', () => {
|
||||
const integrations = ({} as unknown) as ScmIntegrationRegistry;
|
||||
const integrations = {} as unknown as ScmIntegrationRegistry;
|
||||
const base: LocationSpec = {
|
||||
type: 'file',
|
||||
target: `some${path.sep}path${path.sep}catalog-info.yaml`,
|
||||
|
||||
+13
-14
@@ -24,17 +24,16 @@ export type BitbucketRepositoryParser = (options: {
|
||||
logger: Logger;
|
||||
}) => AsyncIterable<CatalogProcessorResult>;
|
||||
|
||||
export const defaultRepositoryParser: BitbucketRepositoryParser = async function* defaultRepositoryParser({
|
||||
target,
|
||||
}) {
|
||||
yield results.location(
|
||||
{
|
||||
type: 'url',
|
||||
target: target,
|
||||
},
|
||||
// Not all locations may actually exist, since the user defined them as a wildcard pattern.
|
||||
// Thus, we emit them as optional and let the downstream processor find them while not outputting
|
||||
// an error if it couldn't.
|
||||
true,
|
||||
);
|
||||
};
|
||||
export const defaultRepositoryParser: BitbucketRepositoryParser =
|
||||
async function* defaultRepositoryParser({ target }) {
|
||||
yield results.location(
|
||||
{
|
||||
type: 'url',
|
||||
target: target,
|
||||
},
|
||||
// Not all locations may actually exist, since the user defined them as a wildcard pattern.
|
||||
// Thus, we emit them as optional and let the downstream processor find them while not outputting
|
||||
// an error if it couldn't.
|
||||
true,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -330,7 +330,7 @@ export async function queryWithPaging<
|
||||
GraphqlType,
|
||||
OutputType,
|
||||
Variables extends {},
|
||||
Response = QueryResponse
|
||||
Response = QueryResponse,
|
||||
>(
|
||||
client: typeof graphql,
|
||||
query: string,
|
||||
|
||||
@@ -58,11 +58,9 @@ export function* parseEntityYaml(
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultEntityDataParser: CatalogProcessorParser = async function* defaultEntityDataParser({
|
||||
data,
|
||||
location,
|
||||
}) {
|
||||
for (const e of parseEntityYaml(data, location)) {
|
||||
yield e;
|
||||
}
|
||||
};
|
||||
export const defaultEntityDataParser: CatalogProcessorParser =
|
||||
async function* defaultEntityDataParser({ data, location }) {
|
||||
for (const e of parseEntityYaml(data, location)) {
|
||||
yield e;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -31,9 +31,9 @@ describe('ConfigLocationEntityProvider', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const mockConnection = ({
|
||||
const mockConnection = {
|
||||
applyMutation: jest.fn(),
|
||||
} as unknown) as EntityProviderConnection;
|
||||
} as unknown as EntityProviderConnection;
|
||||
const locationProvider = new ConfigLocationEntityProvider(mockConfig);
|
||||
|
||||
await locationProvider.connect(mockConnection);
|
||||
|
||||
@@ -22,17 +22,17 @@ import { CatalogProcessingOrchestrator } from './processing/types';
|
||||
import { Stitcher } from './stitching/Stitcher';
|
||||
|
||||
describe('DefaultCatalogProcessingEngine', () => {
|
||||
const db = ({
|
||||
const db = {
|
||||
transaction: jest.fn(),
|
||||
getProcessableEntities: jest.fn(),
|
||||
updateProcessedEntity: jest.fn(),
|
||||
} as unknown) as jest.Mocked<DefaultProcessingDatabase>;
|
||||
} as unknown as jest.Mocked<DefaultProcessingDatabase>;
|
||||
const orchestrator: jest.Mocked<CatalogProcessingOrchestrator> = {
|
||||
process: jest.fn(),
|
||||
};
|
||||
const stitcher = ({
|
||||
const stitcher = {
|
||||
stitch: jest.fn(),
|
||||
} as unknown) as jest.Mocked<Stitcher>;
|
||||
} as unknown as jest.Mocked<Stitcher>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
|
||||
@@ -28,9 +28,10 @@ import {
|
||||
DbSearchRow,
|
||||
} from './database/tables';
|
||||
|
||||
function parsePagination(
|
||||
input?: EntityPagination,
|
||||
): { limit?: number; offset?: number } {
|
||||
function parsePagination(input?: EntityPagination): {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
} {
|
||||
if (!input) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -47,13 +47,8 @@ export interface RouterOptions {
|
||||
export async function createNextRouter(
|
||||
options: RouterOptions,
|
||||
): Promise<express.Router> {
|
||||
const {
|
||||
entitiesCatalog,
|
||||
locationAnalyzer,
|
||||
locationService,
|
||||
config,
|
||||
logger,
|
||||
} = options;
|
||||
const { entitiesCatalog, locationAnalyzer, locationService, config, logger } =
|
||||
options;
|
||||
|
||||
const router = Router();
|
||||
router.use(express.json());
|
||||
|
||||
@@ -79,10 +79,10 @@ describe('Default Processing Database', () => {
|
||||
'updates refresh state with varying location keys, %p',
|
||||
async databaseId => {
|
||||
const mockWarn = jest.fn();
|
||||
const { db } = await createDatabase(databaseId, ({
|
||||
const { db } = await createDatabase(databaseId, {
|
||||
debug: jest.fn(),
|
||||
warn: mockWarn,
|
||||
} as unknown) as Logger);
|
||||
} as unknown as Logger);
|
||||
await db.transaction(async tx => {
|
||||
const knexTx = tx as Knex.Transaction;
|
||||
|
||||
|
||||
@@ -54,7 +54,8 @@ type Context = {
|
||||
};
|
||||
|
||||
export class DefaultCatalogProcessingOrchestrator
|
||||
implements CatalogProcessingOrchestrator {
|
||||
implements CatalogProcessingOrchestrator
|
||||
{
|
||||
constructor(
|
||||
private readonly options: {
|
||||
processors: CatalogProcessor[];
|
||||
|
||||
@@ -59,23 +59,21 @@ export class DefaultCatalogCollator implements DocumentCollator {
|
||||
const baseUrl = await this.discovery.getBaseUrl('catalog');
|
||||
const res = await fetch(`${baseUrl}/entities`);
|
||||
const entities: Entity[] = await res.json();
|
||||
return entities.map(
|
||||
(entity: Entity): CatalogEntityDocument => {
|
||||
return {
|
||||
title: entity.metadata.name,
|
||||
location: this.applyArgsToFormat(this.locationTemplate, {
|
||||
namespace: entity.metadata.namespace || 'default',
|
||||
kind: entity.kind,
|
||||
name: entity.metadata.name,
|
||||
}),
|
||||
text: entity.metadata.description || '',
|
||||
componentType: entity.spec?.type?.toString() || 'other',
|
||||
return entities.map((entity: Entity): CatalogEntityDocument => {
|
||||
return {
|
||||
title: entity.metadata.name,
|
||||
location: this.applyArgsToFormat(this.locationTemplate, {
|
||||
namespace: entity.metadata.namespace || 'default',
|
||||
kind: entity.kind,
|
||||
lifecycle: (entity.spec?.lifecycle as string) || '',
|
||||
owner: (entity.spec?.owner as string) || '',
|
||||
};
|
||||
},
|
||||
);
|
||||
name: entity.metadata.name,
|
||||
}),
|
||||
text: entity.metadata.description || '',
|
||||
componentType: entity.spec?.type?.toString() || 'other',
|
||||
namespace: entity.metadata.namespace || 'default',
|
||||
kind: entity.kind,
|
||||
lifecycle: (entity.spec?.lifecycle as string) || '',
|
||||
owner: (entity.spec?.owner as string) || '',
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,8 +60,8 @@ describe('parseEntityTransformParams', () => {
|
||||
expect(
|
||||
parseEntityTransformParams({ fields: 'kind,metadata.name' })!(entity),
|
||||
).toEqual({ kind: 'k', metadata: { name: 'n' } });
|
||||
expect(
|
||||
parseEntityTransformParams({ fields: 'metadata' })!(entity),
|
||||
).toEqual({ metadata: { name: 'n', tags: ['t1', 't2'] } });
|
||||
expect(parseEntityTransformParams({ fields: 'metadata' })!(entity)).toEqual(
|
||||
{ metadata: { name: 'n', tags: ['t1', 't2'] } },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -291,10 +291,10 @@ describe('createRouter readonly disabled', () => {
|
||||
|
||||
describe('POST /locations', () => {
|
||||
it('rejects malformed locations', async () => {
|
||||
const spec = ({
|
||||
const spec = {
|
||||
typez: 'b',
|
||||
target: 'c',
|
||||
} as unknown) as LocationSpec;
|
||||
} as unknown as LocationSpec;
|
||||
|
||||
const response = await request(app).post('/locations').send(spec);
|
||||
|
||||
|
||||
@@ -49,11 +49,8 @@ export async function startStandaloneServer(
|
||||
config,
|
||||
reader,
|
||||
});
|
||||
const {
|
||||
entitiesCatalog,
|
||||
locationsCatalog,
|
||||
higherOrderOperation,
|
||||
} = await builder.build();
|
||||
const { entitiesCatalog, locationsCatalog, higherOrderOperation } =
|
||||
await builder.build();
|
||||
|
||||
logger.debug('Starting application server...');
|
||||
const router = await createRouter({
|
||||
|
||||
@@ -52,7 +52,7 @@ export async function validateRequestBody<T>(
|
||||
throw new InputError(`Malformed request: ${e}`);
|
||||
}
|
||||
|
||||
return (body as unknown) as T;
|
||||
return body as unknown as T;
|
||||
}
|
||||
|
||||
export function disallowReadonlyMode(readonly: boolean) {
|
||||
|
||||
@@ -219,7 +219,7 @@ export interface SubscriptionSubscriberObject<
|
||||
TKey extends string,
|
||||
TParent,
|
||||
TContext,
|
||||
TArgs
|
||||
TArgs,
|
||||
> {
|
||||
subscribe: SubscriptionSubscribeFn<
|
||||
{ [key in TKey]: TResult },
|
||||
@@ -245,7 +245,7 @@ export type SubscriptionObject<
|
||||
TKey extends string,
|
||||
TParent,
|
||||
TContext,
|
||||
TArgs
|
||||
TArgs,
|
||||
> =
|
||||
| SubscriptionSubscriberObject<TResult, TKey, TParent, TContext, TArgs>
|
||||
| SubscriptionResolverObject<TResult, TParent, TContext, TArgs>;
|
||||
@@ -255,7 +255,7 @@ export type SubscriptionResolver<
|
||||
TKey extends string,
|
||||
TParent = {},
|
||||
TContext = {},
|
||||
TArgs = {}
|
||||
TArgs = {},
|
||||
> =
|
||||
| ((
|
||||
...args: any[]
|
||||
@@ -279,7 +279,7 @@ export type DirectiveResolverFn<
|
||||
TResult = {},
|
||||
TParent = {},
|
||||
TContext = {},
|
||||
TArgs = {}
|
||||
TArgs = {},
|
||||
> = (
|
||||
next: NextResolverFn<TResult>,
|
||||
parent: TParent,
|
||||
@@ -364,7 +364,7 @@ export interface JsonObjectScalarConfig
|
||||
|
||||
export type EntityMetadataResolvers<
|
||||
ContextType = ModuleContext,
|
||||
ParentType = ResolversParentTypes['EntityMetadata']
|
||||
ParentType = ResolversParentTypes['EntityMetadata'],
|
||||
> = ResolversObject<{
|
||||
__resolveType: TypeResolveFn<
|
||||
'DefaultEntityMetadata' | 'ComponentMetadata' | 'TemplateMetadata',
|
||||
@@ -393,7 +393,7 @@ export type EntityMetadataResolvers<
|
||||
|
||||
export type DefaultEntityMetadataResolvers<
|
||||
ContextType = ModuleContext,
|
||||
ParentType = ResolversParentTypes['DefaultEntityMetadata']
|
||||
ParentType = ResolversParentTypes['DefaultEntityMetadata'],
|
||||
> = ResolversObject<{
|
||||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
annotations?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
|
||||
@@ -418,7 +418,7 @@ export type DefaultEntityMetadataResolvers<
|
||||
|
||||
export type ComponentMetadataResolvers<
|
||||
ContextType = ModuleContext,
|
||||
ParentType = ResolversParentTypes['ComponentMetadata']
|
||||
ParentType = ResolversParentTypes['ComponentMetadata'],
|
||||
> = ResolversObject<{
|
||||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
annotations?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
|
||||
@@ -448,7 +448,7 @@ export type ComponentMetadataResolvers<
|
||||
|
||||
export type TemplateMetadataResolvers<
|
||||
ContextType = ModuleContext,
|
||||
ParentType = ResolversParentTypes['TemplateMetadata']
|
||||
ParentType = ResolversParentTypes['TemplateMetadata'],
|
||||
> = ResolversObject<{
|
||||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
annotations?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
|
||||
@@ -478,7 +478,7 @@ export type TemplateMetadataResolvers<
|
||||
|
||||
export type TemplateEntitySpecResolvers<
|
||||
ContextType = ModuleContext,
|
||||
ParentType = ResolversParentTypes['TemplateEntitySpec']
|
||||
ParentType = ResolversParentTypes['TemplateEntitySpec'],
|
||||
> = ResolversObject<{
|
||||
type?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
path?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
@@ -489,7 +489,7 @@ export type TemplateEntitySpecResolvers<
|
||||
|
||||
export type ComponentEntitySpecResolvers<
|
||||
ContextType = ModuleContext,
|
||||
ParentType = ResolversParentTypes['ComponentEntitySpec']
|
||||
ParentType = ResolversParentTypes['ComponentEntitySpec'],
|
||||
> = ResolversObject<{
|
||||
title?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
lifecycle?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
@@ -499,7 +499,7 @@ export type ComponentEntitySpecResolvers<
|
||||
|
||||
export type LocationEntitySpecResolvers<
|
||||
ContextType = ModuleContext,
|
||||
ParentType = ResolversParentTypes['LocationEntitySpec']
|
||||
ParentType = ResolversParentTypes['LocationEntitySpec'],
|
||||
> = ResolversObject<{
|
||||
type?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
target?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
@@ -509,7 +509,7 @@ export type LocationEntitySpecResolvers<
|
||||
|
||||
export type DefaultEntitySpecResolvers<
|
||||
ContextType = ModuleContext,
|
||||
ParentType = ResolversParentTypes['DefaultEntitySpec']
|
||||
ParentType = ResolversParentTypes['DefaultEntitySpec'],
|
||||
> = ResolversObject<{
|
||||
raw?: Resolver<Maybe<ResolversTypes['JSONObject']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType>;
|
||||
@@ -517,7 +517,7 @@ export type DefaultEntitySpecResolvers<
|
||||
|
||||
export type EntitySpecResolvers<
|
||||
ContextType = ModuleContext,
|
||||
ParentType = ResolversParentTypes['EntitySpec']
|
||||
ParentType = ResolversParentTypes['EntitySpec'],
|
||||
> = ResolversObject<{
|
||||
__resolveType: TypeResolveFn<
|
||||
| 'DefaultEntitySpec'
|
||||
@@ -531,7 +531,7 @@ export type EntitySpecResolvers<
|
||||
|
||||
export type CatalogEntityResolvers<
|
||||
ContextType = ModuleContext,
|
||||
ParentType = ResolversParentTypes['CatalogEntity']
|
||||
ParentType = ResolversParentTypes['CatalogEntity'],
|
||||
> = ResolversObject<{
|
||||
apiVersion?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
kind?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
@@ -546,7 +546,7 @@ export type CatalogEntityResolvers<
|
||||
|
||||
export type CatalogQueryResolvers<
|
||||
ContextType = ModuleContext,
|
||||
ParentType = ResolversParentTypes['CatalogQuery']
|
||||
ParentType = ResolversParentTypes['CatalogQuery'],
|
||||
> = ResolversObject<{
|
||||
list?: Resolver<
|
||||
Array<ResolversTypes['CatalogEntity']>,
|
||||
@@ -558,7 +558,7 @@ export type CatalogQueryResolvers<
|
||||
|
||||
export type QueryResolvers<
|
||||
ContextType = ModuleContext,
|
||||
ParentType = ResolversParentTypes['Query']
|
||||
ParentType = ResolversParentTypes['Query'],
|
||||
> = ResolversObject<{
|
||||
catalog?: Resolver<ResolversTypes['CatalogQuery'], ParentType, ContextType>;
|
||||
}>;
|
||||
|
||||
@@ -178,7 +178,7 @@ export const ImportStepper: ({
|
||||
//
|
||||
// @public
|
||||
export const PreparePullRequestForm: <
|
||||
TFieldValues extends Record<string, any>
|
||||
TFieldValues extends Record<string, any>,
|
||||
>({
|
||||
defaultValues,
|
||||
onSubmit,
|
||||
|
||||
@@ -192,7 +192,7 @@ describe('CatalogImportClient', () => {
|
||||
});
|
||||
|
||||
it('should find locations from github', async () => {
|
||||
((new Octokit().search.code as any) as jest.Mock).mockResolvedValueOnce({
|
||||
(new Octokit().search.code as any as jest.Mock).mockResolvedValueOnce({
|
||||
data: {
|
||||
total_count: 2,
|
||||
items: [
|
||||
@@ -248,7 +248,7 @@ describe('CatalogImportClient', () => {
|
||||
});
|
||||
|
||||
it('should find repository from github', async () => {
|
||||
((new Octokit().search.code as any) as jest.Mock).mockResolvedValueOnce({
|
||||
(new Octokit().search.code as any as jest.Mock).mockResolvedValueOnce({
|
||||
data: { total_count: 0, items: [] },
|
||||
});
|
||||
|
||||
@@ -312,7 +312,7 @@ describe('CatalogImportClient', () => {
|
||||
});
|
||||
|
||||
expect(
|
||||
((new Octokit().git.createRef as any) as jest.Mock).mock.calls[0][0],
|
||||
(new Octokit().git.createRef as any as jest.Mock).mock.calls[0][0],
|
||||
).toEqual({
|
||||
owner: 'backstage',
|
||||
repo: 'backstage',
|
||||
@@ -320,7 +320,7 @@ describe('CatalogImportClient', () => {
|
||||
sha: 'any',
|
||||
});
|
||||
expect(
|
||||
((new Octokit().repos.createOrUpdateFileContents as any) as jest.Mock)
|
||||
(new Octokit().repos.createOrUpdateFileContents as any as jest.Mock)
|
||||
.mock.calls[0][0],
|
||||
).toEqual({
|
||||
owner: 'backstage',
|
||||
@@ -331,7 +331,7 @@ describe('CatalogImportClient', () => {
|
||||
branch: 'backstage-integration',
|
||||
});
|
||||
expect(
|
||||
((new Octokit().pulls.create as any) as jest.Mock).mock.calls[0][0],
|
||||
(new Octokit().pulls.create as any as jest.Mock).mock.calls[0][0],
|
||||
).toEqual({
|
||||
owner: 'backstage',
|
||||
repo: 'backstage',
|
||||
|
||||
@@ -53,8 +53,7 @@ export const ImportComponentPage = (opts: StepperProviderOpts) => {
|
||||
title="Register an existing component"
|
||||
deepLink={{
|
||||
title: 'Learn more about the Software Catalog',
|
||||
link:
|
||||
'https://backstage.io/docs/features/software-catalog/software-catalog-overview',
|
||||
link: 'https://backstage.io/docs/features/software-catalog/software-catalog-overview',
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2" paragraph>
|
||||
|
||||
@@ -39,9 +39,10 @@ import { ConfigApi } from '@backstage/core-plugin-api';
|
||||
export type StepperProviderOpts = {
|
||||
pullRequest?: {
|
||||
disable?: boolean;
|
||||
preparePullRequest?: (
|
||||
apis: StepperApis,
|
||||
) => { title?: string; body?: string };
|
||||
preparePullRequest?: (apis: StepperApis) => {
|
||||
title?: string;
|
||||
body?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -382,7 +382,7 @@ describe('<StepInitAnalyzeUrl />', () => {
|
||||
);
|
||||
|
||||
catalogImportApi.analyzeUrl.mockReturnValueOnce(
|
||||
Promise.resolve(({ type: 'unknown' } as any) as AnalyzeResult),
|
||||
Promise.resolve({ type: 'unknown' } as any as AnalyzeResult),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
|
||||
+3
-8
@@ -49,19 +49,14 @@ type Props<TFieldValues extends Record<string, any>> = Pick<
|
||||
* @param render render the form elements
|
||||
*/
|
||||
export const PreparePullRequestForm = <
|
||||
TFieldValues extends Record<string, any>
|
||||
TFieldValues extends Record<string, any>,
|
||||
>({
|
||||
defaultValues,
|
||||
onSubmit,
|
||||
render,
|
||||
}: Props<TFieldValues>) => {
|
||||
const {
|
||||
handleSubmit,
|
||||
watch,
|
||||
control,
|
||||
register,
|
||||
errors,
|
||||
} = useForm<TFieldValues>({ mode: 'onTouched', defaultValues });
|
||||
const { handleSubmit, watch, control, register, errors } =
|
||||
useForm<TFieldValues>({ mode: 'onTouched', defaultValues });
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
|
||||
@@ -516,9 +516,7 @@ export const entityRoute: RouteRef<{
|
||||
// Warning: (ae-missing-release-tag) "entityRouteParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export function entityRouteParams(
|
||||
entity: Entity,
|
||||
): {
|
||||
export function entityRouteParams(entity: Entity): {
|
||||
readonly kind: string;
|
||||
readonly namespace: string;
|
||||
readonly name: string;
|
||||
@@ -721,7 +719,7 @@ export const useEntityFromUrl: () => EntityLoadingStatus;
|
||||
//
|
||||
// @public (undocumented)
|
||||
export function useEntityListProvider<
|
||||
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters
|
||||
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,
|
||||
>(): EntityListContextProps<EntityFilters>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "useEntityOwnership" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
|
||||
+2
-6
@@ -34,12 +34,8 @@ const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
|
||||
const checkedIcon = <CheckBoxIcon fontSize="small" />;
|
||||
|
||||
export const EntityLifecyclePicker = () => {
|
||||
const {
|
||||
updateFilters,
|
||||
backendEntities,
|
||||
filters,
|
||||
queryParameters,
|
||||
} = useEntityListProvider();
|
||||
const { updateFilters, backendEntities, filters, queryParameters } =
|
||||
useEntityListProvider();
|
||||
|
||||
const queryParamLifecycles = [queryParameters.lifecycles]
|
||||
.flat()
|
||||
|
||||
@@ -36,12 +36,8 @@ const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
|
||||
const checkedIcon = <CheckBoxIcon fontSize="small" />;
|
||||
|
||||
export const EntityOwnerPicker = () => {
|
||||
const {
|
||||
updateFilters,
|
||||
backendEntities,
|
||||
filters,
|
||||
queryParameters,
|
||||
} = useEntityListProvider();
|
||||
const { updateFilters, backendEntities, filters, queryParameters } =
|
||||
useEntityListProvider();
|
||||
|
||||
const queryParamOwners = [queryParameters.owners]
|
||||
.flat()
|
||||
|
||||
@@ -134,7 +134,7 @@ export function createSystemColumn<T extends Entity>(): TableColumn<T> {
|
||||
}
|
||||
|
||||
export function createMetadataDescriptionColumn<
|
||||
T extends Entity
|
||||
T extends Entity,
|
||||
>(): TableColumn<T> {
|
||||
return {
|
||||
title: 'Description',
|
||||
|
||||
@@ -34,12 +34,8 @@ const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
|
||||
const checkedIcon = <CheckBoxIcon fontSize="small" />;
|
||||
|
||||
export const EntityTagPicker = () => {
|
||||
const {
|
||||
updateFilters,
|
||||
backendEntities,
|
||||
filters,
|
||||
queryParameters,
|
||||
} = useEntityListProvider();
|
||||
const { updateFilters, backendEntities, filters, queryParameters } =
|
||||
useEntityListProvider();
|
||||
|
||||
const queryParamTags = [queryParameters.tags]
|
||||
.flat()
|
||||
|
||||
@@ -63,17 +63,17 @@ const entities: Entity[] = [
|
||||
const apis = ApiRegistry.from([
|
||||
[
|
||||
catalogApiRef,
|
||||
({
|
||||
{
|
||||
getEntities: jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve({ items: entities })),
|
||||
} as unknown) as CatalogApi,
|
||||
} as unknown as CatalogApi,
|
||||
],
|
||||
[
|
||||
alertApiRef,
|
||||
({
|
||||
{
|
||||
post: jest.fn(),
|
||||
} as unknown) as AlertApi,
|
||||
} as unknown as AlertApi,
|
||||
],
|
||||
]);
|
||||
|
||||
|
||||
@@ -24,12 +24,8 @@ import { Select } from '@backstage/core-components';
|
||||
|
||||
export const EntityTypePicker = () => {
|
||||
const alertApi = useApi(alertApiRef);
|
||||
const {
|
||||
error,
|
||||
availableTypes,
|
||||
selectedTypes,
|
||||
setSelectedTypes,
|
||||
} = useEntityTypeFilter();
|
||||
const { error, availableTypes, selectedTypes, setSelectedTypes } =
|
||||
useEntityTypeFilter();
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
|
||||
@@ -129,12 +129,8 @@ export const UserListPicker = ({
|
||||
}))
|
||||
.filter(({ items }) => !!items.length);
|
||||
|
||||
const {
|
||||
filters,
|
||||
updateFilters,
|
||||
backendEntities,
|
||||
queryParameters,
|
||||
} = useEntityListProvider();
|
||||
const { filters, updateFilters, backendEntities, queryParameters } =
|
||||
useEntityListProvider();
|
||||
|
||||
const { isStarredEntity } = useStarredEntities();
|
||||
const { isOwnedEntity } = useEntityOwnership();
|
||||
@@ -166,9 +162,8 @@ export const UserListPicker = ({
|
||||
|
||||
// To show proper counts for each section, apply all other frontend filters _except_ the user
|
||||
// filter that's controlled by this picker.
|
||||
const [entitiesWithoutUserFilter, setEntitiesWithoutUserFilter] = useState(
|
||||
backendEntities,
|
||||
);
|
||||
const [entitiesWithoutUserFilter, setEntitiesWithoutUserFilter] =
|
||||
useState(backendEntities);
|
||||
useEffect(() => {
|
||||
const filterFn = reduceEntityFilters(
|
||||
compact(Object.values({ ...filters, user: undefined })),
|
||||
|
||||
@@ -41,7 +41,12 @@ export const useEntityFromUrl = (): EntityLoadingStatus => {
|
||||
const errorApi = useApi(errorApiRef);
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
|
||||
const { value: entity, error, loading, retry: refresh } = useAsyncRetry(
|
||||
const {
|
||||
value: entity,
|
||||
error,
|
||||
loading,
|
||||
retry: refresh,
|
||||
} = useAsyncRetry(
|
||||
() => catalogApi.getEntityByName({ kind, namespace, name }),
|
||||
[catalogApi, kind, namespace, name],
|
||||
);
|
||||
|
||||
@@ -51,7 +51,7 @@ export type DefaultEntityFilters = {
|
||||
};
|
||||
|
||||
export type EntityListContextProps<
|
||||
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters
|
||||
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,
|
||||
> = {
|
||||
/**
|
||||
* The currently registered filters, adhering to the shape of DefaultEntityFilters or an extension
|
||||
@@ -215,7 +215,7 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({
|
||||
};
|
||||
|
||||
export function useEntityListProvider<
|
||||
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters
|
||||
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,
|
||||
>(): EntityListContextProps<EntityFilters> {
|
||||
const context = useContext(EntityListContext);
|
||||
if (!context)
|
||||
|
||||
@@ -46,8 +46,8 @@ describe('useEntityOwnership', () => {
|
||||
getEntityByName: jest.fn(),
|
||||
};
|
||||
|
||||
const identityApi = (mockIdentityApi as unknown) as IdentityApi;
|
||||
const catalogApi = (mockCatalogApi as unknown) as CatalogApi;
|
||||
const identityApi = mockIdentityApi as unknown as IdentityApi;
|
||||
const catalogApi = mockCatalogApi as unknown as CatalogApi;
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider
|
||||
|
||||
@@ -131,9 +131,10 @@ export function useEntityOwnership(): {
|
||||
const isOwnedEntity = useMemo(() => {
|
||||
const myOwnerRefs = new Set(refs ?? []);
|
||||
return (entity: Entity | EntityName) => {
|
||||
const entityOwnerRefs = ('metadata' in entity
|
||||
? getEntityRelations(entity, RELATION_OWNED_BY)
|
||||
: [entity]
|
||||
const entityOwnerRefs = (
|
||||
'metadata' in entity
|
||||
? getEntityRelations(entity, RELATION_OWNED_BY)
|
||||
: [entity]
|
||||
).map(stringifyEntityRef);
|
||||
for (const ref of entityOwnerRefs) {
|
||||
if (myOwnerRefs.has(ref)) {
|
||||
|
||||
@@ -53,7 +53,11 @@ export function useEntityTypeFilter(): EntityTypeReturn {
|
||||
|
||||
// Load all valid spec.type values straight from the catalogApi, paying attention to only the
|
||||
// kind filter for a complete list.
|
||||
const { error, loading, value: entities } = useAsync(async () => {
|
||||
const {
|
||||
error,
|
||||
loading,
|
||||
value: entities,
|
||||
} = useAsync(async () => {
|
||||
if (kind) {
|
||||
const items = await catalogApi
|
||||
.getEntities({
|
||||
|
||||
@@ -30,7 +30,11 @@ export function useRelatedEntities(
|
||||
error: Error | undefined;
|
||||
} {
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
const { loading, value: entities, error } = useAsync(async () => {
|
||||
const {
|
||||
loading,
|
||||
value: entities,
|
||||
error,
|
||||
} = useAsync(async () => {
|
||||
const relations =
|
||||
entity.relations &&
|
||||
entity.relations.filter(
|
||||
|
||||
@@ -158,7 +158,11 @@ export function SystemDiagramCard() {
|
||||
const systemEdges = new Array<{ from: string; to: string; label: string }>();
|
||||
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
const { loading, error, value: catalogResponse } = useAsync(() => {
|
||||
const {
|
||||
loading,
|
||||
error,
|
||||
value: catalogResponse,
|
||||
} = useAsync(() => {
|
||||
return catalogApi.getEntities({
|
||||
filter: {
|
||||
kind: ['Component', 'API', 'Resource', 'System', 'Domain'],
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ describe('useUnregisterEntityDialogState', () => {
|
||||
removeLocationById: jest.fn(),
|
||||
removeEntityByUid: jest.fn(),
|
||||
};
|
||||
const catalogApi = (catalogApiMock as Partial<CatalogApi>) as CatalogApi;
|
||||
const catalogApi = catalogApiMock as Partial<CatalogApi> as CatalogApi;
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={ApiRegistry.with(catalogApiRef, catalogApi)}>
|
||||
|
||||
@@ -35,7 +35,7 @@ import {
|
||||
Link,
|
||||
} from '@backstage/core-components';
|
||||
|
||||
const IconLink = (IconButton as any) as typeof MaterialLink;
|
||||
const IconLink = IconButton as any as typeof MaterialLink;
|
||||
|
||||
const BuildName = ({ build }: { build?: BuildWithSteps }) => (
|
||||
<Box display="flex" alignItems="center">
|
||||
|
||||
@@ -62,9 +62,10 @@ export function useBuildWithSteps(buildId: number) {
|
||||
}
|
||||
};
|
||||
|
||||
const { loading, value, retry } = useAsyncRetry(() => getBuildWithSteps(), [
|
||||
getBuildWithSteps,
|
||||
]);
|
||||
const { loading, value, retry } = useAsyncRetry(
|
||||
() => getBuildWithSteps(),
|
||||
[getBuildWithSteps],
|
||||
);
|
||||
|
||||
const { startPolling, stopPolling } = useAsyncPolling(
|
||||
getBuildWithSteps,
|
||||
|
||||
@@ -26,21 +26,23 @@ import { errorApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
const makeReadableStatus = (status: string | undefined) => {
|
||||
if (!status) return '';
|
||||
return ({
|
||||
retried: 'Retried',
|
||||
canceled: 'Canceled',
|
||||
infrastructure_fail: 'Infra fail',
|
||||
timedout: 'Timedout',
|
||||
not_run: 'Not run',
|
||||
running: 'Running',
|
||||
failed: 'Failed',
|
||||
queued: 'Queued',
|
||||
scheduled: 'Scheduled',
|
||||
not_running: 'Not running',
|
||||
no_tests: 'No tests',
|
||||
fixed: 'Fixed',
|
||||
success: 'Success',
|
||||
} as Record<string, string>)[status];
|
||||
return (
|
||||
{
|
||||
retried: 'Retried',
|
||||
canceled: 'Canceled',
|
||||
infrastructure_fail: 'Infra fail',
|
||||
timedout: 'Timedout',
|
||||
not_run: 'Not run',
|
||||
running: 'Running',
|
||||
failed: 'Failed',
|
||||
queued: 'Queued',
|
||||
scheduled: 'Scheduled',
|
||||
not_running: 'Not running',
|
||||
no_tests: 'No tests',
|
||||
fixed: 'Fixed',
|
||||
success: 'Success',
|
||||
} as Record<string, string>
|
||||
)[status];
|
||||
};
|
||||
|
||||
const mapWorkflowDetails = (buildData: BuildSummary) => {
|
||||
|
||||
@@ -60,7 +60,8 @@ export class CloudbuildClient implements CloudbuildApi {
|
||||
},
|
||||
);
|
||||
|
||||
const builds: ActionsListWorkflowRunsForRepoResponseData = await workflowRuns.json();
|
||||
const builds: ActionsListWorkflowRunsForRepoResponseData =
|
||||
await workflowRuns.json();
|
||||
|
||||
return builds;
|
||||
}
|
||||
|
||||
@@ -17,15 +17,16 @@ import { useAsync } from 'react-use';
|
||||
import { ActionsListWorkflowRunsForRepoResponseData } from '../../api/types';
|
||||
|
||||
export const useWorkflowRunJobs = (jobsUrl?: string) => {
|
||||
const jobs = useAsync(async (): Promise<ActionsListWorkflowRunsForRepoResponseData> => {
|
||||
if (jobsUrl === undefined) {
|
||||
return {
|
||||
builds: [],
|
||||
};
|
||||
}
|
||||
const jobs =
|
||||
useAsync(async (): Promise<ActionsListWorkflowRunsForRepoResponseData> => {
|
||||
if (jobsUrl === undefined) {
|
||||
return {
|
||||
builds: [],
|
||||
};
|
||||
}
|
||||
|
||||
const data = await fetch(jobsUrl).then(d => d.json());
|
||||
return data;
|
||||
}, [jobsUrl]);
|
||||
const data = await fetch(jobsUrl).then(d => d.json());
|
||||
return data;
|
||||
}, [jobsUrl]);
|
||||
return jobs;
|
||||
};
|
||||
|
||||
@@ -41,9 +41,12 @@ export function useWorkflowRuns({ projectId }: { projectId: string }) {
|
||||
const [page, setPage] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(5);
|
||||
|
||||
const { loading, value: runs, retry, error } = useAsyncRetry<
|
||||
WorkflowRun[]
|
||||
>(async () => {
|
||||
const {
|
||||
loading,
|
||||
value: runs,
|
||||
retry,
|
||||
error,
|
||||
} = useAsyncRetry<WorkflowRun[]>(async () => {
|
||||
return api
|
||||
.listWorkflowRuns({
|
||||
projectId,
|
||||
|
||||
@@ -117,8 +117,8 @@ export class Cobertura implements Converter {
|
||||
const lines = [classLines, methodLines].flat().filter(Boolean);
|
||||
const lineHits = lines.map(l => {
|
||||
return {
|
||||
number: parseInt((l.$.number as unknown) as string, 10),
|
||||
hits: parseInt((l.$.hits as unknown) as string, 10),
|
||||
number: parseInt(l.$.number as unknown as string, 10),
|
||||
hits: parseInt(l.$.hits as unknown as string, 10),
|
||||
'condition-coverage': l.$['condition-coverage'],
|
||||
branch: l.$.branch,
|
||||
};
|
||||
|
||||
@@ -189,12 +189,8 @@ export const makeRouter = async (
|
||||
throw new InputError(`Unsupported coverage type '${coverageType}`);
|
||||
}
|
||||
|
||||
const {
|
||||
sourceLocation,
|
||||
vcs,
|
||||
scmFiles,
|
||||
body,
|
||||
} = await utils.processCoveragePayload(entityLookup, req);
|
||||
const { sourceLocation, vcs, scmFiles, body } =
|
||||
await utils.processCoveragePayload(entityLookup, req);
|
||||
|
||||
const files = converter.convert(body, scmFiles);
|
||||
if (!files || files.length === 0) {
|
||||
|
||||
@@ -31,7 +31,7 @@ createDevApp()
|
||||
factory: () => ({
|
||||
schema$: () =>
|
||||
new Observable<ConfigSchemaResult>(sub =>
|
||||
sub.next({ schema: (exampleSchema as unknown) as Schema }),
|
||||
sub.next({ schema: exampleSchema as unknown as Schema }),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -100,7 +100,7 @@ export enum AlertDismissReason {
|
||||
// @public (undocumented)
|
||||
export type AlertForm<
|
||||
A extends Alert = any,
|
||||
Data = any
|
||||
Data = any,
|
||||
> = ForwardRefExoticComponent<
|
||||
AlertFormProps<A, Data> & RefAttributes<HTMLFormElement>
|
||||
>;
|
||||
|
||||
@@ -41,10 +41,10 @@ import { ScrollAnchor } from '../../utils/scroll';
|
||||
|
||||
type MapLoadingtoAlerts = (isLoading: boolean) => void;
|
||||
|
||||
const mapLoadingToAlerts: MapLoadingToProps<MapLoadingtoAlerts> = ({
|
||||
dispatch,
|
||||
}) => (isLoading: boolean) =>
|
||||
dispatch({ [DefaultLoadingAction.CostInsightsAlerts]: isLoading });
|
||||
const mapLoadingToAlerts: MapLoadingToProps<MapLoadingtoAlerts> =
|
||||
({ dispatch }) =>
|
||||
(isLoading: boolean) =>
|
||||
dispatch({ [DefaultLoadingAction.CostInsightsAlerts]: isLoading });
|
||||
|
||||
type AlertInsightsProps = {
|
||||
group: string;
|
||||
|
||||
@@ -30,12 +30,12 @@ export const BarChartSteps = ({
|
||||
onClick,
|
||||
}: BarChartStepsProps) => {
|
||||
const classes = useStyles();
|
||||
const handleOnClick = (index: number) => (
|
||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
|
||||
) => {
|
||||
event.preventDefault();
|
||||
onClick(index);
|
||||
};
|
||||
const handleOnClick =
|
||||
(index: number) =>
|
||||
(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
event.preventDefault();
|
||||
onClick(index);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.steps}>
|
||||
|
||||
@@ -31,21 +31,18 @@ type CostInsightsPageLoadingProps = {
|
||||
dispatchReset: (loadingActions: string[]) => void;
|
||||
};
|
||||
|
||||
export const mapLoadingToProps: MapLoadingToProps<CostInsightsPageLoadingProps> = ({
|
||||
state,
|
||||
actions,
|
||||
dispatch,
|
||||
}) => ({
|
||||
loadingActions: actions,
|
||||
loadingGroups: state[DefaultLoadingAction.UserGroups],
|
||||
loadingBillingDate: state[DefaultLoadingAction.LastCompleteBillingDate],
|
||||
loadingInitial: state[DefaultLoadingAction.CostInsightsInitial],
|
||||
dispatchInitial: (isLoading: boolean) =>
|
||||
dispatch({ [DefaultLoadingAction.CostInsightsInitial]: isLoading }),
|
||||
dispatchInsights: (isLoading: boolean) =>
|
||||
dispatch({ [DefaultLoadingAction.CostInsightsPage]: isLoading }),
|
||||
dispatchNone: (loadingActions: string[]) =>
|
||||
dispatch(getResetState(loadingActions)),
|
||||
dispatchReset: (loadingActions: string[]) =>
|
||||
dispatch(getResetStateWithoutInitial(loadingActions)),
|
||||
});
|
||||
export const mapLoadingToProps: MapLoadingToProps<CostInsightsPageLoadingProps> =
|
||||
({ state, actions, dispatch }) => ({
|
||||
loadingActions: actions,
|
||||
loadingGroups: state[DefaultLoadingAction.UserGroups],
|
||||
loadingBillingDate: state[DefaultLoadingAction.LastCompleteBillingDate],
|
||||
loadingInitial: state[DefaultLoadingAction.CostInsightsInitial],
|
||||
dispatchInitial: (isLoading: boolean) =>
|
||||
dispatch({ [DefaultLoadingAction.CostInsightsInitial]: isLoading }),
|
||||
dispatchInsights: (isLoading: boolean) =>
|
||||
dispatch({ [DefaultLoadingAction.CostInsightsPage]: isLoading }),
|
||||
dispatchNone: (loadingActions: string[]) =>
|
||||
dispatch(getResetState(loadingActions)),
|
||||
dispatchReset: (loadingActions: string[]) =>
|
||||
dispatch(getResetStateWithoutInitial(loadingActions)),
|
||||
});
|
||||
|
||||
@@ -28,24 +28,20 @@ type CostInsightsTabsLoadingProps = {
|
||||
dispatchReset: (loadingActions: string[]) => void;
|
||||
};
|
||||
|
||||
export const mapFiltersToProps: MapFiltersToProps<CostInsightsTabsFilterProps> = ({
|
||||
pageFilters,
|
||||
setPageFilters,
|
||||
}) => ({
|
||||
...pageFilters,
|
||||
setGroup: (group: Group) =>
|
||||
setPageFilters({
|
||||
...pageFilters,
|
||||
group: group.id,
|
||||
project: null,
|
||||
}),
|
||||
});
|
||||
export const mapFiltersToProps: MapFiltersToProps<CostInsightsTabsFilterProps> =
|
||||
({ pageFilters, setPageFilters }) => ({
|
||||
...pageFilters,
|
||||
setGroup: (group: Group) =>
|
||||
setPageFilters({
|
||||
...pageFilters,
|
||||
group: group.id,
|
||||
project: null,
|
||||
}),
|
||||
});
|
||||
|
||||
export const mapLoadingToProps: MapLoadingToProps<CostInsightsTabsLoadingProps> = ({
|
||||
actions,
|
||||
dispatch,
|
||||
}) => ({
|
||||
loadingActions: actions,
|
||||
dispatchReset: (loadingActions: string[]) =>
|
||||
dispatch(getResetStateWithoutInitial(loadingActions)),
|
||||
});
|
||||
export const mapLoadingToProps: MapLoadingToProps<CostInsightsTabsLoadingProps> =
|
||||
({ actions, dispatch }) => ({
|
||||
loadingActions: actions,
|
||||
dispatchReset: (loadingActions: string[]) =>
|
||||
dispatch(getResetStateWithoutInitial(loadingActions)),
|
||||
});
|
||||
|
||||
@@ -51,9 +51,8 @@ export const CostOverviewCard = ({
|
||||
const styles = useOverviewTabsStyles(theme);
|
||||
const config = useConfig();
|
||||
const [tabIndex, setTabIndex] = useState(0);
|
||||
const { setDuration, setProject, setMetric, ...filters } = useFilters(
|
||||
mapFiltersToProps,
|
||||
);
|
||||
const { setDuration, setProject, setMetric, ...filters } =
|
||||
useFilters(mapFiltersToProps);
|
||||
|
||||
// Reset tabIndex if breakdowns available change
|
||||
useEffect(() => {
|
||||
|
||||
@@ -170,7 +170,8 @@ describe('<ProductInsights />', () => {
|
||||
}
|
||||
|
||||
it('should render each product panel', async () => {
|
||||
const noComputeEngineCostsRgx = /There are no Compute Engine costs within this time frame for your team's projects./;
|
||||
const noComputeEngineCostsRgx =
|
||||
/There are no Compute Engine costs within this time frame for your team's projects./;
|
||||
const { getByText } = await renderInContext(
|
||||
<ProductInsights
|
||||
onLoaded={jest.fn()}
|
||||
|
||||
@@ -37,9 +37,10 @@ import { useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
type LoadingProps = (isLoading: boolean) => void;
|
||||
|
||||
const mapLoadingToProps: MapLoadingToProps<LoadingProps> = ({ dispatch }) => (
|
||||
isLoading: boolean,
|
||||
) => dispatch({ [DefaultLoadingAction.CostInsightsProducts]: isLoading });
|
||||
const mapLoadingToProps: MapLoadingToProps<LoadingProps> =
|
||||
({ dispatch }) =>
|
||||
(isLoading: boolean) =>
|
||||
dispatch({ [DefaultLoadingAction.CostInsightsProducts]: isLoading });
|
||||
|
||||
type ProductInsightsProps = {
|
||||
group: string;
|
||||
|
||||
@@ -49,9 +49,10 @@ export type ProductInsightsCardProps = {
|
||||
onSelectAsync: (product: Product, duration: Duration) => Promise<Entity>;
|
||||
};
|
||||
|
||||
const mapLoadingToProps: MapLoadingToProps<LoadingProps> = ({ dispatch }) => (
|
||||
isLoading: boolean,
|
||||
) => dispatch({ [DefaultLoadingAction.CostInsightsProducts]: isLoading });
|
||||
const mapLoadingToProps: MapLoadingToProps<LoadingProps> =
|
||||
({ dispatch }) =>
|
||||
(isLoading: boolean) =>
|
||||
dispatch({ [DefaultLoadingAction.CostInsightsProducts]: isLoading });
|
||||
|
||||
export const ProductInsightsCard = ({
|
||||
initialState,
|
||||
|
||||
@@ -29,29 +29,22 @@ type ProductInsightsCardLoadingProps = {
|
||||
dispatchLoading: (isLoading: boolean) => void;
|
||||
};
|
||||
|
||||
export const mapFiltersToProps = (
|
||||
product: string,
|
||||
): MapFiltersToProps<ProductInsightsCardFilterProps> => ({
|
||||
pageFilters,
|
||||
productFilters,
|
||||
setProductFilters,
|
||||
}) => ({
|
||||
...pageFilters,
|
||||
product: findAlways(productFilters, p => p.productType === product),
|
||||
setProduct: (duration: Duration) =>
|
||||
setProductFilters(
|
||||
productFilters.map(period =>
|
||||
period.productType === product ? { ...period, duration } : period,
|
||||
export const mapFiltersToProps =
|
||||
(product: string): MapFiltersToProps<ProductInsightsCardFilterProps> =>
|
||||
({ pageFilters, productFilters, setProductFilters }) => ({
|
||||
...pageFilters,
|
||||
product: findAlways(productFilters, p => p.productType === product),
|
||||
setProduct: (duration: Duration) =>
|
||||
setProductFilters(
|
||||
productFilters.map(period =>
|
||||
period.productType === product ? { ...period, duration } : period,
|
||||
),
|
||||
),
|
||||
),
|
||||
});
|
||||
});
|
||||
|
||||
export const mapLoadingToProps = (
|
||||
product: string,
|
||||
): MapLoadingToProps<ProductInsightsCardLoadingProps> => ({
|
||||
state,
|
||||
dispatch,
|
||||
}) => ({
|
||||
loadingProduct: state[product],
|
||||
dispatchLoading: (isLoading: boolean) => dispatch({ [product]: isLoading }),
|
||||
});
|
||||
export const mapLoadingToProps =
|
||||
(product: string): MapLoadingToProps<ProductInsightsCardLoadingProps> =>
|
||||
({ state, dispatch }) => ({
|
||||
loadingProduct: state[product],
|
||||
dispatchLoading: (isLoading: boolean) => dispatch({ [product]: isLoading }),
|
||||
});
|
||||
|
||||
+3
-4
@@ -22,14 +22,13 @@ import {
|
||||
} from '../../testUtils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
const MockUnlabeledDataflowAlertMultipleProjects = createMockUnlabeledDataflowData(
|
||||
data => ({
|
||||
const MockUnlabeledDataflowAlertMultipleProjects =
|
||||
createMockUnlabeledDataflowData(data => ({
|
||||
...data,
|
||||
projects: [...Array(10)].map(() =>
|
||||
createMockUnlabeledDataflowAlertProject(),
|
||||
),
|
||||
}),
|
||||
);
|
||||
}));
|
||||
|
||||
const MockUnlabeledDataflowAlertSingleProject = createMockUnlabeledDataflowData(
|
||||
data => ({
|
||||
|
||||
@@ -54,9 +54,8 @@ export const FilterProvider = ({ children }: PropsWithChildren<{}>) => {
|
||||
const groups = useGroups();
|
||||
const [error, setError] = useState<Maybe<Error>>(null);
|
||||
const [pageFilters, setPageFilters] = useState<Maybe<PageFilters>>(null);
|
||||
const [productFilters, setProductFilters] = useState<Maybe<ProductFilters>>(
|
||||
null,
|
||||
);
|
||||
const [productFilters, setProductFilters] =
|
||||
useState<Maybe<ProductFilters>>(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function setPageFiltersFromLocation() {
|
||||
|
||||
@@ -59,9 +59,8 @@ export const BillingDateProvider = ({ children }: PropsWithChildren<{}>) => {
|
||||
const [error, setError] = useState<Maybe<Error>>(null);
|
||||
const { dispatchLoadingBillingDate } = useLoading(mapLoadingToProps);
|
||||
|
||||
const [lastCompleteBillingDate, setLastCompeteBillingDate] = useState<
|
||||
Maybe<string>
|
||||
>(null);
|
||||
const [lastCompleteBillingDate, setLastCompeteBillingDate] =
|
||||
useState<Maybe<string>>(null);
|
||||
|
||||
useEffect(() => {
|
||||
dispatchLoadingBillingDate(true);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user