catalog-backend: migrate to support new auth services
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
---
|
||||
|
||||
Migrated to support new auth services. The `CatalogBuilder.create` method now accepts a `discovery` option, which is recommended to forward from the plugin environment, as it will otherwise fall back to use the `HostDiscovery` implementation.
|
||||
@@ -11,6 +11,7 @@ import { AnalyzeLocationGenerateEntity as AnalyzeLocationGenerateEntity_2 } from
|
||||
import { AnalyzeLocationRequest as AnalyzeLocationRequest_2 } from '@backstage/plugin-catalog-common';
|
||||
import { AnalyzeLocationResponse as AnalyzeLocationResponse_2 } from '@backstage/plugin-catalog-common';
|
||||
import { AnalyzeOptions as AnalyzeOptions_2 } from '@backstage/plugin-catalog-node';
|
||||
import { AuthService } from '@backstage/backend-plugin-api';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { CatalogCollatorEntityTransformer as CatalogCollatorEntityTransformer_2 } from '@backstage/plugin-search-backend-module-catalog';
|
||||
import { CatalogEntityDocument } from '@backstage/plugin-catalog-common';
|
||||
@@ -28,6 +29,7 @@ import { Config } from '@backstage/config';
|
||||
import { DefaultCatalogCollatorFactory as DefaultCatalogCollatorFactory_2 } from '@backstage/plugin-search-backend-module-catalog';
|
||||
import { DefaultCatalogCollatorFactoryOptions as DefaultCatalogCollatorFactoryOptions_2 } from '@backstage/plugin-search-backend-module-catalog';
|
||||
import { DeferredEntity as DeferredEntity_2 } from '@backstage/plugin-catalog-node';
|
||||
import { DiscoveryService } from '@backstage/backend-plugin-api';
|
||||
import { EntitiesSearchFilter as EntitiesSearchFilter_2 } from '@backstage/plugin-catalog-node';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntityFilter as EntityFilter_2 } from '@backstage/plugin-catalog-node';
|
||||
@@ -38,15 +40,16 @@ import { EntityProviderMutation as EntityProviderMutation_2 } from '@backstage/p
|
||||
import { EntityRelationSpec as EntityRelationSpec_2 } from '@backstage/plugin-catalog-node';
|
||||
import { EventBroker } from '@backstage/plugin-events-node';
|
||||
import { GetEntitiesRequest } from '@backstage/catalog-client';
|
||||
import { HttpAuthService } from '@backstage/backend-plugin-api';
|
||||
import { LocationSpec as LocationSpec_2 } from '@backstage/plugin-catalog-common';
|
||||
import { locationSpecToLocationEntity as locationSpecToLocationEntity_2 } from '@backstage/plugin-catalog-node';
|
||||
import { locationSpecToMetadataName as locationSpecToMetadataName_2 } from '@backstage/plugin-catalog-node';
|
||||
import { Logger } from 'winston';
|
||||
import { Permission } from '@backstage/plugin-permission-common';
|
||||
import { PermissionAuthorizer } from '@backstage/plugin-permission-common';
|
||||
import { PermissionEvaluator } from '@backstage/plugin-permission-common';
|
||||
import { PermissionRule } from '@backstage/plugin-permission-node';
|
||||
import { PermissionRuleParams } from '@backstage/plugin-permission-common';
|
||||
import { PermissionsService } from '@backstage/backend-plugin-api';
|
||||
import { PlaceholderResolver as PlaceholderResolver_2 } from '@backstage/plugin-catalog-node';
|
||||
import { PlaceholderResolverParams as PlaceholderResolverParams_2 } from '@backstage/plugin-catalog-node';
|
||||
import { PlaceholderResolverRead as PlaceholderResolverRead_2 } from '@backstage/plugin-catalog-node';
|
||||
@@ -189,8 +192,11 @@ export type CatalogEnvironment = {
|
||||
database: PluginDatabaseManager;
|
||||
config: Config;
|
||||
reader: UrlReader;
|
||||
permissions: PermissionEvaluator | PermissionAuthorizer;
|
||||
permissions: PermissionsService | PermissionAuthorizer;
|
||||
scheduler?: PluginTaskScheduler;
|
||||
discovery?: DiscoveryService;
|
||||
auth?: AuthService;
|
||||
httpAuth?: HttpAuthService;
|
||||
};
|
||||
|
||||
// @public
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BackstageCredentials } from '@backstage/backend-plugin-api';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntityFilter } from '@backstage/plugin-catalog-node';
|
||||
|
||||
@@ -48,7 +49,7 @@ export type EntitiesRequest = {
|
||||
fields?: (entity: Entity) => Entity;
|
||||
order?: EntityOrder[];
|
||||
pagination?: EntityPagination;
|
||||
authorizationToken?: string;
|
||||
credentials: BackstageCredentials;
|
||||
};
|
||||
|
||||
export type EntitiesResponse = {
|
||||
@@ -75,9 +76,9 @@ export interface EntitiesBatchRequest {
|
||||
*/
|
||||
fields?: (entity: Entity) => Entity;
|
||||
/**
|
||||
* The optional token that authorizes the action.
|
||||
* The credentials that authorizes the action.
|
||||
*/
|
||||
authorizationToken?: string;
|
||||
credentials: BackstageCredentials;
|
||||
}
|
||||
|
||||
export interface EntitiesBatchResponse {
|
||||
@@ -115,9 +116,9 @@ export interface EntityFacetsRequest {
|
||||
*/
|
||||
facets: string[];
|
||||
/**
|
||||
* The optional token that authorizes the action.
|
||||
* The credentials that authorizes the action.
|
||||
*/
|
||||
authorizationToken?: string;
|
||||
credentials: BackstageCredentials;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,7 +158,7 @@ export interface EntitiesCatalog {
|
||||
*/
|
||||
removeEntityByUid(
|
||||
uid: string,
|
||||
options?: { authorizationToken?: string },
|
||||
options: { credentials: BackstageCredentials },
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
@@ -167,7 +168,7 @@ export interface EntitiesCatalog {
|
||||
*/
|
||||
entityAncestry(
|
||||
entityRef: string,
|
||||
options?: { authorizationToken?: string },
|
||||
options: { credentials: BackstageCredentials },
|
||||
): Promise<EntityAncestryResponse>;
|
||||
|
||||
/**
|
||||
@@ -192,7 +193,7 @@ export type QueryEntitiesRequest =
|
||||
* for the current and the next pagination requests.
|
||||
*/
|
||||
export interface QueryEntitiesInitialRequest {
|
||||
authorizationToken?: string;
|
||||
credentials: BackstageCredentials;
|
||||
fields?: (entity: Entity) => Entity;
|
||||
limit?: number;
|
||||
filter?: EntityFilter;
|
||||
@@ -208,7 +209,7 @@ export interface QueryEntitiesInitialRequest {
|
||||
* move forward or backward on the data.
|
||||
*/
|
||||
export interface QueryEntitiesCursorRequest {
|
||||
authorizationToken?: string;
|
||||
credentials: BackstageCredentials;
|
||||
fields?: (entity: Entity) => Entity;
|
||||
limit?: number;
|
||||
cursor: Cursor;
|
||||
|
||||
@@ -23,6 +23,7 @@ import { AuthorizedEntitiesCatalog } from './AuthorizedEntitiesCatalog';
|
||||
import { Cursor, QueryEntitiesResponse } from '../catalog/types';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntityFilter } from '@backstage/plugin-catalog-node';
|
||||
import { mockCredentials } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('AuthorizedEntitiesCatalog', () => {
|
||||
const fakeCatalog = {
|
||||
@@ -60,7 +61,7 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
|
||||
expect(
|
||||
await catalog.entities({
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
}),
|
||||
).toEqual({
|
||||
entities: [],
|
||||
@@ -77,10 +78,10 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
]);
|
||||
const catalog = createCatalog(isEntityKind);
|
||||
|
||||
await catalog.entities({ authorizationToken: 'abcd' });
|
||||
await catalog.entities({ credentials: mockCredentials.none() });
|
||||
|
||||
expect(fakeCatalog.entities).toHaveBeenCalledWith({
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
filter: { key: 'kind', values: ['b'] },
|
||||
});
|
||||
});
|
||||
@@ -91,10 +92,10 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
]);
|
||||
const catalog = createCatalog();
|
||||
|
||||
await catalog.entities({ authorizationToken: 'abcd' });
|
||||
await catalog.entities({ credentials: mockCredentials.none() });
|
||||
|
||||
expect(fakeCatalog.entities).toHaveBeenCalledWith({
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -109,7 +110,7 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
await expect(
|
||||
catalog.entitiesBatch({
|
||||
entityRefs: ['component:default/component-a'],
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
items: [null],
|
||||
@@ -132,12 +133,12 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
|
||||
await catalog.entitiesBatch({
|
||||
entityRefs: ['component:default/component-a'],
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
expect(fakeCatalog.entitiesBatch).toHaveBeenCalledWith({
|
||||
entityRefs: ['component:default/component-a'],
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
filter: { key: 'kind', values: ['b'] },
|
||||
});
|
||||
});
|
||||
@@ -150,12 +151,12 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
|
||||
await catalog.entitiesBatch({
|
||||
entityRefs: ['component:default/component-a'],
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
expect(fakeCatalog.entitiesBatch).toHaveBeenCalledWith({
|
||||
entityRefs: ['component:default/component-a'],
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -169,7 +170,7 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
|
||||
await expect(
|
||||
catalog.queryEntities({
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
filter: { key: 'kind', values: ['b'] },
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
@@ -188,12 +189,12 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
const catalog = createCatalog();
|
||||
|
||||
await catalog.queryEntities({
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
filter: { key: 'kind', values: ['b'] },
|
||||
});
|
||||
|
||||
expect(fakeCatalog.queryEntities).toHaveBeenCalledWith({
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
filter: { key: 'kind', values: ['b'] },
|
||||
});
|
||||
});
|
||||
@@ -243,12 +244,12 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
const catalog = createCatalog(isEntityKind);
|
||||
|
||||
let response = await catalog.queryEntities({
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
filter: { key: 'name', values: ['name'] },
|
||||
});
|
||||
|
||||
expect(fakeCatalog.queryEntities).toHaveBeenCalledWith({
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
filter: { allOf: [{ key: 'kind', values: ['b'] }, requestFilter] },
|
||||
});
|
||||
|
||||
@@ -276,12 +277,12 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
orderFieldValues: ['a', null],
|
||||
};
|
||||
response = await catalog.queryEntities({
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
cursor,
|
||||
});
|
||||
|
||||
expect(fakeCatalog.queryEntities).toHaveBeenNthCalledWith(2, {
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
cursor: {
|
||||
...cursor,
|
||||
filter: { allOf: [{ key: 'kind', values: ['b'] }, requestFilter] },
|
||||
@@ -324,7 +325,9 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
);
|
||||
|
||||
await expect(() =>
|
||||
catalog.removeEntityByUid('uid', { authorizationToken: 'abcd' }),
|
||||
catalog.removeEntityByUid('uid', {
|
||||
credentials: mockCredentials.none(),
|
||||
}),
|
||||
).rejects.toThrow(NotAllowedError);
|
||||
});
|
||||
|
||||
@@ -343,7 +346,9 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
);
|
||||
|
||||
await expect(() =>
|
||||
catalog.removeEntityByUid('uid', { authorizationToken: 'abcd' }),
|
||||
catalog.removeEntityByUid('uid', {
|
||||
credentials: mockCredentials.none(),
|
||||
}),
|
||||
).rejects.toThrow(NotAllowedError);
|
||||
});
|
||||
|
||||
@@ -363,9 +368,13 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
createConditionTransformer([isEntityKind]),
|
||||
);
|
||||
|
||||
await catalog.removeEntityByUid('uid', { authorizationToken: 'abcd' });
|
||||
await catalog.removeEntityByUid('uid', {
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
expect(fakeCatalog.removeEntityByUid).toHaveBeenCalledWith('uid');
|
||||
expect(fakeCatalog.removeEntityByUid).toHaveBeenCalledWith('uid', {
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
});
|
||||
|
||||
it('calls underlying catalog method on ALLOW', async () => {
|
||||
@@ -383,9 +392,13 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
createConditionTransformer([]),
|
||||
);
|
||||
|
||||
await catalog.removeEntityByUid('uid', { authorizationToken: 'abcd' });
|
||||
await catalog.removeEntityByUid('uid', {
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
expect(fakeCatalog.removeEntityByUid).toHaveBeenCalledWith('uid');
|
||||
expect(fakeCatalog.removeEntityByUid).toHaveBeenCalledWith('uid', {
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -398,7 +411,7 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
|
||||
await expect(() =>
|
||||
catalog.entityAncestry('backstage:default/component', {
|
||||
authorizationToken: 'Bearer abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
}),
|
||||
).rejects.toThrow(NotAllowedError);
|
||||
});
|
||||
@@ -443,7 +456,7 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
|
||||
const ancestryResult = await catalog.entityAncestry(
|
||||
'backstage:default/a',
|
||||
{ authorizationToken: 'Bearer abcd' },
|
||||
{ credentials: mockCredentials.none() },
|
||||
);
|
||||
|
||||
expect(ancestryResult).toEqual({
|
||||
@@ -476,7 +489,7 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
expect(
|
||||
await catalog.facets({
|
||||
facets: ['a'],
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
}),
|
||||
).toEqual({
|
||||
facets: { a: [] },
|
||||
@@ -492,11 +505,14 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
]);
|
||||
const catalog = createCatalog(isEntityKind);
|
||||
|
||||
await catalog.facets({ facets: ['a'], authorizationToken: 'abcd' });
|
||||
await catalog.facets({
|
||||
facets: ['a'],
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
expect(fakeCatalog.facets).toHaveBeenCalledWith({
|
||||
facets: ['a'],
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
filter: { key: 'kind', values: ['b'] },
|
||||
});
|
||||
});
|
||||
@@ -507,11 +523,14 @@ describe('AuthorizedEntitiesCatalog', () => {
|
||||
]);
|
||||
const catalog = createCatalog();
|
||||
|
||||
await catalog.facets({ facets: ['a'], authorizationToken: 'abcd' });
|
||||
await catalog.facets({
|
||||
facets: ['a'],
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
expect(fakeCatalog.facets).toHaveBeenCalledWith({
|
||||
facets: ['a'],
|
||||
authorizationToken: 'abcd',
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,10 +20,7 @@ import {
|
||||
catalogEntityReadPermission,
|
||||
} from '@backstage/plugin-catalog-common/alpha';
|
||||
import { Entity, stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import {
|
||||
AuthorizeResult,
|
||||
PermissionEvaluator,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import { ConditionTransformer } from '@backstage/plugin-permission-node';
|
||||
import {
|
||||
Cursor,
|
||||
@@ -41,19 +38,23 @@ import {
|
||||
import { basicEntityFilter } from './request';
|
||||
import { isQueryEntitiesCursorRequest } from './util';
|
||||
import { EntityFilter } from '@backstage/plugin-catalog-node';
|
||||
import {
|
||||
BackstageCredentials,
|
||||
PermissionsService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
|
||||
export class AuthorizedEntitiesCatalog implements EntitiesCatalog {
|
||||
constructor(
|
||||
private readonly entitiesCatalog: EntitiesCatalog,
|
||||
private readonly permissionApi: PermissionEvaluator,
|
||||
private readonly permissionApi: PermissionsService,
|
||||
private readonly transformConditions: ConditionTransformer<EntityFilter>,
|
||||
) {}
|
||||
|
||||
async entities(request?: EntitiesRequest): Promise<EntitiesResponse> {
|
||||
async entities(request: EntitiesRequest): Promise<EntitiesResponse> {
|
||||
const authorizeDecision = (
|
||||
await this.permissionApi.authorizeConditional(
|
||||
[{ permission: catalogEntityReadPermission }],
|
||||
{ token: request?.authorizationToken },
|
||||
{ credentials: request.credentials },
|
||||
)
|
||||
)[0];
|
||||
|
||||
@@ -85,7 +86,7 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog {
|
||||
const authorizeDecision = (
|
||||
await this.permissionApi.authorizeConditional(
|
||||
[{ permission: catalogEntityReadPermission }],
|
||||
{ token: request?.authorizationToken },
|
||||
{ credentials: request.credentials },
|
||||
)
|
||||
)[0];
|
||||
|
||||
@@ -116,7 +117,7 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog {
|
||||
const authorizeDecision = (
|
||||
await this.permissionApi.authorizeConditional(
|
||||
[{ permission: catalogEntityReadPermission }],
|
||||
{ token: request.authorizationToken },
|
||||
{ credentials: request.credentials },
|
||||
)
|
||||
)[0];
|
||||
|
||||
@@ -186,12 +187,12 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog {
|
||||
|
||||
async removeEntityByUid(
|
||||
uid: string,
|
||||
options?: { authorizationToken?: string },
|
||||
options: { credentials: BackstageCredentials },
|
||||
): Promise<void> {
|
||||
const authorizeResponse = (
|
||||
await this.permissionApi.authorizeConditional(
|
||||
[{ permission: catalogEntityDeletePermission }],
|
||||
{ token: options?.authorizationToken },
|
||||
{ credentials: options.credentials },
|
||||
)
|
||||
)[0];
|
||||
if (authorizeResponse.result === AuthorizeResult.DENY) {
|
||||
@@ -202,6 +203,7 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog {
|
||||
authorizeResponse.conditions,
|
||||
);
|
||||
const { entities } = await this.entitiesCatalog.entities({
|
||||
credentials: options.credentials,
|
||||
filter: {
|
||||
allOf: [permissionFilter, basicEntityFilter({ 'metadata.uid': uid })],
|
||||
},
|
||||
@@ -210,30 +212,35 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog {
|
||||
throw new NotAllowedError();
|
||||
}
|
||||
}
|
||||
return this.entitiesCatalog.removeEntityByUid(uid);
|
||||
return this.entitiesCatalog.removeEntityByUid(uid, {
|
||||
credentials: options.credentials,
|
||||
});
|
||||
}
|
||||
|
||||
async entityAncestry(
|
||||
entityRef: string,
|
||||
options?: { authorizationToken?: string },
|
||||
options: { credentials: BackstageCredentials },
|
||||
): Promise<EntityAncestryResponse> {
|
||||
const rootEntityAuthorizeResponse = (
|
||||
await this.permissionApi.authorize(
|
||||
[{ permission: catalogEntityReadPermission, resourceRef: entityRef }],
|
||||
{ token: options?.authorizationToken },
|
||||
{ credentials: options.credentials },
|
||||
)
|
||||
)[0];
|
||||
if (rootEntityAuthorizeResponse.result === AuthorizeResult.DENY) {
|
||||
throw new NotAllowedError();
|
||||
}
|
||||
|
||||
const ancestryResult = await this.entitiesCatalog.entityAncestry(entityRef);
|
||||
const ancestryResult = await this.entitiesCatalog.entityAncestry(
|
||||
entityRef,
|
||||
{ credentials: options.credentials },
|
||||
);
|
||||
const authorizeResponse = await this.permissionApi.authorize(
|
||||
ancestryResult.items.map(item => ({
|
||||
permission: catalogEntityReadPermission,
|
||||
resourceRef: stringifyEntityRef(item.entity),
|
||||
})),
|
||||
{ token: options?.authorizationToken },
|
||||
{ credentials: options.credentials },
|
||||
);
|
||||
const unauthorizedAncestryItems = ancestryResult.items.filter(
|
||||
(_, index) => authorizeResponse[index].result === AuthorizeResult.DENY,
|
||||
@@ -268,7 +275,7 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog {
|
||||
const authorizeDecision = (
|
||||
await this.permissionApi.authorizeConditional(
|
||||
[{ permission: catalogEntityReadPermission }],
|
||||
{ token: request?.authorizationToken },
|
||||
{ credentials: request.credentials },
|
||||
)
|
||||
)[0];
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import { NotAllowedError, NotFoundError } from '@backstage/errors';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import { AuthorizedLocationService } from './AuthorizedLocationService';
|
||||
import { mockCredentials } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('AuthorizedLocationService', () => {
|
||||
const fakeLocationService = {
|
||||
@@ -45,6 +46,10 @@ describe('AuthorizedLocationService', () => {
|
||||
const createService = () =>
|
||||
new AuthorizedLocationService(fakeLocationService, fakePermissionApi);
|
||||
|
||||
const mockOptions = {
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
@@ -55,13 +60,12 @@ describe('AuthorizedLocationService', () => {
|
||||
const service = createService();
|
||||
|
||||
const spec = { type: 'type', target: 'target' };
|
||||
await service.createLocation(spec, false, {
|
||||
authorizationToken: 'Bearer authtoken',
|
||||
});
|
||||
await service.createLocation(spec, false, mockOptions);
|
||||
|
||||
expect(fakeLocationService.createLocation).toHaveBeenCalledWith(
|
||||
spec,
|
||||
false,
|
||||
mockOptions,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -71,9 +75,7 @@ describe('AuthorizedLocationService', () => {
|
||||
|
||||
const spec = { type: 'type', target: 'target' };
|
||||
await expect(() =>
|
||||
service.createLocation(spec, false, {
|
||||
authorizationToken: 'Bearer authtoken',
|
||||
}),
|
||||
service.createLocation(spec, false, mockOptions),
|
||||
).rejects.toThrow(NotAllowedError);
|
||||
});
|
||||
});
|
||||
@@ -83,7 +85,7 @@ describe('AuthorizedLocationService', () => {
|
||||
mockAllow();
|
||||
const service = createService();
|
||||
|
||||
await service.listLocations({ authorizationToken: 'Bearer authtoken' });
|
||||
await service.listLocations(mockOptions);
|
||||
|
||||
expect(fakeLocationService.listLocations).toHaveBeenCalled();
|
||||
});
|
||||
@@ -92,9 +94,7 @@ describe('AuthorizedLocationService', () => {
|
||||
mockDeny();
|
||||
const service = createService();
|
||||
|
||||
const locations = await service.listLocations({
|
||||
authorizationToken: 'Bearer authtoken',
|
||||
});
|
||||
const locations = await service.listLocations(mockOptions);
|
||||
|
||||
expect(locations).toEqual([]);
|
||||
});
|
||||
@@ -105,11 +105,12 @@ describe('AuthorizedLocationService', () => {
|
||||
mockAllow();
|
||||
const service = createService();
|
||||
|
||||
await service.getLocation('id', {
|
||||
authorizationToken: 'Bearer authtoken',
|
||||
});
|
||||
await service.getLocation('id', mockOptions);
|
||||
|
||||
expect(fakeLocationService.getLocation).toHaveBeenCalledWith('id');
|
||||
expect(fakeLocationService.getLocation).toHaveBeenCalledWith(
|
||||
'id',
|
||||
mockOptions,
|
||||
);
|
||||
});
|
||||
|
||||
it('throws error on DENY', async () => {
|
||||
@@ -117,7 +118,7 @@ describe('AuthorizedLocationService', () => {
|
||||
const service = createService();
|
||||
|
||||
await expect(() =>
|
||||
service.getLocation('id', { authorizationToken: 'Bearer authtoken' }),
|
||||
service.getLocation('id', mockOptions),
|
||||
).rejects.toThrow(NotFoundError);
|
||||
});
|
||||
});
|
||||
@@ -127,11 +128,12 @@ describe('AuthorizedLocationService', () => {
|
||||
mockAllow();
|
||||
const service = createService();
|
||||
|
||||
await service.deleteLocation('id', {
|
||||
authorizationToken: 'Bearer authtoken',
|
||||
});
|
||||
await service.deleteLocation('id', mockOptions);
|
||||
|
||||
expect(fakeLocationService.deleteLocation).toHaveBeenCalledWith('id');
|
||||
expect(fakeLocationService.deleteLocation).toHaveBeenCalledWith(
|
||||
'id',
|
||||
mockOptions,
|
||||
);
|
||||
});
|
||||
|
||||
it('throws error on DENY', async () => {
|
||||
@@ -139,9 +141,7 @@ describe('AuthorizedLocationService', () => {
|
||||
const service = createService();
|
||||
|
||||
await expect(() =>
|
||||
service.deleteLocation('id', {
|
||||
authorizationToken: 'Bearer authtoken',
|
||||
}),
|
||||
service.deleteLocation('id', mockOptions),
|
||||
).rejects.toThrow(NotAllowedError);
|
||||
});
|
||||
});
|
||||
@@ -153,16 +153,17 @@ describe('AuthorizedLocationService', () => {
|
||||
|
||||
await service.getLocationByEntity(
|
||||
{ kind: 'c', namespace: 'ns', name: 'n' },
|
||||
{
|
||||
authorizationToken: 'Bearer authtoken',
|
||||
},
|
||||
mockOptions,
|
||||
);
|
||||
|
||||
expect(fakeLocationService.getLocationByEntity).toHaveBeenCalledWith({
|
||||
kind: 'c',
|
||||
namespace: 'ns',
|
||||
name: 'n',
|
||||
});
|
||||
expect(fakeLocationService.getLocationByEntity).toHaveBeenCalledWith(
|
||||
{
|
||||
kind: 'c',
|
||||
namespace: 'ns',
|
||||
name: 'n',
|
||||
},
|
||||
mockOptions,
|
||||
);
|
||||
});
|
||||
|
||||
it('throws error on DENY', async () => {
|
||||
@@ -172,7 +173,7 @@ describe('AuthorizedLocationService', () => {
|
||||
await expect(() =>
|
||||
service.getLocationByEntity(
|
||||
{ kind: 'c', namespace: 'ns', name: 'n' },
|
||||
{ authorizationToken: 'Bearer authtoken' },
|
||||
mockOptions,
|
||||
),
|
||||
).rejects.toThrow(NotFoundError);
|
||||
});
|
||||
|
||||
@@ -22,23 +22,24 @@ import {
|
||||
catalogLocationDeletePermission,
|
||||
catalogLocationReadPermission,
|
||||
} from '@backstage/plugin-catalog-common/alpha';
|
||||
import {
|
||||
AuthorizeResult,
|
||||
PermissionEvaluator,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import { LocationInput, LocationService } from './types';
|
||||
import {
|
||||
BackstageCredentials,
|
||||
PermissionsService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
|
||||
export class AuthorizedLocationService implements LocationService {
|
||||
constructor(
|
||||
private readonly locationService: LocationService,
|
||||
private readonly permissionApi: PermissionEvaluator,
|
||||
private readonly permissionApi: PermissionsService,
|
||||
) {}
|
||||
|
||||
async createLocation(
|
||||
spec: LocationInput,
|
||||
dryRun: boolean,
|
||||
options?: {
|
||||
authorizationToken?: string;
|
||||
options: {
|
||||
credentials: BackstageCredentials;
|
||||
},
|
||||
): Promise<{
|
||||
location: Location;
|
||||
@@ -48,7 +49,7 @@ export class AuthorizedLocationService implements LocationService {
|
||||
const authorizationResponse = (
|
||||
await this.permissionApi.authorize(
|
||||
[{ permission: catalogLocationCreatePermission }],
|
||||
{ token: options?.authorizationToken },
|
||||
{ credentials: options.credentials },
|
||||
)
|
||||
)[0];
|
||||
|
||||
@@ -56,16 +57,16 @@ export class AuthorizedLocationService implements LocationService {
|
||||
throw new NotAllowedError();
|
||||
}
|
||||
|
||||
return this.locationService.createLocation(spec, dryRun);
|
||||
return this.locationService.createLocation(spec, dryRun, options);
|
||||
}
|
||||
|
||||
async listLocations(options?: {
|
||||
authorizationToken?: string;
|
||||
async listLocations(options: {
|
||||
credentials: BackstageCredentials;
|
||||
}): Promise<Location[]> {
|
||||
const authorizationResponse = (
|
||||
await this.permissionApi.authorize(
|
||||
[{ permission: catalogLocationReadPermission }],
|
||||
{ token: options?.authorizationToken },
|
||||
{ credentials: options.credentials },
|
||||
)
|
||||
)[0];
|
||||
|
||||
@@ -73,17 +74,17 @@ export class AuthorizedLocationService implements LocationService {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.locationService.listLocations();
|
||||
return this.locationService.listLocations(options);
|
||||
}
|
||||
|
||||
async getLocation(
|
||||
id: string,
|
||||
options?: { authorizationToken?: string },
|
||||
options: { credentials: BackstageCredentials },
|
||||
): Promise<Location> {
|
||||
const authorizationResponse = (
|
||||
await this.permissionApi.authorize(
|
||||
[{ permission: catalogLocationReadPermission }],
|
||||
{ token: options?.authorizationToken },
|
||||
{ credentials: options.credentials },
|
||||
)
|
||||
)[0];
|
||||
|
||||
@@ -91,17 +92,17 @@ export class AuthorizedLocationService implements LocationService {
|
||||
throw new NotFoundError(`Found no location with ID ${id}`);
|
||||
}
|
||||
|
||||
return this.locationService.getLocation(id);
|
||||
return this.locationService.getLocation(id, options);
|
||||
}
|
||||
|
||||
async deleteLocation(
|
||||
id: string,
|
||||
options?: { authorizationToken?: string },
|
||||
options: { credentials: BackstageCredentials },
|
||||
): Promise<void> {
|
||||
const authorizationResponse = (
|
||||
await this.permissionApi.authorize(
|
||||
[{ permission: catalogLocationDeletePermission }],
|
||||
{ token: options?.authorizationToken },
|
||||
{ credentials: options.credentials },
|
||||
)
|
||||
)[0];
|
||||
|
||||
@@ -109,23 +110,23 @@ export class AuthorizedLocationService implements LocationService {
|
||||
throw new NotAllowedError();
|
||||
}
|
||||
|
||||
return this.locationService.deleteLocation(id);
|
||||
return this.locationService.deleteLocation(id, options);
|
||||
}
|
||||
|
||||
async getLocationByEntity(
|
||||
entityRef: CompoundEntityRef | string,
|
||||
options?: { authorizationToken?: string | undefined } | undefined,
|
||||
options: { credentials: BackstageCredentials },
|
||||
): Promise<Location> {
|
||||
const authorizationResponse = (
|
||||
await this.permissionApi.authorize(
|
||||
[{ permission: catalogLocationReadPermission }],
|
||||
{ token: options?.authorizationToken },
|
||||
{ credentials: options.credentials },
|
||||
)
|
||||
)[0];
|
||||
|
||||
if (authorizationResponse.result === AuthorizeResult.DENY) {
|
||||
throw new NotFoundError();
|
||||
}
|
||||
return this.locationService.getLocationByEntity(entityRef);
|
||||
return this.locationService.getLocationByEntity(entityRef, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { NotAllowedError } from '@backstage/errors';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import { ServerPermissionClient } from '@backstage/plugin-permission-node';
|
||||
import { AuthorizedRefreshService } from './AuthorizedRefreshService';
|
||||
import { mockCredentials } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('AuthorizedRefreshService', () => {
|
||||
const refreshService = {
|
||||
@@ -46,7 +47,7 @@ describe('AuthorizedRefreshService', () => {
|
||||
await expect(() =>
|
||||
authorizedService.refresh({
|
||||
entityRef: 'some entity ref',
|
||||
authorizationToken: 'some auth token',
|
||||
credentials: mockCredentials.none(),
|
||||
}),
|
||||
).rejects.toThrow(NotAllowedError);
|
||||
});
|
||||
@@ -64,7 +65,7 @@ describe('AuthorizedRefreshService', () => {
|
||||
|
||||
const options = {
|
||||
entityRef: 'some entity ref',
|
||||
authorizationToken: 'some auth token',
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
await authorizedService.refresh(options);
|
||||
|
||||
|
||||
@@ -16,16 +16,14 @@
|
||||
|
||||
import { NotAllowedError } from '@backstage/errors';
|
||||
import { catalogEntityRefreshPermission } from '@backstage/plugin-catalog-common/alpha';
|
||||
import {
|
||||
AuthorizeResult,
|
||||
PermissionEvaluator,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import { RefreshOptions, RefreshService } from './types';
|
||||
import { PermissionsService } from '@backstage/backend-plugin-api';
|
||||
|
||||
export class AuthorizedRefreshService implements RefreshService {
|
||||
constructor(
|
||||
private readonly service: RefreshService,
|
||||
private readonly permissionApi: PermissionEvaluator,
|
||||
private readonly permissionApi: PermissionsService,
|
||||
) {}
|
||||
|
||||
async refresh(options: RefreshOptions) {
|
||||
@@ -37,7 +35,7 @@ export class AuthorizedRefreshService implements RefreshService {
|
||||
resourceRef: options.entityRef,
|
||||
},
|
||||
],
|
||||
{ token: options.authorizationToken },
|
||||
{ credentials: options.credentials },
|
||||
)
|
||||
)[0];
|
||||
if (authorizeDecision.result !== AuthorizeResult.ALLOW) {
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { PluginDatabaseManager, UrlReader } from '@backstage/backend-common';
|
||||
import {
|
||||
PluginDatabaseManager,
|
||||
HostDiscovery,
|
||||
UrlReader,
|
||||
createLegacyAuthAdapters,
|
||||
} from '@backstage/backend-common';
|
||||
import { PluginTaskScheduler } from '@backstage/backend-tasks';
|
||||
import {
|
||||
DefaultNamespaceEntityPolicy,
|
||||
@@ -84,7 +89,6 @@ import { permissionRules as catalogPermissionRules } from '../permissions/rules'
|
||||
import { PermissionRule } from '@backstage/plugin-permission-node';
|
||||
import {
|
||||
PermissionAuthorizer,
|
||||
PermissionEvaluator,
|
||||
toPermissionEvaluator,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
@@ -102,6 +106,12 @@ import { DefaultProviderDatabase } from '../database/DefaultProviderDatabase';
|
||||
import { DefaultCatalogDatabase } from '../database/DefaultCatalogDatabase';
|
||||
import { EventBroker } from '@backstage/plugin-events-node';
|
||||
import { durationToMilliseconds } from '@backstage/types';
|
||||
import {
|
||||
DiscoveryService,
|
||||
AuthService,
|
||||
HttpAuthService,
|
||||
PermissionsService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
|
||||
/**
|
||||
* This is a duplicate of the alpha `CatalogPermissionRule` type, for use in the stable API.
|
||||
@@ -118,8 +128,11 @@ export type CatalogEnvironment = {
|
||||
database: PluginDatabaseManager;
|
||||
config: Config;
|
||||
reader: UrlReader;
|
||||
permissions: PermissionEvaluator | PermissionAuthorizer;
|
||||
permissions: PermissionsService | PermissionAuthorizer;
|
||||
scheduler?: PluginTaskScheduler;
|
||||
discovery?: DiscoveryService;
|
||||
auth?: AuthService;
|
||||
httpAuth?: HttpAuthService;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -438,7 +451,19 @@ export class CatalogBuilder {
|
||||
processingEngine: CatalogProcessingEngine;
|
||||
router: Router;
|
||||
}> {
|
||||
const { config, database, logger, permissions, scheduler } = this.env;
|
||||
const {
|
||||
config,
|
||||
database,
|
||||
logger,
|
||||
permissions,
|
||||
scheduler,
|
||||
discovery = HostDiscovery.fromConfig(config),
|
||||
} = this.env;
|
||||
|
||||
const { auth, httpAuth } = createLegacyAuthAdapters({
|
||||
...this.env,
|
||||
discovery,
|
||||
});
|
||||
|
||||
const policy = this.buildEntityPolicy();
|
||||
const processors = this.buildProcessors();
|
||||
@@ -486,25 +511,26 @@ export class CatalogBuilder {
|
||||
stitcher,
|
||||
});
|
||||
|
||||
let permissionEvaluator: PermissionEvaluator;
|
||||
let permissionsService: PermissionsService;
|
||||
if ('authorizeConditional' in permissions) {
|
||||
permissionEvaluator = permissions as PermissionEvaluator;
|
||||
permissionsService = permissions as PermissionsService;
|
||||
} else {
|
||||
logger.warn(
|
||||
'PermissionAuthorizer is deprecated. Please use an instance of PermissionEvaluator instead of PermissionAuthorizer in PluginEnvironment#permissions',
|
||||
);
|
||||
permissionEvaluator = toPermissionEvaluator(permissions);
|
||||
permissionsService = toPermissionEvaluator(permissions);
|
||||
}
|
||||
|
||||
const entitiesCatalog = new AuthorizedEntitiesCatalog(
|
||||
unauthorizedEntitiesCatalog,
|
||||
permissionEvaluator,
|
||||
permissionsService,
|
||||
createConditionTransformer(this.permissionRules),
|
||||
);
|
||||
const permissionIntegrationRouter = createPermissionIntegrationRouter({
|
||||
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
|
||||
getResources: async (resourceRefs: string[]) => {
|
||||
const { entities } = await unauthorizedEntitiesCatalog.entities({
|
||||
credentials: await auth.getOwnServiceCredentials(),
|
||||
filter: {
|
||||
anyOf: resourceRefs.map(resourceRef => {
|
||||
const { kind, namespace, name } = parseEntityRef(resourceRef);
|
||||
@@ -558,12 +584,13 @@ export class CatalogBuilder {
|
||||
new DefaultLocationService(locationStore, orchestrator, {
|
||||
allowedLocationTypes: this.allowedLocationType,
|
||||
}),
|
||||
permissionEvaluator,
|
||||
permissionsService,
|
||||
);
|
||||
const refreshService = new AuthorizedRefreshService(
|
||||
new DefaultRefreshService({ database: catalogDatabase }),
|
||||
permissionEvaluator,
|
||||
permissionsService,
|
||||
);
|
||||
|
||||
const router = await createRouter({
|
||||
entitiesCatalog,
|
||||
locationAnalyzer,
|
||||
@@ -573,6 +600,8 @@ export class CatalogBuilder {
|
||||
logger,
|
||||
config,
|
||||
permissionIntegrationRouter,
|
||||
auth,
|
||||
httpAuth,
|
||||
});
|
||||
|
||||
await connectEntityProviders(providerDatabase, entityProviders);
|
||||
|
||||
@@ -178,6 +178,9 @@ export const catalogPlugin = createBackendPlugin({
|
||||
httpRouter: coreServices.httpRouter,
|
||||
lifecycle: coreServices.lifecycle,
|
||||
scheduler: coreServices.scheduler,
|
||||
discovery: coreServices.discovery,
|
||||
auth: coreServices.auth,
|
||||
httpAuth: coreServices.httpAuth,
|
||||
},
|
||||
async init({
|
||||
logger,
|
||||
@@ -188,6 +191,9 @@ export const catalogPlugin = createBackendPlugin({
|
||||
httpRouter,
|
||||
lifecycle,
|
||||
scheduler,
|
||||
discovery,
|
||||
auth,
|
||||
httpAuth,
|
||||
}) {
|
||||
const winstonLogger = loggerToWinstonLogger(logger);
|
||||
const builder = await CatalogBuilder.create({
|
||||
@@ -197,6 +203,9 @@ export const catalogPlugin = createBackendPlugin({
|
||||
database,
|
||||
scheduler,
|
||||
logger: winstonLogger,
|
||||
discovery,
|
||||
auth,
|
||||
httpAuth,
|
||||
});
|
||||
if (processingExtensions.onProcessingErrorHandler) {
|
||||
builder.subscribe({
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
*/
|
||||
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils';
|
||||
import {
|
||||
TestDatabaseId,
|
||||
TestDatabases,
|
||||
mockCredentials,
|
||||
} from '@backstage/backend-test-utils';
|
||||
import { Entity, stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { Knex } from 'knex';
|
||||
import { v4 as uuid, v4 } from 'uuid';
|
||||
@@ -304,8 +308,10 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const testFilter = {
|
||||
key: 'spec.test',
|
||||
};
|
||||
const request = { filter: testFilter };
|
||||
const { entities } = await catalog.entities(request);
|
||||
const { entities } = await catalog.entities({
|
||||
filter: testFilter,
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
expect(entities.length).toBe(1);
|
||||
expect(entities[0]).toEqual(entity2);
|
||||
@@ -343,8 +349,10 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
key: 'spec.test',
|
||||
},
|
||||
};
|
||||
const request = { filter: testFilter };
|
||||
const { entities } = await catalog.entities(request);
|
||||
const { entities } = await catalog.entities({
|
||||
filter: testFilter,
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
expect(entities.length).toBe(1);
|
||||
expect(entities[0]).toEqual(entity1);
|
||||
@@ -406,7 +414,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
values: ['red'],
|
||||
},
|
||||
};
|
||||
const request = {
|
||||
const { entities } = await catalog.entities({
|
||||
filter: {
|
||||
allOf: [
|
||||
testFilter1,
|
||||
@@ -415,8 +423,8 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const { entities } = await catalog.entities(request);
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
expect(entities.length).toBe(2);
|
||||
expect(entities).toContainEqual(entity2);
|
||||
@@ -455,14 +463,15 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const testFilter2 = {
|
||||
key: 'metadata.desc',
|
||||
};
|
||||
const request = {
|
||||
const { entities } = await catalog.entities({
|
||||
filter: {
|
||||
not: {
|
||||
allOf: [testFilter1, testFilter2],
|
||||
},
|
||||
},
|
||||
};
|
||||
const { entities } = await catalog.entities(request);
|
||||
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
expect(entities.length).toBe(1);
|
||||
expect(entities).toContainEqual(entity1);
|
||||
@@ -498,8 +507,10 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
key: 'kind',
|
||||
values: [],
|
||||
};
|
||||
const request = { filter: testFilter };
|
||||
const { entities } = await catalog.entities(request);
|
||||
const { entities } = await catalog.entities({
|
||||
filter: testFilter,
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
expect(entities.length).toBe(0);
|
||||
},
|
||||
@@ -603,9 +614,11 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
stitcher,
|
||||
});
|
||||
|
||||
function f(request: EntitiesRequest): Promise<string[]> {
|
||||
function f(
|
||||
request: Omit<EntitiesRequest, 'credentials'>,
|
||||
): Promise<string[]> {
|
||||
return catalog
|
||||
.entities(request)
|
||||
.entities({ ...request, credentials: mockCredentials.none() })
|
||||
.then(response => response.entities.map(e => e.metadata.name));
|
||||
}
|
||||
|
||||
@@ -701,6 +714,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
'k:default/does-not-exist',
|
||||
'k:default/two',
|
||||
],
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
expect(items.map(e => e && stringifyEntityRef(e))).toEqual([
|
||||
@@ -749,6 +763,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const { items } = await catalog.entitiesBatch({
|
||||
entityRefs: ['k:default/two', 'k:default/one'],
|
||||
filter: { key: 'spec.owner', values: ['me'] },
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
expect(items.map(e => e && stringifyEntityRef(e))).toEqual([
|
||||
@@ -804,6 +819,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
filter,
|
||||
limit,
|
||||
orderFields: [{ field: 'metadata.name', order: 'asc' }],
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response1 = await catalog.queryEntities(request1);
|
||||
expect(response1.items).toEqual([entityFrom('A'), entityFrom('B')]);
|
||||
@@ -815,6 +831,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request2: QueryEntitiesCursorRequest = {
|
||||
cursor: response1.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response2 = await catalog.queryEntities(request2);
|
||||
expect(response2.items).toEqual([entityFrom('C'), entityFrom('D')]);
|
||||
@@ -826,6 +843,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request3: QueryEntitiesCursorRequest = {
|
||||
cursor: response2.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response3 = await catalog.queryEntities(request3);
|
||||
expect(response3.items).toEqual([entityFrom('E'), entityFrom('F')]);
|
||||
@@ -837,6 +855,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request4: QueryEntitiesCursorRequest = {
|
||||
cursor: response3.pageInfo.prevCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response4 = await catalog.queryEntities(request4);
|
||||
expect(response4.items).toEqual([entityFrom('C'), entityFrom('D')]);
|
||||
@@ -848,6 +867,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request5: QueryEntitiesCursorRequest = {
|
||||
cursor: response4.pageInfo.prevCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response5 = await catalog.queryEntities(request5);
|
||||
expect(response5.items).toEqual([entityFrom('A'), entityFrom('B')]);
|
||||
@@ -859,6 +879,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request6: QueryEntitiesCursorRequest = {
|
||||
cursor: response5.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response6 = await catalog.queryEntities(request6);
|
||||
expect(response6.items).toEqual([entityFrom('C'), entityFrom('D')]);
|
||||
@@ -870,6 +891,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request7: QueryEntitiesCursorRequest = {
|
||||
cursor: response6.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response7 = await catalog.queryEntities(request7);
|
||||
expect(response7.items).toEqual([entityFrom('E'), entityFrom('F')]);
|
||||
@@ -881,6 +903,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request7bis: QueryEntitiesCursorRequest = {
|
||||
cursor: response6.pageInfo.nextCursor!,
|
||||
limit: limit + 1,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response7bis = await catalog.queryEntities(request7bis);
|
||||
expect(response7bis.items).toEqual([
|
||||
@@ -896,6 +919,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request8: QueryEntitiesCursorRequest = {
|
||||
cursor: response7.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response8 = await catalog.queryEntities(request8);
|
||||
expect(response8.items).toEqual([entityFrom('G')]);
|
||||
@@ -949,6 +973,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
filter,
|
||||
limit,
|
||||
orderFields: [{ field: 'metadata.name', order: 'desc' }],
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response1 = await catalog.queryEntities(request1);
|
||||
expect(response1.items).toEqual([entityFrom('G'), entityFrom('F')]);
|
||||
@@ -960,6 +985,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request2: QueryEntitiesCursorRequest = {
|
||||
cursor: response1.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response2 = await catalog.queryEntities(request2);
|
||||
expect(response2.items).toEqual([entityFrom('E'), entityFrom('D')]);
|
||||
@@ -971,6 +997,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request3: QueryEntitiesCursorRequest = {
|
||||
cursor: response2.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response3 = await catalog.queryEntities(request3);
|
||||
expect(response3.items).toEqual([entityFrom('C'), entityFrom('B')]);
|
||||
@@ -982,6 +1009,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request4: QueryEntitiesCursorRequest = {
|
||||
cursor: response3.pageInfo.prevCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response4 = await catalog.queryEntities(request4);
|
||||
|
||||
@@ -994,6 +1022,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request5: QueryEntitiesCursorRequest = {
|
||||
cursor: response4.pageInfo.prevCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response5 = await catalog.queryEntities(request5);
|
||||
expect(response5.items).toEqual([entityFrom('G'), entityFrom('F')]);
|
||||
@@ -1005,6 +1034,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request6: QueryEntitiesCursorRequest = {
|
||||
cursor: response5.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response6 = await catalog.queryEntities(request6);
|
||||
expect(response6.items).toEqual([entityFrom('E'), entityFrom('D')]);
|
||||
@@ -1016,6 +1046,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request7: QueryEntitiesCursorRequest = {
|
||||
cursor: response6.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response7 = await catalog.queryEntities(request7);
|
||||
expect(response7.items).toEqual([entityFrom('C'), entityFrom('B')]);
|
||||
@@ -1027,6 +1058,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request7bis: QueryEntitiesCursorRequest = {
|
||||
cursor: response6.pageInfo.nextCursor!,
|
||||
limit: limit + 1,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response7bis = await catalog.queryEntities(request7bis);
|
||||
expect(response7bis.items).toEqual([
|
||||
@@ -1042,6 +1074,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request8: QueryEntitiesCursorRequest = {
|
||||
cursor: response7.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response8 = await catalog.queryEntities(request8);
|
||||
expect(response8.items).toEqual([entityFrom('A')]);
|
||||
@@ -1094,6 +1127,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
|
||||
orderFields: [{ field: 'metadata.name', order: 'asc' }],
|
||||
fullTextFilter: { term: 'cAt ' },
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response = await catalog.queryEntities(request);
|
||||
expect(response.items).toEqual([
|
||||
@@ -1152,6 +1186,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
filter,
|
||||
limit: 100,
|
||||
fullTextFilter: { term: 'cAt ', fields: ['metadata.title'] },
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response = await catalog.queryEntities(request);
|
||||
expect(response.items).toEqual([
|
||||
@@ -1177,6 +1212,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
|
||||
const paginatedResponseNext = await catalog.queryEntities({
|
||||
cursor: paginatedResponse.pageInfo.nextCursor!,
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
expect(paginatedResponseNext.items).toEqual([
|
||||
entityFrom('4', { uid: 'id4', title: 'dogcat' }),
|
||||
@@ -1187,6 +1223,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
|
||||
const paginatedResponsePrev = await catalog.queryEntities({
|
||||
cursor: paginatedResponseNext.pageInfo.prevCursor!,
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
expect(paginatedResponsePrev).toMatchObject(paginatedResponse);
|
||||
},
|
||||
@@ -1251,6 +1288,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
term: 'KiNg ',
|
||||
fields: ['metadata.title', 'metadata.name'],
|
||||
},
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response = await catalog.queryEntities(request);
|
||||
|
||||
@@ -1278,6 +1316,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
|
||||
const paginatedResponseNext = await catalog.queryEntities({
|
||||
cursor: paginatedResponse.pageInfo.nextCursor!,
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
expect(paginatedResponseNext.items).toEqual([
|
||||
entityFrom('NotACatKing', { uid: 'id2', title: 'atcatss' }),
|
||||
@@ -1289,6 +1328,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
|
||||
const paginatedResponsePrev = await catalog.queryEntities({
|
||||
cursor: paginatedResponseNext.pageInfo.prevCursor!,
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
expect(paginatedResponsePrev).toMatchObject(paginatedResponse);
|
||||
},
|
||||
@@ -1319,6 +1359,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
|
||||
const request: QueryEntitiesInitialRequest = {
|
||||
limit: 0,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response = await catalog.queryEntities(request);
|
||||
expect(response).toEqual({ totalItems: 20, items: [], pageInfo: {} });
|
||||
@@ -1351,6 +1392,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request1: QueryEntitiesInitialRequest = {
|
||||
limit,
|
||||
orderFields: [{ field: 'metadata.name', order: 'asc' }],
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response1 = await catalog.queryEntities(request1);
|
||||
expect(response1.items).toMatchObject([
|
||||
@@ -1365,6 +1407,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request2: QueryEntitiesCursorRequest = {
|
||||
cursor: response1.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response2 = await catalog.queryEntities(request2);
|
||||
expect(response2.items).toMatchObject([
|
||||
@@ -1379,6 +1422,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request3: QueryEntitiesCursorRequest = {
|
||||
cursor: response2.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response3 = await catalog.queryEntities(request3);
|
||||
expect(response3.items).toEqual([entityFrom('CC'), entityFrom('DD')]);
|
||||
@@ -1390,6 +1434,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request4: QueryEntitiesCursorRequest = {
|
||||
cursor: response3.pageInfo.prevCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response4 = await catalog.queryEntities(request4);
|
||||
expect(response4.items).toMatchObject([
|
||||
@@ -1404,6 +1449,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request5: QueryEntitiesCursorRequest = {
|
||||
cursor: response4.pageInfo.prevCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response5 = await catalog.queryEntities(request5);
|
||||
expect(response5.items).toMatchObject([
|
||||
@@ -1471,6 +1517,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
values: ['included'],
|
||||
},
|
||||
orderFields: [{ field: 'metadata.name', order: 'asc' }],
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response1 = await catalog.queryEntities(request1);
|
||||
expect(response1.items).toMatchObject([
|
||||
@@ -1485,6 +1532,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request2: QueryEntitiesCursorRequest = {
|
||||
cursor: response1.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response2 = await catalog.queryEntities(request2);
|
||||
expect(response2.items).toMatchObject([
|
||||
@@ -1528,6 +1576,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
// initial request
|
||||
const request1: QueryEntitiesInitialRequest = {
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response1 = await catalog.queryEntities(request1);
|
||||
expect(response1.items).toMatchObject([
|
||||
@@ -1542,6 +1591,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request2: QueryEntitiesCursorRequest = {
|
||||
cursor: response1.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response2 = await catalog.queryEntities(request2);
|
||||
expect(response2.items).toMatchObject([
|
||||
@@ -1556,6 +1606,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request3: QueryEntitiesCursorRequest = {
|
||||
cursor: response2.pageInfo.nextCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response3 = await catalog.queryEntities(request3);
|
||||
expect(response3.items).toMatchObject([
|
||||
@@ -1570,6 +1621,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request4: QueryEntitiesCursorRequest = {
|
||||
cursor: response3.pageInfo.prevCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response4 = await catalog.queryEntities(request4);
|
||||
expect(response4.items).toMatchObject([
|
||||
@@ -1584,6 +1636,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
const request5: QueryEntitiesCursorRequest = {
|
||||
cursor: response4.pageInfo.prevCursor!,
|
||||
limit,
|
||||
credentials: mockCredentials.none(),
|
||||
};
|
||||
const response5 = await catalog.queryEntities(request5);
|
||||
expect(response5.items).toMatchObject([
|
||||
@@ -1719,7 +1772,12 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
stitcher,
|
||||
});
|
||||
|
||||
await expect(catalog.facets({ facets: ['kind'] })).resolves.toEqual({
|
||||
await expect(
|
||||
catalog.facets({
|
||||
facets: ['kind'],
|
||||
credentials: mockCredentials.none(),
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
facets: {
|
||||
kind: [
|
||||
{ value: 'k', count: 2 },
|
||||
@@ -1732,6 +1790,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
catalog.facets({
|
||||
facets: ['kind'],
|
||||
filter: { not: { key: 'metadata.name', values: ['two'] } },
|
||||
credentials: mockCredentials.none(),
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
facets: {
|
||||
@@ -1775,6 +1834,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
await expect(
|
||||
catalog.facets({
|
||||
facets: ['metadata.annotations.a.b/c.d', 'metadata.labels.e.f/g.h'],
|
||||
credentials: mockCredentials.none(),
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
facets: {
|
||||
@@ -1823,6 +1883,7 @@ describe('DefaultEntitiesCatalog', () => {
|
||||
await expect(
|
||||
catalog.facets({
|
||||
facets: ['metadata.tags'],
|
||||
credentials: mockCredentials.none(),
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
facets: {
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
*/
|
||||
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils';
|
||||
import {
|
||||
TestDatabaseId,
|
||||
TestDatabases,
|
||||
mockCredentials,
|
||||
} from '@backstage/backend-test-utils';
|
||||
import { Entity, stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { createHash } from 'crypto';
|
||||
import { Knex } from 'knex';
|
||||
@@ -220,6 +224,7 @@ describe('DefaultRefreshService', () => {
|
||||
|
||||
await refreshService.refresh({
|
||||
entityRef: 'component:default/mycomp',
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
await expect(
|
||||
@@ -273,6 +278,7 @@ describe('DefaultRefreshService', () => {
|
||||
|
||||
await refreshService.refresh({
|
||||
entityRef: 'api:default/myapi',
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
await expect(waitForRefresh(knex, 'api:default/myapi')).resolves.toBe(
|
||||
@@ -324,6 +330,7 @@ describe('DefaultRefreshService', () => {
|
||||
|
||||
await refreshService.refresh({
|
||||
entityRef: 'component:default/mycomp',
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
await expect(
|
||||
@@ -334,6 +341,7 @@ describe('DefaultRefreshService', () => {
|
||||
|
||||
await refreshService.refresh({
|
||||
entityRef: 'component:default/mycomp',
|
||||
credentials: mockCredentials.none(),
|
||||
});
|
||||
|
||||
await expect(
|
||||
|
||||
@@ -41,6 +41,7 @@ import { z } from 'zod';
|
||||
import { decodeCursor, encodeCursor } from './util';
|
||||
import { wrapInOpenApiTestServer } from '@backstage/backend-openapi-utils';
|
||||
import { Server } from 'http';
|
||||
import { mockCredentials, mockServices } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('createRouter readonly disabled', () => {
|
||||
let entitiesCatalog: jest.Mocked<EntitiesCatalog>;
|
||||
@@ -75,6 +76,8 @@ describe('createRouter readonly disabled', () => {
|
||||
refreshService,
|
||||
config: new ConfigReader(undefined),
|
||||
permissionIntegrationRouter: express.Router(),
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
});
|
||||
app = wrapInOpenApiTestServer(express().use(router));
|
||||
});
|
||||
@@ -88,15 +91,30 @@ describe('createRouter readonly disabled', () => {
|
||||
const response = await request(app)
|
||||
.post('/refresh')
|
||||
.set('Content-Type', 'application/json')
|
||||
.set('authorization', 'Bearer someauthtoken')
|
||||
.send({ entityRef: 'Component/default:foo' });
|
||||
expect(response.status).toBe(200);
|
||||
expect(refreshService.refresh).toHaveBeenCalledWith({
|
||||
entityRef: 'Component/default:foo',
|
||||
authorizationToken: 'someauthtoken',
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
});
|
||||
|
||||
it('should support passing the token in the request body for backwards compatibility', async () => {
|
||||
const response = await request(app)
|
||||
.post('/refresh')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send({
|
||||
entityRef: 'Component/default:foo',
|
||||
authorizationToken: mockCredentials.user.token('user:default/other'),
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
expect(refreshService.refresh).toHaveBeenCalledWith({
|
||||
entityRef: 'Component/default:foo',
|
||||
credentials: mockCredentials.user('user:default/other'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /entities', () => {
|
||||
it('happy path: lists entities', async () => {
|
||||
const entities: Entity[] = [
|
||||
@@ -137,6 +155,7 @@ describe('createRouter readonly disabled', () => {
|
||||
{ allOf: [{ key: 'c', values: ['4'] }] },
|
||||
],
|
||||
},
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -196,6 +215,7 @@ describe('createRouter readonly disabled', () => {
|
||||
fields: undefined,
|
||||
term: '',
|
||||
},
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -235,6 +255,7 @@ describe('createRouter readonly disabled', () => {
|
||||
fields: undefined,
|
||||
term: '',
|
||||
},
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -257,6 +278,7 @@ describe('createRouter readonly disabled', () => {
|
||||
expect(entitiesCatalog.queryEntities).toHaveBeenCalledTimes(1);
|
||||
expect(entitiesCatalog.queryEntities).toHaveBeenCalledWith({
|
||||
cursor,
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual({
|
||||
@@ -291,6 +313,7 @@ describe('createRouter readonly disabled', () => {
|
||||
expect(entitiesCatalog.queryEntities).toHaveBeenCalledTimes(1);
|
||||
expect(entitiesCatalog.queryEntities).toHaveBeenCalledWith({
|
||||
cursor,
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual({
|
||||
@@ -370,6 +393,7 @@ describe('createRouter readonly disabled', () => {
|
||||
expect(entitiesCatalog.entities).toHaveBeenCalledTimes(1);
|
||||
expect(entitiesCatalog.entities).toHaveBeenCalledWith({
|
||||
filter: basicEntityFilter({ 'metadata.uid': 'zzz' }),
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual(expect.objectContaining(entity));
|
||||
@@ -386,6 +410,7 @@ describe('createRouter readonly disabled', () => {
|
||||
expect(entitiesCatalog.entities).toHaveBeenCalledTimes(1);
|
||||
expect(entitiesCatalog.entities).toHaveBeenCalledWith({
|
||||
filter: basicEntityFilter({ 'metadata.uid': 'zzz' }),
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(404);
|
||||
expect(response.text).toMatch(/uid/);
|
||||
@@ -416,6 +441,7 @@ describe('createRouter readonly disabled', () => {
|
||||
'metadata.namespace': 'ns',
|
||||
'metadata.name': 'n',
|
||||
}),
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual(expect.objectContaining(entity));
|
||||
@@ -436,6 +462,7 @@ describe('createRouter readonly disabled', () => {
|
||||
'metadata.namespace': 'd',
|
||||
'metadata.name': 'c',
|
||||
}),
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(404);
|
||||
expect(response.text).toMatch(/name/);
|
||||
@@ -446,13 +473,10 @@ describe('createRouter readonly disabled', () => {
|
||||
it('can remove', async () => {
|
||||
entitiesCatalog.removeEntityByUid.mockResolvedValue(undefined);
|
||||
|
||||
const response = await request(app)
|
||||
.delete('/entities/by-uid/apa')
|
||||
.set('authorization', 'Bearer someauthtoken');
|
||||
|
||||
const response = await request(app).delete('/entities/by-uid/apa');
|
||||
expect(entitiesCatalog.removeEntityByUid).toHaveBeenCalledTimes(1);
|
||||
expect(entitiesCatalog.removeEntityByUid).toHaveBeenCalledWith('apa', {
|
||||
authorizationToken: 'someauthtoken',
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(204);
|
||||
});
|
||||
@@ -462,13 +486,10 @@ describe('createRouter readonly disabled', () => {
|
||||
new NotFoundError('nope'),
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
.delete('/entities/by-uid/apa')
|
||||
.set('authorization', 'Bearer someauthtoken');
|
||||
|
||||
const response = await request(app).delete('/entities/by-uid/apa');
|
||||
expect(entitiesCatalog.removeEntityByUid).toHaveBeenCalledTimes(1);
|
||||
expect(entitiesCatalog.removeEntityByUid).toHaveBeenCalledWith('apa', {
|
||||
authorizationToken: 'someauthtoken',
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(404);
|
||||
});
|
||||
@@ -518,6 +539,7 @@ describe('createRouter readonly disabled', () => {
|
||||
expect(entitiesCatalog.entitiesBatch).toHaveBeenCalledWith({
|
||||
entityRefs: [entityRef],
|
||||
fields: expect.any(Function),
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual({ items: [entity] });
|
||||
@@ -531,13 +553,10 @@ describe('createRouter readonly disabled', () => {
|
||||
];
|
||||
locationService.listLocations.mockResolvedValueOnce(locations);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/locations')
|
||||
.set('authorization', 'Bearer someauthtoken');
|
||||
|
||||
const response = await request(app).get('/locations');
|
||||
expect(locationService.listLocations).toHaveBeenCalledTimes(1);
|
||||
expect(locationService.listLocations).toHaveBeenCalledWith({
|
||||
authorizationToken: 'someauthtoken',
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual([
|
||||
@@ -555,13 +574,10 @@ describe('createRouter readonly disabled', () => {
|
||||
};
|
||||
locationService.getLocation.mockResolvedValueOnce(location);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/locations/foo')
|
||||
.set('authorization', 'Bearer someauthtoken');
|
||||
|
||||
const response = await request(app).get('/locations/foo');
|
||||
expect(locationService.getLocation).toHaveBeenCalledTimes(1);
|
||||
expect(locationService.getLocation).toHaveBeenCalledWith('foo', {
|
||||
authorizationToken: 'someauthtoken',
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
@@ -582,7 +598,7 @@ describe('createRouter readonly disabled', () => {
|
||||
|
||||
const response = await request(app)
|
||||
.post('/locations')
|
||||
.set('authorization', 'Bearer someauthtoken')
|
||||
|
||||
.send(spec);
|
||||
|
||||
expect(locationService.createLocation).not.toHaveBeenCalled();
|
||||
@@ -602,12 +618,12 @@ describe('createRouter readonly disabled', () => {
|
||||
|
||||
const response = await request(app)
|
||||
.post('/locations')
|
||||
.set('authorization', 'Bearer someauthtoken')
|
||||
|
||||
.send(spec);
|
||||
|
||||
expect(locationService.createLocation).toHaveBeenCalledTimes(1);
|
||||
expect(locationService.createLocation).toHaveBeenCalledWith(spec, false, {
|
||||
authorizationToken: 'someauthtoken',
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(201);
|
||||
expect(response.body).toEqual(
|
||||
@@ -630,12 +646,12 @@ describe('createRouter readonly disabled', () => {
|
||||
|
||||
const response = await request(app)
|
||||
.post('/locations?dryRun=true')
|
||||
.set('authorization', 'Bearer someauthtoken')
|
||||
|
||||
.send(spec);
|
||||
|
||||
expect(locationService.createLocation).toHaveBeenCalledTimes(1);
|
||||
expect(locationService.createLocation).toHaveBeenCalledWith(spec, true, {
|
||||
authorizationToken: 'someauthtoken',
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(201);
|
||||
expect(response.body).toEqual(
|
||||
@@ -650,13 +666,10 @@ describe('createRouter readonly disabled', () => {
|
||||
it('deletes the location', async () => {
|
||||
locationService.deleteLocation.mockResolvedValueOnce(undefined);
|
||||
|
||||
const response = await request(app)
|
||||
.delete('/locations/foo')
|
||||
.set('authorization', 'Bearer someauthtoken');
|
||||
|
||||
const response = await request(app).delete('/locations/foo');
|
||||
expect(locationService.deleteLocation).toHaveBeenCalledTimes(1);
|
||||
expect(locationService.deleteLocation).toHaveBeenCalledWith('foo', {
|
||||
authorizationToken: 'someauthtoken',
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(204);
|
||||
@@ -672,15 +685,12 @@ describe('createRouter readonly disabled', () => {
|
||||
};
|
||||
locationService.getLocationByEntity.mockResolvedValueOnce(location);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/locations/by-entity/c/ns/n')
|
||||
.set('authorization', 'Bearer someauthtoken');
|
||||
|
||||
const response = await request(app).get('/locations/by-entity/c/ns/n');
|
||||
expect(locationService.getLocationByEntity).toHaveBeenCalledTimes(1);
|
||||
expect(locationService.getLocationByEntity).toHaveBeenCalledWith(
|
||||
{ kind: 'c', namespace: 'ns', name: 'n' },
|
||||
{
|
||||
authorizationToken: 'someauthtoken',
|
||||
credentials: mockCredentials.user(),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -837,6 +847,8 @@ describe('createRouter readonly enabled', () => {
|
||||
},
|
||||
}),
|
||||
permissionIntegrationRouter: express.Router(),
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
});
|
||||
app = express().use(router);
|
||||
});
|
||||
@@ -866,13 +878,10 @@ describe('createRouter readonly enabled', () => {
|
||||
describe('DELETE /entities/by-uid/:uid', () => {
|
||||
// this delete is allowed as there is no other way to remove entities
|
||||
it('is allowed', async () => {
|
||||
const response = await request(app)
|
||||
.delete('/entities/by-uid/apa')
|
||||
.set('authorization', 'Bearer someauthtoken');
|
||||
|
||||
const response = await request(app).delete('/entities/by-uid/apa');
|
||||
expect(entitiesCatalog.removeEntityByUid).toHaveBeenCalledTimes(1);
|
||||
expect(entitiesCatalog.removeEntityByUid).toHaveBeenCalledWith('apa', {
|
||||
authorizationToken: 'someauthtoken',
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(204);
|
||||
});
|
||||
@@ -885,13 +894,10 @@ describe('createRouter readonly enabled', () => {
|
||||
];
|
||||
locationService.listLocations.mockResolvedValueOnce(locations);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/locations')
|
||||
.set('authorization', 'Bearer someauthtoken');
|
||||
|
||||
const response = await request(app).get('/locations');
|
||||
expect(locationService.listLocations).toHaveBeenCalledTimes(1);
|
||||
expect(locationService.listLocations).toHaveBeenCalledWith({
|
||||
authorizationToken: 'someauthtoken',
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
@@ -910,13 +916,10 @@ describe('createRouter readonly enabled', () => {
|
||||
};
|
||||
locationService.getLocation.mockResolvedValueOnce(location);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/locations/foo')
|
||||
.set('authorization', 'Bearer someauthtoken');
|
||||
|
||||
const response = await request(app).get('/locations/foo');
|
||||
expect(locationService.getLocation).toHaveBeenCalledTimes(1);
|
||||
expect(locationService.getLocation).toHaveBeenCalledWith('foo', {
|
||||
authorizationToken: 'someauthtoken',
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
@@ -937,7 +940,7 @@ describe('createRouter readonly enabled', () => {
|
||||
|
||||
const response = await request(app)
|
||||
.post('/locations')
|
||||
.set('authorization', 'Bearer someauthtoken')
|
||||
|
||||
.send(spec);
|
||||
|
||||
expect(locationService.createLocation).not.toHaveBeenCalled();
|
||||
@@ -958,12 +961,12 @@ describe('createRouter readonly enabled', () => {
|
||||
|
||||
const response = await request(app)
|
||||
.post('/locations?dryRun=true')
|
||||
.set('authorization', 'Bearer someauthtoken')
|
||||
|
||||
.send(spec);
|
||||
|
||||
expect(locationService.createLocation).toHaveBeenCalledTimes(1);
|
||||
expect(locationService.createLocation).toHaveBeenCalledWith(spec, true, {
|
||||
authorizationToken: 'someauthtoken',
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(201);
|
||||
expect(response.body).toEqual(
|
||||
@@ -976,10 +979,7 @@ describe('createRouter readonly enabled', () => {
|
||||
|
||||
describe('DELETE /locations', () => {
|
||||
it('is not allowed', async () => {
|
||||
const response = await request(app)
|
||||
.delete('/locations/foo')
|
||||
.set('authorization', 'Bearer someauthtoken');
|
||||
|
||||
const response = await request(app).delete('/locations/foo');
|
||||
expect(locationService.deleteLocation).not.toHaveBeenCalled();
|
||||
expect(response.status).toEqual(403);
|
||||
});
|
||||
@@ -994,15 +994,12 @@ describe('createRouter readonly enabled', () => {
|
||||
};
|
||||
locationService.getLocationByEntity.mockResolvedValueOnce(location);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/locations/by-entity/c/ns/n')
|
||||
.set('authorization', 'Bearer someauthtoken');
|
||||
|
||||
const response = await request(app).get('/locations/by-entity/c/ns/n');
|
||||
expect(locationService.getLocationByEntity).toHaveBeenCalledTimes(1);
|
||||
expect(locationService.getLocationByEntity).toHaveBeenCalledWith(
|
||||
{ kind: 'c', namespace: 'ns', name: 'n' },
|
||||
{
|
||||
authorizationToken: 'someauthtoken',
|
||||
credentials: mockCredentials.user(),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1065,6 +1062,8 @@ describe('NextRouter permissioning', () => {
|
||||
),
|
||||
),
|
||||
}),
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
});
|
||||
app = express().use(router);
|
||||
});
|
||||
|
||||
@@ -41,7 +41,7 @@ import {
|
||||
} from './request';
|
||||
import { parseEntityFacetParams } from './request/parseEntityFacetParams';
|
||||
import { parseEntityOrderParams } from './request/parseEntityOrderParams';
|
||||
import { LocationService, RefreshOptions, RefreshService } from './types';
|
||||
import { LocationService, RefreshService } from './types';
|
||||
import {
|
||||
disallowReadonlyMode,
|
||||
encodeCursor,
|
||||
@@ -50,8 +50,8 @@ import {
|
||||
} from './util';
|
||||
import { createOpenApiRouter } from '../schema/openapi.generated';
|
||||
import { PluginTaskScheduler } from '@backstage/backend-tasks';
|
||||
import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node';
|
||||
import { parseEntityPaginationParams } from './request/parseEntityPaginationParams';
|
||||
import { AuthService, HttpAuthService } from '@backstage/backend-plugin-api';
|
||||
|
||||
/**
|
||||
* Options used by {@link createRouter}.
|
||||
@@ -68,6 +68,8 @@ export interface RouterOptions {
|
||||
logger: Logger;
|
||||
config: Config;
|
||||
permissionIntegrationRouter?: express.Router;
|
||||
auth: AuthService;
|
||||
httpAuth: HttpAuthService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,6 +96,8 @@ export async function createRouter(
|
||||
config,
|
||||
logger,
|
||||
permissionIntegrationRouter,
|
||||
auth,
|
||||
httpAuth,
|
||||
} = options;
|
||||
|
||||
const readonlyEnabled =
|
||||
@@ -104,12 +108,16 @@ export async function createRouter(
|
||||
|
||||
if (refreshService) {
|
||||
router.post('/refresh', async (req, res) => {
|
||||
const refreshOptions: RefreshOptions = req.body;
|
||||
refreshOptions.authorizationToken = getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
);
|
||||
const { authorizationToken, ...restBody } = req.body;
|
||||
|
||||
await refreshService.refresh(refreshOptions);
|
||||
const credentials = authorizationToken
|
||||
? await auth.authenticate(authorizationToken)
|
||||
: await httpAuth.credentials(req);
|
||||
|
||||
await refreshService.refresh({
|
||||
...restBody,
|
||||
credentials,
|
||||
});
|
||||
res.status(200).end();
|
||||
});
|
||||
}
|
||||
@@ -126,9 +134,7 @@ export async function createRouter(
|
||||
fields: parseEntityTransformParams(req.query),
|
||||
order: parseEntityOrderParams(req.query),
|
||||
pagination: parseEntityPaginationParams(req.query),
|
||||
authorizationToken: getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
),
|
||||
credentials: await httpAuth.credentials(req),
|
||||
});
|
||||
|
||||
// Add a Link header to the next page
|
||||
@@ -147,9 +153,7 @@ export async function createRouter(
|
||||
await entitiesCatalog.queryEntities({
|
||||
limit: req.query.limit,
|
||||
...parseQueryEntitiesParams(req.query),
|
||||
authorizationToken: getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
),
|
||||
credentials: await httpAuth.credentials(req),
|
||||
});
|
||||
|
||||
res.json({
|
||||
@@ -169,9 +173,7 @@ export async function createRouter(
|
||||
const { uid } = req.params;
|
||||
const { entities } = await entitiesCatalog.entities({
|
||||
filter: basicEntityFilter({ 'metadata.uid': uid }),
|
||||
authorizationToken: getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
),
|
||||
credentials: await httpAuth.credentials(req),
|
||||
});
|
||||
if (!entities.length) {
|
||||
throw new NotFoundError(`No entity with uid ${uid}`);
|
||||
@@ -181,9 +183,7 @@ export async function createRouter(
|
||||
.delete('/entities/by-uid/:uid', async (req, res) => {
|
||||
const { uid } = req.params;
|
||||
await entitiesCatalog.removeEntityByUid(uid, {
|
||||
authorizationToken: getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
),
|
||||
credentials: await httpAuth.credentials(req),
|
||||
});
|
||||
res.status(204).end();
|
||||
})
|
||||
@@ -195,9 +195,7 @@ export async function createRouter(
|
||||
'metadata.namespace': namespace,
|
||||
'metadata.name': name,
|
||||
}),
|
||||
authorizationToken: getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
),
|
||||
credentials: await httpAuth.credentials(req),
|
||||
});
|
||||
if (!entities.length) {
|
||||
throw new NotFoundError(
|
||||
@@ -212,22 +210,17 @@ export async function createRouter(
|
||||
const { kind, namespace, name } = req.params;
|
||||
const entityRef = stringifyEntityRef({ kind, namespace, name });
|
||||
const response = await entitiesCatalog.entityAncestry(entityRef, {
|
||||
authorizationToken: getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
),
|
||||
credentials: await httpAuth.credentials(req),
|
||||
});
|
||||
res.status(200).json(response);
|
||||
},
|
||||
)
|
||||
.post('/entities/by-refs', async (req, res) => {
|
||||
const request = entitiesBatchRequest(req);
|
||||
const token = getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
);
|
||||
const response = await entitiesCatalog.entitiesBatch({
|
||||
entityRefs: request.entityRefs,
|
||||
fields: parseEntityTransformParams(req.query, request.fields),
|
||||
authorizationToken: token,
|
||||
credentials: await httpAuth.credentials(req),
|
||||
});
|
||||
res.status(200).json(response);
|
||||
})
|
||||
@@ -235,9 +228,7 @@ export async function createRouter(
|
||||
const response = await entitiesCatalog.facets({
|
||||
filter: parseEntityFilterParams(req.query),
|
||||
facets: parseEntityFacetParams(req.query),
|
||||
authorizationToken: getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
),
|
||||
credentials: await httpAuth.credentials(req),
|
||||
});
|
||||
res.status(200).json(response);
|
||||
});
|
||||
@@ -256,17 +247,13 @@ export async function createRouter(
|
||||
}
|
||||
|
||||
const output = await locationService.createLocation(location, dryRun, {
|
||||
authorizationToken: getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
),
|
||||
credentials: await httpAuth.credentials(req),
|
||||
});
|
||||
res.status(201).json(output);
|
||||
})
|
||||
.get('/locations', async (req, res) => {
|
||||
const locations = await locationService.listLocations({
|
||||
authorizationToken: getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
),
|
||||
credentials: await httpAuth.credentials(req),
|
||||
});
|
||||
res.status(200).json(locations.map(l => ({ data: l })));
|
||||
})
|
||||
@@ -274,9 +261,7 @@ export async function createRouter(
|
||||
.get('/locations/:id', async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const output = await locationService.getLocation(id, {
|
||||
authorizationToken: getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
),
|
||||
credentials: await httpAuth.credentials(req),
|
||||
});
|
||||
res.status(200).json(output);
|
||||
})
|
||||
@@ -285,9 +270,7 @@ export async function createRouter(
|
||||
|
||||
const { id } = req.params;
|
||||
await locationService.deleteLocation(id, {
|
||||
authorizationToken: getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
),
|
||||
credentials: await httpAuth.credentials(req),
|
||||
});
|
||||
res.status(204).end();
|
||||
})
|
||||
@@ -295,11 +278,7 @@ export async function createRouter(
|
||||
const { kind, namespace, name } = req.params;
|
||||
const output = await locationService.getLocationByEntity(
|
||||
{ kind, namespace, name },
|
||||
{
|
||||
authorizationToken: getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
),
|
||||
},
|
||||
{ credentials: await httpAuth.credentials(req) },
|
||||
);
|
||||
res.status(200).json(output);
|
||||
});
|
||||
|
||||
@@ -28,12 +28,12 @@ import { internal } from '@backstage/backend-openapi-utils';
|
||||
|
||||
export function parseQueryEntitiesParams(
|
||||
params: internal.QuerySchema<typeof spec, '/entities/by-query', 'get'>,
|
||||
): Omit<QueryEntitiesRequest, 'authorizationToken' | 'limit'> {
|
||||
): Omit<QueryEntitiesRequest, 'credentials' | 'limit'> {
|
||||
const fields = parseEntityTransformParams(params);
|
||||
|
||||
if (params.cursor) {
|
||||
const decodedCursor = decodeCursor(params.cursor);
|
||||
const response: Omit<QueryEntitiesCursorRequest, 'authorizationToken'> = {
|
||||
const response: Omit<QueryEntitiesCursorRequest, 'credentials'> = {
|
||||
cursor: decodedCursor,
|
||||
fields,
|
||||
};
|
||||
@@ -43,7 +43,7 @@ export function parseQueryEntitiesParams(
|
||||
const filter = parseEntityFilterParams(params);
|
||||
const orderFields = parseEntityOrderFieldParams(params);
|
||||
|
||||
const response: Omit<QueryEntitiesInitialRequest, 'authorizationToken'> = {
|
||||
const response: Omit<QueryEntitiesInitialRequest, 'credentials'> = {
|
||||
fields,
|
||||
filter,
|
||||
orderFields,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import { CompoundEntityRef, Entity } from '@backstage/catalog-model';
|
||||
import { Location } from '@backstage/catalog-client';
|
||||
import { BackstageCredentials } from '@backstage/backend-plugin-api';
|
||||
|
||||
/**
|
||||
* Holds the information required to create a new location in the catalog location store.
|
||||
@@ -35,22 +36,24 @@ export interface LocationService {
|
||||
createLocation(
|
||||
location: LocationInput,
|
||||
dryRun: boolean,
|
||||
options?: {
|
||||
authorizationToken?: string;
|
||||
options: {
|
||||
credentials: BackstageCredentials;
|
||||
},
|
||||
): Promise<{ location: Location; entities: Entity[]; exists?: boolean }>;
|
||||
listLocations(options?: { authorizationToken?: string }): Promise<Location[]>;
|
||||
listLocations(options: {
|
||||
credentials: BackstageCredentials;
|
||||
}): Promise<Location[]>;
|
||||
getLocation(
|
||||
id: string,
|
||||
options?: { authorizationToken?: string },
|
||||
options: { credentials: BackstageCredentials },
|
||||
): Promise<Location>;
|
||||
deleteLocation(
|
||||
id: string,
|
||||
options?: { authorizationToken?: string },
|
||||
options: { credentials: BackstageCredentials },
|
||||
): Promise<void>;
|
||||
getLocationByEntity(
|
||||
entityRef: CompoundEntityRef | string,
|
||||
options?: { authorizationToken?: string },
|
||||
options: { credentials: BackstageCredentials },
|
||||
): Promise<Location>;
|
||||
}
|
||||
|
||||
@@ -62,7 +65,7 @@ export interface LocationService {
|
||||
export type RefreshOptions = {
|
||||
/** The reference to a single entity that should be refreshed */
|
||||
entityRef: string;
|
||||
authorizationToken?: string;
|
||||
credentials: BackstageCredentials;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user