diff --git a/plugins/bazaar-backend/migrations/20210923133459_trigger.js b/plugins/bazaar-backend/migrations/20210923133459_trigger.js deleted file mode 100644 index a778a1577e..0000000000 --- a/plugins/bazaar-backend/migrations/20210923133459_trigger.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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. - */ - -exports.up = function up(knex) { - return knex.raw(` - CREATE OR REPLACE FUNCTION update_timestamp() RETURNS TRIGGER - LANGUAGE plpgsql - AS - $$ - BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; - END; - $$; - `); -}; - -exports.down = function down(knex) { - return knex.raw(` - DROP FUNCTION IF EXISTS update_timestamp() CASCADE; - `); -}; diff --git a/plugins/bazaar-backend/migrations/20210923133506_init.js b/plugins/bazaar-backend/migrations/20211014144054_init.js similarity index 80% rename from plugins/bazaar-backend/migrations/20210923133506_init.js rename to plugins/bazaar-backend/migrations/20211014144054_init.js index f40125ea42..5bf66698ac 100644 --- a/plugins/bazaar-backend/migrations/20210923133506_init.js +++ b/plugins/bazaar-backend/migrations/20211014144054_init.js @@ -17,8 +17,12 @@ exports.up = async function up(knex) { await knex.schema.createTable('metadata', table => { table.comment('The table of Bazaar metadata'); + table + .text('entity_ref') + .notNullable() + .unique() + .comment('The ref of the entity'); table.text('name').notNullable().comment('The name of the entity'); - table.text('entity_ref').notNullable().comment('The ref of the entity'); table .text('community') .comment('Link to where the community can discuss ideas'); @@ -31,20 +35,20 @@ exports.up = async function up(knex) { .defaultTo('proposed') .notNullable() .comment('The status of the Bazaar project'); - table.timestamps(true, true); + table + .text('updated_at') + .notNullable() + .comment('Timestamp on ISO 8601 format when entity was last updated'); }); - await knex.raw(` - CREATE TRIGGER update_timestamp - BEFORE UPDATE - ON metadata - FOR EACH ROW - EXECUTE PROCEDURE update_timestamp(); - `); - await knex.schema.createTable('members', table => { table.comment('The table of Bazaar members'); - table.text('entity_ref').notNullable().comment('The ref of the entity'); + table + .text('entity_ref') + .notNullable() + .references('metadata.entity_ref') + .onDelete('CASCADE') + .comment('The ref of the entity'); table.text('user_id').notNullable().comment('The user id of the member'); table .dateTime('join_date') diff --git a/plugins/bazaar-backend/src/service/DatabaseHandler.test.ts b/plugins/bazaar-backend/src/service/DatabaseHandler.test.ts index 5293b3fc03..4413228006 100644 --- a/plugins/bazaar-backend/src/service/DatabaseHandler.test.ts +++ b/plugins/bazaar-backend/src/service/DatabaseHandler.test.ts @@ -17,12 +17,14 @@ import { DatabaseHandler } from './DatabaseHandler'; import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; -const members: Array = [ - { - entity_ref: 'project1', - user_id: 'member1', - }, -]; +const bazaarProject: any = { + name: 'name', + entityRef: 'ref', + community: '', + status: 'proposed', + announcement: 'a', + membersCount: 0, +}; describe('DatabaseHandler', () => { const databases = TestDatabases.create({ @@ -38,10 +40,11 @@ describe('DatabaseHandler', () => { 'should do a full sync with the locations on connect, %p', async databaseId => { const db = await createDatabaseHandler(databaseId); - await db.addMember(members[0].user_id, members[0].entity_ref); - const cov: any[] = await db.getMembers('project1'); - expect(cov[0].entity_ref).toEqual(members[0].entity_ref); - expect(cov[0].user_id).toEqual(members[0].user_id); + await db.insertMetadata(bazaarProject); + + const entities = await db.getEntities(); + expect(entities.length).toEqual(1); + expect(entities[0].entity_ref).toEqual(bazaarProject.entityRef); }, 60_000, ); diff --git a/plugins/bazaar-backend/src/service/DatabaseHandler.ts b/plugins/bazaar-backend/src/service/DatabaseHandler.ts index 8a96f4212f..d4da6e65e7 100644 --- a/plugins/bazaar-backend/src/service/DatabaseHandler.ts +++ b/plugins/bazaar-backend/src/service/DatabaseHandler.ts @@ -43,20 +43,103 @@ export class DatabaseHandler { this.database = options.database; } - async getMembers(entityRef: any) { + async getMembers(entityRef: string) { return await this.database .select('*') - .from('public.members') + .from('members') .where({ entity_ref: entityRef }); } - async addMember(userId: any, entityRef: any, picture?: string) { + async addMember(userId: string, entityRef: string, picture?: string) { await this.database .insert({ entity_ref: entityRef, user_id: userId, picture: picture, }) - .into('public.members'); + .into('members'); + } + + async deleteMember(userId: string, entityRef: string) { + return await this.database('members') + .where({ entity_ref: decodeURIComponent(entityRef) }) + .andWhere('user_id', userId) + .del(); + } + + async getMetadata(entityRef: string) { + const coalesce = this.database.raw( + 'coalesce(count(members.entity_ref), 0) as members_count', + ); + + const columns = [ + 'members.entity_ref', + 'metadata.entity_ref', + 'metadata.name', + 'metadata.announcement', + 'metadata.status', + 'metadata.updated_at', + 'metadata.community', + ]; + + return await this.database('metadata') + .select([...columns, coalesce]) + .where({ 'metadata.entity_ref': entityRef }) + .groupBy(columns) + .leftJoin('members', 'metadata.entity_ref', '=', 'members.entity_ref'); + } + + async insertMetadata(bazaarProject: any) { + const { name, entityRef, community, announcement, status } = bazaarProject; + + await this.database + .insert({ + name: name, + entity_ref: entityRef, + community: community, + announcement: announcement, + status: status, + updated_at: new Date().toISOString(), + }) + .into('metadata'); + } + + async updateMetadata(bazaarProject: any) { + const { entityRef, community, announcement, status } = bazaarProject; + + return await this.database('metadata') + .where({ entity_ref: entityRef }) + .update({ + announcement: announcement, + community: community, + status: status, + updated_at: new Date().toISOString(), + }); + } + + async deleteMetadata(entityRef: string) { + return await this.database('metadata') + .where({ entity_ref: entityRef }) + .del(); + } + + async getEntities() { + const coalesce = this.database.raw( + 'coalesce(count(members.entity_ref), 0) as members_count', + ); + + const columns = [ + 'members.entity_ref', + 'metadata.entity_ref', + 'metadata.name', + 'metadata.announcement', + 'metadata.status', + 'metadata.updated_at', + 'metadata.community', + ]; + return await this.database('metadata') + .select([...columns, coalesce]) + .groupBy(columns) + .leftJoin('members', 'metadata.entity_ref', '=', 'members.entity_ref'); } } diff --git a/plugins/bazaar-backend/src/service/router.ts b/plugins/bazaar-backend/src/service/router.ts index 46d26c3c37..f0bc02c65b 100644 --- a/plugins/bazaar-backend/src/service/router.ts +++ b/plugins/bazaar-backend/src/service/router.ts @@ -61,21 +61,7 @@ export async function createRouter( router.delete('/member/:ref/:id', async (request, response) => { const { ref, id } = request.params; - const count = await db('public.members') - .where({ entity_ref: decodeURIComponent(ref) }) - .andWhere('user_id', id) - .del(); - - if (count) { - response.json({ status: 'ok' }); - } else { - response.status(404).json({ message: 'Record not found' }); - } - }); - - router.delete('/members/:ref', async (request, response) => { - const ref = decodeURIComponent(request.params.ref); - const count = await db('public.members').where({ entity_ref: ref }).del(); + const count = await dbHandler.deleteMember(id, ref); if (count) { response.json({ status: 'ok' }); @@ -85,97 +71,35 @@ export async function createRouter( }); router.get('/metadata/:ref', async (request, response) => { - const entity_ref = decodeURIComponent(request.params.ref); - - const coalesce = db.raw( - 'coalesce(count(members.entity_ref), 0) as members_count', - ); - - const columns = [ - 'members.entity_ref', - 'metadata.entity_ref', - 'metadata.name', - 'metadata.announcement', - 'metadata.status', - 'metadata.updated_at', - 'metadata.community', - ]; - - const data = await db('public.members as members') - .select([...columns, coalesce]) - .where({ 'metadata.entity_ref': entity_ref }) - .groupBy(columns) - .rightJoin( - 'public.metadata as metadata', - 'metadata.entity_ref', - '=', - 'members.entity_ref', - ); + const ref = decodeURIComponent(request.params.ref); + const data = await dbHandler.getMetadata(ref); response.json({ status: 'ok', data: data }); }); router.get('/entities', async (_, response) => { - const coalesce = db.raw( - 'coalesce(count(members.entity_ref), 0) as members_count', - ); - - const columns = [ - 'members.entity_ref', - 'metadata.entity_ref', - 'metadata.name', - 'metadata.announcement', - 'metadata.status', - 'metadata.updated_at', - 'metadata.community', - ]; - - const data = await db('public.members as members') - .select([...columns, coalesce]) - .groupBy(columns) - .rightJoin( - 'public.metadata as metadata', - 'metadata.entity_ref', - '=', - 'members.entity_ref', - ); + const data = await dbHandler.getEntities(); response.json({ status: 'ok', data: data }); }); router.put('/metadata', async (request, response) => { - const { name, announcement, status, community, entity_ref } = request.body; + const bazaarProject = request.body; - const count = await db('public.metadata') - .where({ entity_ref: entity_ref }) - .update({ - announcement: announcement, - community: community, - status: status, - }); + const count = await dbHandler.updateMetadata(bazaarProject); if (count) { response.json({ status: 'ok' }); } else { - await db - .insert({ - name: name, - entity_ref: entity_ref, - community: community, - announcement: announcement, - status: status, - }) - .into('public.metadata'); + await dbHandler.insertMetadata(bazaarProject); response.json({ status: 'ok' }); } }); router.delete('/metadata/:ref', async (request, response) => { - const entity_ref = decodeURIComponent(request.params.ref); + const ref = decodeURIComponent(request.params.ref); - const count = await db('public.metadata') - .where({ entity_ref: entity_ref }) - .del(); + const count = await dbHandler.deleteMetadata(ref); if (count) { response.json({ status: 'ok' }); diff --git a/plugins/bazaar/README.md b/plugins/bazaar/README.md index 4c089bcfed..cd26f01997 100644 --- a/plugins/bazaar/README.md +++ b/plugins/bazaar/README.md @@ -101,6 +101,10 @@ The metadata related to the Bazaar is stored in a database. Right now there are ## Future work and ideas +- Workflow + + - Make it possible to add a Bazaar project without linking it to a Backstage entity, this would make it easier to just add an idea to the Bazaar. + - Bazaar landing page - Add a tab 'My page', where your personal data is displayed. For example: your projects and its latest activities, projects or tags you are following etc. @@ -117,7 +121,7 @@ The metadata related to the Bazaar is stored in a database. Right now there are - Dialogues - - Extend the dialogue for adding a project with more fields, e.g. chat link and the possibility to add images + - Extend the dialogue for adding a project with more fields, e.g. the possibility to add images - Testing - Add tests to all the components diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json index d8f7927973..5f4be3a3d4 100644 --- a/plugins/bazaar/package.json +++ b/plugins/bazaar/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/cli": "^0.7.2", + "@backstage/cli": "^0.7.15", "@backstage/core-components": "^0.6.1", "@backstage/core-plugin-api": "^0.1.10", "@backstage/plugin-catalog": "^0.7.0", @@ -30,7 +30,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", - "@testing-library/jest-dom": "^5.14.1", + "@testing-library/jest-dom": "^5.10.1", "luxon": "^2.0.2", "react": "^16.13.1", "react-dom": "^16.13.1", diff --git a/plugins/bazaar/src/api.ts b/plugins/bazaar/src/api.ts index b3f7857511..db20e5e0cb 100644 --- a/plugins/bazaar/src/api.ts +++ b/plugins/bazaar/src/api.ts @@ -20,7 +20,7 @@ import { DiscoveryApi, IdentityApi, } from '@backstage/core-plugin-api'; -import { BazaarProject, Status } from './types'; +import { BazaarProject } from './types'; export const bazaarApiRef = createApiRef({ id: 'bazaar', @@ -28,13 +28,7 @@ export const bazaarApiRef = createApiRef({ }); export interface BazaarApi { - updateMetadata( - entity: Entity, - name: string, - community: string, - announcement: string, - status: Status, - ): Promise; + updateMetadata(bazaarProject: BazaarProject): Promise; getMetadata(entity: Entity): Promise; @@ -61,13 +55,7 @@ export class BazaarClient implements BazaarApi { this.discoveryApi = options.discoveryApi; } - async updateMetadata( - entity: Entity, - name: string, - community: string, - announcement: string, - status: Status, - ): Promise { + async updateMetadata(bazaarProject: BazaarProject): Promise { const baseUrl = await this.discoveryApi.getBaseUrl('bazaar'); return await fetch(`${baseUrl}/metadata`, { @@ -76,13 +64,7 @@ export class BazaarClient implements BazaarApi { Accept: 'application/json', 'Content-Type': 'application/json', }, - body: JSON.stringify({ - entity_ref: stringifyEntityRef(entity), - name: name, - announcement: announcement, - community: community, - status: status, - }), + body: JSON.stringify(bazaarProject), }).then(resp => resp.json()); } @@ -155,11 +137,5 @@ export class BazaarClient implements BazaarApi { await fetch(`${baseUrl}/metadata/${encodeURIComponent(entityRef)}`, { method: 'DELETE', }); - - if (bazaarProject.membersCount > 0) { - await fetch(`${baseUrl}/members/${encodeURIComponent(entityRef)}`, { - method: 'DELETE', - }); - } } } diff --git a/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx b/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx index 921c3fc232..82d682b4df 100644 --- a/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx +++ b/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx @@ -15,7 +15,7 @@ */ import React, { useState, useEffect } from 'react'; -import { Entity } from '@backstage/catalog-model'; +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { SubmitHandler } from 'react-hook-form'; import { useApi } from '@backstage/core-plugin-api'; import { ProjectDialog } from '../ProjectDialog'; @@ -70,13 +70,14 @@ export const AddProjectDialog = ({ const formValues = getValues(); if (selectedEntity) { - await bazaarApi.updateMetadata( - selectedEntity, - selectedEntity.metadata.name, - formValues.community, - formValues.announcement, - formValues.status, - ); + await bazaarApi.updateMetadata({ + name: selectedEntity.metadata.name, + entityRef: stringifyEntityRef(selectedEntity), + announcement: formValues.announcement, + status: formValues.status, + community: formValues.community, + membersCount: 0, + } as BazaarProject); fetchBazaarProjects(); fetchCatalogEntities(); diff --git a/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx b/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx index 03d54136cc..172bdf548d 100644 --- a/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx +++ b/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx @@ -15,7 +15,7 @@ */ import React, { useState, useEffect } from 'react'; -import { Entity } from '@backstage/catalog-model'; +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { useApi } from '@backstage/core-plugin-api'; import { ProjectDialog } from '../ProjectDialog'; import { BazaarProject, FormValues } from '../../types'; @@ -56,13 +56,14 @@ export const EditProjectDialog = ({ const handleSave: any = async (getValues: any, _: any) => { const formValues = getValues(); - const updateResponse = await bazaarApi.updateMetadata( - entity!, - entity.metadata.name, - formValues.community, - formValues.announcement, - formValues.status, - ); + const updateResponse = await bazaarApi.updateMetadata({ + name: entity.metadata.name, + entityRef: stringifyEntityRef(entity), + announcement: formValues.announcement, + status: formValues.status, + community: formValues.community, + membersCount: bazaarProject.membersCount, + }); if (updateResponse.status === 'ok') fetchBazaarProject(); handleClose(); diff --git a/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx b/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx index 8c9bc17208..b3c8b3e2e7 100644 --- a/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx +++ b/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx @@ -73,7 +73,9 @@ export const ProjectCard = ({ bazaarProject }: Props) => { > diff --git a/plugins/bazaar/src/components/SortView/SortView.tsx b/plugins/bazaar/src/components/SortView/SortView.tsx index 609c86cd90..99d3f84ac3 100644 --- a/plugins/bazaar/src/components/SortView/SortView.tsx +++ b/plugins/bazaar/src/components/SortView/SortView.tsx @@ -39,12 +39,15 @@ const useStyles = makeStyles({ }, }); -const filterCatalogEntities = (bazaarProjects: any, catalogEntities: any) => { - const bazaarProjectRefs = bazaarProjects?.value?.map( +const filterCatalogEntities = ( + bazaarProjects: BazaarProject[], + catalogEntities: Entity[], +) => { + const bazaarProjectRefs = bazaarProjects.map( (project: BazaarProject) => project.entityRef, ); - const filtered = catalogEntities?.value?.filter((entity: Entity) => { + const filtered = catalogEntities.filter((entity: Entity) => { return !bazaarProjectRefs?.includes(stringifyEntityRef(entity)); }); @@ -110,8 +113,8 @@ export const SortView = () => { useEffect(() => { const filteredCatalogEntities = filterCatalogEntities( - bazaarProjects, - catalogEntities, + bazaarProjects.value || [], + catalogEntities.value || [], ); if (filteredCatalogEntities) { diff --git a/plugins/bazaar/src/index.ts b/plugins/bazaar/src/index.ts index e9b388d736..1b7c6a4b93 100644 --- a/plugins/bazaar/src/index.ts +++ b/plugins/bazaar/src/index.ts @@ -16,3 +16,4 @@ export { bazaarPlugin, BazaarPage } from './plugin'; export { EntityBazaarInfoCard } from './components/EntityBazaarInfoCard'; +export type { BazaarProject } from './types'; diff --git a/yarn.lock b/yarn.lock index fb1fef677c..b2fb7fa420 100644 --- a/yarn.lock +++ b/yarn.lock @@ -343,6 +343,13 @@ dependencies: "@babel/highlight" "^7.14.5" +"@babel/code-frame@^7.15.8": + version "7.15.8" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz#45990c47adadb00c03677baa89221f7cc23d2503" + integrity sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg== + dependencies: + "@babel/highlight" "^7.14.5" + "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.4": version "7.14.4" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.4.tgz#45720fe0cecf3fd42019e1d12cc3d27fadc98d58" @@ -353,6 +360,11 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.9.tgz#ac7996ceaafcf8f410119c8af0d1db4cf914a210" integrity sha512-p3QjZmMGHDGdpcwEYYWu7i7oJShJvtgMjJeb0W95PPhSm++3lm8YXYOh45Y6iCN9PkZLTZ7CIX5nFrp7pw7TXw== +"@babel/compat-data@^7.15.0": + version "7.15.0" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176" + integrity sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA== + "@babel/core@7.12.9": version "7.12.9" resolved "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" @@ -375,7 +387,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.4.4", "@babel/core@^7.7.5": +"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.7.5": version "7.14.8" resolved "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz#20cdf7c84b5d86d83fac8710a8bc605a7ba3f010" integrity sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q== @@ -396,6 +408,27 @@ semver "^6.3.0" source-map "^0.5.0" +"@babel/core@^7.4.4": + version "7.15.8" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.15.8.tgz#195b9f2bffe995d2c6c159e72fe525b4114e8c10" + integrity sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og== + dependencies: + "@babel/code-frame" "^7.15.8" + "@babel/generator" "^7.15.8" + "@babel/helper-compilation-targets" "^7.15.4" + "@babel/helper-module-transforms" "^7.15.8" + "@babel/helpers" "^7.15.4" + "@babel/parser" "^7.15.8" + "@babel/template" "^7.15.4" + "@babel/traverse" "^7.15.4" + "@babel/types" "^7.15.6" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + semver "^6.3.0" + source-map "^0.5.0" + "@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.14.8", "@babel/generator@^7.14.9": version "7.14.9" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.14.9.tgz#23b19c597d38b4f7dc2e3fe42a69c88d9ecfaa16" @@ -414,6 +447,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.15.4", "@babel/generator@^7.15.8": + version "7.15.8" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz#fa56be6b596952ceb231048cf84ee499a19c0cd1" + integrity sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g== + dependencies: + "@babel/types" "^7.15.6" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.10.4", "@babel/helper-annotate-as-pure@^7.12.13": version "7.12.13" resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab" @@ -464,6 +506,16 @@ browserslist "^4.16.6" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.15.4": + version "7.15.4" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz#cf6d94f30fbefc139123e27dd6b02f65aeedb7b9" + integrity sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ== + dependencies: + "@babel/compat-data" "^7.15.0" + "@babel/helper-validator-option" "^7.14.5" + browserslist "^4.16.6" + semver "^6.3.0" + "@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.14.0", "@babel/helper-create-class-features-plugin@^7.14.3": version "7.14.4" resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.4.tgz#abf888d836a441abee783c75229279748705dc42" @@ -564,6 +616,15 @@ "@babel/template" "^7.14.5" "@babel/types" "^7.14.5" +"@babel/helper-function-name@^7.15.4": + version "7.15.4" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz#845744dafc4381a4a5fb6afa6c3d36f98a787ebc" + integrity sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw== + dependencies: + "@babel/helper-get-function-arity" "^7.15.4" + "@babel/template" "^7.15.4" + "@babel/types" "^7.15.4" + "@babel/helper-get-function-arity@^7.12.13": version "7.12.13" resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" @@ -578,6 +639,13 @@ dependencies: "@babel/types" "^7.14.5" +"@babel/helper-get-function-arity@^7.15.4": + version "7.15.4" + resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz#098818934a137fce78b536a3e015864be1e2879b" + integrity sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA== + dependencies: + "@babel/types" "^7.15.4" + "@babel/helper-hoist-variables@^7.13.0": version "7.13.16" resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.16.tgz#1b1651249e94b51f8f0d33439843e33e39775b30" @@ -593,6 +661,13 @@ dependencies: "@babel/types" "^7.14.5" +"@babel/helper-hoist-variables@^7.15.4": + version "7.15.4" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz#09993a3259c0e918f99d104261dfdfc033f178df" + integrity sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA== + dependencies: + "@babel/types" "^7.15.4" + "@babel/helper-member-expression-to-functions@^7.13.12": version "7.13.12" resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72" @@ -607,6 +682,13 @@ dependencies: "@babel/types" "^7.14.5" +"@babel/helper-member-expression-to-functions@^7.15.4": + version "7.15.4" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz#bfd34dc9bba9824a4658b0317ec2fd571a51e6ef" + integrity sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA== + dependencies: + "@babel/types" "^7.15.4" + "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.13.12": version "7.13.12" resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977" @@ -621,6 +703,13 @@ dependencies: "@babel/types" "^7.14.5" +"@babel/helper-module-imports@^7.15.4": + version "7.15.4" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz#e18007d230632dea19b47853b984476e7b4e103f" + integrity sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA== + dependencies: + "@babel/types" "^7.15.4" + "@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.14.5", "@babel/helper-module-transforms@^7.14.8": version "7.14.8" resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz#d4279f7e3fd5f4d5d342d833af36d4dd87d7dc49" @@ -649,6 +738,20 @@ "@babel/traverse" "^7.14.2" "@babel/types" "^7.14.2" +"@babel/helper-module-transforms@^7.15.4", "@babel/helper-module-transforms@^7.15.8": + version "7.15.8" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz#d8c0e75a87a52e374a8f25f855174786a09498b2" + integrity sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg== + dependencies: + "@babel/helper-module-imports" "^7.15.4" + "@babel/helper-replace-supers" "^7.15.4" + "@babel/helper-simple-access" "^7.15.4" + "@babel/helper-split-export-declaration" "^7.15.4" + "@babel/helper-validator-identifier" "^7.15.7" + "@babel/template" "^7.15.4" + "@babel/traverse" "^7.15.4" + "@babel/types" "^7.15.6" + "@babel/helper-optimise-call-expression@^7.12.13": version "7.12.13" resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" @@ -663,6 +766,13 @@ dependencies: "@babel/types" "^7.14.5" +"@babel/helper-optimise-call-expression@^7.15.4": + version "7.15.4" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz#f310a5121a3b9cc52d9ab19122bd729822dee171" + integrity sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw== + dependencies: + "@babel/types" "^7.15.4" + "@babel/helper-plugin-utils@7.10.4": version "7.10.4" resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" @@ -716,6 +826,16 @@ "@babel/traverse" "^7.14.5" "@babel/types" "^7.14.5" +"@babel/helper-replace-supers@^7.15.4": + version "7.15.4" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz#52a8ab26ba918c7f6dee28628b07071ac7b7347a" + integrity sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.15.4" + "@babel/helper-optimise-call-expression" "^7.15.4" + "@babel/traverse" "^7.15.4" + "@babel/types" "^7.15.4" + "@babel/helper-simple-access@^7.13.12": version "7.13.12" resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6" @@ -730,6 +850,13 @@ dependencies: "@babel/types" "^7.14.8" +"@babel/helper-simple-access@^7.15.4": + version "7.15.4" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz#ac368905abf1de8e9781434b635d8f8674bcc13b" + integrity sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg== + dependencies: + "@babel/types" "^7.15.4" + "@babel/helper-skip-transparent-expression-wrappers@^7.12.1": version "7.12.1" resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" @@ -758,6 +885,13 @@ dependencies: "@babel/types" "^7.14.5" +"@babel/helper-split-export-declaration@^7.15.4": + version "7.15.4" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz#aecab92dcdbef6a10aa3b62ab204b085f776e257" + integrity sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw== + dependencies: + "@babel/types" "^7.15.4" + "@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.14.0": version "7.14.0" resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" @@ -768,6 +902,11 @@ resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48" integrity sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g== +"@babel/helper-validator-identifier@^7.15.7": + version "7.15.7" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" + integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== + "@babel/helper-validator-option@^7.12.17": version "7.12.17" resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" @@ -807,6 +946,15 @@ "@babel/traverse" "^7.14.8" "@babel/types" "^7.14.8" +"@babel/helpers@^7.15.4": + version "7.15.4" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz#5f40f02050a3027121a3cf48d497c05c555eaf43" + integrity sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ== + dependencies: + "@babel/template" "^7.15.4" + "@babel/traverse" "^7.15.4" + "@babel/types" "^7.15.4" + "@babel/highlight@^7.0.0", "@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": version "7.14.0" resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" @@ -835,6 +983,11 @@ resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.14.9.tgz#596c1ad67608070058ebf8df50c1eaf65db895a4" integrity sha512-RdUTOseXJ8POjjOeEBEvNMIZU/nm4yu2rufRkcibzkkg7DmQvXU8v3M4Xk9G7uuI86CDGkKcuDWgioqZm+mScQ== +"@babel/parser@^7.15.4", "@babel/parser@^7.15.8": + version "7.15.8" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz#7bacdcbe71bdc3ff936d510c15dcea7cf0b99016" + integrity sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA== + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12": version "7.13.12" resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz#a3484d84d0b549f3fc916b99ee4783f26fabad2a" @@ -1536,7 +1689,7 @@ "@babel/helper-plugin-utils" "^7.14.5" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.14.0", "@babel/plugin-transform-modules-commonjs@^7.14.5", "@babel/plugin-transform-modules-commonjs@^7.4.4": +"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.14.0", "@babel/plugin-transform-modules-commonjs@^7.14.5": version "7.14.5" resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.5.tgz#7aaee0ea98283de94da98b28f8c35701429dad97" integrity sha512-en8GfBtgnydoao2PS+87mKyw62k02k7kJ9ltbKe0fXTHrQmG6QZZflYuGI1VVG7sVpx4E1n7KBpNlPb8m78J+A== @@ -1546,6 +1699,16 @@ "@babel/helper-simple-access" "^7.14.5" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-commonjs@^7.4.4": + version "7.15.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.15.4.tgz#8201101240eabb5a76c08ef61b2954f767b6b4c1" + integrity sha512-qg4DPhwG8hKp4BbVDvX1s8cohM8a6Bvptu4l6Iingq5rW+yRUAhe/YRup/YcW2zCOlrysEWVhftIcKzrEZv3sA== + dependencies: + "@babel/helper-module-transforms" "^7.15.4" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-simple-access" "^7.15.4" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-systemjs@^7.13.8": version "7.13.8" resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz#6d066ee2bff3c7b3d60bf28dec169ad993831ae3" @@ -2130,6 +2293,15 @@ "@babel/parser" "^7.14.5" "@babel/types" "^7.14.5" +"@babel/template@^7.15.4": + version "7.15.4" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" + integrity sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/parser" "^7.15.4" + "@babel/types" "^7.15.4" + "@babel/traverse@7.12.13": version "7.12.13" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz#689f0e4b4c08587ad26622832632735fb8c4e0c0" @@ -2174,6 +2346,21 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.15.4": + version "7.15.4" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz#ff8510367a144bfbff552d9e18e28f3e2889c22d" + integrity sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/generator" "^7.15.4" + "@babel/helper-function-name" "^7.15.4" + "@babel/helper-hoist-variables" "^7.15.4" + "@babel/helper-split-export-declaration" "^7.15.4" + "@babel/parser" "^7.15.4" + "@babel/types" "^7.15.4" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@7.12.13": version "7.12.13" resolved "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz#8be1aa8f2c876da11a9cf650c0ecf656913ad611" @@ -2199,6 +2386,14 @@ "@babel/helper-validator-identifier" "^7.14.9" to-fast-properties "^2.0.0" +"@babel/types@^7.15.4", "@babel/types@^7.15.6": + version "7.15.6" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz#99abdc48218b2881c058dd0a7ab05b99c9be758f" + integrity sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig== + dependencies: + "@babel/helper-validator-identifier" "^7.14.9" + to-fast-properties "^2.0.0" + "@backstage/catalog-client@^0.3.18": version "0.3.19" resolved "https://registry.npmjs.org/@backstage/catalog-client/-/catalog-client-0.3.19.tgz#109b0fde1cc2d9a306635f3775fc8f6174172b14" @@ -6270,7 +6465,7 @@ lz-string "^1.4.4" pretty-format "^26.6.2" -"@testing-library/jest-dom@^5.10.1", "@testing-library/jest-dom@^5.14.1": +"@testing-library/jest-dom@^5.10.1": version "5.14.1" resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.14.1.tgz#8501e16f1e55a55d675fe73eecee32cdaddb9766" integrity sha512-dfB7HVIgTNCxH22M1+KU6viG5of2ldoA5ly8Ar8xkezKHKXjRvznCdbMbqjYGgO2xjRbwnR+rR8MLUIqF3kKbQ== @@ -6421,7 +6616,15 @@ dependencies: "@babel/types" "^7.3.0" -"@types/body-parser@*", "@types/body-parser@1.19.0", "@types/body-parser@^1.19.0": +"@types/body-parser@*", "@types/body-parser@^1.19.0": + version "1.19.1" + resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz#0c0174c42a7d017b818303d4b5d969cb0b75929c" + integrity sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/body-parser@1.19.0": version "1.19.0" resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== @@ -22944,9 +23147,9 @@ react-hook-form@^7.12.2: integrity sha512-kcLDmSmlyLUFx2UU5bG/o4+3NeK753fhKodJa8gkplXohGkpAq0/p+TR24OWjZmkEc3ES7ppC5v5d6KUk+fJTA== react-hook-form@^7.13.0: - version "7.17.3" - resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.17.3.tgz#3ed85bed3a6aa4d013e0027a93799d668064c201" - integrity sha512-hUKOVtz6Wb319VJRl6OKV8vAhLo5P5raOQEG8lJZt/VXBMv6n2adgpIl1e+36rjX126Wq351ipM0goCgj4UUzQ== + version "7.17.4" + resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.17.4.tgz#232b6aaccddb91eb4a228ac20b154abd90866fdb" + integrity sha512-7XhbCr7d9fDC1TgcK/BUbt7D3q0VJMu7jPErfsa0JrxVjv/nni41xWdJcy0Zb7R+Np8OsCkQ2lMyloAtE3DLiQ== react-hot-loader@^4.12.21: version "4.13.0"