diff --git a/.changeset/angry-tables-invite.md b/.changeset/angry-tables-invite.md
new file mode 100644
index 0000000000..a187acda78
--- /dev/null
+++ b/.changeset/angry-tables-invite.md
@@ -0,0 +1,6 @@
+---
+'@backstage/plugin-bazaar': patch
+'@backstage/plugin-bazaar-backend': patch
+---
+
+Add Bazaar plugin to marketplace and some minor refactoring
diff --git a/microsite/data/plugins/bazaar.yaml b/microsite/data/plugins/bazaar.yaml
new file mode 100644
index 0000000000..6e27692d78
--- /dev/null
+++ b/microsite/data/plugins/bazaar.yaml
@@ -0,0 +1,8 @@
+title: Bazaar
+author: Axis Communications AB
+authorUrl: https://www.axis.com
+category: Discovery
+description: A marketplace where engineers can propose projects suitable for inner sourcing
+documentation: https://github.com/backstage/backstage/blob/master/plugins/bazaar/README.md
+iconUrl: img/bazaar.svg
+npmPackageName: '@backstage/plugin-bazaar'
diff --git a/microsite/static/img/bazaar.svg b/microsite/static/img/bazaar.svg
new file mode 100644
index 0000000000..66c53003fe
--- /dev/null
+++ b/microsite/static/img/bazaar.svg
@@ -0,0 +1 @@
+
diff --git a/plugins/bazaar-backend/src/service/router.ts b/plugins/bazaar-backend/src/service/router.ts
index 730d958fcd..3915c8c609 100644
--- a/plugins/bazaar-backend/src/service/router.ts
+++ b/plugins/bazaar-backend/src/service/router.ts
@@ -69,21 +69,19 @@ export async function createRouter(
}
});
- router.get('/projects/id/:id', async (request, response) => {
- const id = decodeURIComponent(request.params.id);
+ router.get('/projects/:idOrRef', async (request, response) => {
+ const idOrRef = decodeURIComponent(request.params.idOrRef);
+ let data;
- const data = await dbHandler.getMetadataById(parseInt(id, 10));
+ if (/^-?\d+$/.test(idOrRef)) {
+ data = await dbHandler.getMetadataById(parseInt(idOrRef, 10));
+ } else {
+ data = await dbHandler.getMetadataByRef(idOrRef);
+ }
response.json({ status: 'ok', data: data });
});
- router.get('/projects/ref/:ref', async (request, response) => {
- const ref = decodeURIComponent(request.params.ref);
-
- const data = await dbHandler.getMetadataByRef(ref);
- response.json({ status: 'ok', data: data });
- });
-
router.get('/projects', async (_, response) => {
const data = await dbHandler.getProjects();
diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json
index 03d6090f01..d081a220e7 100644
--- a/plugins/bazaar/package.json
+++ b/plugins/bazaar/package.json
@@ -21,6 +21,7 @@
"clean": "backstage-cli clean"
},
"dependencies": {
+ "@backstage/catalog-client": "^0.5.3",
"@backstage/catalog-model": "^0.9.7",
"@backstage/cli": "^0.10.5",
"@backstage/core-components": "^0.8.3",
diff --git a/plugins/bazaar/src/api.ts b/plugins/bazaar/src/api.ts
index 9f5010ec43..cae890ea4a 100644
--- a/plugins/bazaar/src/api.ts
+++ b/plugins/bazaar/src/api.ts
@@ -85,9 +85,12 @@ export class BazaarClient implements BazaarApi {
async getProjectById(id: number): Promise {
const baseUrl = await this.discoveryApi.getBaseUrl('bazaar');
- const response = await fetch(`${baseUrl}/projects/id/${id}`, {
- method: 'GET',
- });
+ const response = await fetch(
+ `${baseUrl}/projects/${encodeURIComponent(id)}`,
+ {
+ method: 'GET',
+ },
+ );
return response.ok ? response : null;
}
@@ -96,7 +99,7 @@ export class BazaarClient implements BazaarApi {
const baseUrl = await this.discoveryApi.getBaseUrl('bazaar');
const response = await fetch(
- `${baseUrl}/projects/ref/${encodeURIComponent(entityRef)}`,
+ `${baseUrl}/projects/${encodeURIComponent(entityRef)}`,
{
method: 'GET',
},
@@ -108,32 +111,45 @@ export class BazaarClient implements BazaarApi {
async getMembers(id: number): Promise {
const baseUrl = await this.discoveryApi.getBaseUrl('bazaar');
- return await fetch(`${baseUrl}/projects/${id}/members`, {
- method: 'GET',
- }).then(resp => resp.json());
+ return await fetch(
+ `${baseUrl}/projects/${encodeURIComponent(id)}/members`,
+ {
+ method: 'GET',
+ },
+ ).then(resp => resp.json());
}
async addMember(id: number, userId: string): Promise {
const baseUrl = await this.discoveryApi.getBaseUrl('bazaar');
- await fetch(`${baseUrl}/projects/${id}/member/${userId}`, {
- method: 'PUT',
- headers: {
- Accept: 'application/json',
- 'Content-Type': 'application/json',
+ await fetch(
+ `${baseUrl}/projects/${encodeURIComponent(
+ id,
+ )}/member/${encodeURIComponent(userId)}`,
+ {
+ method: 'PUT',
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ picture: (await this.identityApi.getProfileInfo()).picture,
+ }),
},
- body: JSON.stringify({
- picture: this.identityApi.getProfile()?.picture,
- }),
- });
+ );
}
async deleteMember(id: number, userId: string): Promise {
const baseUrl = await this.discoveryApi.getBaseUrl('bazaar');
- await fetch(`${baseUrl}/projects/${id}/member/${userId}`, {
- method: 'DELETE',
- });
+ await fetch(
+ `${baseUrl}/projects/${encodeURIComponent(
+ id,
+ )}/member/${encodeURIComponent(userId)}`,
+ {
+ method: 'DELETE',
+ },
+ );
}
async getProjects(): Promise {
@@ -147,7 +163,7 @@ export class BazaarClient implements BazaarApi {
async deleteProject(id: number): Promise {
const baseUrl = await this.discoveryApi.getBaseUrl('bazaar');
- await fetch(`${baseUrl}/projects/${id}`, {
+ await fetch(`${baseUrl}/projects/${encodeURIComponent(id)}`, {
method: 'DELETE',
});
}
diff --git a/plugins/bazaar/src/components/About/About.tsx b/plugins/bazaar/src/components/About/About.tsx
index d9c529a209..68525ab642 100644
--- a/plugins/bazaar/src/components/About/About.tsx
+++ b/plugins/bazaar/src/components/About/About.tsx
@@ -15,8 +15,8 @@
*/
import React from 'react';
-import { Grid, Typography, Link, makeStyles } from '@material-ui/core';
-import { InfoCard } from '@backstage/core-components';
+import { Grid, Typography, makeStyles } from '@material-ui/core';
+import { InfoCard, Link } from '@backstage/core-components';
const useStyles = makeStyles({
subheader: {
@@ -39,7 +39,7 @@ export const About = () => {
internal projects suitable for{' '}
Inner Sourcing
diff --git a/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx b/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx
index 4333ff11fd..749fcf3cff 100644
--- a/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx
+++ b/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx
@@ -57,7 +57,10 @@ export const AddProjectDialog = ({
setSelectedEntity(entity);
};
- const handleSubmit: any = async (
+ const handleSubmit: (
+ getValues: UseFormGetValues,
+ reset: UseFormReset,
+ ) => Promise = async (
getValues: UseFormGetValues,
reset: UseFormReset,
) => {
diff --git a/plugins/bazaar/src/components/CardContentFields/CardContentFields.tsx b/plugins/bazaar/src/components/CardContentFields/CardContentFields.tsx
index 5145dc54b8..6c6ce1bb3e 100644
--- a/plugins/bazaar/src/components/CardContentFields/CardContentFields.tsx
+++ b/plugins/bazaar/src/components/CardContentFields/CardContentFields.tsx
@@ -21,10 +21,9 @@ import {
Card,
CardContent,
Typography,
- Link,
GridSize,
} from '@material-ui/core';
-import { Avatar } from '@backstage/core-components';
+import { Avatar, Link } from '@backstage/core-components';
import { AboutField } from '@backstage/plugin-catalog';
import { StatusTag } from '../StatusTag';
import { Member, BazaarProject } from '../../types';
@@ -32,6 +31,7 @@ import { Member, BazaarProject } from '../../types';
const useStyles = makeStyles({
break: {
wordBreak: 'break-word',
+ textAlign: 'justify',
},
});
@@ -111,7 +111,7 @@ export const CardContentFields = ({
/>
{member?.userId}
diff --git a/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx b/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx
index 0b2a8ef391..5073bd0cd9 100644
--- a/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx
+++ b/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx
@@ -36,6 +36,11 @@ const useStyles = makeStyles({
marginLeft: '0',
marginRight: 'auto',
},
+ wordBreak: {
+ wordBreak: 'break-all',
+ whiteSpace: 'normal',
+ margin: '-0.25rem 0',
+ },
});
export const EditProjectDialog = ({
@@ -76,9 +81,9 @@ export const EditProjectDialog = ({
});
}, [bazaarProject]);
- const handleEditSubmit: any = async (
+ const handleEditSubmit: (
getValues: UseFormGetValues,
- ) => {
+ ) => Promise = async (getValues: UseFormGetValues) => {
const formValues = getValues();
const updateResponse = await bazaarApi.updateProject({
@@ -101,7 +106,9 @@ export const EditProjectDialog = ({
handleClose={handleDeleteClose}
message={[
'Are you sure you want to delete ',
- {bazaarProject.name},
+
+ {bazaarProject.name}
+ ,
' from the Bazaar?',
]}
type="delete"
diff --git a/plugins/bazaar/src/components/EntityBazaarInfoContent/EntityBazaarInfoContent.tsx b/plugins/bazaar/src/components/EntityBazaarInfoContent/EntityBazaarInfoContent.tsx
index 470dc824b6..0c9da1ed24 100644
--- a/plugins/bazaar/src/components/EntityBazaarInfoContent/EntityBazaarInfoContent.tsx
+++ b/plugins/bazaar/src/components/EntityBazaarInfoContent/EntityBazaarInfoContent.tsx
@@ -15,7 +15,7 @@
*/
import React, { useState, useEffect } from 'react';
-import { CardHeader, Divider, IconButton } from '@material-ui/core';
+import { CardHeader, Divider, IconButton, makeStyles } from '@material-ui/core';
import {
HeaderIconLinkRow,
IconLinkVerticalProps,
@@ -37,6 +37,14 @@ import { ConfirmationDialog } from '../ConfirmationDialog';
import { CardContentFields } from '../CardContentFields';
import { fetchProjectMembers } from '../../util/fetchMethods';
+const useStyles = makeStyles({
+ wordBreak: {
+ wordBreak: 'break-all',
+ whiteSpace: 'normal',
+ margin: '-0.25rem 0',
+ },
+});
+
type Props = {
bazaarProject: BazaarProject | null | undefined;
fetchBazaarProject: () => Promise;
@@ -46,6 +54,7 @@ export const EntityBazaarInfoContent = ({
bazaarProject,
fetchBazaarProject,
}: Props) => {
+ const classes = useStyles();
const bazaarApi = useApi(bazaarApiRef);
const identity = useApi(identityApiRef);
const [openEdit, setOpenEdit] = useState(false);
@@ -160,9 +169,11 @@ export const EntityBazaarInfoContent = ({
handleClose={handleUnlinkClose}
message={[
'Are you sure you want to unlink ',
- {parseEntityRef(bazaarProject.entityRef!).name},
+
+ {parseEntityRef(bazaarProject.entityRef!).name}
+ ,
' from ',
- {bazaarProject.name},
+ {bazaarProject.name},
' ?',
]}
type="unlink"
@@ -171,7 +182,7 @@ export const EntityBazaarInfoContent = ({
)}
{bazaarProject?.name!}
}
action={
void;
@@ -70,6 +84,7 @@ export const HomePageBazaarInfoCard = ({
handleClose,
initEntity,
}: Props) => {
+ const classes = useStyles();
const catalogLink = useRouteRef(catalogRouteRef);
const bazaarApi = useApi(bazaarApiRef);
const identity = useApi(identityApiRef);
@@ -222,9 +237,11 @@ export const HomePageBazaarInfoCard = ({
handleClose={() => setOpenUnlink(false)}
message={[
'Are you sure you want to unlink ',
- {parseEntityRef(bazaarProject.value?.entityRef!).name},
+
+ {parseEntityRef(bazaarProject.value?.entityRef!).name}
+ ,
' from ',
- {bazaarProject.value?.name},
+ {bazaarProject.value?.name},
' ?',
]}
type="unlink"
@@ -242,7 +259,11 @@ export const HomePageBazaarInfoCard = ({
/>
+ {bazaarProject.value?.name || initProject.name}
+
+ }
action={
setOpenEdit(true)}>
diff --git a/plugins/bazaar/src/components/InputField/InputField.tsx b/plugins/bazaar/src/components/InputField/InputField.tsx
index 76eb28da6c..cae22a1313 100644
--- a/plugins/bazaar/src/components/InputField/InputField.tsx
+++ b/plugins/bazaar/src/components/InputField/InputField.tsx
@@ -15,13 +15,18 @@
*/
import React from 'react';
-import { Controller, Control, FieldError } from 'react-hook-form';
+import {
+ Controller,
+ Control,
+ FieldError,
+ ValidationRule,
+} from 'react-hook-form';
import { TextField } from '@material-ui/core';
import { FormValues } from '../../types';
type Rules = {
required: boolean;
- pattern?: any;
+ pattern?: ValidationRule | undefined;
};
type Props = {
diff --git a/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx b/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx
index a558d347a5..3aeba305a9 100644
--- a/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx
+++ b/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx
@@ -41,12 +41,19 @@ const useStyles = makeStyles({
WebkitLineClamp: 7,
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
- backgroundColor: '',
+ textAlign: 'justify',
},
memberCount: {
float: 'right',
},
- content: { height: '13rem', marginBottom: '-0.5rem' },
+ content: {
+ height: '13rem',
+ },
+ header: {
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ },
});
type Props = {
@@ -82,7 +89,12 @@ export const ProjectCard = ({
setOpenCard(true)}>
+ {name}
+
+ }
subtitle={`updated ${DateTime.fromISO(
new Date(updatedAt!).toISOString(),
).toRelative({
diff --git a/plugins/bazaar/src/components/ProjectDialog/ProjectDialog.tsx b/plugins/bazaar/src/components/ProjectDialog/ProjectDialog.tsx
index 08c43d1c2e..2f8b9e9cb9 100644
--- a/plugins/bazaar/src/components/ProjectDialog/ProjectDialog.tsx
+++ b/plugins/bazaar/src/components/ProjectDialog/ProjectDialog.tsx
@@ -16,12 +16,7 @@
import React from 'react';
import { Button, Dialog } from '@material-ui/core';
-import {
- useForm,
- SubmitHandler,
- UseFormReset,
- UseFormGetValues,
-} from 'react-hook-form';
+import { useForm, UseFormReset, UseFormGetValues } from 'react-hook-form';
import { InputField } from '../InputField/InputField';
import { InputSelector } from '../InputSelector/InputSelector';
import { FormValues } from '../../types';
@@ -36,7 +31,7 @@ type Props = {
handleSave: (
getValues: UseFormGetValues,
reset: UseFormReset,
- ) => SubmitHandler;
+ ) => Promise;
isAddForm: boolean;
title: string;
defaultValues: FormValues;
diff --git a/plugins/bazaar/src/components/SortMethodSelector/SortMethodSelector.tsx b/plugins/bazaar/src/components/SortMethodSelector/SortMethodSelector.tsx
index 93d5649a14..d29211dc44 100644
--- a/plugins/bazaar/src/components/SortMethodSelector/SortMethodSelector.tsx
+++ b/plugins/bazaar/src/components/SortMethodSelector/SortMethodSelector.tsx
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import React from 'react';
+import React, { ChangeEvent, ReactNode } from 'react';
import { FormControl, MenuItem, Select, makeStyles } from '@material-ui/core';
const useStyles = makeStyles({
@@ -27,7 +27,12 @@ const useStyles = makeStyles({
type Props = {
sortMethodNbr: number;
- handleSortMethodChange: any;
+ handleSortMethodChange:
+ | ((
+ event: ChangeEvent<{ name?: string | undefined; value: unknown }>,
+ child: ReactNode,
+ ) => void)
+ | undefined;
};
export const SortMethodSelector = ({
diff --git a/plugins/bazaar/src/components/SortView/SortView.tsx b/plugins/bazaar/src/components/SortView/SortView.tsx
index a7f99625e5..fa97ac69ee 100644
--- a/plugins/bazaar/src/components/SortView/SortView.tsx
+++ b/plugins/bazaar/src/components/SortView/SortView.tsx
@@ -33,7 +33,7 @@ import { fetchCatalogItems } from '../../util/fetchMethods';
import { parseBazaarProject } from '../../util/parseMethods';
const useStyles = makeStyles({
- button: { width: '12rem' },
+ button: { minWidth: '11rem' },
container: {
marginTop: '2rem',
},
@@ -141,7 +141,9 @@ export const SortView = () => {
}
}, [bazaarProjects, catalogEntities]);
- const handleSortMethodChange = (event: ChangeEvent) => {
+ const handleSortMethodChange = (
+ event: ChangeEvent<{ name?: string | undefined; value: unknown }>,
+ ) => {
setSortMethodNbr(
typeof event.target.value === 'number' ? event.target.value : 0,
);
diff --git a/plugins/bazaar/src/util/fetchMethods.ts b/plugins/bazaar/src/util/fetchMethods.ts
index 015abae210..f4bdb0ae38 100644
--- a/plugins/bazaar/src/util/fetchMethods.ts
+++ b/plugins/bazaar/src/util/fetchMethods.ts
@@ -19,6 +19,7 @@ import { parseMember } from './parseMethods';
import { BazaarProject, Member } from '../types';
import { BazaarApi } from '../api';
import { Entity } from '@backstage/catalog-model';
+import { CatalogApi } from '@backstage/plugin-catalog-react';
export const fetchProjectMembers = async (
bazaarApi: BazaarApi,
@@ -37,7 +38,9 @@ export const fetchProjectMembers = async (
return [];
};
-export const fetchCatalogItems = async (catalogApi: any): Promise => {
+export const fetchCatalogItems = async (
+ catalogApi: CatalogApi,
+): Promise => {
const entities = await catalogApi.getEntities({
filter: {
kind: ['Component', 'Resource'],