Skip to content

Commit

Permalink
feat: add owner filter
Browse files Browse the repository at this point in the history
  • Loading branch information
dweber019 committed Aug 31, 2024
1 parent a5c52a6 commit 13a5044
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 18 deletions.
6 changes: 6 additions & 0 deletions .changeset/quick-camels-explain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@dweber019/backstage-plugin-missing-entity-backend': patch
'@dweber019/backstage-plugin-missing-entity': patch
---

Add owner filter.
2 changes: 2 additions & 0 deletions app-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ catalog:
- allow: [Component, System, API, Resource, Location, Group, User]
locations:
# Local example data, file locations are relative to the backend process, typically `packages/backend`
- type: file
target: ../entities/development-guest.yaml
- type: file
target: ../entities/wsdl-api-example.yaml
- type: file
Expand Down
10 changes: 10 additions & 0 deletions packages/entities/development-guest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: backstage.io/v1alpha1
kind: User
metadata:
name: guest
namespace: development
spec:
profile:
displayName: Guest
email: guest@example.com
memberOf: []
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { NotificationService } from '@backstage/plugin-notifications-node';
/** @public */
export interface MissingEntityBackendApi {
getMissingEntities(entityRef: string, refresh: boolean): Promise<EntityMissingResults>;
getAllMissingEntities(onlyWithMissing: boolean): Promise<EntitiesPageResult>;
getAllMissingEntities(onlyWithMissing: boolean, owner: string | undefined): Promise<EntitiesPageResult>;
processEntities(): Promise<void>;
}

Expand Down Expand Up @@ -68,11 +68,29 @@ export class MissingEntityBackendClient implements MissingEntityBackendApi {
return missingEntities!;
}

