Skip to content

Commit eda0343

Browse files
feat(adoption-insights): add frontend plugin for adoption insights (#464)
* feat(adoption-insights): add frontend plugin for adoption insights * resolved the type issue * updated unit tests * add github backend module * remove unwanted column techdocs * remove unwanted column techdocs
1 parent 1776162 commit eda0343

95 files changed

Lines changed: 7978 additions & 478 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

workspaces/adoption-insights/packages/app/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
"@backstage/theme": "^0.6.4",
4646
"@material-ui/core": "^4.12.2",
4747
"@material-ui/icons": "^4.9.1",
48+
"@mui/icons-material": "5.16.4",
49+
"@red-hat-developer-hub/backstage-plugin-adoption-insights": "workspace:^",
4850
"@red-hat-developer-hub/backstage-plugin-analytics-module-adoption-insights": "workspace:^",
4951
"react": "^18.0.2",
5052
"react-dom": "^18.0.2",

workspaces/adoption-insights/packages/app/src/App.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ import { AppRouter, FlatRoutes } from '@backstage/core-app-api';
5151
import { CatalogGraphPage } from '@backstage/plugin-catalog-graph';
5252
import { RequirePermission } from '@backstage/plugin-permission-react';
5353
import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha';
54+
import { AdoptionInsightsPage } from '@red-hat-developer-hub/backstage-plugin-adoption-insights';
55+
import { githubAuthApiRef } from '@backstage/core-plugin-api';
5456

5557
const app = createApp({
5658
apis,
@@ -72,7 +74,22 @@ const app = createApp({
7274
});
7375
},
7476
components: {
75-
SignInPage: props => <SignInPage {...props} auto providers={['guest']} />,
77+
SignInPage: (props: any) => (
78+
<SignInPage
79+
{...props}
80+
align="center"
81+
auto
82+
providers={[
83+
'guest',
84+
{
85+
id: 'github-auth-provider',
86+
title: 'GitHub',
87+
message: 'Sign in using GitHub',
88+
apiRef: githubAuthApiRef,
89+
},
90+
]}
91+
/>
92+
),
7693
},
7794
});
7895

@@ -110,6 +127,7 @@ const routes = (
110127
</Route>
111128
<Route path="/settings" element={<UserSettingsPage />} />
112129
<Route path="/catalog-graph" element={<CatalogGraphPage />} />
130+
<Route path="/adoption-insights" element={<AdoptionInsightsPage />} />
113131
</FlatRoutes>
114132
);
115133

