@@ -3,6 +3,7 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { EntitiesOverview } from '@backstage/plugin-linguist-common';
|
||||
import { EntityResults } from '@backstage/plugin-linguist-common';
|
||||
import express from 'express';
|
||||
@@ -31,8 +32,8 @@ export class LinguistBackendApi {
|
||||
logger: Logger,
|
||||
store: LinguistBackendStore,
|
||||
urlReader: UrlReader,
|
||||
discovery: PluginEndpointDiscovery,
|
||||
tokenManager: TokenManager,
|
||||
catalogApi: CatalogApi,
|
||||
age?: HumanDuration,
|
||||
batchSize?: number,
|
||||
useSourceLocation?: boolean,
|
||||
|
||||
@@ -13,19 +13,20 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
getVoidLogger,
|
||||
PluginEndpointDiscovery,
|
||||
ReadTreeResponse,
|
||||
ServerTokenManager,
|
||||
UrlReader,
|
||||
} from '@backstage/backend-common';
|
||||
import { GetEntitiesResponse } from '@backstage/catalog-client';
|
||||
import { CatalogApi, GetEntitiesResponse } from '@backstage/catalog-client';
|
||||
import { Results } from 'linguist-js/dist/types';
|
||||
import { DateTime } from 'luxon';
|
||||
import { LinguistBackendStore } from '../db';
|
||||
import { kindOrDefault, LinguistBackendApi } from './LinguistBackendApi';
|
||||
import fs from 'fs-extra';
|
||||
import { LINGUIST_ANNOTATION } from '@backstage/plugin-linguist-common';
|
||||
|
||||
const linguistResultMock = Promise.resolve({
|
||||
files: {
|
||||
@@ -70,15 +71,6 @@ describe('kindOrDefault', () => {
|
||||
});
|
||||
|
||||
describe('Linguist backend API', () => {
|
||||
const getEntitiesMock = jest.fn();
|
||||
jest.mock('@backstage/catalog-client', () => {
|
||||
return {
|
||||
CatalogClient: jest
|
||||
.fn()
|
||||
.mockImplementation(() => ({ getEntities: getEntitiesMock })),
|
||||
};
|
||||
});
|
||||
|
||||
const logger = getVoidLogger();
|
||||
|
||||
const store: jest.Mocked<LinguistBackendStore> = {
|
||||
@@ -95,10 +87,10 @@ describe('Linguist backend API', () => {
|
||||
readUrl: jest.fn(),
|
||||
};
|
||||
|
||||
const discovery: jest.Mocked<PluginEndpointDiscovery> = {
|
||||
getBaseUrl: jest.fn(),
|
||||
getExternalBaseUrl: jest.fn(),
|
||||
};
|
||||
const catalogApi: jest.Mocked<CatalogApi> = {
|
||||
getEntities: jest.fn(),
|
||||
getEntityByRef: jest.fn(),
|
||||
} as any;
|
||||
|
||||
const tokenManager = ServerTokenManager.noop();
|
||||
|
||||
@@ -106,8 +98,8 @@ describe('Linguist backend API', () => {
|
||||
logger,
|
||||
store,
|
||||
urlReader,
|
||||
discovery,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -174,7 +166,7 @@ describe('Linguist backend API', () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
getEntitiesMock.mockResolvedValue(testEntityListResponse);
|
||||
catalogApi.getEntities.mockResolvedValue(testEntityListResponse);
|
||||
|
||||
await api.addNewEntities();
|
||||
expect(store.insertNewEntity).toHaveBeenCalledTimes(3);
|
||||
@@ -211,12 +203,12 @@ describe('Linguist backend API', () => {
|
||||
});
|
||||
|
||||
it('should get entity overview with stale items', async () => {
|
||||
const staleApi = new LinguistBackendApi(
|
||||
const apiWithAge = new LinguistBackendApi(
|
||||
logger,
|
||||
store,
|
||||
urlReader,
|
||||
discovery,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
{ days: 5 },
|
||||
);
|
||||
store.getProcessedEntities.mockResolvedValue([
|
||||
@@ -236,7 +228,7 @@ describe('Linguist backend API', () => {
|
||||
'component:default/service-five',
|
||||
]);
|
||||
|
||||
const overview = await staleApi.getEntitiesOverview();
|
||||
const overview = await apiWithAge.getEntitiesOverview();
|
||||
expect(overview.entityCount).toEqual(5);
|
||||
expect(overview.processedCount).toEqual(2);
|
||||
expect(overview.staleCount).toEqual(1);
|
||||
@@ -277,7 +269,7 @@ describe('Linguist backend API', () => {
|
||||
fsSpy.mockClear();
|
||||
});
|
||||
|
||||
it('should generate languages for multiple entities using default', async () => {
|
||||
it('should generate languages for entities using default', async () => {
|
||||
store.getProcessedEntities.mockResolvedValue([
|
||||
{
|
||||
entityRef: 'component:default/service-one',
|
||||
@@ -294,21 +286,56 @@ describe('Linguist backend API', () => {
|
||||
'component:default/service-four',
|
||||
'component:default/service-five',
|
||||
]);
|
||||
|
||||
const entity = {
|
||||
apiVersion: 'backstage.io/v1beta1',
|
||||
metadata: {
|
||||
name: 'service-one',
|
||||
annotations: {
|
||||
[LINGUIST_ANNOTATION]: 'https://some.fake/service/',
|
||||
},
|
||||
},
|
||||
kind: 'Component',
|
||||
};
|
||||
|
||||
catalogApi.getEntityByRef.mockResolvedValue(entity);
|
||||
|
||||
const resultsSpy = jest
|
||||
.spyOn(api, 'getLinguistResults')
|
||||
.mockImplementation(() => linguistResultMock);
|
||||
|
||||
urlReader.readTree.mockResolvedValue({
|
||||
files: async () => [
|
||||
{
|
||||
content: async () => Buffer.from('-- XXX: code-data', 'utf8'),
|
||||
path: 'my-file.js',
|
||||
},
|
||||
],
|
||||
dir: async () => '/temp/my-code',
|
||||
} as ReadTreeResponse);
|
||||
|
||||
const fsSpy = jest.spyOn(fs, 'remove');
|
||||
|
||||
const generateEntityLanguages = jest.spyOn(api, 'generateEntityLanguages');
|
||||
await api.generateEntitiesLanguages();
|
||||
expect(generateEntityLanguages).toHaveBeenCalledTimes(3);
|
||||
|
||||
generateEntityLanguages.mockClear();
|
||||
resultsSpy.mockClear();
|
||||
fsSpy.mockClear();
|
||||
});
|
||||
|
||||
it('should generate languages for multiple entities using defined batch size', async () => {
|
||||
const batchApi = new LinguistBackendApi(
|
||||
it('should generate languages for entities using defined batch size', async () => {
|
||||
const apiWithBatchSize = new LinguistBackendApi(
|
||||
logger,
|
||||
store,
|
||||
urlReader,
|
||||
discovery,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
undefined,
|
||||
1,
|
||||
2,
|
||||
);
|
||||
|
||||
store.getProcessedEntities.mockResolvedValue([
|
||||
{
|
||||
entityRef: 'component:default/service-one',
|
||||
@@ -325,11 +352,45 @@ describe('Linguist backend API', () => {
|
||||
'component:default/service-four',
|
||||
'component:default/service-five',
|
||||
]);
|
||||
|
||||
const entity = {
|
||||
apiVersion: 'backstage.io/v1beta1',
|
||||
metadata: {
|
||||
name: 'service-one',
|
||||
annotations: {
|
||||
[LINGUIST_ANNOTATION]: 'https://some.fake/service/',
|
||||
},
|
||||
},
|
||||
kind: 'Component',
|
||||
};
|
||||
|
||||
catalogApi.getEntityByRef.mockResolvedValue(entity);
|
||||
|
||||
const resultsSpy = jest
|
||||
.spyOn(apiWithBatchSize, 'getLinguistResults')
|
||||
.mockImplementation(() => linguistResultMock);
|
||||
|
||||
urlReader.readTree.mockResolvedValue({
|
||||
files: async () => [
|
||||
{
|
||||
content: async () => Buffer.from('-- XXX: code-data', 'utf8'),
|
||||
path: 'my-file.js',
|
||||
},
|
||||
],
|
||||
dir: async () => '/temp/my-code',
|
||||
} as ReadTreeResponse);
|
||||
|
||||
const fsSpy = jest.spyOn(fs, 'remove');
|
||||
|
||||
const generateEntityLanguages = jest.spyOn(
|
||||
batchApi,
|
||||
apiWithBatchSize,
|
||||
'generateEntityLanguages',
|
||||
);
|
||||
await batchApi.generateEntitiesLanguages();
|
||||
expect(generateEntityLanguages).toHaveBeenCalledTimes(1);
|
||||
await apiWithBatchSize.generateEntitiesLanguages();
|
||||
expect(generateEntityLanguages).toHaveBeenCalledTimes(2);
|
||||
|
||||
generateEntityLanguages.mockClear();
|
||||
resultsSpy.mockClear();
|
||||
fsSpy.mockClear();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,14 +22,10 @@ import {
|
||||
} from '@backstage/plugin-linguist-common';
|
||||
import {
|
||||
CATALOG_FILTER_EXISTS,
|
||||
CatalogClient,
|
||||
GetEntitiesRequest,
|
||||
CatalogApi,
|
||||
} from '@backstage/catalog-client';
|
||||
import {
|
||||
PluginEndpointDiscovery,
|
||||
TokenManager,
|
||||
UrlReader,
|
||||
} from '@backstage/backend-common';
|
||||
import { TokenManager, UrlReader } from '@backstage/backend-common';
|
||||
|
||||
import { DateTime } from 'luxon';
|
||||
import { LINGUIST_ANNOTATION } from '@backstage/plugin-linguist-common';
|
||||
@@ -49,10 +45,9 @@ export class LinguistBackendApi {
|
||||
private readonly logger: Logger;
|
||||
private readonly store: LinguistBackendStore;
|
||||
private readonly urlReader: UrlReader;
|
||||
private readonly discovery: PluginEndpointDiscovery;
|
||||
private readonly tokenManager: TokenManager;
|
||||
|
||||
private readonly catalogClient: CatalogClient;
|
||||
private readonly catalogApi: CatalogApi;
|
||||
private readonly age?: HumanDuration;
|
||||
private readonly batchSize?: number;
|
||||
private readonly useSourceLocation?: boolean;
|
||||
@@ -62,8 +57,8 @@ export class LinguistBackendApi {
|
||||
logger: Logger,
|
||||
store: LinguistBackendStore,
|
||||
urlReader: UrlReader,
|
||||
discovery: PluginEndpointDiscovery,
|
||||
tokenManager: TokenManager,
|
||||
catalogApi: CatalogApi,
|
||||
age?: HumanDuration,
|
||||
batchSize?: number,
|
||||
useSourceLocation?: boolean,
|
||||
@@ -73,9 +68,8 @@ export class LinguistBackendApi {
|
||||
this.logger = logger;
|
||||
this.store = store;
|
||||
this.urlReader = urlReader;
|
||||
this.discovery = discovery;
|
||||
this.tokenManager = tokenManager;
|
||||
this.catalogClient = new CatalogClient({ discoveryApi: this.discovery });
|
||||
this.catalogApi = catalogApi;
|
||||
this.batchSize = batchSize;
|
||||
this.age = age;
|
||||
this.useSourceLocation = useSourceLocation;
|
||||
@@ -112,7 +106,7 @@ export class LinguistBackendApi {
|
||||
};
|
||||
|
||||
const { token } = await this.tokenManager.getToken();
|
||||
const response = await this.catalogClient.getEntities(request, { token });
|
||||
const response = await this.catalogApi.getEntities(request, { token });
|
||||
const entities = response.items;
|
||||
|
||||
entities.forEach(entity => {
|
||||
@@ -131,9 +125,10 @@ export class LinguistBackendApi {
|
||||
0,
|
||||
this.batchSize ?? 20,
|
||||
);
|
||||
entities.forEach(async entityRef => {
|
||||
|
||||
for (const entityRef of entities) {
|
||||
const { token } = await this.tokenManager.getToken();
|
||||
const entity = await this.catalogClient.getEntityByRef(entityRef, {
|
||||
const entity = await this.catalogApi.getEntityByRef(entityRef, {
|
||||
token,
|
||||
});
|
||||
const annotationKey = this.useSourceLocation
|
||||
@@ -148,12 +143,13 @@ export class LinguistBackendApi {
|
||||
try {
|
||||
await this.generateEntityLanguages(entityRef, url);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
assertError(error);
|
||||
this.logger.error(
|
||||
`Unable to process "${entityRef}" using "${url}", message: ${error.message}, stack: ${error.stack}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async getEntitiesOverview(): Promise<EntitiesOverview> {
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
TaskScheduleDefinition,
|
||||
} from '@backstage/backend-tasks';
|
||||
import { HumanDuration } from '@backstage/types';
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
|
||||
/** @public */
|
||||
export interface PluginOptions {
|
||||
@@ -74,14 +75,16 @@ export async function createRouter(
|
||||
await database.getClient(),
|
||||
);
|
||||
|
||||
const catalogClient = new CatalogClient({ discoveryApi: discovery });
|
||||
|
||||
const linguistBackendApi =
|
||||
routerOptions.linguistBackendApi ||
|
||||
new LinguistBackendApi(
|
||||
logger,
|
||||
linguistBackendStore,
|
||||
reader,
|
||||
discovery,
|
||||
tokenManager,
|
||||
catalogClient,
|
||||
age,
|
||||
batchSize,
|
||||
useSourceLocation,
|
||||
|
||||
Reference in New Issue
Block a user