Skip to content

Commit

Permalink
feat: add identify api for tip activation and use ownership for missi…
Browse files Browse the repository at this point in the history
…ng data
  • Loading branch information
dweber019 committed Sep 2, 2024
1 parent 2cd3356 commit 80f9253
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 149 deletions.
7 changes: 7 additions & 0 deletions .changeset/unlucky-planets-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@dweber019/backstage-plugin-tips': patch
---

Add identify api for tip activation and use ownership for missing data.

**breaking**: This changes the interface of a tip to become a promise.
Original file line number Diff line number Diff line change
@@ -1,19 +1,3 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {
ComponentEntity,
EntityLink,
Expand All @@ -25,12 +9,24 @@ import React from 'react';
import { TipsConfig, tipsConfigRef } from '../../config';
import { EntityTipsDialog } from './EntityTipsDialog';
import { extraTips } from '../../lib/extraTips';
import { IdentityApi, identityApiRef } from '@backstage/core-plugin-api';

describe('EntityTipsDialog', () => {
const mockTipsConfig: jest.Mocked<TipsConfig> = {
tips: extraTips,
};

const mockIdentityApi: jest.Mocked<IdentityApi> = {
getBackstageIdentity: jest.fn().mockResolvedValue({
type: 'user',
userEntityRef: 'user:default/owner',
ownershipEntityRefs: ['user:default/owner'],
}),
signOut: jest.fn(),
getCredentials: jest.fn(),
getProfileInfo: jest.fn(),
};

const links: EntityLink[] = [{ url: 'link' }];
const documentationAnnotation = { 'backstage.io/techdocs-ref': 'any' };

Expand All @@ -39,11 +35,11 @@ describe('EntityTipsDialog', () => {
apiVersion: 'backstage.io/v1beta1',
metadata: { name: 'mock', links },
kind: 'Component',
spec: { system: 'any' },
spec: { system: 'any', owner: 'user:default/owner' },
} as ComponentEntity;

const rendered = await renderWithEffects(
<TestApiProvider apis={[[tipsConfigRef, mockTipsConfig]]}>
<TestApiProvider apis={[[tipsConfigRef, mockTipsConfig], [identityApiRef, mockIdentityApi]]}>
<EntityProvider entity={mockEntity}>
<EntityTipsDialog />
</EntityProvider>
Expand All @@ -64,11 +60,11 @@ describe('EntityTipsDialog', () => {
apiVersion: 'backstage.io/v1beta1',
metadata: { name: 'mock', annotations: documentationAnnotation, links },
kind: 'System',
spec: { owner: 'any' },
spec: { owner: 'user:default/owner' },
} as SystemEntity;

const rendered = await renderWithEffects(
<TestApiProvider apis={[[tipsConfigRef, mockTipsConfig]]}>
<TestApiProvider apis={[[tipsConfigRef, mockTipsConfig], [identityApiRef, mockIdentityApi]]}>
<EntityProvider entity={mockEntity}>
<EntityTipsDialog />
</EntityProvider>
Expand All @@ -89,11 +85,11 @@ describe('EntityTipsDialog', () => {
apiVersion: 'backstage.io/v1beta1',
metadata: { name: 'mock', links, annotations: documentationAnnotation },
kind: 'Component',
spec: { system: 'any', type: 'any', owner: 'any', lifecycle: 'any' },
spec: { system: 'any', type: 'any', owner: 'user:default/owner', lifecycle: 'any' },
} as ComponentEntity;

const rendered = await renderWithEffects(
<TestApiProvider apis={[[tipsConfigRef, mockTipsConfig]]}>
<TestApiProvider apis={[[tipsConfigRef, mockTipsConfig], [identityApiRef, mockIdentityApi]]}>
<EntityProvider entity={mockEntity}>
<EntityTipsDialog />
</EntityProvider>
Expand Down
31 changes: 9 additions & 22 deletions plugins/tips/src/components/EntityTipsDialog/EntityTipsDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,4 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import React, { useMemo, useState } from 'react';
import React, { useState } from 'react';
import { useEntity } from '@backstage/plugin-catalog-react';
import { ApiEntity } from '@backstage/catalog-model';
import EmojiObjectsIcon from '@material-ui/icons/EmojiObjects';
Expand All @@ -33,8 +17,9 @@ import {
Zoom,
} from '@material-ui/core';
import { MarkdownContent } from '@backstage/core-components';
import { useApi } from '@backstage/core-plugin-api';
import { identityApiRef, useApi } from '@backstage/core-plugin-api';
import { tipsConfigRef } from '../../config';
import useAsync from 'react-use/lib/useAsync';

const useStyles = makeStyles(theme => ({
fabButton: {
Expand Down Expand Up @@ -86,6 +71,7 @@ export const EntityTipsDialog = () => {
const classes = useStyles();
const { entity } = useEntity<ApiEntity>();
const tipsConfig = useApi(tipsConfigRef);
const identity = useApi(identityApiRef);
const [isDialogOpen, setIsDialogOpen] = useState(false);
const toggleDialog = () => setIsDialogOpen(!isDialogOpen);

Expand All @@ -97,12 +83,13 @@ export const EntityTipsDialog = () => {
setActiveStep(prevActiveStep => prevActiveStep - 1);
};

const tips = useMemo(() => {
const { value: tips, loading, error } = useAsync(async () => {
setActiveStep(0);
return tipsConfig.tips.filter(tip => tip.activate({ entity }));
}, [tipsConfig.tips, entity]);
const resolvedActivates = await Promise.all(tipsConfig.tips.map(tip => tip.activate({ entity, identity })));
return tipsConfig.tips.filter((_, index) => resolvedActivates[index]);
}, [tipsConfig.tips, entity, identity]);

if (tips.length === 0 || !tips[activeStep]) {
if (loading || error || tips === undefined || tips.length === 0 || !tips[activeStep]) {
return <></>;
}

Expand Down
4 changes: 2 additions & 2 deletions plugins/tips/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createApiRef } from '@backstage/core-plugin-api';
import { createApiRef, IdentityApi } from '@backstage/core-plugin-api';
import React from 'react';
import { Entity } from '@backstage/catalog-model';

Expand All @@ -15,5 +15,5 @@ export interface TipsConfig {
export interface Tip {
content: string | React.ReactElement;
title: string;
activate: (options: { entity?: Entity }) => boolean;
activate: (options: { entity?: Entity, identity?: IdentityApi }) => Promise<boolean>;
}
18 changes: 1 addition & 17 deletions plugins/tips/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,6 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/** @packageDocumentation */

export { hasAnnotation, isEntityOfKind } from './lib/utils';
export * from './lib/utils';
export { extraTips } from './lib/extraTips';
export { systemModelTips } from './lib/systemModelTips';
export { tipsPlugin, EntityTipsDialog } from './plugin';
Expand Down
Loading

0 comments on commit 80f9253

Please sign in to comment.