diff --git a/plugins/argocd/.eslintrc.js b/plugins/argocd/.eslintrc.js
new file mode 100644
index 0000000000..e2a53a6ad2
--- /dev/null
+++ b/plugins/argocd/.eslintrc.js
@@ -0,0 +1 @@
+module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
diff --git a/plugins/argocd/OWNERS b/plugins/argocd/OWNERS
new file mode 100644
index 0000000000..9f4ccefff9
--- /dev/null
+++ b/plugins/argocd/OWNERS
@@ -0,0 +1,6 @@
+approvers:
+ - karthikjeeyar
+ - rohitkrai03
+reviewers:
+ - karthikjeeyar
+ - rohitkrai03
\ No newline at end of file
diff --git a/plugins/argocd/README.md b/plugins/argocd/README.md
new file mode 100644
index 0000000000..f62e1f4b92
--- /dev/null
+++ b/plugins/argocd/README.md
@@ -0,0 +1,54 @@
+# argocd
+
+Welcome to the argocd plugin!
+
+_This plugin was created through the Backstage CLI_
+
+## Getting started
+
+Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/argocd](http://localhost:3000/argocd).
+
+You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
+This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
+It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.
+
+## Loading as Dynamic Plugin
+
+This plugin can be loaded in backstage showcase application as a dynamic plugin.
+
+Follow the below steps -
+
+- Export dynamic plugin assets. This will build and create the static assets for the plugin and put it inside dist-scalprum folder.
+
+```sh
+yarn export-dynamic
+```
+
+- Package and copy dist-scalprum folder assets to dynamic-plugins-root folder in showcase application.
+
+```sh
+pkg=../plugins/argocd
+archive=$(npm pack $pkg)
+tar -xzf "$archive" && rm "$archive"
+mv package $(echo $archive | sed -e 's:\.tgz$::')
+```
+
+- Add the extension point inside the app-config.yaml or app-config.local.yaml file.
+
+```yaml
+dynamicPlugins:
+ frontend:
+ janus-idp.backstage-plugin-argocd:
+ mountPoints:
+ - mountPoint: entity.page.cd/cards
+ importName: ArgocdPage
+ config:
+ layout:
+ gridColumn: '1 / -1'
+ if:
+ anyOf:
+ - hasAnnotation: backstage.io/kubernetes-id
+ - hasAnnotation: backstage.io/kubernetes-namespace
+```
+
+For more detailed explanation on dynamic plugins follow this [doc](https://github.com/janus-idp/backstage-showcase/blob/main/showcase-docs/dynamic-plugins.md).
diff --git a/plugins/argocd/app-config.janus-idp.yaml b/plugins/argocd/app-config.janus-idp.yaml
new file mode 100644
index 0000000000..a058ef78a3
--- /dev/null
+++ b/plugins/argocd/app-config.janus-idp.yaml
@@ -0,0 +1,13 @@
+dynamicPlugins:
+ frontend:
+ janus-idp.backstage-plugin-argocd:
+ mountPoints:
+ - mountPoint: entity.page.cd/cards
+ importName: ArgocdPage
+ config:
+ layout:
+ gridColumn: '1 / -1'
+ if:
+ anyOf:
+ - hasAnnotation: backstage.io/kubernetes-id
+ - hasAnnotation: backstage.io/kubernetes-namespace
diff --git a/plugins/argocd/config.d.ts b/plugins/argocd/config.d.ts
new file mode 100644
index 0000000000..5728ccd9e1
--- /dev/null
+++ b/plugins/argocd/config.d.ts
@@ -0,0 +1 @@
+export interface Config {}
diff --git a/plugins/argocd/dev/index.tsx b/plugins/argocd/dev/index.tsx
new file mode 100644
index 0000000000..e7d546aa8f
--- /dev/null
+++ b/plugins/argocd/dev/index.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+
+import { createDevApp } from '@backstage/dev-utils';
+
+import { ArgocdPage, argocdPlugin } from '../src/plugin';
+
+createDevApp()
+ .registerPlugin(argocdPlugin)
+ .addPage({
+ element: ,
+ title: 'Root Page',
+ path: '/argocd',
+ })
+ .render();
diff --git a/plugins/argocd/package.json b/plugins/argocd/package.json
new file mode 100644
index 0000000000..fafa818433
--- /dev/null
+++ b/plugins/argocd/package.json
@@ -0,0 +1,76 @@
+{
+ "name": "@janus-idp/backstage-plugin-argocd",
+ "version": "0.1.0",
+ "main": "src/index.ts",
+ "types": "src/index.ts",
+ "license": "Apache-2.0",
+ "private": true,
+ "publishConfig": {
+ "access": "public",
+ "main": "dist/index.esm.js",
+ "types": "dist/index.d.ts"
+ },
+ "backstage": {
+ "role": "frontend-plugin"
+ },
+ "sideEffects": false,
+ "scripts": {
+ "build": "backstage-cli package build",
+ "clean": "backstage-cli package clean",
+ "export-dynamic": "janus-cli package export-dynamic-plugin",
+ "lint": "backstage-cli package lint",
+ "postpack": "backstage-cli package postpack",
+ "postversion": "yarn run export-dynamic",
+ "prepack": "backstage-cli package prepack",
+ "start": "backstage-cli package start",
+ "test": "backstage-cli package test --passWithNoTests --coverage",
+ "tsc": "tsc"
+ },
+ "dependencies": {
+ "@backstage/core-components": "^0.14.0",
+ "@backstage/core-plugin-api": "^1.9.0",
+ "@backstage/theme": "^0.5.1",
+ "@material-ui/core": "^4.9.13",
+ "@material-ui/icons": "^4.9.1",
+ "@material-ui/lab": "^4.0.0-alpha.61",
+ "react-use": "^17.2.4"
+ },
+ "peerDependencies": {
+ "react": "16.13.1 || ^17.0.0 || ^18.0.0"
+ },
+ "devDependencies": {
+ "@backstage/cli": "0.25.2",
+ "@backstage/core-app-api": "1.12.0",
+ "@backstage/dev-utils": "1.0.27",
+ "@backstage/test-utils": "1.5.0",
+ "@janus-idp/cli": "1.7.5",
+ "@testing-library/jest-dom": "6.0.0",
+ "@testing-library/react": "14.0.0",
+ "@testing-library/user-event": "14.0.0",
+ "msw": "1.0.0"
+ },
+ "files": [
+ "dist",
+ "config.d.ts",
+ "dist-scalprum",
+ "app-config.janus-idp.yaml"
+ ],
+ "scalprum": {
+ "name": "janus-idp.backstage-plugin-argocd",
+ "exposedModules": {
+ "PluginRoot": "./src/index.ts"
+ }
+ },
+ "configSchema": "config.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/janus-idp/backstage-plugins",
+ "directory": "plugins/argocd"
+ },
+ "keywords": [
+ "backstage",
+ "plugin"
+ ],
+ "homepage": "https://janus-idp.io/",
+ "bugs": "https://github.com/janus-idp/backstage-plugins/issues"
+}
diff --git a/plugins/argocd/src/components/ExampleComponent/ExampleComponent.test.tsx b/plugins/argocd/src/components/ExampleComponent/ExampleComponent.test.tsx
new file mode 100644
index 0000000000..3e8049baf3
--- /dev/null
+++ b/plugins/argocd/src/components/ExampleComponent/ExampleComponent.test.tsx
@@ -0,0 +1,30 @@
+import React from 'react';
+
+import {
+ renderInTestApp,
+ setupRequestMockHandlers,
+} from '@backstage/test-utils';
+
+import { screen } from '@testing-library/react';
+import { rest } from 'msw';
+import { setupServer } from 'msw/node';
+
+import { ExampleComponent } from './ExampleComponent';
+
+describe('ExampleComponent', () => {
+ const server = setupServer();
+ // Enable sane handlers for network requests
+ setupRequestMockHandlers(server);
+
+ // setup mock response
+ beforeEach(() => {
+ server.use(
+ rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))),
+ );
+ });
+
+ it('should render', async () => {
+ await renderInTestApp();
+ expect(screen.getByText('Welcome to ArgoCD!')).toBeInTheDocument();
+ });
+});
diff --git a/plugins/argocd/src/components/ExampleComponent/ExampleComponent.tsx b/plugins/argocd/src/components/ExampleComponent/ExampleComponent.tsx
new file mode 100644
index 0000000000..a14435beec
--- /dev/null
+++ b/plugins/argocd/src/components/ExampleComponent/ExampleComponent.tsx
@@ -0,0 +1,46 @@
+import React from 'react';
+
+import {
+ Content,
+ ContentHeader,
+ Header,
+ HeaderLabel,
+ InfoCard,
+ Page,
+ SupportButton,
+} from '@backstage/core-components';
+
+import { Grid, Typography } from '@material-ui/core';
+
+import { ExampleFetchComponent } from '../ExampleFetchComponent';
+
+export const ExampleComponent = () => (
+
+
+
+
+
+ This is the official ArgoCD plugin from Red Hat.
+
+
+
+
+
+
+ All content should be wrapped in a card like this.
+
+
+
+
+
+
+
+
+
+);
diff --git a/plugins/argocd/src/components/ExampleComponent/index.ts b/plugins/argocd/src/components/ExampleComponent/index.ts
new file mode 100644
index 0000000000..8b8437521b
--- /dev/null
+++ b/plugins/argocd/src/components/ExampleComponent/index.ts
@@ -0,0 +1 @@
+export { ExampleComponent } from './ExampleComponent';
diff --git a/plugins/argocd/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx b/plugins/argocd/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx
new file mode 100644
index 0000000000..6abcc6c5d0
--- /dev/null
+++ b/plugins/argocd/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx
@@ -0,0 +1,21 @@
+import React from 'react';
+
+import { render, screen } from '@testing-library/react';
+
+import { ExampleFetchComponent } from './ExampleFetchComponent';
+
+describe('ExampleFetchComponent', () => {
+ it('renders the user table', async () => {
+ render();
+
+ // Wait for the table to render
+ const table = await screen.findByRole('table');
+ const nationality = screen.getAllByText('GB');
+ // Assert that the table contains the expected user data
+ expect(table).toBeInTheDocument();
+ expect(screen.getByAltText('Carolyn')).toBeInTheDocument();
+ expect(screen.getByText('Carolyn Moore')).toBeInTheDocument();
+ expect(screen.getByText('carolyn.moore@example.com')).toBeInTheDocument();
+ expect(nationality[0]).toBeInTheDocument();
+ });
+});
diff --git a/plugins/argocd/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx b/plugins/argocd/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx
new file mode 100644
index 0000000000..ee2a35a5e8
--- /dev/null
+++ b/plugins/argocd/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx
@@ -0,0 +1,310 @@
+import React from 'react';
+import useAsync from 'react-use/lib/useAsync';
+
+import {
+ Progress,
+ ResponseErrorPanel,
+ Table,
+ TableColumn,
+} from '@backstage/core-components';
+
+import { makeStyles } from '@material-ui/core/styles';
+
+export const exampleUsers = {
+ results: [
+ {
+ gender: 'female',
+ name: {
+ title: 'Miss',
+ first: 'Carolyn',
+ last: 'Moore',
+ },
+ email: 'carolyn.moore@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Carolyn',
+ nat: 'GB',
+ },
+ {
+ gender: 'female',
+ name: {
+ title: 'Ms',
+ first: 'Esma',
+ last: 'Berberoğlu',
+ },
+ email: 'esma.berberoglu@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Esma',
+ nat: 'TR',
+ },
+ {
+ gender: 'female',
+ name: {
+ title: 'Ms',
+ first: 'Isabella',
+ last: 'Rhodes',
+ },
+ email: 'isabella.rhodes@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Isabella',
+ nat: 'GB',
+ },
+ {
+ gender: 'male',
+ name: {
+ title: 'Mr',
+ first: 'Derrick',
+ last: 'Carter',
+ },
+ email: 'derrick.carter@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Derrick',
+ nat: 'IE',
+ },
+ {
+ gender: 'female',
+ name: {
+ title: 'Miss',
+ first: 'Mattie',
+ last: 'Lambert',
+ },
+ email: 'mattie.lambert@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mattie',
+ nat: 'AU',
+ },
+ {
+ gender: 'male',
+ name: {
+ title: 'Mr',
+ first: 'Mijat',
+ last: 'Rakić',
+ },
+ email: 'mijat.rakic@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mijat',
+ nat: 'RS',
+ },
+ {
+ gender: 'male',
+ name: {
+ title: 'Mr',
+ first: 'Javier',
+ last: 'Reid',
+ },
+ email: 'javier.reid@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Javier',
+ nat: 'US',
+ },
+ {
+ gender: 'female',
+ name: {
+ title: 'Ms',
+ first: 'Isabella',
+ last: 'Li',
+ },
+ email: 'isabella.li@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Isabella',
+ nat: 'CA',
+ },
+ {
+ gender: 'female',
+ name: {
+ title: 'Mrs',
+ first: 'Stephanie',
+ last: 'Garrett',
+ },
+ email: 'stephanie.garrett@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Stephanie',
+ nat: 'AU',
+ },
+ {
+ gender: 'female',
+ name: {
+ title: 'Ms',
+ first: 'Antonia',
+ last: 'Núñez',
+ },
+ email: 'antonia.nunez@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Antonia',
+ nat: 'ES',
+ },
+ {
+ gender: 'male',
+ name: {
+ title: 'Mr',
+ first: 'Donald',
+ last: 'Young',
+ },
+ email: 'donald.young@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Donald',
+ nat: 'US',
+ },
+ {
+ gender: 'male',
+ name: {
+ title: 'Mr',
+ first: 'Iegor',
+ last: 'Holodovskiy',
+ },
+ email: 'iegor.holodovskiy@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Iegor',
+ nat: 'UA',
+ },
+ {
+ gender: 'female',
+ name: {
+ title: 'Madame',
+ first: 'Jessica',
+ last: 'David',
+ },
+ email: 'jessica.david@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Jessica',
+ nat: 'CH',
+ },
+ {
+ gender: 'female',
+ name: {
+ title: 'Ms',
+ first: 'Eve',
+ last: 'Martinez',
+ },
+ email: 'eve.martinez@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Eve',
+ nat: 'FR',
+ },
+ {
+ gender: 'male',
+ name: {
+ title: 'Mr',
+ first: 'Caleb',
+ last: 'Silva',
+ },
+ email: 'caleb.silva@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Caleb',
+ nat: 'US',
+ },
+ {
+ gender: 'female',
+ name: {
+ title: 'Miss',
+ first: 'Marcia',
+ last: 'Jenkins',
+ },
+ email: 'marcia.jenkins@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Marcia',
+ nat: 'US',
+ },
+ {
+ gender: 'female',
+ name: {
+ title: 'Mrs',
+ first: 'Mackenzie',
+ last: 'Jones',
+ },
+ email: 'mackenzie.jones@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mackenzie',
+ nat: 'NZ',
+ },
+ {
+ gender: 'male',
+ name: {
+ title: 'Mr',
+ first: 'Jeremiah',
+ last: 'Gutierrez',
+ },
+ email: 'jeremiah.gutierrez@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Jeremiah',
+ nat: 'AU',
+ },
+ {
+ gender: 'female',
+ name: {
+ title: 'Ms',
+ first: 'Luciara',
+ last: 'Souza',
+ },
+ email: 'luciara.souza@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Luciara',
+ nat: 'BR',
+ },
+ {
+ gender: 'male',
+ name: {
+ title: 'Mr',
+ first: 'Valgi',
+ last: 'da Cunha',
+ },
+ email: 'valgi.dacunha@example.com',
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Valgi',
+ nat: 'BR',
+ },
+ ],
+};
+
+const useStyles = makeStyles({
+ avatar: {
+ height: 32,
+ width: 32,
+ borderRadius: '50%',
+ },
+});
+
+type User = {
+ gender: string; // "male"
+ name: {
+ title: string; // "Mr",
+ first: string; // "Duane",
+ last: string; // "Reed"
+ };
+ email: string; // "duane.reed@example.com"
+ picture: string; // "https://api.dicebear.com/6.x/open-peeps/svg?seed=Duane"
+ nat: string; // "AU"
+};
+
+type DenseTableProps = {
+ users: User[];
+};
+
+export const DenseTable = ({ users }: DenseTableProps) => {
+ const classes = useStyles();
+
+ const columns: TableColumn[] = [
+ { title: 'Avatar', field: 'avatar' },
+ { title: 'Name', field: 'name' },
+ { title: 'Email', field: 'email' },
+ { title: 'Nationality', field: 'nationality' },
+ ];
+
+ const data = users.map(user => {
+ return {
+ avatar: (
+
+ ),
+ name: `${user.name.first} ${user.name.last}`,
+ email: user.email,
+ nationality: user.nat,
+ };
+ });
+
+ return (
+
+ );
+};
+
+export const ExampleFetchComponent = () => {
+ const { value, loading, error } = useAsync(async (): Promise => {
+ // Would use fetch in a real world example
+ return exampleUsers.results;
+ }, []);
+
+ if (loading) {
+ return ;
+ } else if (error) {
+ return ;
+ }
+
+ return ;
+};
diff --git a/plugins/argocd/src/components/ExampleFetchComponent/index.ts b/plugins/argocd/src/components/ExampleFetchComponent/index.ts
new file mode 100644
index 0000000000..41a43e84f1
--- /dev/null
+++ b/plugins/argocd/src/components/ExampleFetchComponent/index.ts
@@ -0,0 +1 @@
+export { ExampleFetchComponent } from './ExampleFetchComponent';
diff --git a/plugins/argocd/src/index.ts b/plugins/argocd/src/index.ts
new file mode 100644
index 0000000000..75d4ad8eca
--- /dev/null
+++ b/plugins/argocd/src/index.ts
@@ -0,0 +1 @@
+export { argocdPlugin, ArgocdPage } from './plugin';
diff --git a/plugins/argocd/src/plugin.test.ts b/plugins/argocd/src/plugin.test.ts
new file mode 100644
index 0000000000..d9225b45bc
--- /dev/null
+++ b/plugins/argocd/src/plugin.test.ts
@@ -0,0 +1,7 @@
+import { argocdPlugin } from './plugin';
+
+describe('argocd', () => {
+ it('should export plugin', () => {
+ expect(argocdPlugin).toBeDefined();
+ });
+});
diff --git a/plugins/argocd/src/plugin.ts b/plugins/argocd/src/plugin.ts
new file mode 100644
index 0000000000..f96d1b03e3
--- /dev/null
+++ b/plugins/argocd/src/plugin.ts
@@ -0,0 +1,22 @@
+import {
+ createPlugin,
+ createRoutableExtension,
+} from '@backstage/core-plugin-api';
+
+import { rootRouteRef } from './routes';
+
+export const argocdPlugin = createPlugin({
+ id: 'rh-argocd',
+ routes: {
+ root: rootRouteRef,
+ },
+});
+
+export const ArgocdPage = argocdPlugin.provide(
+ createRoutableExtension({
+ name: 'ArgocdPage',
+ component: () =>
+ import('./components/ExampleComponent').then(m => m.ExampleComponent),
+ mountPoint: rootRouteRef,
+ }),
+);
diff --git a/plugins/argocd/src/routes.ts b/plugins/argocd/src/routes.ts
new file mode 100644
index 0000000000..43c22cf1d6
--- /dev/null
+++ b/plugins/argocd/src/routes.ts
@@ -0,0 +1,5 @@
+import { createRouteRef } from '@backstage/core-plugin-api';
+
+export const rootRouteRef = createRouteRef({
+ id: 'rh-argocd',
+});
diff --git a/plugins/argocd/src/setupTests.ts b/plugins/argocd/src/setupTests.ts
new file mode 100644
index 0000000000..7b0828bfa8
--- /dev/null
+++ b/plugins/argocd/src/setupTests.ts
@@ -0,0 +1 @@
+import '@testing-library/jest-dom';
diff --git a/plugins/argocd/tsconfig.json b/plugins/argocd/tsconfig.json
new file mode 100644
index 0000000000..a82a3f1e0d
--- /dev/null
+++ b/plugins/argocd/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "@backstage/cli/config/tsconfig.json",
+ "include": ["src", "dev"],
+ "exclude": ["node_modules"],
+ "compilerOptions": {
+ "outDir": "../../dist-types/plugins/argocd",
+ "rootDir": "."
+ }
+}
diff --git a/plugins/argocd/turbo.json b/plugins/argocd/turbo.json
new file mode 100644
index 0000000000..70074dc851
--- /dev/null
+++ b/plugins/argocd/turbo.json
@@ -0,0 +1,9 @@
+{
+ "extends": ["//"],
+ "pipeline": {
+ "tsc": {
+ "outputs": ["../../dist-types/plugins/argocd/**"],
+ "dependsOn": ["^tsc"]
+ }
+ }
+}