From 485a6c5f7b5f1dc39640014027bf8b6137d8b5c7 Mon Sep 17 00:00:00 2001 From: Paul Schultz Date: Tue, 4 Apr 2023 13:53:03 -0500 Subject: [PATCH 001/189] Finish TODO comment and refactor DefaultEntitiesCatalog.ts Signed-off-by: Paul Schultz --- .changeset/curly-rats-fold.md | 183 ++++++++++++++++++ .../src/service/DefaultEntitiesCatalog.ts | 109 ++++------- 2 files changed, 225 insertions(+), 67 deletions(-) create mode 100644 .changeset/curly-rats-fold.md diff --git a/.changeset/curly-rats-fold.md b/.changeset/curly-rats-fold.md new file mode 100644 index 0000000000..a5ea89b98e --- /dev/null +++ b/.changeset/curly-rats-fold.md @@ -0,0 +1,183 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +- Finished TODO to remove code snippet that was supposed to be removed in April 2022 +- Refactored `parsePagination()`, `stringifyPagination()`, and `addCondition()` to be more readable +- In `parseFilter()`, the `isNegationEntityFilter` check is earlier for faster recursion + +```diff +diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +index ba165f96af..2023c19e13 100644 +--- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts ++++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +@@ -14,11 +14,7 @@ + * limitations under the License. + */ + +-import { +- Entity, +- parseEntityRef, +- stringifyEntityRef, +-} from '@backstage/catalog-model'; ++import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; + import { InputError, NotFoundError } from '@backstage/errors'; + import { Knex } from 'knex'; + import { isEqual, chunk as lodashChunk } from 'lodash'; +@@ -64,43 +60,47 @@ const defaultSortField: EntityOrder = { + + const DEFAULT_LIMIT = 20; + +-function parsePagination(input?: EntityPagination): { +- limit?: number; +- offset?: number; +-} { ++function parsePagination(input?: EntityPagination): EntityPagination { + if (!input) { + return {}; + } + + let { limit, offset } = input; + +- if (input.after !== undefined) { +- let cursor; +- try { +- const json = Buffer.from(input.after, 'base64').toString('utf8'); +- cursor = JSON.parse(json); +- } catch { +- throw new InputError('Malformed after cursor, could not be parsed'); +- } +- if (cursor.limit !== undefined) { +- if (!Number.isInteger(cursor.limit)) { +- throw new InputError('Malformed after cursor, limit was not an number'); +- } +- limit = cursor.limit; ++ if (input.after === undefined) { ++ return { limit, offset }; ++ } ++ ++ let cursor; ++ try { ++ const json = Buffer.from(input.after, 'base64').toString('utf8'); ++ cursor = JSON.parse(json); ++ } catch { ++ throw new InputError('Malformed after cursor, could not be parsed'); ++ } ++ ++ if (cursor.limit !== undefined) { ++ if (!Number.isInteger(cursor.limit)) { ++ throw new InputError('Malformed after cursor, limit was not an number'); + } +- if (cursor.offset !== undefined) { +- if (!Number.isInteger(cursor.offset)) { +- throw new InputError('Malformed after cursor, offset was not a number'); +- } +- offset = cursor.offset; ++ limit = cursor.limit; ++ } ++ ++ if (cursor.offset !== undefined) { ++ if (!Number.isInteger(cursor.offset)) { ++ throw new InputError('Malformed after cursor, offset was not a number'); + } ++ offset = cursor.offset; + } + + return { limit, offset }; + } + +-function stringifyPagination(input: { limit: number; offset: number }) { +- const json = JSON.stringify({ limit: input.limit, offset: input.offset }); ++function stringifyPagination( ++ input: Required>, ++): string { ++ const { limit, offset } = input; ++ const json = JSON.stringify({ limit, offset }); + const base64 = Buffer.from(json, 'utf8').toString('base64'); + return base64; + } +@@ -111,24 +111,21 @@ function addCondition( + filter: EntitiesSearchFilter, + negate: boolean = false, + entityIdField = 'entity_id', +-) { ++): void { ++ const key = filter.key.toLowerCase(); ++ const values = filter.values?.map(v => v.toLowerCase()); ++ + // NOTE(freben): This used to be a set of OUTER JOIN, which may seem to + // make a lot of sense. However, it had abysmal performance on sqlite + // when datasets grew large, so we're using IN instead. + const matchQuery = db('search') + .select('search.entity_id') +- .where({ key: filter.key.toLowerCase() }) ++ .where({ key }) + .andWhere(function keyFilter() { +- if (filter.values) { +- if (filter.values.length === 1) { +- this.where({ value: filter.values[0].toLowerCase() }); +- } else { +- this.andWhere( +- 'value', +- 'in', +- filter.values.map(v => v.toLowerCase()), +- ); +- } ++ if (values?.length === 1) { ++ this.where({ value: values.at(0) }); ++ } else if (values) { ++ this.andWhere('value', 'in', values); + } + }); + queryBuilder.andWhere(entityIdField, negate ? 'not in' : 'in', matchQuery); +@@ -159,16 +156,16 @@ function parseFilter( + negate: boolean = false, + entityIdField = 'entity_id', + ): Knex.QueryBuilder { ++ if (isNegationEntityFilter(filter)) { ++ return parseFilter(filter.not, query, db, !negate, entityIdField); ++ } ++ + if (isEntitiesSearchFilter(filter)) { + return query.andWhere(function filterFunction() { + addCondition(this, db, filter, negate, entityIdField); + }); + } + +- if (isNegationEntityFilter(filter)) { +- return parseFilter(filter.not, query, db, !negate, entityIdField); +- } +- + return query[negate ? 'andWhereNot' : 'andWhere'](function filterFunction() { + if (isOrEntityFilter(filter)) { + for (const subFilter of filter.anyOf ?? []) { +@@ -274,28 +271,6 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { + entities = entities.map(e => request.fields!(e)); + } + +- // TODO(freben): This is added as a compatibility guarantee, until we can be +- // sure that all adopters have re-stitched their entities so that the new +- // targetRef field is present on them, and that they have stopped consuming +- // the now-removed old field +- // TODO(jhaals): Remove this in April 2022 +- for (const entity of entities) { +- if (entity.relations) { +- for (const relation of entity.relations as any) { +- if (!relation.targetRef && relation.target) { +- // This is the case where an old-form entity, not yet stitched with +- // the updated code, was in the database +- relation.targetRef = stringifyEntityRef(relation.target); +- } else if (!relation.target && relation.targetRef) { +- // This is the case where a new-form entity, stitched with the +- // updated code, was in the database but we still want to produce +- // the old data shape as well for compatibility reasons +- relation.target = parseEntityRef(relation.targetRef); +- } +- } +- } +- } +- + return { + entities, + pageInfo, +``` diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index ba165f96af..2023c19e13 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -14,11 +14,7 @@ * limitations under the License. */ -import { - Entity, - parseEntityRef, - stringifyEntityRef, -} from '@backstage/catalog-model'; +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { InputError, NotFoundError } from '@backstage/errors'; import { Knex } from 'knex'; import { isEqual, chunk as lodashChunk } from 'lodash'; @@ -64,43 +60,47 @@ const defaultSortField: EntityOrder = { const DEFAULT_LIMIT = 20; -function parsePagination(input?: EntityPagination): { - limit?: number; - offset?: number; -} { +function parsePagination(input?: EntityPagination): EntityPagination { if (!input) { return {}; } let { limit, offset } = input; - if (input.after !== undefined) { - let cursor; - try { - const json = Buffer.from(input.after, 'base64').toString('utf8'); - cursor = JSON.parse(json); - } catch { - throw new InputError('Malformed after cursor, could not be parsed'); + if (input.after === undefined) { + return { limit, offset }; + } + + let cursor; + try { + const json = Buffer.from(input.after, 'base64').toString('utf8'); + cursor = JSON.parse(json); + } catch { + throw new InputError('Malformed after cursor, could not be parsed'); + } + + if (cursor.limit !== undefined) { + if (!Number.isInteger(cursor.limit)) { + throw new InputError('Malformed after cursor, limit was not an number'); } - if (cursor.limit !== undefined) { - if (!Number.isInteger(cursor.limit)) { - throw new InputError('Malformed after cursor, limit was not an number'); - } - limit = cursor.limit; - } - if (cursor.offset !== undefined) { - if (!Number.isInteger(cursor.offset)) { - throw new InputError('Malformed after cursor, offset was not a number'); - } - offset = cursor.offset; + limit = cursor.limit; + } + + if (cursor.offset !== undefined) { + if (!Number.isInteger(cursor.offset)) { + throw new InputError('Malformed after cursor, offset was not a number'); } + offset = cursor.offset; } return { limit, offset }; } -function stringifyPagination(input: { limit: number; offset: number }) { - const json = JSON.stringify({ limit: input.limit, offset: input.offset }); +function stringifyPagination( + input: Required>, +): string { + const { limit, offset } = input; + const json = JSON.stringify({ limit, offset }); const base64 = Buffer.from(json, 'utf8').toString('base64'); return base64; } @@ -111,24 +111,21 @@ function addCondition( filter: EntitiesSearchFilter, negate: boolean = false, entityIdField = 'entity_id', -) { +): void { + const key = filter.key.toLowerCase(); + const values = filter.values?.map(v => v.toLowerCase()); + // NOTE(freben): This used to be a set of OUTER JOIN, which may seem to // make a lot of sense. However, it had abysmal performance on sqlite // when datasets grew large, so we're using IN instead. const matchQuery = db('search') .select('search.entity_id') - .where({ key: filter.key.toLowerCase() }) + .where({ key }) .andWhere(function keyFilter() { - if (filter.values) { - if (filter.values.length === 1) { - this.where({ value: filter.values[0].toLowerCase() }); - } else { - this.andWhere( - 'value', - 'in', - filter.values.map(v => v.toLowerCase()), - ); - } + if (values?.length === 1) { + this.where({ value: values.at(0) }); + } else if (values) { + this.andWhere('value', 'in', values); } }); queryBuilder.andWhere(entityIdField, negate ? 'not in' : 'in', matchQuery); @@ -159,16 +156,16 @@ function parseFilter( negate: boolean = false, entityIdField = 'entity_id', ): Knex.QueryBuilder { + if (isNegationEntityFilter(filter)) { + return parseFilter(filter.not, query, db, !negate, entityIdField); + } + if (isEntitiesSearchFilter(filter)) { return query.andWhere(function filterFunction() { addCondition(this, db, filter, negate, entityIdField); }); } - if (isNegationEntityFilter(filter)) { - return parseFilter(filter.not, query, db, !negate, entityIdField); - } - return query[negate ? 'andWhereNot' : 'andWhere'](function filterFunction() { if (isOrEntityFilter(filter)) { for (const subFilter of filter.anyOf ?? []) { @@ -274,28 +271,6 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { entities = entities.map(e => request.fields!(e)); } - // TODO(freben): This is added as a compatibility guarantee, until we can be - // sure that all adopters have re-stitched their entities so that the new - // targetRef field is present on them, and that they have stopped consuming - // the now-removed old field - // TODO(jhaals): Remove this in April 2022 - for (const entity of entities) { - if (entity.relations) { - for (const relation of entity.relations as any) { - if (!relation.targetRef && relation.target) { - // This is the case where an old-form entity, not yet stitched with - // the updated code, was in the database - relation.targetRef = stringifyEntityRef(relation.target); - } else if (!relation.target && relation.targetRef) { - // This is the case where a new-form entity, stitched with the - // updated code, was in the database but we still want to produce - // the old data shape as well for compatibility reasons - relation.target = parseEntityRef(relation.targetRef); - } - } - } - } - return { entities, pageInfo, From 293e6aee192adbfbd88cbd87bc32539892d32d7c Mon Sep 17 00:00:00 2001 From: Paul Schultz Date: Tue, 4 Apr 2023 14:31:35 -0500 Subject: [PATCH 002/189] Remove test for the TODO code snippet Signed-off-by: Paul Schultz --- .changeset/curly-rats-fold.md | 73 +++++++++++++++++++ .../service/DefaultEntitiesCatalog.test.ts | 58 --------------- 2 files changed, 73 insertions(+), 58 deletions(-) diff --git a/.changeset/curly-rats-fold.md b/.changeset/curly-rats-fold.md index a5ea89b98e..a20fc4d288 100644 --- a/.changeset/curly-rats-fold.md +++ b/.changeset/curly-rats-fold.md @@ -3,6 +3,7 @@ --- - Finished TODO to remove code snippet that was supposed to be removed in April 2022 +- Removed test for the TODO code snippet - Refactored `parsePagination()`, `stringifyPagination()`, and `addCondition()` to be more readable - In `parseFilter()`, the `isNegationEntityFilter` check is earlier for faster recursion @@ -181,3 +182,75 @@ index ba165f96af..2023c19e13 100644 entities, pageInfo, ``` + +```diff +diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +index 08ae295834..31d4d06971 100644 +--- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts ++++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +@@ -507,64 +507,6 @@ describe('DefaultEntitiesCatalog', () => { + }, + ); + +- it.each(databases.eachSupportedId())( +- 'should return both target and targetRef for entities', +- async databaseId => { +- await createDatabase(databaseId); +- await addEntity( +- { +- apiVersion: 'a', +- kind: 'k', +- metadata: { name: 'one' }, +- spec: {}, +- relations: [{ type: 'r', targetRef: 'x:y/z' } as any], +- }, +- [], +- ); +- await addEntity( +- { +- apiVersion: 'a', +- kind: 'k', +- metadata: { name: 'two' }, +- spec: {}, +- relations: [ +- { +- type: 'r', +- target: { kind: 'x', namespace: 'y', name: 'z' }, +- } as any, +- ], +- }, +- [], +- ); +- const catalog = new DefaultEntitiesCatalog({ +- database: knex, +- logger: getVoidLogger(), +- stitcher, +- }); +- +- const { entities } = await catalog.entities(); +- +- expect( +- entities.find(e => e.metadata.name === 'one')!.relations, +- ).toEqual([ +- { +- type: 'r', +- targetRef: 'x:y/z', +- target: { kind: 'x', namespace: 'y', name: 'z' }, +- }, +- ]); +- expect( +- entities.find(e => e.metadata.name === 'two')!.relations, +- ).toEqual([ +- { +- type: 'r', +- targetRef: 'x:y/z', +- target: { kind: 'x', namespace: 'y', name: 'z' }, +- }, +- ]); +- }, +- ); +- + it.each(databases.eachSupportedId())( + 'can order and combine with filtering, %p', + async databaseId => { +``` diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts index 08ae295834..31d4d06971 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts @@ -507,64 +507,6 @@ describe('DefaultEntitiesCatalog', () => { }, ); - it.each(databases.eachSupportedId())( - 'should return both target and targetRef for entities', - async databaseId => { - await createDatabase(databaseId); - await addEntity( - { - apiVersion: 'a', - kind: 'k', - metadata: { name: 'one' }, - spec: {}, - relations: [{ type: 'r', targetRef: 'x:y/z' } as any], - }, - [], - ); - await addEntity( - { - apiVersion: 'a', - kind: 'k', - metadata: { name: 'two' }, - spec: {}, - relations: [ - { - type: 'r', - target: { kind: 'x', namespace: 'y', name: 'z' }, - } as any, - ], - }, - [], - ); - const catalog = new DefaultEntitiesCatalog({ - database: knex, - logger: getVoidLogger(), - stitcher, - }); - - const { entities } = await catalog.entities(); - - expect( - entities.find(e => e.metadata.name === 'one')!.relations, - ).toEqual([ - { - type: 'r', - targetRef: 'x:y/z', - target: { kind: 'x', namespace: 'y', name: 'z' }, - }, - ]); - expect( - entities.find(e => e.metadata.name === 'two')!.relations, - ).toEqual([ - { - type: 'r', - targetRef: 'x:y/z', - target: { kind: 'x', namespace: 'y', name: 'z' }, - }, - ]); - }, - ); - it.each(databases.eachSupportedId())( 'can order and combine with filtering, %p', async databaseId => { From e0a93a048d2e3aad75e084c45c55a33efc405a91 Mon Sep 17 00:00:00 2001 From: Paul Schultz Date: Wed, 19 Apr 2023 12:33:50 -0500 Subject: [PATCH 003/189] Remove breaking changes Signed-off-by: Paul Schultz --- .changeset/curly-rats-fold.md | 106 +----------------- .../service/DefaultEntitiesCatalog.test.ts | 58 ++++++++++ .../src/service/DefaultEntitiesCatalog.ts | 28 ++++- 3 files changed, 86 insertions(+), 106 deletions(-) diff --git a/.changeset/curly-rats-fold.md b/.changeset/curly-rats-fold.md index a20fc4d288..072f3dcc6c 100644 --- a/.changeset/curly-rats-fold.md +++ b/.changeset/curly-rats-fold.md @@ -2,10 +2,7 @@ '@backstage/plugin-catalog-backend': patch --- -- Finished TODO to remove code snippet that was supposed to be removed in April 2022 -- Removed test for the TODO code snippet -- Refactored `parsePagination()`, `stringifyPagination()`, and `addCondition()` to be more readable -- In `parseFilter()`, the `isNegationEntityFilter` check is earlier for faster recursion +- Internal refactoring for performance in the service handlers ```diff diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -152,105 +149,4 @@ index ba165f96af..2023c19e13 100644 return query[negate ? 'andWhereNot' : 'andWhere'](function filterFunction() { if (isOrEntityFilter(filter)) { for (const subFilter of filter.anyOf ?? []) { -@@ -274,28 +271,6 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { - entities = entities.map(e => request.fields!(e)); - } - -- // TODO(freben): This is added as a compatibility guarantee, until we can be -- // sure that all adopters have re-stitched their entities so that the new -- // targetRef field is present on them, and that they have stopped consuming -- // the now-removed old field -- // TODO(jhaals): Remove this in April 2022 -- for (const entity of entities) { -- if (entity.relations) { -- for (const relation of entity.relations as any) { -- if (!relation.targetRef && relation.target) { -- // This is the case where an old-form entity, not yet stitched with -- // the updated code, was in the database -- relation.targetRef = stringifyEntityRef(relation.target); -- } else if (!relation.target && relation.targetRef) { -- // This is the case where a new-form entity, stitched with the -- // updated code, was in the database but we still want to produce -- // the old data shape as well for compatibility reasons -- relation.target = parseEntityRef(relation.targetRef); -- } -- } -- } -- } -- - return { - entities, - pageInfo, -``` - -```diff -diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts -index 08ae295834..31d4d06971 100644 ---- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts -+++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts -@@ -507,64 +507,6 @@ describe('DefaultEntitiesCatalog', () => { - }, - ); - -- it.each(databases.eachSupportedId())( -- 'should return both target and targetRef for entities', -- async databaseId => { -- await createDatabase(databaseId); -- await addEntity( -- { -- apiVersion: 'a', -- kind: 'k', -- metadata: { name: 'one' }, -- spec: {}, -- relations: [{ type: 'r', targetRef: 'x:y/z' } as any], -- }, -- [], -- ); -- await addEntity( -- { -- apiVersion: 'a', -- kind: 'k', -- metadata: { name: 'two' }, -- spec: {}, -- relations: [ -- { -- type: 'r', -- target: { kind: 'x', namespace: 'y', name: 'z' }, -- } as any, -- ], -- }, -- [], -- ); -- const catalog = new DefaultEntitiesCatalog({ -- database: knex, -- logger: getVoidLogger(), -- stitcher, -- }); -- -- const { entities } = await catalog.entities(); -- -- expect( -- entities.find(e => e.metadata.name === 'one')!.relations, -- ).toEqual([ -- { -- type: 'r', -- targetRef: 'x:y/z', -- target: { kind: 'x', namespace: 'y', name: 'z' }, -- }, -- ]); -- expect( -- entities.find(e => e.metadata.name === 'two')!.relations, -- ).toEqual([ -- { -- type: 'r', -- targetRef: 'x:y/z', -- target: { kind: 'x', namespace: 'y', name: 'z' }, -- }, -- ]); -- }, -- ); -- - it.each(databases.eachSupportedId())( - 'can order and combine with filtering, %p', - async databaseId => { ``` diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts index 31d4d06971..08ae295834 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts @@ -507,6 +507,64 @@ describe('DefaultEntitiesCatalog', () => { }, ); + it.each(databases.eachSupportedId())( + 'should return both target and targetRef for entities', + async databaseId => { + await createDatabase(databaseId); + await addEntity( + { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'one' }, + spec: {}, + relations: [{ type: 'r', targetRef: 'x:y/z' } as any], + }, + [], + ); + await addEntity( + { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'two' }, + spec: {}, + relations: [ + { + type: 'r', + target: { kind: 'x', namespace: 'y', name: 'z' }, + } as any, + ], + }, + [], + ); + const catalog = new DefaultEntitiesCatalog({ + database: knex, + logger: getVoidLogger(), + stitcher, + }); + + const { entities } = await catalog.entities(); + + expect( + entities.find(e => e.metadata.name === 'one')!.relations, + ).toEqual([ + { + type: 'r', + targetRef: 'x:y/z', + target: { kind: 'x', namespace: 'y', name: 'z' }, + }, + ]); + expect( + entities.find(e => e.metadata.name === 'two')!.relations, + ).toEqual([ + { + type: 'r', + targetRef: 'x:y/z', + target: { kind: 'x', namespace: 'y', name: 'z' }, + }, + ]); + }, + ); + it.each(databases.eachSupportedId())( 'can order and combine with filtering, %p', async databaseId => { diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index 2023c19e13..9e78d7cd2c 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; +import { + Entity, + parseEntityRef, + stringifyEntityRef, +} from '@backstage/catalog-model'; import { InputError, NotFoundError } from '@backstage/errors'; import { Knex } from 'knex'; import { isEqual, chunk as lodashChunk } from 'lodash'; @@ -271,6 +275,28 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { entities = entities.map(e => request.fields!(e)); } + // TODO(freben): This is added as a compatibility guarantee, until we can be + // sure that all adopters have re-stitched their entities so that the new + // targetRef field is present on them, and that they have stopped consuming + // the now-removed old field + // TODO(jhaals): Remove this in April 2022 + for (const entity of entities) { + if (entity.relations) { + for (const relation of entity.relations as any) { + if (!relation.targetRef && relation.target) { + // This is the case where an old-form entity, not yet stitched with + // the updated code, was in the database + relation.targetRef = stringifyEntityRef(relation.target); + } else if (!relation.target && relation.targetRef) { + // This is the case where a new-form entity, stitched with the + // updated code, was in the database but we still want to produce + // the old data shape as well for compatibility reasons + relation.target = parseEntityRef(relation.targetRef); + } + } + } + } + return { entities, pageInfo, From 6cd4177c6415712dd9a22a959fbf09b8a496882c Mon Sep 17 00:00:00 2001 From: "antonio.bergas" Date: Fri, 28 Apr 2023 14:51:57 +0200 Subject: [PATCH 004/189] =?UTF-8?q?=E2=9C=A8=20feat(pluginsFilter):=20adde?= =?UTF-8?q?d=20dropdown=20filter=20for=20plugins=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: antonio.bergas --- .../pluginsFilter/pluginsFilter.tsx | 36 ++++ microsite/src/pages/plugins/index.tsx | 171 +++++++++++++----- .../src/pages/plugins/plugins.module.scss | 35 +++- microsite/src/util/types.ts | 4 + 4 files changed, 202 insertions(+), 44 deletions(-) create mode 100644 microsite/src/components/pluginsFilter/pluginsFilter.tsx create mode 100644 microsite/src/util/types.ts diff --git a/microsite/src/components/pluginsFilter/pluginsFilter.tsx b/microsite/src/components/pluginsFilter/pluginsFilter.tsx new file mode 100644 index 0000000000..19b80b9f14 --- /dev/null +++ b/microsite/src/components/pluginsFilter/pluginsFilter.tsx @@ -0,0 +1,36 @@ +import { ChipCategory } from '@site/src/util/types'; +import React from 'react'; + +type Props = { + categories: ChipCategory[]; + handleChipClick: (name: string) => void; +}; + +const PluginsFilter = ({ categories, handleChipClick }: Props) => { + return ( +
+ +
    + {categories.map(chip => { + return ( +
  • +
    + handleChipClick(chip.name)} + /> + {chip.name} +
    +
  • + ); + })} +
+
+ ); +}; + +export default PluginsFilter; diff --git a/microsite/src/pages/plugins/index.tsx b/microsite/src/pages/plugins/index.tsx index 280a165daa..2349e391fa 100644 --- a/microsite/src/pages/plugins/index.tsx +++ b/microsite/src/pages/plugins/index.tsx @@ -1,13 +1,13 @@ import Link from '@docusaurus/Link'; import Layout from '@theme/Layout'; import clsx from 'clsx'; -import React from 'react'; - +import React, { useState } from 'react'; import { IPluginData, PluginCard } from './_pluginCard'; import pluginsStyles from './plugins.module.scss'; +import { ChipCategory } from '@site/src/util/types'; import { truncateDescription } from '@site/src/util/truncateDescription'; +import PluginsFilter from '@site/src/components/pluginsFilter/pluginsFilter'; -//#region Plugin data import const pluginsContext = require.context( '../../../data/plugins', false, @@ -29,50 +29,135 @@ const plugins = pluginsContext.keys().reduce( plugins.corePlugins.sort((a, b) => a.order - b.order); plugins.otherPlugins.sort((a, b) => a.order - b.order); -//#endregion -const Plugins = () => ( - -
-
-
-

Plugin Marketplace

+const Plugins = () => { + const allCategories: ChipCategory[] = []; + plugins.corePlugins.concat(plugins.otherPlugins).forEach(pluginData => { + const index = allCategories.findIndex( + chip => chip.name === pluginData.category, + ); + if (index === -1) { + allCategories.push({ + name: pluginData.category, + isSelected: false, + }); + } + }); -

- Open source plugins that you can add to your Backstage deployment. - Learn how to build a plugin. -

+ const [categories, setCategories] = useState(allCategories); + const [selectedCategories, setSelectedCategories] = useState([]); + const [showCoreFeaturesHeader, setShowCoreFeaturesHeader] = useState(true); + const [showOtherPluginsHeader, setShowOtherPluginsHeader] = useState(true); + + const handleChipClick = (categoryName: string) => { + console.log(categoryName); + const category = categories.find( + category => category.name === categoryName, + ); + const isSelected = category?.isSelected || false; + + let newSelectedCategories = selectedCategories; + + if (isSelected) { + newSelectedCategories = selectedCategories.filter( + c => c !== categoryName, + ); + } else { + newSelectedCategories.push(categoryName); + } + + setSelectedCategories(newSelectedCategories); + + const newCategories = categories.map(category => { + if (category.name === categoryName) { + return { ...category, isSelected: !isSelected }; + } + return category; + }); + + setCategories(newCategories); + + if (!newSelectedCategories.includes('Core Feature')) { + setShowCoreFeaturesHeader(false); + } else { + setShowCoreFeaturesHeader(true); + } + + if ( + newSelectedCategories.length === 1 && + newSelectedCategories[0] === 'Core Feature' + ) { + setShowOtherPluginsHeader(false); + } else { + setShowOtherPluginsHeader(true); + } + + if (newSelectedCategories.length === 0) { + setShowOtherPluginsHeader(true); + setShowCoreFeaturesHeader(true); + } + }; + + return ( + +
+
+
+

Plugin Marketplace

+ +

+ Open source plugins that you can add to your Backstage deployment. + Learn how to build a plugin. +

+
+ + + Add to Marketplace +
- - Add to Marketplace - +
+ + + + {showCoreFeaturesHeader &&

Core Features

} + +
+ {plugins.corePlugins + .filter( + pluginData => + !selectedCategories.length || + selectedCategories.includes(pluginData.category), + ) + .map(pluginData => ( + + ))} +
+ + {showOtherPluginsHeader &&

All Plugins

} + +
+ {plugins.otherPlugins + .filter( + pluginData => + !selectedCategories.length || + selectedCategories.includes(pluginData.category), + ) + .map(pluginData => ( + + ))} +
- -
- -

Core Features

- -
- {plugins.corePlugins.map(pluginData => ( - - ))} -
- -

All Plugins

- -
- {plugins.otherPlugins.map(pluginData => ( - - ))} -
-
- -); + + ); +}; export default Plugins; diff --git a/microsite/src/pages/plugins/plugins.module.scss b/microsite/src/pages/plugins/plugins.module.scss index bb08c2fd33..2f928fcbf0 100644 --- a/microsite/src/pages/plugins/plugins.module.scss +++ b/microsite/src/pages/plugins/plugins.module.scss @@ -28,10 +28,43 @@ :global(.pluginsContainer) { gap: 1rem; display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(250px, 300px)); } :global(.fit-content) { width: fit-content; } + + :global(.dropdown) { + float: right; + } + + :global(.button--info) { + background-color: transparent; + border-color: var(--ifm-color-primary); + color: var(--ifm-color-primary); + min-width: 250px; + } + + :global(label.dropdown__item) { + display: flex; + align-items: center; + cursor: pointer; + margin-top: 5px; + + &:hover { + background-color: var(--ifm-color-primary); + color: black; + } + } + + :global(.dropdown__label) { + font-size: 15px; + cursor: pointer; + } + + :global(.dropdown__checkbox) { + margin-right: 10px; + cursor: pointer; + } } diff --git a/microsite/src/util/types.ts b/microsite/src/util/types.ts new file mode 100644 index 0000000000..fba7c0ba46 --- /dev/null +++ b/microsite/src/util/types.ts @@ -0,0 +1,4 @@ +export type ChipCategory = { + name: string; + isSelected: boolean; +}; From 2c5661f38996250de759d6bb22129fa9536aa457 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Fri, 28 Apr 2023 16:36:35 +0200 Subject: [PATCH 005/189] Allow endpoint to be configured for sqs event publisher Signed-off-by: Scott Guymer --- .changeset/cold-laws-turn.md | 5 +++++ .../src/publisher/AwsSqsConsumingEventPublisher.ts | 6 +++++- .../events-backend-module-aws-sqs/src/publisher/config.ts | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .changeset/cold-laws-turn.md diff --git a/.changeset/cold-laws-turn.md b/.changeset/cold-laws-turn.md new file mode 100644 index 0000000000..6220f088db --- /dev/null +++ b/.changeset/cold-laws-turn.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-events-backend-module-aws-sqs': minor +--- + +Allow endpoint configuration for sqs, enabling use of localstack for testing. diff --git a/plugins/events-backend-module-aws-sqs/src/publisher/AwsSqsConsumingEventPublisher.ts b/plugins/events-backend-module-aws-sqs/src/publisher/AwsSqsConsumingEventPublisher.ts index e649e2ee83..4f4e6ea55d 100644 --- a/plugins/events-backend-module-aws-sqs/src/publisher/AwsSqsConsumingEventPublisher.ts +++ b/plugins/events-backend-module-aws-sqs/src/publisher/AwsSqsConsumingEventPublisher.ts @@ -68,7 +68,11 @@ export class AwsSqsConsumingEventPublisher implements EventPublisher { VisibilityTimeout: config.visibilityTimeout?.as('seconds'), WaitTimeSeconds: config.pollingWaitTime.as('seconds'), }; - this.sqs = new SQSClient({ region: config.region }); + + this.sqs = new SQSClient({ + region: config.region, + endpoint: config.endpoint, + }); this.queueUrl = config.queueUrl; this.taskTimeoutSeconds = config.timeout.as('seconds'); diff --git a/plugins/events-backend-module-aws-sqs/src/publisher/config.ts b/plugins/events-backend-module-aws-sqs/src/publisher/config.ts index c6b74ea8d3..47cd82857c 100644 --- a/plugins/events-backend-module-aws-sqs/src/publisher/config.ts +++ b/plugins/events-backend-module-aws-sqs/src/publisher/config.ts @@ -31,6 +31,7 @@ export interface AwsSqsEventSourceConfig { topic: string; visibilityTimeout?: Duration; waitTimeAfterEmptyReceive: Duration; + endpoint?: string; } // TODO(pjungermann): validation could be improved similar to `convertToHumanDuration` at @backstage/backend-tasks From b10f3e365585e1b113b1b785afb89f3d043de338 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Fri, 28 Apr 2023 16:56:28 +0200 Subject: [PATCH 006/189] Add config mapping Signed-off-by: Scott Guymer --- .../events-backend-module-aws-sqs/src/publisher/config.test.ts | 2 ++ plugins/events-backend-module-aws-sqs/src/publisher/config.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/plugins/events-backend-module-aws-sqs/src/publisher/config.test.ts b/plugins/events-backend-module-aws-sqs/src/publisher/config.test.ts index 1fa9df4ce3..6d7a08d7a7 100644 --- a/plugins/events-backend-module-aws-sqs/src/publisher/config.test.ts +++ b/plugins/events-backend-module-aws-sqs/src/publisher/config.test.ts @@ -79,6 +79,7 @@ describe('readConfig', () => { url: 'https://fake1.queue.url', visibilityTimeout: { minutes: 5 }, waitTime: { seconds: 10 }, + endpoint: 'https://fake.endpoint.url', }, timeout: { minutes: 5 }, waitTimeAfterEmptyReceive: { seconds: 30 }, @@ -97,6 +98,7 @@ describe('readConfig', () => { expect(publisherConfigs[0].topic).toEqual('fake1'); expect(publisherConfigs[0].region).toEqual('eu-west-1'); expect(publisherConfigs[0].queueUrl).toEqual('https://fake1.queue.url'); + expect(publisherConfigs[0].endpoint).toEqual('https://fake.endpoint.url'); expect(publisherConfigs[0].pollingWaitTime.as('seconds')).toBe(10); expect(publisherConfigs[0].timeout.as('seconds')).toBe(300); expect(publisherConfigs[0].waitTimeAfterEmptyReceive.as('seconds')).toBe( diff --git a/plugins/events-backend-module-aws-sqs/src/publisher/config.ts b/plugins/events-backend-module-aws-sqs/src/publisher/config.ts index 47cd82857c..eeb650ff6c 100644 --- a/plugins/events-backend-module-aws-sqs/src/publisher/config.ts +++ b/plugins/events-backend-module-aws-sqs/src/publisher/config.ts @@ -75,6 +75,7 @@ export function readConfig(config: Config): AwsSqsEventSourceConfig[] { } const queueUrl = topicConfig.getString('queue.url'); const region = topicConfig.getString('queue.region'); + const endpoint = topicConfig.getOptionalString('queue.endpoint'); const visibilityTimeout = readOptionalDuration( topicConfig, 'queue.visibilityTimeout', @@ -107,6 +108,7 @@ export function readConfig(config: Config): AwsSqsEventSourceConfig[] { return { pollingWaitTime, queueUrl, + endpoint, region, timeout, topic, From 1d5e42655cdbd4f23b49fbb06e87f29557659f2a Mon Sep 17 00:00:00 2001 From: Alex Crome Date: Mon, 1 May 2023 10:24:22 +0100 Subject: [PATCH 007/189] Correct command to create new plugins in create-app readme Signed-off-by: Alex Crome --- .changeset/chilled-goats-jump.md | 5 +++++ packages/create-app/templates/default-app/plugins/README.md | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/chilled-goats-jump.md diff --git a/.changeset/chilled-goats-jump.md b/.changeset/chilled-goats-jump.md new file mode 100644 index 0000000000..3dd152712a --- /dev/null +++ b/.changeset/chilled-goats-jump.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Correct command to create new plugins diff --git a/packages/create-app/templates/default-app/plugins/README.md b/packages/create-app/templates/default-app/plugins/README.md index 58dc32cb08..d7865fdba3 100644 --- a/packages/create-app/templates/default-app/plugins/README.md +++ b/packages/create-app/templates/default-app/plugins/README.md @@ -4,6 +4,6 @@ This is where your own plugins and their associated modules live, each in a separate folder of its own. If you want to create a new plugin here, go to your project root directory, run -the command `yarn backstage-cli create`, and follow the on-screen instructions. +the command `yarn new`, and follow the on-screen instructions. You can also check out existing plugins on [the plugin marketplace](https://backstage.io/plugins)! From cf95c5137c940470f3bda7fb91820ad5bc460a0a Mon Sep 17 00:00:00 2001 From: Tim Soslow Date: Mon, 1 May 2023 12:39:54 -0500 Subject: [PATCH 008/189] Configuration for action column in Jenkins table Signed-off-by: Tim Soslow --- .changeset/real-baboons-vanish.md | 5 +++ plugins/jenkins/README.md | 18 +++++++++++ plugins/jenkins/api-report.md | 2 +- .../src/assets/customize_action_view.png | Bin 0 -> 52575 bytes .../BuildsPage/lib/CITable/CITable.tsx | 18 +++++++++++ plugins/jenkins/src/options.ts | 29 ++++++++++++++++++ plugins/jenkins/src/plugin.ts | 9 ++++++ 7 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 .changeset/real-baboons-vanish.md create mode 100644 plugins/jenkins/src/assets/customize_action_view.png create mode 100644 plugins/jenkins/src/options.ts diff --git a/.changeset/real-baboons-vanish.md b/.changeset/real-baboons-vanish.md new file mode 100644 index 0000000000..93c30d8389 --- /dev/null +++ b/.changeset/real-baboons-vanish.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-jenkins': minor +--- + +Added configuration for the action column in Jenkins CI/CD table. It can be set to rebuild, view, or replay. diff --git a/plugins/jenkins/README.md b/plugins/jenkins/README.md index 2a0ec451c8..12c6a197da 100644 --- a/plugins/jenkins/README.md +++ b/plugins/jenkins/README.md @@ -86,6 +86,24 @@ spec: 8. Click the component in the catalog. You should now see Jenkins builds, and a last build result for your master build. +## Customize Action + +You can customize the action in the CI/CD table. + +| Action | Description | +| ----------------- | ----------------------- | +| rebuild (default) | Run new build with API | +| view | link to view build page | +| replay | link to start replay | + +```yaml +jenkinsPlugin.__experimentalReconfigure({ + tableAction: 'view', +}); +``` + +Customized action - view + ## Features - View all runs inside a folder diff --git a/plugins/jenkins/api-report.md b/plugins/jenkins/api-report.md index a2ae16988b..bcba48e4b2 100644 --- a/plugins/jenkins/api-report.md +++ b/plugins/jenkins/api-report.md @@ -98,7 +98,7 @@ const jenkinsPlugin: BackstagePlugin< entityContent: RouteRef; }, {}, - {} + JenkinsInputPluginOptions >; export { jenkinsPlugin }; export { jenkinsPlugin as plugin }; diff --git a/plugins/jenkins/src/assets/customize_action_view.png b/plugins/jenkins/src/assets/customize_action_view.png new file mode 100644 index 0000000000000000000000000000000000000000..8be43c5971e6f764abd7d0c010dd967784271828 GIT binary patch literal 52575 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 { if (count !== 0) { @@ -202,6 +204,22 @@ const generatedColumns: TableColumn[] = [ } }; + const { tableAction } = useJenkinsPluginOptions(); + + if (tableAction === 'view') { + return row.lastBuild?.url ? ( + + + + ) : null; + } else if (tableAction === 'replay') { + return row.lastBuild?.url ? ( + + + + ) : null; + } + return ( <> diff --git a/plugins/jenkins/src/options.ts b/plugins/jenkins/src/options.ts new file mode 100644 index 0000000000..f78e6a75e9 --- /dev/null +++ b/plugins/jenkins/src/options.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2020 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. + */ + +import { usePluginOptions } from '@backstage/core-plugin-api/alpha'; + +export type JenkinsPluginOptions = { + tableAction: string; +}; + +/** @ignore */ +export type JenkinsInputPluginOptions = { + tableAction: string; +}; + +export const useJenkinsPluginOptions = () => + usePluginOptions(); diff --git a/plugins/jenkins/src/plugin.ts b/plugins/jenkins/src/plugin.ts index 7dbb6bfde1..ddef0a7dfd 100644 --- a/plugins/jenkins/src/plugin.ts +++ b/plugins/jenkins/src/plugin.ts @@ -25,6 +25,7 @@ import { identityApiRef, } from '@backstage/core-plugin-api'; import { JenkinsClient, jenkinsApiRef } from './api'; +import { JenkinsInputPluginOptions, JenkinsPluginOptions } from './options'; /** @public */ export const rootRouteRef = createRouteRef({ @@ -52,6 +53,14 @@ export const jenkinsPlugin = createPlugin({ routes: { entityContent: rootRouteRef, }, + __experimentalConfigure( + options?: JenkinsInputPluginOptions, + ): JenkinsPluginOptions { + const defaultOptions = { + tableAction: 'rebuild', + }; + return { ...defaultOptions, ...options }; + }, }); /** @public */ From 3d72bdb41c7e59f6f44e587729ae20e095423933 Mon Sep 17 00:00:00 2001 From: Andrew Ochsner Date: Tue, 2 May 2023 13:45:16 -0500 Subject: [PATCH 009/189] upgrade aws-sdk to v3 for search-backend-module-elasticsearch This also enables opensearch serverless support Signed-off-by: Andrew Ochsner --- .changeset/fluffy-crabs-reply.md | 5 + .../api-report.md | 8 +- .../package.json | 6 +- .../src/engines/ElasticSearchClientOptions.ts | 2 + .../ElasticSearchClientWrapper.test.ts | 1 + .../src/engines/ElasticSearchSearchEngine.ts | 24 +- yarn.lock | 242 ++++++++++++++++-- 7 files changed, 256 insertions(+), 32 deletions(-) create mode 100644 .changeset/fluffy-crabs-reply.md diff --git a/.changeset/fluffy-crabs-reply.md b/.changeset/fluffy-crabs-reply.md new file mode 100644 index 0000000000..5570ef0492 --- /dev/null +++ b/.changeset/fluffy-crabs-reply.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-module-elasticsearch': minor +--- + +Upgrade to aws-sdk v3 and include Opensearch Serverless support diff --git a/plugins/search-backend-module-elasticsearch/api-report.md b/plugins/search-backend-module-elasticsearch/api-report.md index c7add1e8cb..b567414b22 100644 --- a/plugins/search-backend-module-elasticsearch/api-report.md +++ b/plugins/search-backend-module-elasticsearch/api-report.md @@ -8,8 +8,8 @@ import { ApiResponse } from '@opensearch-project/opensearch'; import { ApiResponse as ApiResponse_2 } from '@elastic/elasticsearch'; import { BatchSearchEngineIndexer } from '@backstage/plugin-search-backend-node'; -import { BulkHelper } from '@opensearch-project/opensearch/lib/Helpers'; -import { BulkStats } from '@opensearch-project/opensearch/lib/Helpers'; +import { BulkHelper } from '@elastic/elasticsearch/lib/Helpers'; +import { BulkStats } from '@elastic/elasticsearch/lib/Helpers'; import { Config } from '@backstage/config'; import type { ConnectionOptions } from 'tls'; import { IndexableDocument } from '@backstage/plugin-search-common'; @@ -424,6 +424,10 @@ export interface OpenSearchElasticSearchClientOptions nodes?: string | string[] | OpenSearchNodeOptions | OpenSearchNodeOptions[]; // (undocumented) provider?: 'aws' | 'opensearch'; + // (undocumented) + region?: string; + // (undocumented) + service?: 'es' | 'aoss'; } // @public (undocumented) diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index 73c3a5d7dc..76f3265c31 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -36,15 +36,15 @@ "clean": "backstage-cli package clean" }, "dependencies": { + "@aws-sdk/credential-provider-node": "^3.321.1", "@backstage/backend-common": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", "@backstage/plugin-search-backend-node": "workspace:^", "@backstage/plugin-search-common": "workspace:^", "@elastic/elasticsearch": "^7.13.0", - "@opensearch-project/opensearch": "^2.0.0", - "aws-os-connection": "^0.2.0", - "aws-sdk": "^2.948.0", + "@opensearch-project/opensearch": "^2.2.1", + "aws4": "^1.12.0", "elastic-builder": "^2.16.0", "lodash": "^4.17.21", "uuid": "^8.3.2", diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientOptions.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientOptions.ts index 8cb2f67fce..2974f03331 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientOptions.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientOptions.ts @@ -51,6 +51,8 @@ export type ElasticSearchClientOptions = export interface OpenSearchElasticSearchClientOptions extends BaseElasticSearchClientOptions { provider?: 'aws' | 'opensearch'; + region?: string; + service?: 'es' | 'aoss'; auth?: OpenSearchAuth; connection?: OpenSearchConnectionConstructor; node?: string | string[] | OpenSearchNodeOptions | OpenSearchNodeOptions[]; diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientWrapper.test.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientWrapper.test.ts index 7355daf3b9..620d894735 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientWrapper.test.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientWrapper.test.ts @@ -229,6 +229,7 @@ describe('ElasticSearchClientWrapper', () => { osOptions = { provider: 'aws', node: 'https://my-es-cluster.eu-west-1.es.amazonaws.com', + region: 'eu-west-1', // todo(backstage/techdocs-core): Remove the following ts-ignore when // @short.io/opensearch-mock is updated to work w/opensearch >= 2.0.0 // @ts-ignore diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts index 3294ca9d33..a13fbaf92a 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts @@ -21,9 +21,11 @@ import { SearchEngine, SearchQuery, } from '@backstage/plugin-search-common'; -import { awsGetCredentials, createAWSConnection } from 'aws-os-connection'; import { isEmpty, isNumber, isNaN as nan } from 'lodash'; +import { defaultProvider } from '@aws-sdk/credential-provider-node'; // V3 SDK. +import { AwsSigv4Signer } from '@opensearch-project/opensearch/aws'; +import { RequestSigner } from 'aws4'; import { Config } from '@backstage/config'; import { ElasticSearchClientOptions } from './ElasticSearchClientOptions'; import { ElasticSearchClientWrapper } from './ElasticSearchClientWrapper'; @@ -462,15 +464,14 @@ export async function createElasticSearchClientOptions( }; } if (config.getOptionalString('provider') === 'aws') { - const awsCredentials = await awsGetCredentials(); - const AWSConnection = createAWSConnection(awsCredentials); + const requestSigner = new RequestSigner(config.getString('node')); + const service = + config.getOptionalString('service') ?? requestSigner.service; + if (service !== 'es' && service !== 'aoss') + throw new Error(`Unrecognized serivce type: ${service}`); return { provider: 'aws', - // todo(backstage/techdocs-core): Remove the following ts-ignore when - // aws-os-connection is updated to work with opensearch >= 2.0.0 - // @ts-ignore node: config.getString('node'), - ...AWSConnection, ...(sslConfig ? { ssl: { @@ -479,6 +480,15 @@ export async function createElasticSearchClientOptions( }, } : {}), + ...AwsSigv4Signer({ + region: config.getOptionalString('region') ?? requestSigner.region, // for backwards compatibility + service: service, + getCredentials: () => { + // Any other method to acquire a new Credentials object can be used. + const credentialsProvider = defaultProvider(); + return credentialsProvider(); + }, + }), }; } if (config.getOptionalString('provider') === 'opensearch') { diff --git a/yarn.lock b/yarn.lock index 0bd97d81f8..27ee93b8ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -741,6 +741,46 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/client-sso-oidc@npm:3.321.1": + version: 3.321.1 + resolution: "@aws-sdk/client-sso-oidc@npm:3.321.1" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/config-resolver": 3.310.0 + "@aws-sdk/fetch-http-handler": 3.310.0 + "@aws-sdk/hash-node": 3.310.0 + "@aws-sdk/invalid-dependency": 3.310.0 + "@aws-sdk/middleware-content-length": 3.310.0 + "@aws-sdk/middleware-endpoint": 3.310.0 + "@aws-sdk/middleware-host-header": 3.310.0 + "@aws-sdk/middleware-logger": 3.310.0 + "@aws-sdk/middleware-recursion-detection": 3.310.0 + "@aws-sdk/middleware-retry": 3.310.0 + "@aws-sdk/middleware-serde": 3.310.0 + "@aws-sdk/middleware-stack": 3.310.0 + "@aws-sdk/middleware-user-agent": 3.319.0 + "@aws-sdk/node-config-provider": 3.310.0 + "@aws-sdk/node-http-handler": 3.321.1 + "@aws-sdk/protocol-http": 3.310.0 + "@aws-sdk/smithy-client": 3.316.0 + "@aws-sdk/types": 3.310.0 + "@aws-sdk/url-parser": 3.310.0 + "@aws-sdk/util-base64": 3.310.0 + "@aws-sdk/util-body-length-browser": 3.310.0 + "@aws-sdk/util-body-length-node": 3.310.0 + "@aws-sdk/util-defaults-mode-browser": 3.316.0 + "@aws-sdk/util-defaults-mode-node": 3.316.0 + "@aws-sdk/util-endpoints": 3.319.0 + "@aws-sdk/util-retry": 3.310.0 + "@aws-sdk/util-user-agent-browser": 3.310.0 + "@aws-sdk/util-user-agent-node": 3.310.0 + "@aws-sdk/util-utf8": 3.310.0 + tslib: ^2.5.0 + checksum: 25653c4d788b1cf95fa1246e82c6be4eba588bc5ff150c2b6b7495b73b08ad89efa75a9d52312079326feea56ebfd8f715150bd4a1bcf59dd2e0a282397ff09f + languageName: node + linkType: hard + "@aws-sdk/client-sso@npm:3.310.0": version: 3.310.0 resolution: "@aws-sdk/client-sso@npm:3.310.0" @@ -781,6 +821,46 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/client-sso@npm:3.321.1": + version: 3.321.1 + resolution: "@aws-sdk/client-sso@npm:3.321.1" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/config-resolver": 3.310.0 + "@aws-sdk/fetch-http-handler": 3.310.0 + "@aws-sdk/hash-node": 3.310.0 + "@aws-sdk/invalid-dependency": 3.310.0 + "@aws-sdk/middleware-content-length": 3.310.0 + "@aws-sdk/middleware-endpoint": 3.310.0 + "@aws-sdk/middleware-host-header": 3.310.0 + "@aws-sdk/middleware-logger": 3.310.0 + "@aws-sdk/middleware-recursion-detection": 3.310.0 + "@aws-sdk/middleware-retry": 3.310.0 + "@aws-sdk/middleware-serde": 3.310.0 + "@aws-sdk/middleware-stack": 3.310.0 + "@aws-sdk/middleware-user-agent": 3.319.0 + "@aws-sdk/node-config-provider": 3.310.0 + "@aws-sdk/node-http-handler": 3.321.1 + "@aws-sdk/protocol-http": 3.310.0 + "@aws-sdk/smithy-client": 3.316.0 + "@aws-sdk/types": 3.310.0 + "@aws-sdk/url-parser": 3.310.0 + "@aws-sdk/util-base64": 3.310.0 + "@aws-sdk/util-body-length-browser": 3.310.0 + "@aws-sdk/util-body-length-node": 3.310.0 + "@aws-sdk/util-defaults-mode-browser": 3.316.0 + "@aws-sdk/util-defaults-mode-node": 3.316.0 + "@aws-sdk/util-endpoints": 3.319.0 + "@aws-sdk/util-retry": 3.310.0 + "@aws-sdk/util-user-agent-browser": 3.310.0 + "@aws-sdk/util-user-agent-node": 3.310.0 + "@aws-sdk/util-utf8": 3.310.0 + tslib: ^2.5.0 + checksum: be867824663577684520e1abefb3d7989affabd8a89cbabd3bf0eb4d60bb7d47488b8e8fce4ac29b36c479fa97d27f2de6169d5ef98e748c7ac700fdedb41580 + languageName: node + linkType: hard + "@aws-sdk/client-sts@npm:3.312.0, @aws-sdk/client-sts@npm:^3.208.0": version: 3.312.0 resolution: "@aws-sdk/client-sts@npm:3.312.0" @@ -890,7 +970,24 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/credential-provider-node@npm:3.310.0, @aws-sdk/credential-provider-node@npm:^3.208.0": +"@aws-sdk/credential-provider-ini@npm:3.321.1": + version: 3.321.1 + resolution: "@aws-sdk/credential-provider-ini@npm:3.321.1" + dependencies: + "@aws-sdk/credential-provider-env": 3.310.0 + "@aws-sdk/credential-provider-imds": 3.310.0 + "@aws-sdk/credential-provider-process": 3.310.0 + "@aws-sdk/credential-provider-sso": 3.321.1 + "@aws-sdk/credential-provider-web-identity": 3.310.0 + "@aws-sdk/property-provider": 3.310.0 + "@aws-sdk/shared-ini-file-loader": 3.310.0 + "@aws-sdk/types": 3.310.0 + tslib: ^2.5.0 + checksum: ee3d615535efca5cfdd4749f23b02782a5f685289a114799dae46c84e03ba1ebf24ee3b846daaf3b042df43943bd132a83559cc76c4f261cdaadd1150f02a08f + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-node@npm:3.310.0": version: 3.310.0 resolution: "@aws-sdk/credential-provider-node@npm:3.310.0" dependencies: @@ -908,6 +1005,24 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/credential-provider-node@npm:^3.208.0, @aws-sdk/credential-provider-node@npm:^3.321.1": + version: 3.321.1 + resolution: "@aws-sdk/credential-provider-node@npm:3.321.1" + dependencies: + "@aws-sdk/credential-provider-env": 3.310.0 + "@aws-sdk/credential-provider-imds": 3.310.0 + "@aws-sdk/credential-provider-ini": 3.321.1 + "@aws-sdk/credential-provider-process": 3.310.0 + "@aws-sdk/credential-provider-sso": 3.321.1 + "@aws-sdk/credential-provider-web-identity": 3.310.0 + "@aws-sdk/property-provider": 3.310.0 + "@aws-sdk/shared-ini-file-loader": 3.310.0 + "@aws-sdk/types": 3.310.0 + tslib: ^2.5.0 + checksum: 899af9f4636b60be7a5dc022bf03d00bb5df8ff1b39214604d2157322766fc368c69457435bfb21a56fc5997a4b13feca32ec8bb53a57562ee2994a9cee4a968 + languageName: node + linkType: hard + "@aws-sdk/credential-provider-process@npm:3.310.0": version: 3.310.0 resolution: "@aws-sdk/credential-provider-process@npm:3.310.0" @@ -934,6 +1049,20 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/credential-provider-sso@npm:3.321.1": + version: 3.321.1 + resolution: "@aws-sdk/credential-provider-sso@npm:3.321.1" + dependencies: + "@aws-sdk/client-sso": 3.321.1 + "@aws-sdk/property-provider": 3.310.0 + "@aws-sdk/shared-ini-file-loader": 3.310.0 + "@aws-sdk/token-providers": 3.321.1 + "@aws-sdk/types": 3.310.0 + tslib: ^2.5.0 + checksum: a7c588394dbe29a2c9ade15dd5d151131963d1da4d14272432f397aa73aa551e5eb7bb5fb7829a628f136927ee689bfad579b7adc18bbc02d7f47e37d745105e + languageName: node + linkType: hard + "@aws-sdk/credential-provider-web-identity@npm:3.310.0": version: 3.310.0 resolution: "@aws-sdk/credential-provider-web-identity@npm:3.310.0" @@ -1326,6 +1455,18 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-user-agent@npm:3.319.0": + version: 3.319.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.319.0" + dependencies: + "@aws-sdk/protocol-http": 3.310.0 + "@aws-sdk/types": 3.310.0 + "@aws-sdk/util-endpoints": 3.319.0 + tslib: ^2.5.0 + checksum: a8bcb55eb774a96bc7018fe59ac5c3728743e538f7202815887daeebf3186b102835ac5b3bfd7addea1c3215f04ad8017d19d3f00cfe5635cf0eb4645b379c40 + languageName: node + linkType: hard + "@aws-sdk/node-config-provider@npm:3.310.0": version: 3.310.0 resolution: "@aws-sdk/node-config-provider@npm:3.310.0" @@ -1338,7 +1479,7 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/node-http-handler@npm:3.310.0, @aws-sdk/node-http-handler@npm:^3.208.0": +"@aws-sdk/node-http-handler@npm:3.310.0": version: 3.310.0 resolution: "@aws-sdk/node-http-handler@npm:3.310.0" dependencies: @@ -1351,6 +1492,19 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/node-http-handler@npm:3.321.1, @aws-sdk/node-http-handler@npm:^3.208.0": + version: 3.321.1 + resolution: "@aws-sdk/node-http-handler@npm:3.321.1" + dependencies: + "@aws-sdk/abort-controller": 3.310.0 + "@aws-sdk/protocol-http": 3.310.0 + "@aws-sdk/querystring-builder": 3.310.0 + "@aws-sdk/types": 3.310.0 + tslib: ^2.5.0 + checksum: b8546b1b20572e1ca02d3792319967712b40c925e4e06f1fb098d70ce55e32a15e514d655e8809650349567e1f46056dbbc3fb9f55df1257c8619127ca01f8fe + languageName: node + linkType: hard + "@aws-sdk/property-provider@npm:3.310.0": version: 3.310.0 resolution: "@aws-sdk/property-provider@npm:3.310.0" @@ -1452,6 +1606,17 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/smithy-client@npm:3.316.0": + version: 3.316.0 + resolution: "@aws-sdk/smithy-client@npm:3.316.0" + dependencies: + "@aws-sdk/middleware-stack": 3.310.0 + "@aws-sdk/types": 3.310.0 + tslib: ^2.5.0 + checksum: 2a42969efdf6f3e2383aae3f400f9d21623864ffefb4b83da275866621cfa216315e13e4629c5683a280ee61e3bd9908e90b1f4623fbb51fab212fa492a68226 + languageName: node + linkType: hard + "@aws-sdk/token-providers@npm:3.310.0": version: 3.310.0 resolution: "@aws-sdk/token-providers@npm:3.310.0" @@ -1465,6 +1630,19 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/token-providers@npm:3.321.1": + version: 3.321.1 + resolution: "@aws-sdk/token-providers@npm:3.321.1" + dependencies: + "@aws-sdk/client-sso-oidc": 3.321.1 + "@aws-sdk/property-provider": 3.310.0 + "@aws-sdk/shared-ini-file-loader": 3.310.0 + "@aws-sdk/types": 3.310.0 + tslib: ^2.5.0 + checksum: 2a4af584a5bdaa131ee85d62f67cef210d096f558064923c109f8773b402ef2c4830bba3486422b55f33e31371c794dbaecb07cf3d889250704bd815491569df + languageName: node + linkType: hard + "@aws-sdk/types@npm:3.310.0, @aws-sdk/types@npm:^3.208.0, @aws-sdk/types@npm:^3.222.0": version: 3.310.0 resolution: "@aws-sdk/types@npm:3.310.0" @@ -1553,6 +1731,18 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/util-defaults-mode-browser@npm:3.316.0": + version: 3.316.0 + resolution: "@aws-sdk/util-defaults-mode-browser@npm:3.316.0" + dependencies: + "@aws-sdk/property-provider": 3.310.0 + "@aws-sdk/types": 3.310.0 + bowser: ^2.11.0 + tslib: ^2.5.0 + checksum: abd989642a7f04dd87fef911623820e2f2e80932a663b157a716eb87907fbea99c28cbf706c5284b019db71641b464c8ae1448dc22ebfa9e4b3e6d627f800522 + languageName: node + linkType: hard + "@aws-sdk/util-defaults-mode-node@npm:3.310.0": version: 3.310.0 resolution: "@aws-sdk/util-defaults-mode-node@npm:3.310.0" @@ -1567,6 +1757,20 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/util-defaults-mode-node@npm:3.316.0": + version: 3.316.0 + resolution: "@aws-sdk/util-defaults-mode-node@npm:3.316.0" + dependencies: + "@aws-sdk/config-resolver": 3.310.0 + "@aws-sdk/credential-provider-imds": 3.310.0 + "@aws-sdk/node-config-provider": 3.310.0 + "@aws-sdk/property-provider": 3.310.0 + "@aws-sdk/types": 3.310.0 + tslib: ^2.5.0 + checksum: 28408b0ab9717750774d642d30ddf2b547093837bb69706bd2d0d9dc11f1d331c98c0f44412fc1b361b9a17b36f9bb970225b45ed0ec42ed17d2af242d864106 + languageName: node + linkType: hard + "@aws-sdk/util-endpoints@npm:3.310.0": version: 3.310.0 resolution: "@aws-sdk/util-endpoints@npm:3.310.0" @@ -1577,6 +1781,16 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/util-endpoints@npm:3.319.0": + version: 3.319.0 + resolution: "@aws-sdk/util-endpoints@npm:3.319.0" + dependencies: + "@aws-sdk/types": 3.310.0 + tslib: ^2.5.0 + checksum: 7b3e67494bab98f7e979fed0e172ea2179f29184fffea0954e0173f89128367996c09ee9bb2a86b500f8d44ad024b4efc550610b2daa943e223b2050c7a32b56 + languageName: node + linkType: hard + "@aws-sdk/util-hex-encoding@npm:3.310.0": version: 3.310.0 resolution: "@aws-sdk/util-hex-encoding@npm:3.310.0" @@ -8402,6 +8616,7 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-search-backend-module-elasticsearch@workspace:plugins/search-backend-module-elasticsearch" dependencies: + "@aws-sdk/credential-provider-node": ^3.321.1 "@backstage/backend-common": "workspace:^" "@backstage/backend-plugin-api": "workspace:^" "@backstage/cli": "workspace:^" @@ -8410,10 +8625,9 @@ __metadata: "@backstage/plugin-search-common": "workspace:^" "@elastic/elasticsearch": ^7.13.0 "@elastic/elasticsearch-mock": ^1.0.0 - "@opensearch-project/opensearch": ^2.0.0 + "@opensearch-project/opensearch": ^2.2.1 "@short.io/opensearch-mock": ^0.3.1 - aws-os-connection: ^0.2.0 - aws-sdk: ^2.948.0 + aws4: ^1.12.0 elastic-builder: ^2.16.0 lodash: ^4.17.21 uuid: ^8.3.2 @@ -13447,7 +13661,7 @@ __metadata: languageName: node linkType: hard -"@opensearch-project/opensearch@npm:^2.0.0": +"@opensearch-project/opensearch@npm:^2.2.1": version: 2.2.1 resolution: "@opensearch-project/opensearch@npm:2.2.1" dependencies: @@ -18377,18 +18591,6 @@ __metadata: languageName: node linkType: hard -"aws-os-connection@npm:^0.2.0": - version: 0.2.0 - resolution: "aws-os-connection@npm:0.2.0" - dependencies: - aws4: ^1.11.0 - peerDependencies: - "@opensearch-project/opensearch": 2 - aws-sdk: ">=2" - checksum: 1708ffd9bdc5f7bd6b8447f97859936a8657f4df567f61f12dacd040e6f1a25020bbea8027bb28f19fe27d171a0d3a5090c2d84d6f752bb04e387640413fec7c - languageName: node - linkType: hard - "aws-sdk-client-mock-jest@npm:^2.0.0": version: 2.1.1 resolution: "aws-sdk-client-mock-jest@npm:2.1.1" @@ -18423,7 +18625,7 @@ __metadata: languageName: node linkType: hard -"aws-sdk@npm:^2.1231.0, aws-sdk@npm:^2.840.0, aws-sdk@npm:^2.948.0": +"aws-sdk@npm:^2.1231.0, aws-sdk@npm:^2.840.0": version: 2.1279.0 resolution: "aws-sdk@npm:2.1279.0" dependencies: @@ -18448,7 +18650,7 @@ __metadata: languageName: node linkType: hard -"aws4@npm:^1.11.0, aws4@npm:^1.8.0": +"aws4@npm:^1.11.0, aws4@npm:^1.12.0, aws4@npm:^1.8.0": version: 1.12.0 resolution: "aws4@npm:1.12.0" checksum: 68f79708ac7c335992730bf638286a3ee0a645cf12575d557860100767c500c08b30e24726b9f03265d74116417f628af78509e1333575e9f8d52a80edfe8cbc From 0f5ef03794e34e30644ba5c12c2e93a7919dec40 Mon Sep 17 00:00:00 2001 From: Andrew Ochsner Date: Tue, 2 May 2023 14:23:29 -0500 Subject: [PATCH 010/189] spell OpenSearch and accept it Signed-off-by: Andrew Ochsner --- .changeset/fluffy-crabs-reply.md | 2 +- .github/vale/Vocab/Backstage/accept.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.changeset/fluffy-crabs-reply.md b/.changeset/fluffy-crabs-reply.md index 5570ef0492..0665dd864d 100644 --- a/.changeset/fluffy-crabs-reply.md +++ b/.changeset/fluffy-crabs-reply.md @@ -2,4 +2,4 @@ '@backstage/plugin-search-backend-module-elasticsearch': minor --- -Upgrade to aws-sdk v3 and include Opensearch Serverless support +Upgrade to aws-sdk v3 and include OpenSearch Serverless support diff --git a/.github/vale/Vocab/Backstage/accept.txt b/.github/vale/Vocab/Backstage/accept.txt index 83fba8aa56..cbb2279e1b 100644 --- a/.github/vale/Vocab/Backstage/accept.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -247,6 +247,7 @@ Olausson Oldsberg onboarding Onboarding +OpenSearch OpenShift orgs padding From 347aeca204cb19468b4bd5673babd714e10b68e8 Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Fri, 14 Apr 2023 13:46:42 -0500 Subject: [PATCH 011/189] Added DevTools plugin Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- .changeset/blue-eyes-run.md | 7 + packages/app/package.json | 1 + packages/app/src/App.tsx | 5 + packages/app/src/components/Root/Root.tsx | 2 + .../devtools/CustomDevToolsPage.tsx | 44 ++ packages/backend/package.json | 1 + packages/backend/src/index.ts | 4 +- packages/backend/src/plugins/devtools.ts | 29 ++ plugins/devtools-backend/.eslintrc.js | 1 + plugins/devtools-backend/README.md | 56 +++ plugins/devtools-backend/api-report.md | 41 ++ plugins/devtools-backend/config.d.ts | 44 ++ plugins/devtools-backend/package.json | 63 +++ .../src/api/DevToolsBackendApi.ts | 252 ++++++++++++ plugins/devtools-backend/src/api/index.ts | 17 + plugins/devtools-backend/src/index.ts | 18 + plugins/devtools-backend/src/run.ts | 33 ++ .../src/service/router.test.ts | 69 ++++ .../devtools-backend/src/service/router.ts | 130 ++++++ .../src/service/standaloneServer.ts | 68 ++++ plugins/devtools-backend/src/setupTests.ts | 17 + plugins/devtools-backend/src/util/Lockfile.ts | 317 +++++++++++++++ plugins/devtools-common/.eslintrc.js | 1 + plugins/devtools-common/README.md | 3 + plugins/devtools-common/api-report.md | 74 ++++ plugins/devtools-common/package.json | 35 ++ plugins/devtools-common/src/index.ts | 18 + plugins/devtools-common/src/permissions.ts | 49 +++ plugins/devtools-common/src/setupTests.ts | 17 + plugins/devtools-common/src/types.ts | 67 ++++ plugins/devtools/.eslintrc.js | 1 + plugins/devtools/README.md | 375 ++++++++++++++++++ plugins/devtools/api-report.md | 59 +++ plugins/devtools/dev/index.tsx | 27 ++ plugins/devtools/docs/devtools-config-tab.png | Bin 0 -> 142913 bytes .../docs/devtools-external-dependencies.png | Bin 0 -> 107183 bytes plugins/devtools/docs/devtools-info-tab.png | Bin 0 -> 126106 bytes plugins/devtools/package.json | 64 +++ plugins/devtools/src/api/DevToolsApi.ts | 32 ++ plugins/devtools/src/api/DevToolsClient.ts | 78 ++++ plugins/devtools/src/api/index.ts | 18 + .../Content/ConfigContent/ConfigContent.tsx | 95 +++++ .../components/Content/ConfigContent/index.ts | 17 + .../ExternalDependenciesContent.tsx | 140 +++++++ .../ExternalDependenciesContent/index.ts | 17 + .../Content/InfoContent/BackstageLogoIcon.tsx | 25 ++ .../Content/InfoContent/InfoContent.tsx | 138 +++++++ .../InfoContent/InfoDependenciesTable.tsx | 55 +++ .../components/Content/InfoContent/index.ts | 17 + .../devtools/src/components/Content/index.ts | 19 + .../DefaultDevToolsPage.tsx | 42 ++ .../components/DefaultDevToolsPage/index.ts | 17 + .../DevToolsLayout/DevToolsLayout.tsx | 79 ++++ .../src/components/DevToolsLayout/index.tsx | 18 + .../components/DevToolsPage/DevToolsPage.tsx | 25 ++ .../src/components/DevToolsPage/index.ts | 17 + plugins/devtools/src/components/index.ts | 18 + plugins/devtools/src/hooks/index.ts | 19 + plugins/devtools/src/hooks/useConfig.ts | 37 ++ .../src/hooks/useExternalDependencies.ts | 37 ++ plugins/devtools/src/hooks/useInfo.ts | 37 ++ plugins/devtools/src/index.ts | 18 + plugins/devtools/src/plugin.test.ts | 23 ++ plugins/devtools/src/plugin.ts | 52 +++ plugins/devtools/src/routes.ts | 21 + plugins/devtools/src/setupTests.ts | 17 + yarn.lock | 283 ++++++++++++- 67 files changed, 3405 insertions(+), 15 deletions(-) create mode 100644 .changeset/blue-eyes-run.md create mode 100644 packages/app/src/components/devtools/CustomDevToolsPage.tsx create mode 100644 packages/backend/src/plugins/devtools.ts create mode 100644 plugins/devtools-backend/.eslintrc.js create mode 100644 plugins/devtools-backend/README.md create mode 100644 plugins/devtools-backend/api-report.md create mode 100644 plugins/devtools-backend/config.d.ts create mode 100644 plugins/devtools-backend/package.json create mode 100644 plugins/devtools-backend/src/api/DevToolsBackendApi.ts create mode 100644 plugins/devtools-backend/src/api/index.ts create mode 100644 plugins/devtools-backend/src/index.ts create mode 100644 plugins/devtools-backend/src/run.ts create mode 100644 plugins/devtools-backend/src/service/router.test.ts create mode 100644 plugins/devtools-backend/src/service/router.ts create mode 100644 plugins/devtools-backend/src/service/standaloneServer.ts create mode 100644 plugins/devtools-backend/src/setupTests.ts create mode 100644 plugins/devtools-backend/src/util/Lockfile.ts create mode 100644 plugins/devtools-common/.eslintrc.js create mode 100644 plugins/devtools-common/README.md create mode 100644 plugins/devtools-common/api-report.md create mode 100644 plugins/devtools-common/package.json create mode 100644 plugins/devtools-common/src/index.ts create mode 100644 plugins/devtools-common/src/permissions.ts create mode 100644 plugins/devtools-common/src/setupTests.ts create mode 100644 plugins/devtools-common/src/types.ts create mode 100644 plugins/devtools/.eslintrc.js create mode 100644 plugins/devtools/README.md create mode 100644 plugins/devtools/api-report.md create mode 100644 plugins/devtools/dev/index.tsx create mode 100644 plugins/devtools/docs/devtools-config-tab.png create mode 100644 plugins/devtools/docs/devtools-external-dependencies.png create mode 100644 plugins/devtools/docs/devtools-info-tab.png create mode 100644 plugins/devtools/package.json create mode 100644 plugins/devtools/src/api/DevToolsApi.ts create mode 100644 plugins/devtools/src/api/DevToolsClient.ts create mode 100644 plugins/devtools/src/api/index.ts create mode 100644 plugins/devtools/src/components/Content/ConfigContent/ConfigContent.tsx create mode 100644 plugins/devtools/src/components/Content/ConfigContent/index.ts create mode 100644 plugins/devtools/src/components/Content/ExternalDependenciesContent/ExternalDependenciesContent.tsx create mode 100644 plugins/devtools/src/components/Content/ExternalDependenciesContent/index.ts create mode 100644 plugins/devtools/src/components/Content/InfoContent/BackstageLogoIcon.tsx create mode 100644 plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx create mode 100644 plugins/devtools/src/components/Content/InfoContent/InfoDependenciesTable.tsx create mode 100644 plugins/devtools/src/components/Content/InfoContent/index.ts create mode 100644 plugins/devtools/src/components/Content/index.ts create mode 100644 plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx create mode 100644 plugins/devtools/src/components/DefaultDevToolsPage/index.ts create mode 100644 plugins/devtools/src/components/DevToolsLayout/DevToolsLayout.tsx create mode 100644 plugins/devtools/src/components/DevToolsLayout/index.tsx create mode 100644 plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx create mode 100644 plugins/devtools/src/components/DevToolsPage/index.ts create mode 100644 plugins/devtools/src/components/index.ts create mode 100644 plugins/devtools/src/hooks/index.ts create mode 100644 plugins/devtools/src/hooks/useConfig.ts create mode 100644 plugins/devtools/src/hooks/useExternalDependencies.ts create mode 100644 plugins/devtools/src/hooks/useInfo.ts create mode 100644 plugins/devtools/src/index.ts create mode 100644 plugins/devtools/src/plugin.test.ts create mode 100644 plugins/devtools/src/plugin.ts create mode 100644 plugins/devtools/src/routes.ts create mode 100644 plugins/devtools/src/setupTests.ts diff --git a/.changeset/blue-eyes-run.md b/.changeset/blue-eyes-run.md new file mode 100644 index 0000000000..1b96e01f1c --- /dev/null +++ b/.changeset/blue-eyes-run.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-devtools': minor +'@backstage/plugin-devtools-backend': minor +'@backstage/plugin-devtools-common': minor +--- + +Introduced the DevTools plugin, checkout the plugin's [`README.md`](https://github.com/backstage/backstage/tree/master/plugins/devtools) for more details! diff --git a/packages/app/package.json b/packages/app/package.json index d4941ecd48..5987ec44a0 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -29,6 +29,7 @@ "@backstage/plugin-cloudbuild": "workspace:^", "@backstage/plugin-code-coverage": "workspace:^", "@backstage/plugin-cost-insights": "workspace:^", + "@backstage/plugin-devtools": "workspace:^", "@backstage/plugin-dynatrace": "workspace:^", "@backstage/plugin-entity-feedback": "workspace:^", "@backstage/plugin-explore": "workspace:^", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 19edd9640d..bc9eac8c64 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -109,6 +109,8 @@ import { TwoColumnLayout } from './components/scaffolder/customScaffolderLayouts import { ScoreBoardPage } from '@oriflame/backstage-plugin-score-card'; import { StackstormPage } from '@backstage/plugin-stackstorm'; import { PuppetDbPage } from '@backstage/plugin-puppetdb'; +import { DevToolsPage } from '@backstage/plugin-devtools'; +import { customDevToolsPage } from './components/devtools/CustomDevToolsPage'; const app = createApp({ apis, @@ -291,6 +293,9 @@ const routes = ( } /> } /> } /> + }> + {customDevToolsPage} + ); diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 904fa4a7d4..ffaf72296a 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -52,6 +52,7 @@ import { MyGroupsSidebarItem } from '@backstage/plugin-org'; import { SearchModal } from '../search/SearchModal'; import Score from '@material-ui/icons/Score'; import { useApp } from '@backstage/core-plugin-api'; +import BuildIcon from '@material-ui/icons/Build'; const useSidebarLogoStyles = makeStyles({ root: { @@ -176,6 +177,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( to="/settings" > + {children} diff --git a/packages/app/src/components/devtools/CustomDevToolsPage.tsx b/packages/app/src/components/devtools/CustomDevToolsPage.tsx new file mode 100644 index 0000000000..dfe70426aa --- /dev/null +++ b/packages/app/src/components/devtools/CustomDevToolsPage.tsx @@ -0,0 +1,44 @@ +/* + * Copyright 2022 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. + */ + +import { + ConfigContent, + ExternalDependenciesContent, + InfoContent, +} from '@backstage/plugin-devtools'; +import { DevToolsLayout } from '@backstage/plugin-devtools'; +import React from 'react'; + +const DevToolsPage = () => { + return ( + + + + + + + + + + + + ); +}; + +export const customDevToolsPage = ; diff --git a/packages/backend/package.json b/packages/backend/package.json index 900ee150fc..337d557788 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -42,6 +42,7 @@ "@backstage/plugin-catalog-backend": "workspace:^", "@backstage/plugin-catalog-node": "workspace:^", "@backstage/plugin-code-coverage-backend": "workspace:^", + "@backstage/plugin-devtools-backend": "workspace:^", "@backstage/plugin-entity-feedback-backend": "workspace:^", "@backstage/plugin-events-backend": "workspace:^", "@backstage/plugin-events-node": "workspace:^", diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 593d30bc3b..976ad9a577 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -64,6 +64,7 @@ import playlist from './plugins/playlist'; import adr from './plugins/adr'; import lighthouse from './plugins/lighthouse'; import linguist from './plugins/linguist'; +import devTools from './plugins/devtools'; import { PluginEnvironment } from './types'; import { ServerPermissionClient } from '@backstage/plugin-permission-node'; import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; @@ -158,8 +159,8 @@ async function main() { const eventsEnv = useHotMemoize(module, () => createEnv('events')); const exploreEnv = useHotMemoize(module, () => createEnv('explore')); const lighthouseEnv = useHotMemoize(module, () => createEnv('lighthouse')); - const linguistEnv = useHotMemoize(module, () => createEnv('linguist')); + const devToolsEnv = useHotMemoize(module, () => createEnv('devtools')); const apiRouter = Router(); apiRouter.use('/catalog', await catalog(catalogEnv)); @@ -185,6 +186,7 @@ async function main() { apiRouter.use('/entity-feedback', await entityFeedback(entityFeedbackEnv)); apiRouter.use('/adr', await adr(adrEnv)); apiRouter.use('/linguist', await linguist(linguistEnv)); + apiRouter.use('/devtools', await devTools(devToolsEnv)); apiRouter.use(notFoundHandler()); await lighthouse(lighthouseEnv); diff --git a/packages/backend/src/plugins/devtools.ts b/packages/backend/src/plugins/devtools.ts new file mode 100644 index 0000000000..8e1767ddb1 --- /dev/null +++ b/packages/backend/src/plugins/devtools.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2022 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. + */ + +import { createRouter } from '@backstage/plugin-devtools-backend'; +import { Router } from 'express'; +import type { PluginEnvironment } from '../types'; + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + return createRouter({ + logger: env.logger, + config: env.config, + permissions: env.permissions, + }); +} diff --git a/plugins/devtools-backend/.eslintrc.js b/plugins/devtools-backend/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/devtools-backend/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/devtools-backend/README.md b/plugins/devtools-backend/README.md new file mode 100644 index 0000000000..e505a04963 --- /dev/null +++ b/plugins/devtools-backend/README.md @@ -0,0 +1,56 @@ +# DevTools Backend + +Welcome to the DevTools backend plugin! This plugin provides data for the [DevTools frontend](../devtools/) features. + +## Setup + +Here's how to get the DevTools Backend up and running: + +1. First we need to add the `@backstage/plugin-devtools-backend` package to your backend: + + ```sh + # From the Backstage root directory + cd packages/backend + yarn add @backstage/plugin-devtools-backend + ``` + +2. Then we will create a new file named `packages/backend/src/plugins/devtools.ts`, and add the + following to it: + + ```ts + import { createRouter } from '@backstage/plugin-devtools-backend'; + import { Router } from 'express'; + import type { PluginEnvironment } from '../types'; + + export default function createPlugin( + env: PluginEnvironment, + ): Promise { + return createRouter({ + logger: env.logger, + config: env.config, + permissions: env.permissions, + }); + } + ``` + +3. Next we wire this into the overall backend router, edit `packages/backend/src/index.ts`: + + ```ts + import devTools from './plugins/devtools'; + // ... + async function main() { + // ... + // Add this line under the other lines that follow the useHotMemoize pattern + const devToolsEnv = useHotMemoize(module, () => createEnv('devtools')); + // ... + // Insert this line under the other lines that add their routers to apiRouter in the same way + apiRouter.use('/devtools', await devTools(devToolsEnv)); + ``` + +4. Now run `yarn start-backend` from the repo root +5. Finally open `http://localhost:7007/api/devtools/health` in a browser and it should return `{"status":"ok"}` + +## Links + +- [Frontend part of the plugin](../devtools/README.md) +- [The Backstage homepage](https://backstage.io) diff --git a/plugins/devtools-backend/api-report.md b/plugins/devtools-backend/api-report.md new file mode 100644 index 0000000000..adf2189512 --- /dev/null +++ b/plugins/devtools-backend/api-report.md @@ -0,0 +1,41 @@ +## API Report File for "@backstage/plugin-devtools-backend" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { Config } from '@backstage/config'; +import { ConfigInfo } from '@backstage/plugin-devtools-common'; +import { DevToolsInfo } from '@backstage/plugin-devtools-common'; +import express from 'express'; +import { ExternalDependency } from '@backstage/plugin-devtools-common'; +import { Logger } from 'winston'; +import { PermissionEvaluator } from '@backstage/plugin-permission-common'; + +// @public (undocumented) +export function createRouter(options: RouterOptions): Promise; + +// @public (undocumented) +export class DevToolsBackendApi { + constructor(logger: Logger, config: Config); + // (undocumented) + listConfig(): Promise; + // (undocumented) + listExternalDependencyDetails(): Promise; + // (undocumented) + listInfo(): Promise; +} + +// @public (undocumented) +export interface RouterOptions { + // (undocumented) + config: Config; + // (undocumented) + devToolsBackendApi?: DevToolsBackendApi; + // (undocumented) + logger: Logger; + // (undocumented) + permissions: PermissionEvaluator; +} + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/devtools-backend/config.d.ts b/plugins/devtools-backend/config.d.ts new file mode 100644 index 0000000000..15e70afcf8 --- /dev/null +++ b/plugins/devtools-backend/config.d.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2022 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. + */ +export interface Config { + /** + * DevTools configuration. + */ + devTools?: { + /** + * External dependency configuration. + */ + externalDependencies?: { + /** + * The list of endpoints to check. + */ + endpoints?: Array<{ + /** + * The name of the endpoint. + */ + name: string; + /** + * Type of check to perform; currently fetch or ping + */ + type: string; + /** + * The target of the endpoint; currently either a URL for fetch or server name for ping. + */ + target: string; + }>; + }; + }; +} diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json new file mode 100644 index 0000000000..94dd803af4 --- /dev/null +++ b/plugins/devtools-backend/package.json @@ -0,0 +1,63 @@ +{ + "name": "@backstage/plugin-devtools-backend", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/cli-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/config-loader": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "@backstage/plugin-devtools-common": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-permission-node": "workspace:^", + "@backstage/types": "workspace:^", + "@manypkg/get-packages": "^1.1.3", + "@types/express": "*", + "@yarnpkg/lockfile": "^1.1.0", + "@yarnpkg/parsers": "^3.0.0-rc.4", + "express": "^4.18.1", + "express-promise-router": "^4.1.0", + "fs-extra": "^10.0.0", + "lodash": "^4.17.21", + "node-fetch": "^2.6.7", + "ping": "^0.4.1", + "semver": "^7.3.2", + "winston": "^3.2.1", + "yn": "^4.0.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@types/minimist": "^1.2.0", + "@types/ping": "^0.4.1", + "@types/supertest": "^2.0.8", + "@types/yarnpkg__lockfile": "^1.1.4", + "msw": "^0.47.0", + "supertest": "^6.2.4" + }, + "files": [ + "dist", + "config.d.ts" + ], + "configSchema": "config.d.ts" +} diff --git a/plugins/devtools-backend/src/api/DevToolsBackendApi.ts b/plugins/devtools-backend/src/api/DevToolsBackendApi.ts new file mode 100644 index 0000000000..88bd51fa7e --- /dev/null +++ b/plugins/devtools-backend/src/api/DevToolsBackendApi.ts @@ -0,0 +1,252 @@ +/* + * Copyright 2022 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. + */ + +import { Config, ConfigReader } from '@backstage/config'; +import { loadConfigSchema } from '@backstage/config-loader'; +import { + PackageDependency, + DevToolsInfo, + ExternalDependency, + Endpoint, + ExternalDependencyStatus, + ConfigInfo, +} from '@backstage/plugin-devtools-common'; + +import { JsonObject } from '@backstage/types'; +import { Logger } from 'winston'; +import fetch from 'node-fetch'; +import { findPaths } from '@backstage/cli-common'; +import { getPackages } from '@manypkg/get-packages'; +import ping from 'ping'; +import os from 'os'; +import fs from 'fs-extra'; +import { Lockfile } from '../util/Lockfile'; +import { memoize } from 'lodash'; +import { assertError } from '@backstage/errors'; + +/** @public */ +export class DevToolsBackendApi { + public constructor( + private readonly logger: Logger, + private readonly config: Config, + ) {} + + public async listExternalDependencyDetails(): Promise { + const result: ExternalDependency[] = []; + + const endpoints = this.config.getOptional( + 'devTools.externalDependencies.endpoints', + ); + if (!endpoints) { + // No external dependency endpoints configured + return result; + } + for (const endpoint of endpoints) { + this.logger?.info( + `Checking external dependency "${endpoint.name}" at "${endpoint.target}"`, + ); + + switch (endpoint.type) { + case 'ping': { + const pingResult = await this.pingExternalDependency(endpoint); + result.push(pingResult); + break; + } + case 'fetch': { + const fetchResult = await this.fetchExternalDependency(endpoint); + result.push(fetchResult); + break; + } + default: + return result; + } + } + + return result; + } + + private async fetchExternalDependency( + endpoint: Endpoint, + ): Promise { + let status; + let error; + + await fetch(endpoint.target) + .then(res => { + status = + res.status === 200 + ? ExternalDependencyStatus.healthy + : ExternalDependencyStatus.unhealthy; + this.logger.info( + `Fetch for ${endpoint.name} resulted in status code "${res.status}"`, + ); + }) + .catch((err: Error) => { + this.logger.error(`Fetch failed for ${endpoint.name} - ${err.message}`); + error = err.message; + }); + + const result: ExternalDependency = { + name: endpoint.name, + type: endpoint.type, + target: endpoint.target, + status: status ?? ExternalDependencyStatus.unhealthy, + error: error ?? undefined, + }; + + return result; + } + + private async pingExternalDependency( + endpoint: Endpoint, + ): Promise { + const pingResult = await ping.promise.probe(endpoint.target); + + let error; + if ( + pingResult.packetLoss === '100.000' || + pingResult.packetLoss === 'unknown' + ) { + this.logger.error( + `Ping failed for ${endpoint.name} - ${pingResult.output}`, + ); + error = + pingResult.output === '' + ? `${endpoint.target} - Unknown` + : pingResult.output; + } + + this.logger.info(`Ping results for ${endpoint.name}: ${pingResult.output}`); + + const result: ExternalDependency = { + name: endpoint.name, + type: endpoint.type, + target: endpoint.target, + status: pingResult.alive + ? ExternalDependencyStatus.healthy + : ExternalDependencyStatus.unhealthy, + error: error ?? undefined, + }; + + return result; + } + + public async listConfig(): Promise { + /* eslint-disable-next-line no-restricted-syntax */ + const paths = findPaths(__dirname); + + const { packages } = await getPackages(paths.targetDir); + const schemaFunc = async () => { + return await loadConfigSchema({ + dependencies: packages.map(p => p.packageJson.name), + }); + }; + + const schemaMemo = memoize(schemaFunc); + const schema = await schemaMemo(); + + const configInfo: ConfigInfo = { + config: undefined, + error: undefined, + }; + try { + const config = { + data: this.config.get() as JsonObject, + context: 'inline', + }; + const sanitizedConfigs = schema.process([config], { + ignoreSchemaErrors: false, + valueTransform: (value, context) => + context.visibility === 'secret' ? '' : value, + }); + + const data = ConfigReader.fromConfigs(sanitizedConfigs).get(); + configInfo.config = data; + } catch (error) { + assertError(error); + // The config is not valid for some reason but we want to be able to see it still + const config = { + data: this.config.get() as JsonObject, + context: 'inline', + }; + const sanitizedConfigs = schema.process([config], { + ignoreSchemaErrors: true, + valueTransform: (value, context) => + context.visibility === 'secret' ? '' : value, + }); + + const data = ConfigReader.fromConfigs(sanitizedConfigs).get(); + configInfo.config = data; + configInfo.error = { + name: error.name, + message: error.message, + messages: error.messages as string[] | undefined, + stack: error.stack, + }; + } + + return configInfo; + } + + public async listInfo(): Promise { + const operatingSystem = `${os.type} ${os.release} - ${os.platform}/${os.arch}`; + const nodeJsVersion = process.version; + + /* eslint-disable-next-line no-restricted-syntax */ + const paths = findPaths(__dirname); + const backstageFile = paths.resolveTargetRoot('backstage.json'); + let backstageJson = undefined; + if (fs.existsSync(backstageFile)) { + const buffer = await fs.readFile(backstageFile); + backstageJson = JSON.parse(buffer.toString()); + } + + const lockfilePath = paths.resolveTargetRoot('yarn.lock'); + const lockfile = await Lockfile.load(lockfilePath); + + const deps = [...lockfile.keys()].filter(n => n.startsWith('@backstage/')); + + const infoDependencies: PackageDependency[] = []; + for (const dep of deps) { + const versions = new Set(lockfile.get(dep)!.map(i => i.version)); + const infoDependency: PackageDependency = { + name: dep, + versions: [...versions].join(', '), + }; + infoDependencies.push(infoDependency); + } + + const info: DevToolsInfo = { + operatingSystem: operatingSystem ?? 'N/A', + nodeJsVersion: nodeJsVersion ?? 'N/A', + backstageVersion: + backstageJson && backstageJson.version ? backstageJson.version : 'N/A', + dependencies: infoDependencies, + }; + + return info; + } +} + +export function isValidUrl(url: string): boolean { + try { + // eslint-disable-next-line no-new + new URL(url); + return true; + } catch { + return false; + } +} diff --git a/plugins/devtools-backend/src/api/index.ts b/plugins/devtools-backend/src/api/index.ts new file mode 100644 index 0000000000..ed3390b113 --- /dev/null +++ b/plugins/devtools-backend/src/api/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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. + */ + +export { DevToolsBackendApi } from './DevToolsBackendApi'; diff --git a/plugins/devtools-backend/src/index.ts b/plugins/devtools-backend/src/index.ts new file mode 100644 index 0000000000..134b797761 --- /dev/null +++ b/plugins/devtools-backend/src/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2022 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. + */ + +export { DevToolsBackendApi } from './api'; +export * from './service/router'; diff --git a/plugins/devtools-backend/src/run.ts b/plugins/devtools-backend/src/run.ts new file mode 100644 index 0000000000..d945aa13f0 --- /dev/null +++ b/plugins/devtools-backend/src/run.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2022 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. + */ + +import { getRootLogger } from '@backstage/backend-common'; +import yn from 'yn'; +import { startStandaloneServer } from './service/standaloneServer'; + +const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7007; +const enableCors = yn(process.env.PLUGIN_CORS, { default: false }); +const logger = getRootLogger(); + +startStandaloneServer({ port, enableCors, logger }).catch(err => { + logger.error(err); + process.exit(1); +}); + +process.on('SIGINT', () => { + logger.info('CTRL+C pressed; exiting.'); + process.exit(0); +}); diff --git a/plugins/devtools-backend/src/service/router.test.ts b/plugins/devtools-backend/src/service/router.test.ts new file mode 100644 index 0000000000..f3c78242cf --- /dev/null +++ b/plugins/devtools-backend/src/service/router.test.ts @@ -0,0 +1,69 @@ +/* + * Copyright 2022 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. + */ + +import { getVoidLogger } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import express from 'express'; +import request from 'supertest'; +import { PermissionEvaluator } from '@backstage/plugin-permission-common'; +import { createRouter } from './router'; + +const mockedAuthorize: jest.MockedFunction = + jest.fn(); +const mockedPermissionQuery: jest.MockedFunction< + PermissionEvaluator['authorizeConditional'] +> = jest.fn(); + +const permissionEvaluator: PermissionEvaluator = { + authorize: mockedAuthorize, + authorizeConditional: mockedPermissionQuery, +}; + +describe('createRouter', () => { + let app: express.Express; + + beforeAll(async () => { + const router = await createRouter({ + logger: getVoidLogger(), + config: new ConfigReader({ + healthCheck: { + endpoint: [ + { + name: '', + type: '', + target: '', + }, + ], + }, + }), + permissions: permissionEvaluator, + }); + app = express().use(router); + }); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + describe('GET /health', () => { + it('returns ok', async () => { + const response = await request(app).get('/health'); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ status: 'ok' }); + }); + }); +}); diff --git a/plugins/devtools-backend/src/service/router.ts b/plugins/devtools-backend/src/service/router.ts new file mode 100644 index 0000000000..a346ad3183 --- /dev/null +++ b/plugins/devtools-backend/src/service/router.ts @@ -0,0 +1,130 @@ +/* + * Copyright 2022 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. + */ +import { + AuthorizeResult, + PermissionEvaluator, +} from '@backstage/plugin-permission-common'; +import { + devToolsConfigReadPermission, + devToolsExternalDependenciesReadPermission, + devToolsInfoReadPermission, +} from '@backstage/plugin-devtools-common'; + +import { Config } from '@backstage/config'; +import { DevToolsBackendApi } from '../api'; +import { Logger } from 'winston'; +import { NotAllowedError } from '@backstage/errors'; +import Router from 'express-promise-router'; +import { errorHandler } from '@backstage/backend-common'; +import express from 'express'; +import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node'; + +/** @public */ +export interface RouterOptions { + devToolsBackendApi?: DevToolsBackendApi; + logger: Logger; + config: Config; + permissions: PermissionEvaluator; +} + +/** @public */ +export async function createRouter( + options: RouterOptions, +): Promise { + const { logger, config, permissions } = options; + + const devToolsBackendApi = + options.devToolsBackendApi || new DevToolsBackendApi(logger, config); + + const router = Router(); + router.use(express.json()); + + router.get('/health', (_req, res) => { + res.status(200).json({ status: 'ok' }); + }); + + router.get('/info', async (req, response) => { + const token = getBearerTokenFromAuthorizationHeader( + req.header('authorization'), + ); + + const decision = ( + await permissions.authorize( + [{ permission: devToolsInfoReadPermission }], + { + token, + }, + ) + )[0]; + + if (decision.result === AuthorizeResult.DENY) { + throw new NotAllowedError('Unauthorized'); + } + + const info = await devToolsBackendApi.listInfo(); + + response.status(200).json(info); + }); + + router.get('/config', async (req, response) => { + const token = getBearerTokenFromAuthorizationHeader( + req.header('authorization'), + ); + + const decision = ( + await permissions.authorize( + [{ permission: devToolsConfigReadPermission }], + { + token, + }, + ) + )[0]; + + if (decision.result === AuthorizeResult.DENY) { + throw new NotAllowedError('Unauthorized'); + } + + const configList = await devToolsBackendApi.listConfig(); + + response.status(200).json(configList); + }); + + router.get('/external-dependencies', async (req, response) => { + const token = getBearerTokenFromAuthorizationHeader( + req.header('authorization'), + ); + + const decision = ( + await permissions.authorize( + [{ permission: devToolsExternalDependenciesReadPermission }], + { + token, + }, + ) + )[0]; + + if (decision.result === AuthorizeResult.DENY) { + throw new NotAllowedError('Unauthorized'); + } + + const health = await devToolsBackendApi.listExternalDependencyDetails(); + + response.status(200).json(health); + }); + + router.use(errorHandler()); + return router; +} diff --git a/plugins/devtools-backend/src/service/standaloneServer.ts b/plugins/devtools-backend/src/service/standaloneServer.ts new file mode 100644 index 0000000000..d2fdce169d --- /dev/null +++ b/plugins/devtools-backend/src/service/standaloneServer.ts @@ -0,0 +1,68 @@ +/* + * Copyright 2022 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. + */ + +import { + ServerTokenManager, + SingleHostDiscovery, + createServiceBuilder, + loadBackendConfig, +} from '@backstage/backend-common'; + +import { Logger } from 'winston'; +import { Server } from 'http'; +import { ServerPermissionClient } from '@backstage/plugin-permission-node'; +import { createRouter } from './router'; + +export interface ServerOptions { + port: number; + enableCors: boolean; + logger: Logger; +} + +export async function startStandaloneServer( + options: ServerOptions, +): Promise { + const logger = options.logger.child({ service: 'devtools-backend-backend' }); + const config = await loadBackendConfig({ logger, argv: process.argv }); + const discovery = SingleHostDiscovery.fromConfig(config); + const tokenManager = ServerTokenManager.fromConfig(config, { + logger, + }); + const permissions = ServerPermissionClient.fromConfig(config, { + discovery, + tokenManager, + }); + logger.debug('Starting application server...'); + const router = await createRouter({ + logger, + config, + permissions, + }); + + let service = createServiceBuilder(module) + .setPort(options.port) + .addRouter('/devtools-backend', router); + if (options.enableCors) { + service = service.enableCors({ origin: 'http://localhost:3000' }); + } + + return await service.start().catch(err => { + logger.error(err); + process.exit(1); + }); +} + +module.hot?.accept(); diff --git a/plugins/devtools-backend/src/setupTests.ts b/plugins/devtools-backend/src/setupTests.ts new file mode 100644 index 0000000000..813cdeaae3 --- /dev/null +++ b/plugins/devtools-backend/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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. + */ + +export {}; diff --git a/plugins/devtools-backend/src/util/Lockfile.ts b/plugins/devtools-backend/src/util/Lockfile.ts new file mode 100644 index 0000000000..52dbb847bc --- /dev/null +++ b/plugins/devtools-backend/src/util/Lockfile.ts @@ -0,0 +1,317 @@ +/* + * Copyright 2022 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. + */ + +import fs from 'fs-extra'; +import semver from 'semver'; +import { parseSyml, stringifySyml } from '@yarnpkg/parsers'; +import { stringify as legacyStringifyLockfile } from '@yarnpkg/lockfile'; + +const ENTRY_PATTERN = /^((?:@[^/]+\/)?[^@/]+)@(.+)$/; + +type LockfileData = { + [entry: string]: { + version: string; + resolved?: string; + integrity?: string; + dependencies?: { [name: string]: string }; + }; +}; + +type LockfileQueryEntry = { + range: string; + version: string; +}; + +/** Entries that have an invalid version range, for example an npm tag */ +type AnalyzeResultInvalidRange = { + name: string; + range: string; +}; + +/** Entries that can be deduplicated by bumping to an existing higher version */ +type AnalyzeResultNewVersion = { + name: string; + range: string; + oldVersion: string; + newVersion: string; +}; + +/** Entries that would need a dependency update in package.json to be deduplicated */ +type AnalyzeResultNewRange = { + name: string; + oldRange: string; + newRange: string; + oldVersion: string; + newVersion: string; +}; + +type AnalyzeResult = { + invalidRanges: AnalyzeResultInvalidRange[]; + newVersions: AnalyzeResultNewVersion[]; + newRanges: AnalyzeResultNewRange[]; +}; + +function parseLockfile(lockfileContents: string) { + try { + return { + object: parseSyml(lockfileContents), + type: 'success', + }; + } catch (err) { + return { + object: null, + type: err, + }; + } +} + +// the new yarn header is handled out of band of the parsing +// https://github.com/yarnpkg/berry/blob/0c5974f193a9397630e9aee2b3876cca62611149/packages/yarnpkg-core/sources/Project.ts#L1741-L1746 +const NEW_HEADER = `${[ + `# This file is generated by running "yarn install" inside your project.\n`, + `# Manual changes might be lost - proceed with caution!\n`, +].join(``)}\n`; + +function stringifyLockfile(data: LockfileData, legacy: boolean) { + return legacy + ? legacyStringifyLockfile(data) + : NEW_HEADER + stringifySyml(data); +} +// taken from yarn parser package +// https://github.com/yarnpkg/berry/blob/0c5974f193a9397630e9aee2b3876cca62611149/packages/yarnpkg-parsers/sources/syml.ts#L136 +const LEGACY_REGEX = /^(#.*(\r?\n))*?#\s+yarn\s+lockfile\s+v1\r?\n/i; + +// these are special top level yarn keys. +// https://github.com/yarnpkg/berry/blob/9bd61fbffb83d0b8166a9cc26bec3a58743aa453/packages/yarnpkg-parsers/sources/syml.ts#L9 +const SPECIAL_OBJECT_KEYS = [ + `__metadata`, + `version`, + `resolution`, + `dependencies`, + `peerDependencies`, + `dependenciesMeta`, + `peerDependenciesMeta`, + `binaries`, +]; + +export class Lockfile { + static async load(path: string) { + const lockfileContents = await fs.readFile(path, 'utf8'); + const legacy = LEGACY_REGEX.test(lockfileContents); + const lockfile = parseLockfile(lockfileContents); + if (lockfile.type !== 'success') { + throw new Error(`Failed yarn.lock parse with ${lockfile.type}`); + } + + const data = lockfile.object as LockfileData; + const packages = new Map(); + + for (const [key, value] of Object.entries(data)) { + if (SPECIAL_OBJECT_KEYS.includes(key)) continue; + + const [, name, range] = ENTRY_PATTERN.exec(key) ?? []; + if (!name) { + throw new Error(`Failed to parse yarn.lock entry '${key}'`); + } + + let queries = packages.get(name); + if (!queries) { + queries = []; + packages.set(name, queries); + } + queries.push({ range, version: value.version }); + } + + return new Lockfile(path, packages, data, legacy); + } + + private constructor( + private readonly path: string, + private readonly packages: Map, + private readonly data: LockfileData, + private readonly legacy: boolean = false, + ) {} + + /** Get the entries for a single package in the lockfile */ + get(name: string): LockfileQueryEntry[] | undefined { + return this.packages.get(name); + } + + /** Returns the name of all packages available in the lockfile */ + keys(): IterableIterator { + return this.packages.keys(); + } + + /** Analyzes the lockfile to identify possible actions and warnings for the entries */ + analyze(options?: { filter?: (name: string) => boolean }): AnalyzeResult { + const { filter } = options ?? {}; + const result: AnalyzeResult = { + invalidRanges: [], + newVersions: [], + newRanges: [], + }; + + for (const [name, allEntries] of this.packages) { + if (filter && !filter(name)) { + continue; + } + + // Get rid of and signal any invalid ranges upfront + const invalid = allEntries.filter(e => !semver.validRange(e.range)); + result.invalidRanges.push( + ...invalid.map(({ range }) => ({ name, range })), + ); + + // Grab all valid entries, if there aren't at least 2 different valid ones we're done + const entries = allEntries.filter(e => semver.validRange(e.range)); + if (entries.length < 2) { + continue; + } + + // Find all versions currently in use + const versions = Array.from(new Set(entries.map(e => e.version))).sort( + (v1, v2) => semver.rcompare(v1, v2), + ); + + // If we're not using at least 2 different versions we're done + if (versions.length < 2) { + continue; + } + + const acceptedVersions = new Set(); + for (const { version, range } of entries) { + // Finds the highest matching version from the the known versions + // TODO(Rugvip): We may want to select the version that satisfies the most ranges rather than the highest one + const acceptedVersion = versions.find(v => semver.satisfies(v, range)); + if (!acceptedVersion) { + throw new Error( + `No existing version was accepted for range ${range}, searching through ${versions}, for package ${name}`, + ); + } + + if (acceptedVersion !== version) { + result.newVersions.push({ + name, + range, + newVersion: acceptedVersion, + oldVersion: version, + }); + } + + acceptedVersions.add(acceptedVersion); + } + + // If all ranges were able to accept the same version, we're done + if (acceptedVersions.size === 1) { + continue; + } + + // Find the max version that we may want bump older packages to + const maxVersion = Array.from(acceptedVersions).sort(semver.rcompare)[0]; + // Find all existing ranges that satisfy the new max version, and pick the one that + // results in the highest minimum allowed version, usually being the more specific one + const maxEntry = entries + .filter(e => semver.satisfies(maxVersion, e.range)) + .map(e => ({ e, min: semver.minVersion(e.range) })) + .filter(p => p.min) + .sort((a, b) => semver.rcompare(a.min!, b.min!))[0]?.e; + if (!maxEntry) { + throw new Error( + `No entry found that satisfies max version '${maxVersion}'`, + ); + } + + // Find all entries that don't satisfy the max version + for (const { version, range } of entries) { + if (semver.satisfies(maxVersion, range)) { + continue; + } + + result.newRanges.push({ + name, + oldRange: range, + newRange: maxEntry.range, + oldVersion: version, + newVersion: maxVersion, + }); + } + } + + return result; + } + + remove(name: string, range: string): boolean { + const query = `${name}@${range}`; + const existed = Boolean(this.data[query]); + delete this.data[query]; + + const newEntries = this.packages.get(name)?.filter(e => e.range !== range); + if (newEntries) { + this.packages.set(name, newEntries); + } + + return existed; + } + + /** Modifies the lockfile by bumping packages to the suggested versions */ + replaceVersions(results: AnalyzeResultNewVersion[]) { + for (const { name, range, oldVersion, newVersion } of results) { + const query = `${name}@${range}`; + + // Update the backing data + const entryData = this.data[query]; + if (!entryData) { + throw new Error(`No entry data for ${query}`); + } + if (entryData.version !== oldVersion) { + throw new Error( + `Expected existing version data for ${query} to be ${oldVersion}, was ${entryData.version}`, + ); + } + + // Modifying the data in the entry is not enough, we need to reference an existing version object + const matchingEntry = Object.entries(this.data).find( + ([q, e]) => q.startsWith(`${name}@`) && e.version === newVersion, + ); + if (!matchingEntry) { + throw new Error( + `No matching entry found for ${name} at version ${newVersion}`, + ); + } + this.data[query] = matchingEntry[1]; + + // Update our internal data structure + const entry = this.packages.get(name)?.find(e => e.range === range); + if (!entry) { + throw new Error(`No entry data for ${query}`); + } + if (entry.version !== oldVersion) { + throw new Error( + `Expected existing version data for ${query} to be ${oldVersion}, was ${entryData.version}`, + ); + } + entry.version = newVersion; + } + } + + async save() { + await fs.writeFile(this.path, this.toString(), 'utf8'); + } + + toString() { + return stringifyLockfile(this.data, this.legacy); + } +} diff --git a/plugins/devtools-common/.eslintrc.js b/plugins/devtools-common/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/devtools-common/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/devtools-common/README.md b/plugins/devtools-common/README.md new file mode 100644 index 0000000000..5be5e58773 --- /dev/null +++ b/plugins/devtools-common/README.md @@ -0,0 +1,3 @@ +# DevTools Common + +Common types and permissions for the DevTools plugin. diff --git a/plugins/devtools-common/api-report.md b/plugins/devtools-common/api-report.md new file mode 100644 index 0000000000..dae445abde --- /dev/null +++ b/plugins/devtools-common/api-report.md @@ -0,0 +1,74 @@ +## API Report File for "@backstage/plugin-devtools-common" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BasicPermission } from '@backstage/plugin-permission-common'; +import { JsonValue } from '@backstage/types'; + +// @public (undocumented) +export type ConfigError = { + name: string; + message: string; + messages?: string[]; + stack?: string; +}; + +// @public (undocumented) +export type ConfigInfo = { + config?: JsonValue; + error?: ConfigError; +}; + +// @public (undocumented) +export const devToolsAdministerPermission: BasicPermission; + +// @public (undocumented) +export const devToolsConfigReadPermission: BasicPermission; + +// @public (undocumented) +export const devToolsExternalDependenciesReadPermission: BasicPermission; + +// @public (undocumented) +export type DevToolsInfo = { + operatingSystem: string; + nodeJsVersion: string; + backstageVersion: string; + dependencies: PackageDependency[]; +}; + +// @public (undocumented) +export const devToolsInfoReadPermission: BasicPermission; + +// @public (undocumented) +export type Endpoint = { + name: string; + type: string; + target: string; +}; + +// @public (undocumented) +export type ExternalDependency = { + name: string; + type: string; + target: string; + status: string; + error?: string; +}; + +// @public (undocumented) +export enum ExternalDependencyStatus { + // (undocumented) + healthy = 'Healthy', + // (undocumented) + unhealthy = 'Unhealthy', +} + +// @public (undocumented) +export type PackageDependency = { + name: string; + versions: string; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/devtools-common/package.json b/plugins/devtools-common/package.json new file mode 100644 index 0000000000..e4ceb3ee58 --- /dev/null +++ b/plugins/devtools-common/package.json @@ -0,0 +1,35 @@ +{ + "name": "@backstage/plugin-devtools-common", + "description": "Common functionalities for the devtools plugin", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "common-library" + }, + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/types": "workspace:^" + }, + "devDependencies": { + "@backstage/cli": "workspace:^" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/devtools-common/src/index.ts b/plugins/devtools-common/src/index.ts new file mode 100644 index 0000000000..ca1cec9ac1 --- /dev/null +++ b/plugins/devtools-common/src/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2022 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. + */ + +export * from './types'; +export * from './permissions'; diff --git a/plugins/devtools-common/src/permissions.ts b/plugins/devtools-common/src/permissions.ts new file mode 100644 index 0000000000..ae62cfdc19 --- /dev/null +++ b/plugins/devtools-common/src/permissions.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2022 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. + */ + +import { createPermission } from '@backstage/plugin-permission-common'; + +/** + * @public + */ +export const devToolsAdministerPermission = createPermission({ + name: 'devtools.administer', + attributes: { action: 'read' }, +}); + +/** + * @public + */ +export const devToolsInfoReadPermission = createPermission({ + name: 'devtools.info', + attributes: { action: 'read' }, +}); + +/** + * @public + */ +export const devToolsConfigReadPermission = createPermission({ + name: 'devtools.config', + attributes: { action: 'read' }, +}); + +/** + * @public + */ +export const devToolsExternalDependenciesReadPermission = createPermission({ + name: 'devtools.external-dependencies', + attributes: { action: 'read' }, +}); diff --git a/plugins/devtools-common/src/setupTests.ts b/plugins/devtools-common/src/setupTests.ts new file mode 100644 index 0000000000..813cdeaae3 --- /dev/null +++ b/plugins/devtools-common/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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. + */ + +export {}; diff --git a/plugins/devtools-common/src/types.ts b/plugins/devtools-common/src/types.ts new file mode 100644 index 0000000000..d08232b155 --- /dev/null +++ b/plugins/devtools-common/src/types.ts @@ -0,0 +1,67 @@ +/* + * Copyright 2022 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. + */ + +import { JsonValue } from '@backstage/types'; + +/** @public */ +export type Endpoint = { + name: string; + type: string; + target: string; +}; + +/** @public */ +export type ExternalDependency = { + name: string; + type: string; + target: string; + status: string; + error?: string; +}; + +/** @public */ +export type DevToolsInfo = { + operatingSystem: string; + nodeJsVersion: string; + backstageVersion: string; + dependencies: PackageDependency[]; +}; + +/** @public */ +export type PackageDependency = { + name: string; + versions: string; +}; + +/** @public */ +export enum ExternalDependencyStatus { + healthy = 'Healthy', + unhealthy = 'Unhealthy', +} + +/** @public */ +export type ConfigInfo = { + config?: JsonValue; + error?: ConfigError; +}; + +/** @public */ +export type ConfigError = { + name: string; + message: string; + messages?: string[]; + stack?: string; +}; diff --git a/plugins/devtools/.eslintrc.js b/plugins/devtools/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/devtools/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/devtools/README.md b/plugins/devtools/README.md new file mode 100644 index 0000000000..d20142a746 --- /dev/null +++ b/plugins/devtools/README.md @@ -0,0 +1,375 @@ +# DevTools + +Welcome to the DevTools plugin! + +## Features + +The DevTools plugin comes with two tabs out of the box. + +### Info + +Lists helpful information about your current running Backstage instance such as: OS, NodeJS version, Backstage version, and package versions. + +![Example of Info tab](./docs/devtools-info-tab.png) + +### Config + +Lists the configuration being used by your current running Backstage instance. + +![Example of Config tab](./docs/devtools-config-tab.png) + +## Optional Features + +The DevTools plugin can be setup with other tabs with additional helpful features. + +### External Dependencies + +Lists the status of configured External Dependencies based on your current running Backstage instance's ability to reach them + +![Example of external dependencies tab](./docs/devtools-external-dependencies.png) + +## Setup + +The following sections will help you get the DevTools plugin setup and running. + +### Backend + +You need to setup the [DevTools backend plugin](../devtools-backend/README.md) before you move forward with any of the following steps if you haven't already. + +### Frontend + +To setup the DevTools frontend you'll need to do the following steps: + +1. First we need to add the `@backstage/plugin-devtools` package to your frontend app: + + ```sh + # From your Backstage root directory + yarn add --cwd packages/app @backstage/plugin-devtools + ``` + +2. Now open the `packages/app/src/App.tsx` file +3. Then after all the import statements add the following line: + + ```ts + import { DevToolsPage } from '@backstage/plugin-devtools'; + ``` + +4. In this same file just before the closing ``, this will be near the bottom of the file, add this line: + + ```ts + } /> + ``` + +5. Next open the `packages/app/src/components/Root/Root.tsx` file +6. We want to add this icon import after all the existing import statements: + + ```ts + import BuildIcon from '@material-ui/icons/Build'; + ``` + +7. Then add this line just after the `` line: + + ```ts + + ``` + +8. Now run `yarn dev` from the root of your project and you should see the DevTools option show up just below Settings in your sidebar and clicking on it will get you to the [Info tab](#info) + +## Customizing + +The DevTools plugin has been designed so that you can customize the tabs to suite your needs. You may only want some or none of the out of the box tabs or you may want to add your own. The following sections explains how to do that (assuming you've already done the [setup steps](#setup)). As part of this example we'll also be showing how you can add the optional [External Dependencies](#external-dependencies) tab. + +1. In the `packages/app/src/components` folder create a new sub-folder called `devtools` +2. Then in this new `devtools` folder add a file called `CustomDevToolsPage.tsx` +3. In the `CustomDevToolsPage.tsx` file add the following content: + + ```tsx + import { + ConfigContent, + ExternalDependenciesContent, + InfoContent, + } from '@backstage/plugin-devtools'; + import { DevToolsLayout } from '@backstage/plugin-devtools'; + import React from 'react'; + + export const DevToolsPage = () => { + return ( + + + + + + + + + + + + ); + }; + + export const customDevToolsPage = ; + ``` + +4. Now open the `packages/app/src/App.tsx` file and add the following import after all the existing import statements: + + ```ts + import { customDevToolsPage } from './components/devtools/CustomDevToolsPage'; + ``` + +5. Then we need to adjust our route as follows + + ```diff + - } /> + + } > + + {customDevToolsPage} + + + ``` + +6. Now run `yarn dev` from the root of your project. When you go to the DevTools you'll now see you have a third tab for [External Dependencies](#external-dependencies) + +With this setup you can add or remove the tabs as you'd like or add your own simply by editing your `CustomDevToolsPage.tsx` file + +## Permissions + +The DevTools plugin supports the [permissions framework](https://backstage.io/docs/permissions/overview), the following sections outline how you can use them with the assumption that you have the permissions framework setup and working. + +**Note:** These sections are intended as guidance and are completely optional. The DevTools plugin will work with the permission framework off or on without any specific policy setup. + +### Secure Sidebar Option + +To use the permission framework to secure the DevTools sidebar option you'll want to do the following: + +1. First we need to add the `@backstage/plugin-devtools-common` package to your frontend app: + + ```sh + # From your Backstage root directory + yarn add --cwd packages/app @backstage/plugin-devtools + ``` + +2. Then open the `packages/app/src/components/Root/Root.tsx` file +3. The add these imports after all the existing import statements: + + ```ts + import { devToolsAdministerPermission } from '@backstage/plugin-devtools-common'; + import { RequirePermission } from '@backstage/plugin-permission-react'; + ``` + +4. Then make the following change: + + ```diff + - + + }> + + + + + ``` + +### Secure the DevTools Route + +To use the permission framework to secure the DevTools route you'll want to do the following: + +1. First we need to add the `@backstage/plugin-devtools-common` package to your frontend app (skip this step if you've already done this): + + ```sh + # From your Backstage root directory + yarn add --cwd packages/app @backstage/plugin-devtools-common + ``` + +2. Then open the `packages/app/src/App.tsx` file +3. The add this import after all the existing import statements: + + ```ts + import { devToolsAdministerPermission } from '@backstage/plugin-devtools-common'; + ``` + +4. Then make the following change: + + ```diff + - } /> + + + + + + + + } + + /> + ``` + +Note: if you are using a `customDevToolsPage` as per the [Customizing](#customizing) documentation the changes for Step 4 will be: + +```diff +- } /> ++ ++ ++ ++ } ++ > ++ {customDevToolsPage} ++ +``` + +### Permission Policy + +Here is an example permission policy that you might use to secure the DevTools plugin: + +```ts +// packages/backend/src/plugins/permission.ts + +class TestPermissionPolicy implements PermissionPolicy { + async handle(request: PolicyQuery): Promise { + if (isPermission(request.permission, devToolsAdministerPermission)) { + if ( + user?.identity.ownershipEntityRefs.includes( + 'group:default/backstage-admins', + ) + ) { + return { result: AuthorizeResult.ALLOW }; + } + return { result: AuthorizeResult.DENY }; + } + + if (isPermission(request.permission, devToolsInfoReadPermission)) { + if ( + user?.identity.ownershipEntityRefs.includes( + 'group:default/backstage-admins', + ) + ) { + return { result: AuthorizeResult.ALLOW }; + } + return { result: AuthorizeResult.DENY }; + } + + if (isPermission(request.permission, devToolsConfigReadPermission)) { + if ( + user?.identity.ownershipEntityRefs.includes( + 'group:default/backstage-admins', + ) + ) { + return { result: AuthorizeResult.ALLOW }; + } + return { result: AuthorizeResult.DENY }; + } + + if ( + isPermission( + request.permission, + devToolsExternalDependenciesReadPermission, + ) + ) { + if ( + user?.identity.ownershipEntityRefs.includes( + 'group:default/backstage-admins', + ) + ) { + return { result: AuthorizeResult.ALLOW }; + } + return { result: AuthorizeResult.DENY }; + } + + return { result: AuthorizeResult.ALLOW }; + } +} +``` + +To use this policy you'll need to make sure to add the `@backstage/plugin-devtools-common` package to your backend you can do that by running this command: + +```sh +# From your Backstage root directory +yarn add --cwd packages/backend @backstage/plugin-devtools-common +``` + +You'll also need to add these imports: + +```ts +import { + devToolsAdministerPermission, + devToolsConfigReadPermission, + devToolsExternalDependenciesReadPermission, + devToolsInfoReadPermission, +} from '@backstage/plugin-devtools-common'; +``` + +**Note:** The group "group:default/backstage-admins" is simply an example and does not exist. You can point this to any group you have in your catalog instead. + +### Customizing with Permissions + +If you followed the [Customizing](#customizing) documentation and want to use permission there this is what your `CustomDevToolsPage.tsx` would look like: + +```tsx +import { + ConfigContent, + ExternalDependenciesContent, + InfoContent, +} from '@backstage/plugin-devtools'; +import { DevToolsLayout } from '@backstage/plugin-devtools'; +import { + devToolsConfigReadPermission, + devToolsExternalDependenciesReadPermission, + devToolsInfoReadPermission, +} from '@backstage/plugin-devtools-common'; +import { RequirePermission } from '@backstage/plugin-permission-react'; +import React from 'react'; + +const DevToolsPage = () => { + return ( + + + + + + + + + + + + + + + + + + ); +}; + +export const customDevToolsPage = ; +``` + +## Configuration + +The following sections outline the configuration for the DevTools plugin + +### External Dependencies Configuration + +If you decide to use the External Dependencies tab then you'll need to setup the configuration for it in your `app-config.yaml`, if there is no config setup then the tab will be empty. Here's an example: + +```yaml +devTools: + externalDependencies: + endpoints: + - name: 'Google' + type: 'fetch' + target: 'https://google.ca' + - name: 'Google Public DNS' + type: 'ping' + target: '8.8.8.8' +``` + +Configuration details: + +- `endpoints` is an array +- `name` is the friendly name for your endpoint +- `type` can be either `ping` or `fetch` and will perform the respective action on the `target` +- `target` is either a URL or server that you want to trigger a `type` action on diff --git a/plugins/devtools/api-report.md b/plugins/devtools/api-report.md new file mode 100644 index 0000000000..3933d50e76 --- /dev/null +++ b/plugins/devtools/api-report.md @@ -0,0 +1,59 @@ +## API Report File for "@backstage/plugin-devtools" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { default as default_2 } from 'react'; +import { RouteRef } from '@backstage/core-plugin-api'; +import { TabProps } from '@material-ui/core'; + +// @public (undocumented) +export const ConfigContent: () => JSX.Element; + +// @public +export const DevToolsLayout: { + ({ children }: DevToolsLayoutProps): JSX.Element; + Route: (props: SubRoute) => null; +}; + +// @public (undocumented) +export type DevToolsLayoutProps = { + children?: default_2.ReactNode; +}; + +// @public (undocumented) +export const DevToolsPage: () => JSX.Element; + +// @public (undocumented) +export const devToolsPlugin: BackstagePlugin< + { + root: RouteRef; + }, + {}, + {} +>; + +// @public (undocumented) +export const ExternalDependenciesContent: () => JSX.Element; + +// @public (undocumented) +export const InfoContent: () => JSX.Element; + +// @public (undocumented) +export type SubRoute = { + path: string; + title: string; + children: JSX.Element; + tabProps?: TabProps< + default_2.ElementType, + { + component?: default_2.ElementType; + } + >; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/devtools/dev/index.tsx b/plugins/devtools/dev/index.tsx new file mode 100644 index 0000000000..00c966a801 --- /dev/null +++ b/plugins/devtools/dev/index.tsx @@ -0,0 +1,27 @@ +/* + * Copyright 2022 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. + */ +import React from 'react'; +import { createDevApp } from '@backstage/dev-utils'; +import { devToolsPlugin, DevToolsPage } from '../src/plugin'; + +createDevApp() + .registerPlugin(devToolsPlugin) + .addPage({ + element: , + title: 'Root Page', + path: '/devtools', + }) + .render(); diff --git a/plugins/devtools/docs/devtools-config-tab.png b/plugins/devtools/docs/devtools-config-tab.png new file mode 100644 index 0000000000000000000000000000000000000000..b5c22f691b38eb5aa48d84e1154495362d3fd71b GIT binary patch literal 142913 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 LY=eO3(H4BK4of#|RdQ|W3Bmgx5t#W8GlRr~2L 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 W(Y?6JtE30W7f4b;qEgH-@c#k4-*{L6 literal 0 HcmV?d00001 diff --git a/plugins/devtools/docs/devtools-external-dependencies.png b/plugins/devtools/docs/devtools-external-dependencies.png new file mode 100644 index 0000000000000000000000000000000000000000..9356d83ef52fd5eec11096249ea3b85cb8f6323d GIT binary patch literal 107183 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

^fiXglKH{Nr2w4B`tOP0>+rs&!>& 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 z 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 literal 0 HcmV?d00001 diff --git a/plugins/devtools/docs/devtools-info-tab.png b/plugins/devtools/docs/devtools-info-tab.png new file mode 100644 index 0000000000000000000000000000000000000000..739045a77fbc872d763934214e74e998e0b9c4f6 GIT binary patch literal 126106 zcmZU)1z1#J*ER|QA`*jugmi<%5K1#ch;*ZXlyr9wUDDkRf`pVH-QC?F-JLV!0B7`l z|Nr~WIdfh6nrA<`_p{f&*SgnQJ5)(Q68q(wmq#u=K2s*>i8j_CQC_S1O8Dx7>by}6)>K%3 z&sS-m&%%AuDCByZA5gvgTtvq~At13@j4w4|JfdRNWyZLEY%03f{E1(jD3k9u?N+w! z{E}RH-DfeWO!-|ry+3N|XsLUSo#l~|?*(^8JOdm;B3N&-Mn1I$OO7_ZYGC1TRr!e} z2LOU9t&JyzZVqjKjwWbwn0+n^cwR@|o`-kXgp=H?NcM}F;5>J$dZ;iio?UNRXz2XK zIktUttUmLGq`~`zoI+B*$0L2g>ep5h`kblsB5AqOPmcSzcaU}SXQm+ug} z{n=PU+C*L+=`EuC0tq?T90?UsLPi{K5C`I+#C=0TNBrXvO{6G#aAQu-Kq6eF!yN#2+ z8=H+I?SCfuKl6Mrb~JP_w{tSLwW0buufBn;vy(6l&EJLo@9#g?Y3yeHKP%Zd{`a;J zHwgUO0_0@p0RG>(5krOk)(R+@yBS+)elWL2Y!6}$5HBB((7*owzn1@5@qdlf_@9xS z+?@Zfq5rGt|2y=Pqp^dytuqn*Xl*zZ?HMPzd<<*8kU5{AZv4)gm?;^il}; ze;*p?CDDo5G7^#qlJo~rH8+PK(7eQ@krLAcE<;z+@6@w> zvQMXrf0qB8@={oJEIBz{s%ZOJ(W+KiQc~iY;kx3^Sl)Q>lLoyBebjw@Dx6%x`J2ys!%aVc@#T-~yWgUNCIaWm)VD$H%Emvo)O!jB%apcx zum+;{yA5+wSA%H1cSp&4&NmrL$JI}2?-=)sx%WfnL??sW1$3?R@1Yjr6rPZ?y@`$f zgNd+~w>&dH?>d6GTd}3~$5G&pW}_%pl3RP7B&*N!t!7;}NTN=VlZf~m?0V-qW+Gu5 z{fl4hovdp#{>)ST!TOCJwjG29(;Cd`rx>SLYo6MP3TkLDu=j4&NJ$4K5qwysoO&Aa z4ug-tdEoVFn|2hho{6@p?iXA#Gi}GU1@=Xk+{b4MvIwk6xuTJRiOM39ulc!JPu^Uf zrBJ~z2N+=+Ua`RJEk*F%Ug~qfwOJ*PeBCYm1Jf@1Ht7~Is-f&{o8O8T>kl#cdEUO& zs=Wg@%16BJU+mxFGW8n=C)S01XzCQ&*t<(>{rDJz%hLDxQQtwV2vxqcjrEYwKmC$x z@v;9(tRr_@c>>decsF1Yrxue^c#39O=okA33Tlo;?;jco)-0JQtNcxv}2@=W(b43#IXYhNo6oH?u&uYkNN&8liIvnEpUts`y|A7qT+P?z(XoOnm)A0;)Lxi0L_f<7 zU;{gIAM$5)UrMu@r}@@1r=(|o3XhY7(&b`g>1>PosDg<`Z+NJTe|zfiZF0+9BMn89 zh16j%TP_Z|>bsO{%$E@YTq=slakfWLDNL-$hQi}nFOQ!k6A@h+Y`HKWeLLXEhkohqUrOMJLf-C`VU){dnyuVylVOf+QM_dWAxc@YF$jU~=FHdSUuQX+7-1f|_SzFju za^VtFhs$cd>($i>ig8Pf3r_bTd9Fop#od(O>#jfoi(VAKi-HF;$9ZPqiW9*z!sk2q zRqUxyWSV!<#*$y3n=2?0ruw1b#wg!`eLVvofq@>`46|dCIJ0>m-s?}*WXey=+QF(D zLtuyCyth8J9-aDZcbsPAWO^^|i{G-H#w45&x3&&pW`S@+dFEz(Urq$I_s*i}$dQkm zJH2~H(K|bTeVpPU4 ztvHOCHdYq$T&PLgKWvyhpxrgzOk=tYDz%~9AcF4&H{PX^)EsIiFETX?wcUT^y!=Ay zv|Sv|2%bbv_N&J}xHverW4}!h1UcUphv4!Rx-bqGlVhg$MM-3y(>M>$*#Lg&#C85d z&V^DyH#gZb3S>zfeG!vA~9VXpfXv`WDADjZ$hk9(@r&FNblCNvzw zkj2MXQRhAmA`2e6X*}G!P(PVG<{r0J0oMKz$c&*l-R1L94i@2-%5s(ZoMorIbc?3w zm|vf#%-Q%xX`zXDoIgUaULah056{3u{cr{~jJ;bYa%xR`plc(5HxjcSD^F<6qD@(j z%idHNS1|Rslv;-K$Zf4$x}B_`3*~o~SpNTS1rrF`j_id85qxt6};QA0Alb~D1_ zC<*E%WvlJXKbu65oq83Vpel3AI1tMEL`QNyB4PeIn_0e&kDqheo#D006s@@!%X9T= zYYsI!qk*8eM5AsGMrzws?9^=qrU7P0+6}za&c;IL#AcSnTSU(cjbvs=G^ss>T^e{;5|@X8%O^oya^#Pc8;h znUS*ktuP5Zc83~~IwzTIn*RW)jCwg?2Y}wnny|RajuPI)tW4B6qs_s*e9Ug`bnz_P zY9-&QII+v2Dpn4qeE4WqIF^dA1XnzmEfKvI{sL% zk8u(82XWKAP7QQ?Y_nOYs?U3Wbo7}mPQw);BA||Se2Rv%fM@ab!=#SnlOaDm2c0{J z7Z}Q>gW%Geov&>Fr1n)<1u&{Z4XKTZ7zZ;mU+X%q$E(J_e@jvuj0)nz^rME$r6~y; zfr>_9YSTcFyAeMx8Z6$bXSFx_E~3O+mE!ByiG9{8P#G)RVTB%rN`-x1^q$4nrabTe z$XdZuh$&wqy3suv?>|{9``Y$Vs0#x4$~d)R40qX|s~pUXw?xyO7|d-#=n)r5n(7I? zs_fzMM+N}AbM3-sYM-I4d~@C_ay2qoF@ds6xx&d1oqn`C>5^4Qd&U&Q;Hwhy)BTF` zW_%Dr)f6SCAHs*JhFZ!WWhQv;jT_y`i$5m{S9@~?R0sdX(z8TLcPT-dpW$y$ zA0Zg-LG5PuiT>0sO0}AR!!JIiZ5KGwkVWJ%??+W~=FRAjJ^KbR_#d*pa|4p2C3U_O zu{(RFDV&;3yst`8(CcyeHjG~X>5wN&i9%&pro1?`1F=Z;s`<5k@^RM|bFht@Z-s#Y zQSG6~y^UGpLv?$}qIS%Z_pE7nT^0IS;z`d+L;Q|C3iD-M0yV9KLg|>QQU0*4O7F17 z@+342m`bMwRnc_Y?_Q)Bb&uha809;PUPPYYX6`2RJbEd}Q0W^PV<2l)pfERtir&45 zufpC!Q!3*B1!7-ifa?}pprxJtN>b;oZlU@LwWezOc51j64Z*Wyw91f9MmdJS&4vr1 zw@0#)l2aAWw}zZHc%;&Ja!RQGK<#dSFt=zo&fi>O(7CwKP-frsOUQGBUIPzf??b^i zSJ@%hQwvaA8iiTLc*^X(`5bA%wN}$bXd6Cw>%G|;@&Jf)hq4FkG{q<*v*gTKJ~bBrF^)j z+S$ZmU)u{Vxh;J|EPij?Y>nrbM7LuKd5rQCrn=cNZ9Q^WS<5p_t4_A3#=Ywa)Zm|t zin$FN)4Kp+2n^j?Yd#7FySP~hD0;1thC=BbM!+>XPD@|31w#LtoJ>oZZem1 z4BugLAlI@p+r!TH%AQ^FIG&1-dO_h+=4YzdWx=qfZojQi+N$4K^;}|pqX(bZfXRcs zE-yN}W7$VKyy#4@O$ZHr0<iH!NSnifxqmQ;wPr;dg$85o@uZA?-kOXc8B3b3m!r7xzF` zs7>aqnZpbk5~Pzb{H4?Qvh%g=W(OZg6w7ZIh2P-aU=@0nlSd!OB zz38{U`u|ykEaR&{AnV?0rH+NY3Y*Lh(0NMmxa*>1aT{H1F*%9e2oN!b!uOl7Ej^m- zGiLm@)G^7vcMAY+cGSUJbz-k4&;Xf_b-nlI$4I8} z=u6i>o|*^IPA8}{w-Tx2!0E*LKsijmiL2{g$vslMeBA;c&s$>D+B8clMSP8!=6Gr*vsr`+;ol*!^6zJ~Hf*1|rI{pN)K;Nn|BVMMyL`!k~X zBG*ArYchyv8$#Cx@t2>ok0ePMR*z{^>2nM`2Fk=6JiUT_>{*|g*+@xa3=AaDQh~~F zfXmn=>$a~BF$eX@ycOOi$H*9r=l64Jm^tl0rxbl=a>awK%=s1E*V-Fg%i%Fq8^gmz z8#@5S&rYJxMJ`AfySeb4YC8&`R6Z9d>?ro~h46e=-!D|~{)^C{gg(!1jE+B7;*IQJ zOrsThu>;CyeYr%0Z_hs@A0{Fw%TJSY3}r1`|B3y)lLS~%H_2&0-kqt-ZUiU~1Lm9t zmg#Vj^g+01{)!mNRrp&!Tp$ECAy#28|#0YNe?Ul*#^2@i)L>wi9u5s6M7OSdZc z_BDBaWtpYtXjoxHDHkP1W!P;auOMFWTY&f2quCtgT9i(F3904XYo(0SU=PA!YmV$m zTNP%eQ$S2`UQm?!C1tCF+Vgv1*N$pPrrG zjv%@RoI5VBc?!a4Y_W=}rTdu603|6MFQvN?Nz(#}-==B`1Lt#Be$Df>4y04FDK8opeUP$6_Mk zXbafu#TdQ}$(&b0BxAxg-+rTpi_Ul``8?kcjT#}NQZBUENpe;lwh(7fo-+8;@m8so zU#}5kOEy1cTY=W!#ZMOK8$dyR$m{L!6=qqN04RBftzHbA!2xjQMkU=i!>U>K%m(LN!I4X4eA$^4JF8}0gg+i6Z_KZ3Uf`@>`cYz0Dj|l)W-J;PgyS243#^;<$r%P(? zKfH%4zB^MGST#x-d$ND_iVd3#`k!aMQv;3Lo*>gc0 zhZomK*&JNKn5LejF;(1FD=1bzTSxx^M)Lr-5#qL%1EJy>81{nzFDQh2M6foUT|9#n z!8g*cEOzP=g@3g+D@ zgUBH3udg@zx=v9zZ4<7Omqh6)6I6W;S|(WCg_z+REym6AR#KQj*cl?)}&9BZgok zfj2!lX7xb2%;xUFMv-%)kdyd#Xm9;kx<9`q${u&iX3rH$D>(eF@M{903MfRnDCpn) z=wlL?zi_Et5;`6+=_|6Yd)ATObzn6>)_aI7N9>TI3z%O#g?E6IlJ!A2nI6gG4 zR*V-}rR{hdX>b_-J3@hjsdpcEPw&My~~p~%bPK-i4`z%TJ1_{nP%R2;M&@ooO`t$qga1&^j^t2NcSva#)3 z9-(%08hP1%P?!R&=Xo70N!@%q=I*M>9`xS3r>+5;U^;Bu3M_BUJ=gWtmL-3cOX2j@ zhC6RmyQiGf4bv+eqc{Mt2;a;zO&jjxqfEGDK$vFV9^WxUd{5?odd420p#IXHtdCSk zC97h&6X)m*b&K!S%O5z-bCupncXQ_-8}kb(J1=0zIJu12N1-oy(BqNG*aJGPug2N6 z+{H*EG+(6QC_Uf;bH$uflsA{yoyjm3nRk$y!KM)MKz>fqMf^D1ggY@X+JLgluh!u4 zc!pYd-myAPRFyb0!w`S9n*r?mgM3%BcvWv+ozDrrCDYx&4$_2T>d|XKcC|)n^aCz) z;e0sz0qVn`zJ$d=n@x$6A?(a@LC(T-XgEWSZ@8x&IObp+96XfPXj1DnhD?ijgTFP#A`WJ&LrD`c7)r{6EZ^6ajmW0 zMZJD#9VdzW+1a22Mf_gYJzU8fUFxQh!C63AK-s4dp$v$&Sm!Nlg%0%$kT_@6l5BZ) z%3ZGa6X6egSK#!~L}v6o+pT~7%)P)UGiu<K^FIY9$&5ik#h8=QeRpFeR4)R&_*n9aXmZp;A)B(r~GKlrp;Jg@L>=F}OIe?d;4 zUDnLOx5pT7xt)CaaeXzYS%>_naV_0^oZ10L2%Eu{g_HxsR79&6u}KEVe8=JOO@u_@ zUQHJbv8+#s2vZsqsbGRH-C0@qMnkD@ZeFbIy7I^3rok>*B{b&ky+jjXS0%Hdaj6Y6 z1l#fpC4LDJAHxE$HyctmD3OFKAU*X-D~^l-8?zaw(nmh>RwJc5_zX90kc&%IMhB;| z$4NH-%a4axSQS)L+cwE4+*}(ei-ytnxS#GXoMS56)k*}WS$(QXZ7Y58@B^n+S2f&X&0kQ!yJA9D>M!j>ZOWgT0uuwf*we+o_(^h*}LbwA*sUcvh+n7H9gjPuS zB?}_fkJ}aPzUW)dK3}-X2g_lt7TbICEHxO)FnS|Ul>JL<>qMoA-oBNtwY$X~J}=Ue zQ~FoF?o}OJ{&8EEZzCQeS7U1@e-*9O%FlEA5807A@i)hsStmUMh`?JjF@NjNM0#s+ zz*l&;WX)N+x~cgk=JL&*tyIkA_UAp1OLu;pz=8eM?4a@ZkBgh>l<`s5__?-eu@qPH z&u+OQ5ftK5lplC8k!sG~A}6qM4#95Ym(=B9I$rYv?tAhbttotQ*9&XLONPnxU6w2n zlyXDo(3^STYQrdb4?kdRr{QPqU%jt0EJ`j9TDOYs&LZ#G$~E}a8Xhj9EtAE!H_dpb zvpE*L6Ffe|Tqjd6sK8#XFF%(@co;TklwG90bLBtBQqaNDQ47cP-@Hm*NL7%D7HDeH zNERrLU+;&`T5^Pa0Bp)G9VCl4Q4r^P*r-NRA&~;lV%d%|y3wCJskgHRlL~Df&4)IpFwL1a<&VgWJRv<}-iFrF zFijo)lznNCEC&ORtc=$LWO}@ zpfqp}{{e)>;dPa(ZUx%gWDTy~^8#&$?7Q(9)X#)(zf!zwObGfpE<<-~7_*JfiSJK2 zDH+5U5a0(7Nsrm*k@|8VE`;{Y#Y#g?Vwd3ApXsiiKcBPRzF}YfYBr?0nt_k=gNx?^HTZc>Y??b&OiB#^D-F>?4xd>#u_)g>OFVgnko2Y48U8#j8 zVg3>76yqVF-^QYc(1;yVVP<=u)nTJ3X;>tC^n)2Z~7 z7wdvA<^3%`z9Pz~J8le&s$;J%6VLQihwvUMdy+aX5aC5<#z{^u#I?G-5tM!zn{M8? z=@-MZL~i|?p_1aP=2$~lCuh|nJYFM0dyuknUuTroXx#6xkGbEKu{4C*CBFtvuit)Do(Yp^iz-Ps2{B4bS`@8E>AP3Pq=BtX5 zxw=~kn~p*ER0G*C-AZSymwg9G1?plgPLQe0nD6Q7e{NJS5ICEi=i6{0*Z)#3gEb^r zWXBvYE%6nm{Pb&wkUwA3A@y$e@NOY!+uy*}g{3P9lC>b+LZ@&mFRlqlu57fT!z zgK+TPN=bnh0E;`vCXMie-_5HrSN0i>9k?x0C>{p>)3kKi)xeMyhbP53 zL36Whw?bVb2IS1bOw*TcV#^Sgxyyx+U9m;C+# zSnuLJux1a`5JdP$p5BZ1wMkv(!Mx)HA)fIx6mkJBMH2Plm@7U(H{j0lH}}KbH-41{TR4C3Hs( z^mB5_eAMb@-u{!so@#qZL9G6?)Vz~wScLxZ*!AqkiE|A{u|7IQc_}N9$YbVkhu;Wb z1aMkKfg?!MWAYo7-r{^A3k*7jd}!I`I3`c`p#8!|Q?&i|`7MXB0*R$;{1vaY>|mrw z*aN5~)kR}DolGsAfoG;3)asB6wIeRk3TunI2uxQ}F6b}ja)lbWyot1!oZDkM6i`W3 zxT^~IG}D$0M!mpD#r~c#%=m(V!oXE7fX;_BpY=cjfk(5VCvGqOm?2cr#h7qTe5R#rHvGX)M-~n7Nc0wjEEYhTboU6E#?#h*HFOwtoY7jn{`Ni zGN|{E+fFU=d_t9RmMEQUQmQ^*SHE*1j?dCuetBtE=@hvZtb%8YuR{2T`ky^@PvpR` z!jR8;W!udS^+3YJ(3Wer8K@b785kO>UK^W5Z(=niUl?rhJ+zP;&%mDK3)A5R*nJtM zpN{K$JGYwe^cidUqnbtu-tB8pj@*oUOO2lPv!M36II6T!Jtn!AA`(jU)b;2n=R5Az zq5_<8Z#h?cJ(ET6lt%-s_aiPvvxRFtE1_G+_vXM&F{-1;m$ClOk2*+74=!Vcqf18LS?O5sUQ*`e7+&n z?Ll9?`8R}3A-|8X*C%`5U=L%pcy~r*F^!H|ewQKc+!isRlgE1h5Qt-h*D-B~Cy>G= zs9Dit)IZIO8l9!2k;0=K9bKeOcP4|+fXDxC*H5};b%(V-!;_+aq-QsCAb))atxvi1 zaIT40%csVp?#^ufF)?v~IQ@qSLag3x_%)EgkjXqV|JXlT4r*j2FuKGtcibh|Tawk% z4M?R_6%042I)$^P5s*k5r>gYPpwTZF*b`Im80h2KIxdLCWpGZXzH8Zd9X`g{tUS0g zXC5+Sihx|2?7b942a75R`ldVVOAIV~(g-|ak>Os9<*iOUdl3-zZ_%w?{9p|!SW0v~ zKNF`gYMkU$jCa*}q9S~;6dZFr70|c<4@?(5nl>c72Ql11S@|5}9C92| zg}^98x`+xoC0OsFV}b3`aT|ft%W+WfHtt`k8A8y$5f9%7PJaA8i2gADJDsgx z1l~2E)p+2610xMJGOTuIY&@CY0Y6p+at~9?H&TdzEA5?rYv|~yI_C1Xh_g(;S@7YC z=08n%pxm))<^3{zD{Oxvw3!jT$z|MV*By*%9z0#g4us|D)<9))i66Hl@BNRJy?J(9 zwtCz`4|(IJKcIRS4HgEg>QR#QsW%wzNLOxu#{$}p)8_{WDb11sG!p|4%udd@PbX8N z>gwHEw?-tR_O3KJe>Q}?6Q3b;^*Rp?tJP!fg=}qF7NGHLzSSz#{nX3cu(yOWaOr*6 zvsk0&u}mtkzM%!?e!v8)h#WBZ(Q;Kl8lJ3u^TqOwK%{h#0ZMMJfL2|Y;iV)U|AdKki`Q7Hm$P`k=kZZ=N-78A#; z14*bo8x|UxV$6-$3CDyV1!~0c!ee(^j?`-ek=tC>NyDXqa%ra2H-l*`d(`7wj=g>; zJ*kCRCLaOori6vBcUMkI-##9YRsbL2nYPRzul>0aKHd2&riCn~d<9Q~Kf7!=8EP5s zGA)tQt7t^iQ%e0i@r)SOdJ?phlc{{0UNP>TcF6*gUDdV-M{Hy08zIH69@_ksoZoNe zXms&m!LUdke8y=-z8Qbgb7BqQSI2Dg6Fv#X8xa)&xv(_2_}5H1Jps=R%oN|Tkt9>u zjffxi&uhg8>Hgwgt@=NW`n!!9arW9u{hz`;vK>fuOOL4jc`Nz6ifnC;=g)fet)_63Qr~{Z9CCQRm&_IsAM9 z!NY}uuF7pAqTSuaT}3g*`7jKt>%76zq3AiftE0tF!(}7;zv(M~o00kb1)*TEtrE*N z69HpRgpVD;!)BfB7%5)H1|M?}&;Xw8%d$S%cA*?84y3y=eE?le<;GM=DK_JLA6?g{ z^L{aJ@29t{-gN#dLiOro%H1sa<8)g@-w0qIh@bWtAUU3Hllh8jV_TU3#L`Bg^9E0ywUyf1(Zozpy6fq%l$DLP_* z_|v*@@Vz7Ht_jc$9Ekzs!Ue0_lJ5dY8y?j;3iRv|@Mn>~(zy9uR+$j7zWdz$( zj6<&W6lH{~=pr`pWZXdZc#msSudBh|=jWhKxABWOG)Nkb+t|!RqOz}yF{IE0D5>a- z)Ig#1s<I~rkGO{3|&kQ+M;^Sp$ z@0fPFkr!I6l#-H1d|I6r+dbPgv+aNX4<1I>rD^-63Vg@WL8AP zzV74M+v~|rc)R;Pv7Y<49R92>o2WKC19a*g$NXJV5EQ=;8{bsutK3>M`#S*a85Tw* zMEVgb8$;ITl`w4CH0I#dC3-MoKOGoXclc#-^Ly-h9B9USV9c5A*l|K*a~nH9@hiuq z3E)U9#|!U5P7R}hnCMxh{YLvB#SGyP(4nGyVRcWYMsJbZte8B!AXsl2P!>Ku+7OWl z-3Kb;ts{|H`y^r?%x&{&=@F_$V47(^^frrVH^aSY!yEw7I3HE?5|fOrz>tpaNDy@q zi?Dpt`iSzRfiPmV7FsVBfGQ}$X>MTng5XLiPCa-wdQYe9ClD)|)=tip5|FAk5yhi!m#j!L1W}tUj2p|LN@|j=Q{JS1vY*Zt@Zt>Nw2O_2IhPk#@TXg~ z;P`}AGR1}qh3c{9v+mF;gkW3_#x1h(H|dfo@76_F*3oS%;#bSl>|NJVJxD(OL(TFl zOz9)Vv~Sh^o}s2GC&)*>Tzbd7>TZ=RhL<;$77_BvF^+$&BdbqySG8209@HYD+mdU;Tl&7lHGH?x zhikJ~ohk4Q=cYwjofPdY&G{&tMHHKGp9av z8v%onp1uI^=}HeJcvn4qSyuLw0K4v;tRfreu?F~hZrO86MCjE{GG93fB?@PXV{jn` zJ>}rt7H=!pc<2;Ge%pm6kj&u-z4c7=e86k{P8hzc!h84GGgn=GH z6C)r@=QFK(8~7>!BipKyf)`nN_C*XM%yB&;nWGb}CO;P+Pp>5xch+ztmqQJGKyen3 zl!wWc%|d?e_URSxw&YBM%*|QknC_x^Ktc~3k;p^Bvx}^E+dEA}NogVbHwEc$g_eh* zpyf*ytqa7w+0W~3=WJT=Q644QiQLb>KCf`2RK-l#ZY~FBzmeo0e@unDni1{pfZ-QRB1O_4kPx!aQ^S1YPa}dAk_-$;a)4ulP zZ7~wWwdP(Ol5)RfxI|()22joY@RMk2or5==z2x=gEuO0f{G^1hV^)!kT)Y_B%`<#c z9a5P$SO|@t2*2fG73Qc!lV;}-!_FWXAdfBgZ`gAOmJrg9c`by`un#0RNRdWLl78gS zC;4;nq`RG@qO|Y|luJn7b{WqSR`>8&@|=*qQskC2+RMOiIjGhb?%KFr7LL9QYu*^G zYm~R&)mkl>Z}a$3zf(XOW~*!Oe(neKEVx;!1I|Rgyk!Om0=J2(CeC$l{9M%|qa$(c zt~XT9H!z>KDR`eJw`piYdI0EZVaG?$xvp_Da&J@g>a$n)A?UC~hvwSHsl#$M_WK3J zu%U8zC`jva@UY^yaZPWg&$S2PHQ!;>Yp~lr7fizO?EY?hbqgTvg$n(GAbp@o3IR+A{Xzd{6YxMy4GTO^RAwZ;UwB!+h9FBX2 z;v)YYxYW&`@G_3QYsK>iFNfkFW#g}Da5B7LO5U3UC@k_6d>7ypXyV z1UFpm_JBR_vcrgk5A0FsTw6wtOCw*0kP>c3M5LIqGo11vaHI3OtppO8Dab#Os;no%YW2AOTUSYFjoo$Xb_>i(ymETv z7`FRUYX1J9uC3VjG*xdhZ7!xueN!~LP?YF#XDGaB_3DbD7$&@Qr|DbOn)*z$!g@TP{Pp#|=!zA9N9ql}KX8vEWUeEIF}3!$Kq}xuY%m)V zB;x2V`bhmN>IuGN#VPn(%jayTXmcq^fFfQhSWyW~^7j*l@MHh~uvCu!veeiAadqe; zTpff7;ju9Tkm+Q-sQY>Ts8x&_DnDQH1dSY_0`FQwgA{Prk>BwK@@SX#Rc}E$G`_b| z^-VbC;eqps`wW2fr%wD45J_C@otG_$b{DAcFz9ZR^JT(C3B4E*m8@Kc^!<1%wT~WA z@`6l{z8g+*X)!r;`|iR{yZ!w$dyFB1Cm-|RYzK}+2=cc;w>xkCG}tPm;$oM>nV2(K zI;>;~SFFQ%yif_qw&)$A4t7ia)hCmU?p4(|#}4*%{NR%yb73k>(*hXu1PO4nl}_I` zsBA;|w^;W59{q^9YI`gjh(j-_&R>zDW&m#Cr=A7vsj;H@hv%KDHbX-&F+YLAS@!jnG{12Yq`(&y;8>1Ru+rsXddwZ<#lJ?1SogKh%Y* zgYJXG!($m9ssx0tud}a~Hef)nhO5w{=^~&D%VQUduC-3YyW59O@>A{RO~7Nm+Zw;? zT)kCR z)%+as%1w(+g9mA|^tmZSymkO&?rewrh)DKYsE9j#(i87CvX;*ECY(EKH4< zb5|Ab?4Up9MH@<8%u1Ok+ze4obvD-?_-7Qq4~?`P1ss1yA2;#j=|hN)PDiEIU4NRu zkizk3XFzC*z37T!dsZXW?i}YOW&(=`zEiFVF?&~xTTYs@|w?{nrOVyCF{iY+{?$9~cS!+E{QBs_h+am9ta?LaY4 zH;hYT0S*E&slYsa*@U)s3NAG&*2+hp<{rDn)`uQx77tkcZ~_y5EJXPuYeWq=>B%LJ z@(@SbkrR)yumd1SyWMB0@gk&8jPBWJlu`X1rryqWch2wM;)W)h!7kmex9~AZok*VX zA8A|TR(yFny0Z-DR^%@;@MH8U{Q2B#1~yl+Wc>T-m=V;%w>$jj^SkO2`xfovF z=-0c&a|x#&g-vSvZAC6GAA=deB{%VP4`oqJ8@pg&>kr-i*6_7++?unSMc1~L!2rx7 z0k?LY6ANsJPIAdOu^9D9xd>>@<(&NidGr8fQNvPcrbnd}Z0KLNG&T>5~^n z2=e0tc*bB)^$7}q;eXaE>{cK+Tl$mrUhVnQChe1ITb7qvre(kd|I^9!+bpoAEgN9s4?}4Cm8)s<67g#B1ziM&?Q_?XA`-r_ zri9zCByhU_ARIIM|+nRl@Z61CF9jD-_*gK-r>z(hABzQdrObPemSFOC_ zT`xl9d`FlfEbV-Er0`f$NnY4Fk6WbHY0hWtgiRj(7p_;bxsEJXD3!n3Hh)dOt!_in zk&1D#(Igi zVsDoaM-rMe>)VYb$yN*NdleqNInZ(I^>h<`F33eb4cT3>>9nQs{)hEaV*hwo<%`fY ze=f|5g@x&k>{Yj}c7M7HL_|_Z@VxoYYOdQL3WZE}x{B8~%NP0a;rBn)yGefGzYZ@uB6~HakPesr z!;{}EFkeft6)hcy)h*9tjtD79zS+o)G3{JCbb}nJ-`SHniw+w61#|L*PqA}6^`%jO zc7LH!)i0ka*sO!wA+lPbg(3>y4ycS{M}xRQT~2^sS@9_61pQYU=*;0x|24*-Lxsny zddK-+1q%WT?AVnCxtU-abfdXyf~DGiu<|k|qE)hN;TYkV9lyvCZ0FXkZwIP_m-Oc* z@e9$@_DPo#h``gfF&MdsXaO?yg(TXOf!&oXX}{Mqg#;pIj{Y@9x8?Tz@qT47nu-Ej zB~4D6FEu3A^oKgk-MnBvce(`XxDOQAkI?T}tU!NDf51!KDOnhK}#;yBe zqtRcQ=A8Q8==*Nt&!FD?qOj9#dIkP^pULoV!osGA&$oC9p~1E|fNbD^XRdQWT;Ub~ zXp2Mo;W4m_fWJT*P~A!u+9TD_G*L?|9Z=6SupAe%%d))mIy7jcWJc1imoVaa;@i2! zn~A-H8`{jw5b>KC-YcRFDTl>JlxEgP-Wps+(rNUzn}v<8WY2P$-JVr1xOfl#$&*M+ zN+{n^K4kPZn)UI{>vS?;OX8sGUE6)WDb^Weui<(cGp^B5Jz(zkW%BwV7~c8?iPhEO z`z2jwphj~gT8d59`fazKr<9-iZ=;)lwg+1XZ&5_kRY7{#)6J8PM)Pi>kFLa~$;SSH zGqlZn6}-oX#WUtHkA~ouzo>3{qrL5@aQgo|?i;jpo%?&NR>*kt22d))VIHeL2pwl3 zJ$y@uWE&$>_iz+Ob$5F|^^NS(quai*$4e2o<>Pvph4MwhTbw5FKwg*{5tC|nut+L! zCk-BpK8FA?K_2VzkBTkDisT9cV$;-mRA5r*HYq)}^XzMF7#O0mjyE2Dy-)S~yOyZt zI;{H^wy@C;aUX9Q(QCaZ$t#qbM5N4`&S#)25pA7`HcLxcQ%lYY<1^}f8*%sDWa9r9 zkh=fqvpesrJOPr#S2WoaUjEoKNWDx15z5rJGR7?QXmv~i@ZzfJIOwpen53& zm%*rr&!r<4-y0?yREio9Tlr{kGCtWC`}U^wS=1+fCuoR(fqo)uGcJ6>f*xwJM_;gr z-zhj%FFbxn5;s56?^|KM`TGB%>#W0~>ehxYAtBN#ATe}Ecem0Vg3>M0-6bX6-I5B@ z-JR0iIdsF&GklxpdCq&C_x;ZKZ!Tu`-fOS5xYwP(`=}A9KNuSblwz`ED~(j#nH1+U z8YPvwf3x$(^%Ef8Y{i|t08_EHv9|}%($Sa8$Lqm1%IJ+*t(OM~)lrQ*UYwB%#QbO# z?X+E8Q}D)|MZXds$C}Vvf!v9tHl_Tyk>^x;+L+@%bpt`gWQ@*c8wYE(wztz7uYp+3 zHBziG3^=gWPq5+;PwtlrDfw)nnWM0wXOV@xX3HCI_?WVjFEtvG@o5puyxT4wU7gOf z+8#KZn7|G06_E4dvMEq?hoseMmj$G@y(1lqg9uUn%Mj=-GVFm26P$uo#>tZ5&rG&y zrDUe^*gp!$m{&61@^=(!t9Q_ULEm#QX1d)nB|`(`w()7}lpv3lonofXk5)hbDR_$uqS03AD@eCnqin%P zZ7_XgveOP;%cG#w+n_e)8&TdQh`%Em(%FAGn6K0GgU=tQsib@e0orm~co8-ip0r60 zjo-)Irr@|`&9t3zJRh)i)kRpfc4}M8jnzc_q=6P+>f1_2Rel^O*6N2~N7i=!r?Vc0 z?VPRH)y!P`pyG)Yhs@;w!-*X&@cCW_o=-Myv*|k12ZymxO7f*b^cFylzttp$P@zX3uW9Bi1Gz~n92%H>nZ<1uWT((__)k2V19&TxF{hHg zAZ(NmVVCJ`CRlmaBh_m=mf-r$*2m!80={0ISPd_jiPQXBX=ayQgV-pA&w}GhEq&~9Q|R4k`)RZj9RGO3uc)rbbh>cEyMM4USJD1VPaV`H$~>rm!9tcT-U214 zBkI3B0zDQr!?EAe6P5M(Nak<8lnAOCp~b`MIAHL{`_h`fiya*~qV2q~db{_UPaf8k z;m!#oOnMZN6n)O#j(kJrDR;!;b$lE3E?N>P-7RD_j0EDkjA|J3I;{8P_E7j>cAV<> z312NjLJ!<)s^d7-q&L5i4}ky1ldKT`b9&@JWwM)tfdv1oc;>zON!;_amz4^)H+P3u zfIPC27R+mxzZ{3CLlT-G%9@`pDl=JQo+3F);n7|C8{wlY%Ls?u1Ac!6cVG+M(n$u5 z$aBK62!A=Gg}alg?ZjhoifAryY~C7PQwtF}8`cu_m2d7Qrw%3X(@xhcNTa6wD|6sW zljKK@Fv$F{p|W7}hkH|>_eZ>n#sb+(=HEXsLDlzHyq@Q!}CDW%d;oxIeeY3^q zoerd{o>vIPvtRyDcq9<! zt{W1$^DpROqlMGmr#gJ?_Q@FR$J1Z%LK~g76$@wm&$+rqF}3*xfNE)kTg8hFUS?3HbchbRJJ&U6@7W#*cQWe)$bB#LIPAh zk2zinI+REa$~?z^N2ind%QBik^hNoaW;jExth>2}jzHgs;p}ZaMMCD+D*`;(t#o#1 zkm2W^&ydL|vy5e+Cfco;>By#wOjyT}-Q<*|x5{tVZVaw-we{ZRdqK1@^X43zcI01P}0+~r_IF$+TN5=IKq=csPCBXUgPfNpz5N)jgMwe+HDB_ z#mCIVu~s7Pc?0hRehcwSWB!R*vLxvlbMIM$bsOn(>srGi-FoL_Rl_sr`#{t!FO>?1 zLg?X4PU2>*g1nf?;4HZ|%_vQAv5Dj)z5J}-R&D;bUW-z5dHWZnu_ouBbvb#*Z)|g} z{f^p0gy5{=vL9{Q8CGr<5cSL8pHb6GCnGM?{5;^|%0E%W?xUXl6qlu;v#s}5?#Xb> zFW=>gKs#P{1fR>b;x7%-C{`>B@CJ#_iUd|*_t^Lwq<8()=99u0hiI4{>r}0h&djH#8d%ftLV)K^N zQn=jXLsrvEkA+KaC=o)A>Px}PzAoeNii;|!8@98OA)Y$w^nCnR61*Yq$!jR@Pxo(k zzH7011zbti=R>Z0aCgE}pganW3w9EqG}vmx06x-!1>piN2$5HqRIt~9z;d%GGs|Op z0E>BN6v`bfGcpxtug$Y5SrN=;$&x7ShS-?2HFHl*y#QUz3N}(!Kd}uIS4Y`1}L?q7F48Ao_RlReM^ufQKJ@2Zmb-S7N=d*T|D=2SL=v8apunDENj?-(?r?8Lx;y= zk}0j1TR~brzLpyCVm23P0MuNLb=xKk)d-sN-muV{u=`Syr00~ct0rb-b~AL{np-dB z#mdiBUiI+(jx$BQ+UWkAed%h`HdsrA2 zbx#UK)_|3`qr>h2&F7CtBVW=!(vTR{gyN!$ukq^#*r@ksojz^AtSb)V_SvAg3x1wo z1&F5Lq6==2OO_skwZr}z{f(l>*zNPdm)vwH_-d&EsVN;Am?JXbymaJ8JfwX>A|LfS z?fSV*%3=0O;(P}c(g$4w!5rPHq{vQUryRG*PqQyyA5b-R35eylb@xP6?5oZ_^Frpr zXSLaMeNXz$xBy_(Gj%V1DF3C{MRZN<*ha zc7O>6Tw5RTFqitzx%IhmZY5vM+d62+-QAY*uAYsvMA)Me=wZ5PXe*+IPK;7fGC8Ee zddMjceRTu)^hU?0W&^VQR`C@Hn263U+0B9Kj;QjF5~J28QqnS-aWTc{Odcs2d)c$F zeJZuv1KS)-F34K2ycqYd2+c29wJaFIi(A|E?td9u#L-K{A+xo&&#f4EHu|3NAu@0o zlHoKxxhc0gk{a!-w{llKN8v%e1Ur&eD)eP7bYlw!yFVmz4RaG3u8No8eJ<1Wh~0JW z%lUS;d_1?HujzCdZw6|FnMc(4&+*G$ZtM10;CVoowF1b-<-9|DuCwc^>6EwsJ3~@hcS1vLg+$VYQNjMh@Q#;o7rI#=+^t=G)>H%5b5uR`@+w zu_Pu*gC>~Ay#B~8AxYyRSn-wf^as}mB*&ji^OCvRsTlhgwKXLrPD@hGKyall(Jtvt zrq}a^fOC$4#-8*k4nKqTkEpVzM1D$B-Z#A6K-z2{)D`p=8>N02Gx7MD7O?Aa<}9In zS$Losza<8?gQ(lg4kvBJl~o~EBBx%rZ&%1e#?$QUw!XQ$c6wK11bz8|*D)nm+-&q~ zlx)W>-E3y(?V*PMZi2+NCo`?yu)yTuDzEVKJVD==Tpm=!6XIgcS@#!UELdL573&Cc zzLyYRfi%CSkTTY4SUGP!j()?c@#kj&PyjojEsQdN47;D|<2~D^jFjSc-I9{3m7B!I zyfousq*G&J@&3RpV{Z`KL_t=mt=~;mD|Mqpl5t*dtEzIhHqP643i2w`qxK!-(E~l;5L!sQDPG2 znV~#Fv>rbSTpL7{O4+r7%XCfyHriTGGSrEfGatYA{VLO8!kxytC9uUm&KJHDwVPdB z%2e)AB{?12Sk8hO+kqiqf^<2aPhRyqDVW}f%IH2ttwCPX=(7hF8U${c2_jEB}s(k6wmX93LE8<4;-&~x#(d$ zgNtnGPt4VlJ&5hTWny7S9Mh8nDIL&}UREd2-!yE{h3!4LkP19}!BQCAMr7wz&HuQX zytT1*#E)wJ*pUj)fT#tn^qT=E)hwyA(R$yeyOOhZo}j zIQsE&gGs89PYMXzC0p+g-SG8$IyuTEIS1aPILCw8G!@q9CavlxJGcTl5Y(gV$5CGb z_1iZ;8WHDL9q>oK>dx`&F^n?LwDuAgxl*^_N60=%85}?Vtd|XXtXDkie8J^F!9jI}(SJ z`#l8x4lLwfZMQ&`BW%b6_PAjo++3omx|8!1Mx$p^;)r^~e^JhJRrj?c99PSc8k}98 zdvl5>%w0}O_qr$GW}{4-JU6y<9cPm9@!Up_@l=M-nT_pX|9P7l!>?Hx;hVtGSF6xH z#ZdlVn77)tC-->Fu3JXZKh}5J9L&!QAdPM|3{!Sx-uG#L1*|XKJioX5-TDBi1LcPu zz~z4P>y9?cT~}>M)hqwrCF|i2e3=7#J7yW|p8MuQozYv46OKbcY+?OPsPN2 z*ecZY%%-Kxk%HbiXZ^JmL3uH*nbFI@R6mhKyrBr&?;2mF zg=c+CzffG*>ogF4LuEMu`wMlJ&ljj8hofahIl0(uja3y2$}wt@b-1Es@UQueWP>{w>OvwTGZ|6 zJy^&FH^gXHzh9cwy$-5ylXdy{OF0?qMR`T|i!_$)$t3UJu03fmJ?I%~+-gUzd z=sn!osMjBD2xdWdHe!cbbK~Yr49_NBo^KyOv+fDpXH?!FaHjjF6*y$L^AUIcaI zRjL2iXv@|1;oBVaq>F*rkbB{6C;Qf&0wTtqsj5gVaMV{Aon-j>QhHY6)tYx<-cNtc z@BnCmx6@5I5)6N?3$~yr-Ew~wwB<1Wi3ZJC=Kf$e#rxZS10vzsUGb6()=)z9gQsgW z)HpBO=4IF7n|h6g^)WK1W-;#wt@_`pNxzw;IzKlMsvLWFlbaBrcoe}4(8q#m*5_k4 zz18`D#Ys|V_%v3`ihP!F->AUC9RfVbO`+V{c_R_sq|{sIsDx|xLf3Q%y()X11U;6>L*3`#OWM_nRhd77DAd z8-N3m?H8aB5C7)p2R8EeV>mR%=6lJ~t{>LSB_&pn#)oWC$DnszJG?HHWdKkKd{+_h z*=ME}De5J=f*ZCZZ7iW_Sz0OYPENkXm92m0T2 z^D6dwI0#=&nrNLXrC%~bNXT)pRX}&*l>E$Ax5*$Z(T%*FPpQUo_xrYEfdQKLzpusN zz5(@7a?w*x7gX%TJ58CjHa>pAncO5oMXN6(VlvDZuSE{fVl^GNj-{G|biC6GmZ1HC zGTviB+Z(T~S~Jm8f4>?FA)69ic+a=tWUIb>spoP^-Qs|a_6^VHHtcK8(O`~cBfENp zPN&bqADXaH&x?Z|`pnyh4VX*oVmpR7+fD2OB=~|#_$q;!Z4mXW#>znyM-fT5RLGqI zfnT%xY#|_4XzLD0k}r4N&T3O(Bp1qY?G-1{O>cC0RI0U@?sHxFZ3#O8xtGCsFi~j< zc}lsb6cia)bW1FIHHJo8mWhL$y*DUGqHnWSARo~OiZN2b_s<~}S!ONUQDJ6+K)iQz z8n5Oow3c8@TJOBY=Aeo}*mIz5|AW+6IeTQ_)~F{}b4g86oe2xsML1ezxVEjfyPCaw z9HL;8cwR_c^cnStE8$l5p?{)hvVfX9t=ro|Z8WA>iB{v5L|Z#uuLvzK&-J4HT-(+y z4x|~$1&JkO-aVPXfAZ!4y$4Fdi*+q}Rs1u$GI=$S-FtylD9=U2D^D>p(A49{a5%tF z8f6(lVE^i=%kG$3Wt3ug;gs0#*B=XnlH@*`Rv^bQ*K9nvV1Md~W zkUt%r9ln#jhLV~{`v~9nXzeO#ti_HG^SoKd((6^;_|3#LG@qv z@HKiP{=CdakyxG|j^ewm2M#b-O4|AvZ(|gL;?CM5FGl!Jy45w%kBKZkzxMt_=y^8C z3GX6_BT1yY-206RUf7U*WVWhCe{yXwS zJC#p?(8hV3MJZir^ZEiTJE?aeElg2g>OGOt@ov2064t1~;`6h+3*u?(0j&&AnX@V& z!>#pt5_C{zIWU666}-m2&rqP1<@vSd{`_78R$c$FUwnxHKUfIM6-j4xXqp_MRhX|U z<8d1mf;K`qxtELDLSYw#He>!c;FM^40>x(eB4h-rb|gAH)#tplST+b(qrM%6>31Sw z@AM8YvpuVte;TPqKUhVx^?nj^znXSN7KeEu41EG^de|B{h#&E-jPWB{!gBP)NI%3x z0qShf9*I6b)nC?>VcW*vddl1ipdACQXUikJ;L%N|ci!oN)jEEcXb`*ga1e4H6PJ$| zj0fIDZC|5x`WuxVlqC3&fx<(-*!|h{o8;?FvX}7pXbYR2;)^xSS)JdoZS`0RtnOe_ zlA|2^1iv&A6nBjPGA|@@XvK^0zDZMRo-5h1t+H!sws$o=>?%nm`YN^AvVNEZETmJ) z^&Jp0@&V;xevpA~0zHkk(3E`%XmIFBP7C;LveqF&&TSW+&O6uFo>*G9;mvaNSHPaM~+1~ynb z71l%ywpendBGjAcrC&JT22jTgQcLb5$iI}NF`ZhWKA%{)=w`w+gLWk)=x6m;<%bWI!1v8(Wp z`<+?d8Z>x0-h<&mcb9yZ$}_mwmjrMnX}AuM< zUXvS~c+W%M&Thgo+s%VG)9f~alfMWrmWlpP`)eI2DmKCZ&ZQ?}Z;~%6EP>?IBHp8}Mshy71T#_L1pZ2Q0@{`fxz1*DHw^Cx&?(D_ZY4HQe1y$xJ0o zvNN4rzhp51<>@$X_oNV%M2f@fGtS9ukpIstbQ=#z8mE*1X#7!NCAk~w(0g;_#UV<4 z?+E_v@?H_P8RFOp5(gJb=MUtk77KTq+@l^Tf7oIy9D)tvrK38n81O* z;7S%EH&Te3 z<{ode-GpB(IqSWjuf2a;5GVpv_5L{0faSx;>@8u2LU+SK6rdFbkw?i!B`I&Ra;y8_ z+*@R!^1euAcB{6#+kTiE3L+6!RQ4Nau{zhaHlneOeGIg~+(Z+rr?H8-ibs_OVEp`{ z!}c{JB;(86pn)2(rGV;Yqnd`k`0S-L#6tBp)q0?Qe5%Z@IB?(y-;ic-aa>Ysep|a0 zI?-hja_@3Nauez-T(exPjd676qO!#L*fH#&TavDZI@N!)5YQyC%tUD<6dC0){0nn+ zuCxBGGTsBNx+fW6HKyvug!bT*eaStF(xwe6qRi@fR{%!of@e}4r1G_*kt%-{{?}XR z(_E%FQ<z!K7bX%U3Zmt zPA`8J##>ZzQ#0kE-|h#0QZzCyoCw-AIxolsbPL)YEZ84R+R@#^S==d#sdeI>(!d@= zYt!2H$bp)Oh)`jt0f69BF3>;l4?|h_FNU(I&(2?B=JjD#2*UBFh5WW3!wwV1pLM)g zCRJ^-nqPT;0lk#p+Nnp*T-)hXwoV6)kIJtn2>vpwosLqT{Am-1#b@BzL0?w2FHfXu zbKH8;Wwh`zT%7OO0<{XS(7lNmWFsIIf1SeDVIOW}=`h^-q!tzgjXY$wgo=OhpeGD? zY_ojhJ=mf4^HD#PIPPdpffe5(tt=WI7USnFPIS1})}gVH)5GL8qto>dcuY}vso8&v zmS5~(@)>dDax}O-^nH%}=9cW2b29-0dA{D{xa(t8Ox2`wi>1p?VJs&A#O8Q~CS6Y) zJIT^H)~+EfU!sq7uO05oguB#j#=G1BJY{fd{2dNIsYWLe9vvT#W(3i4zB{}Tbxu^e zZ6rAe0EYfrjy*_Aqezl99ZN?xzUAuUbl49?PCd>t$0Olb>*Azu!JK6g>e?2FUNlW* zR^G7#y?8W$!=I%(?2%+5&ohs4Fu|-B^hM@*Sc^#e%0iVdEVcJIuDgJ-Sw%IH{Ay9v zGks%~GV8BCsAM?n(S-w4e> zWk3eZAXO|#pdVNy9TISc6I&qtB@uVw`93Ft9UAp(J;uajgLAN)}?CwduXFXHeY5vqgGN z;FQA*?1{AR+fxv1B3v>+{UlI@1Htb4s#|W1YRrRysc*&{S>4ePtC?(E4v7L_D)UTI zefMue{3GG^z-g>k_v(uRAgjQ$txf+45}@=lk9gZv_AOy}f;fr240@9>%MoHJH2=YL zGVZQ1d+Vq%(YZTZgDoqpj!}2PMwWXsZ`t%SgMx6wIVTW!sap4#;+>mgOwrqPP8j@h z_5`eX6myp9B^4> zkRCkMd=9;0!GTEgDOh!)vR4bV9M-2vXm5caP%Zmnw(v#=nnfrbR_G4|W7y{&55&B> z4kZ#UzCDMhgMphrpSWu*p$`vK9Q<2Xji12-d`PdSzRVd)i4Cnm=N-dAoUgj>K{O?6pomfPBIZ{mid^2T;5Zmj)* zONu03RsiXaS{*#%`w7Zvoz5E4mEVLfcMlNG7JV<#hC-fzWWGl+=|BK((Kmh=7UG(; zcHBeNLXUIwrh{|DS+MnOl)9)P5%~(WaZ|!3^Bw9_1{%28Bi36)i(yDILDeY;T(UwI zXqSdL!LxEM^lcOWRC-@{pQpREucvU&F>cklGm2QXi7LvdZVO16X&7f_Ef-GUDHG3T zXd`tW^2%cdLE^>$4gF`*?+o9OH&Iun7g6yiGKH3)>WC9v_aTAhr!N7}LG~YHem{1s zBrN`pdz*#)GEAjj=WntXVLcPlVLkG}n0v%=@-5Rf8J=GjQeC7{Nmx%4^xTWE*}wl# zJ(R)Pj8~@`=l(RA7FsfyOUFy*xwVEXwn{C(==9v*Rp*OVFyAd(i>k?FhG~h8F@0h5 zc>;_Wyg=>q$~$+fXPD4g&d@+bpYuq`w+w@iDUj zO;QRPMk}?_jSd8STS=gB`x0);U9W>a44EI&?|a5h9tg4sa~BIEYD)4(3L!(dxbmhB zUTFc124b)+_;=$D70(^C=kw;W0MyE8tyrc}UlfptK}cKl1hfjN%sC(U_GjbZMhXN^ zH9B?Bhyfq=a%>h!)nws(dapx3v!27n7KaAi)sN|QQ2M+y>|8pAF6)MNOhco6Rzp!D zhW=t^kJQ`>5&)5$xGj};56QK{t`l_#MzIswg51g$s0vTuCSgH}nONG;XTTBi!NPyq z-QU6wH&@ilw+r>7d{xRHD)4$}^ zjK`Bth(e$p-slYgoQHEU(Adfp?AciIRP(XC%6c{I`t*I%Cfrrm)Za|nd9B}%l0HFv zQ7yW)z6HI!IHcbqyLL6!>ubRyc3R5_+pfqSGu&K|+k&}}5!eKhs72V0!pSdKQDe6= z1=%^p+=4~Q#$yzp`Q&N>+MSXfUIe4=zS)yhA{s<^AI-D3g(0>I&8bdu#!cm$JJkfr zyxrmdt}2K4TVXHm-^ZrU>N0vla2c0fb&d8PYB$|WB@JAEq5sUpDfmvVDpX`os ze9uOlv5o<(xw=6H3cWPG@ISWiktVe@SZ;a)tf$p;D|_)LSgexvOBizJ&LfJ_=C>?{y+5M=J!%B&cO>!CxN-wZX!_oev)itF`H27r&RU!iGNTO3-HiE_P&qwLmpSFz{XCh*#Mby-FNh$I12 ztkWJ4!~KI$ARMBIqv9}v3kW$KtYzH&^t%)C@v~iW>@kT;J!bf4t_c!i#Bq;K0|>6a zeU9q+wC)3-udT0qd|Ca#pj6~TUIrYtGIZuuP5M!;!`0D5p8L_K6g3q7KUZ71W}mCu z0-#&#{wmC>ZC{D7ukqX;lp+Ikr=jAQWf!=UUUw{vHLWZ@LH;CjbslvqjBd*vX<>wz z#6)s`k|z<|g7YRhO~m#w2Kw#vrO+I=bx?Qb8gb^#T|Q(1PWqf5d-6dvM=>hixDZNb zdum1-vDPi_L8rn68#EnM-Su9AIC}%OZoS;AaWXcxVMM`bw0hQ^(W2mI$Wy#Ai|Zx* zb7>R3>FESkZkXY0X1l-6b|vPi_4S-0Zkm00&kVZ|K6{=P{A@puSnqSkN|2n+n!^IH zJh~NpenKD1*c8+AUTOVh+VQ9YgzDuqH5pD%yWQQGjm9*anLc^WKkoZ;!|C34du4oi zwMpCh*X@VIBWfAwd_;LkitjAPGMkn)86-rR3124sdC03|*pYHRN zJF3TyJMz6j?O|x$G`1d;iq)&L3N_Mo1WzQE}Suu9%7>XPY*AzXMUpaYy6`Qxc+x{EZNlg6W zW@juJ+4AT$B-wB8iB0D7KHawM^LGIN-3D(av2gc%BIRhcTYC1jW+7<8i;QbI?*x9t z{W*V56q;nSP}3*TnYaSmyX1%Os*xe=ndL{0gQ$LE&mAWbd;RlhO5H}f}>xSWf z)$`Vnavp_RHzaQy>VJ+R=)#NK*9YT5u1(#$9z!Q<)7mou;JsNOhumVUqu92KQ5|w)*E7ToG1F8(8df+$qyxXIc_SJ(v!tdA@&*Po1)^{fW%ejY%O)P zSl5XAwHS)Uj-@Yz7XW6M>4Nio4g-t(VQt5sbDt+pm5*iVvI~jO@x?jioFl1^X&ld+ z0!y>dwLN-QR-Gr<3Ds%`y5O4GWK1LLGLm#0yq8md@90O8OTrw+EM}&U$fxJis#PSa zT$}RgXP={RAx9z;s+hAW8B&t^4a&|a0m9-4xA*$A%!CRV7w36;)>xU^rNq5@;KllCyH(?Z*v5^S+@2h`j2Ah-vQxfX_V1u^taw_=u}2_EZ_+IiHu)~Lb)-iw+x_iJV@4^0v|3Z?_j zNlT>#BiT{AO~|v6wN|in;VW*&rzRY$c_SUGL}l>ANr`}3{Az5%2&5*CofxiAL7yXh zs;EoBt9e^3ioE&}WjcrPHrA$a&@|j>hO6DVc(`9B_0UOUUrO$FZqP-IIjFyHE1jSE zqI0W=#AU|@2rN$6%Mg;ZCuyBQ4!xQ_c5jFf#-@S?K7^Pym|8FPAT*pTxKk0cT`iUv ze!B1$yCQ~G!-kRe+Qas^T6My@x;)b9>2^XVW|X|*&M)5tcbNpJL`nee&}@SeqECHd zj7u_3tWyO?5_Lb{P?P~3zqq%eR@6RZ0VG##gSOEl)6?UD2i3HzZ6@|RL2_Bh)bdj`FIxv$xNC_)fhXB3nz6Z+g zz9VySRUw%Kb`lwu8*mVxB7oszBVps%=nqxX*aX2486BZFbG|p9-;|7!l_#e7KfC%S zEIuz3WG5eNul&4lsLz)S%yf{DS7=#4F?@wUydkbH&AH$M$g^2fs6G^tNOBBZ@ZO;A z8BqQ$%p1hc?jiIIqj4$xoO6gj&pSr(MIJM4@RQ2lP6YZ(|8EPVFL;WE9zEjh!vzL-AnF%fIh<(%NgL_Tt0nv7qTonv@ex@Yp8l; zxx(XILKrHq6!xS--^w3F=OssD#Nt|Pr%)Ofa!V7_Hqm>pw*xG~ZV~p#wMhe_^ZfAA zcr?eaO|3KX?F)&_P+gbh1ip#S(mo^SoA{8SO>LJKYAfvh&WuA+z2X;s0+}B~kK-+eKC71T@lF)* zeJ+sIBG5oKg21=ETHe0|GS@C;>I;d284IikK*I>Gf%gk()>CPpB{r*_9Cp|#Ee0d` z$1IKSP=odvEXO23Z<8j>{kW|kVl>2dLFWMjedQk{7fT%G0N&J{)77|25KHA(0#{}gB78o z%+s=2eB;NK$9~}_8@jZoNFQoge-$6A8#sk)jlynF)=z#a>QzInK#@n-1(cn7mYb`W z4ZCLV8;^zumEa^$eVOot^?Z~b0X!c-x3NT9QNaY^Z09HQTuTBINET-OR|kMHz#L^i zj6~)kQKtA?20D-c{T;ImTm;%-(Qp509dPjx_)a;y84usJetA()vyTv3_VSBj+Si@w zhr^PUvk*)uWXEalgZT<`qTWF*WHYwxU}45vr@9gj+*=@d07xxpnG@|JuAK(qUBax_eqfk2uBZKAKtk zRHU#1Kf4iYi*zPHC?}(cuGm~q4wiG%E<%#}ld7@w2621f?&}||&z0WMzvG3I*}O~W z!4n^KdJyS3s`JH*JSXITc#}!8NHiw#{tqC{}M! z*U?BU1*K6o@eaAq@6B)I6ABmHEfLdCf6redi>Z=Z>emaTOri}$-Bx25<|OZN^%JLa zeWAB86Wm2L<8y+Wy6Z==lXoY4WgMZCihwud2meIFlkh9>j7$2RX-`x5DJo?)9J8lm zmRfOpCsJiXI{jQSuvI$pH<4zPWCHy~lcRnMB_rTLlV>pI7Zn13>_8dSCdJpR@{EpIHu3a(v!!D2Z79F{^@tJYXeWY=XSufq1}7l@MPvN+K9 zwD9!uf-h}aAJ#e?wJ99YcQDda_%jbaW}=4h6sVqX*yuFng2z>{$rSvZDrkv5;fz90T4<=1j5BoG=Ea99|lcn$gmGVpm` zv|}{q_LIcfU>xU?52caq$ExtqgIwHLjkXl5e^kV--5}~^HArbYhU0wUGI&67V$i}I z6Z-lERaT>gxgxQZ!fGQ)d+>7?W~SohSnfAZxUhq_I6vKl0C{C_fhUk1M9z>B*Uqwk zRb;%goEgtDGTr`KLg@W#w)U4MY~&Kt9s1lj3mJUm9~~ttzZ{rl7Fky6UH|)IZ_`W zWN0)Ibj6$F4X-1Il7N}XAgP_Jdp-ah*|zcXOc1}Qz?Dep) zLXv1O8VryR@*)czck;~^wx1Y9jx~1PAAed=kD!;IQ*6?S_SS!@9)9w@-iv+(`tAs$ zN-gu00YKwxfp)v-RO~FuJf>||%ZCu-s_C#c$kOOvB4}Nw#qLA(eqbiKAJ=sZPxOU?86GYG~0;MPs3$(@+OihL&hEKa~ zt!+qP-folv|D-K;e8MP5D6^2!$ol28!HFG>f=xd*@4H;?uYHvJH`PH;j9BBe1+qO{ zBj>lJBg1{Uzd7XKo3IS|H@KW489`N^lGBVIVMXVjw@s?T)EbJ)tr_p)1{w^re>wKh z+-yW^)FwegVf^N6IJ z@#Q?7L&*@r5*;swUP1LekgZzbB(Oh$?mX}%G^)^~bv`~y)ie3CzWiSLyVV4nBcBQv z65H5UCZJ<}j7VCX4@#nxDDQ&5`%)^xQPB>dy!(N#q{f0aNP{HOV_QV}&hVMC5+w@l z_t=>1@01A$i0=iy;iJT!EEZns?B7ve{<%CH84)@;sau>}o}QjobkWd&9{m0lO!DrS zDCKfn?|ONWp+8`yG}t(8F$r@^_IVm|&0wW`HKU7$7s2QJTreu%T#ARWy{5Ks+t1xT z@7f)b3}cdttfPvR;q$f~u#d&2=kSJ&m$8{g+H%&H@uJGZ!|;nBYiF37xZq0X>@IIe z`7P8~s|qrwtGj*O=9%eAV(3w_Gb&C5+S_xMZd~{zoE0Ur1FCx1w8OD$Gn8!|ad>Ow zn3%Lya!yqDLej(4TZ_C^O16aivqUmq;udNIL*~#&*@@cS@9qA5TmBSAEaM6MZ3Pl% zk>J_~Z~GO~gNhc=Q2ojp{sEH*4c2Pa_n-58i-l3)=na?s_%X7Kuz*+T9=~g-u};JT zzSfc~_8AeFUqD~r_%~TXyldY9z-*`{(r+FgV93YzwkYp!->JMhaAE+t9gfG`EiM@@ zz*88>L^4qL&@pc=sVBM#By>h0{(z=4t259OC*{{op58*hecOwhd@9g9(H6Ey3rMR( z^2Z^G>k9Q7h3g)tZoAJJj`HiA|Sfnh!om+lQ0 zdXGf)13a687|sZZ2er8Uc~?{;kPBz5kQcW&_)qjYa!7=wZY@z;4TFaxw2?bt;G+K>kZ zuU(=iOh3ERh=2=Y7lv9zm`C znOGAMT{`kTHlifWvoy`yoUAtnnh&_So^3Rwh6ZR)zvkY{-K*viMMIL`>xJ3B^CfC{keNUn91_}(3yO^ zQ&4`+Vbxl-xjBL7XU5#-pkVN(-eaF2@usGX+TmzlfLxV*6p@A#!D_}kS$B@!QG04UNHZ^~;EYdupe|Fi8$Gm2S)Nk?(oe#@n>S1#e-W3Qv+ym3#oicJLKnvNAFl)EK>{U!6!m2@WgIPC$_yeFxnCg4{2zXmsQ#Ik%k=*SVYGKat#5x`w?G9*rJNWSUKB7oDD?s`S;A0Yc5R}T)=QG(P_s>!l${Y^GX;-jgK zAI^)^|GmQh^CsIgN;4DW-Sud(#bvGTGDB}IN|d!-mYsSI=c_2J5GoM0+1D!wru?Mp zL;mkB`R5+(&v5X$MRCaNn~rMf#i9d-YZtAxo_wo4=7kwtN=3g=qK4lO`88tUQox~3 zM81;y7YpD&?mQu!q93^tVWc4_4WmN$d9r;JY=~0Ll-N|aixy=qci#(OMaAuN?IZgi_cSna+@$tov{=WX-sJXzln;2;9|lYVmYDwAn56apJRkTd zW=#5WoNY!&=D*#vOZ4e??Qb@vdfjtPa+#JRIveEw%ND{(#z*W03V@8mS;+}QVl9nS z2z!&K{?}am+i3jO$yu1HEZHR}tG-YO1dS&UvQF4h_<#RzJLo0IkB;5+PcpyRNV;7^ zFF2UK2Xs>?}8*{1sJ=}^+?41k}cGCYRn*M&LTR5tHF4>1o%iZ(hmb?c_$73St3y?f&6|# zKA6b;mtg__A)j%TqAiE}TK?NFL?RGhQCb9;t3j|_4=egYwX7TRZ$^D!s)_-=@^7lCDTae#DX;bN(`@!$96n3*WlEhAG3wO;sf@L>-!Z2kb zkHd-{2|}y*T4J}Ym>yANVl^VWZtNae-Sb%TNP2&5lLYlpJ?OrMIeX`%nehGmMzHXM z0S-l>LdNHkx)t{xvS1>WlIHV4u(-}i^fR#z8=w2d4Mm?j)ef7(0Yz{7inCrqJXwZ{ z?k|=oukdtT4eS=J>Zo$Q-IwqGDlYKgw_5P2dUImi9K!e{De?>uK7Go z$MFvoq0$vEMJ^hlXbEu zoh0kzb=l_LgSId3KHzMWHJKl!G%^9Nkvp#rwA&>~vP;`ZZM|1?RUaM}Xh$~g5AnpW zWtr8?xVLPt&juXLI#G)+1^aaR@wIGyIM{OeM7ELi99pHp_t~Hhct;AI=fCvrd_b5a z_md@(#i;DI!&gAIIy;hh{KDyIb~MBvD}BF-#dU;dn<}AUGR){d!YL1;j&t zRA0UDbT5zrV$g-l_q%>b;{isT|MdGT#e_*^N~cyT!x;qy9>?+24D5^_NUw3}aMjOy z4}apT;dp0%6+xfB+$Eg|!$Glyt^|(#f58I{cq*{P?!=?$#5zA_-H2;45*Qbiuc zPw1lqikn0@CpESzH%(bpJE{fW+5#C8; znbPwbNLM&x1E|eC<(rd5ROs%`v>#4rBoo@=>*0^g&)uOme=n!|3CvC6QMDdaA^>cIh8VI)q@R7-bOrq7m8SRjMu|47?-$|`r zfzZ=o+es~PJ|i=?&w-`a;d^JlDewq%4>NOlP<&NTbf8RSYF`9LnS;D?spmlE#h3)A zFOa_~CnnzW3A7@Q6tPO#5^<3M=%~PuA6t?LxJkVf)9%EpY|A_)>m2C*0Ay2UBB$eY zA1av)bhllFH9H4~5wHlJ3pAnAdP1`;{MAl1dRG4bm>N+&D#wSjhnD~JcO;-qABZ%J zTB}MaN^ssekC20W)&#e<)Ain$F^mtJ=|`(bLQlGTZDQ^lF~grOfErhmq|#_&JC3`k z+dM_dpbQv=v+4HB%t-tmLgVky}I4BNuY^FwwKq(a@qu-g-=p4Ze%lmetkYX+3#i>eWXb zsiE6Dra$s)EC{_kEz6FrT>SgHR<=25n&lX41n~u~Yhd>{3)6hRq8H_*%neT>Fyj)O`~m*ZxRf;|6+r{&IEU2vi%SPDycO%p&W{W=u0R*+M%eN*X?!Lp(0x02W^`g!|cnDo!l96P#%|9Ya_zGJI zvn2A^|4Xea5PtdUe>|;1Ex#ZsQb7-qE3lpU6rauiWaq9jXxrS0$!tmKd$z?SJ;rn& zgdm9c5y&M*MBSyem7KnM5nZArZUeQ?r(IRwr>>uR>>@_N3b|C}!2ZcqSwI%^YNu;7 zcwIzgnq!!x*)%4sUkrl19>I#-(+pY;$9p7>ZnP@g?^H}`7gSmWI!@>xsmT7-+tNNx z5#k(%9C zKoW>ZS-QGB))#*Y9It(-7$%dbZQxTFg+7v02ls_XXGW#KHfX2ZkWBBI&xO~SQ8d^r zEd0l}1$wm;YyPez{`hHv>OUi^l`kS!denE5$Fvrcw%e z_6fUnRx(g%m9rM=^$sbZ$s38s#5jah!Iy_m`q`aI4jsq(Em}bDV2%`D+Yzhby#4^b zCG5_*P`Bw{Yz7Zv5nZKKqe<`*1WElCRuby*E#hfJPyKpG5F-)O7>&od_*05lOnOBXf#| z{`~2oq`D(u%eydzF;P--VCb8FER7c^5hDJO$(Ci+R~RBi&J5;ka>u> zmuEuHx`+fcEx#5J)oN7hDZ+lN0ND6{9oMWU$aGT*J~gz3Qnvv~H5HJcAHFuW_%0%J zgnp#KXUG893^a>9=^d|$Nsy$qxLeid(0EGVip2D(`N}aX7<7<^Jeajl*CSCw4*U#> zw+p<@!H!ydpJNc^sS`ers2J*T3+T_f(v*PLy?TCrM>dh<2xN`o9r?@K|l@ zAywo0Cg0d^bk|3)FTL;naUS}A`}fc24ue+e=FWAY8wRAviQ}vn`hUu&fx>P&s;0;( zV-u!P%F}AzG~#DrScLs9aiaW91754j$$wzseDUwF8Q^rSzJz7+az|NAdjl*6VZ z;8ZojaS;56hyK@yk^sQQRuNI$8waoec}!dhG5bGG!~gYrhuVOR0nvT`iqBn#VFT6@ zW>Zi`=RZR7e?2XH5wP)SWQ^v<0dc@C#}gr(^N+pnzkY8_3b1jJmhQz(19Ve?iB{Id zy%FGf#!L7@Ob?b zy5Sw4<~-V4bATg6R0(eC|MR^6&ww#{tp1d(#A1~j@C!NY`=o#e+{%)_0gYe|U&IGy zw{NuU#sT+$0sgm!OKx;^4`BL(RyW3P9DoN5kP36qz0rrnubm8Y@Z`n;x3MQ5;=7E= zkiw5FsgJ=%WegP9#!U6&C9J)^|K)4{^JjQCb4d(ZZXTB${9m5*Uw?)V%FB@qJ~%!N z$76g#UfQtGe%jUCs=PdYTdQPgbX3j?8T+r!a2Nk4c=lFY{*AtPc|G4G#&$QN!*rzo z@f6(r1=jzv=bwL;gkbAeYUVx)y%D1X#+P7WwYZ9hq1bPnHCbTZ&$_>x-1up6fdTY# zUgbqUTOpmb?n*7?`3e*OB<=^Y*TY51vU zTOW1)>Sr*F#C)x~;U_!f@YBGszHPajc_62;{r>7E(*;>^DiV+Vn4#CH&jA~x9>9{E z54L@73{-+b1PN6!_98;Ux-9CyxM)SqZvcX=QRM$|Gq0vNe_MN2bv6A0O-;#gwn8gKsBe-KE;#kpZ@rNH-zw5E$c7+DfV4??vCS(>q2b(!y10JvdtBi z|2nlLOgMkHJ0u+X5@>xkzT{3Oe`-khNN(eOv^CpXZTj(X-*j!E_=2x~d#N^t1-2gQ zYEZJp;|>A9-s~_-Z)f+h3nA(yaNf7{`Sg+>x9Is~lY(k^TLQLA-2!FYi!u zQ<*>Mj7@Q!{O`RTcpd95vc}W({P;{`%fPORrO4}u`I93&h6%W#_vcDX+Q<2CZ}ih8 zW|I%@CNKy7aIT~TtFypR@{(VuY*{1FsCa?J26`xPG3bMTb>;BC_{+TFZk^7(t;dV` zEZfGch<$Cb}gRT#Ob3hGO2?F~Jn|J}oW@?bT)mKnb^i;6ZpCZJ^f&Hh+O z#{Fkmvdm*mEBLcs$W1$J_$9eqtwMuB`>z--)uR_rqa+kl666(m7;H)Z)le*g!@^t7 z3}x>OdeJKi_r<%y1ec1h+^IrX+;_8wpdiy6$AA65@J{fU&cf4q95wbjXGi=LfEsyP zAH-RId{A-k4zqy5zj}bRIl-Yu`-kD9#ziBYo2ZRJWjT3o?*)%qqknyd9QLX@Fu)uW z@bNdEo0fhDklZuOnAvsx&-(hOHwy*>F*jzn=$Z0OwweGhb34dI^QLqrIyo!? zy|Wtact8a_0f4_JEM zdz%ER`yWuVV6sAshsqMZVhf;jJXzQ9`sS-_?a=31DXGBfR#fXiI&ykcQL}}IL#!=D zJR5p&fET{4-f`|N=aDd&*1-y7o|3Wde=m-gtN=utuh*?%Aq!`~F*`{xaJTiiq8-S= zr0*z}n0xh7##c&jJNK5x^Z(e={SRBj`Nbs|pOrbhjv071Rfcp74yzzB&KH5075-;; z$!KJ-^#SfnC}5;OvOB;*%wk+ie8E6pGg(ChNE6YCC&gFil_nvlYfMYyl~yD&C)Pqi zM_a70DKA2$|LjCLGS8pgAE&*3Dl5(xmuNKX87&!&C-%D7?T{CmML6uY!t*`anoI)M zGCAbbgEUG{9X!sVkFm)(?2pb@NJ440 zlPpvSyNA8seV*bj`unYN3gn>T<*5~HZfxQA-S(G3jJzAp?zjpQ-?CEnIfwM7S}!3_ z^%!T)9IGyUxXl-rr!PdJK`U6Dt?-kTnD|Vi$8SuYzIoNRw^gK$eZgUEIT_kl1RPpu z?qRQIcY86l5)u%Lguj9RcJ5YN{#8}WU(@{S?c?47X5|KV)H;KmO@h~HUGnp4_>NDp z8FDX5fEVD6vMGL7f7Gr6sH}=Q!G4yzhNGC>(5se{Yy7uwJ;4rOMT${hJwkk{kNi%% znJ(YY*r#j7U7wqKwWYQVu8+&T_oTK`c8m)-+Rpcf;{h16bc8d}C4!753%PDs`^<3v zvJK!v)?3Jlt>hsCo0Y{fOKsZX=00D=xcH&dEn@rW+3n|RWigZZ7=VQO00%%)D_4M{ z=slYAu&Qpq{M#$FbUKn1h}N+BJ7L>@KY$^T<4V3@G-&Z4?znaL`RV$PkjdLv7426S zyDssQi0J%|_h>PI!_jzl6nMGn@l|Y^*Kwr4lmQ^kJ}>n=9AXBn7kK*Hd2;wp$oVRh zz?6wyBC058$C>8tn!=FW#oHz1@*M%rw#z@Y;?M?qHbGUKXts6+)`|*cY_iwI9p9QQ z_7Zny&csrBq{&SKfa_r)0u2#PIfIjXtyVR=X9f3c4w1i zv{diBz-J^^fR8vziLPPJz2!$sM(?2IVJS5wz%5SN0f`w9^xv;#-zJz<0z$)Q()aBy zd^C7GWHJguv|mli6MVczLTDGU;beNVeN|x?%byXN;RliA+MJ94FJDG8nsZ)nLqfD4fI`)=u&>yUOli6^rcjshAY@$xq=M;OJl=wROm z(bOZO<5gHY4vqnC%u9v0PfBoC<}L^hlUX6q;WRRJ$h19P2+*11NA7ir9%+<`rX{1) z4uLi7-L~LV6zCY|0=Qznht++08pYGSZjxdOJNWlb7vX-PguuJIo9p==GJj*74z69; zpw0PEMtl9N*hjGFL654E+WzMd+6bXmUr_>LZeU>x;AMW^1a`LnecbI1 zkHb1XucLshq9i#sYUVgiP*Eyu`9eIyeJ6-(YBRq0V#}!)h-4d|=tl>y0dqCo61Mxh zJ=3i_euHL$vWz;f0VM9z%+soOkHq_r0lrvSCUzXak5^E5vM2K4*`yJu4VmL6-;<_| zA~Ph4vX-+5cx0t4s%<5jexj}Wo>a9QanItPngGh(=et3Z9!QE!?O333WvIHvYfMS- zam!L6l;cgtvRBR=@Jj~Ef0X_{0AMiG)#dILVY4P!So9;nBME#>N>a_Iw^YSrgj>Ho zSv3uNf41!rDn*hOX;H%#nX{TKD?`~_eEP+S%2$u&B0(bnnEA>>t7n_9)RxPoPS~=| z(@;$UGaZem}&i)B379OOhBu+%AinR*o?cV4ez7)EdRWM<(+^VP=2 zKkpMMN-dJzPk}M^28zoaL5?6ZdTJrR&6XaUhhF>v89o-C$FU&Zj8iKLKlUwi%C8F< z9Hz;N;Qxl?Fibp7LZ+>-48dd$-{?Gl`=);bn8hrm8CYgey&lFA5Kc`>{ornguIJdL zIdQ$PTunXwJKZkB_R3VIOM72#KN zg91-j*<@|!O^5EljeVdF@Aw+#Kb!!rUVN|YOLx$akP3sB(%-K_CRO{ogo1z#HXDTj z{8JVI3mFajN#skwTBehqFxfCXknOt%G)R=v$z>Ja=k0q?IUnUxKFoyjs<&xesM245GwiR+w~%U^QdLF?X03pDDw%FKu8JJ_n=HLPC^x2 z*tI>kcvLT{*TW%$i8WAL&I7Rtb}?&O)4P4rtLwz2FJF2OwxF5rKFPlpGwAckDnJzO zSQb(2bW4y-%Ln*1zrDgFhiZ29eV(?YmtQv%c1K^QA3e9AyrmOzSd5^wF5!=M(!i@~ zPZz1|wJcrm`yIgWneQ{54ya7P3*oN1de*K(0>LXp&i;OcJHz}fu6b-lOK=|R?Hm?5 z08}2ogMyUa#vW1QD9j+-Te{c@nEc#DR+m1M`XC1gGkUL(jEAYi;dGya8bH)<;E*^$T#i!Js^G{^f|9g> zHfVi(9CSeWZLoL4g1?5P?t3sv-$1cI+X>2-G17WJ07t(k6Nc^ma3GXCOU@$ju0p8z zg6o-HjkCYVHOWf7biOW5ly!RYJ~;k*Z>jJ&F>c$>jNtDMj#rFy+J8tV1b5;x-n-Y8 zD6CT!5OJKee`l9*msXKu?1_3H0!i$Z2-98U8<9_@XgffQ;Fq3PFpSqgd;D0eCzusO z+r;ObK^)1^1!RZxPTc3nW^k{Dn)5Kq5U_IU^v;#5UF-x_s-- zG%|*0`u>{Ih*lZNw3=x~wABzS!H%}n;k&Svd5i{uuSQAD`G>iKWQUMhH#%u;AUlF$ zPvQ!wRlKIhrr6?SO>9WTZPiyz< znN+eBL_CO~p*)(@mg0*d7}X%G!!L#_KPu@5_FJY`5uuuY<&G7aO~Dg-h<@v2iC8ec z_XWikNE{>c)Ddxy!e6NGi6T;I-g<(uF9f=aJV=YdW+bB7>~;Y|IVbuiWVxB~*@zpp zUltSM6k#*OaLR+}%O;ZH;b9$ee`jc&3R1fsIomA(2jZfh#GFAWH*-8+y$MQDo^hPr z#Lyv*n)*2UM5srFTiV=X-U<1IRP|FQM6fzgOFJ(hKN=euLrRv*P~i#}L}mI;nL4p0 zGaMZ-9@OY{Dr=UXF)wz*10gKdXf|ZNK|%LkAG)>wHb^nS1W3Wtr|)I14!E!cm$6~5 zv&8eJA(LqrBkZ0~fS2SX*aV(9!qd{l_Zg!}BPv1IT&D)#1M*>s(``qe)!=MPCllFi z-$a?5GpOhs0F6%Nt3Hx_dg5Ba#x#L$Y{ip{k%5^Yc$OVLfjLKnqo=mix%f+hZ<*7~ zFqGE-(~>w{U#W^RrJP^33#vdA&T?)OJPl$o-RD=aV`mJ*KSL`c%xSu)SCt_dUl}kg z72za$`{;BXMmGj@Yp-4MF->x-AgU~tawP%=+ovV2l^dZ=-}Sl|`H9{GDOC8v8{3x4 zfnkV@W;L*Zn*_Y0=QtD;pF&+Xg0|Mn+GM|i=u}iI*MexQGuSSH{Fe&tWYPlHFG&qW zZ6{A`1MiJ!xep4>k7_DlA?!(yCg%wzoR ztXFoN+4E=3L!0{_)HC-kh6Jh#<^;)7I(k?E%(MsTaqx;RnE|MsY)FlsmwG*Y7)fIU zG|lcGmLaSUjH1`+AcYWUtYu-o?^VB~y#F%HC^Tf%bdYHihAg|q)Cczl!#*UcW7&eE z9yJT-VX4fZo69`!_t%>}S|)*P`zGG=z$N@!F3_Xe1-{hleBxyAfK>kVdq}XZ>dOel zm(q?T?3UatrwN@DZ!tSvdud8D#4C|YzF6HnWx0TJb&%S!daX3|wh{`ZxfzIdLE`+} zJn;>_x^Nz{;OK_C{h3ajCKLTN;xZH&C>!XMvs|IKI0UcWstmHG+2uUdgm3FM^+FJy zHljt#?-*c=N^OzZzG!9M6@@zzsJOkaS)vrnl-hS_ZPEjBI36jBSMWlwO z%gJBriV0toRTtlB?-z!wi3OkeGtovw$wJ>a&qHcUgjkz3Rxg6qGL4hsQV+vQI>Vi$ zn|TgGyv+{{!ti!QBb+Qlz))M}yClKGiV3XHUGBsP(Ha(Q@2?0~ur67J>-LahMkc3J z$`v}R^3B|N@^RrlskD6QmU2-PT-EZ28WO)1kxJ9g|7lF&%`}m;0gdI0eX_+Q2cfh* zw1x(M6Z(nby*WX;7|91i=B_S=>vrwz;gOGA* zeEMq2;8JF~=xO^| zQY2?N%0iKjYepsBpOFd+%!|acyPrzDtH_>Gq9NX8YWX_Z(=cnzNS*t@raZNAILlaK z=xpB)YGC(17%51Lm(Ks#x3g5ZyXiHn;1T<(`NZc~{%oLaSsjAjoJWDR6lc-MSL0R3 z;=o(sSMROr%}ea9MoJl`{$L=wY_+l947twJiX#QbV*4Kzl#L+SQD3t07(C_PDxp?# zj7td=103mOhN9)v*cg)87VV-{IAuqhPPCyE%OHF@f=jrKzb%>v*?&H+Oy=RX7__)X zGg+?}45vY1MLhv6)XE;!CBG^$TyonV0Yq2NME`2)&>X3AH{I8vyQ`$^ zECs`{8YSt-idn-4WRO88g%tZ-qRg)-n;_YJI=cJLa=i2rtd>)v=FCViFUnc9`_3-M z((=#kjyg0rVbtUS9hY|*9`3Yewk--`MFjC!Ha{e^Iictg?{i$s*ExYr)j%oi*0P3$ z10^f6&y%4#*fzkj0vi~R z_}S_+v&_?di3`4PZ32Tqfz{LB-&X4;KasXEojQnb?$H1ksx8276wn;Mao`?2nKPyI zDlufhfU8(k$w-{LKE5)BEEvduZXHamK`o&aT&gO^bVp#^48HpDJS{BxT@~)%4#PxQ z<4+lFXr@!#^)w$SQfm;NS5LkYlI8CTXo(cJNFK^cHZxdMF@DmF16IiBC2tnjgC`3m zN@TguUF%2niDd2s)^O~IoN>(@sD#rc6ZWZ=(m!Oxa==R6Gt4D3%Fn@hix%^8IQ>X5 zH>3egc%yp6tv;1HoiW+>P=SU~nRuMkt{Wy1*#J8QcPlTi^h+t^z{tNUSt^<9&GyGP z=>^zDVl{_PGRw7*X9Xqdcb3G1?-B>v5eJej@L;kW5m81kag}Bh$m8Yjia#VdjQ%XJ zd+?c61q0VOC^(8#7#3ARWkPjau?x{+YdmYFV5?3#f~4^iP?vqA2HvL=W8+rQSL-@j}%w%#LrdXMBLpP6gtcW~J+2Y96){J9zRH zE+JwmX3+)Wb#A=t@zwhrSo!0ga;DL~k!y*(hWqUjHOQVSashp4%b<_*Hw6ELA?npj zDxM9q!wee){=T!xwK&(glt1$OPg^GE6;sHlfSGH2@H^y^AHg@ZlI-&jBfe;_M8lBW z;>o4|><5B=w(N5T8#tVT^&ek<(ev6~Pdn~EnLDIcPpKvNrd<#l^2W?(ma);?<+Ma<-Y0HCt!OQzB#C<{r2-1fjV_O*J zlgQaL-y|<#=sk(>=EdLV!hyBj`ofWe!J$Bstg0WP9nH6VfB6(hS}8C|V30Q?RGGQA zN@9^9GHRYC4NTmb`r8pYJrHB^C{~5R8g-{jaNW$khxRTzd~Z2(Fv$D?5;vA7^my3D z#L3?a+BInRq@+VC&ZPtO@T9P^&LU)1O-7*&fZ*Ea2Yg8Hd zSi_`8BDY+6U$D!MUcmVkh_DQ*tSHgN8wf7Sae!ELkr%B9d6)4>u1y=QA~sflWo?P? zn=Y%?ydcl~qZ%N!Mr0V?YQcY3J?@^m<9?mazfSDH` z7fK>YbtVSf4Pcs3Dy`t$WMfmV&*&dcNMvy5w20+Xv-}8)@UhDOoUA76ScZu>jcD4?U6h5XV-+6g56Z4(=syxy>4F`X@8MXV zH=66ZQdoU-GoT?QT?7s*mGV;<_tLJh9ZT|PwFf!9*)C268WVAXa%^FNlUaqAcJ>RclHx!ufl~XzK&>*V4v}P< zK?(P!@$dUJEj#K(Fj2&%djZc%rWUk zKFWcE!=BGL)KtF3kCsIzijD}rF_tQ;i^3(`QGsiF zX*TToX9lzCkOO{_qn5LAcN_st!G4Bb2!w}{aD=-bu~~8oO-+If*1cXBQ|b9eB@;mPgYD4-dNJ%+l#4#r~z6t(5um z#vqqtwCiLf^yp);Rv?K*-J3m;EPEL$Kr`N)OfI|OX3SWcniCue=c?6XfB8~`i6qOX zVfYGv@(ZFkHef)iTS~agpIqRSkA;5|eHun|9QFIFX^-R+;!5zHWc?w9dHuek^FHym zR2++)m+avoWIrFYS5NpPoqsfB{PC;#*NDJi_P{hvQ3Rs}G6>$)&uZ~*LOAPe?>)P~ zPS4L+7;VyT8;L~s#D}I$Sg+?AC6aat{5mW0gyu~%Hu3n}x-%GCTy^$Z24h~+mnm1f+S{&euO;vuWjAE6+7^ELK5lVG zN2yP&w1vg4dW9=YZ)Op|XeTs3ttl{-vTRhmSC=Q-rRQD;5Jt0ySyS#fw+OEyU!Flq zIha65{5;xKu2hV$5K$3MFtqJr+k?7gr`hFinPEXPbP=zd8k4lMUP#A+kG%M5^Q?lss`rXt3POa7_mf)X$wh6nJ96z39)E0i6XTc z?I>`M76O(ILCVN^0K;jv&0{%O>LCC5p!Ur(8-z7dNAJXKefAq?Tl0P%%KA`P*J7I%N5BjJ??qW=m4O$*tN&socE zU0BaH+)o9r_Fb}J9q>@Sz0f~+tSW2QJRVlAbq{+1EtY{bAS<|k@3K_*t8o1q4y4w` z%+Tw@2ohJoX)#r>D4^}*7j~w^>I|=wDd8fXz}su!wqa3)vG?-F?nei1>t>7#RiMEc zSkc%3jZeN8C~$OO?i#Q49SJsf<3^k|)@w@mebZBU1*7E7Hylz#JEOJGfe1eYL4uB! zX+ocUwZ-K$=S3Y~v)d%IW*|Gj8T8T@;BxLWv;XPbQ>v7bIxclh?N2xMkr~R*NPQx= zL8FCz!9J!HqLw%Gr^5ST5Lddgx!+ng!b+kY z^I&ejQd-S#_fK*rIz6xLJC5~Ac%z|T!@3KTSYtp`h(7k>Z!>F3y#ibIrQAQ23Dqc} z4d;TWUz$^Rz7xU{Zf{O>7t^$rBMSJ~W&7P{BI15g;JYL)qS(43rb+b_-k4ez)kopw z?@bz32*U8}eXY3rnO^kh)Aak0)Rx0uB=vjE3F`S@b}ftckgC?Tqfi-H@@XjO-<~d@{^vNDQvE8a|MK68(@d%=T`-9kx;1{B9E7 z#GmvMsV(z^Q6(24Av_L;@A{9#+7pCLGv3*@&gSAz zb9=b?o42*roTm{SKbe=<~_bycO&IGJpIY9UAJCE?jB_t8 zb-O5QWqxV!Y`&%|fjNsP2}ytM#c;NV-SxOtUvf=*B8zGIw+GHQ?{4m0)h1U(nQ#08 zPcy0Zg-;CrxH}s&cIihCdXOHy{?^IhLBD_D-4yBjdFbl#tCoc}?Nv56M7v-u3(Py@ zSuQ%lt3|ZQ3fqtLr&CeJMrvw+f7<%P3D}~%MHI4p++@PFvQm9#S+TjhD);YQyYLOg zCtxKAUD_4I76nx-iWCn>SV2s)EWDo8;JFZJ(Bu`a;dAbr;CS@czW2^v`I z!|V02SSU=O^>E^lKAhGi;4YW$5s4E6jyzp0LWCNRR`>;@ujb{@7?s3U$LHLYlP*CE zPoDTx2E|$?rEf6eMxzlXyUIz86byp)*KL7)zbR9WKSM5}Z+QJC0U}yZs7no3AcQX3 zyQ*t#-e*)xF=GWY!n85EA=}Zy)X&(*5jvT_DEgxcv7*%2z|h^}bvW{^FS_HLpT^E- zk@dlZG_j&NN}yR~%;CXNT`@I;(RJ!`KLOfK0^0WZ%*wF0kK5_Bl$+s*B(Dt|?+F*` zHVhorCA52siL9CZ@M(fduSEJhZ`EHWHZ+nHYQl4H*s=2}i9)NLjxdPn;{TDF`h zJmCUAi+H+M9GgU$L4l@{UY@vCqnT5L7e>QEnK*1n$4zQ4IXbs=c_iw3Z=-V@4e)1b zb`ukS3NUB6t6_)x`9)raHQ)(y4OA%}kzF*oj^^v$#ss*F_XROtm%SXZTBl!{thg|8 zy3X;7b0dqDhdn&b=PMYE@?z>@D&_PuiIj~7gEd2TNsFAJU8|Wvk+0)8zZ}2tF#& zRInW7dfQ7@${Lkvo`8M=WmBxKrkX<&e1Gj%nq5^H#o6#dj`nPL?!XQeu;3gIpX2jl zW5VqY%JIq?m>HCyWzCuT3$#ehB+ffdqI#}{y^WtReKyZ;vBiY`WT@5-)V7586Zr1S z=*K4POlRnrkYF$vnvDzP6AAV%7K6UsK*iCPDYYN#(%#-RUOE6)lg9c)SF$od?c<6+ zI|sP-F?K76CLa2RSKVlkr|Ip(iD+>Mj3kvi8WDvU#3?>Z>GoWo0fY_dx1s{9X<7&N zYZ|}Bdg63yfIMJ*i;ku3q^zK`9P1Q*bRo?FA+x(}`JK80K;xip7quE3u}W!Cf10Q# zTlMVjW>{+IH)_m|l4k;(bZ<;&@2_2fb%M*wMHLHLtR%1I;b;@J7i1{w`>^Ice{`_P z7qhSisE=UJJPra0=kI&CT6b{Q#zZKo*4qQKrG$*M>3N|V2iytVC2YsK3lxhvi`a54 z=FiXVEiM5pep-W5NJ0CVqVbQAidh_N98m07lJp z!^z=Xg}veR+(32y=iu+p`B&3#;rv*Wq;ja(Ny zP-^)4V6J>cYDD(xoK=3NXr{}8$ z3R*?$?V6nKW3yR?`BHkYs*+JJRDv=T3tT8dzq=vw9j1KC9LXNAs}hkYMGldIV(CG7 zHIwrsID_2gdo^g1ZaWGO9jI;-Rl%OZu|n`YA+pXi;akTK+bx?LC!d@BmJNS%A*;M9 zGDas?bvt@_pqs33mlQmF#*#36MfMscyojJ=%Gc#N0F#}?* z{}U-ZcS^WY0c;P56fgrNIPuF=Lgi}xHX8=RqdGK+oov62E0K*bN>%Fdc~xFLm0sg@ zwYHoa4z8rmTZkJ_RrJP6#4bUp+0;(+AzeRuWj4B>*)qvyjnNk031RKU|Lh5iwQ_6r z&J@D2=?SIR=f_$7ocHvqXN3yQ6+cSMXhThM@$3>s`runS;U7XlMHuFM+Qr1Z*S4aJ z>k5#5rQ~+&ul4&E2scm#AvIL1eLu670?~mVho9@pGnW_1PABkxdjYuANH)jr%px+s zj>yuDF+GO}zVi@jh^2X1mpe1yZ=Rr6d-_g{L#~R1c8i(L`TCQuFps%dPL59bALOmT z`;deLasXbd$cCqgK#OBTA|RnOEOwS4VxhG67*vE{obkF;vAIRgE&D~IjV~jH64AyEneXii>$#nU6L46^?uW~V6yr&{ zEsP&=5@=dt8!m0kPl6sJ5)kn#+lT=g%k-uvgI+6=gI;wbyZ%OEZrdoYc0eT;{t%zo zyU0N02Wg!@P>_cL^cHHRk8)}4goN|`h&?_7m9~`_+RbQyd{6S#8EK{8j3}H-6kjx% z2TIAE)6!?O8{8b-9BnC~c>SeB9vsu8o35YiKTw@S#W+RUm|OK7x{10-+DKeQ)4S)e z-?n{}_u)ApQPF0grT-W=zEHF?)znF$GfpZyYpl&de&NY59d5YNjt^+NL&nv2uvmry%$WZCtMje zO?)QAO$UBNR&Q}hfytT1AplwrbTE^?&a+BVdHj1w0K}^EaI;+0YZy368Vk-2kZJIy zLa8tl-BOGzq(S{si>Z#2FvS@_;}#E8{IVwAAG8+*`1BqqO-^{;c`<><`j=e&;FE49 zZILJatPgM;gB9EOzBB?e=UsK{2#*2ZYi?sfKblw3%5%8+Ba@ZhN|xts|l; zLL^4M#S9acu@gDF7B0X=j@TJ&zT_l)F-TJR1yJ2WKTf83lV+0kR2i-%9g~5lNBj9| zE~P;dHnciqqc9XK!QPfmG-=r~ifEJp<0HG`wHUaOsI8!6{&hXqn%&p&GMZ>Q0gua0 z-RORcXw7$r0GvL96yn}y(?O264!p6*=aj5|oy`O17~#L%fm-J9+TGCa$RQ*4GfPXhslfPR@Qj_5u z_uX%EW9pBcS>H7>7YSpH2oLj${BXWzc})dFLY{;bgC2@y$N|NejHA+ zCk`m%Y=MrPuKA*Lj-K0cab9!5V^t|jg-^N+YX+S2@vAVW> z1X-c}qY4>>rlv@%`cyIbOQ#4n>`xOVi%VX4D`EVhX4_cxEtSP!pch0%UGi(3u@73o ze$+H9&-vNLJg^P->7Y!Qkmb|lnwjc@d_o_76Edp!3s777@1H7``fRe`#Ds9a^<1Zl z9Tzy!c;a5*Xpm($`d~o{D3XZhsYMEB^6YWHjCu5N$iG|l>AWgibdC2H_>V7-Gt=|# zulf_HF1G`8O}5?Fw@jyO(_tKF03I{i^xluv$h$8m8H>41PaDV;I8DG&pvwSwYhuLV zbF)mB*J=2Ter8u?P_zD-TzHX@^DR!IfyA0xO6pG$`_gWdGRegoFRe!1Ll{FxHLSnj zsAN%#K&00SrOIU97b2nCE>=8? zKoPe%1^IR9_cw2!UP@!ud+#O`X_N??={)(ltr0#6o#FJsg?-OA8~l8jNQF+{fByX- zVql>^sH)8l_ z;aa|y|9Gh^Uad?51SZ|5g%%!XZ15+*2%8g_S&%z^wHk?3#J|2Cs0C#G%%WJ?yi|bW z`l@_Vf}f5jhh!xEeuc%23g8OIFa!K3IGJEY2+Ddo0^aNYs6)fsqjnSkfsv^IkoouX z$b&_{X^UP!e*MR`vd~{WQ*vrE4IxIxrUS+F7{sp6=_v0{Xb5Pn#T^4mxHm194?T&m z^!)8LKRQO(w|Gy`uz%NAvsNH`oo1EvC+d5yRg&M2)!=UzoKhie5qL%uMAWH*5dH4lLyf{kk-b=cGStKkZiADAR6#_X46ET9!^R0_Lv;qOq`yh=LHV!I z5Y~y-%kzU#osYpQbxJ1Y=d>}{)SkEWH^Jjw0^uztJx8`~32`PPnx39V?~**~Npx@> zRmvWH0-DpXX9b${psq{p)m-JF@;NqP))VJ|>Xx@@to!)7Q23qhr^kPO%jkmUo|Gj{ zEcXEAz`kUz3Y=YYkxo%S3rxny?LX62vKE9a_GLrl)4kQ+_`~Vr0Lj@Ibxa(8{NNq1 zqsUP9*ArPfS#~!>XN|+DB*jPXphR{xG6RwU?NdA;Bz&10RVltJfeLWLshLhkLfRPgo`t zy9vm7#!l2%ZQb7usfkuR1=27VfKtr{kvqK89(H0dd&O@W5JKx*d(n8PiOo88^Km~-o{^EWp!f=?`h?GY2)pU!ZzZc*xTe?T~`IwQQx9e5Me?=ZR~ zh;;^HXgU<}>p*(Mb!5~&eZw7Vj976I(xv3K>wwfrh+Ae^O@$_sl<)^W_PO3^`?V}p zrY5#atd!OEb}nyJfFr}|YW$wz1IO(lJQW!0vI#c@e(}|D=#{w6vFnJ=kG!U=nD?f!ZLwy{!^b zzY)oe!!?>WF)dqMn)if%VnBYC_zVQ*n%rC1nA0|)bC}h+JdGAdg6}?82qYx& z$P)bpxWa8O{GLEMVB^^LyzbbSNB)zwot5ery-AUA`CY!t)X_e#uMvMB?XLbc?hX3& zal1-Y5;Z}6{ug_18C6xch7Bu{iom8DHk%Zr8w8{q z5u_B51}W)~&P{ixh#(S5NGK&;N~n~SG$IYs@Xh6U&Ut*!kulyez8~NFAOEyN4%DTZvi$I%Xw4+A7r7#u)Kqf8{$5tbi8 z(T__cglcP^p7Ddf#N&E)o8|%VaWVHO^I&gnt)2AlJO=$-N&5!aF|=3htEP6S;oEZ! zDvdHq-mJ$!mnXJ`7O%?s(4Hvfn4S|8^=-GcfX&X!Goes_p!HKg>-BZTNMbBRH0)$K3UI?; z#D?Q_r#_lb*wuT#6K8P8U7h=E4I~+HK}xevFUM}HRQ7nkbG%#33#jn;2OR#YJH7R= zLe-GDEW=@sn;i_7u_D2Bz9g3my|P%ja`~!Hm1E8Kx09itqGiJhajZyLrTSW!6EzOa zUr$+=)iHX=(J|9WEXt-#Y=*_74Q>EQzs zsDkxj_a8}7>%Ev}nBFdR)lQ}Eit`~P+MHcA8tPB)Ke3By6SzTarglg4u2ih_LvOIR zXAEx%&*R$PY@6kL`wpYYXApux8f*Cnj%Mjgc2mZ)CNR$7Qq&L zV}29QSy%uQ=A`R05#Kf;-IUhUk(s@~19|F-9wcqIlhT}Y5;}3cZ|7aFH?tNGqkv+vJdjU<<69{+w$h{J=V1_EVlPH@IkOmJBhxrN_`sHCXfWb-(vX# zF+KNHK?*y{hxa92_F}^|I9Qn-0$rXf)WW$v4{V`1K%u*as<*q^~4W8!F zv#%maWYYe!#MEw4Kl#RGM;>-q?Uc;<&_# zN#mn4(6FlGb5#r~$LP~HY{Lm%SIC1f2!d^HoMB42;|0mUg@<6&ex;EL55Vbib<%6-SRMGW?bI% zDP0R&!y2}KjNp!4iHF-B6uP^Rftt0q94q_6)^5kF*ua;_@@hvZwp-tV2;w^tEA#v7 zh`>#gsvDp7#07?n{I=Kz_C&CwNn31~h{^EQwgGmJ2T?vXtW12u9kw8ha6(BCYO+ynzXhCp4D&IUPqf zV>7A_9YuHUJC;n|TcJ7AW^%tkQx>t+Koxcfu_@uA-AYk8hSvl0m;x zfuiMB2oqEP<&nTG8~83Q=C`a662&t)(vEcC>X#(Qly_YNV3#@jSJdRl1-G$^^gtiO z*2&>R(kt5?sUNbJ!rdx+4qP-(tn!;R2w++J>02xc+U_EYhbSxa4T}xf-vYMoboah2 zX!`P|P7Nhc@?q+Bx|VY9wBwXR;~b`&mkEq?E86scpd_S_O1c ziMa3Pt(v&I*`R^FRp}x5tUrGyDxT?bqnulgR=^zrZMAl)L3SFa7a8s^sau2SPT;gd zdi|#K?nJU~PeCswZ&)JD`9Y^fVqgA;OWz6Ow>%%2Ke&>osca|7K;7RfjH+;T+P!EN zt=rHJ8#888Vqx}I=M?Md8EDvu6J>-E!Ujnbin8FcoYxqY@uXi!(3WrsT_Zvng|{WW z(apN*C|U!4T*w&{$X9^ z@P2f2*ZwjnoG69f$qu~I+Wc_g#B8;+M%6^sXOSmIVcN>`2`7xkEZXjR+!oYcc6%%+ z1SZPR?S@Q1#|x4rU*YUnE%1-oBpsz+La;O1}&I|_9)`x8ws~QJ2bv@xI^3F+UQFyPR}SJEMBl{P39ZrQS<0-rl2#bTiGRt14!UmLIj*?4HJ~Z z@m~b`d8kkkMoFP0gf^QJ(C>&?`=ou^tN|c>v;^qd@5IoxPPw{|OLSSL|DXwYR>uh8O*?oe$s?GP<+H78@~E<)epS3+S_jm zOy_|HHoN+ND2M&$Jm^5XnEr@RnCO?jhSi++X+k@Kdx`S!`CXIm z@UYEPw|+av(;duY%GPP1GnCs?nv{D#@gRJZ0?$s2g_y#rJ)3p%v z>x==dLmsWdZP5!(N9u{9(K&ZjO=PVqph=aDvCSyi?w|5?zbSU5rlNMp#G-xy8pZGE zLO6>qklz5x@CnVeSn-pd!b1!9FA(wYLuF+2w=&7H36ig;6xnia%iwkPisnE?N zussILACq3Z%Bw&AusIf%%5A1N{mOCzO`#*GuYqPIJ&IX~jEw}oGoA2lyg~MlE3f~1 z#sLloM`g6`2ktf@@6=Oezot`(t5=6Yl0Pa(Cf<1uB3AyaUzA#lD(P+-#@^0-uD_q8 z0#(4E0Od24eeXe!enoG9$-C2=h*us5jU>CpgM}fJ7%uCtv%UX5OL`|y_Ja0apO}o%ra<{#| zj(z*?S;DvICsBX8+{0NsiC0%|SE$0dF-Ofr_hDM(ShCN=0GCod)1Sq7NcF&}OM%+X&+@Gc z<#^mdT+_Ldy{lnHU8uBO^`}PyQZmxHhDS*O^LV%D4Z{C8??G@3#2V*wr{SM2un6#n zL-3=tz$c|Yjy9Bz0hNh%jTXef4)n6QfCli1^R?4=Ui})At3&l z$D35<0CFLJoGO7ptkH2`BX;G2x7CaXNeAU+tUUkoCMy4M0Fn+mqT=@c^9Arp=}0?yk_ zQUw5`{Nr#*NrT}++7+bG{d4dB_j=-g8|X#~ExUE?Ua-_ZL;`U|oWT#fcQ1P9e}O@1 zVi(;b%pb%VH=%fEfxa*M);mX?D;I=HU|i)MK0xbUtNvOx3c?$ayb7RM;zH=djot{{ zAR<8LL-ERUHQ=8B3Fd!xKG)eeST#J?6Fpjfwo;|$-a-%#X&snk1vv=OjP$(`s01{X zd>y;Gh4eMoC$ckuHDUp2^oZE%KJ-Tm2eLpcUqueYfqcUHEF_MPr4J&BaA163v`Jul z^?!GJeSA{w+Z7sI@z!n6?y7YUyBz}(fLS@%rfaaP4rU`rtV_ye%R$#W5!9o>W!96J z{O!1I2U7Ck0_n8#Rn(<@AhmNUI@#P7g~!1xBs!D0QojJGs4S2`O=1gVPtguUz`2`?_^kftwF9}#$YNSD1@Y6} zPZ2=9mtAY=K3%2@!r&uG9EB{_Oq2^?+Y~lI1e*3=75i_&B-E1|G4E;pmZr^8^pnq2 z0S9O{HxTEB!Davnw(P8O@Si@`<$&f?)klGaBnHY04v5}W{Rg3;RkW{X!&!+TS)`*U z!TdqYQq6Lk!ZOgE!f2l%^g7Qi@z{Sls@h!qV>HM_kfeS{~8KfGV zJ-hwCIUHDee%YKjs38L8p@?UFg>)FTe$CciNm9$l8#q@eYypvZZ|8{yi1YGG_9$R$ zwQ38#8J|+0FSt7(^{JZ%ROn|x`A2?s4upi^zCawvN@Pye=Z6UmtE%9)Ami_!xop)) zX_yn{OI|&hT)lesi=z3DA#OIM<+K&;#KCTk#2T}E01SvQN>jPVp z*R?AJ8AG-44(N=XPeo3Nv>&KXICN6ABVt7388bluAPe@;`-Ey9JsNb&UcC_L zCx#E6ti5T6RAL~fk@kWNyY#cgYZJy-@+cjGOMxQQjvfhHEViK$(d>dna?L?dc9%T{Uozs63e5@JT6)dJKFU?v(=}dLp1f_``A(;#{>? z^snIa`blry488ZhSkbKL0rEk_8=zL-__~wE*J}%?{uHjg)&p)O z5gKZ@__+L8JGosMl+(%TefNqD8NTgyP}nOQDG2nB0InHVztL;s)Jxa}ugzYdcZe>e zCmVRK%-{NuniuIX-c`%q5jvk{+Co!S`eU>59!fOJ#BYIQ|hny0!gd{3vMnXY=>#Fp!ps1=O>B{ z^FRfQg|zHZhHbUN9(py;(j}KtC>v7ewM%FYqLVn?gd9S8dFK)}%LJ-LAEK;NBDv^` zW^#a_rpng0+q(ChEy=?~77=%d31K%$UlAMwlNR-QAl-8lZ@q`ugbZRs?SvUedK>_L zMAZRsP0Sq;2YXx*H5m4dg7B)FZw=UDAbn4@J77EW@2kToq(81w; zd-L4C25w*hb(X*H>op4MwC1I$*^T7+$P7d%UJ=PrdzhAkg;s8Wetsc3Q#JeIS#)}#(F{{Yjg(NRnvQKENXg}V!Z*Xzm(Y44*@)6%F zQd42u`9{iY8Y_#fp3#DuKnf38`Uxg+%>iWaQBu)!<8X1d*zokxY{nB8NI+(V{Jz=Z zytID zZxzJ*q0QsSV_l^!6c1==iLO%EyuNMY`kS;UXBg%k?eHnxECYVCG1r6_$#ZR1s1^Yg zGD7LH9+VHs$#!UF@G8J}$h4FoYc#>=A0~O}DeN}BwN{LoFEaT95NcetP5KBDe;9?+ zx4e4+liD#sN+`~LihqE|*|{VWL&{rdVkf9<<>){n95-eaMnW39K&tgo+QgMWAv z`a2dQ;)NsLD!opV7NtJk$5U?^j$OAu9)Hh#+0B^2=1U(QwQHL<&uLzYw4=|Wc+Zko z3K*@gL<40^DP*gt25n*8okDr@FPMT<@~*;Q5aT<&Nm=p^ya(H4t@j#xZ7#2rg!k#u z9&1>V^b^O2${uT3!w`r+9Cq)Jmh zrqB$K(DmZ!x8hw(Ta%(kd?2@jCef90&YtS|tg+iV-1m`0WS@RQYgx9}cK2O^6UikK zH>7t7tjEbjU`eqWOt+;akVWm#(-F6_Na#onER2P~lE#~S&v!=*eHSzy6n?1P2*efZ z!iLBj;e%<&518Iw0<{8-)|wraokEhe*QD6Q)@$cxO?*Oc^`n;KVLXq2gHC~3FQ3M- z!Aglo0QfPJcXyD93nJ}1_RQwNhwF5G_;Gq1D(~t}Lv1nJAL{`bt)2aETb(p&QX$yg zoucA&f*y9ixyecNBP2q!og!^s6p;HW4PR9(N*w(1nV*frzf8jKg`9N9qdZeJ7{Ws< z%NX`nab+Acv0YpHz}RJ2jLwfXZ|i8gm*W!%fYKppF1D+(Iat1OeXVx2-$LRg+LVb| z-eBrVOA{jqVM?QTOp!0D@p|ON#ei& zR9-I^)#+!0X2IimauHNKBa$8eYpgfts+hhM>3>pEj6W zTW6+Yzc+Fov#?#)2vxh0<4uXs>C&_e=Jx07_wydWoDXYYMdQ*2@pi9%3srYTQKSVc zH19bXd?4bsR$sK|J_}(HIQxifS~_^g8qE@#quPv}CdCxXCuxi0d?OE@_aeiq2(*462Ca>DEh>!$(|2JA?>BniFRMQhm~uB^ z$6#v9A1$SXr||C0w2_9d&1dL+!&p5Jh7ZB9jhV9DjWbmtVL}5$EP=Epw-{+i9KT^V zQo;=TeN^|Q+K{-H(VS86=J!|T#MPzMhQ4sMncO{X)!`^Ops~qZ%KG3T-^%2Y(WP;h zvjy1~xrDwCVqkVqNDa|Z^u@rX&K$wMNyJQ9g-$U}+fH`I$?%2TzzCkXlh$RJE#9#o zzJN7bSH3>q?0-e+932leLRF)&=!cxs5smswb)uD}_wknU*)(~49C_hjqO2)v3#V=i*Gp3P`x4P`U- za3%G1(D4@4`f?+`lh}6ei}tJ9_gL!gurN7inid=$gM1W3<~@S%(e+T6fH3$h#1(TB6Lkp80lHSdlJ`j`L6HSU4(X%c^U!6!H zp~drW#yul$?~jroI_se21lp^_-8HYzL_rJ5Myxu~d;W>eX<-u(=XrebNtOsDKsF^v zHDSh8w2BO?QZ4faS$w)^zt34>+$-W`5QdOSJV8(iS_lDg=jgnW)yy|jX!L>T< z(Ge^24rL4*kqeVX83#cy+t4HqH;Hijo2=pK?T+n)`?DTC-J!H(#?}rzv_l&vZp*XY z?Q@E@+zDi`=j#0CB6CjpMkE@n;Z~FC(fiv^ghupiZj_em%oCmFf55X9GB?Rd)^R$H zk?->?mVOzJ6Ei4h9^v6|{U>lGG>gV$w{!j+f9ClQu<#{}R+5YVPN`Nw`O@K6WRp53 zKJ8uZNEp>rN!tn2hg7$myC6i$H}|{S)|i< zn-U(RrS#DX?G=&=cf%#O!X&3Pnfs$Gwm%(Gj03snKv^Fk&B? zcM5j#X=7t<$4g?>jZ=BR@iW2cgBd|(vATWf{zK`s(K|1+4)sR#hI|>_k&&j0sZnlt zYhlZgOMOk3hxg~F*FYw!Q%qs-8wj(!h^mKwII6>YLd6emTcFw<2*5;j1&N&5n%*R` zXFjvN!-Dx-XV?VGncm+xo&S_sR?*e8X}owX%jy=aI zH=qu9NP4~%ib=ZU&g9i-nZK7D;VAu}V>}m`8A|CWzm@D2SsQ%NHJ((`6k6`iWxdnh z!IdfXIMOE;U1?H!GMEVMi(se7td6uu#Uds}9pTO4Ft+TuTEUD^`A&3knXRIFFKZu0 zmm1M1(V-hN$XXJ%(V1XAiHt`JR#Ou(xmqkY2+CK1CTgDw9*aDo^?mXc5+iAgp;xn& zi+ZA;m^+_Xm$MA(ZlwBRNUs~WfBspEyJQ79 zF(w^OCTm}idJE@- zx}uP08fx<>ur~0;;HWm}>hje4nr}ezY&LvVp(Q{+DuU(Z`%q^7Jc3rJ|1W;IMM#?7 z^_byqg!bX0paV@@YJMUjo)EE$#}Q%8K-``U{1of`C1lPK_xQd??eh)mAaI;^Ni&jY z58%VE3$P?D8B7Qg4%$O!G#Zl`t*(G}6FEH~6}!C(oNtRYRX#W&@4?XeMBbSqaG>7h z=Wkew{P>Mem3`hroea-CS#1kiE0I2sqR;b+_A4D4)Lh}1$~i5PqqAhe<&Bmnp=3)l z-HftT-Q&?V$WQpJ>$CeHUSCX{Gxi2Sn1W5VhFxiULH(3oPDBPPJida!eNet=iSisB zX)u4@{AhUM>{AEGTv83&-#j=x+pasX&-Ky`eB`}V|J%ygPuo0ggDSo`TgyWZ);hIq zHw<5qj)*298qejYO=deT9bBV@*($l|q-56e9eeAlMfwPO#AG_}T0#D!4N$0?JQZ?I z-%5J*1zGz(uUYy^ylm96Ru!^smh#;{XTu%_# z2wd2b=}5;Ogl0AO3oT`OQCMSjFbYcxmjMep531X!_wIg-5UCY=LTNf7*&0Id4x-o&*|Osv(rkv$cMxwEHTWa z0rc+@cJl1#9>HqysC8Ep_y}KtWOwLoybZpl)o{gKnwMG6^z+0J+nY#4v^)~{47Hdm zA*RBokQ^2OM!d0c)6E_7oJiPUjP@u{ ziqUfDD}B#6ZrLYtE{3OU)M`8~otnHMsd_8lgdppFfBGk1$_m@+v4X0tAz&|?=}vU+ zJ-|+Jxtu=mcc&kJ+5zfS1=;&i-b?nHC>E)^a()khG`o{4weNA9OORjqE2RNNE|a42 zz5A*tYYLa%muC-<=9#(?BaEij1L&h<^XWVFmJp8tkhS1~PzaPc!FnI9%RHQ8!V`(d zmIIs{(XWhx$jxbJ`8xY(um5AWagS%uL!|ozuMeRpD6X2rT z*Ra&%1P_a)@$$=9Y5~xG7VK7_MIXh0#95AVUV1D z0ySsq_HB`XZ$Ne5Ae`RjaXA@{&H>p|JUg&=-qEuSBy!a(@Xk{_+`l&;eIS!hJ^1oV zB=Q~|**U?J49b9b7{IVd#grAXS&gQ2i4chd??4P zq~~TbOUv})M!@W}A|(NRu0k#kzGWoz8Z3VJTJIH&yza_-3Okz6OL8srobukE)S^&T zpOCr(=S!^`|M;kcMLw0Yh47qIp2RL3M%5wgd4iH=oQf%*uWt?dw)3%elq|tF+s|Xs zZYaI522&xMb{nc(0xJFJGNYdhm~F~s=_(&5{QxUj&zzL0WUdtU8aDbheqVSLnR+-$ zf9}NS0(%?x-2s@#Vf1$2*e)uZP`xVK0k`NAFe5O)VOkPO+`!X+6;Rbk-zFg>9=!lqIKgajM<@Iv5x~iR8Z-C+s;8+ z0KXyRQaAgT5&_90{y`g%=jHcYP`@ly$`#0qWc1IL^h^8#yop5%b9okZFb@IhaU(3Q zHuoLZ8sKk>!%Jmx2e3rHGe+# zr8JA|%$5)2Sd){{fX6?y$jS48SHq2_Z0@%Z?B5lj+yuPG*Sm#rTlnZ#FKt)+y&e8m zdj&beK(e*`&vn--%Gf_10S+Fp0<|*NaXFLD3yMDjrhZ;{?|GrcMsFq+#zpI)(UEA)~0>f3{q07bCGx&-oseplYlc5Dl++wc)&csMdJ&AG1uEb;j@FePoN=Ny(Sawf>%fDUv@iJ3ROX-qik#8 zH4p&!ZBh99PfJ@A?+_fS@o|^o(hXqLPh<+m@Grd4GAV8Jy%?dm9jkC{KKHoo9E`CKVXXV8aY&i5M@`}a*kw?41~9ihuA zJVdybz0cs>_U8w>6)F*m8UJQD7OD<83JaJ$kg8bnf>9Oyap{xjryYO?n3}kbF@`&W zX7=uk@zmK#p2o=N|F$6i*Uxj036}A0SFl)(VJ5(Cj-AqkBK}MT0%OJa>&$Uepr2gG z_FehH%@J>`OrN6_kH?_(P-UMgAMx9YzI+!Il&W6s9xmk+PWv3+i~@3|R-lu`&iULI zlySW_K(v_ywVCoBH9kV~1^E0f$m{0C22!RPo@ldv_xRVq0zhXuF@PSHm~SP(5MR-% z;2Mu}0?upIL1xj{KqmP3;W&WOlx`u=&YnmjtH)*dN0g6r*{tyM--?snj%Z(^7$^+!OK?-KlSuLSo#SBlxcPid&5I-X5Y&rqJaDE zR@*L6bYE2^K1}* zz4N+${-gzqsjm~T)>VI|iJxq?@;WaMOl&|^^y7X4WzO5|8uf0VJyDUhOkk!Rz|6k~)ga!Qo3z~uii){7Dsw23P z>5>@~XeE8y)a0ft;*5?6FwKl{L;a9PtLyx|XO+e%2sQ*NbYwNjQx38JC^A=}`vW%w z-Y8J%7aHFLPpJ^WRv};Z^LV34?N!s6r&x!tj6@qV1;N2#+iRLuTyZbTMK?O)6kYQ) zFXi1iC60zfYVhd0pd+^s3S1k&$mo*NW^M{s&6hoi1W7mP(7|+3C!t#N$75n;n!&V1H<@v2C_ql}~b!|dnpom(r9Daa& zeF(0d4++13z}Q1qJ`1c@&tCkM+4-wV<$k&vpA~Y4WF3>I7&0V$3+NHW(X)j)K8pDr zYFzDvV?wP8p693gpMadw&zdKo;9Qj{8576{yiuM513ZQXP}%lCCtfOxqbY6ZM zn*3jrNH6J9z#QaA@eCCh${}yx&4b;PiZ!k9Nl@W08WST}h1eKHeIOpoL^ihV%isZX zYpS@%09yU19t}AH{45fRK`OMF4sNDEDLQ3Q9k}LFg#*>#CMZ;@A|pB}osy z@^T%>Wu>=LtvE#{UoDC}%N23F+$y?h7A z2TbV0TfJ#K1dKu9iZ0-yIF1Q=HJ}H9L-~_7>4T zVYtWd?tuQpuvAd*-Z@oH1=aul!-Jy2?4Au!xpxbE3lTk>fr$yfJNR7gU+*Clr{#?Q z83pr44v?xxVkSu|0|T<)6|jW#vwQ}F;Uxo)CA39B2j6}sx-tD;{C5>iMxR5A9Pf^z z_?zWz__~+8fkcSX(ySk?Ke&#tFa52o5dh6(BM>X0z4T}-)4#%o+)5ayWCc*`(E8Eu zcliqbDjzin@v!6y;$`0Q2hbONhA=unQ}$TOqUh^$?*84rQxK`c_ms!Z(x#4i|i-c26KJg5{kl5K*SiC2+J5Xi;Z##*K#fkU+Rrn z4U6I`)>o@ydo|Mk+kQ3~b4fiyS^-M@!n)+?U}Kjv5+4n{iRc)TUdU)r=CV1=bBxe` zS#-@rMG;kPII3+^DlOF)#2rq~^Vc})V}q54k@->O9ptPj5j5pws?v`;4&9mFVYY^H zkW;L`W4%HPSF0q_tK!OjHhr^!S6RL{yi_DD z_N!j19vNAY^nEMyx3}95#|xmR7{D7uCdtV;;=}=7OUnG~mqa6ya)aFk$2{$4Yz|>Zdf6}& zP&8=b(`P7#DI9Kkz;L*N#M-(6slHZo5;$iXglCr|-j@D6PoJP1KbjwsxRv^OX$K-< zt)_V_rcq!fsSEj5mI5EQ!#SXpOi<`V&wNUoC4#ELuFCgCvraijjIk~0;a>|+R=8i& zQ{gXjmKh&khg`Srf1PbgnlU+ZM}>D#+M}a}S-G^UI`-KdY`r~u+DGp{jvef=)e&|a$SW}2jeqks3}0s1Yj;r2DGn?n z0^vu0Idm>-qI$Z$oElBzN4y4g69Jt8Vg5>^z_dJ41q`~Y+;q$lS>#}kRiv1guAAMP z&>(M_m9)4S{#V(fYYiDyS`S&R640RFg#zVBlGIH?&QI~Yi!jbKF(SolG2TF6c z#h5vf2Z<#!(s`#RS_LT#+hhVB1K{|rX*$f2zm$5>#vvzddP~uDkon<~NUa`o*1IqDML&fE;B&4v+o$mlohovzk7?bS{0sp5tSW~+_rHHB z{&1t_!o*7gMFyn|uABd@{O{%YzY?zk!1ex0_$ukoy$Rse$^fpn3_10M|8EOSb1PJ~ z^y)6U#2tE|?*D)NEeJ=^3awRrDge8l&>CP>hM8Q*2w(O=6%MAY8&YI7gX7mD93YiZD*i^)}0Q_A5_5vPnkc_A_)-X+Nf`AZ84oj7A zPXXsrS=8;1*LRjhp}HZDkEN>+iGAk*Pq2t3@7gH{2bHg2;3z|uaKc~<_ym)qj05b= zQfFZIQvg<~V$Csf>yo%Kzy=#y8Wt42k%~;h%7m>&=4w1J3 z_420o&wsPhK~bF_1xf|S%`&jk$8yTCkDKB6%Iq(gYXQ|0mrA(m=b{8|5UF8MIo|>Q6*_bo zgEAK4W`C}+pZ;O!w%3;)9=MnPua75}3Uk-aehba0TuamR4HpHAZj^HC4rUZ)G!%s& z0R(3V$hsw1JOK^V6IFm;Ul7KWEf@u|lNHGNd-4Em=Y>?Rqwo)a{^~Kj|8}*ERbT40 z7|>Fva1sE7?nMhX@YBRHrwu@q&w@OStDqzZaIFnjKs>{&&p|zN0C8r+Jp9VCs%4AXE<{L5})Mnz94NT=Zv@}O+EoiTq|G#a%>qz zB5Q!Z>3!mQhxxx*P6O(wcEH$|7bIm%$WADII<}NnFaTcy(Yv-oh^&0frJ=%i?`PVU z^`=|Tb@44=H>`RE9lJ|x9Mt_%Ao6BVjx4bPp9n~`hk)hp)C~8Sl-s0>wwQK*PKz(3 zqq?KBS>8%wd&ejk7kpek(S%v{jfWK+p1>2{tcf$c5#)hZ;A80*WB0Kcj457nlA~(; zYZ=y4%$?MzPIIAu^s2}eDD`M&mD@bhpR5!0DoiyF=LPS(;C&!QA*38GqLyz%Af!Of zuKF2Ng!EcAq0Wh%Rx({61X%gP9T<=7X{PG>15e-^ z3>fZ$9E}%{K^GHU+r9!qr+DxIvXYGF2jFAq3Iq73f>x2{Ct!sIA)f#Vy@l_1rYyK> zZF)VgLzqykN|E#62{`tjfVHL9>v6bAq~-%_N>Odf9x#h|b~3<*d$-hLA1n{^8<#eW zc12R#g{EM>MsmLH{1cGMkU1=UqYe1Bq$AKCzx-`dz)-~iG7I8x0zS+1F!ztz6^aXW zO?N#Q`xM65hs6Qr!EjQlxbW;2YVWqoMA*EkWNo&1FfO2_rkGF1_Q6jcdVTbieSJyt z2ZV*S-|=$svSs|lHE!@xVn&;@GskSPM3ZE3aj+OByzM~7AkxRVpjO> z+MxM^`D;G9=VPNxBGe{eTFw3{xC?k8sEMB@W))Z!q%q2^tB4)WGbkc;!5ZLnrc6j5 zO`wX0Lb9qtHzlLV3X2w$6BMZmz7&=kDi!kWiITXy2@ zxJMtA81k9uwGI-K`7!JN#7emcOb8XlIxn$AL~=^bdNIt2`dADy8(Qgiy?A!_uvV+2 zkgM}x=*tO9n;IRf!gZ#17!i@ram7zKRk*w9?`6<#6X4_*&zU6Xwn>Y@l1(>fcA^1_ zIEJw(D#o5L1TPuht}EvMj`kNS%L75z3gS|hCDpzBX2|f!_7^bcTDfACOGeRe&hRSm z7+dj<<%>Iz%8{Q<#X{w~xMb^qM>rh~0)>t`zEM)sjO$BUe-+G_UVyfm2udw_nx|V8P`dP_r(rhAJe$I!UyXX^PSg1&Ti;`>z^g-DAHcdhpM@R_dnFepx4df@iW+xX&Xk&JC!oB_C-8e48#({%>(l1HvOH*-XKAaWk`DOg@d zIY0mUhVRiMQ!31ee6+UoSj=e5m<26D1WT_{vDzhABo&Intqvg&h{ROnx`4Ib{C2FR zWYBw-kmoL;-zEs7`q>7eKDD#mm?c=f25rGYLtH)F8G2$Glq0iLMuc0}VP&a4%87Jz zEURMiA0=-H6utjqo))HMx-Uh^a@6lYila5S627zb(Pf+luq+ihhGG=dIv@YUvuqt} zyG(yKj`;%syoCy*Q#PFSKSp9ItQU%E`P};GK;;l`+3gNv2!EfMc9=&bK0+b4B^GUc z>)m#R7yEjn!HVY=<;qHaK0+RWk6=UK|6?*o@{d~z^*$fvCl~@eCC@F*m0FesmWVip zyHzim((Sd`C^}_)i_^t}`$MAu0gPdqNIaPV`6w)IB9KEr`wjjXy^rOs^M>SpmD9iK zFS&0ptDXeWt4AZF@-HE{E3)bH=m(ABT0RK%5ItjJ4NERXdn)oJ@RQ7Ea?vy(I)gY( zy{?DHaHxEh{iC8Flk`;idj(SUsFJwAu1@0#w#E;7yrUo^A#1OYYRenLw1PLo?sa+c zvSmlX-^Lw%a2YnE=Z^nuOm|av2i6%*VoP_GE^lZfZxFI1&S+xFH!#V+AF7UP!aQEt zFLwV3c|gwc#UXyoXgEOH+3d8_I9&)9pA=r4}TES?yVH*eeQ?$f!L!n6oaM_e`_@ zp$LSQCk?ezXvCQPCwP~zhBeF8yNxn>9hh)+M;hMiBfU{AbC@UIDLj3kfADBQmKVX% z(=Rv|dC2WU-$=cuy|cDi6P~bk5Lj+xbkp~$VLI0U3IFo@Wg369gY-9%jf5i#g*FEX z2kDsU3%V91Tw$X<(^uYjt?R9M{u?;ZL|LPTD`3EAO-v}H6@uh^xE!M=vsuI9;@(bv zzWlRoyTq)^Gcf;{4zGeA4ntzv3d#Vi-a;WV2>0U$kglYu!_Uf}8Db`u1a~Rl=nxht zr{$Lmp|yIzA5EOkve5ORlG4eg0`EwO%-Lz7OS1IuR~Z>F*{X zsbUWYagVw>_~8ZLO1M<%s94UMui(S0SQ(|c#Am~av^hQcb_GEL+-fr4^9q8%O`a=wDHl=P(-4%O-9)_i#ef}PTDiGzpq=3Z!8 zlv;GMiBMpCbDph5$P&v;Z6CQ)3r>1rxT}_6DSj94uwcv9Kbyh*8@Gr?O&u^3qCU~_ zVrMK=aASE&<5`e0r2=za#02;r0^`xq%4A$ppfK!sDN_%j_N z9<&i!yASk>D>VyF{8kU1BU28PFr$Emygs{DZw@&iHacyhD`CdfNu6(E!Em;|S>}`n zacJ4N0r0q+;Ka0NpaTG+NtHgNBm@h}%uv;7jATiSab;x$LV-t?*sJIMO0RRDqaIaN z1uRa{pqNVEj_;wOGs}$N{$Ts8aHec<-iouTH-wYxTI44;z|2_SedyXzpjHwlubU?kR-LauE@xx5f16Uub(_&P>#Up$ zw*y;{ZInusPRc>j1MDHLMF7fcw_9Wu&lHr_%#(-dm|#eDgaUP2 zNJfICXYyZ_%i`kDDiI}eR2$f&`n=9&C|bqz+myDEk?(jE^-M>Na^#7tH2V7d=mnaAKKO8o-B=jQgiZ4> zPP0a(Rpar)l+rLea-vioZ4y}@Y8=!Z7@(5Q5L9q6_^_{i6W}36?bo?-YrNBN-u{Np zOl#}qoaAyN(;F~aKf~w&1wBDA=2OY$k_D&A%h7)UVwN{i8bYvGq!2E-Z*JH=x+QZN z-w2^>WdX0h6Vj? zG2Sj0WtJXp_N~pu>)f$;(Wx_Dd|~1AUy4VQDVIDiH_07P`PoVG+nBahJAbAkhU@-t z&o|-G2y-Fksu=Ao^NcjPXS6bjXVoxf881stBeO;60;q&FCAfQyra>BVB3BKLhi5M4>e9; zItq7el#kzx2Wv2J#d{VXPDELK;{I2BE_+>1$Pl zdbYG#=bu%4K!u>1(dP8RON9MmE+vayd=B~Cn^?Q+Uy8Nw58F>(bRIucMF2CiKi&MV zNBA3=w6q4Gqpiy5(FM#VT7P_SulQGS|Fd)d{o7xVsed)F6dfB5Dg{Dkf5cGnnPJNs z!rOqE5-B)~q#7!Vx(M+`V3NYeV4rO@E;!RINP-R5dlLuJSNpHq`1LBNZbRZ%FTNIB z0FZhzgJ+>WnxU$`-~izOa0R(LH7b9aMc^Io-T|X^Yxli~%>@TIqk=0$=h|ib`3CWq zp8^p0$#S6E=tT!$qBMj89uaJ9{Gz9zAGq?bH;+qME%j$j8ZbKGQCPlG-JayX;KdF| z0jlE90gR2;7aaftw<;3gDN+rpoB%?daz}Urj>g*PqY>NhbxYYw0E)XmLgjZWg>R*|s^U|ZHKUb0_O zyH)+uZRtBt^26Q*qi}45J z!rY~OTGog}3c!JpTVELnJ?qe^y7TQQLY>S(S#CQZVFVHtQXS;w3wb(Q{)m_Chwmnl z>iI3t8VIFZ9_IbOpC3|>*p|UylLb#|9&Ttn56#f<-josrX5zVe1(Pm5t(Y)UYRn;N z_z1c%j3m&*88(V;L}bLJ>VtjP7KV_l4;qr<8;hTWH1?A9wNa1Q4%fhKSQGtneWj-f zM?y?*gUOURV^SBA3dBBn_3K+sQA{eaCNf8WS#u5!@I2nayYE#hh#@@*V3xHP-$5t5 z{x;a%4G5)du=4Y&0cv3ll8ic%$2GSLBQ(-M504T6St$ox9h{5s_5Y6;3jZtYX&KDf z5J!CIJ81~AWDlzO9K_bDA$pVnM6Io2){s%lpk1aL9yetec7{lGRq!g9BCwV7&eL=X zC)uk_{`{U$w`3!!qUUqr?@DfbACKk&o3LcDSwGr;_7Of z5S*%g8tUS<$#h6b=!Hc+N*@zvi=oRw)HzSMiffgnzI~oeP@0%Jtbrt)-!=he97V+N zAqs`=2e@MC&F{CO6z>}7&L>L172WXw8}{v;i_LdzLulMuaG$P0VWY^F;wbHlUpl<} zXGx0(*1#%Hw@5G!<~pYin6t=z)WIq+2G4ND008UUry8vR zcEouTRmCo`0$B&ik7Up7_Du;*JWp|M7pE$q zBvup9B;8AB9D#;?(M{Q}UroZf<5SZBUdrxsX<(-2vnYT;L|-*vCqKXkN_egHQ{E~4&VeoK3@Pk|;YIu;)mpe0+7P)Z2^T>=rjbvWhM zEs{}b2qq^hDpE%+pNiuGqdcmKqcN3UTk5i!CZG$O58n*L>jiWv?T7L?JRh?~Ra{Lx zR16y7fAn1&DS{LR0oF={CqkJ309}oF3s!vE_>T|>F5It>)G~h`7!@IXUS>}MH`%O- z2S%XSWWSv}f+ENzadHDWR~--?)$>u^?zP9szT!v)>B7Qncgf`X97dSM-RsKR8?4w9 za%?gSw|HmcML$1lWxAk}%CjacRN_q7nu_fCE(*(i@Kik?a^(&V9TwikDTmQB)y|da zzPr|>iB3;FdU!I4Y#28Wy{(U%3CRI~Pl7}TD$Jv>ZJYP~I_J86K5Uzu=j}0g;RpAU z3c~JXq}c?I>KW1PFTj14%y#wlkVUdsba+$;_5mos>4Y3;R&SK{%G5H`D0q=dVBue} zedd}bbqwnWj#Q20C+pnQ)m63aZ+haArSX2cHVw!Ac^yh2t=QAI_2k$X{fqC8Z9zYl zwbvlbs)4Y?F$n=~8+I``cE(7nVHlX4H3-`eqDy%U%R%s^P%CEP-18SPFPZknsZkV4 z*EA${%sio+;W9!ZrDNJuZiN#itREUBd&Zv)`m(Izl)(#a zeniMx@iDjis`(gfsxHvS3Q+E@gyy{<|icQk`UNzL#@``6Dw*^)8=aNUYkLTRS+& zULKKIm-Cl5EHEo2*P=a2Dv#4jKBp;5AO=*MHPV=w*<<)qR?^q(_D)HF+P@MdyLp)< za}VrggJVR*Umy10%i_|_^Df<`(lH!ARwb}6QpH_`&$A~VB-@~oLAl}+8rhH#%F26$ z&aGF8E&uZ5L3-WyMjn}lhUO-MY$I=R*4Fu*yp6r$XLw$h{;8(AW#M*J!|opnC#Gn{ z8j*btb3zz4Q8Zx;gfXksq|#Sc_$Nr^htb9FWGhh7qKk}2b-Ogk}o*I=oQaxK%|ORQntm>Rl;+V@)9BRt+#tM z6xj#@yP&{Glbjf=yvTJuMS>)sh-|pYW+Md6^Qs05uRnv?pwLP$4AF> z8Rm-jf+!Xq-bUQ0)L_FM+*dm&jrq#Y_+6F1Bkt3c2SK5~?O#%aHI~Lok1`k*qOWMT z3g0kF>w4JMT)sMGm9Y;%rYO&K;>B04_|d_oxZ_c-_TCN4>5t$|us}uxYHMrO#eHXT zEKc7mAhsUhjuWBzaOoaxOy<2#OE>6}3>Wn|ESO5-q^3r@x${dCw}>m7Rcs}WVp9j5 z&md4rx5%~nqI8?pB=V^=BThIfMpy_YLS~Swlh#r6xFa?fq^;w;YZu4z?WK2)a6#im zTcKXT;!0FR%LT>*LLsFq3=i+!<{7%oYVmk--<_@PR*XCgW1nXeOK@Y^fV+XAAlHw1GwN*t3sxZ@P`RW$4YfTryO`d~q}Pd*rF| z`!sejE|k$3aZJ0fVpiG{hIkBC`0tK`e^uL4Zrv{5lKS>XlS_%K^XsUz7vUl!t{)x_ zXZ<8>vqitqu}lfKtg-yp&P(bhU`hv{hI_ZKrMxuTzGlcnIoF=T^ZIGvMnyR32I4=& z^QP&qDzGri2^wcA79gxb#V)D#@z2p#srTHON>DQE?Y7n5u59ERf9aC{>*R9w&;J&P zSg=LKq+?wBl%<*H4^|k9G;GBJfN5jOEhXYs@Z^ld>$z(;TlZFAfxbl~sG9J3Xq(J~ zGp5sViYl@az2c$$rkaji{pMd2hV&WnP<8Om0hQZX`IK${O`OX(NhNaF%+b+V_n71v z9kqCP1hb7h6fF|oXYRoi5v?$H<_${Ih=x}j!TE`;JEMc6qgQP9Tr-z3CbAFA)-s%*Z(uU*E|5CWpen4q4;|1&5UNvcp$T(2DB%qt z7v|nCCYyh$unTV@7^GwN0|;AP>TBx?C&+8jT0Zp*%-2&CB~!TD7|$~{G?`>odgEss%TVklR-Qta22|`my-uXK*-!Q-o zxD1Ijo+_|(91~%DRTrbbH@KYBUs;;w^!aLp9Ju|yCNA4~CblM4NvvMU2Z^{lK)7Neq=~#sZymyOtS0D_O-xPZNWAsFC0^G=lD2= zGdPlg9oQ@A`mV8Iq&R*r@3AMc)XZkl;m{!7Tl}120J`4$6>g$c6!Uq-bGS{Gtk246 z(>*L(Df`VVGt%&|ltyy=WG^%75P3`ez_2WJoZb6=a{d9o8{f@3I^@+^b71#FS7uzCq%c#Cd=fSZ^)erB{&z4HosJC9?k^Zb;mIu=^YETufVOr@Vu^@4I6y z18ZHBU|S}(b!?wQ%5`^Q@wRq>PTpi3^z%sV(=LL~jajSWSH9oxqx)t$!noUNArRVI z3iVo~;(!fT#ur8OUH(okj+MRSd<(Hy16-v!1?FiFZL&H+YW>ftioG^@KR*ty{c+ik zBpBkQEK$o1b(T7ypgLHx>pLH2EL^r@TCYi8X7|$aO~q=pQnUFzRtQ~x#Xd~qXq+X z2YIreppN$k{V>oG2+DPiMl&*m*8xy~x-^s#ob1GJOIr3zj4e*Vxgt#aka5l6x%f8* z`*`%2*?39Z%TjXt9v&@4!Wze-`@wqKu$QxFKI0VG6=%6Ah`Hc8*h7HI=V&qzZ(l8% zHurcpwQ)Iq5-oiHkOdjuVPh(<1kH zDK@UmYw-G`7HdoLn(kodpwPp8KfINW`;MnWNDHg zsf0t{_&*3feCX`GJ=*a*M|QE^DwYfO8lGNEE26QLC!)|Np`|!705*2M=(_8^`Ffp` zZP<=42*2s^=fn#)lHFe)R?eWu*V242>7t3rGN*nzEzBW%kuURl-pDFSQ?Nm?U_7CR z_3k`ktNHNEi4Y02qb^s%HYy(%BTZFnC^Im0G)t^4SSKhj@XJtJvW_Hg{kfjU5$RAu z@m=_1Y17%MiAr?W8`AKnAreBmn6GJ`xEwM*+{Tw^{?Eqp$Ud2`t!aZ|=vbgu)ht~< zW22e7_D`a=t3*uJCvT9_Rm70D-rmQtgAcDPxu%3c9`~i_eTv&w|HxYb@r1MW3wmA# zvX@9dl+2fE=r4tP%|G1o>|&JK#o^$1fmV!TQ)jVmiO%>?Y*n&kg}N|Al9$AIr@gZh^nj{BWKlcuUJf z`5V&jnhM^V|K@T3KVOe%Rup?!T)L?IE^O2bwqIW_e}`}3b?(p3;6Kz@DKIFuu9h;* zi{IC?EGjp#sNAhQZ07&>k(UqzxTy_UDB7t9(knGE*(#CB$rn7l7ME?9J^!b#@PCix z@FQG}koM`R`-G(W{CpX7B2lw?B<$s+^U6))aet>W3zQxYKqiCWI`mJix4tM03w|Gj zeL&1;SQ2h@j4cZ8gmW&nZo4Z$NtPL?$b{glRUpG0*8v*J0t)hc@*84}||87q*)g+q2mG z^}lMo|5zf5kHv8kibiPT?Ehu}Cu#xt@+afJpO*h~z>D&ru8&;)^jG)$WBR!}e{VDh z!8cqC9uEG`F-~4o83* z{qD(9Lh!Gp1$1W)U|c1pqTP)K2x<8Nq~-obPy0LnOAD78Kt^WVPP-E8xeJ!RoPc8| zb0P$X*;7H}okId&U`(U#+toJC53pRXJhKHfL=ig8_H!<_wA2s-WDTX|E!~GgpB-h8 zc!FP2HsY%s292Q?QL`;loSLAVrn~S<&4vNebATGX{H11tyyU@!(3ZAWtYN;@;d*$i zP2>*RX|LcXyV!U`=hmR=+rLoX8D!{jKOBXj@ALdJWr!Kl`namPnvMv`kmja=xN<1} zjra9~)SX_&aIW6CYg~sDQl27|`MIP5FYu=twMdV#CfmhCf^1m=nQvystL^+aT;th3vw+xb|sTMWXq&MgdKvv0t($=0qDxQfk3yP6fh*q1LJ+ZfOXmn2PLi&nQ={wuE6v-02-xF;+@M`p zBigjEKVsc>LXA>j0et|CEKLx2Mnj2j3RWYah^~@5$|HNT z93+7Dd@kfs(qBx$I0fPfTJa*{HDH|_`5@zFdl^B5a32_i1VCjp3t0mcjqCdJfpl0v zoc2{a(>q@f!{o)c*~#zmBLQ44L8dx_a>#L`c4(@+X8@q)^W!&7LEgV&u?7a^uPX@a ze5$9MT&e9s6Tyd%y4SeHAwXv5i>D;CZVE~OwatYr4W`#l@Qvafp4<>S8MFwgmt2bg z6NSyt2G>Je$<_?I9l9drx5TR9i7(=J>leiiobcBP^a(wp@*47;{XH-=?d+_20I~dj zL+)qkaMZ31I?U7icPHub5281~e1BN~ppni*n+sW)C9@gf7B?orE%02}s&n$k*r+P_ zxrVk7A5br--v_T(x5E%O9VN6wHXp)^|7{7}gDiZPkv2>9!zf~8aJ(phwUeg4Y+g9k zXW+(cUNsY5>?H{5$r!JO`*78RYkP2eC!!GP7bx7Hcr^YF$r{cRW-xlMkv}sjnB(*Q z@Zm@)1NR6z?(Jm?i`#$0fRm1^lbBo^g!uCB-uP@*(C_NVn1D`3Do?C|H{4s(>$iF< zQP^-(jJ~~;EPp32&!RDiO37~<7Mrs6~*_8={|-DC#ZDi09< z`d7C^d)AK@|IPm*!-kg#G%@Q`GJ6Ohjo5|bX+)@nWcgFLaSOv;shfOYS(-KDI}y_w zuiuK-w@^ajmr~mJ$3iU}12`sbylB*aE{S+QNyVF?JI2+3rfm?R9&$DnLujZa9_bat zOzH+62Cg6^b|FlZ(14;IdT!C_R9z&6#7`2+5`H$1N!{N^q;Oy%6 zVt!_x7CA22V6!Vna#0EM|H+7SNv65$#2h_vUI`@eWhT5u*E|9eo0mtVU(EY{|OD7k7jG_ zSP|6nc{dA(NCq7Q6VwL@#O`hnA|A=e3fZiNnV&#Sc zrF9lp@Wicg+k+X&gVwFSHEr9SH&>`O?jHIHD6n@0CTVZYHDEjH67CT?217+&!HPe^ z(uP}W381Iai>^?sv%!jCIKimnVUt0`vSxG$D9k`^EGs-gJ-*OQip6X~d)ri45|l8M zpKR?o4vIUZ=Y$DWw70NpR`q9);;o@2*Iq}C_07>AT&js;k%oH+w`S^|C zHj^}k&-|uFiG~0Y+Y3m;N!dqiNXbPik+()7OTZPUzd(;m)X=65EqZ?r#6nZc)+rBhmyH zv12?jE)D8EvgkBubLkT@ClRh#-Z$oGG5)QRlNCP|R(T;8$}5{?O5Wo##oy?}{RJiO z)J>Sh*qCH1yU!5A_#nDd`abWxv~IFF>+$3f0;jB5$!23=J-J-Y&gP2))_xdt6MJPS z7PB}_{gBLKJWKwi=M5|k)doMUamlc?EUJF(w^=Ccd#?snnA~^-x%*mK>{FrM`DSAL z7MtO^d!J##7&ZERXKaPubDYfTMpD8ZRha73ob*l;&ubOJv)gKCK zFlLVD=w2?QR=lk}vhrFUz=snv6EfOu&CIr?yRDd02lwyr`9Gr=QZQDaxvgVF8G1^W zbtLhrWiO2%Ar*u-P;LitUD8p3&Af7z_IBX5!>`qRe&Y~w4(Mm>&K-VXY|EB5^vwTK(P*WWQz zv0e`6%*qlHr!dc2i;1)O-`MdWNl42@jO#q_H)C8mrVL)(m9w?y zw8})f7M3AHAztpP;0lKZe=hFrqRq?XKC*H719WfVzIV3_iwhS}ttLTcePlmbv@T%o4Wp#?&}Q@*DJB6 zz7uN{U$XDmigc*H&}?%3cR32NVcrd1A@*${V`Rjs+s@HynqT;)!uL(hEME1fA@XZ}$nS1y;=CoEVp(lQ+jo4S=i-@HWyK`@);twzTDQcY4fT9p5R zku(0l$bno#B)&HaBbT*zydKAQU9V{VBb}bw^V`3qV!N`hE_Y-+;x;j=oDiprs!a0~ zDm2S^lB6(98PQc|rXirP-BilXRIQGXedx-AL>*ksbD)$4G5&A|y+?iLoJsbMg7 zj$YJpu*u(yz&OP+!mh}F+3$SS_C&>36mbzb@p$2Rq4dN)%QXo^>5hCtB(^Dkq}JQ29FFaLv z%df7u;~AX7#E2hq@={b6`u+QV$l}HmLOfO5CXwqh4JHBhS{H>`BOZk+3L4~@1)jiz zO!pG4FUH9c#_lLQB}&{yd~)8$0zeike5IctPcGo3mY(tIp?t@r#QQyF8$IC;@0 ze(QNzM#cr>cK`aj9oHH8NZ8^AAcg4BosxpS8`wI%A7u>hp=`A$K7X7isNu_!|5 zi!dGqAMf-ny{)zr4CU$3bQE5t!oQET1Kv$B51pA%mGM}lLo2&B;Vk5NFFQ?W33 zb|N|y*i}TGA$hV`=1RruFKB6w;)?bYk6vH%i4Sg0$;l5nl#@Xs{6ih+3@McK^N8nKsU@cr zwsrWhZ80;SQT;uOcN!A@ppXGMsG0-D_a`LT<4K-fIi?w~ZTklt=J`xQ7Xjcfr`q(y zyNTCM`7s1dqh~5iXxqI9KYl9EwEf?#q7^ArSQf@>Ii2$Ur5qmr zooV??@kvege`j}RX~y*XU-YByd?aD-dGp-Ozx_b@sdeMZ%BBVOf$|XJzkLj(y-7Xp zf+l~G5Z2u3)Y7QmuZ;hc#iRyUA#riQ*LTzCFEr})`ye_!@-%e&+wj`!$?yaJ|DW(r zfMFi~C;h(wr;3l~iGBqb7JTyZ`m5yY%7FwJ<~5sJ{x5+GRHg3HQ|oiXBj0x`>6{u+ z@Am1b1O9@@QYzKpw94Nt`tLx>!v)dp3<3X#J%=K5bvjo9U-%}U_{^eURcPqkCMqrzx|9W1hKn=at*-iP^qm(Ct zqhw|e|BEm!g~bh{;)a}q|G=C7QgaW#m_Q61CDSE+)xWGDA}bNNH{$tAPW<&Kqv0s2 zPCa{0`U9v^L@|p77%#R!G{|jn3)!Uu$M@RJzWANMiPvD5D}RUE?DU^28M*xO|2X|4 zo|O$pGB1OYlm+bJ1;m~{m*Uc?6hiB1WdJVb4@lGw60oN=pbM}Y5>D_?;`MZ`rUK1F zL=UjI;sLs34&>N8)!!1_$^pFW;?@>;o|O;__bzNfldoOr`0*<+0iuffxIP$=L1@Cf zSUjMOZX<+vVw1=`R>0~uMNr__~2gH;Xgw3+=Vr$2k4O`_$zE* zP@Kqo5hRt*6_n~+P@pAXmWuows9rAz+yCFFUe{-FivaBjuJk5p_|HmEKZi&zMg|fj z0m7qf3sGG8n_^Y@ltAQ&CW^K+Mm$@?a5TTvNlTxX@N;kC2)mGRdcRT(fP$6a2AyOM znl|UJLpa{|4=^Y#dKzd+0f=zO29TQbCtRiM`Nm_^)iAz=9~HI@fe*Qet#*_j#vSD1 z34P}M75@M`R~4ijN^_D}t~vGrX4Sp|_;V2H_EFR>$^dnp;#lV_EX#+2 zz!kkoL9&tiVCa!|YU1()IlHVMG9E}wg;uC?Z}olRD5BfabdnjlGn_XsE8Sf~G=HSF zarzE&cMPNvmGo9los@kEnnM|U9pAP`zY;cJ=-X5kWX{E(I!W~dk_PyaVQ{^)t#P$c6`@X{|_f%+ovogA+^z59fJ6+9nbhcp3_XlLecy9rZo`s zECgjoCbpUA`*%AV$U&G**7}J+T!EHn$q4%pk%`N-0GZS!@N+_Ja`K&&E7qvT?`~p1 zn%WrLZyNmVyfUO`5s-qqG1Sik9F?>)RXmmN1mvGY7e_5i zE{Cmz>k;M6YCjsi0~BPIwz^1mt@U2>ul#l|pi`Ys5dSoS0ggr8`dpZU$QmTjBo`>7 zcRfdsBbxQ*BaoLO7v4etp>;?y{H-lgTrEAVzjLUxtDJH@cZrDM{-b_xtoCo}kWpxz z5yskfm#+8?44zVnG-PsihFNg$JBMMxYuN-n75slie4kFj;?d9$-_Q{q^FO9MFzLVh zG*G~~_X)%pECekF$GQPko`aGnXL$YIZVmt9!h=7c@21~E>hT0>xq&jy)`^1J)FV7F z7c5|GiI(~<@n?oNGLCodge*_g5;MPfcf2G0UDfO9@Aej+7A)_IlYbZvEVa8{y?S+2 z{DEbWrO|Ba z_o&Lff5HThl@TO<%+izRAYS&E0L>|!*+tO5#dV9a&c)k^mMTuhOJ-g>tWi3cy1Ogt z4Gx~h(S)IEe>k6zn@=xY+X&UGUG{=>uzL1P43VW=xiT)dEU}$_Ix3L6QA>sUtQxC? zX`2t+8k*SArxd2C_=`AX@Ft@YE%=y32H+)0*AkO?3rjnKV#e;K7-JDHT`MWG@X6^> zu$U4Ox+22L8|VOKxYu27P&B_`py3FiQK9@_%w|z$2ic`O=v`L09|lkU;O!%)Vr2@r z)YL9CaOr{mHa!02KAcZXA~!VqVt$M`(oPvm8?zu&kZQ|8N#KKI%JuLzikY&~e83w| zjzA0;knkYhgRP217K>*Z#iek!(4_an`9h}T7%!QdC-}V7Z?e_}tTNw4yBrjqaQ+*50$#HeWyQNo8 z6d=*c9NX}tg8}CUf>~QAt|tCyraKkQ1$xpy*pr5RBRT-%DRn_QcY>Pbm zL}l8HlE8e7gLhslPsm&NmSEA=UEeq99E7=XbWYSPYC~g&@bM`)yUI=%XUh7V8 zcvWJ$ot*Jqwd}gfm)HwoJ7IIJTNK27FTPDbSSLSxh|Vzj7ck88QjvS3M#%H3}b9PRGY{Tt&xiuH9d0KE~D)sw9aFCDbH~kqa{h*w|C#xF;qn zkWq#x|GrZgMIu&2xLfAvubU}n-}|tVft1|U1L@cDT~~G$o0u$^haQlt#&Imyd-)EA zh$vml6vS#TFrkn3QQQ&8LA{I7kJ35LFYb*}^*en1+`gG(C+ily6H=~}cRHKvg;(n_^IlxnD!QV*GRpmR8H@}@ zO|_GR++M$@adRQibg*j&oGf*{1YGPIY`jw~L8>PF4%GY4smU6sH3ewjI96rU&`(|B zHG35EGvV$&0Q#SMg(>(a1kCUEMz#+)*NSJZi&G7C;o;#KH=e!9_4DR5>SLs-Ds}Qy zNLX9tdzakmjPJNV)}npBdHLSh=g(dsU&7F6$He#+S>ye#rsW;5!02O?;@_zyb*r$_ z@bT-xbiB62p|q&JleWf|OdFEW{(Q84)*zh4?wp+N*5hb>&|G~H&2{G1<1C@#;qWch zU9Dgv5~VGRPt28_RliQV#gFyM!>&*^_As|h%nMeG{C*FM7Z25tv%RX)u!#=k$5R;S zjyP%B6@Ag8rZSc(qbG0;V9+2mqiVXr_q?S|CA&2xAW@r-8=cI)MSqRQyxaUcUU9J* zrHK{GT4b-0va()ruvDC}mGgN%`c_O9GdVv|V65-X_!EH_6s@=}67LDLiJ2Z^6qH|{ z>Q_`nk1Su{FAuik^-95t4hehIA!@cMoPSbw({l5Y#GLSM`+bQwjvXJHWRmxYDUlA? z_GQZc8_c{qt&v%(N$oOxM6HbGFAODZujaS|sz8-*pIA0?L;oUQ7lXNLFR6BP2-(yz z--JP~Z)uF52uvB-uV|cLo_CV~XH!p>>by;kX?PWXt7->jm)X;PA{=+VRkl$ zc3Mkam1;f%y^rjFZ?t9&vAaX9h}Wf)%;dY6X=1AlhAlVnV~4ygD3ZM&bhHq>wE?qF z)H{9O&{c=sOCRDt=vnco){v-f=3Zdw(clWHKgQnUE7lTyp%cyC6JdBPc7p>Qxbh^h z+e@x?e?fGvL~Wq8iZNCB_?77C^P(?*9R6W7mhSTuF%R1n_iNP-W}O+xACkx06>}AD zUVC3}swa6OHhkki??bfQD|8v}-4=VK z5GOo{^Sdc);>!4nP`9GlG0QGk@$Q3F1HPYx*Gd`<8$wm4no8uX(DkY}&-c(VM!KM^ zsWF9q2m9HJp%>c1%khUL!6_Hfc8&L%$2UQOTMWF6trg~*iS}Ycyw}`lsqhXOOIeHY z+)A=v+DtO6t->St+(QhL5!xCZs?97RmPlEcRHT5a(UI+pq$+d>7N zjc=dGJ^6)%=Di4dlb7X`QSrRLmVCq3y$d|$cRus?M`c}q8)|vmeI&a+J&@SFq%z69=bZ=MQxQPy9 zHu^kfp=-+eY$#n~t<72NMQf+*Pl$=5Qh7~5=XolVh`Vv8Jl@rAVa%^uFC@h_*Mw#6 zk}H)cb?Wsj?dR5?7d*TVz1g7Lx}lj-Cx+S;aTRbg*6SHNTN4`Mm^pdE!s3mVTO7rv z&thThOeqG-{vvET8s#}XXZ(~AT}(sJds@74*U+}mZDWKp(pM<1QR<`5G; zfiE_mX!pTpGJwf+ruAc}&Y(+V3A@LpVVoP28~O;EE-}%v`h7yCYu1qd)!<8E-|Hv9r!i8Qxc}%>K@$=;|(#^I)A^4KjD5t?`O`=0MlH<#;n-+o9rIlQ}e$%itpBUKzdhC-5^k2@1Zfl5bL8gM3Le+|)Bw{SpzZ8M)P?cbRVQROQL(7M(ZrQK@3PmKvDBC;@F z8?rDXLW^anwNW* zV~$;I0&Mw&f#8QIQ^F_I=?zS*^f|^X=i5n`Xv;Nr>}r%Jm2hu1?A#pg8@q`0Ar7eJ z$yTf>rO}54&kg-jVr{34bWRJ$awqmPKI*Ts{V=h6)Xj=w#d`hg@OnAXfX&3X6K;(Z6xn74*Q;Sy5&vDYGtNPV_k(y!iztK|7v`xKzW}DIDR#rL5aQlOX zP*wAR3XN$KKKA$5rOE}#V>oP!KBMz1@%Ues-u2twu~v~ODs3|A`nlYl26otUDG*8K z6r9Mnfo%UR*!lSF|A*8BP@X_UO{f5Ka(rihiPIzdh@(TFt>zb6rXxR04#yucKOZ-E zQ!!>rNs!6BK2J8+USV?R_nYD0^}zc$C5a4)%CEHLndkQlxqrs-pNl=JbS;CMpDFxt z`OAHrSNMdIe+u1yQzNB(h^^K_hM8m0wo)bn7J-7+KU?-T7c58rn!QRBE_KY7vh4P< z6cw#jeNqg^d~_}=TP$B+9Vycf91j#I;8T@-1$5VyFQ;bW`N^)bJ3K1l{D$HfR}jU<#p4#~H~`7e5Rs z{}(WM_$~5Pk5oH?*mMTUtS8rAI|Q7Y?|)X}@EW(Z0jEBiu80B6<}no@xU;(0_vPwe z&%h*+)mpCIRq2PgibfxPDBJ&8#s4LUx~gI=rZFTo_J5fg8YE_Aiq5Wex0aIcD$D9t)HH?Z|F65I%j5+lq1t+ZdVeej+pPKKU^C9T63)LJ&f^650W{s8j_7}14n--MqO-=s z4}b68{%uhsBB(0-Kthz)Urq^qfG+c6hN1hsi^_jfoY`5sZx6mQZsh)ah=cz7J@;!} zJbsAdy4aho!giLz z(eZ5+lA8z+H+FHP{n8~QTXYbz4(eeo)dTcSifHlEZdcqt!?X?(h`D@QCzxNl2101m zz$&C_2)%ocY`!-k9jOVJR1~89#=Cu3n7p=TkR*;n@OviC%^*T|(DCa*eVO_um!*yNqfN~2n1dm0j$@9CgI(n*}MYfQr>hG;(c*(N)dS`$w6)D4IY6o zq#M>hz7#mTjA8ifJ{i$dLery>Mki>emuz%ZJas|xU`In3#Dh=vikTQbWyo&huLDYV z*uix9&y=AOi2w$f;2;Rj5aTLt=R4Z*4xnUg%v%VxD_>~k<%|Nr6ddmtgTN%to`gY~ zkmO?SjzgQsJRNuN{ntVA1Z^i|^YV5IPs;k(o2%&?Ivk!T9hS!o`e!wpZKT}jf|+s% zhMW+|Hf0g^Gb;6<y4=DP6}RcK0BHk+@p>jG0t?&0KdkR;L$YGV*jGv+5F6zOq?npqpg#}{hx*#J9nQ&F!NHq>uOLrYzIF#9 zQXXzzq3VJx4k{StlkC0#Tp!&%hq|tAh4lW+3Wx`f~eKuEIERS{2oFR^L{~~ z-Up{1L7l4(+6{1ADW#>-oPQVl*i=u)9YgL9_`g$fA)~lZpQd(ID40+Xky(m{Mcd4+@I} z2Ah}gF&95Wi|D{6O1%Qc$w+_yQdti{F;t%Gv#8zmIpU{M6udWUs)uv&tRg|`lk~y7 z)FBWDum6hp+k2;3J5{ibHdzUFe}Dd>NV$SkKv1+gej7?X)0|mY?9?0G;0PrvBA$=~ zCDQVvv%{yuhqw*0t~m4Xdn5HnkSR8*a9@uD`Hut9Vi6NzG4_0H9FOE=jmxryK^UG8 zl0f3s30OXq=ZR4lSgw&2T_Iv5%KR&mejsoWQGt1Zaco-S`=pxJfoOfY*KdD(1=AOrXnA?ieCQ5x0(}f7 zxuybwV!7*KZVK@|>bk|pf%vCGxhJ3v6*dt!U-dOcCF527=OBgCO+t|U8;rn@_V@Y6 zaP;5QkgW0KH|h(}wu`E26fK{BGj`Wq9Iz}mlA`R+gRS6s4{!)fxTEjifNYCb$VeY1 zlFSDZyaweb+69)Vwn&PJGZS3UKQw9rh<+k1CiaKT*kV( zFpG66(a=-YtQ$Rp(2~2_UeS=n+#P_#i}!`<;6DlMvHEz|?CAPDxhV$KahXr9XINE& z_@k>$bmucShpodhFiHCH6OIV`YX{hboaDab*Wz-Eg7GhH(qusTuf=JzC}V@{nydzF zrk1<1!VGs?;!G=*+AkJ$;P6qS6hdh_?;QCfgKS|(%!%za3y7_v_vEm3- z32d4v9&ib3kOQBdX&f4WG+&x=W$+UqGdZJGL6^P|93_vG9oZXe{j&+~+SV|$sKnIh zm`^9q(<5z}1hfZOIzj;5KJOrdV`5_wi0O?(x3+IU4;b|_0`o3orF%e@SdHXIldw4} zO(qwQBbwtoJS5nOVr$IpGmwJ4vzMQ=Nx6yLmYT&aj@0QE4z6Om!UVLNqP7?w%JN$@ zxb#a!;=SLa1X$kG!%wFE;3rSOcl?SL>@lBw%|)ZQ`P|-(INSGSjGGH=;Q_cBxK<{R z{~mcj^Wbdzownbl!{as7%~Y#(RY7ECRn4Yzt(&W*pjFpb8woACso`9o9n)t%@+$n} zm@>Iy=8?Pc^3~D$O4h<>`DLH!?ZT_LUPoK)N}(5@L{${Tct=7_4HYmHu6{w-X948EG??4@>J=uJ&j&>-4d>0C;BFY#Jzl= zAJ087%|D}P!Mw=vX`1c$)Vub5Mhu}W2RmDi21c%}R)kI7Fk2YB&yz_p38``0aK}~_ z?xk#!-)nuU+Po%05wnoHhs_Xw*RBTI9-_XogDWB{O7^RU+wJ9H87npSRC^Z;;>;|AgcFgO;yErvEpu-&25h!6J{LYLm;nIo^siJULgl3GFQkRJXw@RqCh9b3 zmrhoGc)&Hj9&so^YNKX{OXsWOlNrvSpEnngeU95KVOf1Yl(l~Y;!a z`ac^;e{2Kgv4j2F_SQVKH-xZxP28pFvztSi(n!=uCxJnyz`Mi;++Z%1(cKg*r&v4* z)B6l>6XfK$3f0H@v^8k>X|%5u?XU7TTHo+&Bxku6G4w^pq(g%5fL96K!fyE_F8MYC zX9u75-Hs0gC@oC-76+l8e$T_Q+^d@|`_P81UYFUVpPIuqA;{zm>u@y`Ip4a+NvXkY z$g7yR1G55iP2(-Tw`^aqWcoo-AB`DWuQ!n6SRH5+ z8H+9uD&^ct^0DzM^@0i2$^)@{R3F@9YmFMx_SXht%04=CPc_Z_kw?CzAK9IQTgz79 z@_IiMMgvyYcDs?&d~T!bt7R`|h1O9Wj-t0=Q%-soCnD;zE)s93zRk=HJ;j#tDw_3c z*qEFJbMxcup2{IbPeN`TwOri~`a`}xzcs8=NBNnrNx#I8l5<7BS1zM2&P4j<0>!}W zYR7b$O)==k2s1p7C>kR%yAV?v()v3oSk~YmY72*vw4(_}g|!<7S`iMYnXTAiY07(` ze{ck_^2fy3@F7XT#*G9s4I~Y+=AVbgs9%3}RvCLD9A`uvcP;lZT>%4?8A(o!cjLM; z%O^Waw}et+^mTn6^bHGjs$H|iCnOk@cG>C4wR|?P&F>?*WV*sUi`#4mVfa ztT%(r!cqMG2MTETUqQ6=Uht&m)hqj`c>np0fo z#Ifl81O^LSS;~{9Uuy+hNn~C)rJ9NM?!%)4N6Z?1T%K*}69N=`Zm9dpYZ9MeFBXly z=9U&YX%K7GK$696_$=b2vC=ftfZ|PGMlO#Dm-`L(a+8^KwKv%Z>ULA(l!;-a3C5H~ zyU4(iYb#pIU2m1dmh{p~7T#CJZI#BAn*B7=)Wz6Rg5)O&sHI<+=$Xh{bd1|UR`4Tda z)NurC?*ioYu^M=FY#w9bZ1v-mg!``!H-e^i<(dQ zK1+$AHM4gs2yY!`Of?AaY)gB;_0J9y-04<|2iv7t#8LS?SnXp)@(dG`U{J*HKNr)C za<-IJ^c9yW+nD>ncA8d|S|V59D6L&VOxC7c5dYiyYm{A z8(}j~3Br(%+Ula8!ffq&JTbQgTWK4`Sv9;Dm*kQ&1iX4W?YH3O*#Qa$6^^Z`--Ce%<46o?#FZ0sNAIUB3WFFD= z*4TD%`c611CKZa?XBRK6-DU6fdpG{=(UFhhv%~^~#9vhn*La%*T04UTi8rmd&fVnQ zOV}O(d?<4|gKrkZ zGmZ`8oO^g~uFr<@(a8&xhZh!%4(4xGc)832N_BHn^XNz)I^>VhdHC`o*sptE%uavI znyIp}EMpPjjfedPW`^DbyfuXSLsILnPH1c`jbzr=Tke`~3Lc+of&GYj=E=+~AGw$0 z+3l-*R4J0T_JxO81EYM}dHGM2tC)S5L*?p5^{b;&bVMJZ7rjNa0<^r@;{Uj*|w#=_QfozJ5KHo#8p}b>*`+ zVWSh%sZ1%fG5-A54eRQQwKvjL&9q15UoF+Ep*(Ce_wicMh}PZPzG8Lf^6j za>0yx`q)as+2nR2GN(%|7B9s=-N;_a5?r>4q-b5iKa=MU|KgTv3{KaLj&$|u z)pLJS^$Ig&)2L#lZmbuIimNO8{#~qgC0gbf%UG+waqg!G_lJ&Icn0m7qe}Um{CTC} znax(Yzre5A1{j})qt&%W-Cwj@y}f`R`#;C=?R#Y}5*412=TC`|911%2hWszvjFz3H z@B18NS1_XV4$QUOZD@X%O=V3wSuF%`4a`B|cXC37`CN81R)0@L9{I z5RM{>6q~KTIBWR#oXO6S;&Fs{oAA4{jY4}Ul)X|U%sCc4u_YhH<+oSQak11n~ zZTVS_;ZnXwcNkqC_TvA`9#=~F2bnboRqY$NTDQcjw_09F`dh|4y}zbHwtW)!Me+U_ z4~{>!`D-9#rHBi|gGYyshm>FWk8Ab&gJZ4+W7Hd0mDc1|+3P&zC{{fxkl5m)Ru27T zFIBRAF)M2sItnZ}?YM_~lNw_F9L^u7H~T)OTVd>Cb-)pc+dt8na-&6S!k(nWb8xK%wPwTo8LU%4eanxW=BBP}L)%`!MLX_NL2Q${G zmO=aqMhasNfT>P`T`?9CmaGrn5lt|apaQfvYv9}i$eNvm83-rXAd9eNdYFS-u2kyF z8L$E%LbPNnnz1*xkT<00c#eE<5Tc5CLj%l<#e2&&CQonN25aNGhZ^>ad%)#RRe1v@ zapLhFd~-8oexE?N?J!5XxBanRCi7wrKN?(NHr+|+0U<<9;3h$gNSvT9gR0OebjtBD zuzJn#9w!?^!s8}kKTj=oJu{2_3UKXlaF{UE2hpwxq-QcjVRkHd1`u%BL$FnT)yyLC zomrp50FhauQKS1(Q-eQxURe;myeJo_T4s`i# z2!>cQHqAXF13l6tsvoocb5LqHuQ*gj8T~f+p*T!K(MO!6yf#o3Pm!+LbiPcxdqqu% zn3M_2&4Jtda7~y2!j1{3vsU_-r=S|D?eg{_!SCM`WT=g^lmJ6K3GpB&7rz1<+5|Kl z72=h^3oC|TX`dYo{?DC9nyvy|jBCk=Tps-x)5(baf&*m4o3%$XOoEJn184k3y7T9; zM^{DXvTvDR0D(CXyg)7`gE2xAoN&6ayKAPE=5-Jb){i8JIl$O}ybPjeGeiW^Yzh!( zK0Qjwmx(~ZJ+XIhfz541`_c=1@2Eo4`xH%l) zjuay{AX{)@PnW{Q%2)!Iw{r;?Y+RHzB}V)MEZ1NU+UBzW<8Owm`VD(rypOSr`UQ|M zm)}8T9j(zvvUJg7jZeYO!hXtYC+F(UBxTL8r8yEDzw)?6`tvagH1DNOMXt>!Z=C<} zDA!|&g;{+uaIvgC8_gpIiDpON zCvZcKm<}XZXVv_YJ3VwBU`r7QJ9oF=O7JDHsbr6&;Cz(@ggQg(-c;%I-Oy6v0QPkwu}4IB+ld8veLjgNP}optM9MmC`|+f4 z28j52G?2FSJrMSn#ND9=gdak=BMNbCpjSU3K!U#p4%UMX=5F!3bmkyCeRCy9r5OWs zw*D#46vuI-xB<@)8g{m}WTrV{i_evVn%I{7?u(;B(Tb|klaB!74Mb$y$OK)=*7nhq z7Lc5YB9uy35Y76|DRTl^4up&61*tL^?oC0*u+woVE;Z^<6zI=BWA98FF^iP5g3Msn zJnhl0q8L5y)QnqDKS-6#pw^*H{ST%Y9Z|Do>B#RJqnju1DjC%v)Um&-&K-Ge;lPJM z=&w|%tk%NOT$AjX5eIRkywMu#tFG4bg1kl<^TXNEs6%Yw@vDCPPRJjY2if1>1jozI zzb%of2uz#Y5N3HOK6Fd1YdGG%cV%W&z)Z8l{Wr7Rr&aJ9SavR=t|TLVW#hoQwfkkB zOo(j%;{U+P7sSumN`xL&=a0L2a~z2hpI+6lxt5!%$u&?@5VxiDea_W)9D`EH&C~7_ zs-HVm_E1^C}Y`0hf5)?Nt5CX`qd2rD7s zRM9nzVgzZG_}sQ-aVMo)rRNKjn1Yco?hOrebs?+t3jdXm_NLdT-3Zgs*RJ{>A;rY3 zLn;Vw;4JG%nz5Vbm-F1$6KKRBR+vVqs|PGRvy3)dM>6Wl@HhA#zj)0{fu(-Vs=gL4 zpt}jlnof1^7-})jYmuS?Zq|syB9_9c{AjIvX@QO8x)5aVDK^J{txr~Evlryi2UC)D zC#VZ4$O~f`<4AYQmQ!OP+VLWhL^(&T2!=s+cP26UW2bD%P}A-bvQ(ky>Bw%Lo-T2f zc+UI4tF9cFIW$qobzUQ~EaxB7q%Q^Y|B+;=4r_A8p^E-^uS}_)AIox>qhyO=_`*#f znv)^r4yl9p^`Fb_0$(jPCaD>nWJDHQtx&s>a8H5qn(+s8LrR~>59R~ubZ%hA`hm?L zB0eaNMwORP!~77XI}{AL2dBR}p5Ekdj0z2HRSF*95l>cK{l;mYmn@R_F-dqq-`9t- zkj#(7ibl+jla;@T$L*2Kyy5JgnlY%-h1s)pXGZvrlFXd4oGs9`=C|MO7ULDydl(`r z!(nsFU#XQTfc=QTBl0{b6XcryB9ZyJ_4SD4qjf)lJos4f%nP*lpdKGv{jMGdx~!5c zZ$p>KlyV@=%oK`&YNp2NO0JEh@fy?-M{T&&%1>1IlUo`m`*)x$W$v)q6JBl$>tk`{ zj|e9EM1pke6b(^Hr;AXHc*)7U=_Lu#>x^3E18B94--n;h^OI;we!P2Jk?OKqA}foU z_g(hsHbISk!%fLAv)>nd$ULr4>CnYEKUum~+H*oP0gKxk-rTRG9rKKlH z8)YT9#E%vIfAYryVkmCraH8f|)$8jn$8pZb;)UU;R-SBSv9dfa$bDw1@2XISVZO7T z-!FCemMr?^l#g^H!*hMoqe+J#Y*MfN+NH` z5QyXeCdY)-h`*1PSF^|aMjQ8RGMuR(A7*3D`qltFvwZPm5YID(>7=fSnuJw2Ay0Fw|pvgfQuk41DV8t?mTR>7Gc69-;d44OISjA5L+JnkcCJV%07@>?? zll(3(wo3-b_p2IVa2>U&YM@s%wK&9^@_N5O` zY(`xuu@`S+B?ojIRNi6s^TUNkJZV|N#kO122Gi+PT?kKX#nQz^h{DGW8b5V>!y01e z9P2bUPJBFjvM@~WdcWt}pVl!a{a1x>G!~z4TvNOm+!k%C63U|$WTl}@+SvNKDTKL# zD6*Ur+^1ArK0}l;JmD?$21Fl{1m=gBKQr$kL#H_@IhHduT3rD%rDufx6 zW07SLzf94nTM<>WZfQHpSeb0b%1iJOpRlbqL=dm_H8jKxxVKQ+8@SL^vf1+(jR%aV zb*#YX674pIyqA-)WGatfqq6GivB;Zz22-u!=E&;xbB>adJ=>DfISjLu)M-N6{F|fkdP7>Sg>DB3;xjXrNwHPPW6 zk^_@4-Y{@3Zbh7n)iQb^2lgRMe4mdPcghjmvc2CZNubu+k(qZkWo<>!vGW061@AFF ztU*p*3J>zc5}(jR7V63V@%{8IqKPO;b@}e|{9=SbPv+_r~Z z71(7g*>wYqxe4-7A}PU=7gHVdS}smRTK6h)h?$d(TSoB^!qMLYVHKMMhFW^h*=GI#Uxqs!o=W zg?e`0^5MPBm(Ta;6h)Rs?X04OTh=e3gPpNqG~7n+o96p4_t^lm#5pPT*LiG3H|6Ye zZ2P6u@9CZh_@FT}puQT*6;jIBCv9`?q_Itu@6_Sjwv58Uo%>V9jLp-wz zbs@rd8?Wd7$Vq_UxQjtw^8|m1y|nHFhK(|-d%RttI=7eElTuz8&gRjTzpgo|K$^>v zF!70kj>px!J##O)XQWj=m`kX-kvuaKI<~#` zAu5mSRorlZrFw{j$+lceV*(%VE%o!32chrchn>8MqA+!=s!ut~wfMjyX+@gFfFT>9fy?uB|LUJZ&oMaQm1CTtR>%7JSM60FZ~(c`^RKMPPKg-X3rB1PN0 z<(1^|VV8ZgmIIbMEB{#TBw%Li^zqsxR_1FLR6DfQD9pdSXWe(-X7Y}W;5z-8wfrjT zifK8Jd6V$CoviXc|$z18ITu`v0^h=2d_ zi$-=%eekPoWf8Zzz8=3Albm0-_rFiUfpPa*kj8ts1!kZc+Zn}fhmrpn(`=f3E5 ziT~OB&#$Yb0F_Tzelhf4yZ>XvzikHh|KdFpiw3L=C2WhLv;Q@)zq;mQI4v+UwgiQ^c9TA zUl1uJiZm`b%G|_rZ$%hbL?b~uJ82x=<6Zc#Gv$VZ=4hv)TmBTPmKRuVIeS8I*7Is= zi*5IFmS63bfz$*MO_aeKCS+y^_5^{buHW<{glWA2eNT1n`_E?N5kU4Kn1N!L7)c#r~Yup=JWh}unD4Mju@2OxfL`+2y|5V%_nX!``5);q_(h=ncP<#ui*uYy-X>oK~{ybYm`&x0M67$7f#MFj& zmCz<-w-3jJ994DHF>RoJ*jK{qF|eOPsW+4Dk!6|(#L_i`v_^&)usVVNW+1}Vro-Fa z494|=5lKi#z0YoS5kn|psDVUo5}!@iH6#1lMnpw})I_Ex01984L%YQe5^;|x#zy?i z6d>B|pYNJ^{qt7$kBM%GW{}T4TnpZ(w-KLN*Z`Z(nFElI%s@!oCnJod)0A9kd2Kan zdl#4I$@1yfAnzjT@VSkpC}a+ z9R!SO0YN^$&&O206Hz>5KG&Di?<5H@2I!&)bSuc5vi}x@CG*yI0IN|T%OYJegtvXd zhRs!}jo`^O5Mp-*L{Z>)Q4hQMy8=N4JXHd*YJ6kdLKCmi@kHr4%UX!cz(N2!nA%09 zyH$O+;sK^iIa-GTC?hiHO_2*v0o~>~-hi0odIJu}=M{~kRXPa(ari|iF7lFpp|&*8TLKreH5 zCkT)NCNdQHmXBg@rv@PMC!CwbW$67bxOn-}WQ!OkzX&2uZqcscH@@(>Xb~Yq8Sxb^ zLu{mxUh>%HG=xLrcrTs&azdhezlj`fzcnhyw}5DxpFjET5qwZ0%(rwCDDaSX7s|<3 zbv}B*LfHD}8&_P!AHIhPhL_U}dR-$&BF}JDogETZcS^*r)}tv0)cac*!H(yoB`^7! z|9H)RakbL0Q2=Xidan)4GCq~hgRKi|2DzHF#=Huj+dOyPfW?W+#$o+Dm)toLLq{;T zjU3&F8x?y5YXz7Kjp<<*-(M^(L*JbsW^^@!sO?vRuNO9Q9c{B7L|~cjd=p9Ga*Z;$ zYDvMEiC9j{egL_dn?jMq$|wwS__(gfW5k38O?b}b1W$d2%a0ozTuwOO*$$$*5)w24 z2D#~YxQO$F2e?<5dv1X%%?TvQvB6#%(At;X+;vD2gT_`;! zR}Tbyo7Z89`Y!U9C^ou;dgX2RpL8wc-V)2Qy-(UP`b#LQ(f}_LQ6Sw5`b+Nb^*9Iw_;3*!Jvo?VQRr^!bB$$;JL3AvSsEc z+`h#WPV|enZH`2ETh>?57)9LG&PT$h&DYkh$;=9@t<7r836&DiCntg0k6iM6UYYwH zwzY^tU|TBUSa^JinYX@&Vh9c5e^Z0Y5K@$a%de)_$}I58@{_ZpH(hwE{G}2$g?HrF zyYnlm%}pMf>z3mYGV);|4i6#@P&;!@%q-(QUyjj%r@qj>{(hW*JUTfFV#YWJ3a2)?RT)2RTDlB7B@W8 zLhpCg+Jt9LSR|t^v^HMPVL#~;6&C6IYq^#Yb*Nwj39`$>MhXIGV_iqa*1E|0GqJ|K z^QAkhw02k9&hDZTt(qttyYj_kFbpa8l~}Njviys@J@F`ESMdWIb@D?c3q6BC+RIaP zxCR<`Np0V_%(|w1pF3vPVvG(&w>XuYjeH@^_&Dp>gsHjJqXPFAPbYeOsSz9(zoIc& z-G8JFhf~hVoPTsH9mPFPTzRJ8vE`20j&}WNcUpX9)kf={m~NG7z3LZQ$5AX+euPTk z?hsEr$kxg@Gm%crRH{o2(XnQI*>e2^`JCHO2<4J;I-18zk?_$&noT}-KR%1TMOXVX zylgMEMPW)vvq=|eWMXHDi6E8&+MTGaGji4D!V_IJyvwl^9lq8303}AW{vvWmo)1GE zYieLLY&l1ejcUX`o35NoFQ2n*UF<3U(e4Gy;c^=Jw5nuNrCLdQP5xW>0_s=G4Z4ol z%axnh9#`*Xyd_vw{H3bsU7q1?RmRV0HBI;-e213yuA>Ga22zP{U&U{HG>cByxf(g% zxMg8L%}*ZZi&)3xap&7%G{=6TB*M}~CTx5kv)iK;igV@pnDnB(Z{jH|ogXYx1Xky# zbPLHBl|>94H;XC4W;T+>hAGM3QS6$!Cj`GXWoC@zjo~E$U-<5tVY-n(@>_$rJaU@; zH62H@_?{D7th8>tijwbU=BF7!;tLxs9(RsIKNW-h?QCGMCQ!MWtlaM#dtoW&s&ud ze0{>b*aE~#CB>>6CpYn2(@HcuM-0yJ(^-aD^`;12T664Zb+i-w;x#mM;P`WLEZatZ z(>GT^oNA7Xc0r%p<}2)<(CE1$DMzePYqlx9-4CB=1cmX>S~g{!q-J$C=wbV~{tdlt zy=!Z?wBOa3Ct{^$RRyqV_Ho{8!{7dX#F27YE z?PcZ7W9PAwVd=B1)X|MtHh-ZUctR$fSdV}>!P)wjE7HZ`A3UXfLqq#Y1{jP4IYjFu zqv?QSgIe`S&jRn|*5p#j7Pf!Dw@j6z9t2^Y#2Mud3JxbDte+}V|^Z*~CvMh)rklBJKP`9y=ibIFLq^C9Jf1dH0J_@?IA zyDo7VhFrR>EcV7Oj^v}>GWyf8UW&EC0$U+YIBKQ0msEton;_kH)#seJ2$WL;ldVhE zw}E0vOdZ6}+BU;U$Q|7*9B{t!D^tOha3aTplk1jJ&e^mM+s+KV6UR>|APr358!I+e zRPFh>b~bm|G$5gq_M{#rYtvxe8*eNA#NaI*pFmdd*Iie4=b#zAoTsvs?N=z7I~q74 z;7@9$adr6W-KLNM$4Y{@S#_s8EU|Bj>#>8SR|tK;wqch zrmb12ecj?{(vfP8#R;jIF-wKji8vQIRi60(-NYBS%(fV)V)@B?x&|9SYR!%6mKzRx z-|-q1%I`L6?i=$ByPX*)zltfJ`Cem0e7gBHQ}W^9Blcu>y0sCRbxQ|iR$gs`F}9Rq zUUNq^L}oR8;_?(zrPEPV*)U$xi#xfjnq}T0Rw~RhjQqn1y+|QOvq-Zr&kH0Er%lY( zSMyzQ^_)z?T~iIJF2{G`)9+se~;p!h|>wU z;_O@;XWedF9?4baCAaSvm^)wTq$g+?GmeO@eWz`Pyq7o8WI$`(JIL)#Ph#;e2X6(R z|M6DfY5eA`z{>1UkbghXb8n3O$pQHM(O(5Sgr-Hy?18-eQ{-TM^O&}lG!gbsF+XwzX`36eytu2YYWe8_k z$MeHA`G<`9uOIDLpr_=1{y*5fe+(z`V)Z#&n2?5z^xH~iBI zQUYI@J$Sltoa#!ASq;b2h80$#Pq}PP15ht_P;|=119;+FjF;NWB6u$2w!D?HH^=qH@Z^G$76WZWs^REUNm%`w;d_b<^nSr*7g}}Io zayL-Q>+t%a6JSq4NHEto+3)s=^O*R+%i4YghKq!^?4OY7S(2L}#V~{-R_Ofp;bzbI zfY(zdxehkASa1d|>w|MUIz=WjOALsObLO%%>VdH|gQhxcMC8r(Ic}A~cd_=U!U&?` zSHtS1Qo?&{GT02%(@0nu_L!hYkoO|4Qr5ZK#%>oC)p`Btz}DCaURlAwy2X{=<3~jt zY|bP81g~(G6v-|MEW zt{03z>xIozlH-u768kpGhn+@6CN!@B{^X z@MJ1llhcf;)I;F@phx_S%)o#mA}hSKI0Wks`z#eah4NnKR^)E1Sf*SeZ#KrK-d-ZI zpkNY8czG2>{Twg^n1rB0oaDIVYT$k4%na~8tRd#OK*VS1ce)@PVDB1=#6QeVoSE1HA;$arv}9(MW>`szXgI0*^s8dmzxIc7ys- zsgxt1c?Bznqd_i~E8*LV+z47)I6ELcy0&@PzDO@JvYv^<-@a8_F)J^pKX6Wsb*_I6 z$?6-*o4cac6+Q(=5)n5{9i{pH>fv#L!}UYDXKq7(f;20QY0K*7v6s3i3vgo&8w)?6 zW&de%m@SPZxEysY6|1X;-)Jj2;$#G1f)`%i?`t`Xr zXeq3EGC^*Y9 z#dd4FCiFh0pJxL+AS$Z^#|O$HxnkYPgsFRG4cD7~Pb8R59X_ej3KG3iQq<9sv#grC zf@PU_l%S-VAJN%KH#t>x*{|SWuq?r(|04?jZE%l#TU<%QJ7OCLRL5cTqZs8%deKj0 zuj%#=DBGwGS8CaJ=XDqeWIiL@^1_gcY*ow}#81#+K4)4-r09pA@BE_&i6&Tf@Xn@q z&$Wn3CKiF%FW4QaT|FV@8wC#XIH%Pz^_b&$cf}G$bou?wG(s^e?i3BB80bj5;-Pv- zIEO$t9W@PPXd*bl(SE*#9HN`l0tgu_zn(kZ&1I#^C(rj(zeEyndGmBsr<-I}97Bh1 z`2jnd)?qp;H!K|CavlnFoyEGaOYUwKuH4|1O?ZyZ2rgA z{~G6uTeOg2$#f=Ihvf^r5R@G=?nS6c2wd%}J3C~FzMs(=Y<0OKx1>n*rEXa6vtznc zm%epwWKR>!jXDQ?Qn6=f5RW2v3OU?)kMP(N-B0RP)CRXL39JN|2~Uhn2+UB1P*|Pp zh-Y@a3SoV=%#6up-M!;hyOUC_q_eDRe3zNZGbnVrHPmi!$e6u7L(8t$L7#__#!mpo zwlCrrl`nN4=(eWg;tY~3%_GyUs9pza5@FAi8J(x9bC!G83Mb8_FknK&L3_$ zVoH@VU1E%Xs;L%*#1IVN&$|*CYt{O$&35>H@K%^gFo70ba>DH;jW=^Vzt?g8N*K^b z@kCTC^KHAO80h(DzUH&I4fuZZtX1?BE z3|b`2$Xxl-wxTdyN>FlAM|y7HEf*A0?5dXEGW}^>%SrtY<66~3I)S{AhM^V}mgyKA zxd4}W5=#;n+z}N6ZwGk~hCAgHvE6;EhiKFO&;)bFP;Nl4j8EkXI(R*gjZYkiq_H!pXgI(+k=1Pb9{q1xd!EI{eyF#XYgqc5MFf%!BS+hh>X8VHr1Trm__K(?BsFm2` zD>-#!UfP`CuB$Wk%xcJK^(DK*=mFRKYe;`LE>^M~J{iJJcIQT6Vm7Mg>bv?X!3amh zk*e@fC(X@Biz0g3@ql^@G}`-NBg*gFTE*X|XB}q|B)|M>?w#fP%WXvZ#1%=atarzhKm8dE(WzGTOPsy(+roazPES!07X8J>=&6E! zTt!+Y2H~_CovCveR?O_*{T z6T%ZFGHtY0z#9pZ#0(0q)h2quIMAKG`u>B%a+C~(D|x7;pt+^4yW9dyW_7Zc!C6+I ze1Uffdef4&9um>k1vGXH-UEK*i+*`y+?}rH&?QFse268PW~%2|7{mF@Idy~7tJ&~fDEOZ^VB8{MP$Ff-H_IWRmTUczU!oCd{X(HrlAKM6 z${;yJs;WG^${tBQ&U`r6bYA0NZjS%yk*@v7DtZ#p<= zj?izRqON%)nG&sX)O0U;SFuPUH-5grI4k%KJ@s06@ho2WyjD}RSFca5TW~NwOPts{ z>D^l3kepGNj`}~k@ooTJ+R^`WUM zJ=%Ww!8>4R(Cl;8HnGJs)-LRN(R)P$*08)o zD9@dSK&2fU^0t_-ZZ?>*3Svb#P>l<4Tb)hW?oKl*Ik5&aWDIpy90zF#G+DPCt~Gm_ zvPT)bz4l(Ff)+0&Fh2f$uTd#LrJZu*#04*&tjdpI8&jaCcw-h6yJ+nx1-f|izf8_i zTs}Lc`?1c>ESAPkZ%m*WcY484&eE?QmIcmFK8mM^eFl!k+exFqUS)d1YZK)ERyLA5 zj#W=qrDonF42#_>!2iiz01Mxve)HMgzAQy0`q4->{XE6N7EeOrd+O}LN^ES4hC`Xv zmOSUv+%$c%i;Jl54aw{zNK;%rzj=4M|6MxIGj7Y#OCPI7JtqULW|v~UmmkU3K0B7W zBcf7&N#>hfmaHe*-frvH`R%Wqndq6?_AJ77Dv8!6qhT;qjWVTsl?4nVh87QIecKtC zv6o?Xjxz9!FG74YoZYKN^)TkQ>sBqANi3zb%NbL2?yMi{??MkN$H(cU`%SaJK@gs~ zd7P$vTmM-9kC~bqDWV{Fun94b5&9BqzAzMz7l@lt9p+$RDN!CY?C~KE;y;~+M=i>m z*r%5$52fTU@pVaQRkS(iWv;(pt-r)caUl@M+@Cy!FU5SP_uq!FmDv7-wkqz9NvBrx z|MAB7*N+VXt;=+xLa|fmB=4jn-j-(e?o1)PRx&e z&d9jQGM2HA=jHR4V=82VW4h)hKppm%aTk|>QR(pwe)_v9;osul5Fva(yp94-@h=CR zks&Mp)OZ4xU~g^M!Pe70uh;kY!r_no5|{R`YmY|Ek-sXIzp3^Ua`}5ISq^4p8bd9R zm`So<=KAy2rJt&f@v;tTjs7h9|CS|xYM@{g?3Mx`Wv~x42|J44=x#@X+cRC4CmQ>m zN`w!Mewlyuz_8-{I~mLN==1Zg;O^?51saRQH#k~l zlO8l>8!~x*nNKeBDzSvNmFb7@5o5^fO=Oq6`&WCE0%(tdQi(Vb#Z0I@RrsHc!WySf z8e{Z%G(!JSU3841m7?_dAn*IMeUI;}cmr+(CZw2&m_dId{x`r95WFT>(a<3*Es|li z49n~I(b9?@@yT!5*}a_qlb0U$agFseb&XIfBuv1R%w4mp8| z@AgdU&V=ebNRLJnZ)n6m!`XfaYSK$LEZ~52YChM^+u7MO{o3)qvuKE*JNNeS^%ud2 zM_LXP(r+(<$ldD%jo87NQeHs-n2D`}Yri+tr*5}dUayBFFEdExzyWRASQa~>n4qg# z*~sz9N^2SX<|-YsB3SsWw1kAPB6%S#&iA1^{F^9)dPJMc;)gGIc~47nML{33$(1j6rFK^UCR`%{h8}Ezn_+ZaUcOBrjY4@ zGKdQRbSUe-9t6FdmK%DN`yj&E2{66$FI%Cp$CVsBv_(OY&*SUB_zK$FxD9l`V2MRm zcX3vz%SJ7gcX1!i!+W96{fCRK=FSz+{c?tDUX--BH(WVppfE)V+=FZ~ z+t;ACHO1>`QnW&|HS%7KFP(#`z;Vg9-Z{5P!z$_@jo+}hrF)0pf{SK{H1=n%ejB)(z96BaMs9YGGA1MZH7~T#A5jXG5 zDpGww%dOJ|?ov$0CJT5?Gw7-g+gwIeb-;m57c7tlX;=bY&(pnTl~Q|QVa)UvZj(H| z)KH-pzkUZn^7U~~rdNC0#Vc2aQhq-SWU;Y$b0k;3+Apa$+%LH&UB5JWJefW+`@>Cn z+sKpz3fvFcsaA<%XYW{Q35x{=$y)^kVZ|EiFmmYR7hU#k9kjWn9gUu!Uz9SHep&6b zCQH+Gzg^dirKfGKy<$mTwVQ9cu1fXR&OTXs{BHmAzC`@|-ln1b)rWGUKqkAKHGBHG zJ!Awx@ozbJ73b`#+P#5Yl%4 z(YW}og@P(#gK%zXM)H8>U;-SB6ZW?egN@y!1d;A685!(?EtKRf_r1)il&u6Bid=Tr z8g_r@A-CBPqXpCyINYt z%3zgCK0O9E)&QWz$yzT{FL1G97DyhmCU7Pyekm=&z9&a+QsZQ1aw6C^e^Y^2VFrLJ z&R!a8;{FXNra7>&Dvf!xl`&sCCZDdL0Kh%*;82m?Ai!@)7v`Mt2zB^c)N&7PJvzNZ z)WhC8wcBLXo0+jam9C3-v$pkm;}<25LCxR&PHz>n&z>wm>RHe3CEU8_-2*qrr7Ka< z23^Z%R&FVw$aStLkuc6SU+273xO(`VBk1b3*KgAw?zvE5w^;kk(5r{f*ZU*yC{d$9 zaggM}F1);_6BY7Vp5Yf3Xlh&=c)gOe^3{O3?Ycrp%QCj-=XegiRZnkMlP@KiQMHyG zu`qS^-dYxP9Iae9L;TPMZo&7Z_WQJjn+^}Y&-~X2Z5AUKPUJBY(i9iikR4GeC0qI! z!MB-U%{a=i#b8&qdM1Msbz&ewc$D1{-b$f!)m+uDb0t{<+0t=P>?-L=h%SJwBPt-u zL|COZME#Msgk+5~ov%f5r*#;txV@$5o~#GIypYRY^jN@dsL(sndcuK#MhuYjC!ISL zS^J#0w34FOL70UlP8H%|NN(wf90Rz4x@+avF83T4IaNjO+R(wMMkG&Qc`e1$6k+Ql zlPzb+tg)L%P7k#>HQBu=CM9Mh2(4`4vqb6N-%y;5Sz3Unyyo6m)Y38Ks8JMDf%L4J zUxzfbgtjJf86{>kd23JOzuSFXX5or`KEtf_jWcf0JV^}p(;~~A#VxB^%IT(jx zpmYRgEkv`$6tgR#;Ywbllc(Nf;5vHD$0 zQc3kP#6D0kD7m29jy-7#S(bm@6w2^Y95pAtqfp62lWrYJD<#hTY#~EjbT3U7=P6M8 zM9(`73|d17aB}#sycfk#EsdL!D_i0nXwHI~nD-WR`&jaEZw;lvJ=gEI^0a@W+a9i& zQ{ge|jhm`=_!dok3i@2)EzdpQLGOy~>idov z7xmZfhxFf&WOgjBA z#>sT_Kt^46!t4;#w2!5c;dOmJwJ2BWxKjV=7B!YCGnFz~&Qr(FmFS-@9aqX)iR2Aa z3Bo+DKF{(AFDAY&DxV~-Q=`sR`Bd%DbF*N%K!w)cSzf#GA;XLh;=PxwGp=>=44_oC z+EmaR`I>W@Kfz@l_eSn{meQEOCINQ%#fD^(lpAXbh0Kh?CJI@@<;hnsr`b)L;-o8A zzJGVLcwO-n?W;e>jUanm^l~)&S0sQmsUn<`Vi!#4GhSmow&6^~rxGslxVKj;uxNYc zl47{UjO4Qg2(sp>6(D)SEH1-aLOR^9JX_tJiTxzU9Js z7pBW;*d-0Rfz$+O?l-lrnEuwXY`;M7F&m~GomwsL*$eM2M3d*>P1gU>?s-FUVY-a?4Crmt(o~u}?4lzrt zn}zr-w9}xm6gP@8lV7taL=T;8h;9v63F-@d7#yecLS8sH^lZ3Ooz0mX=TDLYiFI+r ztm^D!ZFjWN@2jlI0$AH3(t7i`C^^&ZissNJF^fzE@qtWS!`BA9I5~0aM8X!0uRExe zL@%T>Wz;6Sn_M-iO74@fXydy@cK(6Sm7$H`c;{T|$tJ1%)rX84L(Efjr}+Z}W^;qs zp4_^&KKSx_i}gTE;f2u2Lu*&r2P##+Ox_Uf3`tUydqlca8u?=Phqo6|bVDaW!zBul zE5<#vcJF=Fl*&BmgDxqHF$vo!v@yTJ(!?7C$J}~c4X3G`dwMJy2fUqh>4hR zkKV~(9F{$+fl@;15kl%ykr9|G3LQWa(C-FY(h^cPG`ZZzVQi7f^q1*AB`lloN7}3f zm0*5Eao+lfAx$vU;7vhAU3fS2QiztJ7}*YX@vKGcQ^Bj_Pxd7Iy)fxbxq%f?e)|r6 z1?S!OHPRA+cJzY8yK~J<1rq|A#6_bHlBX9Hbkzwy>xEanO>&}mfb`N%q*vsGo0GM(Q20qbb2&FRc2rXb?hJEt51o2^@ec|)9Kibsd^ zm<>tF5*Xi#dgbfU0nb=_ajyuuG3L-GqcQvRBT8RMXLfn09%igd@S-?Hn5U3NMA-4= zs?WU9$mA__p=%R26$9R^LjXGsIK4H zHVvIbrgCXVcI~K|&(6m^zcP@>hoPWQ3p0Gd-PEnLm9UI^9EvTu9zslOn9jtPPJG3q zJAUaT+M$zetyZy>jbzPj^+44OY^_XpU26@unD&HqpKp(_xib|*tjhD(-491SM1%dp zJVLZWO~Woxqp_w@P4)8Q?YT)W0ny?41hafk*t9IGY-Q|b_Tio8le4i}_3L*tqf|*x zG-B^r-Q;BkP>?m}#m&Qa7q)!Dr*%HkInp^^B31s)yOHbl7P+e1R{}1S;n#;h z4<}2?GYo$<<;t=oy7VLO4|!5)VJzo$Ocb$TOkIsT9LO`)Zq833u%+7^jQ`+M__@_Q zuKw_RdlB<5-csJZ%QIoVe(MomUr6T0Yo|ZhG+C;3nyTI7E~58zNDN?bVtcPtOFj^qw{G z{ER=?#$lO|TByQEq`GhRNX|~2^t$*0zUwu-f4=ffXB|7oqL1JI!8Yz-A3oAyvd4OY z*^AJZjaGw;=6r!c25W5Gy7Q+Yw;)`#759Yf-^M$CZVs1NJKBLf0r%G<_g~yA==!BZ z@Kie~xAe1L;opY}av~*bHvI_iNvgjbtSs6^mcG%Kcu$_$V?f>cO%>h~qrVKSPsmNe zSf9Brm%_gs&)htQp;pag-Fo-xWlvh-YPy%NW~aCQy6dd0eWVPvxuLHQBB zvh!(2vg$Qw8UGxb?>piDfz=eZXUKlX0tgukxc#|vxD{D1u7*G6TDiFr?# zhd!eY{)?#Uf1s4D5iO`t{>-PSNBJ+q^Fplwv%_lNiaO~p`uPrn|3KJG*Y0FxdhB05 z!rX_#Q2Rf9oq0S|?fb{eqv)G5LkKY#iG~zK_NB3A35C%jLs>%BsVPj9jO-qotb^=X zvKC>=KFGc#WZx<=_IXP5Jx z3Zd6V7-vm|EV$_Ha!9B$D~oVRiY}`cV&Hr-0I+AamU3~rIO@jWyS$$5zOG8&gB`S= zw&hf;|9H_4qLedky?=CrvogeM@ug@ue*|T{e^RjrF*=pY>FQpbHu6M+Ej=K3%*?ydG|o#?Y7abSyr=vo0us~B3vRAlMBTX)Ss z%@Xe{*PSebf`E~-XT&>S#?=0OlDw~I4*@q;y`R9O@#>fG!!l!`X92c}ZrNCC!Nn`i zc{7hK0~cOB@LJLEn?a2cC2naySa~-Xw+sOCSz`y#!WQ1Y* zIbN;IjD-$%3+P-OORx=^i4WlOKfN{~O)43{GRx`@>EqUo*t?zTonn_ksiYe)(o5l# zye)XF2>hXA8i2BI9(4-$>wvW=aeJOXS{!0E0#(qj+JSNDb2Brp5N<_aby+al_wuhL ziyil8hLNU0oCdz`nzYtB%h(W5s(sdZr1~(&7bv1G1V&U`%Fs`l^_Y~i1uD#ow9Z|n zfF*ri;$|@49O3AQm2qt+R4k^rk&hFSX{g2*Cwa#w^w*vgq$!~`tbz~Io{p7a7uq)l zsoR90ZcQ?Qf4b8jtk8;b#(>4&SnnZiM?#uRfA;n&a4H7c*HoH$;s+)B>GqUqpqtjs z^_;o0kU0x}%SvE5r!Ed!8;KsF$pdYHcvOqDy_DP7L*1IiO-^-7S5Sg%LF289G?R!2 zfO$9e1i+j&SDXEj9-x}N9VpGBW_tm?>F`t999{$!`N?%KoIH=y`@Ps2Z2w#5=>;Oy zKcaIq$28ArDpu9MBWSvhxhjCZ-l5NP*yk)~f|XL|!Wd>yO7AaJ~}>cqv#@p-SPRIoehFWdPH8TMG@nWFo+r}@U`o_1-{+DA+p z#TrKmFduWptyaNY_Op)ip0m5mE*lU;8HAb+E~ZMSfNJ~f;YU6+$JmW6T7qkNfJ6zp zYeiICuK>A%4h_ZmutJc9PJ2_alx_R9Gs9Vg>Xkn3O5k4qtz~XiqjPv?FU@J?m@mF&PO5V7Gvpvt(NV^u~U@!K5M_AM3HvHy9sR1U~Q8CM0Lr zG*5Q3Vx?SMPM)+mF21mY$ayP}D?uwNKcEpZI{}!t$lwMTIr-tv);a<>rtszmAGdCl zb!2WG!}?3+_WC!U?OwS7QAG-1t@nDhHh>_%ptOc$b5AD4x z?DA?JV=MiG8f^Q7Uub zf$3u@g8181L&lw=Z!Be+9RDim2yBGMxd)ev-=xqQ!olDTTca>)y)ST*H*wPn1984z2}yyf4^o2fja@#1Yzy)HZt1*s zYqoPWF;x&f1&oTLyw*F2&rLG4w3z>4Zu7db*rOhJ$7-kVyfzm(teH&V3k^G|q+sh2 zdPommR+Ouf7j>>Ha3OL=CZ}7mxy`&SiK?_s36U`p&M^p1H>|cz>n({s@jotP88lKZT&O)epX5>oay6wcw%4$`N=E_0(Kn1U5C{gIeyX!Ax>? ztSY8~q17cKTy;mLYRPD}NKJB#W2uM(F!-51XuhgiBJwID5>YcAAnuuffw$Z)l1_q_ zAR`1ugfdPh=U=)Md*~K=Uuizf@U_O9885sO{^5MGCSD(W z7Tp4)l{+JutP8@hYJ(L!!umx;Z(vP6zHe$o(=>F#LPZ_}w zlBkEIKJm?fG{-oHdzW1Pcq4%zJ`6XK@ZaafK7dNMZc~sVimvB)2wi$~cF~O8>u#Rz z>bGlEEj8jl>h0G0c?L$Yzak0%Dr~Mt#TYO1KB>if1+GNSG;U#n!)Gqm&`c_sG7wq*HJrD(-kYgd&0b z2zrVhPm8!zu)j*?LhBd`sSK!?*?F`e-HEMvv=Nt`dVXb7rb8R#9MsVNzTgtqOLnVI z^~KRqO^G&Hyne%DVHGz5L#>w1f8EO;$RRy%YjvaT&F|&>XS7?czXwJEOONg;=_3J6 zt9~_si6g?c=8G^?(*(^+N?fXu=zn|6m--g8{v30SfC-exc+J`N^5wUiUA@|u5?JsF zcJ4S?xfB#fE$ok2t*{@{405G!xC`F0*u>bH2MuBtfgyM;yVM?u9FA|v9a0nIbDRAQ zan)E}G=lRoc;Eelw=@$9=sybxQkBB93Z(B9dzOC=YlAKAA8?SfaodjjXqgVXaB>-B z+2ilX8q#lo{7=2ApvVBfdb?cnN02g#g*{pIdvPD6w8tzlY*xqFUN#?+bF{>VCgb>! z0&XKWhfOJ*AHoVbMUSbr*Jxe%(3>PSLKEx9#d|LI_6IASH_JM0$%tR9(>~Qwi9QzS zTpP$azHt6dr-v19AyMHW=G`KD60FyK;|*!c;RgeMc#xN==fp8NZuX95?kJNV^}pZS zKL=J<<^TNs%&kM z^JwjO%Vwg2`>_%j;O*7^sD8M@JNSjo7)firoVZ}(MFn@MxDV%3pUU-&z5q5|{il1Z ztP{6EDEcv@FEE5r`^g0#=KG4S{tsOl#J6OCH`RPU*=2;2F$l1|dcbp~I8{LmBSw%? zwYqhKUZQiMB%p;-@N1ut@QPl0(=YSHA%py|2zxbhB-PmTL(N{e83j=PCbcXr|5Uw0 zh_|!=?_IOAjuRfj;tvpV-;r-<{m@{QDXP_EN8-WZms@P$jf5gk~1i zQ!WEqWqw|QF@PodHfE6)&l5Ewgra z-o+^F(6>xE(|vywq=}#MBWWbVp}4$yJ!9sB%XzkkxTQu4Mi8S)BM5;#;n>G{rns&v zCSdr7-yHB-1Z*v83r*jW>$UW1yk^Cvs*=IOq(RsK9`2$-i#)JZ>QYw7Ie8#7DZmB5 znvZHl)uyZXiw7_VlbZDU?diy9C7`3*cP6iW?qQlvRCt{_(JF->DWHvxt*Yad!`Q3p zj7!s1oQ|0U_W(g0&-;B35cFoLN=rk}(#$>hSI$^$7+jxzWwfGBFGvkz@|I|3ldF^& z>bm-Ls_XjG)y9A;dd7~>v1eh}fu&fqQfG$6;be0o*VO!Obm6UQ$)T@LUUWmZ;RUbd zTWN8fK_i1b#$GHl-m~azwa`#9G?KOK{Av-&VVyl+61mm-eC^T&21^~#^TOC`Qc^_5 z=@wHKA1fQGWK>#DctvAu0_^4rB)g%6$T*YnPYOF!Z>CH1a|T*zwSxgQ(`Nd|xfv57 z=4Usn%w{N8DZcPmX~unTpHgK&G?!V_L%B}=l9&Tm*%w(myN|v;-4cZ@?>^H~qa=P? zUG3p{XP!Y)yDV6jzfGDffBQ8kiwbmBIrD?iZSF^os#5k=)fjgvF@j==O-;cx^%-I% znGfYK#G|IRoGRSgvyW^SyU`~jHOo0rJp2*tLzc=ab)wh7Hgdi}9MSO$qW>R4GRc^w zt&)EZ5u*@OG%)-f&XO--C=opRz>nv?MUkK~m$Zl!V>lAa;;x;EO`ghnekt~kvOya| zx@_Gfwda&4Q%41?uHi@D@=kgZ_Ry#QRekZ_jPZ`$1B;C#T?#?!f@7wEfUGeqFZB3@ z7xtNZ<&OTlMg5;ZB7vVSCBlr$?o&sQoqxE30y}XRtI53w+?c(kRPwRe(ZVP(mwc@C zQ2jqB;(y~E0oswhnVRf=1edQEd}tSa!*aI5E^UDx(MLxq-ziA7=Sbv7HpfVdlb%g( z?OJENC`Ol3m{bB6{Og2I@*f!h?W`6Z=u$cZ`|MX7QvaWV^he4aJ)z%k_Q&s-dpjO= z@a;OEcX7mw;(0s2-A_o1&H)rf)@ZZOyjBrbEv-585nxwa(SXw5rP?|H z$0D44|AB1`Uxq-h66uEZF8$KIC-`3{>H3MLr08?<_sS3Y!*{bYB^{@Exk;(GP*^jH z+?7dj$*g~#I^c4nAHSa;Z#1%9gRi6&_D9T2tZE^=SfpXb zOv)~o1olIwZ1k-#*F$8Bkb$`U@*X&@o1tTwL-xd-Gk52}D|7FyuIJ`x(oZS!W6}|$ zN{Sh%xd7jk%3Us!Cc?|6hrqFMnB~>WT&1<|3n>c$Mvd;09YY@;cYvIF>5>Iz*V&R) pLH`LA^Eu2qP!2JG`6f1v+lw}9kWKWcBJTlTT55X89MzjX{{`;sPW}J@ literal 0 HcmV?d00001 diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json new file mode 100644 index 0000000000..779886749c --- /dev/null +++ b/plugins/devtools/package.json @@ -0,0 +1,64 @@ +{ + "name": "@backstage/plugin-devtools", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "frontend-plugin" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/devtools" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-devtools-common": "workspace:^", + "@backstage/plugin-permission-react": "workspace:^", + "@backstage/theme": "workspace:^", + "@backstage/types": "workspace:^", + "@material-ui/core": "^4.9.13", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "^4.0.0-alpha.57", + "react-json-view": "^1.21.3", + "react-use": "^17.2.4" + }, + "peerDependencies": { + "@types/react": "^16.13.1 || ^17.0.0", + "react": "^16.13.1 || ^17.0.0", + "react-router-dom": "6.0.0-beta.0 || ^6.3.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^12.1.3", + "@testing-library/user-event": "^14.0.0", + "@types/node": "*", + "cross-fetch": "^3.1.5", + "msw": "^0.47.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/devtools/src/api/DevToolsApi.ts b/plugins/devtools/src/api/DevToolsApi.ts new file mode 100644 index 0000000000..ff58a4ea16 --- /dev/null +++ b/plugins/devtools/src/api/DevToolsApi.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2022 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. + */ + +import { createApiRef } from '@backstage/core-plugin-api'; +import { + ConfigInfo, + DevToolsInfo, + ExternalDependency, +} from '@backstage/plugin-devtools-common'; + +export const devToolsApiRef = createApiRef({ + id: 'plugin.devtools.service', +}); + +export interface DevToolsApi { + getConfig(): Promise; + getExternalDependencies(): Promise; + getInfo(): Promise; +} diff --git a/plugins/devtools/src/api/DevToolsClient.ts b/plugins/devtools/src/api/DevToolsClient.ts new file mode 100644 index 0000000000..0be8294b3f --- /dev/null +++ b/plugins/devtools/src/api/DevToolsClient.ts @@ -0,0 +1,78 @@ +/* + * Copyright 2022 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. + */ + +import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; +import { + ConfigInfo, + DevToolsInfo, + ExternalDependency, +} from '@backstage/plugin-devtools-common'; +import { ResponseError } from '@backstage/errors'; +import { DevToolsApi } from './DevToolsApi'; + +export class DevToolsClient implements DevToolsApi { + private readonly discoveryApi: DiscoveryApi; + private readonly identityApi: IdentityApi; + + public constructor(options: { + discoveryApi: DiscoveryApi; + identityApi: IdentityApi; + }) { + this.discoveryApi = options.discoveryApi; + this.identityApi = options.identityApi; + } + + public async getConfig(): Promise { + const urlSegment = 'config'; + + const configInfo = await this.get(urlSegment); + return configInfo; + } + + public async getExternalDependencies(): Promise< + ExternalDependency[] | undefined + > { + const urlSegment = 'external-dependencies'; + + const externalDependencies = await this.get< + ExternalDependency[] | undefined + >(urlSegment); + return externalDependencies; + } + + public async getInfo(): Promise { + const urlSegment = 'info'; + + const info = await this.get(urlSegment); + return info; + } + + private async get(path: string): Promise { + const baseUrl = `${await this.discoveryApi.getBaseUrl('devtools')}/`; + const url = new URL(path, baseUrl); + + const { token } = await this.identityApi.getCredentials(); + const response = await fetch(url.toString(), { + headers: token ? { Authorization: `Bearer ${token}` } : {}, + }); + + if (!response.ok) { + throw await ResponseError.fromResponse(response); + } + + return response.json() as Promise; + } +} diff --git a/plugins/devtools/src/api/index.ts b/plugins/devtools/src/api/index.ts new file mode 100644 index 0000000000..6bea0a07aa --- /dev/null +++ b/plugins/devtools/src/api/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2022 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. + */ + +export * from './DevToolsApi'; +export * from './DevToolsClient'; diff --git a/plugins/devtools/src/components/Content/ConfigContent/ConfigContent.tsx b/plugins/devtools/src/components/Content/ConfigContent/ConfigContent.tsx new file mode 100644 index 0000000000..66129b5dae --- /dev/null +++ b/plugins/devtools/src/components/Content/ConfigContent/ConfigContent.tsx @@ -0,0 +1,95 @@ +/* + * Copyright 2022 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. + */ + +import { Progress, WarningPanel } from '@backstage/core-components'; +import { + Box, + createStyles, + makeStyles, + Paper, + Theme, + Typography, + useTheme, +} from '@material-ui/core'; +import { Alert } from '@material-ui/lab'; +import React from 'react'; +import ReactJson from 'react-json-view'; +import { useConfig } from '../../../hooks'; +import { ConfigError } from '@backstage/plugin-devtools-common'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + warningStyle: { + paddingBottom: theme.spacing(2), + }, + paperStyle: { + padding: theme.spacing(2), + }, + }), +); + +export const WarningContent = ({ error }: { error: ConfigError }) => { + if (!error.messages) { + return {error.message}; + } + + const messages = error.messages as string[]; + + return ( + + {messages.map(message => ( + {message} + ))} + + ); +}; + +/** @public */ +export const ConfigContent = () => { + const classes = useStyles(); + const theme = useTheme(); + const { configInfo, loading, error } = useConfig(); + + if (loading) { + return ; + } else if (error) { + return {error.message}; + } + + if (!configInfo) { + return Unable to load config data; + } + + return ( + + {configInfo && configInfo.error && ( + + + + + + )} + + + + + ); +}; diff --git a/plugins/devtools/src/components/Content/ConfigContent/index.ts b/plugins/devtools/src/components/Content/ConfigContent/index.ts new file mode 100644 index 0000000000..b1cc671de8 --- /dev/null +++ b/plugins/devtools/src/components/Content/ConfigContent/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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. + */ + +export { ConfigContent } from './ConfigContent'; diff --git a/plugins/devtools/src/components/Content/ExternalDependenciesContent/ExternalDependenciesContent.tsx b/plugins/devtools/src/components/Content/ExternalDependenciesContent/ExternalDependenciesContent.tsx new file mode 100644 index 0000000000..a5dd825fe5 --- /dev/null +++ b/plugins/devtools/src/components/Content/ExternalDependenciesContent/ExternalDependenciesContent.tsx @@ -0,0 +1,140 @@ +/* + * Copyright 2022 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. + */ + +import { + Progress, + StatusError, + StatusOK, + StatusWarning, + Table, + TableColumn, +} from '@backstage/core-components'; +import { ExternalDependency } from '@backstage/plugin-devtools-common'; +import { + Box, + createStyles, + Grid, + makeStyles, + Paper, + Theme, + Typography, +} from '@material-ui/core'; +import { Alert } from '@material-ui/lab'; +import React from 'react'; +import { useExternalDependencies } from '../../../hooks'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + paperStyle: { + padding: theme.spacing(2), + }, + }), +); + +export const getExternalDependencyStatus = ( + result: Partial | undefined, +) => { + switch (result?.status) { + case 'Healthy': + return ( + + {result.status} + + ); + case 'Unhealthy': + return ( + + {`${result.status}`} + + ); + case undefined: + default: + return ( + + Unknown + + ); + } +}; + +const columns: TableColumn[] = [ + { + title: 'Name', + width: 'auto', + field: 'name', + }, + { + title: 'Target', + width: 'auto', + field: 'target', + }, + { + title: 'Type', + width: 'auto', + field: 'type', + }, + { + title: 'Status', + width: 'auto', + render: (row: Partial) => ( + + + + {getExternalDependencyStatus(row)} + + + {row.error && {row.error}} + + ), + }, +]; + +/** @public */ +export const ExternalDependenciesContent = () => { + const classes = useStyles(); + const { externalDependencies, loading, error } = useExternalDependencies(); + + if (loading) { + return ; + } else if (error) { + return {error.message}; + } + + if (!externalDependencies || externalDependencies.length === 0) { + return ( + + + No external dependencies found + + + ); + } + + return ( + + ); +}; diff --git a/plugins/devtools/src/components/Content/ExternalDependenciesContent/index.ts b/plugins/devtools/src/components/Content/ExternalDependenciesContent/index.ts new file mode 100644 index 0000000000..bf747f3065 --- /dev/null +++ b/plugins/devtools/src/components/Content/ExternalDependenciesContent/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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. + */ + +export { ExternalDependenciesContent } from './ExternalDependenciesContent'; diff --git a/plugins/devtools/src/components/Content/InfoContent/BackstageLogoIcon.tsx b/plugins/devtools/src/components/Content/InfoContent/BackstageLogoIcon.tsx new file mode 100644 index 0000000000..32f35600f2 --- /dev/null +++ b/plugins/devtools/src/components/Content/InfoContent/BackstageLogoIcon.tsx @@ -0,0 +1,25 @@ +/* + * Copyright 2022 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. + */ + +import { SvgIcon, SvgIconProps } from '@material-ui/core'; + +import React from 'react'; + +export const BackstageLogoIcon = (props: SvgIconProps) => ( + + + +); diff --git a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx new file mode 100644 index 0000000000..7a0ebb3e06 --- /dev/null +++ b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx @@ -0,0 +1,138 @@ +/* + * Copyright 2022 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. + */ + +import { Progress } from '@backstage/core-components'; +import { + Avatar, + Box, + createStyles, + Divider, + List, + ListItem, + ListItemAvatar, + ListItemText, + makeStyles, + Paper, + Theme, +} from '@material-ui/core'; +import { Alert } from '@material-ui/lab'; +import React from 'react'; +import { useInfo } from '../../../hooks'; +import { InfoDependenciesTable } from './InfoDependenciesTable'; +import DescriptionIcon from '@material-ui/icons/Description'; +import DeveloperBoardIcon from '@material-ui/icons/DeveloperBoard'; +import { BackstageLogoIcon } from './BackstageLogoIcon'; +import FileCopyIcon from '@material-ui/icons/FileCopy'; +import { DevToolsInfo } from '@backstage/plugin-devtools-common'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + paperStyle: { + marginBottom: theme.spacing(2), + }, + flexContainer: { + display: 'flex', + flexDirection: 'row', + padding: 0, + }, + copyButton: { + float: 'left', + margin: theme.spacing(2), + }, + }), +); + +const copyToClipboard = ({ about }: { about: DevToolsInfo | undefined }) => { + if (about) { + let formatted = `OS: ${about.operatingSystem}\nnode: ${about.nodeJsVersion}\nbackstage: ${about.backstageVersion}\nDependencies:\n`; + const deps = about.dependencies; + for (const key in deps) { + if (Object.prototype.hasOwnProperty.call(deps, key)) { + formatted = `${formatted} ${deps[key].name}: ${deps[key].versions}\n`; + } + } + window.navigator.clipboard.writeText(formatted); + } +}; + +/** @public */ +export const InfoContent = () => { + const classes = useStyles(); + const { about, loading, error } = useInfo(); + + if (loading) { + return ; + } else if (error) { + return {error.message}; + } + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + copyToClipboard({ about }); + }} + className={classes.copyButton} + > + + + + + + + + + + + + ); +}; diff --git a/plugins/devtools/src/components/Content/InfoContent/InfoDependenciesTable.tsx b/plugins/devtools/src/components/Content/InfoContent/InfoDependenciesTable.tsx new file mode 100644 index 0000000000..e1ab59f279 --- /dev/null +++ b/plugins/devtools/src/components/Content/InfoContent/InfoDependenciesTable.tsx @@ -0,0 +1,55 @@ +/* + * Copyright 2022 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. + */ + +import { Table, TableColumn } from '@backstage/core-components'; +import { PackageDependency } from '@backstage/plugin-devtools-common'; + +import React from 'react'; + +const columns: TableColumn[] = [ + { + title: 'Name', + width: 'auto', + field: 'name', + defaultSort: 'asc', + }, + { + title: 'Versions', + width: 'auto', + field: 'versions', + }, +]; + +export const InfoDependenciesTable = ({ + infoDependencies, +}: { + infoDependencies: PackageDependency[] | undefined; +}) => { + return ( +
+ ); +}; diff --git a/plugins/devtools/src/components/Content/InfoContent/index.ts b/plugins/devtools/src/components/Content/InfoContent/index.ts new file mode 100644 index 0000000000..6d5bad53f8 --- /dev/null +++ b/plugins/devtools/src/components/Content/InfoContent/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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. + */ + +export { InfoContent } from './InfoContent'; diff --git a/plugins/devtools/src/components/Content/index.ts b/plugins/devtools/src/components/Content/index.ts new file mode 100644 index 0000000000..f5bd64f7c0 --- /dev/null +++ b/plugins/devtools/src/components/Content/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2022 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. + */ + +export * from './ConfigContent'; +export * from './InfoContent'; +export * from './ExternalDependenciesContent'; diff --git a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx new file mode 100644 index 0000000000..a5e4f2439b --- /dev/null +++ b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx @@ -0,0 +1,42 @@ +/* + * Copyright 2022 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. + */ + +import { + devToolsConfigReadPermission, + devToolsInfoReadPermission, +} from '@backstage/plugin-devtools-common'; + +import { ConfigContent } from '../Content/ConfigContent'; +import { DevToolsLayout } from '../DevToolsLayout'; +import { InfoContent } from '../Content/InfoContent'; +import React from 'react'; +import { RequirePermission } from '@backstage/plugin-permission-react'; + +/** @public */ +export const DefaultDevToolsPage = () => ( + + + + + + + + + + + + +); diff --git a/plugins/devtools/src/components/DefaultDevToolsPage/index.ts b/plugins/devtools/src/components/DefaultDevToolsPage/index.ts new file mode 100644 index 0000000000..133dcf6e3a --- /dev/null +++ b/plugins/devtools/src/components/DefaultDevToolsPage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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. + */ + +export { DefaultDevToolsPage } from './DefaultDevToolsPage'; diff --git a/plugins/devtools/src/components/DevToolsLayout/DevToolsLayout.tsx b/plugins/devtools/src/components/DevToolsLayout/DevToolsLayout.tsx new file mode 100644 index 0000000000..12cd3f7666 --- /dev/null +++ b/plugins/devtools/src/components/DevToolsLayout/DevToolsLayout.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 2022 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. + */ + +import { Header, Page, RoutedTabs } from '@backstage/core-components'; +import { + attachComponentData, + useElementFilter, +} from '@backstage/core-plugin-api'; +import { TabProps } from '@material-ui/core'; +import { default as React } from 'react'; + +/** @public */ +export type SubRoute = { + path: string; + title: string; + children: JSX.Element; + tabProps?: TabProps; +}; + +const dataKey = 'plugin.devtools.devtoolsLayoutRoute'; + +const Route: (props: SubRoute) => null = () => null; +attachComponentData(Route, dataKey, true); + +// This causes all mount points that are discovered within this route to use the path of the route itself +attachComponentData(Route, 'core.gatherMountPoints', true); + +/** @public */ +export type DevToolsLayoutProps = { + children?: React.ReactNode; +}; + +/** + * DevTools is a compound component, which allows you to define a custom layout + * + * @example + * ```jsx + * + * + *
This is rendered under /example/anything-here route
+ *
+ *
+ * ``` + * @public + */ +export const DevToolsLayout = ({ children }: DevToolsLayoutProps) => { + const routes = useElementFilter(children, elements => + elements + .selectByComponentData({ + key: dataKey, + withStrictError: + 'Child of DevToolsLayout must be an DevToolsLayout.Route', + }) + .getElements() + .map(child => child.props), + ); + + return ( + +
+ + + ); +}; + +DevToolsLayout.Route = Route; diff --git a/plugins/devtools/src/components/DevToolsLayout/index.tsx b/plugins/devtools/src/components/DevToolsLayout/index.tsx new file mode 100644 index 0000000000..0bef371b05 --- /dev/null +++ b/plugins/devtools/src/components/DevToolsLayout/index.tsx @@ -0,0 +1,18 @@ +/* + * Copyright 2022 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. + */ + +export type { DevToolsLayoutProps, SubRoute } from './DevToolsLayout'; +export { DevToolsLayout } from './DevToolsLayout'; diff --git a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx new file mode 100644 index 0000000000..880efed68c --- /dev/null +++ b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx @@ -0,0 +1,25 @@ +/* + * Copyright 2022 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. + */ + +import React from 'react'; +import { useOutlet } from 'react-router-dom'; +import { DefaultDevToolsPage } from '../DefaultDevToolsPage'; + +export const DevToolsPage = () => { + const outlet = useOutlet(); + + return <>{outlet || }; +}; diff --git a/plugins/devtools/src/components/DevToolsPage/index.ts b/plugins/devtools/src/components/DevToolsPage/index.ts new file mode 100644 index 0000000000..88d8bc06fa --- /dev/null +++ b/plugins/devtools/src/components/DevToolsPage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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. + */ + +export { DevToolsPage } from './DevToolsPage'; diff --git a/plugins/devtools/src/components/index.ts b/plugins/devtools/src/components/index.ts new file mode 100644 index 0000000000..9938866bcc --- /dev/null +++ b/plugins/devtools/src/components/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2022 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. + */ + +export * from './Content'; +export * from './DevToolsLayout'; diff --git a/plugins/devtools/src/hooks/index.ts b/plugins/devtools/src/hooks/index.ts new file mode 100644 index 0000000000..bb2e83d06f --- /dev/null +++ b/plugins/devtools/src/hooks/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2022 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. + */ + +export { useConfig } from './useConfig'; +export { useExternalDependencies } from './useExternalDependencies'; +export { useInfo } from './useInfo'; diff --git a/plugins/devtools/src/hooks/useConfig.ts b/plugins/devtools/src/hooks/useConfig.ts new file mode 100644 index 0000000000..6703e7ea59 --- /dev/null +++ b/plugins/devtools/src/hooks/useConfig.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2022 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. + */ + +import { devToolsApiRef } from '../api'; +import { useApi } from '@backstage/core-plugin-api'; +import useAsync from 'react-use/lib/useAsync'; +import { ConfigInfo } from '@backstage/plugin-devtools-common'; + +export function useConfig(): { + configInfo?: ConfigInfo; + loading: boolean; + error?: Error; +} { + const api = useApi(devToolsApiRef); + const { value, loading, error } = useAsync(() => { + return api.getConfig(); + }, [api]); + + return { + configInfo: value, + loading, + error, + }; +} diff --git a/plugins/devtools/src/hooks/useExternalDependencies.ts b/plugins/devtools/src/hooks/useExternalDependencies.ts new file mode 100644 index 0000000000..df2f3e193b --- /dev/null +++ b/plugins/devtools/src/hooks/useExternalDependencies.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2022 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. + */ + +import { devToolsApiRef } from '../api'; +import { useApi } from '@backstage/core-plugin-api'; +import useAsync from 'react-use/lib/useAsync'; +import { ExternalDependency } from '@backstage/plugin-devtools-common'; + +export function useExternalDependencies(): { + externalDependencies?: ExternalDependency[]; + loading: boolean; + error?: Error; +} { + const api = useApi(devToolsApiRef); + const { value, loading, error } = useAsync(() => { + return api.getExternalDependencies(); + }, [api]); + + return { + externalDependencies: value, + loading, + error, + }; +} diff --git a/plugins/devtools/src/hooks/useInfo.ts b/plugins/devtools/src/hooks/useInfo.ts new file mode 100644 index 0000000000..ad700cb3fe --- /dev/null +++ b/plugins/devtools/src/hooks/useInfo.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2022 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. + */ + +import { devToolsApiRef } from '../api'; +import { useApi } from '@backstage/core-plugin-api'; +import useAsync from 'react-use/lib/useAsync'; +import { DevToolsInfo } from '@backstage/plugin-devtools-common'; + +export function useInfo(): { + about?: DevToolsInfo; + loading: boolean; + error?: Error; +} { + const api = useApi(devToolsApiRef); + const { value, loading, error } = useAsync(() => { + return api.getInfo(); + }, [api]); + + return { + about: value, + loading, + error, + }; +} diff --git a/plugins/devtools/src/index.ts b/plugins/devtools/src/index.ts new file mode 100644 index 0000000000..576046ca1b --- /dev/null +++ b/plugins/devtools/src/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2022 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. + */ + +export { devToolsPlugin, DevToolsPage } from './plugin'; +export * from './components'; diff --git a/plugins/devtools/src/plugin.test.ts b/plugins/devtools/src/plugin.test.ts new file mode 100644 index 0000000000..62f4ae5a61 --- /dev/null +++ b/plugins/devtools/src/plugin.test.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2022 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. + */ + +import { devToolsPlugin } from './plugin'; + +describe('devtools', () => { + it('should export plugin', () => { + expect(devToolsPlugin).toBeDefined(); + }); +}); diff --git a/plugins/devtools/src/plugin.ts b/plugins/devtools/src/plugin.ts new file mode 100644 index 0000000000..df2aca3447 --- /dev/null +++ b/plugins/devtools/src/plugin.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2022 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. + */ + +import { + createApiFactory, + createPlugin, + createRoutableExtension, + discoveryApiRef, + identityApiRef, +} from '@backstage/core-plugin-api'; +import { devToolsApiRef, DevToolsClient } from './api'; + +import { rootRouteRef } from './routes'; + +/** @public */ +export const devToolsPlugin = createPlugin({ + id: 'devtools', + apis: [ + createApiFactory({ + api: devToolsApiRef, + deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef }, + factory: ({ discoveryApi, identityApi }) => + new DevToolsClient({ discoveryApi, identityApi }), + }), + ], + routes: { + root: rootRouteRef, + }, +}); + +/** @public */ +export const DevToolsPage = devToolsPlugin.provide( + createRoutableExtension({ + name: 'DevToolsPage', + component: () => + import('./components/DevToolsPage').then(m => m.DevToolsPage), + mountPoint: rootRouteRef, + }), +); diff --git a/plugins/devtools/src/routes.ts b/plugins/devtools/src/routes.ts new file mode 100644 index 0000000000..7825f928be --- /dev/null +++ b/plugins/devtools/src/routes.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2022 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. + */ + +import { createRouteRef } from '@backstage/core-plugin-api'; + +export const rootRouteRef = createRouteRef({ + id: 'devtools', +}); diff --git a/plugins/devtools/src/setupTests.ts b/plugins/devtools/src/setupTests.ts new file mode 100644 index 0000000000..7a459ed24e --- /dev/null +++ b/plugins/devtools/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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. + */ + +import '@testing-library/jest-dom'; diff --git a/yarn.lock b/yarn.lock index 0bd97d81f8..d69acf7aab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3384,7 +3384,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.6, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.6, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": version: 7.21.0 resolution: "@babel/runtime@npm:7.21.0" dependencies: @@ -6142,6 +6142,86 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-devtools-backend@workspace:^, @backstage/plugin-devtools-backend@workspace:plugins/devtools-backend": + version: 0.0.0-use.local + resolution: "@backstage/plugin-devtools-backend@workspace:plugins/devtools-backend" + dependencies: + "@backstage/backend-common": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/cli-common": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/config-loader": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/plugin-auth-node": "workspace:^" + "@backstage/plugin-devtools-common": "workspace:^" + "@backstage/plugin-permission-common": "workspace:^" + "@backstage/plugin-permission-node": "workspace:^" + "@backstage/types": "workspace:^" + "@manypkg/get-packages": ^1.1.3 + "@types/express": "*" + "@types/minimist": ^1.2.0 + "@types/ping": ^0.4.1 + "@types/supertest": ^2.0.8 + "@types/yarnpkg__lockfile": ^1.1.4 + "@yarnpkg/lockfile": ^1.1.0 + "@yarnpkg/parsers": ^3.0.0-rc.4 + express: ^4.18.1 + express-promise-router: ^4.1.0 + fs-extra: ^10.0.0 + lodash: ^4.17.21 + msw: ^0.47.0 + node-fetch: ^2.6.7 + ping: ^0.4.1 + semver: ^7.3.2 + supertest: ^6.2.4 + winston: ^3.2.1 + yn: ^4.0.0 + languageName: unknown + linkType: soft + +"@backstage/plugin-devtools-common@workspace:^, @backstage/plugin-devtools-common@workspace:plugins/devtools-common": + version: 0.0.0-use.local + resolution: "@backstage/plugin-devtools-common@workspace:plugins/devtools-common" + dependencies: + "@backstage/cli": "workspace:^" + "@backstage/plugin-permission-common": "workspace:^" + "@backstage/types": "workspace:^" + languageName: unknown + linkType: soft + +"@backstage/plugin-devtools@workspace:^, @backstage/plugin-devtools@workspace:plugins/devtools": + version: 0.0.0-use.local + resolution: "@backstage/plugin-devtools@workspace:plugins/devtools" + dependencies: + "@backstage/cli": "workspace:^" + "@backstage/core-app-api": "workspace:^" + "@backstage/core-components": "workspace:^" + "@backstage/core-plugin-api": "workspace:^" + "@backstage/dev-utils": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/plugin-devtools-common": "workspace:^" + "@backstage/plugin-permission-react": "workspace:^" + "@backstage/test-utils": "workspace:^" + "@backstage/theme": "workspace:^" + "@backstage/types": "workspace:^" + "@material-ui/core": ^4.9.13 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": ^4.0.0-alpha.57 + "@testing-library/jest-dom": ^5.10.1 + "@testing-library/react": ^12.1.3 + "@testing-library/user-event": ^14.0.0 + "@types/node": "*" + cross-fetch: ^3.1.5 + msw: ^0.47.0 + react-json-view: ^1.21.3 + react-use: ^17.2.4 + peerDependencies: + "@types/react": ^16.13.1 || ^17.0.0 + react: ^16.13.1 || ^17.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + languageName: unknown + linkType: soft + "@backstage/plugin-dynatrace@workspace:^, @backstage/plugin-dynatrace@workspace:plugins/dynatrace": version: 0.0.0-use.local resolution: "@backstage/plugin-dynatrace@workspace:plugins/dynatrace" @@ -16383,6 +16463,13 @@ __metadata: languageName: node linkType: hard +"@types/ping@npm:^0.4.1": + version: 0.4.1 + resolution: "@types/ping@npm:0.4.1" + checksum: 9b94837fe66df70558c5a42b0e0c8371b4950ab56b96c42c8df809ff2cf52477dd0a7e01d2e6b38af8bb6683b3dcb54587960b96b4b1f3d40fdb529aea348ad0 + languageName: node + linkType: hard + "@types/pluralize@npm:^0.0.29": version: 0.0.29 resolution: "@types/pluralize@npm:0.0.29" @@ -18784,6 +18871,13 @@ __metadata: languageName: node linkType: hard +"base16@npm:^1.0.0": + version: 1.0.0 + resolution: "base16@npm:1.0.0" + checksum: 0cd449a2db0f0f957e4b6b57e33bc43c9e20d4f1dd744065db94b5da35e8e71fa4dc4bc7a901e59a84d5f8b6936e3c520e2471787f667fc155fb0f50d8540f5d + languageName: node + linkType: hard + "base64-js@npm:^1.0.2, base64-js@npm:^1.3.0, base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" @@ -20882,7 +20976,7 @@ __metadata: languageName: node linkType: hard -"cross-fetch@npm:3.1.5, cross-fetch@npm:^3.0.4, cross-fetch@npm:^3.1.3, cross-fetch@npm:^3.1.5": +"cross-fetch@npm:3.1.5, cross-fetch@npm:^3.1.3, cross-fetch@npm:^3.1.5": version: 3.1.5 resolution: "cross-fetch@npm:3.1.5" dependencies: @@ -23618,6 +23712,7 @@ __metadata: "@backstage/plugin-cloudbuild": "workspace:^" "@backstage/plugin-code-coverage": "workspace:^" "@backstage/plugin-cost-insights": "workspace:^" + "@backstage/plugin-devtools": "workspace:^" "@backstage/plugin-dynatrace": "workspace:^" "@backstage/plugin-entity-feedback": "workspace:^" "@backstage/plugin-explore": "workspace:^" @@ -23741,6 +23836,7 @@ __metadata: "@backstage/plugin-catalog-backend": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-code-coverage-backend": "workspace:^" + "@backstage/plugin-devtools-backend": "workspace:^" "@backstage/plugin-entity-feedback-backend": "workspace:^" "@backstage/plugin-events-backend": "workspace:^" "@backstage/plugin-events-node": "workspace:^" @@ -24222,6 +24318,15 @@ __metadata: languageName: node linkType: hard +"fbemitter@npm:^3.0.0": + version: 3.0.0 + resolution: "fbemitter@npm:3.0.0" + dependencies: + fbjs: ^3.0.0 + checksum: 069690b8cdff3521ade3c9beb92ba0a38d818a86ef36dff8690e66749aef58809db4ac0d6938eb1cacea2dbef5f2a508952d455669590264cdc146bbe839f605 + languageName: node + linkType: hard + "fbjs-css-vars@npm:^1.0.0": version: 1.0.2 resolution: "fbjs-css-vars@npm:1.0.2" @@ -24244,18 +24349,18 @@ __metadata: languageName: node linkType: hard -"fbjs@npm:^3.0.0": - version: 3.0.0 - resolution: "fbjs@npm:3.0.0" +"fbjs@npm:^3.0.0, fbjs@npm:^3.0.1": + version: 3.0.4 + resolution: "fbjs@npm:3.0.4" dependencies: - cross-fetch: ^3.0.4 + cross-fetch: ^3.1.5 fbjs-css-vars: ^1.0.0 loose-envify: ^1.0.0 object-assign: ^4.1.0 promise: ^7.1.1 setimmediate: ^1.0.5 - ua-parser-js: ^0.7.18 - checksum: 85ec57d8dbeddd7c82bf8f111a3c7de1abc1f4d7c603d6ccbcc1ec8dce35ff5b7a113dd34acbf7930093e5533c37a2298a92d342077f967bef34dc7cf2f3f07e + ua-parser-js: ^0.7.30 + checksum: 8b23a3550fcda8a9109fca9475a3416590c18bb6825ea884192864ed686f67fcd618e308a140c9e5444fbd0168732e1ff3c092ba3d0c0ae1768969f32ba280c7 languageName: node linkType: hard @@ -24499,6 +24604,18 @@ __metadata: languageName: node linkType: hard +"flux@npm:^4.0.1": + version: 4.0.4 + resolution: "flux@npm:4.0.4" + dependencies: + fbemitter: ^3.0.0 + fbjs: ^3.0.1 + peerDependencies: + react: ^15.0.2 || ^16.0.0 || ^17.0.0 + checksum: 8fa5c2f9322258de3e331f67c6f1078a7f91c4dec9dbe8a54c4b8a80eed19a4f91889028b768668af4a796e8f2ee75e461e1571b8615432a3920ae95cc4ff794 + languageName: node + linkType: hard + "fn.name@npm:1.x.x": version: 1.1.0 resolution: "fn.name@npm:1.1.0" @@ -29439,6 +29556,13 @@ __metadata: languageName: node linkType: hard +"lodash.curry@npm:^4.0.1": + version: 4.1.1 + resolution: "lodash.curry@npm:4.1.1" + checksum: 9192b70fe7df4d1ff780c0260bee271afa9168c93fe4fa24bc861900240531b59781b5fdaadf4644fea8f4fbcd96f0700539ab294b579ffc1022c6c15dcc462a + languageName: node + linkType: hard + "lodash.debounce@npm:^4, lodash.debounce@npm:^4.0.8": version: 4.0.8 resolution: "lodash.debounce@npm:4.0.8" @@ -29474,6 +29598,13 @@ __metadata: languageName: node linkType: hard +"lodash.flow@npm:^3.3.0": + version: 3.5.0 + resolution: "lodash.flow@npm:3.5.0" + checksum: a9a62ad344e3c5a1f42bc121da20f64dd855aaafecee24b1db640f29b88bd165d81c37ff7e380a7191de6f70b26f5918abcebbee8396624f78f3618a0b18634c + languageName: node + linkType: hard + "lodash.get@npm:^4.4.2": version: 4.4.2 resolution: "lodash.get@npm:4.4.2" @@ -31083,6 +31214,41 @@ __metadata: languageName: node linkType: hard +"msw@npm:^0.47.0": + version: 0.47.4 + resolution: "msw@npm:0.47.4" + dependencies: + "@mswjs/cookies": ^0.2.2 + "@mswjs/interceptors": ^0.17.5 + "@open-draft/until": ^1.0.3 + "@types/cookie": ^0.4.1 + "@types/js-levenshtein": ^1.1.1 + chalk: 4.1.1 + chokidar: ^3.4.2 + cookie: ^0.4.2 + graphql: ^15.0.0 || ^16.0.0 + headers-polyfill: ^3.1.0 + inquirer: ^8.2.0 + is-node-process: ^1.0.1 + js-levenshtein: ^1.1.6 + node-fetch: ^2.6.7 + outvariant: ^1.3.0 + path-to-regexp: ^6.2.0 + statuses: ^2.0.0 + strict-event-emitter: ^0.2.6 + type-fest: ^2.19.0 + yargs: ^17.3.1 + peerDependencies: + typescript: ">= 4.2.x <= 4.8.x" + peerDependenciesMeta: + typescript: + optional: true + bin: + msw: cli/index.js + checksum: 10ff632641d40384d6622abf4df6399e4ae649db0f676b5d1ee2d0a515ec96f33abe9d4fecba08cdba4b2e43255af419da9eefc020d40a7e10669d0906457197 + languageName: node + linkType: hard + "msw@npm:^0.49.0": version: 0.49.3 resolution: "msw@npm:0.49.3" @@ -33154,6 +33320,13 @@ __metadata: languageName: node linkType: hard +"ping@npm:^0.4.1": + version: 0.4.4 + resolution: "ping@npm:0.4.4" + checksum: cab10af309312e3a6822eccb7f323f0c9a1e17ef5895954114e31b6a1cced5e2ced3563990b79c66d074372e75aa4c846d9001120ee296b365de6a2adef5b8f9 + languageName: node + linkType: hard + "pinkie-promise@npm:^2.0.0": version: 2.0.1 resolution: "pinkie-promise@npm:2.0.1" @@ -34242,6 +34415,13 @@ __metadata: languageName: node linkType: hard +"pure-color@npm:^1.2.0": + version: 1.3.0 + resolution: "pure-color@npm:1.3.0" + checksum: 646d8bed6e6eab89affdd5e2c11f607a85b631a7fb03c061dfa658eb4dc4806881a15feed2ac5fd8c0bad8c00c632c640d5b1cb8b9a972e6e947393a1329371b + languageName: node + linkType: hard + "pvtsutils@npm:^1.3.2": version: 1.3.2 resolution: "pvtsutils@npm:1.3.2" @@ -34474,6 +34654,18 @@ __metadata: languageName: node linkType: hard +"react-base16-styling@npm:^0.6.0": + version: 0.6.0 + resolution: "react-base16-styling@npm:0.6.0" + dependencies: + base16: ^1.0.0 + lodash.curry: ^4.0.1 + lodash.flow: ^3.3.0 + pure-color: ^1.2.0 + checksum: 00a12dddafc8a9025cca933b0dcb65fca41c81fa176d1fc3a6a9d0242127042e2c0a604f4c724a3254dd2c6aeb5ef55095522ff22f5462e419641c1341a658e4 + languageName: node + linkType: hard + "react-beautiful-dnd@npm:^13.0.0": version: 13.0.0 resolution: "react-beautiful-dnd@npm:13.0.0" @@ -34743,6 +34935,21 @@ __metadata: languageName: node linkType: hard +"react-json-view@npm:^1.21.3": + version: 1.21.3 + resolution: "react-json-view@npm:1.21.3" + dependencies: + flux: ^4.0.1 + react-base16-styling: ^0.6.0 + react-lifecycles-compat: ^3.0.4 + react-textarea-autosize: ^8.3.2 + peerDependencies: + react: ^17.0.0 || ^16.3.0 || ^15.5.4 + react-dom: ^17.0.0 || ^16.3.0 || ^15.5.4 + checksum: 5718bcd9210ad5b06eb9469cf8b9b44be9498845a7702e621343618e8251f26357e6e1c865532cf170db6165df1cb30202787e057309d8848c220bc600ec0d1a + languageName: node + linkType: hard + "react-lifecycles-compat@npm:^3.0.2, react-lifecycles-compat@npm:^3.0.4": version: 3.0.4 resolution: "react-lifecycles-compat@npm:3.0.4" @@ -35010,6 +35217,19 @@ __metadata: languageName: node linkType: hard +"react-textarea-autosize@npm:^8.3.2": + version: 8.4.1 + resolution: "react-textarea-autosize@npm:8.4.1" + dependencies: + "@babel/runtime": ^7.20.13 + use-composed-ref: ^1.3.0 + use-latest: ^1.2.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: b200437cd68938c23b13944fe6fdfeb32a6d949ac88588307f14d6fcdaba3044b8c7d8e239851b081f2101d433b93d4cf5aa027543b170b84f2a0cbe6fc9093f + languageName: node + linkType: hard + "react-transition-group@npm:2.9.0, react-transition-group@npm:^2.2.1": version: 2.9.0 resolution: "react-transition-group@npm:2.9.0" @@ -37374,7 +37594,7 @@ __metadata: languageName: node linkType: hard -"statuses@npm:2.0.1": +"statuses@npm:2.0.1, statuses@npm:^2.0.0": version: 2.0.1 resolution: "statuses@npm:2.0.1" checksum: 18c7623fdb8f646fb213ca4051be4df7efb3484d4ab662937ca6fbef7ced9b9e12842709872eb3020cc3504b93bde88935c9f6417489627a7786f24f8031cbcb @@ -37476,12 +37696,12 @@ __metadata: languageName: node linkType: hard -"strict-event-emitter@npm:^0.2.4": - version: 0.2.7 - resolution: "strict-event-emitter@npm:0.2.7" +"strict-event-emitter@npm:^0.2.4, strict-event-emitter@npm:^0.2.6": + version: 0.2.8 + resolution: "strict-event-emitter@npm:0.2.8" dependencies: events: ^3.3.0 - checksum: 111691e7d3fce0810586ccd8e8234af883ad3b121ef69091c7e260c32299d1ba085a95238ad09b43478bc5e9e80370f2fcb8114716e343be6f44bfc08fab4142 + checksum: 6ac06fe72a6ee6ae64d20f1dd42838ea67342f1b5f32b03b3050d73ee6ecee44b4d5c4ed2965a7154b47991e215f373d4e789e2b2be2769cd80e356126c2ca53 languageName: node linkType: hard @@ -39046,7 +39266,7 @@ __metadata: languageName: node linkType: hard -"ua-parser-js@npm:^0.7.18, ua-parser-js@npm:^0.7.30": +"ua-parser-js@npm:^0.7.30": version: 0.7.33 resolution: "ua-parser-js@npm:0.7.33" checksum: 1510e9ec26fcaf0d8c6ae8f1078a8230e8816f083e1b5f453ea19d06b8ef2b8a596601c92148fd41899e8b3e5f83fa69c42332bd5729b931a721040339831696 @@ -39446,6 +39666,15 @@ __metadata: languageName: node linkType: hard +"use-composed-ref@npm:^1.3.0": + version: 1.3.0 + resolution: "use-composed-ref@npm:1.3.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: f771cbadfdc91e03b7ab9eb32d0fc0cc647755711801bf507e891ad38c4bbc5f02b2509acadf9c965ec9c5f2f642fd33bdfdfb17b0873c4ad0a9b1f5e5e724bf + languageName: node + linkType: hard + "use-deep-compare-effect@npm:^1.8.1": version: 1.8.1 resolution: "use-deep-compare-effect@npm:1.8.1" @@ -39468,6 +39697,32 @@ __metadata: languageName: node linkType: hard +"use-isomorphic-layout-effect@npm:^1.1.1": + version: 1.1.2 + resolution: "use-isomorphic-layout-effect@npm:1.1.2" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: a6532f7fc9ae222c3725ff0308aaf1f1ddbd3c00d685ef9eee6714fd0684de5cb9741b432fbf51e61a784e2955424864f7ea9f99734a02f237b17ad3e18ea5cb + languageName: node + linkType: hard + +"use-latest@npm:^1.2.1": + version: 1.2.1 + resolution: "use-latest@npm:1.2.1" + dependencies: + use-isomorphic-layout-effect: ^1.1.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: ed3f2ddddf6f21825e2ede4c2e0f0db8dcce5129802b69d1f0575fc1b42380436e8c76a6cd885d4e9aa8e292e60fb8b959c955f33c6a9123b83814a1a1875367 + languageName: node + linkType: hard + "use-memo-one@npm:^1.1.1": version: 1.1.1 resolution: "use-memo-one@npm:1.1.1" From d29f5916408a4ddd00d475fb419b5bfa84105eec Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Thu, 20 Apr 2023 11:29:06 -0500 Subject: [PATCH 012/189] Updated based on feedback Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- plugins/devtools-backend/api-report.md | 2 -- plugins/devtools-backend/src/api/DevToolsBackendApi.ts | 6 ++++-- plugins/devtools-backend/src/index.ts | 6 ++++++ plugins/devtools-common/api-report.md | 2 -- plugins/devtools-common/src/index.ts | 6 ++++++ plugins/devtools/api-report.md | 2 -- plugins/devtools/src/index.ts | 6 ++++++ 7 files changed, 22 insertions(+), 8 deletions(-) diff --git a/plugins/devtools-backend/api-report.md b/plugins/devtools-backend/api-report.md index adf2189512..e28f0697ab 100644 --- a/plugins/devtools-backend/api-report.md +++ b/plugins/devtools-backend/api-report.md @@ -36,6 +36,4 @@ export interface RouterOptions { // (undocumented) permissions: PermissionEvaluator; } - -// (No @packageDocumentation comment for this package) ``` diff --git a/plugins/devtools-backend/src/api/DevToolsBackendApi.ts b/plugins/devtools-backend/src/api/DevToolsBackendApi.ts index 88bd51fa7e..efd0407b31 100644 --- a/plugins/devtools-backend/src/api/DevToolsBackendApi.ts +++ b/plugins/devtools-backend/src/api/DevToolsBackendApi.ts @@ -90,7 +90,7 @@ export class DevToolsBackendApi { res.status === 200 ? ExternalDependencyStatus.healthy : ExternalDependencyStatus.unhealthy; - this.logger.info( + this.logger.debug( `Fetch for ${endpoint.name} resulted in status code "${res.status}"`, ); }) @@ -129,7 +129,9 @@ export class DevToolsBackendApi { : pingResult.output; } - this.logger.info(`Ping results for ${endpoint.name}: ${pingResult.output}`); + this.logger.debug( + `Ping results for ${endpoint.name}: ${pingResult.output}`, + ); const result: ExternalDependency = { name: endpoint.name, diff --git a/plugins/devtools-backend/src/index.ts b/plugins/devtools-backend/src/index.ts index 134b797761..e1545e0022 100644 --- a/plugins/devtools-backend/src/index.ts +++ b/plugins/devtools-backend/src/index.ts @@ -14,5 +14,11 @@ * limitations under the License. */ +/** + * Backstage DevTools backend plugin that contains the API for retrieving package dependencies and current configuration which is used by the Backstage DevTools frontend plugin. + * + * @packageDocumentation + */ + export { DevToolsBackendApi } from './api'; export * from './service/router'; diff --git a/plugins/devtools-common/api-report.md b/plugins/devtools-common/api-report.md index dae445abde..7e38ff05b0 100644 --- a/plugins/devtools-common/api-report.md +++ b/plugins/devtools-common/api-report.md @@ -69,6 +69,4 @@ export type PackageDependency = { name: string; versions: string; }; - -// (No @packageDocumentation comment for this package) ``` diff --git a/plugins/devtools-common/src/index.ts b/plugins/devtools-common/src/index.ts index ca1cec9ac1..be7426394b 100644 --- a/plugins/devtools-common/src/index.ts +++ b/plugins/devtools-common/src/index.ts @@ -14,5 +14,11 @@ * limitations under the License. */ +/** + * Common types and permissions for the DevTools frontend and backend plugins. + * + * @packageDocumentation + */ + export * from './types'; export * from './permissions'; diff --git a/plugins/devtools/api-report.md b/plugins/devtools/api-report.md index 3933d50e76..c9cfbf77e5 100644 --- a/plugins/devtools/api-report.md +++ b/plugins/devtools/api-report.md @@ -54,6 +54,4 @@ export type SubRoute = { } >; }; - -// (No @packageDocumentation comment for this package) ``` diff --git a/plugins/devtools/src/index.ts b/plugins/devtools/src/index.ts index 576046ca1b..1077a2518c 100644 --- a/plugins/devtools/src/index.ts +++ b/plugins/devtools/src/index.ts @@ -14,5 +14,11 @@ * limitations under the License. */ +/** + * Backstage DevTools frontend plugin with a tab that shows package dependencies and another tab that shows your current configuration. + * + * @packageDocumentation + */ + export { devToolsPlugin, DevToolsPage } from './plugin'; export * from './components'; From 155cfab1af94687d43beec2d89c4f564005297b2 Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Thu, 20 Apr 2023 11:53:49 -0500 Subject: [PATCH 013/189] Improved README based on feedback Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- plugins/devtools/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/devtools/README.md b/plugins/devtools/README.md index d20142a746..fc9f1f7312 100644 --- a/plugins/devtools/README.md +++ b/plugins/devtools/README.md @@ -1,6 +1,6 @@ # DevTools -Welcome to the DevTools plugin! +Welcome to the DevTools plugin! This plugin helps elevate useful information about a Backstage instance through the frontend, which can be helpful for an integrator for troubleshooting, reviewing, and understanding their installation. ## Features @@ -24,7 +24,7 @@ The DevTools plugin can be setup with other tabs with additional helpful feature ### External Dependencies -Lists the status of configured External Dependencies based on your current running Backstage instance's ability to reach them +Lists the status of configured External Dependencies based on your current running Backstage instance's ability to reach them. ![Example of external dependencies tab](./docs/devtools-external-dependencies.png) From 2b663945cb6b12e654e6d3a5c0081713f0be86c8 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Tue, 2 May 2023 19:39:03 -0400 Subject: [PATCH 014/189] fix: Minor product capitalization Signed-off-by: Adam Harvey --- plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx | 2 +- plugins/techdocs/src/reader/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx b/plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx index 52511bda19..a6192c13ee 100644 --- a/plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx +++ b/plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx @@ -29,7 +29,7 @@ export type TechDocsPageWrapperProps = { }; /** - * Component wrapping a techdocs page with Page and Header components + * Component wrapping a TechDocs page with Page and Header components * * @public */ diff --git a/plugins/techdocs/src/reader/README.md b/plugins/techdocs/src/reader/README.md index fe1d1ff488..58697298c6 100644 --- a/plugins/techdocs/src/reader/README.md +++ b/plugins/techdocs/src/reader/README.md @@ -2,7 +2,7 @@ The TechDocs reader is a component that fetches a remote page, runs transformers on it and renders it into a shadow dom. -Currently there's no easy way to customize which transformers to run or add new ones. If that is needed you would have to fork the techdocs plugin and make your changes in that fork. +Currently there's no easy way to customize which transformers to run or add new ones. If that is needed you would have to fork the TechDocs plugin and make your changes in that fork. Transformers are functions that optionally takes in parameters from the Reader.tsx component and returns a function which gets passed the DOM of the fetched page. A very simple transformer can look like this. From a606cae041c30ca76fc1b5f50d2c6c7693935884 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Tue, 2 May 2023 19:39:47 -0400 Subject: [PATCH 015/189] chore: Remove deprecations and use shared react imports Signed-off-by: Adam Harvey --- plugins/techdocs/src/client.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/techdocs/src/client.ts b/plugins/techdocs/src/client.ts index 499a56580c..8680e14468 100644 --- a/plugins/techdocs/src/client.ts +++ b/plugins/techdocs/src/client.ts @@ -23,11 +23,13 @@ import { } from '@backstage/core-plugin-api'; import { NotFoundError, ResponseError } from '@backstage/errors'; import { + SyncResult, + TechDocsApi, TechDocsEntityMetadata, TechDocsMetadata, + TechDocsStorageApi, } from '@backstage/plugin-techdocs-react'; import { EventSourcePolyfill } from 'event-source-polyfill'; -import { SyncResult, TechDocsApi, TechDocsStorageApi } from './api'; /** * API to talk to `techdocs-backend`. @@ -189,7 +191,7 @@ export class TechDocsStorageClient implements TechDocsStorageApi { * @param entityId - Object containing entity data like name, namespace, etc. * @param logHandler - Callback to receive log messages from the build process * @returns Whether documents are currently synchronized to newest version - * @throws Throws error on error from sync endpoint in Techdocs Backend + * @throws Throws error on error from sync endpoint in TechDocs Backend */ async syncEntityDocs( entityId: CompoundEntityRef, From 956d09e8ea6835ae47a35609fdad11486274b788 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Tue, 2 May 2023 19:45:41 -0400 Subject: [PATCH 016/189] chore: Add changeset Signed-off-by: Adam Harvey --- .changeset/short-seahorses-kiss.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/short-seahorses-kiss.md diff --git a/.changeset/short-seahorses-kiss.md b/.changeset/short-seahorses-kiss.md new file mode 100644 index 0000000000..0127e4c575 --- /dev/null +++ b/.changeset/short-seahorses-kiss.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Change deprecated local references to import from shared `plugin-techdocs-react` plugin From 40421e79563992d503a918703746964cabe611a5 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Tue, 2 May 2023 20:08:15 -0400 Subject: [PATCH 017/189] chore: Add updated API report Signed-off-by: Adam Harvey --- plugins/techdocs/api-report.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/techdocs/api-report.md b/plugins/techdocs/api-report.md index 6e14c52939..27af44bb13 100644 --- a/plugins/techdocs/api-report.md +++ b/plugins/techdocs/api-report.md @@ -20,11 +20,14 @@ import { ReactNode } from 'react'; import { ResultHighlight } from '@backstage/plugin-search-common'; import { RouteRef } from '@backstage/core-plugin-api'; import { SearchResultListItemExtensionProps } from '@backstage/plugin-search-react'; +import { SyncResult as SyncResult_2 } from '@backstage/plugin-techdocs-react'; import { TableColumn } from '@backstage/core-components'; import { TableOptions } from '@backstage/core-components'; import { TableProps } from '@backstage/core-components'; +import { TechDocsApi as TechDocsApi_2 } from '@backstage/plugin-techdocs-react'; import { TechDocsEntityMetadata as TechDocsEntityMetadata_2 } from '@backstage/plugin-techdocs-react'; import { TechDocsMetadata as TechDocsMetadata_2 } from '@backstage/plugin-techdocs-react'; +import { TechDocsStorageApi as TechDocsStorageApi_2 } from '@backstage/plugin-techdocs-react'; import { ToolbarProps } from '@material-ui/core'; import { UserListFilterKind } from '@backstage/plugin-catalog-react'; @@ -237,7 +240,7 @@ export interface TechDocsApi { export const techdocsApiRef: ApiRef; // @public -export class TechDocsClient implements TechDocsApi { +export class TechDocsClient implements TechDocsApi_2 { constructor(options: { configApi: Config; discoveryApi: DiscoveryApi; @@ -442,7 +445,7 @@ export interface TechDocsStorageApi { export const techdocsStorageApiRef: ApiRef; // @public -export class TechDocsStorageClient implements TechDocsStorageApi { +export class TechDocsStorageClient implements TechDocsStorageApi_2 { constructor(options: { configApi: Config; discoveryApi: DiscoveryApi; @@ -471,6 +474,6 @@ export class TechDocsStorageClient implements TechDocsStorageApi { syncEntityDocs( entityId: CompoundEntityRef, logHandler?: (line: string) => void, - ): Promise; + ): Promise; } ``` From 12a345317ab19b5489b8bdaadd74e557fa398aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 3 May 2023 13:17:03 +0200 Subject: [PATCH 018/189] Slightly optimize the facets request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/honest-turkeys-learn.md | 5 +++++ .../src/service/DefaultEntitiesCatalog.test.ts | 11 +++++++++++ .../src/service/DefaultEntitiesCatalog.ts | 5 ++--- 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 .changeset/honest-turkeys-learn.md diff --git a/.changeset/honest-turkeys-learn.md b/.changeset/honest-turkeys-learn.md new file mode 100644 index 0000000000..e4e6aae5fc --- /dev/null +++ b/.changeset/honest-turkeys-learn.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Remove unnecessary join in the entity facets endpoint, exclude nulls diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts index 93a37fb04a..4f011813e6 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts @@ -1729,6 +1729,17 @@ describe('DefaultEntitiesCatalog', () => { ], }, }); + + await expect( + catalog.facets({ + facets: ['kind'], + filter: { not: { key: 'metadata.name', values: ['two'] } }, + }), + ).resolves.toEqual({ + facets: { + kind: [{ value: 'k', count: 1 }], + }, + }); }, ); diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index 5fca950cac..8affa78991 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -678,10 +678,9 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { for (const facet of request.facets) { const dbQuery = db('search') - .join('final_entities', 'search.entity_id', 'final_entities.entity_id') .where('search.key', facet.toLocaleLowerCase('en-US')) - .count('search.entity_id as count') - .select({ value: 'search.original_value' }) + .whereNotNull('search.original_value') + .select({ value: 'search.original_value', count: db.raw('count(*)') }) .groupBy('search.original_value'); if (request?.filter) { From b01684d280ff1843f3aab29a3ba531332bf58df2 Mon Sep 17 00:00:00 2001 From: Andrew Ochsner Date: Wed, 3 May 2023 16:34:33 -0500 Subject: [PATCH 019/189] use integration-aws-node credential provider Signed-off-by: Andrew Ochsner --- .../package.json | 1 + .../ElasticSearchClientWrapper.test.ts | 14 +- .../src/engines/ElasticSearchSearchEngine.ts | 214 +++++++++--------- yarn.lock | 1 + 4 files changed, 115 insertions(+), 115 deletions(-) diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index 76f3265c31..0d4f08e46c 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -40,6 +40,7 @@ "@backstage/backend-common": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", + "@backstage/integration-aws-node": "workspace:^", "@backstage/plugin-search-backend-node": "workspace:^", "@backstage/plugin-search-common": "workspace:^", "@elastic/elasticsearch": "^7.13.0", diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientWrapper.test.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientWrapper.test.ts index 620d894735..75f2e36b2b 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientWrapper.test.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientWrapper.test.ts @@ -14,17 +14,13 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/config'; import { Client as ElasticSearchClient } from '@elastic/elasticsearch'; import { Client as OpenSearchClient } from '@opensearch-project/opensearch'; import Mock from '@short.io/opensearch-mock'; import { Readable } from 'stream'; import { ElasticSearchClientWrapper } from './ElasticSearchClientWrapper'; -import { - createElasticSearchClientOptions, - ElasticSearchClientOptions, -} from './ElasticSearchSearchEngine'; +import { ElasticSearchClientOptions } from './ElasticSearchSearchEngine'; jest.mock('@elastic/elasticsearch', () => ({ ...jest.requireActual('@elastic/elasticsearch'), @@ -103,11 +99,9 @@ describe('ElasticSearchClientWrapper', () => { let esOptions: ElasticSearchClientOptions; beforeEach(async () => { - esOptions = await createElasticSearchClientOptions( - new ConfigReader({ - node: 'http://localhost:9200', - }), - ); + esOptions = { + node: 'http://localhost:9200', + }; jest.clearAllMocks(); }); diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts index a13fbaf92a..baeeffd765 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts @@ -23,7 +23,6 @@ import { } from '@backstage/plugin-search-common'; import { isEmpty, isNumber, isNaN as nan } from 'lodash'; -import { defaultProvider } from '@aws-sdk/credential-provider-node'; // V3 SDK. import { AwsSigv4Signer } from '@opensearch-project/opensearch/aws'; import { RequestSigner } from 'aws4'; import { Config } from '@backstage/config'; @@ -35,6 +34,10 @@ import { Logger } from 'winston'; import { MissingIndexError } from '@backstage/plugin-search-backend-node'; import esb from 'elastic-builder'; import { v4 as uuid } from 'uuid'; +import { + AwsCredentialsManager, + DefaultAwsCredentialsManager, +} from '@backstage/integration-aws-node'; export type { ElasticSearchClientOptions }; @@ -118,6 +121,7 @@ const DEFAULT_INDEXER_BATCH_SIZE = 1000; export class ElasticSearchSearchEngine implements SearchEngine { private readonly elasticSearchClientWrapper: ElasticSearchClientWrapper; private readonly highlightOptions: ElasticSearchHighlightConfig; + private static credentialProvider: AwsCredentialsManager; constructor( private readonly elasticSearchClientOptions: ElasticSearchClientOptions, @@ -147,8 +151,8 @@ export class ElasticSearchSearchEngine implements SearchEngine { aliasPostfix = `search`, indexPrefix = ``, } = options; - - const clientOptions = await createElasticSearchClientOptions( + this.credentialProvider = DefaultAwsCredentialsManager.fromConfig(config); + const clientOptions = await this.createElasticSearchClientOptions( config.getConfig('search.elasticsearch'), ); if (clientOptions.provider === 'elastic') { @@ -414,6 +418,108 @@ export class ElasticSearchSearchEngine implements SearchEngine { const postFix = this.aliasPostfix ? `__${this.aliasPostfix}` : ''; return `${this.indexPrefix}${type}${postFix}`; } + + private static async createElasticSearchClientOptions( + config?: Config, + ): Promise { + if (!config) { + throw new Error('No elastic search config found'); + } + const clientOptionsConfig = config.getOptionalConfig('clientOptions'); + const sslConfig = clientOptionsConfig?.getOptionalConfig('ssl'); + + if (config.getOptionalString('provider') === 'elastic') { + const authConfig = config.getConfig('auth'); + return { + provider: 'elastic', + cloud: { + id: config.getString('cloudId'), + }, + auth: { + username: authConfig.getString('username'), + password: authConfig.getString('password'), + }, + ...(sslConfig + ? { + ssl: { + rejectUnauthorized: + sslConfig?.getOptionalBoolean('rejectUnauthorized'), + }, + } + : {}), + }; + } + if (config.getOptionalString('provider') === 'aws') { + const requestSigner = new RequestSigner(config.getString('node')); + const service = + config.getOptionalString('service') ?? requestSigner.service; + if (service !== 'es' && service !== 'aoss') + throw new Error(`Unrecognized serivce type: ${service}`); + return { + provider: 'aws', + node: config.getString('node'), + ...(sslConfig + ? { + ssl: { + rejectUnauthorized: + sslConfig?.getOptionalBoolean('rejectUnauthorized'), + }, + } + : {}), + ...AwsSigv4Signer({ + region: config.getOptionalString('region') ?? requestSigner.region, // for backwards compatibility + service: service, + getCredentials: async () => { + const provider = + await this.credentialProvider.getCredentialProvider(); + return await provider.sdkCredentialProvider(); + }, + }), + }; + } + if (config.getOptionalString('provider') === 'opensearch') { + const authConfig = config.getConfig('auth'); + return { + provider: 'opensearch', + node: config.getString('node'), + auth: { + username: authConfig.getString('username'), + password: authConfig.getString('password'), + }, + ...(sslConfig + ? { + ssl: { + rejectUnauthorized: + sslConfig?.getOptionalBoolean('rejectUnauthorized'), + }, + } + : {}), + }; + } + const authConfig = config.getOptionalConfig('auth'); + const auth = + authConfig && + (authConfig.has('apiKey') + ? { + apiKey: authConfig.getString('apiKey'), + } + : { + username: authConfig.getString('username'), + password: authConfig.getString('password'), + }); + return { + node: config.getString('node'), + auth, + ...(sslConfig + ? { + ssl: { + rejectUnauthorized: + sslConfig?.getOptionalBoolean('rejectUnauthorized'), + }, + } + : {}), + }; + } } /** @@ -432,105 +538,3 @@ export function decodePageCursor(pageCursor?: string): { page: number } { export function encodePageCursor({ page }: { page: number }): string { return Buffer.from(`${page}`, 'utf-8').toString('base64'); } - -export async function createElasticSearchClientOptions( - config?: Config, -): Promise { - if (!config) { - throw new Error('No elastic search config found'); - } - const clientOptionsConfig = config.getOptionalConfig('clientOptions'); - const sslConfig = clientOptionsConfig?.getOptionalConfig('ssl'); - - if (config.getOptionalString('provider') === 'elastic') { - const authConfig = config.getConfig('auth'); - return { - provider: 'elastic', - cloud: { - id: config.getString('cloudId'), - }, - auth: { - username: authConfig.getString('username'), - password: authConfig.getString('password'), - }, - ...(sslConfig - ? { - ssl: { - rejectUnauthorized: - sslConfig?.getOptionalBoolean('rejectUnauthorized'), - }, - } - : {}), - }; - } - if (config.getOptionalString('provider') === 'aws') { - const requestSigner = new RequestSigner(config.getString('node')); - const service = - config.getOptionalString('service') ?? requestSigner.service; - if (service !== 'es' && service !== 'aoss') - throw new Error(`Unrecognized serivce type: ${service}`); - return { - provider: 'aws', - node: config.getString('node'), - ...(sslConfig - ? { - ssl: { - rejectUnauthorized: - sslConfig?.getOptionalBoolean('rejectUnauthorized'), - }, - } - : {}), - ...AwsSigv4Signer({ - region: config.getOptionalString('region') ?? requestSigner.region, // for backwards compatibility - service: service, - getCredentials: () => { - // Any other method to acquire a new Credentials object can be used. - const credentialsProvider = defaultProvider(); - return credentialsProvider(); - }, - }), - }; - } - if (config.getOptionalString('provider') === 'opensearch') { - const authConfig = config.getConfig('auth'); - return { - provider: 'opensearch', - node: config.getString('node'), - auth: { - username: authConfig.getString('username'), - password: authConfig.getString('password'), - }, - ...(sslConfig - ? { - ssl: { - rejectUnauthorized: - sslConfig?.getOptionalBoolean('rejectUnauthorized'), - }, - } - : {}), - }; - } - const authConfig = config.getOptionalConfig('auth'); - const auth = - authConfig && - (authConfig.has('apiKey') - ? { - apiKey: authConfig.getString('apiKey'), - } - : { - username: authConfig.getString('username'), - password: authConfig.getString('password'), - }); - return { - node: config.getString('node'), - auth, - ...(sslConfig - ? { - ssl: { - rejectUnauthorized: - sslConfig?.getOptionalBoolean('rejectUnauthorized'), - }, - } - : {}), - }; -} diff --git a/yarn.lock b/yarn.lock index fbc069a7e0..d9de87d0d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8622,6 +8622,7 @@ __metadata: "@backstage/backend-plugin-api": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" + "@backstage/integration-aws-node": "workspace:^" "@backstage/plugin-search-backend-node": "workspace:^" "@backstage/plugin-search-common": "workspace:^" "@elastic/elasticsearch": ^7.13.0 From 906dc9223c8897f5e5f877f87a528c2a327ef739 Mon Sep 17 00:00:00 2001 From: Zander Franks Date: Wed, 3 May 2023 17:35:16 -0500 Subject: [PATCH 020/189] feat(scaffolder): support text outputs from templates Signed-off-by: Zander Franks --- .../src/Template.v1beta3.schema.json | 27 +++++++++ plugins/scaffolder-react/src/api/types.ts | 8 +++ .../DefaultTemplateOutputs.tsx | 52 +++++++++++++---- .../TemplateOutputs/TextOutputs.tsx | 58 +++++++++++++++++++ 4 files changed, 135 insertions(+), 10 deletions(-) create mode 100644 plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx diff --git a/plugins/scaffolder-common/src/Template.v1beta3.schema.json b/plugins/scaffolder-common/src/Template.v1beta3.schema.json index beb0ca5e31..beb86f43cb 100644 --- a/plugins/scaffolder-common/src/Template.v1beta3.schema.json +++ b/plugins/scaffolder-common/src/Template.v1beta3.schema.json @@ -218,6 +218,33 @@ } } } + }, + "text": { + "type": "array", + "description": "A list of text blobs, like output data from the template.", + "items": { + "type": "object", + "required": [], + "properties": { + "title": { + "type": "string", + "description": "A user friendly display name for the text blob.", + "examples": ["Output Data"], + "minLength": 1 + }, + "icon": { + "type": "string", + "description": "A key representing a visual icon to be displayed in the UI.", + "examples": ["dashboard"], + "minLength": 1 + }, + "data": { + "type": "string", + "description": "The text blob to display in the UI.", + "examples": ["{}"] + } + } + } } }, "additionalProperties": { diff --git a/plugins/scaffolder-react/src/api/types.ts b/plugins/scaffolder-react/src/api/types.ts index b39555c14f..64bb0d05ac 100644 --- a/plugins/scaffolder-react/src/api/types.ts +++ b/plugins/scaffolder-react/src/api/types.ts @@ -84,9 +84,17 @@ export type ScaffolderOutputLink = { entityRef?: string; }; +/** @public */ +export type ScaffolderOutputText = { + title?: string; + icon?: string; + data?: string; +}; + /** @public */ export type ScaffolderTaskOutput = { links?: ScaffolderOutputLink[]; + text?: ScaffolderOutputText[]; } & { [key: string]: unknown; }; diff --git a/plugins/scaffolder-react/src/next/components/TemplateOutputs/DefaultTemplateOutputs.tsx b/plugins/scaffolder-react/src/next/components/TemplateOutputs/DefaultTemplateOutputs.tsx index 4d908461b0..de52b62700 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateOutputs/DefaultTemplateOutputs.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateOutputs/DefaultTemplateOutputs.tsx @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; -import { Box, Paper } from '@material-ui/core'; -import { LinkOutputs } from './LinkOutputs'; +import { InfoCard, MarkdownContent } from '@backstage/core-components'; import { ScaffolderTaskOutput } from '@backstage/plugin-scaffolder-react'; +import { Box, Paper } from '@material-ui/core'; +import React, { useMemo, useState } from 'react'; +import { LinkOutputs } from './LinkOutputs'; +import { TextOutputs } from './TextOutputs'; /** * The DefaultOutputs renderer for the scaffolder task output @@ -26,17 +28,47 @@ import { ScaffolderTaskOutput } from '@backstage/plugin-scaffolder-react'; export const DefaultTemplateOutputs = (props: { output?: ScaffolderTaskOutput; }) => { - if (!props.output?.links) { + const [textOutputIndex, setTextOutputIndex] = useState(); + + const textOutput = useMemo( + () => + textOutputIndex !== undefined + ? props.output?.text?.[textOutputIndex] + : null, + [props.output, textOutputIndex], + ); + + if (!props.output) { return null; } return ( - - - - + <> + + + + + + + + + {textOutput ? ( + + + + + + - - + ) : null} + ); }; diff --git a/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx b/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx new file mode 100644 index 0000000000..8600a281a0 --- /dev/null +++ b/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx @@ -0,0 +1,58 @@ +/* + * Copyright 2023 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. + */ +import { IconComponent, useApp } from '@backstage/core-plugin-api'; +import { Button } from '@material-ui/core'; +import WebIcon from '@material-ui/icons/Web'; +import React from 'react'; +import { ScaffolderTaskOutput } from '../../../api'; + +export const TextOutputs = (props: { + output: ScaffolderTaskOutput; + index?: number; + setIndex?: (index: number | undefined) => void; +}) => { + const { + output: { text = [] }, + index, + setIndex, + } = props; + + const app = useApp(); + + const iconResolver = (key?: string): IconComponent => + app.getSystemIcon(key!) ?? WebIcon; + + return ( + <> + {text + .filter(({ data }) => data) + .map(({ title, icon }, i) => { + const Icon = iconResolver(icon); + return ( + + ); + })} + + ); +}; From 82e10a6939c2a9fbf641393598972d30d634f57d Mon Sep 17 00:00:00 2001 From: Zander Franks Date: Wed, 3 May 2023 17:42:07 -0500 Subject: [PATCH 021/189] chore: add changesets Signed-off-by: Zander Franks --- .changeset/heavy-penguins-burn.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/heavy-penguins-burn.md diff --git a/.changeset/heavy-penguins-burn.md b/.changeset/heavy-penguins-burn.md new file mode 100644 index 0000000000..f9fba9bfb8 --- /dev/null +++ b/.changeset/heavy-penguins-burn.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-common': patch +'@backstage/plugin-scaffolder-react': patch +--- + +Add support for Markdown text blob outputs from templates From d2727a2eb0024e9e9c7d8bffa0edee1517b1fefc Mon Sep 17 00:00:00 2001 From: Zander Franks Date: Wed, 3 May 2023 17:57:32 -0500 Subject: [PATCH 022/189] chore: add docs Signed-off-by: Zander Franks --- docs/features/software-templates/writing-templates.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index 15e6a833cc..da8e54875b 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -510,10 +510,8 @@ take a look at, or you can Each individual step can output some variables that can be used in the scaffolder frontend for after the job is finished. This is useful for things -like linking to the entity that has been created with the backend, and also -linking to the created repository. - -The main two that are used are the following: +like linking to the entity that has been created with the backend, linking +to the created repository, or showing Markdown text blobs. ```yaml output: @@ -523,6 +521,10 @@ output: - title: Open in catalog icon: catalog entityRef: ${{ steps['register'].output.entityRef }} # link to the entity that has been ingested to the catalog + text: + - title: More data + data: | + Access the [remote repository](${{ steps['publish'].output.remoteUrl }}). ``` ## The templating syntax From 8c444f37d8156350673c9a670416a5ca556469de Mon Sep 17 00:00:00 2001 From: Zander Franks Date: Wed, 3 May 2023 18:19:09 -0500 Subject: [PATCH 023/189] chore: scaffolder api report Signed-off-by: Zander Franks --- plugins/scaffolder-react/api-report.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/scaffolder-react/api-report.md b/plugins/scaffolder-react/api-report.md index baf5afb312..a2a7dca3b2 100644 --- a/plugins/scaffolder-react/api-report.md +++ b/plugins/scaffolder-react/api-report.md @@ -217,6 +217,13 @@ export type ScaffolderOutputLink = { entityRef?: string; }; +// @public (undocumented) +export type ScaffolderOutputText = { + title?: string; + icon?: string; + data?: string; +}; + // @public export interface ScaffolderScaffoldOptions { // (undocumented) @@ -261,6 +268,7 @@ export type ScaffolderTask = { // @public (undocumented) export type ScaffolderTaskOutput = { links?: ScaffolderOutputLink[]; + text?: ScaffolderOutputText[]; } & { [key: string]: unknown; }; From ebf3c0e7a1f7249063d2cd7f2924c0ed901885aa Mon Sep 17 00:00:00 2001 From: Zander Franks Date: Wed, 3 May 2023 18:39:09 -0500 Subject: [PATCH 024/189] chore: fix docs Markdown example link Signed-off-by: Zander Franks --- docs/features/software-templates/writing-templates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index da8e54875b..2508f4a592 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -524,7 +524,7 @@ output: text: - title: More data data: | - Access the [remote repository](${{ steps['publish'].output.remoteUrl }}). + Access the \[remote repository\](${{ steps['publish'].output.remoteUrl }}). ``` ## The templating syntax From 877df261085b2047a5211cfab86bcc59756b6ffa Mon Sep 17 00:00:00 2001 From: Sebastian Paredero Date: Thu, 4 May 2023 10:59:17 -0300 Subject: [PATCH 025/189] feat(plugins/azure-devops): support for multiple builds-definitions Signed-off-by: Sebastian Paredero --- .changeset/quiet-stingrays-fly.md | 5 +++++ plugins/azure-devops/README.md | 2 +- .../azure-devops/src/api/AzureDevOpsClient.ts | 18 +++++++++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 .changeset/quiet-stingrays-fly.md diff --git a/.changeset/quiet-stingrays-fly.md b/.changeset/quiet-stingrays-fly.md new file mode 100644 index 0000000000..536ee18a6f --- /dev/null +++ b/.changeset/quiet-stingrays-fly.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-devops': minor +--- + +The getBuildRuns function now checks contains multiple comma-separated builds and splits them to send multiple requests for each and concatenates the results. diff --git a/plugins/azure-devops/README.md b/plugins/azure-devops/README.md index 5079fa4f16..7eeae09813 100644 --- a/plugins/azure-devops/README.md +++ b/plugins/azure-devops/README.md @@ -70,7 +70,7 @@ dev.azure.com/project: dev.azure.com/build-definition: ``` -In this case `` will be the name of your Team Project and `` will be the name of the Build Definition you would like to see Builds for. If the Build Definition name has spaces in it make sure to put quotes around it +In this case `` will be the name of your Team Project and `` will be the name of the Build Definition you would like to see Builds for, and it's possible to add more Builds separated by a comma. If the Build Definition name has spaces in it make sure to put quotes around it. ### Azure Pipelines Component diff --git a/plugins/azure-devops/src/api/AzureDevOpsClient.ts b/plugins/azure-devops/src/api/AzureDevOpsClient.ts index 68d8896538..a202008798 100644 --- a/plugins/azure-devops/src/api/AzureDevOpsClient.ts +++ b/plugins/azure-devops/src/api/AzureDevOpsClient.ts @@ -120,7 +120,23 @@ export class AzureDevOpsClient implements AzureDevOpsApi { queryString.append('repoName', repoName); } if (definitionName) { - queryString.append('definitionName', definitionName); + const definitionNames = definitionName.split(','); + if (definitionNames.length > 1) { + const buildRuns: BuildRun[] = []; + for (const name of definitionNames) { + queryString.set('definitionName', name.trim()); + if (options?.top) { + queryString.set('top', options.top.toString()); + } + const urlSegment = `builds/${encodeURIComponent( + projectName, + )}?${queryString}`; + const items = await this.get(urlSegment); + buildRuns.push(...items); + } + return { items: buildRuns }; + } + queryString.append('definitionName', definitionName.trim()); } if (options?.top) { queryString.append('top', options.top.toString()); From 970678adbe2d7b40cf8321741d6a7262ddd8f1e9 Mon Sep 17 00:00:00 2001 From: Phil Kuang Date: Wed, 19 Apr 2023 14:21:56 -0400 Subject: [PATCH 026/189] feat(GithubMultiOrgEntityProvider): support events Signed-off-by: Phil Kuang --- .changeset/unlucky-deers-teach.md | 5 + docs/integrations/github/org.md | 72 + .../api-report.md | 10 +- .../src/lib/github.test.ts | 32 + .../src/lib/github.ts | 37 + .../GithubMultiOrgEntityProvider.test.ts | 1249 ++++++++++++++++- .../providers/GithubMultiOrgEntityProvider.ts | 550 +++++++- 7 files changed, 1928 insertions(+), 27 deletions(-) create mode 100644 .changeset/unlucky-deers-teach.md diff --git a/.changeset/unlucky-deers-teach.md b/.changeset/unlucky-deers-teach.md new file mode 100644 index 0000000000..4073c92ba4 --- /dev/null +++ b/.changeset/unlucky-deers-teach.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-github': patch +--- + +Implement events support for `GithubMultiOrgEntityProvider` diff --git a/docs/integrations/github/org.md b/docs/integrations/github/org.md index ccffe60bd1..39532e806f 100644 --- a/docs/integrations/github/org.md +++ b/docs/integrations/github/org.md @@ -65,6 +65,42 @@ export default async function createPlugin( } ``` +Alternatively, if you wish to ingest data from multiple GitHub organizations you can use +the `GithubMultiOrgEntityProvider` instead. Note that by default, this provider will namespace +groups according to the org they originate from to avoid potential name duplicates: + +```ts title="packages/backend/src/plugins/catalog.ts" +/* highlight-add-next-line */ +import { GithubMultiOrgEntityProvider } from '@backstage/plugin-catalog-backend-module-github'; + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + const builder = await CatalogBuilder.create(env); + + /* highlight-add-start */ + // The GitHub URL below needs to match a configured integrations.github entry + // specified in your app-config. + builder.addEntityProvider( + GithubMultiOrgEntityProvider.fromConfig(env.config, { + id: 'production', + githubUrl: 'https://github.com', + // Set the following to list the GitHub orgs you wish to ingest from. You can + // also omit this option to ingest all orgs accessible by your GitHub integration + orgs: ['org-a', 'org-b'], + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 60 }, + timeout: { minutes: 15 }, + }), + }), + ); + /* highlight-add-end */ + + // .. +} +``` + ## Installation with Events Support Please follow the installation instructions at @@ -110,6 +146,42 @@ export default async function createPlugin( } ``` +Or, alternatively, if using the `GithubMultiOrgEntityProvider`: + +```ts title="packages/backend/src/plugins/catalog.ts" +/* highlight-add-next-line */ +import { GithubMultiOrgEntityProvider } from '@backstage/plugin-catalog-backend-module-github'; + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + const builder = await CatalogBuilder.create(env); + + /* highlight-add-start */ + // The GitHub URL below needs to match a configured integrations.github entry + // specified in your app-config. + builder.addEntityProvider( + GithubMultiOrgEntityProvider.fromConfig(env.config, { + id: 'production', + githubUrl: 'https://github.com', + // Set the following to list the GitHub orgs you wish to ingest from. You can + // also omit this option to ingest all orgs accessible by your GitHub integration + orgs: ['org-a', 'org-b'], + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 60 }, + timeout: { minutes: 15 }, + }), + // Pass in the eventBroker to allow this provider to subscribe to GitHub events + eventBroker: env.eventBroker, + }), + ); + /* highlight-add-end */ + + // .. +} +``` + You can check the official docs to [configure your webhook](https://docs.github.com/en/developers/webhooks-and-events/webhooks/creating-webhooks) and to [secure your request](https://docs.github.com/en/developers/webhooks-and-events/webhooks/securing-your-webhooks). The webhook will need to be configured to forward `organization`,`team` and `membership` events. diff --git a/plugins/catalog-backend-module-github/api-report.md b/plugins/catalog-backend-module-github/api-report.md index 0698fae8a0..6366a2d571 100644 --- a/plugins/catalog-backend-module-github/api-report.md +++ b/plugins/catalog-backend-module-github/api-report.md @@ -10,6 +10,7 @@ import { Config } from '@backstage/config'; import { Entity } from '@backstage/catalog-model'; import { EntityProvider } from '@backstage/plugin-catalog-node'; import { EntityProviderConnection } from '@backstage/plugin-catalog-node'; +import { EventBroker } from '@backstage/plugin-events-node'; import { EventParams } from '@backstage/plugin-events-node'; import { EventSubscriber } from '@backstage/plugin-events-node'; import { GithubCredentialsProvider } from '@backstage/integration'; @@ -133,7 +134,9 @@ export type GithubMultiOrgConfig = Array<{ }>; // @public -export class GithubMultiOrgEntityProvider implements EntityProvider { +export class GithubMultiOrgEntityProvider + implements EntityProvider, EventSubscriber +{ constructor(options: { id: string; gitHubConfig: GithubIntegrationConfig; @@ -153,11 +156,16 @@ export class GithubMultiOrgEntityProvider implements EntityProvider { ): GithubMultiOrgEntityProvider; // (undocumented) getProviderName(): string; + // (undocumented) + onEvent(params: EventParams): Promise; read(options?: { logger?: Logger }): Promise; + // (undocumented) + supportsEventTopics(): string[]; } // @public export interface GithubMultiOrgEntityProviderOptions { + eventBroker?: EventBroker; githubCredentialsProvider?: GithubCredentialsProvider; githubUrl: string; id: string; diff --git a/plugins/catalog-backend-module-github/src/lib/github.test.ts b/plugins/catalog-backend-module-github/src/lib/github.test.ts index df4565a174..7a0a29e098 100644 --- a/plugins/catalog-backend-module-github/src/lib/github.test.ts +++ b/plugins/catalog-backend-module-github/src/lib/github.test.ts @@ -21,6 +21,7 @@ import { graphql as graphqlMsw } from 'msw'; import { setupServer } from 'msw/node'; import { TeamTransformer, UserTransformer } from './defaultTransformers'; import { + getOrganizationsFromUser, getOrganizationTeams, getOrganizationUsers, getTeamMembers, @@ -448,6 +449,37 @@ describe('github', () => { }); }); + describe('getOrganizationsFromUser', () => { + it('reads orgs from user', async () => { + const input: QueryResponse = { + user: { + organizations: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + login: 'a', + }, + { + login: 'b', + }, + { + login: 'c', + }, + ], + }, + }, + }; + + server.use( + graphqlMsw.query('orgs', (_req, res, ctx) => res(ctx.data(input))), + ); + + await expect(getOrganizationsFromUser(graphql, 'foo')).resolves.toEqual({ + orgs: ['a', 'b', 'c'], + }); + }); + }); + describe('getTeamMembers', () => { it('reads team members', async () => { const input: QueryResponse = { diff --git a/plugins/catalog-backend-module-github/src/lib/github.ts b/plugins/catalog-backend-module-github/src/lib/github.ts index d0b629092a..412b751c38 100644 --- a/plugins/catalog-backend-module-github/src/lib/github.ts +++ b/plugins/catalog-backend-module-github/src/lib/github.ts @@ -33,6 +33,7 @@ import { DeferredEntity } from '@backstage/plugin-catalog-node'; export type QueryResponse = { organization?: OrganizationResponse; repositoryOwner?: RepositoryOwnerResponse; + user?: UserResponse; }; type RepositoryOwnerResponse = { @@ -46,11 +47,19 @@ export type OrganizationResponse = { repositories?: Connection; }; +export type UserResponse = { + organizations?: Connection; +}; + export type PageInfo = { hasNextPage: boolean; endCursor?: string; }; +export type GithubOrg = { + login: string; +}; + /** * Github User * @@ -334,6 +343,34 @@ export async function getOrganizationTeamsFromUsers( return { groups }; } +export async function getOrganizationsFromUser( + client: typeof graphql, + user: string, +): Promise<{ + orgs: string[]; +}> { + const query = ` + query orgs($user: String!) { + user(login: $user) { + organizations(first: 100) { + nodes { login } + pageInfo { hasNextPage, endCursor } + } + } + }`; + + const orgs = await queryWithPaging( + client, + query, + '', + r => r.user?.organizations, + async o => o.login, + { user }, + ); + + return { orgs }; +} + export async function getOrganizationTeam( client: typeof graphql, org: string, diff --git a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts index 76ab55552d..69076bf737 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts @@ -160,7 +160,7 @@ describe('GithubMultiOrgEntityProvider', () => { const logger = getVoidLogger(); const gitHubConfig: GithubIntegrationConfig = { - host: 'https://github.com', + host: 'github.com', }; const mockGetCredentials = jest.fn().mockReturnValue({ @@ -201,9 +201,9 @@ describe('GithubMultiOrgEntityProvider', () => { metadata: { annotations: { 'backstage.io/managed-by-location': - 'url:https://https://github.com/a', + 'url:https://github.com/a', 'backstage.io/managed-by-origin-location': - 'url:https://https://github.com/a', + 'url:https://github.com/a', 'github.com/user-login': 'a', }, description: 'c', @@ -227,9 +227,9 @@ describe('GithubMultiOrgEntityProvider', () => { metadata: { annotations: { 'backstage.io/managed-by-location': - 'url:https://https://github.com/x', + 'url:https://github.com/x', 'backstage.io/managed-by-origin-location': - 'url:https://https://github.com/x', + 'url:https://github.com/x', 'github.com/user-login': 'x', }, description: 'z', @@ -253,9 +253,9 @@ describe('GithubMultiOrgEntityProvider', () => { metadata: { annotations: { 'backstage.io/managed-by-location': - 'url:https://https://github.com/q', + 'url:https://github.com/q', 'backstage.io/managed-by-origin-location': - 'url:https://https://github.com/q', + 'url:https://github.com/q', 'github.com/user-login': 'q', }, description: 's', @@ -279,9 +279,9 @@ describe('GithubMultiOrgEntityProvider', () => { metadata: { annotations: { 'backstage.io/managed-by-location': - 'url:https://https://github.com/orgs/orgA/teams/team', + 'url:https://github.com/orgs/orgA/teams/team', 'backstage.io/managed-by-origin-location': - 'url:https://https://github.com/orgs/orgA/teams/team', + 'url:https://github.com/orgs/orgA/teams/team', 'github.com/team-slug': 'orgA/team', }, namespace: 'orga', @@ -308,9 +308,9 @@ describe('GithubMultiOrgEntityProvider', () => { metadata: { annotations: { 'backstage.io/managed-by-location': - 'url:https://https://github.com/orgs/orgB/teams/team', + 'url:https://github.com/orgs/orgB/teams/team', 'backstage.io/managed-by-origin-location': - 'url:https://https://github.com/orgs/orgB/teams/team', + 'url:https://github.com/orgs/orgB/teams/team', 'github.com/team-slug': 'orgB/team', }, namespace: 'orgb', @@ -467,7 +467,7 @@ describe('GithubMultiOrgEntityProvider', () => { const logger = getVoidLogger(); const gitHubConfig: GithubIntegrationConfig = { - host: 'https://github.com', + host: 'github.com', }; const mockGetCredentials = jest.fn().mockReturnValue({ @@ -507,9 +507,9 @@ describe('GithubMultiOrgEntityProvider', () => { metadata: { annotations: { 'backstage.io/managed-by-location': - 'url:https://https://github.com/a', + 'url:https://github.com/a', 'backstage.io/managed-by-origin-location': - 'url:https://https://github.com/a', + 'url:https://github.com/a', 'github.com/user-login': 'a', }, description: 'c', @@ -533,9 +533,9 @@ describe('GithubMultiOrgEntityProvider', () => { metadata: { annotations: { 'backstage.io/managed-by-location': - 'url:https://https://github.com/x', + 'url:https://github.com/x', 'backstage.io/managed-by-origin-location': - 'url:https://https://github.com/x', + 'url:https://github.com/x', 'github.com/user-login': 'x', }, description: 'z', @@ -559,9 +559,9 @@ describe('GithubMultiOrgEntityProvider', () => { metadata: { annotations: { 'backstage.io/managed-by-location': - 'url:https://https://github.com/q', + 'url:https://github.com/q', 'backstage.io/managed-by-origin-location': - 'url:https://https://github.com/q', + 'url:https://github.com/q', 'github.com/user-login': 'q', }, description: 's', @@ -585,9 +585,9 @@ describe('GithubMultiOrgEntityProvider', () => { metadata: { annotations: { 'backstage.io/managed-by-location': - 'url:https://https://github.com/orgs/orgC/teams/team', + 'url:https://github.com/orgs/orgC/teams/team', 'backstage.io/managed-by-origin-location': - 'url:https://https://github.com/orgs/orgC/teams/team', + 'url:https://github.com/orgs/orgC/teams/team', 'github.com/team-slug': 'orgC/team', }, namespace: 'orgc', @@ -614,9 +614,9 @@ describe('GithubMultiOrgEntityProvider', () => { metadata: { annotations: { 'backstage.io/managed-by-location': - 'url:https://https://github.com/orgs/orgD/teams/team', + 'url:https://github.com/orgs/orgD/teams/team', 'backstage.io/managed-by-origin-location': - 'url:https://https://github.com/orgs/orgD/teams/team', + 'url:https://github.com/orgs/orgD/teams/team', 'github.com/team-slug': 'orgD/team', }, namespace: 'orgd', @@ -713,4 +713,1209 @@ describe('GithubMultiOrgEntityProvider', () => { }); }); }); + + describe('events', () => { + let entityProvider: GithubMultiOrgEntityProvider; + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + + beforeEach(() => { + const logger = getVoidLogger(); + const gitHubConfig: GithubIntegrationConfig = { + host: 'github.com', + }; + + const mockGetCredentials = jest.fn().mockReturnValue({ + headers: { token: 'blah' }, + type: 'app', + }); + + const githubCredentialsProvider: GithubCredentialsProvider = { + getCredentials: mockGetCredentials, + }; + + entityProvider = new GithubMultiOrgEntityProvider({ + id: 'my-id', + gitHubConfig, + githubCredentialsProvider, + githubUrl: 'https://github.com', + logger, + orgs: ['orgA', 'orgB'], + }); + + entityProvider.connect(entityProviderConnection); + }); + + afterEach(() => jest.resetAllMocks()); + + it('should ignore events from non-applicable orgs', async () => { + await entityProvider.onEvent({ + topic: 'github.organization', + eventPayload: { + action: 'member_added', + membership: { + user: { + name: 'githubuser', + login: 'githubuser', + avatar_url: 'https://avatars.githubusercontent.com/u/83820368', + email: 'user1@test.com', + }, + }, + organization: { + login: 'test-org', + }, + }, + }); + + expect(entityProviderConnection.applyMutation).not.toHaveBeenCalled(); + + await entityProvider.onEvent({ + topic: 'github.installation', + eventPayload: { + action: 'created', + installation: { + account: { + login: 'test-org', + }, + }, + }, + }); + + expect(entityProviderConnection.applyMutation).not.toHaveBeenCalled(); + }); + + describe('installation', () => { + it('adds users and groups from new org', async () => { + const mockClient = jest.fn(); + + mockClient + .mockResolvedValueOnce({ + organization: { + membersWithRole: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + login: 'a', + name: 'b', + bio: 'c', + email: 'd', + avatarUrl: 'e', + }, + ], + }, + }, + }) + .mockResolvedValueOnce({ + organization: { + teams: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + slug: 'team', + combinedSlug: 'orgB/team', + name: 'TeamB', + description: 'The one and only team', + avatarUrl: 'http://example.com/team.jpeg', + parentTeam: { + slug: 'parent', + combinedSlug: '', + members: { pageInfo: { hasNextPage: false }, nodes: [] }, + }, + members: { + pageInfo: { hasNextPage: false }, + nodes: [{ login: 'a' }], + }, + }, + ], + }, + }, + }) + .mockResolvedValueOnce({ + organization: { + teams: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + slug: 'team', + combinedSlug: 'orgA/team', + name: 'TeamA', + description: 'The one and only team', + avatarUrl: 'http://example.com/team.jpeg', + parentTeam: { + slug: 'parent', + combinedSlug: '', + members: { pageInfo: { hasNextPage: false }, nodes: [] }, + }, + members: { + pageInfo: { hasNextPage: false }, + nodes: [{ login: 'a' }], + }, + }, + ], + }, + }, + }) + .mockResolvedValueOnce({ + organization: { + teams: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + slug: 'team', + combinedSlug: 'orgB/team', + name: 'TeamB', + description: 'The one and only team', + avatarUrl: 'http://example.com/team.jpeg', + parentTeam: { + slug: 'parent', + combinedSlug: '', + members: { pageInfo: { hasNextPage: false }, nodes: [] }, + }, + members: { + pageInfo: { hasNextPage: false }, + nodes: [{ login: 'a' }], + }, + }, + ], + }, + }, + }); + + (graphql.defaults as jest.Mock).mockReturnValue(mockClient); + + await entityProvider.onEvent({ + topic: 'github.installation', + eventPayload: { + action: 'created', + installation: { + account: { + login: 'orgB', + }, + }, + }, + }); + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ + type: 'delta', + added: [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'User', + metadata: { + annotations: { + 'backstage.io/managed-by-location': + 'url:https://github.com/a', + 'backstage.io/managed-by-origin-location': + 'url:https://github.com/a', + 'github.com/user-login': 'a', + }, + description: 'c', + name: 'a', + }, + spec: { + memberOf: ['orga/team', 'orgb/team'], + profile: { + displayName: 'b', + email: 'd', + picture: 'e', + }, + }, + }, + locationKey: 'github-multi-org-provider:my-id', + }, + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { + annotations: { + 'backstage.io/managed-by-location': + 'url:https://github.com/orgs/orgB/teams/team', + 'backstage.io/managed-by-origin-location': + 'url:https://github.com/orgs/orgB/teams/team', + 'github.com/team-slug': 'orgB/team', + }, + namespace: 'orgb', + name: 'team', + description: 'The one and only team', + }, + spec: { + children: [], + parent: 'parent', + profile: { + displayName: 'TeamB', + picture: 'http://example.com/team.jpeg', + }, + type: 'team', + members: ['default/a'], + }, + }, + locationKey: 'github-multi-org-provider:my-id', + }, + ], + removed: [], + }); + }); + }); + + describe('organization', () => { + it('should add a new user', async () => { + const mockClient = jest.fn(); + + mockClient + .mockResolvedValueOnce({ + user: { + organizations: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + login: 'orgA', + }, + { + login: 'orgB', + }, + ], + }, + }, + }) + .mockResolvedValueOnce({ + organization: { + teams: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + slug: 'team', + combinedSlug: 'orgA/team', + name: 'TeamB', + description: 'The one and only team', + avatarUrl: 'http://example.com/team.jpeg', + editTeamUrl: 'https://example.com', + parentTeam: { + slug: 'parent', + }, + members: { + pageInfo: { hasNextPage: false }, + nodes: [{ login: 'a', name: 'a' }], + }, + }, + ], + }, + }, + }) + .mockResolvedValueOnce({ + organization: { + teams: { + pageInfo: { hasNextPage: false }, + nodes: [], + }, + }, + }); + + (graphql.defaults as jest.Mock).mockReturnValue(mockClient); + + await entityProvider.onEvent({ + topic: 'github.organization', + eventPayload: { + action: 'member_added', + organization: { + login: 'orgB', + }, + membership: { + user: { + name: 'a', + avatar_url: 'https://avatars.githubusercontent.com/u/83820368', + email: 'user1@test.com', + login: 'a', + }, + }, + }, + }); + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ + type: 'delta', + added: [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'User', + metadata: { + annotations: { + 'backstage.io/managed-by-location': + 'url:https://github.com/a', + 'backstage.io/managed-by-origin-location': + 'url:https://github.com/a', + 'github.com/user-login': 'a', + }, + name: 'a', + }, + spec: { + memberOf: ['orga/team'], + profile: { + displayName: 'a', + email: 'user1@test.com', + picture: 'https://avatars.githubusercontent.com/u/83820368', + }, + }, + }, + locationKey: 'github-multi-org-provider:my-id', + }, + ], + removed: [], + }); + }); + + it('should remove a user', async () => { + const mockClient = jest.fn(); + + mockClient.mockResolvedValueOnce({ + user: { + organizations: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + login: 'orgC', + }, + ], + }, + }, + }); + + (graphql.defaults as jest.Mock).mockReturnValue(mockClient); + + await entityProvider.onEvent({ + topic: 'github.organization', + eventPayload: { + action: 'member_removed', + organization: { + login: 'orgB', + }, + membership: { + user: { + name: 'a', + avatar_url: 'https://avatars.githubusercontent.com/u/83820368', + email: 'user1@test.com', + login: 'a', + }, + }, + }, + }); + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ + type: 'delta', + added: [], + removed: [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'User', + metadata: { + annotations: { + 'backstage.io/managed-by-location': + 'url:https://github.com/a', + 'backstage.io/managed-by-origin-location': + 'url:https://github.com/a', + 'github.com/user-login': 'a', + }, + name: 'a', + }, + spec: { + memberOf: [], + profile: { + displayName: 'a', + email: 'user1@test.com', + picture: 'https://avatars.githubusercontent.com/u/83820368', + }, + }, + }, + locationKey: 'github-multi-org-provider:my-id', + }, + ], + }); + }); + + it('should only update group memberships of a user that still belongs to an applicable org', async () => { + const mockClient = jest.fn(); + + mockClient + .mockResolvedValueOnce({ + user: { + organizations: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + login: 'orgA', + }, + ], + }, + }, + }) + .mockResolvedValueOnce({ + organization: { + teams: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + slug: 'team', + combinedSlug: 'orgA/team', + name: 'TeamB', + description: 'The one and only team', + avatarUrl: 'http://example.com/team.jpeg', + editTeamUrl: 'https://example.com', + parentTeam: { + slug: 'parent', + }, + members: { + pageInfo: { hasNextPage: false }, + nodes: [{ login: 'a', name: 'a' }], + }, + }, + ], + }, + }, + }); + + (graphql.defaults as jest.Mock).mockReturnValue(mockClient); + + await entityProvider.onEvent({ + topic: 'github.organization', + eventPayload: { + action: 'member_removed', + organization: { + login: 'orgB', + }, + membership: { + user: { + name: 'a', + avatar_url: 'https://avatars.githubusercontent.com/u/83820368', + email: 'user1@test.com', + login: 'a', + }, + }, + }, + }); + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ + type: 'delta', + added: [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'User', + metadata: { + annotations: { + 'backstage.io/managed-by-location': + 'url:https://github.com/a', + 'backstage.io/managed-by-origin-location': + 'url:https://github.com/a', + 'github.com/user-login': 'a', + }, + name: 'a', + }, + spec: { + memberOf: ['orga/team'], + profile: { + displayName: 'a', + email: 'user1@test.com', + picture: 'https://avatars.githubusercontent.com/u/83820368', + }, + }, + }, + locationKey: 'github-multi-org-provider:my-id', + }, + ], + removed: [], + }); + }); + }); + + describe('team', () => { + it('should create a new group from a new team', async () => { + await entityProvider.onEvent({ + topic: 'github.team', + eventPayload: { + action: 'created', + organization: { + login: 'orgB', + }, + team: { + name: 'New Team', + slug: 'new-team', + description: 'description from the new team', + html_url: 'https://github.com/orgs/orgB/teams/new-team', + parent: { + slug: 'father-team', + }, + }, + }, + }); + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ + type: 'delta', + added: [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { + name: 'new-team', + namespace: 'orgb', + description: 'description from the new team', + annotations: { + 'backstage.io/edit-url': + 'https://github.com/orgs/orgB/teams/new-team/edit', + 'backstage.io/managed-by-location': + 'url:https://github.com/orgs/orgB/teams/new-team', + 'backstage.io/managed-by-origin-location': + 'url:https://github.com/orgs/orgB/teams/new-team', + 'github.com/team-slug': 'orgB/new-team', + }, + }, + spec: { + type: 'team', + children: [], + members: [], + parent: 'father-team', + profile: { + displayName: 'New Team', + }, + }, + }, + locationKey: 'github-multi-org-provider:my-id', + }, + ], + removed: [], + }); + }); + + it('should remove a group from a deleted team', async () => { + await entityProvider.onEvent({ + topic: 'github.team', + eventPayload: { + action: 'deleted', + organization: { + login: 'orgB', + }, + team: { + name: 'New Team', + slug: 'new-team', + description: 'description from the new team', + html_url: 'https://github.com/orgs/orgB/teams/new-team', + parent: { + slug: 'father-team', + }, + }, + }, + }); + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ + type: 'delta', + added: [], + removed: [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { + name: 'new-team', + namespace: 'orgb', + description: 'description from the new team', + annotations: { + 'backstage.io/edit-url': + 'https://github.com/orgs/orgB/teams/new-team/edit', + 'backstage.io/managed-by-location': + 'url:https://github.com/orgs/orgB/teams/new-team', + 'backstage.io/managed-by-origin-location': + 'url:https://github.com/orgs/orgB/teams/new-team', + 'github.com/team-slug': 'orgB/new-team', + }, + }, + spec: { + type: 'team', + children: [], + members: [], + parent: 'father-team', + profile: { + displayName: 'New Team', + }, + }, + }, + locationKey: 'github-multi-org-provider:my-id', + }, + ], + }); + }); + + it('should update group and user entities when a team is edited', async () => { + const mockClient = jest.fn(); + + mockClient + .mockResolvedValueOnce({ + organization: { + team: { + slug: 'team', + combinedSlug: 'orgA/team', + name: 'TeamA', + description: 'The one and only team', + avatarUrl: 'http://example.com/team.jpeg', + editTeamUrl: 'https://example.com', + parentTeam: { + slug: 'parent', + }, + members: { + pageInfo: { hasNextPage: false }, + nodes: [{ login: 'a', name: 'a' }], + }, + }, + }, + }) + .mockResolvedValueOnce({ + organization: { + membersWithRole: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + login: 'a', + name: 'b', + bio: 'c', + email: 'd', + avatarUrl: 'e', + }, + { + login: 'w', + name: 'x', + bio: 'y', + email: 'z', + avatarUrl: 'q', + }, + ], + }, + }, + }) + .mockResolvedValueOnce({ + organization: { + teams: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + slug: 'team', + combinedSlug: 'orgA/team', + name: 'TeamA', + description: 'The one and only team', + avatarUrl: 'http://example.com/team.jpeg', + editTeamUrl: 'https://example.com', + parentTeam: { + slug: 'parent', + }, + members: { + pageInfo: { hasNextPage: false }, + nodes: [{ login: 'a', name: 'a' }], + }, + }, + ], + }, + }, + }) + .mockResolvedValueOnce({ + organization: { + teams: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + slug: 'team', + combinedSlug: 'orgB/team', + name: 'TeamB', + description: 'The one and only team', + avatarUrl: 'http://example.com/team.jpeg', + editTeamUrl: 'https://example.com', + parentTeam: { + slug: 'parent', + }, + members: { + pageInfo: { hasNextPage: false }, + nodes: [{ login: 'a', name: 'a' }], + }, + }, + ], + }, + }, + }); + + (graphql.defaults as jest.Mock).mockReturnValue(mockClient); + + await entityProvider.onEvent({ + topic: 'github.team', + eventPayload: { + action: 'edited', + changes: { + name: { + from: 'oldName', + }, + description: { + from: 'oldDescription', + }, + }, + team: { + slug: 'team', + parent: { + slug: 'parent', + }, + }, + organization: { + login: 'orgA', + }, + }, + }); + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ + type: 'delta', + added: [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'User', + metadata: { + annotations: { + 'backstage.io/managed-by-location': + 'url:https://github.com/a', + 'backstage.io/managed-by-origin-location': + 'url:https://github.com/a', + 'github.com/user-login': 'a', + }, + name: 'a', + description: 'c', + }, + spec: { + memberOf: ['orga/team', 'orgb/team'], + profile: { + displayName: 'b', + email: 'd', + picture: 'e', + }, + }, + }, + locationKey: 'github-multi-org-provider:my-id', + }, + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { + annotations: { + 'backstage.io/edit-url': 'https://example.com', + 'backstage.io/managed-by-location': + 'url:https://github.com/orgs/orgA/teams/team', + 'backstage.io/managed-by-origin-location': + 'url:https://github.com/orgs/orgA/teams/team', + 'github.com/team-slug': 'orgA/team', + }, + namespace: 'orga', + name: 'team', + description: 'The one and only team', + }, + spec: { + children: [], + parent: 'parent', + profile: { + displayName: 'TeamA', + picture: 'http://example.com/team.jpeg', + }, + type: 'team', + members: ['default/a'], + }, + }, + locationKey: 'github-multi-org-provider:my-id', + }, + ], + removed: [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { + annotations: { + 'backstage.io/managed-by-location': + 'url:https://github.com/orgs/orgA/teams/oldname', + 'backstage.io/managed-by-origin-location': + 'url:https://github.com/orgs/orgA/teams/oldname', + 'github.com/team-slug': 'orgA/oldname', + }, + namespace: 'orga', + name: 'oldname', + description: 'oldDescription', + }, + spec: { + children: [], + parent: 'parent', + profile: { + displayName: 'oldName', + }, + type: 'team', + members: [], + }, + }, + locationKey: 'github-multi-org-provider:my-id', + }, + ], + }); + }); + }); + + describe('membership', () => { + it('should update group and user entities when a member is added', async () => { + const mockClient = jest.fn(); + + mockClient + .mockResolvedValueOnce({ + organization: { + team: { + slug: 'team', + combinedSlug: 'orgA/team', + name: 'TeamA', + description: 'The one and only team', + avatarUrl: 'http://example.com/team.jpeg', + editTeamUrl: 'https://example.com', + parentTeam: { + slug: 'parent', + }, + members: { + pageInfo: { hasNextPage: false }, + nodes: [{ login: 'a', name: 'a' }], + }, + }, + }, + }) + .mockResolvedValueOnce({ + user: { + organizations: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + login: 'orgA', + }, + { + login: 'orgB', + }, + ], + }, + }, + }) + .mockResolvedValueOnce({ + organization: { + teams: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + slug: 'team', + combinedSlug: 'orgA/team', + name: 'TeamA', + description: 'The one and only team', + avatarUrl: 'http://example.com/team.jpeg', + editTeamUrl: 'https://example.com', + parentTeam: { + slug: 'parent', + }, + members: { + pageInfo: { hasNextPage: false }, + nodes: [{ login: 'a', name: 'a' }], + }, + }, + ], + }, + }, + }) + .mockResolvedValueOnce({ + organization: { + teams: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + slug: 'team', + combinedSlug: 'orgB/team', + name: 'TeamB', + description: 'The one and only team', + avatarUrl: 'http://example.com/team.jpeg', + editTeamUrl: 'https://example.com', + parentTeam: { + slug: 'parent', + }, + members: { + pageInfo: { hasNextPage: false }, + nodes: [{ login: 'a', name: 'a' }], + }, + }, + ], + }, + }, + }); + + (graphql.defaults as jest.Mock).mockReturnValue(mockClient); + + await entityProvider.onEvent({ + topic: 'github.membership', + eventPayload: { + action: 'added', + organization: { + login: 'orgA', + }, + team: { + slug: 'teama', + }, + member: { + name: 'a', + login: 'a', + avatar_url: 'https://avatars.githubusercontent.com/u/83820368', + email: 'user1@test.com', + }, + }, + }); + await new Promise(process.nextTick); + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ + type: 'delta', + added: [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'User', + metadata: { + annotations: { + 'backstage.io/managed-by-location': + 'url:https://github.com/a', + 'backstage.io/managed-by-origin-location': + 'url:https://github.com/a', + 'github.com/user-login': 'a', + }, + name: 'a', + }, + spec: { + memberOf: ['orga/team', 'orgb/team'], + profile: { + displayName: 'a', + email: 'user1@test.com', + picture: 'https://avatars.githubusercontent.com/u/83820368', + }, + }, + }, + locationKey: 'github-multi-org-provider:my-id', + }, + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { + annotations: { + 'backstage.io/edit-url': 'https://example.com', + 'backstage.io/managed-by-location': + 'url:https://github.com/orgs/orgA/teams/team', + 'backstage.io/managed-by-origin-location': + 'url:https://github.com/orgs/orgA/teams/team', + 'github.com/team-slug': 'orgA/team', + }, + namespace: 'orga', + name: 'team', + description: 'The one and only team', + }, + spec: { + children: [], + parent: 'parent', + profile: { + displayName: 'TeamA', + picture: 'http://example.com/team.jpeg', + }, + type: 'team', + members: ['default/a'], + }, + }, + locationKey: 'github-multi-org-provider:my-id', + }, + ], + removed: [], + }); + }); + + it('should update group and user entities when a member is removed', async () => { + const mockClient = jest.fn(); + + mockClient + .mockResolvedValueOnce({ + organization: { + team: { + slug: 'team', + combinedSlug: 'orgA/team', + name: 'TeamA', + description: 'The one and only team', + avatarUrl: 'http://example.com/team.jpeg', + editTeamUrl: 'https://example.com', + parentTeam: { + slug: 'parent', + }, + members: { + pageInfo: { hasNextPage: false }, + nodes: [], + }, + }, + }, + }) + .mockResolvedValueOnce({ + user: { + organizations: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + login: 'orgA', + }, + { + login: 'orgB', + }, + ], + }, + }, + }) + .mockResolvedValueOnce({ + organization: { + teams: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + slug: 'team', + combinedSlug: 'orgA/team', + name: 'TeamA', + description: 'The one and only team', + avatarUrl: 'http://example.com/team.jpeg', + editTeamUrl: 'https://example.com', + parentTeam: { + slug: 'parent', + }, + members: { + pageInfo: { hasNextPage: false }, + nodes: [], + }, + }, + ], + }, + }, + }) + .mockResolvedValueOnce({ + organization: { + teams: { + pageInfo: { hasNextPage: false }, + nodes: [ + { + slug: 'team', + combinedSlug: 'orgB/team', + name: 'TeamB', + description: 'The one and only team', + avatarUrl: 'http://example.com/team.jpeg', + editTeamUrl: 'https://example.com', + parentTeam: { + slug: 'parent', + }, + members: { + pageInfo: { hasNextPage: false }, + nodes: [{ login: 'a', name: 'a' }], + }, + }, + ], + }, + }, + }); + + (graphql.defaults as jest.Mock).mockReturnValue(mockClient); + + await entityProvider.onEvent({ + topic: 'github.membership', + eventPayload: { + action: 'removed', + organization: { + login: 'orgA', + }, + team: { + slug: 'teama', + }, + member: { + name: 'a', + login: 'a', + avatar_url: 'https://avatars.githubusercontent.com/u/83820368', + email: 'user1@test.com', + }, + }, + }); + await new Promise(process.nextTick); + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ + type: 'delta', + added: [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'User', + metadata: { + annotations: { + 'backstage.io/managed-by-location': + 'url:https://github.com/a', + 'backstage.io/managed-by-origin-location': + 'url:https://github.com/a', + 'github.com/user-login': 'a', + }, + name: 'a', + }, + spec: { + memberOf: ['orgb/team'], + profile: { + displayName: 'a', + email: 'user1@test.com', + picture: 'https://avatars.githubusercontent.com/u/83820368', + }, + }, + }, + locationKey: 'github-multi-org-provider:my-id', + }, + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { + annotations: { + 'backstage.io/edit-url': 'https://example.com', + 'backstage.io/managed-by-location': + 'url:https://github.com/orgs/orgA/teams/team', + 'backstage.io/managed-by-origin-location': + 'url:https://github.com/orgs/orgA/teams/team', + 'github.com/team-slug': 'orgA/team', + }, + namespace: 'orga', + name: 'team', + description: 'The one and only team', + }, + spec: { + children: [], + parent: 'parent', + profile: { + displayName: 'TeamA', + picture: 'http://example.com/team.jpeg', + }, + type: 'team', + members: [], + }, + }, + locationKey: 'github-multi-org-provider:my-id', + }, + ], + removed: [], + }); + }); + }); + }); }); diff --git a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts index ce45f30d96..d3943bb66a 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts @@ -21,7 +21,9 @@ import { DEFAULT_NAMESPACE, Entity, GroupEntity, + parseEntityRef, stringifyEntityRef, + UserEntity, } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { @@ -32,10 +34,28 @@ import { ScmIntegrations, } from '@backstage/integration'; import { + DeferredEntity, EntityProvider, EntityProviderConnection, } from '@backstage/plugin-catalog-node'; +import { + EventBroker, + EventParams, + EventSubscriber, +} from '@backstage/plugin-events-node'; import { graphql } from '@octokit/graphql'; +import { + InstallationCreatedEvent, + InstallationEvent, + OrganizationEvent, + OrganizationMemberAddedEvent, + OrganizationMemberRemovedEvent, + MembershipEvent, + TeamCreatedEvent, + TeamDeletedEvent, + TeamEditedEvent, + TeamEvent, +} from '@octokit/webhooks-types'; import { merge } from 'lodash'; import * as uuid from 'uuid'; import { Logger } from 'winston'; @@ -44,6 +64,7 @@ import { assignGroupsToUsers, buildOrgHierarchy, defaultOrganizationTeamTransformer, + defaultUserTransformer, getOrganizationTeams, getOrganizationUsers, GithubTeam, @@ -55,6 +76,11 @@ import { ANNOTATION_GITHUB_TEAM_SLUG, ANNOTATION_GITHUB_USER_LOGIN, } from '../lib/annotation'; +import { + getOrganizationsFromUser, + getOrganizationTeam, + getOrganizationTeamsFromUsers, +} from '../lib/github'; import { splitTeamSlug } from '../lib/util'; /** @@ -118,14 +144,26 @@ export interface GithubMultiOrgEntityProviderOptions { * By default groups will be namespaced according to their GitHub org. */ teamTransformer?: TeamTransformer; + + /** + * An EventBroker to subscribe this provider to GitHub events to trigger delta mutations + */ + eventBroker?: EventBroker; } +type CreateDeltaOperation = (entities: Entity[]) => { + added: DeferredEntity[]; + removed: DeferredEntity[]; +}; + /** * Ingests org data (users and groups) from GitHub. * * @public */ -export class GithubMultiOrgEntityProvider implements EntityProvider { +export class GithubMultiOrgEntityProvider + implements EntityProvider, EventSubscriber +{ private connection?: EntityProviderConnection; private scheduleFn?: () => Promise; @@ -161,6 +199,10 @@ export class GithubMultiOrgEntityProvider implements EntityProvider { provider.schedule(options.schedule); + if (options.eventBroker) { + options.eventBroker.subscribe(provider); + } + return provider; } @@ -271,6 +313,478 @@ export class GithubMultiOrgEntityProvider implements EntityProvider { markCommitComplete(); } + /** {@inheritdoc @backstage/plugin-events-node#EventSubscriber.supportsEventTopics} */ + supportsEventTopics(): string[] { + return [ + 'github.installation', + 'github.organization', + 'github.team', + 'github.membership', + ]; + } + + /** {@inheritdoc @backstage/plugin-events-node#EventSubscriber.onEvent} */ + async onEvent(params: EventParams): Promise { + const { logger } = this.options; + logger.debug(`Received event from ${params.topic}`); + + const orgs = this.options.orgs?.length + ? this.options.orgs + : await this.getAllOrgs(this.options.gitHubConfig); + + const eventPayload = params.eventPayload as + | InstallationEvent + | OrganizationEvent + | MembershipEvent + | TeamEvent; + + if ( + !orgs.includes( + (eventPayload as InstallationEvent).installation?.account?.login, + ) && + !orgs.includes( + (eventPayload as OrganizationEvent | MembershipEvent | TeamEvent) + .organization?.login, + ) + ) { + return; + } + + // https://docs.github.com/webhooks-and-events/webhooks/webhook-events-and-payloads#installation + if ( + params.topic.includes('installation') && + eventPayload.action === 'created' + ) { + // We can only respond to installation.created events to add new users/groups since a + // installation.deleted event won't provide us info on what user/groups we should remove and + // we can't query the uninstalled org since we will no longer have access. This will need to be + // eventually resolved via occasional full mutation runs by calling read() + await this.onInstallationChange( + eventPayload as InstallationCreatedEvent, + orgs, + ); + } + + // https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#organization + if ( + params.topic.includes('organization') && + (eventPayload.action === 'member_added' || + eventPayload.action === 'member_removed') + ) { + await this.onMemberChangeInOrganization(eventPayload, orgs); + } + + // https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#team + if (params.topic.includes('team')) { + if ( + eventPayload.action === 'created' || + eventPayload.action === 'deleted' + ) { + await this.onTeamChangeInOrganization( + eventPayload as TeamCreatedEvent | TeamDeletedEvent, + ); + } else if (eventPayload.action === 'edited') { + await this.onTeamEditedInOrganization( + eventPayload as TeamEditedEvent, + orgs, + ); + } + } + + // https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#membership + if (params.topic.includes('membership')) { + this.onMembershipChangedInTeam(eventPayload as MembershipEvent, orgs); + } + + return; + } + + private async onInstallationChange( + event: InstallationCreatedEvent, + applicableOrgs: string[], + ) { + if (!this.connection) { + throw new Error('Not initialized'); + } + + const org = event.installation.account.login; + const { headers, type: tokenType } = + await this.options.githubCredentialsProvider.getCredentials({ + url: `${this.options.githubUrl}/${org}`, + }); + const client = graphql.defaults({ + baseUrl: this.options.gitHubConfig.apiBaseUrl, + headers, + }); + + const { users } = await getOrganizationUsers( + client, + org, + tokenType, + this.options.userTransformer, + ); + + const { groups } = await getOrganizationTeams( + client, + org, + this.defaultMultiOrgTeamTransformer.bind(this), + ); + + // Fetch group memberships of users in case they already exist and + // have memberships in groups from other applicable orgs + for (const userOrg of applicableOrgs) { + const { headers: orgHeaders } = + await this.options.githubCredentialsProvider.getCredentials({ + url: `${this.options.githubUrl}/${userOrg}`, + }); + const orgClient = graphql.defaults({ + baseUrl: this.options.gitHubConfig.apiBaseUrl, + headers: orgHeaders, + }); + + const { groups: userGroups } = await getOrganizationTeamsFromUsers( + orgClient, + userOrg, + users.map( + u => + u.metadata.annotations?.[ANNOTATION_GITHUB_USER_LOGIN] || + u.metadata.name, + ), + this.defaultMultiOrgTeamTransformer.bind(this), + ); + + assignGroupsToUsers(users, userGroups); + } + + const { added, removed } = this.createAddEntitiesOperation([ + ...users, + ...groups, + ]); + await this.connection.applyMutation({ + type: 'delta', + removed, + added, + }); + } + + private async onMemberChangeInOrganization( + event: OrganizationMemberAddedEvent | OrganizationMemberRemovedEvent, + applicableOrgs: string[], + ) { + if (!this.connection) { + throw new Error('Not initialized'); + } + + const userTransformer = + this.options.userTransformer || defaultUserTransformer; + const { name, avatar_url: avatarUrl, email, login } = event.membership.user; + const org = event.organization.login; + const { headers } = + await this.options.githubCredentialsProvider.getCredentials({ + url: `${this.options.githubUrl}/${org}`, + }); + const client = graphql.defaults({ + baseUrl: this.options.gitHubConfig.apiBaseUrl, + headers, + }); + + const { orgs } = await getOrganizationsFromUser(client, login); + const userApplicableOrgs = orgs.filter(o => applicableOrgs.includes(o)); + + let updateMemberships: boolean; + let createDeltaOperation: CreateDeltaOperation; + if (event.action === 'member_removed') { + if (userApplicableOrgs.length) { + // If the user is still associated with another applicable org then we don't want to remove + // them, just update the entity to remove any potential group memberships from the old org + createDeltaOperation = this.createAddEntitiesOperation.bind(this); + updateMemberships = true; + } else { + // User is no longer part of any applicable orgs so we can remove it, + // no need to take memberships into account + createDeltaOperation = this.createRemoveEntitiesOperation.bind(this); + updateMemberships = false; + } + } else { + // We're not sure if this user was already added as part of another applicable org + // so grab the latest memberships (potentially from teams of other orgs) to ensure + // we're not accidentally omitting them + createDeltaOperation = this.createAddEntitiesOperation.bind(this); + updateMemberships = true; + } + + const user = (await userTransformer( + { + name, + avatarUrl, + login, + email: email ?? undefined, + }, + { + org, + client, + query: '', + }, + )) as UserEntity; + + if (updateMemberships) { + for (const userOrg of userApplicableOrgs) { + const { headers: orgHeaders } = + await this.options.githubCredentialsProvider.getCredentials({ + url: `${this.options.githubUrl}/${userOrg}`, + }); + const orgClient = graphql.defaults({ + baseUrl: this.options.gitHubConfig.apiBaseUrl, + headers: orgHeaders, + }); + + const { groups } = await getOrganizationTeamsFromUsers( + orgClient, + userOrg, + [login], + this.defaultMultiOrgTeamTransformer.bind(this), + ); + + assignGroupsToUsers([user], groups); + } + } + + const { added, removed } = createDeltaOperation([user]); + await this.connection.applyMutation({ + type: 'delta', + removed, + added, + }); + } + + private async onTeamChangeInOrganization( + event: TeamCreatedEvent | TeamDeletedEvent, + ) { + if (!this.connection) { + throw new Error('Not initialized'); + } + + const org = event.organization.login; + const { headers } = + await this.options.githubCredentialsProvider.getCredentials({ + url: `${this.options.githubUrl}/${org}`, + }); + const client = graphql.defaults({ + baseUrl: this.options.gitHubConfig.apiBaseUrl, + headers, + }); + + const { name, html_url: url, description, slug } = event.team; + const group = (await this.defaultMultiOrgTeamTransformer( + { + name, + slug, + editTeamUrl: `${url}/edit`, + combinedSlug: `${org}/${slug}`, + description: description ?? undefined, + parentTeam: { slug: event.team?.parent?.slug || '' } as GithubTeam, + // entity will be removed or is new + members: [], + }, + { + org, + client, + query: '', + }, + )) as Entity; + + const createDeltaOperation = + event.action === 'created' + ? this.createAddEntitiesOperation.bind(this) + : this.createRemoveEntitiesOperation.bind(this); + const { added, removed } = createDeltaOperation([group]); + + await this.connection.applyMutation({ + type: 'delta', + removed, + added, + }); + } + + private async onTeamEditedInOrganization( + event: TeamEditedEvent, + applicableOrgs: string[], + ) { + if (!this.connection) { + throw new Error('Not initialized'); + } + + const org = event.organization.login; + const { headers, type: tokenType } = + await this.options.githubCredentialsProvider.getCredentials({ + url: `${this.options.githubUrl}/${org}`, + }); + const client = graphql.defaults({ + baseUrl: this.options.gitHubConfig.apiBaseUrl, + headers, + }); + + const teamSlug = event.team.slug; + const { group } = await getOrganizationTeam( + client, + org, + teamSlug, + this.defaultMultiOrgTeamTransformer.bind(this), + ); + + const { users } = await getOrganizationUsers( + client, + org, + tokenType, + this.options.userTransformer, + ); + + const usersFromChangedGroup = + group.spec.members?.map(m => + stringifyEntityRef(parseEntityRef(m, { defaultKind: 'user' })), + ) || []; + const usersToRebuild = users.filter(u => + usersFromChangedGroup.includes(stringifyEntityRef(u)), + ); + + // Update memberships of associated members of this group in case the group entity ref changed + for (const userOrg of applicableOrgs) { + const { headers: orgHeaders } = + await this.options.githubCredentialsProvider.getCredentials({ + url: `${this.options.githubUrl}/${userOrg}`, + }); + const orgClient = graphql.defaults({ + baseUrl: this.options.gitHubConfig.apiBaseUrl, + headers: orgHeaders, + }); + + const { groups } = await getOrganizationTeamsFromUsers( + orgClient, + userOrg, + usersToRebuild.map( + u => + u.metadata.annotations?.[ANNOTATION_GITHUB_USER_LOGIN] || + u.metadata.name, + ), + this.defaultMultiOrgTeamTransformer.bind(this), + ); + + assignGroupsToUsers(usersToRebuild, groups); + } + + const oldName = event.changes.name?.from || ''; + const oldSlug = oldName.toLowerCase().replaceAll(/\s/gi, '-'); + const oldGroup = (await this.defaultMultiOrgTeamTransformer( + { + name: event.changes.name?.from, + slug: oldSlug, + combinedSlug: `${org}/${oldSlug}`, + description: event.changes.description?.from, + parentTeam: { slug: event.team?.parent?.slug || '' } as GithubTeam, + // entity will be removed + members: [], + }, + { + org, + client, + query: '', + }, + )) as Entity; + + // Remove the old group entity in case the entity ref is now different + const { removed } = this.createRemoveEntitiesOperation([oldGroup]); + const { added } = this.createAddEntitiesOperation([ + ...usersToRebuild, + group, + ]); + await this.connection.applyMutation({ + type: 'delta', + removed, + added, + }); + } + + private async onMembershipChangedInTeam( + event: MembershipEvent, + applicableOrgs: string[], + ) { + if (!this.connection) { + throw new Error('Not initialized'); + } + + // The docs are saying I will receive the slug for the removed event, + // but the types don't reflect that, + // so I will just check to be sure the slug is there + // https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#membership + if (!('slug' in event.team)) { + return; + } + + const org = event.organization.login; + const { headers } = + await this.options.githubCredentialsProvider.getCredentials({ + url: `${this.options.githubUrl}/${org}`, + }); + const client = graphql.defaults({ + baseUrl: this.options.gitHubConfig.apiBaseUrl, + headers, + }); + + const teamSlug = event.team.slug; + const { group } = await getOrganizationTeam( + client, + org, + teamSlug, + this.defaultMultiOrgTeamTransformer.bind(this), + ); + + const userTransformer = + this.options.userTransformer || defaultUserTransformer; + const { name, avatar_url: avatarUrl, email, login } = event.member; + const user = (await userTransformer( + { + name, + avatarUrl, + login, + email: email ?? undefined, + }, + { + org, + client, + query: '', + }, + )) as UserEntity; + + const { orgs } = await getOrganizationsFromUser(client, login); + const userApplicableOrgs = orgs.filter(o => applicableOrgs.includes(o)); + for (const userOrg of userApplicableOrgs) { + const { headers: orgHeaders } = + await this.options.githubCredentialsProvider.getCredentials({ + url: `${this.options.githubUrl}/${userOrg}`, + }); + const orgClient = graphql.defaults({ + baseUrl: this.options.gitHubConfig.apiBaseUrl, + headers: orgHeaders, + }); + + const { groups } = await getOrganizationTeamsFromUsers( + orgClient, + userOrg, + [login], + this.defaultMultiOrgTeamTransformer.bind(this), + ); + + assignGroupsToUsers([user], groups); + } + + const { added, removed } = this.createAddEntitiesOperation([user, group]); + await this.connection.applyMutation({ + type: 'delta', + removed, + added, + }); + } + private schedule(schedule: GithubMultiOrgEntityProviderOptions['schedule']) { if (!schedule || schedule === 'manual') { return; @@ -301,9 +815,11 @@ export class GithubMultiOrgEntityProvider implements EntityProvider { team: GithubTeam, ctx: TransformerContext, ): Promise { - const result = this.options.teamTransformer - ? await this.options.teamTransformer(team, ctx) - : await defaultOrganizationTeamTransformer(team, ctx); + if (this.options.teamTransformer) { + return await this.options.teamTransformer(team, ctx); + } + + const result = await defaultOrganizationTeamTransformer(team, ctx); if (result) { result.metadata.namespace = ctx.org.toLocaleLowerCase('en-US'); @@ -333,6 +849,32 @@ export class GithubMultiOrgEntityProvider implements EntityProvider { ) .filter(Boolean) as string[]; } + + private createAddEntitiesOperation(entities: Entity[]) { + return { + removed: [], + added: entities.map(entity => ({ + locationKey: `github-multi-org-provider:${this.options.id}`, + entity: withLocations( + `https://${this.options.gitHubConfig.host}`, + entity, + ), + })), + }; + } + + private createRemoveEntitiesOperation(entities: Entity[]) { + return { + added: [], + removed: entities.map(entity => ({ + locationKey: `github-multi-org-provider:${this.options.id}`, + entity: withLocations( + `https://${this.options.gitHubConfig.host}`, + entity, + ), + })), + }; + } } // Helps wrap the timing and logging behaviors From 7c89eb4b36a04cb434a0d130271a05954980a34f Mon Sep 17 00:00:00 2001 From: Phil Kuang Date: Thu, 4 May 2023 11:35:03 -0400 Subject: [PATCH 027/189] refactor(GithubMultiOrgEntityProvider): remove EventSubscriber interface declaration Signed-off-by: Phil Kuang --- .changeset/unlucky-deers-teach.md | 4 +- .../api-report.md | 8 +-- .../GithubMultiOrgEntityProvider.test.ts | 50 ++++++++++++------- .../providers/GithubMultiOrgEntityProvider.ts | 21 +++----- 4 files changed, 45 insertions(+), 38 deletions(-) diff --git a/.changeset/unlucky-deers-teach.md b/.changeset/unlucky-deers-teach.md index 4073c92ba4..b773e9090f 100644 --- a/.changeset/unlucky-deers-teach.md +++ b/.changeset/unlucky-deers-teach.md @@ -1,5 +1,7 @@ --- -'@backstage/plugin-catalog-backend-module-github': patch +'@backstage/plugin-catalog-backend-module-github': minor --- Implement events support for `GithubMultiOrgEntityProvider` + +**BREAKING:** Passing in a custom `teamTransformer` will now correctly completely override the default transformer behavior diff --git a/plugins/catalog-backend-module-github/api-report.md b/plugins/catalog-backend-module-github/api-report.md index 6366a2d571..7b944b33a9 100644 --- a/plugins/catalog-backend-module-github/api-report.md +++ b/plugins/catalog-backend-module-github/api-report.md @@ -134,9 +134,7 @@ export type GithubMultiOrgConfig = Array<{ }>; // @public -export class GithubMultiOrgEntityProvider - implements EntityProvider, EventSubscriber -{ +export class GithubMultiOrgEntityProvider implements EntityProvider { constructor(options: { id: string; gitHubConfig: GithubIntegrationConfig; @@ -156,11 +154,7 @@ export class GithubMultiOrgEntityProvider ): GithubMultiOrgEntityProvider; // (undocumented) getProviderName(): string; - // (undocumented) - onEvent(params: EventParams): Promise; read(options?: { logger?: Logger }): Promise; - // (undocumented) - supportsEventTopics(): string[]; } // @public diff --git a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts index 69076bf737..99bef6e074 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts @@ -16,11 +16,13 @@ import { getVoidLogger } from '@backstage/backend-common'; import { GroupEntity, UserEntity } from '@backstage/catalog-model'; +import { ConfigReader } from '@backstage/config'; import { GithubCredentialsProvider, GithubIntegrationConfig, } from '@backstage/integration'; import { EntityProviderConnection } from '@backstage/plugin-catalog-node'; +import { EventSubscriber } from '@backstage/plugin-events-node'; import { graphql } from '@octokit/graphql'; import { GithubMultiOrgEntityProvider, @@ -715,7 +717,8 @@ describe('GithubMultiOrgEntityProvider', () => { }); describe('events', () => { - let entityProvider: GithubMultiOrgEntityProvider; + let onEvent: Function; + const entityProviderConnection: EntityProviderConnection = { applyMutation: jest.fn(), refresh: jest.fn(), @@ -723,9 +726,15 @@ describe('GithubMultiOrgEntityProvider', () => { beforeEach(() => { const logger = getVoidLogger(); - const gitHubConfig: GithubIntegrationConfig = { - host: 'github.com', - }; + const config = new ConfigReader({ + integrations: { + github: [ + { + host: 'github.com', + }, + ], + }, + }); const mockGetCredentials = jest.fn().mockReturnValue({ headers: { token: 'blah' }, @@ -736,13 +745,20 @@ describe('GithubMultiOrgEntityProvider', () => { getCredentials: mockGetCredentials, }; - entityProvider = new GithubMultiOrgEntityProvider({ + const mockEventBroker = { + publish: async () => {}, + subscribe: (subscriber: EventSubscriber) => { + onEvent = subscriber.onEvent; + }, + }; + + const entityProvider = GithubMultiOrgEntityProvider.fromConfig(config, { id: 'my-id', - gitHubConfig, githubCredentialsProvider, githubUrl: 'https://github.com', logger, orgs: ['orgA', 'orgB'], + eventBroker: mockEventBroker, }); entityProvider.connect(entityProviderConnection); @@ -751,7 +767,7 @@ describe('GithubMultiOrgEntityProvider', () => { afterEach(() => jest.resetAllMocks()); it('should ignore events from non-applicable orgs', async () => { - await entityProvider.onEvent({ + await onEvent({ topic: 'github.organization', eventPayload: { action: 'member_added', @@ -771,7 +787,7 @@ describe('GithubMultiOrgEntityProvider', () => { expect(entityProviderConnection.applyMutation).not.toHaveBeenCalled(); - await entityProvider.onEvent({ + await onEvent({ topic: 'github.installation', eventPayload: { action: 'created', @@ -885,7 +901,7 @@ describe('GithubMultiOrgEntityProvider', () => { (graphql.defaults as jest.Mock).mockReturnValue(mockClient); - await entityProvider.onEvent({ + await onEvent({ topic: 'github.installation', eventPayload: { action: 'created', @@ -1017,7 +1033,7 @@ describe('GithubMultiOrgEntityProvider', () => { (graphql.defaults as jest.Mock).mockReturnValue(mockClient); - await entityProvider.onEvent({ + await onEvent({ topic: 'github.organization', eventPayload: { action: 'member_added', @@ -1087,7 +1103,7 @@ describe('GithubMultiOrgEntityProvider', () => { (graphql.defaults as jest.Mock).mockReturnValue(mockClient); - await entityProvider.onEvent({ + await onEvent({ topic: 'github.organization', eventPayload: { action: 'member_removed', @@ -1182,7 +1198,7 @@ describe('GithubMultiOrgEntityProvider', () => { (graphql.defaults as jest.Mock).mockReturnValue(mockClient); - await entityProvider.onEvent({ + await onEvent({ topic: 'github.organization', eventPayload: { action: 'member_removed', @@ -1237,7 +1253,7 @@ describe('GithubMultiOrgEntityProvider', () => { describe('team', () => { it('should create a new group from a new team', async () => { - await entityProvider.onEvent({ + await onEvent({ topic: 'github.team', eventPayload: { action: 'created', @@ -1296,7 +1312,7 @@ describe('GithubMultiOrgEntityProvider', () => { }); it('should remove a group from a deleted team', async () => { - await entityProvider.onEvent({ + await onEvent({ topic: 'github.team', eventPayload: { action: 'deleted', @@ -1451,7 +1467,7 @@ describe('GithubMultiOrgEntityProvider', () => { (graphql.defaults as jest.Mock).mockReturnValue(mockClient); - await entityProvider.onEvent({ + await onEvent({ topic: 'github.team', eventPayload: { action: 'edited', @@ -1660,7 +1676,7 @@ describe('GithubMultiOrgEntityProvider', () => { (graphql.defaults as jest.Mock).mockReturnValue(mockClient); - await entityProvider.onEvent({ + await onEvent({ topic: 'github.membership', eventPayload: { action: 'added', @@ -1833,7 +1849,7 @@ describe('GithubMultiOrgEntityProvider', () => { (graphql.defaults as jest.Mock).mockReturnValue(mockClient); - await entityProvider.onEvent({ + await onEvent({ topic: 'github.membership', eventPayload: { action: 'removed', diff --git a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts index d3943bb66a..51796c2116 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts @@ -38,11 +38,7 @@ import { EntityProvider, EntityProviderConnection, } from '@backstage/plugin-catalog-node'; -import { - EventBroker, - EventParams, - EventSubscriber, -} from '@backstage/plugin-events-node'; +import { EventBroker, EventParams } from '@backstage/plugin-events-node'; import { graphql } from '@octokit/graphql'; import { InstallationCreatedEvent, @@ -161,9 +157,7 @@ type CreateDeltaOperation = (entities: Entity[]) => { * * @public */ -export class GithubMultiOrgEntityProvider - implements EntityProvider, EventSubscriber -{ +export class GithubMultiOrgEntityProvider implements EntityProvider { private connection?: EntityProviderConnection; private scheduleFn?: () => Promise; @@ -200,7 +194,10 @@ export class GithubMultiOrgEntityProvider provider.schedule(options.schedule); if (options.eventBroker) { - options.eventBroker.subscribe(provider); + options.eventBroker.subscribe({ + supportsEventTopics: provider.supportsEventTopics.bind(provider), + onEvent: provider.onEvent.bind(provider), + }); } return provider; @@ -313,8 +310,7 @@ export class GithubMultiOrgEntityProvider markCommitComplete(); } - /** {@inheritdoc @backstage/plugin-events-node#EventSubscriber.supportsEventTopics} */ - supportsEventTopics(): string[] { + private supportsEventTopics(): string[] { return [ 'github.installation', 'github.organization', @@ -323,8 +319,7 @@ export class GithubMultiOrgEntityProvider ]; } - /** {@inheritdoc @backstage/plugin-events-node#EventSubscriber.onEvent} */ - async onEvent(params: EventParams): Promise { + private async onEvent(params: EventParams): Promise { const { logger } = this.options; logger.debug(`Received event from ${params.topic}`); From b779e9875664ee2e966b13d51d91cb615564a8c4 Mon Sep 17 00:00:00 2001 From: Zander Franks Date: Thu, 4 May 2023 12:37:35 -0500 Subject: [PATCH 028/189] fix(scaffolder): requested changes Signed-off-by: Zander Franks --- .changeset/heavy-penguins-burn.md | 4 ++-- .../features/software-templates/writing-templates.md | 4 ++-- .../src/Template.v1beta3.schema.json | 12 ++++++------ plugins/scaffolder-react/api-report.md | 2 +- plugins/scaffolder-react/src/api/types.ts | 2 +- .../TemplateOutputs/DefaultTemplateOutputs.tsx | 2 +- .../next/components/TemplateOutputs/TextOutputs.tsx | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.changeset/heavy-penguins-burn.md b/.changeset/heavy-penguins-burn.md index f9fba9bfb8..b8c243a99b 100644 --- a/.changeset/heavy-penguins-burn.md +++ b/.changeset/heavy-penguins-burn.md @@ -1,6 +1,6 @@ --- -'@backstage/plugin-scaffolder-common': patch -'@backstage/plugin-scaffolder-react': patch +'@backstage/plugin-scaffolder-common': minor +'@backstage/plugin-scaffolder-react': minor --- Add support for Markdown text blob outputs from templates diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index 2508f4a592..44bd5e5928 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -522,8 +522,8 @@ output: icon: catalog entityRef: ${{ steps['register'].output.entityRef }} # link to the entity that has been ingested to the catalog text: - - title: More data - data: | + - title: More information + content: | Access the \[remote repository\](${{ steps['publish'].output.remoteUrl }}). ``` diff --git a/plugins/scaffolder-common/src/Template.v1beta3.schema.json b/plugins/scaffolder-common/src/Template.v1beta3.schema.json index beb86f43cb..1de68205b2 100644 --- a/plugins/scaffolder-common/src/Template.v1beta3.schema.json +++ b/plugins/scaffolder-common/src/Template.v1beta3.schema.json @@ -221,15 +221,15 @@ }, "text": { "type": "array", - "description": "A list of text blobs, like output data from the template.", + "description": "A list of Markdown text blobs, like output data from the template.", "items": { "type": "object", "required": [], "properties": { "title": { "type": "string", - "description": "A user friendly display name for the text blob.", - "examples": ["Output Data"], + "description": "A user friendly display name for the text.", + "examples": ["Output Content"], "minLength": 1 }, "icon": { @@ -238,10 +238,10 @@ "examples": ["dashboard"], "minLength": 1 }, - "data": { + "content": { "type": "string", - "description": "The text blob to display in the UI.", - "examples": ["{}"] + "description": "The text blob to display in the UI, rendered as Markdown.", + "examples": ["**hey** _I'm_ Markdown"] } } } diff --git a/plugins/scaffolder-react/api-report.md b/plugins/scaffolder-react/api-report.md index a2a7dca3b2..1761c88edf 100644 --- a/plugins/scaffolder-react/api-report.md +++ b/plugins/scaffolder-react/api-report.md @@ -221,7 +221,7 @@ export type ScaffolderOutputLink = { export type ScaffolderOutputText = { title?: string; icon?: string; - data?: string; + content?: string; }; // @public diff --git a/plugins/scaffolder-react/src/api/types.ts b/plugins/scaffolder-react/src/api/types.ts index 64bb0d05ac..2adecdd0d2 100644 --- a/plugins/scaffolder-react/src/api/types.ts +++ b/plugins/scaffolder-react/src/api/types.ts @@ -88,7 +88,7 @@ export type ScaffolderOutputLink = { export type ScaffolderOutputText = { title?: string; icon?: string; - data?: string; + content?: string; }; /** @public */ diff --git a/plugins/scaffolder-react/src/next/components/TemplateOutputs/DefaultTemplateOutputs.tsx b/plugins/scaffolder-react/src/next/components/TemplateOutputs/DefaultTemplateOutputs.tsx index de52b62700..a495e4ca9e 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateOutputs/DefaultTemplateOutputs.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateOutputs/DefaultTemplateOutputs.tsx @@ -64,7 +64,7 @@ export const DefaultTemplateOutputs = (props: { titleTypographyProps={{ component: 'h2' }} > - + diff --git a/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx b/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx index 8600a281a0..4a9110d79c 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx @@ -38,7 +38,7 @@ export const TextOutputs = (props: { return ( <> {text - .filter(({ data }) => data) + .filter(({ content }) => content !== undefined) .map(({ title, icon }, i) => { const Icon = iconResolver(icon); return ( From c540b48fc4a569cd71048368714f02c597303d13 Mon Sep 17 00:00:00 2001 From: Alex Crome Date: Thu, 4 May 2023 20:17:34 +0100 Subject: [PATCH 029/189] Update CONTRIBUTING.md Correct code of conduct reference in CONTRIBUTING.md to refer to CNCF's code of conduct, not Spotify's FOSS code of conduct. Signed-off-by: Alex Crome --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7828791080..e324e8a7a9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -92,7 +92,7 @@ For those contributors who have earned write access to the repository, when a pu ## Code of Conduct -This project adheres to the [Spotify FOSS Code of Conduct][code-of-conduct]. By participating, you are expected to honor this code. +This project adheres to the [CNCF Code of Conduct][code-of-conduct]. By participating, you are expected to honor this code. [code-of-conduct]: https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md From bd101cefd3763ca952a05ce32e5de69ad966272c Mon Sep 17 00:00:00 2001 From: danyelleac Date: Thu, 4 May 2023 16:48:40 -0300 Subject: [PATCH 030/189] github-org-edited-team-description Signed-off-by: danyelleac --- .changeset/wet-dolphins-love.md | 5 +++++ .../src/providers/GithubOrgEntityProvider.test.ts | 1 + .../src/providers/GithubOrgEntityProvider.ts | 9 ++++++++- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 .changeset/wet-dolphins-love.md diff --git a/.changeset/wet-dolphins-love.md b/.changeset/wet-dolphins-love.md new file mode 100644 index 0000000000..1b22821f81 --- /dev/null +++ b/.changeset/wet-dolphins-love.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-github': major +--- + +Adding description in function 'onTeamEditedInOrganization` in event of team.edited diff --git a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts index ff42e42718..77f0a050c1 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts @@ -834,6 +834,7 @@ describe('GithubOrgEntityProvider', () => { }, name: 'mygroup-with-spaces', }, + description: 'description-from-the-new-team', apiVersion: 'backstage.io/v1alpha1', kind: 'Group', spec: { diff --git a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts index 02e225c938..c181510a67 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts @@ -371,14 +371,21 @@ export class GithubOrgEntityProvider assignGroupsToUsers(usersToRebuild, groups); buildOrgHierarchy(groups); - const oldName = event.changes.name?.from || ''; + const oldName = event.changes.name?.from || event.team.name; const oldSlug = oldName.toLowerCase().replaceAll(/\s/gi, '-'); + const oldDescription = + event.changes.description?.from || event.team.description; + const oldDescriptionSlug = oldDescription + ?.toLowerCase() + .replaceAll(/\s/gi, '-'); + const { removed } = createDeltaOperation(org, [ { ...group, metadata: { name: oldSlug, + description: oldDescriptionSlug, }, }, ]); From 3ffcdac7d075116468abcc09ff73960d9a9809ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 28 Apr 2023 13:55:17 +0200 Subject: [PATCH 031/189] Added a persistent session store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/thick-pugs-rush.md | 5 + .../migrations/20230428155633_sessions.js | 52 +++++ plugins/auth-backend/package.json | 1 + .../auth-backend/src/database/AuthDatabase.ts | 76 +++++++ .../src/identity/DatabaseKeyStore.test.ts | 187 +++++++++--------- .../src/identity/DatabaseKeyStore.ts | 32 +-- .../src/identity/KeyStores.test.ts | 18 +- .../auth-backend/src/identity/KeyStores.ts | 11 +- plugins/auth-backend/src/migrations.test.ts | 78 ++++++++ plugins/auth-backend/src/service/router.ts | 14 +- yarn.lock | 13 +- 11 files changed, 334 insertions(+), 153 deletions(-) create mode 100644 .changeset/thick-pugs-rush.md create mode 100644 plugins/auth-backend/migrations/20230428155633_sessions.js create mode 100644 plugins/auth-backend/src/database/AuthDatabase.ts create mode 100644 plugins/auth-backend/src/migrations.test.ts diff --git a/.changeset/thick-pugs-rush.md b/.changeset/thick-pugs-rush.md new file mode 100644 index 0000000000..7f0171eb83 --- /dev/null +++ b/.changeset/thick-pugs-rush.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Added a persistent session store through the database diff --git a/plugins/auth-backend/migrations/20230428155633_sessions.js b/plugins/auth-backend/migrations/20230428155633_sessions.js new file mode 100644 index 0000000000..49462ac503 --- /dev/null +++ b/plugins/auth-backend/migrations/20230428155633_sessions.js @@ -0,0 +1,52 @@ +/* + * Copyright 2023 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. + */ + +// @ts-check + +/** + * @param {import('knex').Knex} knex + */ +exports.up = async function up(knex) { + // See https://github.com/gx0r/connect-session-knex + // Modeled loosely after https://github.com/gx0r/connect-session-knex/blob/4e0e36a9afbb13c3000a89f5e341f2d2d4339a02/lib/index.js#L114 + // For simplicity we always make the session a string + // Do NOT change around this table or column names; the connect-session-knex library makes assumptions about them + await knex.schema.createTable('sessions', table => { + table.comment('Session data'); + table.string('sid').primary().notNullable().comment('ID of the session'); + table + .text('sess', 'longtext') + .notNullable() + .comment('Session data, JSON serialized'); + table + .timestamp('expired') + .notNullable() + .comment('The point in time when the session expires'); + table.index('sid', 'sessions_sid_idx'); + table.index('expired', 'sessions_expired_idx'); + }); +}; + +/** + * @param {import('knex').Knex} knex + */ +exports.down = async function down(knex) { + await knex.schema.alterTable('sessions', table => { + table.dropIndex([], 'sessions_sid_idx'); + table.dropIndex([], 'sessions_expired_idx'); + }); + await knex.schema.dropTable('sessions'); +}; diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 61c3fe9a8d..aa57e06708 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -44,6 +44,7 @@ "@types/express": "^4.17.6", "@types/passport": "^1.0.3", "compression": "^1.7.4", + "connect-session-knex": "^3.0.1", "cookie-parser": "^1.4.5", "cors": "^2.8.5", "express": "^4.17.1", diff --git a/plugins/auth-backend/src/database/AuthDatabase.ts b/plugins/auth-backend/src/database/AuthDatabase.ts new file mode 100644 index 0000000000..9c112e0aac --- /dev/null +++ b/plugins/auth-backend/src/database/AuthDatabase.ts @@ -0,0 +1,76 @@ +/* + * Copyright 2023 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. + */ + +import { + DatabaseManager, + PluginDatabaseManager, + resolvePackagePath, +} from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { Knex } from 'knex'; + +const migrationsDir = resolvePackagePath( + '@backstage/plugin-auth-backend', + 'migrations', +); + +/** + * Ensures that a database connection is established exactly once and only when + * asked for, and runs migrations. + */ +export class AuthDatabase { + readonly #database: PluginDatabaseManager; + #promise: Promise | undefined; + + static create(database: PluginDatabaseManager): AuthDatabase { + return new AuthDatabase(database); + } + + static forTesting(): AuthDatabase { + const config = new ConfigReader({ + backend: { + database: { + client: 'better-sqlite3', + connection: ':memory:', + useNullAsDefault: true, + }, + }, + }); + const database = DatabaseManager.fromConfig(config).forPlugin('auth'); + return new AuthDatabase(database); + } + + static async runMigrations(knex: Knex): Promise { + await knex.migrate.latest({ + directory: migrationsDir, + }); + } + + private constructor(database: PluginDatabaseManager) { + this.#database = database; + } + + get(): Promise { + this.#promise ??= this.#database.getClient().then(async client => { + if (!this.#database.migrations?.skip) { + await AuthDatabase.runMigrations(client); + } + return client; + }); + + return this.#promise; + } +} diff --git a/plugins/auth-backend/src/identity/DatabaseKeyStore.test.ts b/plugins/auth-backend/src/identity/DatabaseKeyStore.test.ts index da67a2f88f..913dfcef22 100644 --- a/plugins/auth-backend/src/identity/DatabaseKeyStore.test.ts +++ b/plugins/auth-backend/src/identity/DatabaseKeyStore.test.ts @@ -14,33 +14,10 @@ * limitations under the License. */ -import Knex, { Knex as KnexType } from 'knex'; -import { DatabaseKeyStore } from './DatabaseKeyStore'; import { DateTime } from 'luxon'; - -function createDatabaseManager( - client: KnexType, - skipMigrations: boolean = false, -) { - return { - getClient: async () => client, - migrations: { - skip: skipMigrations, - }, - }; -} - -function createDB() { - const knex = Knex({ - client: 'better-sqlite3', - connection: ':memory:', - useNullAsDefault: true, - }); - knex.client.pool.on('createSuccess', (_eventId: any, resource: any) => { - resource.run('PRAGMA foreign_keys = ON', () => {}); - }); - return knex; -} +import { AuthDatabase } from '../database/AuthDatabase'; +import { DatabaseKeyStore } from './DatabaseKeyStore'; +import { TestDatabases } from '@backstage/backend-test-utils'; const keyBase = { use: 'sig', @@ -48,95 +25,107 @@ const keyBase = { alg: 'Base64', } as const; +jest.setTimeout(60_000); + describe('DatabaseKeyStore', () => { - it('should store a key', async () => { - const client = createDB(); - const store = await DatabaseKeyStore.create({ - database: createDatabaseManager(client), - }); - - const key = { - kid: '123', - ...keyBase, - }; - - await expect(store.listKeys()).resolves.toEqual({ items: [] }); - await store.addKey(key); - - const { items } = await store.listKeys(); - expect(items).toEqual([{ createdAt: expect.anything(), key }]); - expect( - Math.abs( - DateTime.fromJSDate(items[0].createdAt).diffNow('seconds').seconds, - ), - ).toBeLessThan(10); + const databases = TestDatabases.create({ + ids: ['POSTGRES_13', 'POSTGRES_9', 'SQLITE_3'], }); - it('should remove stored keys', async () => { - const client = createDB(); - const store = await DatabaseKeyStore.create({ - database: createDatabaseManager(client), - }); + it.each(databases.eachSupportedId())( + 'should store a key, %p', + async databaseId => { + const knex = await databases.init(databaseId); + await AuthDatabase.runMigrations(knex); - const key1 = { kid: '1', ...keyBase }; - const key2 = { kid: '2', ...keyBase }; - const key3 = { kid: '3', ...keyBase }; + const store = new DatabaseKeyStore(knex); - await store.addKey(key1); - await store.addKey(key2); - await store.addKey(key3); + const key = { + kid: '123', + ...keyBase, + }; - await expect(store.listKeys()).resolves.toEqual({ - items: [ - { key: key1, createdAt: expect.anything() }, - { key: key2, createdAt: expect.anything() }, - { key: key3, createdAt: expect.anything() }, - ], - }); + await expect(store.listKeys()).resolves.toEqual({ items: [] }); + await store.addKey(key); - store.removeKeys(['1']); + const { items } = await store.listKeys(); + expect(items).toEqual([{ createdAt: expect.anything(), key }]); + expect( + Math.abs( + DateTime.fromJSDate(items[0].createdAt).diffNow('seconds').seconds, + ), + ).toBeLessThan(10); + }, + ); - await expect(store.listKeys()).resolves.toEqual({ - items: [ - { key: key2, createdAt: expect.anything() }, - { key: key3, createdAt: expect.anything() }, - ], - }); + it.each(databases.eachSupportedId())( + 'should remove stored keys, %p', + async databaseId => { + const knex = await databases.init(databaseId); + await AuthDatabase.runMigrations(knex); - store.removeKeys(['1', '2']); + const store = new DatabaseKeyStore(knex); - await expect(store.listKeys()).resolves.toEqual({ - items: [{ key: key3, createdAt: expect.anything() }], - }); + const key1 = { kid: '1', ...keyBase }; + const key2 = { kid: '2', ...keyBase }; + const key3 = { kid: '3', ...keyBase }; - store.removeKeys([]); + await store.addKey(key1); + await store.addKey(key2); + await store.addKey(key3); - await expect(store.listKeys()).resolves.toEqual({ - items: [{ key: key3, createdAt: expect.anything() }], - }); + await expect(store.listKeys()).resolves.toEqual({ + items: [ + { key: key1, createdAt: expect.anything() }, + { key: key2, createdAt: expect.anything() }, + { key: key3, createdAt: expect.anything() }, + ], + }); - store.removeKeys(['3', '4']); + await store.removeKeys(['1']); - await expect(store.listKeys()).resolves.toEqual({ - items: [], - }); + await expect(store.listKeys()).resolves.toEqual({ + items: [ + { key: key2, createdAt: expect.anything() }, + { key: key3, createdAt: expect.anything() }, + ], + }); - await store.addKey(key1); - await store.addKey(key2); - await store.addKey(key3); + await store.removeKeys(['1', '2']); - await expect(store.listKeys()).resolves.toEqual({ - items: [ - { key: key1, createdAt: expect.anything() }, - { key: key2, createdAt: expect.anything() }, - { key: key3, createdAt: expect.anything() }, - ], - }); + await expect(store.listKeys()).resolves.toEqual({ + items: [{ key: key3, createdAt: expect.anything() }], + }); - store.removeKeys(['1', '2', '3']); + await store.removeKeys([]); - await expect(store.listKeys()).resolves.toEqual({ - items: [], - }); - }); + await expect(store.listKeys()).resolves.toEqual({ + items: [{ key: key3, createdAt: expect.anything() }], + }); + + await store.removeKeys(['3', '4']); + + await expect(store.listKeys()).resolves.toEqual({ + items: [], + }); + + await store.addKey(key1); + await store.addKey(key2); + await store.addKey(key3); + + await expect(store.listKeys()).resolves.toEqual({ + items: [ + { key: key1, createdAt: expect.anything() }, + { key: key2, createdAt: expect.anything() }, + { key: key3, createdAt: expect.anything() }, + ], + }); + + await store.removeKeys(['1', '2', '3']); + + await expect(store.listKeys()).resolves.toEqual({ + items: [], + }); + }, + ); }); diff --git a/plugins/auth-backend/src/identity/DatabaseKeyStore.ts b/plugins/auth-backend/src/identity/DatabaseKeyStore.ts index 8039837469..0559b698e9 100644 --- a/plugins/auth-backend/src/identity/DatabaseKeyStore.ts +++ b/plugins/auth-backend/src/identity/DatabaseKeyStore.ts @@ -14,19 +14,10 @@ * limitations under the License. */ -import { - PluginDatabaseManager, - resolvePackagePath, -} from '@backstage/backend-common'; import { Knex } from 'knex'; import { DateTime } from 'luxon'; import { AnyJWK, KeyStore, StoredKey } from './types'; -const migrationsDir = resolvePackagePath( - '@backstage/plugin-auth-backend', - 'migrations', -); - const TABLE = 'signing_keys'; type Row = { @@ -35,10 +26,6 @@ type Row = { key: string; }; -type Options = { - database: PluginDatabaseManager; -}; - const parseDate = (date: string | Date) => { const parsedDate = typeof date === 'string' @@ -55,24 +42,7 @@ const parseDate = (date: string | Date) => { }; export class DatabaseKeyStore implements KeyStore { - static async create(options: Options): Promise { - const { database } = options; - const client = await database.getClient(); - - if (!database.migrations?.skip) { - await client.migrate.latest({ - directory: migrationsDir, - }); - } - - return new DatabaseKeyStore(client); - } - - private readonly client: Knex; - - private constructor(client: Knex) { - this.client = client; - } + constructor(private readonly client: Knex) {} async addKey(key: AnyJWK): Promise { await this.client(TABLE).insert({ diff --git a/plugins/auth-backend/src/identity/KeyStores.test.ts b/plugins/auth-backend/src/identity/KeyStores.test.ts index 375db4698b..d186db6c46 100644 --- a/plugins/auth-backend/src/identity/KeyStores.test.ts +++ b/plugins/auth-backend/src/identity/KeyStores.test.ts @@ -14,13 +14,12 @@ * limitations under the License. */ -import { DatabaseManager } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; - -import { MemoryKeyStore } from './MemoryKeyStore'; +import { AuthDatabase } from '../database/AuthDatabase'; import { DatabaseKeyStore } from './DatabaseKeyStore'; import { FirestoreKeyStore } from './FirestoreKeyStore'; import { KeyStores } from './KeyStores'; +import { MemoryKeyStore } from './MemoryKeyStore'; describe('KeyStores', () => { const defaultConfigOptions = { @@ -46,18 +45,9 @@ describe('KeyStores', () => { }); it('can handle without auth config', async () => { - const config = new ConfigReader({ - backend: { - database: { - client: 'better-sqlite3', - connection: ':memory:', - }, - }, + const keyStore = await KeyStores.fromConfig(new ConfigReader({}), { + database: AuthDatabase.forTesting(), }); - const database = - DatabaseManager.fromConfig(config).forPlugin('auth-backend'); - const keyStore = await KeyStores.fromConfig(config, { database }); - expect(keyStore).toBeInstanceOf(DatabaseKeyStore); }); diff --git a/plugins/auth-backend/src/identity/KeyStores.ts b/plugins/auth-backend/src/identity/KeyStores.ts index e29b4b6fc4..d000a96879 100644 --- a/plugins/auth-backend/src/identity/KeyStores.ts +++ b/plugins/auth-backend/src/identity/KeyStores.ts @@ -14,20 +14,20 @@ * limitations under the License. */ -import { Logger } from 'winston'; import { pickBy } from 'lodash'; +import { Logger } from 'winston'; -import { PluginDatabaseManager } from '@backstage/backend-common'; import { Config } from '@backstage/config'; +import { AuthDatabase } from '../database/AuthDatabase'; import { DatabaseKeyStore } from './DatabaseKeyStore'; -import { MemoryKeyStore } from './MemoryKeyStore'; import { FirestoreKeyStore } from './FirestoreKeyStore'; +import { MemoryKeyStore } from './MemoryKeyStore'; import { KeyStore } from './types'; type Options = { logger?: Logger; - database?: PluginDatabaseManager; + database: AuthDatabase; }; export class KeyStores { @@ -52,8 +52,7 @@ export class KeyStores { if (!database) { throw new Error('This KeyStore provider requires a database'); } - - return await DatabaseKeyStore.create({ database }); + return new DatabaseKeyStore(await database.get()); } if (provider === 'memory') { diff --git a/plugins/auth-backend/src/migrations.test.ts b/plugins/auth-backend/src/migrations.test.ts new file mode 100644 index 0000000000..75df0de3e2 --- /dev/null +++ b/plugins/auth-backend/src/migrations.test.ts @@ -0,0 +1,78 @@ +/* + * Copyright 2022 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. + */ + +import { Knex } from 'knex'; +import { TestDatabases } from '@backstage/backend-test-utils'; +import fs from 'fs'; + +const migrationsDir = `${__dirname}/../migrations`; +const migrationsFiles = fs.readdirSync(migrationsDir).sort(); + +async function migrateUpOnce(knex: Knex): Promise { + await knex.migrate.up({ directory: migrationsDir }); +} + +async function migrateDownOnce(knex: Knex): Promise { + await knex.migrate.down({ directory: migrationsDir }); +} + +async function migrateUntilBefore(knex: Knex, target: string): Promise { + const index = migrationsFiles.indexOf(target); + if (index === -1) { + throw new Error(`Migration ${target} not found`); + } + for (let i = 0; i < index; i++) { + await migrateUpOnce(knex); + } +} + +jest.setTimeout(60_000); + +describe('migrations', () => { + const databases = TestDatabases.create({ + ids: ['POSTGRES_13', 'POSTGRES_9', 'SQLITE_3'], + }); + + it.each(databases.eachSupportedId())( + '20230428155633_sessions.js, %p', + async databaseId => { + const knex = await databases.init(databaseId); + + await migrateUntilBefore(knex, '20230428155633_sessions.js'); + await migrateUpOnce(knex); + + // Ensure that large cookies are supported + const data = `{"cookie":"${'a'.repeat(100_000)}"}`; + await knex + .insert({ sid: 'abc', expired: knex.fn.now(), sess: data }) + .into('sessions'); + await knex + .insert({ sid: 'def', expired: knex.fn.now(), sess: data }) + .into('sessions'); + + await expect(knex('sessions').orderBy('sid', 'asc')).resolves.toEqual([ + { sid: 'abc', expired: expect.anything(), sess: data }, + { sid: 'def', expired: expect.anything(), sess: data }, + ]); + + await migrateDownOnce(knex); + + await expect(knex('sessions')).rejects.toThrow(); + + await knex.destroy(); + }, + ); +}); diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index 8013777415..2996ff68be 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -32,9 +32,11 @@ import { CatalogApi, CatalogClient } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import { createOidcRouter, TokenFactory, KeyStores } from '../identity'; import session from 'express-session'; +import connectSessionKnex from 'connect-session-knex'; import passport from 'passport'; import { Minimatch } from 'minimatch'; import { CatalogAuthResolverContext } from '../lib/resolvers'; +import { AuthDatabase } from '../database/AuthDatabase'; /** @public */ export type ProviderFactories = { [s: string]: AuthProviderFactory }; @@ -70,7 +72,11 @@ export async function createRouter( const appUrl = config.getString('app.baseUrl'); const authUrl = await discovery.getExternalBaseUrl('auth'); - const keyStore = await KeyStores.fromConfig(config, { logger, database }); + const authDb = AuthDatabase.create(database); + const keyStore = await KeyStores.fromConfig(config, { + logger, + database: authDb, + }); const keyDurationSeconds = 3600; const tokenIssuer = new TokenFactory({ @@ -84,14 +90,18 @@ export async function createRouter( const secret = config.getOptionalString('auth.session.secret'); if (secret) { router.use(cookieParser(secret)); - // TODO: Configure the server-side session storage. The default MemoryStore is not designed for production const enforceCookieSSL = authUrl.startsWith('https'); + const KnexSessionStore = connectSessionKnex(session); router.use( session({ secret, saveUninitialized: false, resave: false, cookie: { secure: enforceCookieSSL ? 'auto' : false }, + store: new KnexSessionStore({ + createtable: false, + knex: await authDb.get(), + }), }), ); router.use(passport.initialize()); diff --git a/yarn.lock b/yarn.lock index d9be5d6064..f0648b5a13 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4796,6 +4796,7 @@ __metadata: "@types/passport-strategy": ^0.2.35 "@types/xml2js": ^0.4.7 compression: ^1.7.4 + connect-session-knex: ^3.0.1 cookie-parser: ^1.4.5 cors: ^2.8.5 express: ^4.17.1 @@ -20544,6 +20545,16 @@ __metadata: languageName: node linkType: hard +"connect-session-knex@npm:^3.0.1": + version: 3.0.1 + resolution: "connect-session-knex@npm:3.0.1" + dependencies: + bluebird: ^3.7.2 + knex: ^2.3.0 + checksum: f5a80c3c34d30e7cd4e79a6aae09d112825986d51ddc3a4563ef95ade425178239d4e3e05420fb3b6204b2353b0dfd754d318b288989b539583f6c0e52758b7a + languageName: node + linkType: hard + "consola@npm:^2.15.0": version: 2.15.3 resolution: "consola@npm:2.15.3" @@ -28982,7 +28993,7 @@ __metadata: languageName: node linkType: hard -"knex@npm:^2.0.0, knex@npm:^2.4.2": +"knex@npm:^2.0.0, knex@npm:^2.3.0, knex@npm:^2.4.2": version: 2.4.2 resolution: "knex@npm:2.4.2" dependencies: From 6028b54ebf101caa7db08146dd3605b3408823f9 Mon Sep 17 00:00:00 2001 From: danyelleac Date: Thu, 4 May 2023 17:10:01 -0300 Subject: [PATCH 032/189] test-github-org-edited-team-description Signed-off-by: danyelleac --- .../src/providers/GithubOrgEntityProvider.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts index 77f0a050c1..3e58c1473f 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts @@ -833,8 +833,8 @@ describe('GithubOrgEntityProvider', () => { 'url:https://github.com/orgs/backstage/teams/mygroup-with-spaces', }, name: 'mygroup-with-spaces', + description: 'description-from-the-new-team', }, - description: 'description-from-the-new-team', apiVersion: 'backstage.io/v1alpha1', kind: 'Group', spec: { From af321b324c36910d08f3b1bf1172c6c49f65a00c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 4 May 2023 22:10:14 +0200 Subject: [PATCH 033/189] address comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- plugins/auth-backend/src/database/AuthDatabase.ts | 1 + .../auth-backend/src/identity/KeyStores.test.ts | 12 ++++++++++-- plugins/auth-backend/src/identity/KeyStores.ts | 14 ++++---------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/plugins/auth-backend/src/database/AuthDatabase.ts b/plugins/auth-backend/src/database/AuthDatabase.ts index 9c112e0aac..97e4236754 100644 --- a/plugins/auth-backend/src/database/AuthDatabase.ts +++ b/plugins/auth-backend/src/database/AuthDatabase.ts @@ -39,6 +39,7 @@ export class AuthDatabase { return new AuthDatabase(database); } + /** @internal */ static forTesting(): AuthDatabase { const config = new ConfigReader({ backend: { diff --git a/plugins/auth-backend/src/identity/KeyStores.test.ts b/plugins/auth-backend/src/identity/KeyStores.test.ts index d186db6c46..6c034eb64b 100644 --- a/plugins/auth-backend/src/identity/KeyStores.test.ts +++ b/plugins/auth-backend/src/identity/KeyStores.test.ts @@ -20,6 +20,7 @@ import { DatabaseKeyStore } from './DatabaseKeyStore'; import { FirestoreKeyStore } from './FirestoreKeyStore'; import { KeyStores } from './KeyStores'; import { MemoryKeyStore } from './MemoryKeyStore'; +import { getVoidLogger } from '@backstage/backend-common'; describe('KeyStores', () => { const defaultConfigOptions = { @@ -33,7 +34,10 @@ describe('KeyStores', () => { it('reads auth section from config', async () => { const configSpy = jest.spyOn(defaultConfig, 'getOptionalConfig'); - const keyStore = await KeyStores.fromConfig(defaultConfig); + const keyStore = await KeyStores.fromConfig(defaultConfig, { + logger: getVoidLogger(), + database: AuthDatabase.forTesting(), + }); expect(keyStore).toBeInstanceOf(MemoryKeyStore); expect(configSpy).toHaveBeenCalledWith('auth.keyStore'); @@ -46,6 +50,7 @@ describe('KeyStores', () => { it('can handle without auth config', async () => { const keyStore = await KeyStores.fromConfig(new ConfigReader({}), { + logger: getVoidLogger(), database: AuthDatabase.forTesting(), }); expect(keyStore).toBeInstanceOf(DatabaseKeyStore); @@ -72,7 +77,10 @@ describe('KeyStores', () => { }, }; const config = new ConfigReader(configOptions); - const keyStore = await KeyStores.fromConfig(config); + const keyStore = await KeyStores.fromConfig(config, { + logger: getVoidLogger(), + database: AuthDatabase.forTesting(), + }); expect(keyStore).toBeInstanceOf(FirestoreKeyStore); expect(createSpy).toHaveBeenCalledWith( diff --git a/plugins/auth-backend/src/identity/KeyStores.ts b/plugins/auth-backend/src/identity/KeyStores.ts index d000a96879..4cd8f91842 100644 --- a/plugins/auth-backend/src/identity/KeyStores.ts +++ b/plugins/auth-backend/src/identity/KeyStores.ts @@ -26,7 +26,7 @@ import { MemoryKeyStore } from './MemoryKeyStore'; import { KeyStore } from './types'; type Options = { - logger?: Logger; + logger: Logger; database: AuthDatabase; }; @@ -37,21 +37,15 @@ export class KeyStores { * * @returns a KeyStore store */ - static async fromConfig( - config: Config, - options?: Options, - ): Promise { - const { logger, database } = options ?? {}; + static async fromConfig(config: Config, options: Options): Promise { + const { logger, database } = options; const ks = config.getOptionalConfig('auth.keyStore'); const provider = ks?.getOptionalString('provider') ?? 'database'; - logger?.info(`Configuring "${provider}" as KeyStore provider`); + logger.info(`Configuring "${provider}" as KeyStore provider`); if (provider === 'database') { - if (!database) { - throw new Error('This KeyStore provider requires a database'); - } return new DatabaseKeyStore(await database.get()); } From 17afe1a0bf0f8de7c64e5a394d0fbae84823beba Mon Sep 17 00:00:00 2001 From: Tim Soslow Date: Thu, 4 May 2023 17:27:46 -0500 Subject: [PATCH 034/189] Update action column with both view & replay icons Signed-off-by: Tim Soslow --- .changeset/real-baboons-vanish.md | 2 +- plugins/jenkins/README.md | 18 ---- plugins/jenkins/api-report.md | 2 +- .../src/assets/customize_action_view.png | Bin 52575 -> 0 bytes plugins/jenkins/src/assets/folder-results.png | Bin 61908 -> 58657 bytes .../BuildsPage/lib/CITable/CITable.tsx | 79 ++++-------------- plugins/jenkins/src/options.ts | 29 ------- plugins/jenkins/src/plugin.ts | 9 -- 8 files changed, 16 insertions(+), 123 deletions(-) delete mode 100644 plugins/jenkins/src/assets/customize_action_view.png delete mode 100644 plugins/jenkins/src/options.ts diff --git a/.changeset/real-baboons-vanish.md b/.changeset/real-baboons-vanish.md index 93c30d8389..9c4a19cd49 100644 --- a/.changeset/real-baboons-vanish.md +++ b/.changeset/real-baboons-vanish.md @@ -2,4 +2,4 @@ '@backstage/plugin-jenkins': minor --- -Added configuration for the action column in Jenkins CI/CD table. It can be set to rebuild, view, or replay. +Updated action column in Jenkins CI/CD table to include links to view and replay. The API based rebuild action was replaced because it does not work for Jenkins workflows that have parameters. diff --git a/plugins/jenkins/README.md b/plugins/jenkins/README.md index 12c6a197da..2a0ec451c8 100644 --- a/plugins/jenkins/README.md +++ b/plugins/jenkins/README.md @@ -86,24 +86,6 @@ spec: 8. Click the component in the catalog. You should now see Jenkins builds, and a last build result for your master build. -## Customize Action - -You can customize the action in the CI/CD table. - -| Action | Description | -| ----------------- | ----------------------- | -| rebuild (default) | Run new build with API | -| view | link to view build page | -| replay | link to start replay | - -```yaml -jenkinsPlugin.__experimentalReconfigure({ - tableAction: 'view', -}); -``` - -Customized action - view - ## Features - View all runs inside a folder diff --git a/plugins/jenkins/api-report.md b/plugins/jenkins/api-report.md index bcba48e4b2..a2ae16988b 100644 --- a/plugins/jenkins/api-report.md +++ b/plugins/jenkins/api-report.md @@ -98,7 +98,7 @@ const jenkinsPlugin: BackstagePlugin< entityContent: RouteRef; }, {}, - JenkinsInputPluginOptions + {} >; export { jenkinsPlugin }; export { jenkinsPlugin as plugin }; diff --git a/plugins/jenkins/src/assets/customize_action_view.png b/plugins/jenkins/src/assets/customize_action_view.png deleted file mode 100644 index 8be43c5971e6f764abd7d0c010dd967784271828..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52575 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

G%Gf7bS0P$yaBcGYKD$3fm^*iFx?)WFG@#@{`o4 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 z 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

Anlwd% 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 diff --git a/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx b/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx index b0a8b84718..6fc5f2d35c 100644 --- a/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx +++ b/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx @@ -13,20 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Link, Progress, Table, TableColumn } from '@backstage/core-components'; -import { alertApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api'; -import { useEntityPermission } from '@backstage/plugin-catalog-react/alpha'; -import { Box, IconButton, Tooltip, Typography } from '@material-ui/core'; +import { Link, Table, TableColumn } from '@backstage/core-components'; +import { useRouteRef } from '@backstage/core-plugin-api'; +import { Box, IconButton, Typography } from '@material-ui/core'; import RetryIcon from '@material-ui/icons/Replay'; import VisibilityIcon from '@material-ui/icons/Visibility'; -import { default as React, useState } from 'react'; +import { default as React } from 'react'; import { Project } from '../../../../api/JenkinsApi'; import JenkinsLogo from '../../../../assets/JenkinsLogo.svg'; import { buildRouteRef } from '../../../../plugin'; import { useBuilds } from '../../../useBuilds'; import { JenkinsRunStatus } from '../Status'; -import { jenkinsExecutePermission } from '@backstage/plugin-jenkins-common'; -import { useJenkinsPluginOptions } from '../../../../options'; const FailCount = ({ count }: { count: number }): JSX.Element | null => { if (count !== 0) { @@ -176,64 +173,16 @@ const generatedColumns: TableColumn[] = [ title: 'Actions', sorting: false, render: (row: Partial) => { - const ActionWrapper = () => { - const [isLoadingRebuild, setIsLoadingRebuild] = useState(false); - const { allowed, loading } = useEntityPermission( - jenkinsExecutePermission, - ); - - const alertApi = useApi(alertApiRef); - - const onRebuild = async () => { - if (row.onRestartClick) { - setIsLoadingRebuild(true); - try { - await row.onRestartClick(); - alertApi.post({ - message: 'Jenkins re-build has successfully executed', - severity: 'success', - }); - } catch (e) { - alertApi.post({ - message: `Jenkins re-build has failed. Error: ${e.message}`, - severity: 'error', - }); - } finally { - setIsLoadingRebuild(false); - } - } - }; - - const { tableAction } = useJenkinsPluginOptions(); - - if (tableAction === 'view') { - return row.lastBuild?.url ? ( - - - - ) : null; - } else if (tableAction === 'replay') { - return row.lastBuild?.url ? ( - - - - ) : null; - } - - return ( - - <> - {isLoadingRebuild && } - {!isLoadingRebuild && ( - - - - )} - - - ); - }; - return ; + return row.lastBuild?.url ? ( +

+ + + + + + +
+ ) : null; }, width: '10%', }, diff --git a/plugins/jenkins/src/options.ts b/plugins/jenkins/src/options.ts deleted file mode 100644 index f78e6a75e9..0000000000 --- a/plugins/jenkins/src/options.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2020 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. - */ - -import { usePluginOptions } from '@backstage/core-plugin-api/alpha'; - -export type JenkinsPluginOptions = { - tableAction: string; -}; - -/** @ignore */ -export type JenkinsInputPluginOptions = { - tableAction: string; -}; - -export const useJenkinsPluginOptions = () => - usePluginOptions(); diff --git a/plugins/jenkins/src/plugin.ts b/plugins/jenkins/src/plugin.ts index ddef0a7dfd..7dbb6bfde1 100644 --- a/plugins/jenkins/src/plugin.ts +++ b/plugins/jenkins/src/plugin.ts @@ -25,7 +25,6 @@ import { identityApiRef, } from '@backstage/core-plugin-api'; import { JenkinsClient, jenkinsApiRef } from './api'; -import { JenkinsInputPluginOptions, JenkinsPluginOptions } from './options'; /** @public */ export const rootRouteRef = createRouteRef({ @@ -53,14 +52,6 @@ export const jenkinsPlugin = createPlugin({ routes: { entityContent: rootRouteRef, }, - __experimentalConfigure( - options?: JenkinsInputPluginOptions, - ): JenkinsPluginOptions { - const defaultOptions = { - tableAction: 'rebuild', - }; - return { ...defaultOptions, ...options }; - }, }); /** @public */ From bd5b60b80f8b627d89004287bf68188b15b34ad3 Mon Sep 17 00:00:00 2001 From: mipolak <40818407+mipolak@users.noreply.github.com> Date: Fri, 5 May 2023 10:35:33 +0200 Subject: [PATCH 035/189] added prerequisites added prerequisites make and build-essential Signed-off-by: mipolak <40818407+mipolak@users.noreply.github.com> --- docs/getting-started/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index 7398e9a311..cf2b48091d 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -21,6 +21,7 @@ you to use the [Contributors](contributors.md) guide to do a repository-based in - Access to a Unix-based operating system, such as Linux, MacOS or [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/) +- `make` and `build-essential` installed - An account with elevated rights to install the dependencies - `curl` or `wget` installed - Node.js [Active LTS Release](https://nodejs.org/en/blog/release/) installed using one of these From ae397f8ef8c009294bda9effda95b2bd9ce4ddf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Vit=C3=A1sek?= Date: Fri, 5 May 2023 11:15:12 +0200 Subject: [PATCH 036/189] New `xkcd comic` plugin to the market place microsite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ladislav Vitásek --- microsite/data/plugins/xkcd.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 microsite/data/plugins/xkcd.yaml diff --git a/microsite/data/plugins/xkcd.yaml b/microsite/data/plugins/xkcd.yaml new file mode 100644 index 0000000000..370718a08f --- /dev/null +++ b/microsite/data/plugins/xkcd.yaml @@ -0,0 +1,10 @@ +--- +title: xkcd comic +author: Vity +authorUrl: https://github.com/Vity01 +category: Humor +description: xkcd comic within Backstage +documentation: https://github.com/Vity01/backstage-xkcd/ +iconUrl: https://raw.githubusercontent.com/Vity01/backstage-xkcd/main/docs/xkcd.svg +npmPackageName: 'backstage-plugin-xkcd' +addedDate: '2023-05-05' From 447172cb6ac5f29e957b6529eecd4fe21402d93e Mon Sep 17 00:00:00 2001 From: PIYUSH NEGI <43876655+npiyush97@users.noreply.github.com> Date: Fri, 5 May 2023 22:08:29 +0530 Subject: [PATCH 037/189] Update docusaurus.config.js related to fix => https://github.com/backstage/backstage/issues/17620 forget to add link here Signed-off-by: PIYUSH NEGI <43876655+npiyush97@users.noreply.github.com> --- microsite/docusaurus.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/docusaurus.config.js b/microsite/docusaurus.config.js index e6137b572b..d0f6bb277f 100644 --- a/microsite/docusaurus.config.js +++ b/microsite/docusaurus.config.js @@ -241,7 +241,7 @@ module.exports = { }, { label: 'Subscribe to our newsletter', - to: 'https://mailchi.mp/spotify/backstage-community', + to: 'https://info.backstage.spotify.com/newsletter_subscribe', }, { label: 'CNCF Incubation', From 977aa9130023c003b29286950bde0e02d7c4c055 Mon Sep 17 00:00:00 2001 From: Zander Date: Fri, 5 May 2023 11:55:25 -0500 Subject: [PATCH 038/189] feat(scaffolder): DefaultTemplateOutputs test Signed-off-by: Zander --- .../DefaultTemplateOutputs.test.tsx | 56 +++++++++++++++++++ .../TemplateOutputs/TextOutputs.tsx | 1 + 2 files changed, 57 insertions(+) create mode 100644 plugins/scaffolder-react/src/next/components/TemplateOutputs/DefaultTemplateOutputs.test.tsx diff --git a/plugins/scaffolder-react/src/next/components/TemplateOutputs/DefaultTemplateOutputs.test.tsx b/plugins/scaffolder-react/src/next/components/TemplateOutputs/DefaultTemplateOutputs.test.tsx new file mode 100644 index 0000000000..8366dcadf8 --- /dev/null +++ b/plugins/scaffolder-react/src/next/components/TemplateOutputs/DefaultTemplateOutputs.test.tsx @@ -0,0 +1,56 @@ +/* + * Copyright 2022 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. + */ + +import { entityRouteRef } from '@backstage/plugin-catalog-react'; +import { renderInTestApp } from '@backstage/test-utils'; +import { fireEvent } from '@testing-library/react'; +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { DefaultTemplateOutputs } from '.'; + +describe('', () => { + it('should render template output', async () => { + const output = { + links: [{ title: 'Link 1', url: 'https://backstage.io/' }], + text: [{ title: 'Text 1', content: 'Hello, **world**!' }], + }; + + const { getByRole } = await renderInTestApp( + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ); + + // test link outputs + for (const link of output.links ?? []) { + expect( + getByRole('button', { name: link.title }).closest('a'), + ).toHaveAttribute('href', link.url); + } + + // test text outputs + for (const text of output.text ?? []) { + await act(async () => { + fireEvent.click(getByRole('button', { name: text.title })); + }); + + expect(getByRole('heading', { level: 2 }).innerHTML).toBe(text.title); + } + }); +}); diff --git a/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx b/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx index 4a9110d79c..7e64603e92 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx @@ -43,6 +43,7 @@ export const TextOutputs = (props: { const Icon = iconResolver(icon); return (