workspaces/adoption-insights/packages/app/src/components/Root/Root.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import HomeIcon from '@material-ui/icons/Home';
1919
import ExtensionIcon from '@material-ui/icons/Extension';
2020
import LibraryBooks from '@material-ui/icons/LibraryBooks';
2121
import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
22+
import QueryStatsIcon from '@mui/icons-material/QueryStats';
2223
import LogoFull from './LogoFull';
2324
import LogoIcon from './LogoIcon';
2425
import {
@@ -90,6 +91,11 @@ export const Root = ({ children }: PropsWithChildren<{}>) => (
9091
<SidebarItem icon={ExtensionIcon} to="api-docs" text="APIs" />
9192
<SidebarItem icon={LibraryBooks} to="docs" text="Docs" />
9293
<SidebarItem icon={CreateComponentIcon} to="create" text="Create..." />
94+
<SidebarItem
95+
icon={QueryStatsIcon}
96+
to="adoption-insights"
97+
text="Adoption Insights"
98+
/>
9399
{/* End global nav */}
94100
<SidebarDivider />
95101
<SidebarScrollWrapper>

workspaces/adoption-insights/packages/backend/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,6 @@ backend.add(
6464
import('@red-hat-developer-hub/backstage-plugin-adoption-insights-backend'),
6565
);
6666

67+
backend.add(import('@backstage/plugin-auth-backend-module-github-provider'));
68+
6769
backend.start();
Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
# adoption-insights
1+
# Adoption Insights Plugin for Backstage
22

3-
Welcome to the adoption insights plugin!
4-
5-
This plugin helps gather user analytics data from Backstage and generates a dashboard that provides insights into the platform's adoption.
3+
The Adoption Insights plugin provides an interactive dashboard to visualize analytics data in Backstage. This frontend plugin integrates with the Adoption Insights backend to deliver insights into adoption trends and usage statistics.
64

75
## Getting started
86

@@ -11,3 +9,56 @@ Your plugin has been added to the example app in this repository, meaning you'll
119
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
1210
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
1311
It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.
12+
13+
## For Administrators
14+
15+
### Prerequisites
16+
17+
Before installing the frontend plugin, ensure that the Adoption Insights backend is integrated into your Backstage instance. Follow the [Adoption Insights backend plugin README](https://github.com/redhat-developer/rhdh-plugins/blob/main/workspaces/adoption-insights/plugins/adoption-insights-backend/README.md) for setup instructions.
18+
19+
### Installation
20+
21+
To install the Adoption Insights plugin, run the following command:
22+
23+
```sh
24+
yarn workspace app add @red-hat-developer-hub/backstage-plugin-adoption-insights
25+
```
26+
27+
### Configuration
28+
29+
1. Add the **Adoption Insights** page to your Backstage application by modifying `packages/app/src/App.tsx`:
30+
31+
```tsx
32+
import { AdoptionInsightsPage } from '@red-hat-developer-hub/backstage-plugin-adoption-insights';
33+
34+
<Route path="/adoption-insights" element={<AdoptionInsightsPage />} />;
35+
```
36+
37+
2. Add a navigation item to the Backstage sidebar by updating `packages/app/src/components/Root/Root.tsx`:
38+
39+
```tsx
40+
import QueryStatsIcon from '@mui/icons-material/QueryStats';
41+
42+
<SidebarItem
43+
icon={QueryStatsIcon}
44+
to="adoption-insights"
45+
text="Adoption Insights"
46+
/>;
47+
```
48+
49+
## For Users
50+
51+
### Using the Adoption Insights Plugin
52+
53+
The Adoption Insights plugin allows users to explore analytics data through an interactive dashboard.
54+
55+
#### Prerequisites
56+
57+
- A running Backstage application.
58+
- The Adoption Insights plugin is installed and configured. See [Installation](#installation) for setup instructions.
59+
60+
#### Accessing the Plugin
61+
62+
1. Open your Backstage application.
63+
2. Navigate to the **Adoption Insights** section from the sidebar.
64+
3. Explore and analyze adoption metrics using the interactive dashboard.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import { format, subDays, subHours, subMonths, subWeeks } from 'date-fns';
17+
18+
export const generateHourlyData = () => {
19+
return Array.from({ length: 24 }, (_, i) => {
20+
const date = format(subHours(new Date(), i), 'yyyy-MM-dd HH:00');
21+
const new_users = Math.floor(Math.random() * (300 - 150 + 1)) + 150; // NOSONAR
22+
const returning_users = Math.floor(Math.random() * (300 - 150 + 1)) + 150; // NOSONAR
23+
const total_users = new_users + returning_users;
24+
return { date, total_users, new_users, returning_users };
25+
}).reverse();
26+
};
27+
28+
export const generateDaysData = () => {
29+
return Array.from({ length: 5 }, (_, i) => {
30+
const date = format(subDays(new Date(), i), 'yyyy-MM-dd');
31+
const new_users = Math.floor(Math.random() * (300 - 150 + 1)) + 150; // NOSONAR
32+
const returning_users = Math.floor(Math.random() * (300 - 150 + 1)) + 150; // NOSONAR
33+
const total_users = new_users + returning_users;
34+
return { date, total_users, new_users, returning_users };
35+
}).reverse();
36+
};
37+
38+
export const generateWeeksData = () => {
39+
return Array.from({ length: 4 }, (_, i) => {
40+
const date = format(subWeeks(new Date(), i), 'yyyy-MM-dd');
41+
const new_users = Math.floor(Math.random() * (3000 - 1500 + 1)) + 1500; // NOSONAR
42+
const returning_users =
43+
Math.floor(Math.random() * (3000 - 1500 + 1)) + 1500; // NOSONAR
44+
const total_users = new_users + returning_users;
45+
return { date, total_users, new_users, returning_users };
46+
}).reverse();
47+
};
48+
49+
export const generateMonthsData = () => {
50+
return Array.from({ length: 9 }, (_, i) => {
51+
const date = format(subMonths(new Date(), i), 'yyyy-MM-dd');
52+
const new_users = Math.floor(Math.random() * (9000 - 4500 + 1)) + 4500; // NOSONAR
53+
const returning_users =
54+
Math.floor(Math.random() * (9000 - 4500 + 1)) + 4500; // NOSONAR
55+
const total_users = new_users + returning_users;
56+
return { date, total_users, new_users, returning_users };
57+
}).reverse();
58+
};
59+
60+
export default {
61+
grouping: 'weekly',
62+
data: generateWeeksData(),
63+
};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
export const generateCatalogEntitiesData = (n: number): any[] => {
17+
const kinds = ['Component', 'API', 'System', 'Resource'];
18+
const names = ['devhub', 'hg-dev-hub-starter', 'netbox', 'sample-plugin'];
19+
const namespaces = ['default', 'internal', 'external'];
20+
21+
return Array.from({ length: n }, (_, i) => ({
22+
plugin_id: (i + 1).toString(),
23+
name: names[i % names.length],
24+
kind: kinds[i % kinds.length],
25+
last_used: new Date(
26+
Date.now() - Math.random() * 1000 * 60 * 60 * 24 * 10, // NOSONAR
27+
).toISOString(),
28+
count: Math.floor(Math.random() * 3000), // NOSONAR
29+
namespace: namespaces[i % namespaces.length],
30+
}));
31+
};
32+
33+
export default {
34+
data: generateCatalogEntitiesData(9),
35+
};
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import { format, subDays, subHours } from 'date-fns';
17+
import { PluginTrend } from '../../src/types';
18+
19+
export const generateHourlyData = () => {
20+
return Array.from({ length: 24 }, (_, i) => {
21+
const date = format(subHours(new Date(), i), 'yyyy-MM-dd HH:00');
22+
const count = Math.floor(Math.random() * (300 - 150 + 1)) + 150; // NOSONAR
23+
return { date, count };
24+
}).reverse();
25+
};
26+
27+
export const generateLast30DaysData = () => {
28+
return Array.from({ length: 30 }, (_, i) => {
29+
const date = format(subDays(new Date(), i), 'yyyy-MM-dd');
30+
const count = Math.floor(Math.random() * (100 - 50 + 1)) + 50; // NOSONAR
31+
return { date, count };
32+
}).reverse();
33+
};
34+
35+
export const mockPluginViewOne: PluginTrend = {
36+
plugin_id: 'catalog',
37+
visit_count: 77,
38+
trend_percentage: '18.67',
39+
trend: generateHourlyData(),
40+
};
41+
42+
export const mockPluginViewTwo: PluginTrend = {
43+
plugin_id: 'ansible',
44+
visit_count: 77,
45+
trend_percentage: '18.67',
46+
trend: generateHourlyData(),
47+
};
48+
49+
export const mockPluginViewThree: PluginTrend = {
50+
plugin_id: 'argoCD',
51+
visit_count: 7792,
52+
trend_percentage: '-18.67',
53+
trend: generateHourlyData(),
54+
};
55+
56+
export const mockPluginViewFour: PluginTrend = {
57+
plugin_id: 'argoCD Scaffolder',
58+
visit_count: 77,
59+
trend_percentage: '18.67',
60+
trend: generateHourlyData(),
61+
};
62+
63+
export const mockPluginViewFive: PluginTrend = {
64+
plugin_id: 'tech docs',
65+
visit_count: 77,
66+
trend_percentage: '18.67',
67+
trend: generateHourlyData(),
68+
};
69+
70+
export const mockPluginViewSix: PluginTrend = {
71+
plugin_id: 'catalog info',
72+
visit_count: 44,
73+
trend_percentage: '-66.67',
74+
trend: generateHourlyData(),
75+
};
76+
77+
export const mockPluginViews = {
78+
grouping: 'hourly',
79+
data: [
80+
mockPluginViewOne,
81+
mockPluginViewTwo,
82+
mockPluginViewThree,
83+
mockPluginViewFour,
84+
mockPluginViewFive,
85+
mockPluginViewSix,
86+
],
87+
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import { format, subDays, subMonths, subHours } from 'date-fns';
17+
18+
export const generateHourlyData = () => {
19+
return Array.from({ length: 24 }, (_, i) => {
20+
const date = format(subHours(new Date(), i), 'yyyy-MM-dd HH:00');
21+
const count = Math.floor(Math.random() * (300 - 150 + 1)) + 150; // NOSONAR
22+
return { date, count };
23+
}).reverse();
24+
};
25+
26+
export const generateDaysData = () => {
27+
return Array.from({ length: 12 }, (_, i) => {
28+
const date = format(subDays(new Date(), i), 'yyyy-MM-dd');
29+
const count = Math.floor(Math.random() * (300 - 150 + 1)) + 150; // NOSONAR
30+
return { date, count };
31+
}).reverse();
32+
};
33+
34+
export const generateMonthsData = () => {
35+
return Array.from({ length: 12 }, (_, i) => {
36+
const date = format(subMonths(new Date(), i), 'yyyy-MM-dd');
37+
const count = Math.floor(Math.random() * (3000 - 1500 + 1)) + 1500; // NOSONAR
38+
return { date, count };
39+
}).reverse();
40+
};
41+
42+
export default {
43+
grouping: 'hourly',
44+
data: generateHourlyData(),
45+
};

0 commit comments

Comments
 (0)