fixed some review comments
Signed-off-by: Lykke Axlin <lykkeaxlin@hotmail.com> Co-authored-by: klaraab <klarabroman@live.se>
@@ -4,6 +4,7 @@
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.cjs.js",
|
||||
|
||||
@@ -50,11 +50,6 @@ export async function createRouter(
|
||||
const router = Router();
|
||||
router.use(express.json());
|
||||
|
||||
router.get('/health', (_, response) => {
|
||||
logger.info('PONG!');
|
||||
response.send({ status: 'ok' });
|
||||
});
|
||||
|
||||
router.get('/members', async (request, response) => {
|
||||
const entityRef = request.headers.entity_ref;
|
||||
|
||||
@@ -64,9 +59,9 @@ export async function createRouter(
|
||||
.where({ entity_ref: entityRef });
|
||||
|
||||
if (data?.length) {
|
||||
response.send({ status: 'ok', data: data });
|
||||
response.json({ status: 'ok', data: data });
|
||||
} else {
|
||||
response.send({ status: 'ok', data: [] });
|
||||
response.json({ status: 'ok', data: [] });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -80,22 +75,20 @@ export async function createRouter(
|
||||
user_id: userId,
|
||||
})
|
||||
.into('public.members');
|
||||
response.send({ status: 'ok' });
|
||||
response.json({ status: 'ok' });
|
||||
});
|
||||
|
||||
router.delete('/member', async (request, response) => {
|
||||
const userId = request.headers.user_id;
|
||||
const entityRef = request.headers.entity_ref;
|
||||
|
||||
console.log('------- user id ----- ', userId, entityRef);
|
||||
|
||||
const count = await db?.('public.members')
|
||||
.where({ entity_ref: entityRef })
|
||||
.andWhere('user_id', userId)
|
||||
.del();
|
||||
|
||||
if (count) {
|
||||
response.send({ status: 'ok' });
|
||||
response.json({ status: 'ok' });
|
||||
} else {
|
||||
response.status(404).json({ message: 'Record not found' });
|
||||
}
|
||||
@@ -109,7 +102,7 @@ export async function createRouter(
|
||||
.del();
|
||||
|
||||
if (count) {
|
||||
response.send({ status: 'ok' });
|
||||
response.json({ status: 'ok' });
|
||||
} else {
|
||||
response.status(404).json({ message: 'Record not found' });
|
||||
}
|
||||
@@ -124,16 +117,32 @@ export async function createRouter(
|
||||
.where({ entity_ref: entityRef });
|
||||
|
||||
if (data?.length) {
|
||||
response.send({ status: 'ok', data: data });
|
||||
response.json({ status: 'ok', data: data });
|
||||
} else {
|
||||
response.status(404).json({ message: 'Record not found' });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/entities', async (_, response) => {
|
||||
const data = await db?.select('*').from('public.metadata');
|
||||
const columns = [
|
||||
'members.entity_ref',
|
||||
'metadata.name',
|
||||
'metadata.announcement',
|
||||
'metadata.status',
|
||||
'metadata.updated_at',
|
||||
];
|
||||
const data = await db?.('public.members as members')
|
||||
.select(columns)
|
||||
.count('members.entity_ref as members_count')
|
||||
.groupBy(columns)
|
||||
.join(
|
||||
'public.metadata as metadata',
|
||||
'metadata.entity_ref',
|
||||
'=',
|
||||
'members.entity_ref',
|
||||
);
|
||||
|
||||
response.send({ status: 'ok', data: data });
|
||||
response.json({ status: 'ok', data: data });
|
||||
});
|
||||
|
||||
router.put('/metadata', async (request, response) => {
|
||||
@@ -149,7 +158,7 @@ export async function createRouter(
|
||||
});
|
||||
|
||||
if (count) {
|
||||
response.send({ status: 'ok' });
|
||||
response.json({ status: 'ok' });
|
||||
} else {
|
||||
await db
|
||||
?.insert({
|
||||
@@ -160,7 +169,7 @@ export async function createRouter(
|
||||
status: status,
|
||||
})
|
||||
.into('public.metadata');
|
||||
response.send({ status: 'ok' });
|
||||
response.json({ status: 'ok' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -172,7 +181,7 @@ export async function createRouter(
|
||||
.del();
|
||||
|
||||
if (count) {
|
||||
response.send({ status: 'ok' });
|
||||
response.json({ status: 'ok' });
|
||||
} else {
|
||||
response.status(404).json({ message: 'Record not found' });
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 3.4 MiB |
|
Before Width: | Height: | Size: 3.6 MiB |
|
Before Width: | Height: | Size: 2.8 MiB |
|
Before Width: | Height: | Size: 388 KiB |
|
Before Width: | Height: | Size: 235 KiB |
|
Before Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 206 KiB |
|
Before Width: | Height: | Size: 88 KiB |
@@ -4,6 +4,7 @@
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
@@ -30,6 +31,7 @@
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"luxon": "^2.0.2",
|
||||
"moment": "^2.29.1",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
|
||||
@@ -14,17 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Entity,
|
||||
EntityRef,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { Entity, stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import {
|
||||
createApiRef,
|
||||
DiscoveryApi,
|
||||
IdentityApi,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { BazaarProject, Status } from './types';
|
||||
import { Status } from './types';
|
||||
|
||||
export const bazaarApiRef = createApiRef<BazaarApi>({
|
||||
id: 'bazaar',
|
||||
@@ -42,10 +38,6 @@ export interface BazaarApi {
|
||||
|
||||
getMetadata(entity: Entity): Promise<any>;
|
||||
|
||||
getMemberCounts(
|
||||
bazaarProjects: BazaarProject[],
|
||||
): Promise<Map<EntityRef, number>>;
|
||||
|
||||
getMembers(entity: Entity): Promise<any>;
|
||||
|
||||
deleteMember(entity: Entity): Promise<void>;
|
||||
@@ -107,27 +99,6 @@ export class BazaarClient implements BazaarApi {
|
||||
});
|
||||
}
|
||||
|
||||
async getMemberCounts(
|
||||
bazaarProjects: BazaarProject[],
|
||||
): Promise<Map<EntityRef, number>> {
|
||||
const baseUrl = await this.discoveryApi.getBaseUrl('bazaar');
|
||||
|
||||
const members = new Map<EntityRef, number>();
|
||||
for (const project of bazaarProjects) {
|
||||
const response = await fetch(`${baseUrl}/members`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
entity_ref: project.entityRef as string,
|
||||
},
|
||||
});
|
||||
|
||||
const json = await response.json();
|
||||
const nbrOfMembers = await json.data.length;
|
||||
members.set(project.entityRef, nbrOfMembers);
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
async getMembers(entity: Entity): Promise<any> {
|
||||
const baseUrl = await this.discoveryApi.getBaseUrl('bazaar');
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ export const AddProjectDialog = ({
|
||||
announcement: formValues.announcement,
|
||||
status: formValues.status,
|
||||
updatedAt: new Date().toISOString(),
|
||||
membersCount: 0,
|
||||
};
|
||||
|
||||
setBazaarProjects((oldProjects: BazaarProject[]) => {
|
||||
|
||||
@@ -47,11 +47,16 @@ import DeleteIcon from '@material-ui/icons/Delete';
|
||||
import { EditProjectDialog } from '../EditProjectDialog';
|
||||
import { DeleteProjectDialog } from '../DeleteProjectDialog';
|
||||
import ExitToAppIcon from '@material-ui/icons/ExitToApp';
|
||||
import { useApi, identityApiRef } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
useApi,
|
||||
identityApiRef,
|
||||
useRouteRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { useAsync } from 'react-use';
|
||||
import { Member, BazaarProject } from '../../types';
|
||||
import { bazaarApiRef } from '../../api';
|
||||
import { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { rootRouteRef } from '../../routes';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
description: {
|
||||
@@ -73,7 +78,7 @@ const useStyles = makeStyles({
|
||||
});
|
||||
|
||||
const sortMembers = (m1: Member, m2: Member) => {
|
||||
return new Date(m2.joinDate).getTime() - new Date(m1.joinDate).getTime();
|
||||
return new Date(m2.joinDate!).getTime() - new Date(m1.joinDate!).getTime();
|
||||
};
|
||||
|
||||
export const EntityBazaarInfoCard = () => {
|
||||
@@ -95,8 +100,10 @@ export const EntityBazaarInfoCard = () => {
|
||||
announcement: '',
|
||||
status: 'proposed',
|
||||
updatedAt: '',
|
||||
membersCount: 0,
|
||||
});
|
||||
const [isBazaar, setIsBazaar] = useState(false);
|
||||
const routeRef = useRouteRef(rootRouteRef);
|
||||
|
||||
const getInitMemberStatus = async () => {
|
||||
const response = await bazaarApi.getMembers(entity);
|
||||
@@ -134,6 +141,7 @@ export const EntityBazaarInfoCard = () => {
|
||||
announcement: data[0].announcement,
|
||||
status: data[0].status,
|
||||
updatedAt: data[0].updatedAt,
|
||||
membersCount: data[0].membersCount,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -166,12 +174,12 @@ export const EntityBazaarInfoCard = () => {
|
||||
const newMember: Member = {
|
||||
userId: identity.getUserId(),
|
||||
entityRef: stringifyEntityRef(entity),
|
||||
joinDate: new Date().toISOString(),
|
||||
};
|
||||
|
||||
if (!isMember) {
|
||||
setMembers((prevMembers: Member[]) => {
|
||||
const newMembers: Member[] = [...prevMembers, newMember];
|
||||
const newMembers: Member[] = [newMember, ...prevMembers];
|
||||
|
||||
newMembers.sort(sortMembers);
|
||||
return newMembers;
|
||||
});
|
||||
@@ -213,11 +221,11 @@ export const EntityBazaarInfoCard = () => {
|
||||
<CardContent>
|
||||
<Typography variant="body1">
|
||||
This project is not in the Bazaar. Go to the{' '}
|
||||
<Link className={classes.link} to="/bazaar">
|
||||
<Link className={classes.link} to={`/${routeRef()}`}>
|
||||
Bazaar
|
||||
</Link>{' '}
|
||||
to add the project or to{' '}
|
||||
<Link className={classes.link} to="/bazaar/about">
|
||||
<Link className={classes.link} to={`/${routeRef()}/about`}>
|
||||
read more
|
||||
</Link>
|
||||
.
|
||||
@@ -287,16 +295,18 @@ export const EntityBazaarInfoCard = () => {
|
||||
<Grid item xs={12}>
|
||||
<AboutField label="Announcement">
|
||||
{bazaarProject.announcement
|
||||
? bazaarProject.announcement.split('\n').map((str: string) => (
|
||||
<Typography
|
||||
key={Math.floor(Math.random() * 1000)}
|
||||
variant="body2"
|
||||
paragraph
|
||||
className={classes.description}
|
||||
>
|
||||
{str}
|
||||
</Typography>
|
||||
))
|
||||
? bazaarProject.announcement
|
||||
.split('\n')
|
||||
.map((str: string, i: number) => (
|
||||
<Typography
|
||||
key={i}
|
||||
variant="body2"
|
||||
paragraph
|
||||
className={classes.description}
|
||||
>
|
||||
{str}
|
||||
</Typography>
|
||||
))
|
||||
: 'No announcement'}
|
||||
</AboutField>
|
||||
</Grid>
|
||||
|
||||
@@ -25,11 +25,11 @@ import {
|
||||
} from '@material-ui/core';
|
||||
import { StatusTag } from '../StatusTag/StatusTag';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import moment from 'moment';
|
||||
import { catalogRouteRef } from '@backstage/plugin-catalog-react';
|
||||
import { useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { BazaarProject } from '../../types';
|
||||
import { parseEntityName } from '@backstage/catalog-model';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
statusTag: {
|
||||
@@ -51,12 +51,12 @@ const useStyles = makeStyles({
|
||||
|
||||
type Props = {
|
||||
bazaarProject: BazaarProject;
|
||||
memberCount: number;
|
||||
};
|
||||
|
||||
export const ProjectCard = ({ bazaarProject, memberCount }: Props) => {
|
||||
export const ProjectCard = ({ bazaarProject }: Props) => {
|
||||
const classes = useStyles();
|
||||
const { entityRef, name, status, updatedAt, announcement } = bazaarProject;
|
||||
const { entityRef, name, status, updatedAt, announcement, membersCount } =
|
||||
bazaarProject;
|
||||
const catalogLink = useRouteRef(catalogRouteRef);
|
||||
const { namespace, kind } = parseEntityName(entityRef);
|
||||
|
||||
@@ -73,14 +73,16 @@ export const ProjectCard = ({ bazaarProject, memberCount }: Props) => {
|
||||
>
|
||||
<ItemCardHeader
|
||||
title={name}
|
||||
subtitle={`updated ${moment(updatedAt).fromNow()}`}
|
||||
subtitle={`updated ${DateTime.fromISO(updatedAt!).toRelative({
|
||||
base: DateTime.now(),
|
||||
})}`}
|
||||
/>
|
||||
<CardContent style={{ height: '12rem' }}>
|
||||
<StatusTag styles={classes.statusTag} status={status} />
|
||||
<Typography variant="body2" className={classes.memberCount}>
|
||||
{memberCount === 1
|
||||
? `${memberCount} member`
|
||||
: `${memberCount} members`}
|
||||
{membersCount === 1
|
||||
? `${membersCount} member`
|
||||
: `${membersCount} members`}
|
||||
</Typography>
|
||||
<div style={{ minHeight: '6.5rem', maxHeight: '6.5rem' }}>
|
||||
<Typography variant="body2" className={classes.announcement}>
|
||||
|
||||
@@ -20,12 +20,10 @@ import { ProjectCard } from '../ProjectCard/ProjectCard';
|
||||
import { makeStyles, Grid } from '@material-ui/core';
|
||||
import Pagination from '@material-ui/lab/Pagination';
|
||||
import { BazaarProject } from '../../types';
|
||||
import { EntityRef } from '@backstage/catalog-model';
|
||||
|
||||
type Props = {
|
||||
bazaarProjects: BazaarProject[];
|
||||
sortingMethod: (arg0: BazaarProject, arg1: BazaarProject) => number;
|
||||
bazaarMembers: Map<EntityRef, number>;
|
||||
};
|
||||
|
||||
const useStyles = makeStyles({
|
||||
@@ -42,11 +40,7 @@ const useStyles = makeStyles({
|
||||
},
|
||||
});
|
||||
|
||||
export const ProjectPreview = ({
|
||||
bazaarProjects,
|
||||
sortingMethod,
|
||||
bazaarMembers,
|
||||
}: Props) => {
|
||||
export const ProjectPreview = ({ bazaarProjects, sortingMethod }: Props) => {
|
||||
const classes = useStyles();
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const pageSize = 10;
|
||||
@@ -92,7 +86,6 @@ export const ProjectPreview = ({
|
||||
<ProjectCard
|
||||
bazaarProject={bazaarProject}
|
||||
key={Math.random()}
|
||||
memberCount={bazaarMembers.get(entityRef) || 0}
|
||||
/>
|
||||
</Grid>
|
||||
);
|
||||
|
||||
@@ -26,16 +26,9 @@ import { AlertBanner } from '../AlertBanner';
|
||||
import { ProjectPreview } from '../ProjectPreview/ProjectPreview';
|
||||
import { Button, makeStyles, Link } from '@material-ui/core';
|
||||
import { useAsync } from 'react-use';
|
||||
import {
|
||||
Entity,
|
||||
EntityRef,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { Entity, stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
catalogApiRef,
|
||||
CATALOG_FILTER_EXISTS,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { BazaarProject } from '../../types';
|
||||
import { bazaarApiRef } from '../../api';
|
||||
|
||||
@@ -50,9 +43,6 @@ export const SortView = () => {
|
||||
const [openAdd, setOpenAdd] = useState(false);
|
||||
const [openNoProjects, setOpenNoProjects] = useState(false);
|
||||
const [catalogEntities, setCatalogEntities] = useState<Entity[]>([]);
|
||||
const [bazaarMembers, setBazaarMembers] = useState<Map<EntityRef, number>>(
|
||||
new Map(),
|
||||
);
|
||||
const [bazaarProjects, setBazaarProjects] = useState<BazaarProject[]>([]);
|
||||
const bazaarApi = useApi(bazaarApiRef);
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
@@ -61,8 +51,8 @@ export const SortView = () => {
|
||||
a: BazaarProject,
|
||||
b: BazaarProject,
|
||||
): number => {
|
||||
const dateA = new Date(a.updatedAt).getTime();
|
||||
const dateB = new Date(b.updatedAt).getTime();
|
||||
const dateA = new Date(a.updatedAt!).getTime();
|
||||
const dateB = new Date(b.updatedAt!).getTime();
|
||||
return dateB - dateA;
|
||||
};
|
||||
|
||||
@@ -73,10 +63,9 @@ export const SortView = () => {
|
||||
const { loading } = useAsync(async () => {
|
||||
const entities = await catalogApi.getEntities({
|
||||
filter: {
|
||||
kind: 'Component',
|
||||
'metadata.annotations.backstage.io/edit-url': CATALOG_FILTER_EXISTS,
|
||||
kind: ['Component', 'API', 'Resource', 'System', 'Domain'],
|
||||
},
|
||||
fields: ['apiVersion', 'kind', 'metadata', 'spec'],
|
||||
fields: ['kind', 'metadata.name', 'metadata.namespace'],
|
||||
});
|
||||
|
||||
const response = await bazaarApi.getEntities();
|
||||
@@ -91,12 +80,12 @@ export const SortView = () => {
|
||||
announcement: project.announcement,
|
||||
community: project.community,
|
||||
updatedAt: project.updated_at,
|
||||
membersCount: project.members_count,
|
||||
});
|
||||
|
||||
bazaarProjectRefs.push(project.entity_ref);
|
||||
});
|
||||
|
||||
setBazaarMembers(await bazaarApi.getMemberCounts(dbProjects));
|
||||
setBazaarProjects(dbProjects);
|
||||
setCatalogEntities(
|
||||
entities.items.filter((entity: Entity) => {
|
||||
@@ -155,7 +144,6 @@ export const SortView = () => {
|
||||
<ProjectPreview
|
||||
bazaarProjects={bazaarProjects || []}
|
||||
sortingMethod={compareProjectsByDate}
|
||||
bazaarMembers={bazaarMembers}
|
||||
/>
|
||||
<Content noPadding className={classes.container} />
|
||||
</Content>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// This file is read by tools that parse documentation comments conforming to the TSDoc standard.
|
||||
// It should be published with your NPM package. It should not be tracked by Git.
|
||||
{
|
||||
"tsdocVersion": "0.12",
|
||||
"toolPackages": [
|
||||
{
|
||||
"packageName": "@microsoft/api-extractor",
|
||||
"packageVersion": "7.18.9"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import { EntityRef } from '@backstage/catalog-model';
|
||||
export type Member = {
|
||||
entityRef: EntityRef;
|
||||
userId: string;
|
||||
joinDate: string;
|
||||
joinDate?: string;
|
||||
};
|
||||
|
||||
export type Status = 'ongoing' | 'proposed';
|
||||
@@ -30,7 +30,8 @@ export type BazaarProject = {
|
||||
community: string;
|
||||
status: Status;
|
||||
announcement: string;
|
||||
updatedAt: string;
|
||||
updatedAt?: string;
|
||||
membersCount: number;
|
||||
};
|
||||
|
||||
export type FormValues = {
|
||||
|
||||