async getAllMissingEntities(onlyWithMissing = false): Promise<EntitiesPageResult> {
async getAllMissingEntities(onlyWithMissing = false, owner: string | undefined): Promise<EntitiesPageResult> {
this.logger?.debug(`Getting all missing entities"`);

let entityResult: EntityMissingResults[] = [];
if (owner) {
const { token } = await this.auth.getPluginRequestToken({
onBehalfOf: await this.auth.getOwnServiceCredentials(),
targetPluginId: 'catalog',
});
const response = await this.catalogApi.queryEntities({
filter: {
'relations.ownedBy': owner.split(','),
},
fields: [ 'kind', 'metadata.name', 'metadata.namespace' ]
}, { token });
entityResult = await this.store.getEntitiesByRefs(response.items.map(item => stringifyEntityRef(item)), onlyWithMissing);
} else {
entityResult = await this.store.getAllEntities(onlyWithMissing);
}

return {
overview: await this.getEntitiesOverview(),
entities: await this.store.getAllEntities(onlyWithMissing),
entities: entityResult,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface MissingEntityBackendStore {
getProcessedEntities(): Promise<ProcessedEntity[]>;
getUnprocessedEntities(): Promise<string[]>;
getAllEntities(onlyWithMissing: boolean): Promise<EntityMissingResults[]>;
getEntitiesByRefs(entityRefs: string[], onlyWithMissing: boolean): Promise<EntityMissingResults[]>;
deleteEntity(entityRef: string): Promise<void>;
}

Expand Down Expand Up @@ -68,7 +69,7 @@ export class MissingEntityBackendDatabase implements MissingEntityBackendStore {
try {
return {
entityRef: entityResults.entity_ref,
missingEntityRefs: JSON.parse(entityResults.entity_refs_missing),
missingEntityRefs: entityResults.entity_refs_missing ? JSON.parse(entityResults.entity_refs_missing) : [],
};
} catch (error) {
throw new Error(`Failed to query missing entity for '${entityRef}', ${error}`);
Expand Down Expand Up @@ -136,14 +137,22 @@ export class MissingEntityBackendDatabase implements MissingEntityBackendStore {
return rawEntities.map(rawEntity => {
return {
entityRef: rawEntity.entity_ref,
missingEntityRefs: JSON.parse(rawEntity.entity_refs_missing),
missingEntityRefs: rawEntity.entity_refs_missing ? JSON.parse(rawEntity.entity_refs_missing) : [],
};
});
}

async getEntitiesByRefs(entityRefs: string[]): Promise<EntityMissingResults[]> {
const rawEntities = await this.db<RawDbRow>('missing_entity')
.whereIn('entity_ref', entityRefs);
async getEntitiesByRefs(entityRefs: string[], onlyWithMissing = false): Promise<EntityMissingResults[]> {
let rawEntities;
if (onlyWithMissing) {
rawEntities = await this.db<RawDbRow>('missing_entity')
.whereIn('entity_ref', entityRefs).and
.whereNotNull('entity_refs_missing')
.andWhereRaw('LENGTH(entity_refs_missing) > 2');
} else {
rawEntities = await this.db<RawDbRow>('missing_entity')
.whereIn('entity_ref', entityRefs);
}

if (!rawEntities) {
return [];
Expand All @@ -152,7 +161,7 @@ export class MissingEntityBackendDatabase implements MissingEntityBackendStore {
return rawEntities.map(rawEntity => {
return {
entityRef: rawEntity.entity_ref,
missingEntityRefs: JSON.parse(rawEntity.entity_refs_missing),
missingEntityRefs: rawEntity.entity_refs_missing ? JSON.parse(rawEntity.entity_refs_missing) : [],
};
});
}
Expand Down
5 changes: 3 additions & 2 deletions plugins/missing-entity-backend/src/service/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,11 @@ export async function createRouter(
* /entities?onlyWithMissing=true
*/
router.get('/entities', async (req, res) => {
const { onlyWithMissing: onlyWithMissing } = req.query;
const { onlyWithMissing: onlyWithMissing, owner: owner } = req.query;

const missingEntities = await missingEntityBackendClient.getAllMissingEntities(
onlyWithMissing === undefined ? true : (onlyWithMissing === 'true')
onlyWithMissing === undefined ? true : (onlyWithMissing === 'true'),
owner as string | undefined,
);
res.status(200).json(missingEntities);
});
Expand Down
2 changes: 1 addition & 1 deletion plugins/missing-entity/examples/resource-1.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
apiVersion: backstage.io/v1alpha1
kind: resource
kind: Resource
metadata:
name: missing-entity
spec:
Expand Down
2 changes: 1 addition & 1 deletion plugins/missing-entity/src/api/MissingEntityApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export const missingEntityApiRef = createApiRef<MissingEntityApi>({

export interface MissingEntityApi {
getMissingEntities(entityRef: string, refresh: boolean): Promise<EntityMissingResults>;
getAllMissingEntities(onlyWithMissing: boolean): Promise<EntitiesPageResult>;
getAllMissingEntities(onlyWithMissing: boolean, owner: string | undefined): Promise<EntitiesPageResult>;
}
5 changes: 4 additions & 1 deletion plugins/missing-entity/src/api/MissingEntityClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ export class MissingEntityClient implements MissingEntityApi {
return await this.get<EntityMissingResults>(urlSegment);
}

public async getAllMissingEntities(onlyWithMissing = false): Promise<EntitiesPageResult> {
public async getAllMissingEntities(onlyWithMissing = false, owner: string | undefined): Promise<EntitiesPageResult> {
const queryString = new URLSearchParams();
queryString.append('onlyWithMissing', `${onlyWithMissing}`);
if (owner) {
queryString.append('owner', `${owner}`);
}

const urlSegment = `entities?${queryString}`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from '@backstage/core-components';
import Alert from '@material-ui/lab/Alert';
import React, { useCallback, useState } from 'react';
import { useApi } from '@backstage/core-plugin-api';
import { identityApiRef, useApi } from '@backstage/core-plugin-api';
import { missingEntityApiRef } from '../../api';
import { EntityMissingResults } from '@dweber019/backstage-plugin-missing-entity-common';
import { EntityRefLink } from '@backstage/plugin-catalog-react';
Expand All @@ -18,11 +18,14 @@ import { useAsyncRetry } from 'react-use';

export const MissingEntityPage = () => {
const missingEntityApi = useApi(missingEntityApiRef);
const identityApi = useApi(identityApiRef);
const [withMissing, setWithMissing] = useState(true);
const [myEntities, setMyEntities] = useState(true);

const { value, loading, error, retry } = useAsyncRetry(() => {
return missingEntityApi.getAllMissingEntities(withMissing);
}, [missingEntityApi, withMissing]);
const { value, loading, error, retry } = useAsyncRetry(async () => {
const userIdentity = await identityApi.getBackstageIdentity();
return missingEntityApi.getAllMissingEntities(withMissing, myEntities ? userIdentity.ownershipEntityRefs.join(',') : undefined);
}, [missingEntityApi, identityApi, withMissing, myEntities]);

const reloadEntity = useCallback(async (entityRef: string) => {
await missingEntityApi.getMissingEntities(entityRef, true);
Expand Down Expand Up @@ -80,6 +83,21 @@ export const MissingEntityPage = () => {
},
]}
/>
<Select
onChange={selected => setMyEntities(Boolean(selected))}
selected={1}
label="Only my entities"
items={[
{
label: 'Yes',
value: 1,
},
{
label: 'No',
value: 0,
},
]}
/>
</Grid>
<Grid item xs={10}>
<Table
Expand Down

0 comments on commit 13a5044

Please sign in to comment.