Merge pull request #9856 from backstage/freben/catalog-rearrange

Rearrange a bit inside the catalog backend
This commit is contained in:
Fredrik Adelöw
2022-03-02 11:05:49 +01:00
committed by GitHub
112 changed files with 594 additions and 280 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': patch
---
**DEPRECATED**: The `results` export, and instead adding `processingResult` with the same shape and purpose.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': patch
---
Internal restructuring to collect the various provider files in a `modules` folder while waiting to be externalized
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/plugin-catalog-backend-module-aws': patch
'@backstage/plugin-catalog-backend-module-ldap': patch
'@backstage/plugin-catalog-backend-module-msgraph': patch
'@backstage/plugin-scaffolder-backend': patch
---
Use the new `processingResult` export from the catalog backend
@@ -57,7 +57,7 @@ The recommended way of instantiating the catalog backend classes is to use the
`CatalogBuilder`, as illustrated in the
[example backend here](https://github.com/backstage/backstage/blob/master/packages/backend/src/plugins/catalog.ts).
We will create a new
[`EntityProvider`](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/src/providers/types.ts)
[`EntityProvider`](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/src/api/provider.ts)
subclass that can be added to this catalog builder.
Let's make a simple provider that can refresh a set of entities based on a
@@ -355,7 +355,7 @@ The recommended way of instantiating the catalog backend classes is to use the
`CatalogBuilder`, as illustrated in the
[example backend here](https://github.com/backstage/backstage/blob/master/packages/backend/src/plugins/catalog.ts).
We will create a new
[`CatalogProcessor`](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/src/ingestion/processors/types.ts)
[`CatalogProcessor`](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/src/api/processor.ts)
subclass that can be added to this catalog builder.
It is up to you where you put the code for this new processor class. For quick
@@ -20,7 +20,7 @@ import {
CatalogProcessor,
CatalogProcessorEmit,
LocationSpec,
results,
processingResult,
} from '@backstage/plugin-catalog-backend';
import AWS, { Credentials, Organizations } from 'aws-sdk';
import { Account, ListAccountsResponse } from 'aws-sdk/clients/organizations';
@@ -112,7 +112,7 @@ export class AwsOrganizationCloudAccountProcessor implements CatalogProcessor {
return true;
})
.forEach(entity => {
emit(results.entity(location, entity));
emit(processingResult.entity(location, entity));
});
return true;
@@ -28,7 +28,7 @@ import {
CatalogProcessor,
CatalogProcessorEmit,
LocationSpec,
results,
processingResult,
} from '@backstage/plugin-catalog-backend';
/**
@@ -119,10 +119,10 @@ export class LdapOrgReaderProcessor implements CatalogProcessor {
// Done!
for (const group of groups) {
emit(results.entity(location, group));
emit(processingResult.entity(location, group));
}
for (const user of users) {
emit(results.entity(location, user));
emit(processingResult.entity(location, user));
}
return true;
@@ -19,7 +19,7 @@ import {
CatalogProcessor,
CatalogProcessorEmit,
LocationSpec,
results,
processingResult,
} from '@backstage/plugin-catalog-backend';
import { Logger } from 'winston';
import {
@@ -125,10 +125,10 @@ export class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor {
// Done!
for (const group of groups) {
emit(results.entity(location, group));
emit(processingResult.entity(location, group));
}
for (const user of users) {
emit(results.entity(location, user));
emit(processingResult.entity(location, user));
}
return true;
+31 -6
View File
@@ -524,7 +524,7 @@ export type EntitiesSearchFilter = {
values?: string[];
};
// @public (undocumented)
// @public @deprecated (undocumented)
function entity(
atLocation: LocationSpec,
newEntity: Entity,
@@ -641,7 +641,7 @@ export class FileReaderProcessor implements CatalogProcessor {
): Promise<boolean>;
}
// @public (undocumented)
// @public @deprecated (undocumented)
function generalError(
atLocation: LocationSpec,
message: string,
@@ -776,13 +776,13 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor {
): Promise<boolean>;
}
// @public (undocumented)
// @public @deprecated (undocumented)
function inputError(
atLocation: LocationSpec,
message: string,
): CatalogProcessorResult;
// @public (undocumented)
// @public @deprecated (undocumented)
function location_2(
newLocation: LocationSpec,
optional?: boolean,
@@ -876,7 +876,7 @@ export interface LocationStore {
listLocations(): Promise<Location_2[]>;
}
// @public (undocumented)
// @public @deprecated (undocumented)
function notFoundError(
atLocation: LocationSpec,
message: string,
@@ -963,6 +963,31 @@ export type PlaceholderResolverResolveUrl = (
base: string,
) => string;
// @public
export const processingResult: Readonly<{
readonly notFoundError: (
atLocation: LocationSpec,
message: string,
) => CatalogProcessorResult;
readonly inputError: (
atLocation: LocationSpec,
message: string,
) => CatalogProcessorResult;
readonly generalError: (
atLocation: LocationSpec,
message: string,
) => CatalogProcessorResult;
readonly location: (
newLocation: LocationSpec,
optional?: boolean | undefined,
) => CatalogProcessorResult;
readonly entity: (
atLocation: LocationSpec,
newEntity: Entity,
) => CatalogProcessorResult;
readonly relation: (spec: EntityRelationSpec) => CatalogProcessorResult;
}>;
// @public
export type RecursivePartial<T> = {
[P in keyof T]?: T[P] extends (infer U)[]
@@ -986,7 +1011,7 @@ export interface RefreshService {
refresh(options: RefreshOptions): Promise<void>;
}
// @public (undocumented)
// @public @deprecated (undocumented)
function relation(spec: EntityRelationSpec): CatalogProcessorResult;
declare namespace results {
+56
View File
@@ -0,0 +1,56 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { EntityName } from '@backstage/catalog-model';
/**
* Holds the entity location information.
*
* @remarks
*
* `presence` flag: when using repo importer plugin, location is being created before the component yaml file is merged to the main branch.
* This flag is then set to indicate that the file can be not present.
* default value: 'required'.
*
* @public
*/
export type LocationSpec = {
type: string;
target: string;
presence?: 'optional' | 'required';
};
/**
* Holds the relation data for entities.
*
* @public
*/
export type EntityRelationSpec = {
/**
* The source entity of this relation.
*/
source: EntityName;
/**
* The type of the relation.
*/
type: string;
/**
* The target entity of this relation.
*/
target: EntityName;
};
@@ -16,10 +16,15 @@
import { InputError, NotFoundError } from '@backstage/errors';
import { Entity } from '@backstage/catalog-model';
import { CatalogProcessorResult, LocationSpec } from './types';
import { EntityRelationSpec } from '../../processing/types';
import { CatalogProcessorResult } from './processor';
import { EntityRelationSpec, LocationSpec } from './common';
/** @public */
// NOTE: This entire file is deprecated and should be eventually removed along with the `result` export
/**
* @public
* @deprecated import the processingResult symbol instead and use its fields
*/
export function notFoundError(
atLocation: LocationSpec,
message: string,
@@ -31,7 +36,10 @@ export function notFoundError(
};
}
/** @public */
/**
* @public
* @deprecated import the processingResult symbol instead and use its fields
*/
export function inputError(
atLocation: LocationSpec,
message: string,
@@ -43,7 +51,10 @@ export function inputError(
};
}
/** @public */
/**
* @public
* @deprecated import the processingResult symbol instead and use its fields
*/
export function generalError(
atLocation: LocationSpec,
message: string,
@@ -51,7 +62,10 @@ export function generalError(
return { type: 'error', location: atLocation, error: new Error(message) };
}
/** @public */
/**
* @public
* @deprecated import the processingResult symbol instead and use its fields
*/
export function location(
newLocation: LocationSpec,
optional?: boolean,
@@ -59,7 +73,10 @@ export function location(
return { type: 'location', location: newLocation, optional };
}
/** @public */
/**
* @public
* @deprecated import the processingResult symbol instead and use its fields
*/
export function entity(
atLocation: LocationSpec,
newEntity: Entity,
@@ -67,7 +84,10 @@ export function entity(
return { type: 'entity', location: atLocation, entity: newEntity };
}
/** @public */
/**
* @public
* @deprecated import the processingResult symbol instead and use its fields
*/
export function relation(spec: EntityRelationSpec): CatalogProcessorResult {
return { type: 'relation', relation: spec };
}
+38
View File
@@ -0,0 +1,38 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as results from './deprecatedResult';
export { results };
export { processingResult } from './processingResult';
export type { EntityRelationSpec, LocationSpec } from './common';
export type {
CatalogProcessor,
CatalogProcessorParser,
CatalogProcessorCache,
CatalogProcessorEmit,
CatalogProcessorLocationResult,
CatalogProcessorEntityResult,
CatalogProcessorRelationResult,
CatalogProcessorErrorResult,
CatalogProcessorResult,
} from './processor';
export type {
EntityProvider,
EntityProviderConnection,
EntityProviderMutation,
} from './provider';
@@ -0,0 +1,71 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { InputError, NotFoundError } from '@backstage/errors';
import { Entity } from '@backstage/catalog-model';
import { CatalogProcessorResult } from './processor';
import { EntityRelationSpec, LocationSpec } from './common';
/**
* Factory functions for the standard processing result types.
*
* @public
*/
export const processingResult = Object.freeze({
notFoundError(
atLocation: LocationSpec,
message: string,
): CatalogProcessorResult {
return {
type: 'error',
location: atLocation,
error: new NotFoundError(message),
};
},
inputError(
atLocation: LocationSpec,
message: string,
): CatalogProcessorResult {
return {
type: 'error',
location: atLocation,
error: new InputError(message),
};
},
generalError(
atLocation: LocationSpec,
message: string,
): CatalogProcessorResult {
return { type: 'error', location: atLocation, error: new Error(message) };
},
location(
newLocation: LocationSpec,
optional?: boolean,
): CatalogProcessorResult {
return { type: 'location', location: newLocation, optional };
},
entity(atLocation: LocationSpec, newEntity: Entity): CatalogProcessorResult {
return { type: 'entity', location: atLocation, entity: newEntity };
},
relation(spec: EntityRelationSpec): CatalogProcessorResult {
return { type: 'relation', relation: spec };
},
} as const);
@@ -16,24 +16,7 @@
import { Entity } from '@backstage/catalog-model';
import { JsonValue } from '@backstage/types';
import { EntityRelationSpec } from '../../processing/types';
/**
* Holds the entity location information.
*
* @remarks
*
* `presence` flag: when using repo importer plugin, location is being created before the component yaml file is merged to the main branch.
* This flag is then set to indicate that the file can be not present.
* default value: 'required'.
*
* @public
*/
export type LocationSpec = {
type: string;
target: string;
presence?: 'optional' | 'required';
};
import { EntityRelationSpec, LocationSpec } from './common';
/**
* @public
@@ -44,7 +44,7 @@ export interface EntityProviderConnection {
* @public
*/
export interface EntityProvider {
/** Unique name provider name used internally for caching. */
/** Unique provider name used internally for caching. */
getProviderName(): string;
/** Connect is called upon initialization by the catalog engine. */
connect(connection: EntityProviderConnection): Promise<void>;
@@ -17,7 +17,8 @@
import { Entity } from '@backstage/catalog-model';
import { JsonObject } from '@backstage/types';
import { DateTime } from 'luxon';
import { DeferredEntity, EntityRelationSpec } from '../processing/types';
import { EntityRelationSpec } from '../api';
import { DeferredEntity } from '../processing/types';
/**
* An abstraction for transactions of the underlying database technology.
+2 -1
View File
@@ -20,11 +20,12 @@
* @packageDocumentation
*/
export * from './api';
export * from './catalog';
export * from './ingestion';
export * from './modules';
export * from './search';
export * from './util';
export * from './processing';
export * from './providers';
export * from './service';
export * from './permissions';
@@ -17,7 +17,7 @@
import { Entity } from '@backstage/catalog-model';
import { ConfigReader } from '@backstage/config';
import { DefaultCatalogRulesEnforcer } from './CatalogRules';
import { LocationSpec } from './processors';
import { LocationSpec } from '../api';
const entity = {
user: {
@@ -17,7 +17,7 @@
import { Config } from '@backstage/config';
import { Entity } from '@backstage/catalog-model';
import path from 'path';
import { LocationSpec } from './processors';
import { LocationSpec } from '../api';
/**
* Rules to apply to catalog entities.
@@ -16,8 +16,6 @@
export { DefaultCatalogRulesEnforcer } from './CatalogRules';
export type { CatalogRule, CatalogRulesEnforcer } from './CatalogRules';
export * from './processors';
export * from './providers';
export type {
AnalyzeLocationEntityField,
AnalyzeLocationExistingEntity,
@@ -16,7 +16,7 @@
import { Entity } from '@backstage/catalog-model';
import { RecursivePartial } from '../util/RecursivePartial';
import { LocationSpec } from './processors';
import { LocationSpec } from '../api';
/** @public */
export type LocationAnalyzer = {
@@ -13,11 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { getVoidLogger, UrlReaders } from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import { AwsS3DiscoveryProcessor } from './AwsS3DiscoveryProcessor';
import { CatalogProcessorEntityResult, CatalogProcessorResult } from './types';
import { defaultEntityDataParser } from './util/parse';
import {
CatalogProcessorEntityResult,
CatalogProcessorResult,
} from '../../api';
import { defaultEntityDataParser } from '../util/parse';
import AWSMock from 'aws-sdk-mock';
import aws from 'aws-sdk';
import path from 'path';
@@ -36,10 +40,7 @@ AWSMock.mock(
'getObject',
Buffer.from(
require('fs').readFileSync(
path.resolve(
__dirname,
'__fixtures__/fileReaderProcessor/awsS3/awsS3-mock-object.txt',
),
path.resolve(__dirname, '__fixtures__/awsS3-mock-object.txt'),
),
),
);
@@ -17,13 +17,13 @@
import { UrlReader } from '@backstage/backend-common';
import { isError } from '@backstage/errors';
import limiterFactory from 'p-limit';
import * as result from './results';
import {
CatalogProcessor,
CatalogProcessorEmit,
CatalogProcessorParser,
LocationSpec,
} from './types';
processingResult,
} from '../../api';
/** @public */
export class AwsS3DiscoveryProcessor implements CatalogProcessor {
@@ -58,10 +58,10 @@ export class AwsS3DiscoveryProcessor implements CatalogProcessor {
if (isError(error) && error.name === 'NotFoundError') {
if (!optional) {
emit(result.notFoundError(location, message));
emit(processingResult.notFoundError(location, message));
}
} else {
emit(result.generalError(location, message));
emit(processingResult.generalError(location, message));
}
}
return true;
@@ -1,5 +1,5 @@
/*
* Copyright 2021 The Backstage Authors
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,4 +14,4 @@
* limitations under the License.
*/
export { GitHubOrgEntityProvider } from './GitHubOrgEntityProvider';
export { AwsS3DiscoveryProcessor } from './AwsS3DiscoveryProcessor';
@@ -16,14 +16,14 @@
import { getVoidLogger } from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import { codeSearch } from './azure';
import { codeSearch } from './lib';
import {
AzureDevOpsDiscoveryProcessor,
parseUrl,
} from './AzureDevOpsDiscoveryProcessor';
import { LocationSpec } from './types';
import { LocationSpec } from '../../api';
jest.mock('./azure');
jest.mock('./lib');
const mockCodeSearch = codeSearch as jest.MockedFunction<typeof codeSearch>;
describe('AzureDevOpsDiscoveryProcessor', () => {
@@ -20,9 +20,13 @@ import {
ScmIntegrations,
} from '@backstage/integration';
import { Logger } from 'winston';
import * as results from './results';
import { CatalogProcessor, CatalogProcessorEmit, LocationSpec } from './types';
import { codeSearch } from './azure';
import {
CatalogProcessor,
CatalogProcessorEmit,
LocationSpec,
processingResult,
} from '../../api';
import { codeSearch } from './lib';
/**
* Extracts repositories out of an Azure DevOps org.
@@ -102,7 +106,7 @@ export class AzureDevOpsDiscoveryProcessor implements CatalogProcessor {
for (const file of files) {
emit(
results.location({
processingResult.location({
type: 'url',
target: `${baseUrl}/${org}/${project}/_git/${file.repository.name}?path=${file.path}`,
// Not all locations may actually exist, since the user defined them as a wildcard pattern.
@@ -1,5 +1,5 @@
/*
* Copyright 2021 The Backstage Authors
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,8 +14,4 @@
* limitations under the License.
*/
export type {
EntityProvider,
EntityProviderConnection,
EntityProviderMutation,
} from './types';
export { AzureDevOpsDiscoveryProcessor } from './AzureDevOpsDiscoveryProcessor';
@@ -13,18 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { getVoidLogger } from '@backstage/backend-common';
import { BitbucketDiscoveryProcessor } from './BitbucketDiscoveryProcessor';
import { ConfigReader } from '@backstage/config';
import {
BitbucketRepository20,
PagedResponse,
PagedResponse20,
} from './bitbucket';
import { LocationSpec } from './types';
import { results } from './index';
import { RequestHandler, rest } from 'msw';
import { setupServer } from 'msw/node';
import { BitbucketRepository20, PagedResponse, PagedResponse20 } from './lib';
import { LocationSpec, processingResult } from '../../api';
const server = setupServer();
@@ -755,7 +751,7 @@ describe('BitbucketDiscoveryProcessor', () => {
}),
{
parser: async function* customRepositoryParser({}) {
yield results.location({
yield processingResult.location({
type: 'custom-location-type',
target: 'custom-target',
presence: 'optional',
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Logger } from 'winston';
import { Config } from '@backstage/config';
@@ -28,13 +29,13 @@ import {
paginated20,
BitbucketRepository,
BitbucketRepository20,
} from './bitbucket';
} from './lib';
import {
CatalogProcessor,
CatalogProcessorEmit,
CatalogProcessorResult,
LocationSpec,
} from './types';
} from '../../api';
const DEFAULT_BRANCH = 'master';
const DEFAULT_CATALOG_LOCATION = '/catalog-info.yaml';
@@ -0,0 +1,18 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { BitbucketDiscoveryProcessor } from './BitbucketDiscoveryProcessor';
export type { BitbucketRepositoryParser } from './lib';
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { processingResult } from '../../../api';
import { defaultRepositoryParser } from './BitbucketRepositoryParser';
import { results } from '../index';
describe('BitbucketRepositoryParser', () => {
describe('defaultRepositoryParser', () => {
@@ -23,7 +24,7 @@ describe('BitbucketRepositoryParser', () => {
'https://bitbucket.mycompany.com/projects/project-key/repos/repo-slug/browse';
const path = '/catalog-info.yaml';
const expected = [
results.location({
processingResult.location({
type: 'url',
target: `${browseUrl}${path}`,
presence: 'optional',
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CatalogProcessorResult } from '../types';
import { results } from '../index';
import { Logger } from 'winston';
import { BitbucketIntegration } from '@backstage/integration';
import { Logger } from 'winston';
import { CatalogProcessorResult, processingResult } from '../../../api';
/**
* @public
@@ -28,17 +28,14 @@ export type BitbucketRepositoryParser = (options: {
logger: Logger;
}) => AsyncIterable<CatalogProcessorResult>;
export const defaultRepositoryParser = async function* defaultRepositoryParser({
target,
}: {
target: string;
}) {
yield results.location({
type: 'url',
target: target,
// Not all locations may actually exist, since the user defined them as a wildcard pattern.
// Thus, we emit them as optional and let the downstream processor find them while not outputting
// an error if it couldn't.
presence: 'optional',
});
};
export const defaultRepositoryParser =
async function* defaultRepositoryParser(options: { target: string }) {
yield processingResult.location({
type: 'url',
target: options.target,
// Not all locations may actually exist, since the user defined them as a wildcard pattern.
// Thus, we emit them as optional and let the downstream processor find them while not outputting
// an error if it couldn't.
presence: 'optional',
});
};
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fetch from 'node-fetch';
import fetch from 'node-fetch';
import {
BitbucketIntegrationConfig,
getBitbucketRequestOptions,
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { BitbucketClient, paginated, paginated20 } from './client';
export { defaultRepositoryParser } from './BitbucketRepositoryParser';
export type { PagedResponse, PagedResponse20 } from './client';
@@ -17,7 +17,7 @@
import { getVoidLogger } from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import { CodeOwnersProcessor } from './CodeOwnersProcessor';
import { LocationSpec } from './types';
import { LocationSpec } from '../../api';
const mockCodeOwnersText = () => `
* @acme/team-foo @acme/team-bar
@@ -22,11 +22,10 @@ import {
ScmIntegrations,
} from '@backstage/integration';
import { Logger } from 'winston';
import { findCodeOwnerByTarget } from './codeowners';
import { CatalogProcessor, LocationSpec } from './types';
import { CatalogProcessor, LocationSpec } from '../../api';
import { findCodeOwnerByTarget } from './lib';
const ALLOWED_KINDS = ['API', 'Component', 'Domain', 'Resource', 'System'];
const ALLOWED_LOCATION_TYPES = ['url'];
/** @public */
@@ -0,0 +1,17 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { CodeOwnersProcessor } from './CodeOwnersProcessor';
@@ -17,7 +17,7 @@
import { Entity } from '@backstage/catalog-model';
import { ConfigReader } from '@backstage/config';
import { ScmIntegrations } from '@backstage/integration';
import { LocationSpec } from './types';
import { LocationSpec } from '../../api';
import { AnnotateLocationEntityProcessor } from './AnnotateLocationEntityProcessor';
describe('AnnotateLocationEntityProcessor', () => {
@@ -25,7 +25,11 @@ import {
} from '@backstage/catalog-model';
import { ScmIntegrationRegistry } from '@backstage/integration';
import { identity, merge, pickBy } from 'lodash';
import { CatalogProcessor, CatalogProcessorEmit, LocationSpec } from './types';
import {
CatalogProcessor,
CatalogProcessorEmit,
LocationSpec,
} from '../../api';
/** @public */
export class AnnotateLocationEntityProcessor implements CatalogProcessor {
@@ -16,7 +16,7 @@
import { Entity } from '@backstage/catalog-model';
import { ConfigReader } from '@backstage/config';
import { AnnotateScmSlugEntityProcessor } from './AnnotateScmSlugEntityProcessor';
import { LocationSpec } from './types';
import { LocationSpec } from '../../api';
describe('AnnotateScmSlugEntityProcessor', () => {
describe('github', () => {
@@ -21,7 +21,7 @@ import {
} from '@backstage/integration';
import parseGitUrl from 'git-url-parse';
import { identity, merge, pickBy } from 'lodash';
import { CatalogProcessor, LocationSpec } from './types';
import { CatalogProcessor, LocationSpec } from '../../api';
const GITHUB_ACTIONS_ANNOTATION = 'github.com/project-slug';
@@ -52,8 +52,12 @@ import {
TemplateEntityV1beta2,
templateEntityV1beta2Validator,
} from '@backstage/plugin-scaffolder-common';
import * as result from './results';
import { CatalogProcessor, CatalogProcessorEmit, LocationSpec } from './types';
import {
CatalogProcessor,
CatalogProcessorEmit,
LocationSpec,
processingResult,
} from '../../api';
/** @public */
export class BuiltinKindsEntityProcessor implements CatalogProcessor {
@@ -107,7 +111,7 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor {
for (const target of [targets].flat()) {
const targetRef = parseEntityRef(target, context);
emit(
result.relation({
processingResult.relation({
source: selfRef,
type: outgoingRelation,
target: {
@@ -118,7 +122,7 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor {
}),
);
emit(
result.relation({
processingResult.relation({
source: {
kind: targetRef.kind,
namespace: targetRef.namespace,
@@ -17,7 +17,7 @@
import { ConfigReader } from '@backstage/config';
import path from 'path';
import { ConfigLocationEntityProvider } from './ConfigLocationEntityProvider';
import { EntityProviderConnection } from './types';
import { EntityProviderConnection } from '../../api';
describe('ConfigLocationEntityProvider', () => {
it('should apply mutation with the correct paths in the config', async () => {
@@ -16,9 +16,9 @@
import { Config } from '@backstage/config';
import path from 'path';
import { getEntityLocationRef } from '../processing/util';
import { EntityProvider, EntityProviderConnection } from './types';
import { locationSpecToLocationEntity } from '../util/conversion';
import { getEntityLocationRef } from '../../processing/util';
import { EntityProvider, EntityProviderConnection } from '../../api';
import { locationSpecToLocationEntity } from '../../util/conversion';
export class ConfigLocationEntityProvider implements EntityProvider {
constructor(private readonly config: Config) {}
@@ -15,7 +15,7 @@
*/
import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils';
import { v4 as uuid } from 'uuid';
import { applyDatabaseMigrations } from '../database/migrations';
import { applyDatabaseMigrations } from '../../database/migrations';
import { DefaultLocationStore } from './DefaultLocationStore';
describe('DefaultLocationStore', () => {
@@ -18,11 +18,11 @@ import { Location } from '@backstage/catalog-client';
import { ConflictError, NotFoundError } from '@backstage/errors';
import { Knex } from 'knex';
import { v4 as uuid } from 'uuid';
import { DbLocationsRow } from '../database/tables';
import { getEntityLocationRef } from '../processing/util';
import { EntityProvider, EntityProviderConnection } from './types';
import { locationSpecToLocationEntity } from '../util/conversion';
import { LocationInput, LocationStore } from '../service';
import { DbLocationsRow } from '../../database/tables';
import { getEntityLocationRef } from '../../processing/util';
import { EntityProvider, EntityProviderConnection } from '../../api';
import { locationSpecToLocationEntity } from '../../util/conversion';
import { LocationInput, LocationStore } from '../../service';
export class DefaultLocationStore implements LocationStore, EntityProvider {
private _connection: EntityProviderConnection | undefined;
@@ -19,12 +19,16 @@ import {
CatalogProcessorEntityResult,
CatalogProcessorErrorResult,
CatalogProcessorResult,
} from './types';
} from '../../api';
import path from 'path';
import { defaultEntityDataParser } from './util/parse';
import { defaultEntityDataParser } from '../util/parse';
describe('FileReaderProcessor', () => {
const fixturesRoot = path.join(__dirname, '__fixtures__/fileReaderProcessor');
const fixturesRoot = path.join(
__dirname,
'__fixtures__',
'fileReaderProcessor',
);
it('should load from file', async () => {
const processor = new FileReaderProcessor();
@@ -18,13 +18,13 @@ import fs from 'fs-extra';
import g from 'glob';
import path from 'path';
import { promisify } from 'util';
import * as result from './results';
import {
CatalogProcessor,
CatalogProcessorEmit,
CatalogProcessorParser,
LocationSpec,
} from './types';
processingResult,
} from '../../api';
const glob = promisify(g);
@@ -65,11 +65,11 @@ export class FileReaderProcessor implements CatalogProcessor {
}
} else if (!optional) {
const message = `${location.type} ${location.target} does not exist`;
emit(result.notFoundError(location, message));
emit(processingResult.notFoundError(location, message));
}
} catch (e) {
const message = `${location.type} ${location.target} could not be read, ${e}`;
emit(result.generalError(location, message));
emit(processingResult.generalError(location, message));
}
return true;
@@ -21,7 +21,7 @@ import {
} from '@backstage/integration';
import path from 'path';
import { toAbsoluteUrl } from './LocationEntityProcessor';
import { LocationSpec } from './types';
import { LocationSpec } from '../../api';
describe('LocationEntityProcessor', () => {
describe('toAbsoluteUrl', () => {
@@ -17,8 +17,12 @@
import { Entity, LocationEntity } from '@backstage/catalog-model';
import { ScmIntegrationRegistry } from '@backstage/integration';
import path from 'path';
import * as result from './results';
import { CatalogProcessor, CatalogProcessorEmit, LocationSpec } from './types';
import {
processingResult,
CatalogProcessor,
CatalogProcessorEmit,
LocationSpec,
} from '../../api';
export function toAbsoluteUrl(
integrations: ScmIntegrationRegistry,
@@ -62,7 +66,7 @@ export class LocationEntityProcessor implements CatalogProcessor {
const type = locationEntity.spec.type || location.type;
if (type === 'file' && location.target.endsWith(path.sep)) {
emit(
result.inputError(
processingResult.inputError(
location,
`LocationEntityProcessor cannot handle ${type} type location with target ${location.target} that ends with a path separator`,
),
@@ -83,7 +87,7 @@ export class LocationEntityProcessor implements CatalogProcessor {
location,
maybeRelativeTarget,
);
emit(result.location({ type, target }));
emit(processingResult.location({ type, target }));
}
}
@@ -19,7 +19,7 @@ import { Entity } from '@backstage/catalog-model';
import { JsonValue } from '@backstage/types';
import { ScmIntegrationRegistry } from '@backstage/integration';
import yaml from 'yaml';
import { CatalogProcessor, LocationSpec } from './types';
import { CatalogProcessor, LocationSpec } from '../../api';
/** @public */
export type PlaceholderResolverRead = (url: string) => Promise<Buffer>;
@@ -15,8 +15,11 @@
*/
import { Config } from '@backstage/config';
import * as result from './results';
import { CatalogProcessorEmit, LocationSpec } from './types';
import {
processingResult,
CatalogProcessorEmit,
LocationSpec,
} from '../../api';
/**
* @deprecated no longer in use, replaced by the ConfigLocationEntityProvider.
@@ -48,7 +51,7 @@ export class StaticLocationProcessor implements StaticLocationProcessor {
}
for (const staticLocation of this.staticLocations) {
emit(result.location(staticLocation));
emit(processingResult.location(staticLocation));
}
return true;
@@ -28,9 +28,9 @@ import {
CatalogProcessorEntityResult,
CatalogProcessorErrorResult,
CatalogProcessorResult,
} from './types';
} from '../../api';
import { defaultEntityDataParser } from '../util/parse';
import { UrlReaderProcessor } from './UrlReaderProcessor';
import { defaultEntityDataParser } from './util/parse';
describe('UrlReaderProcessor', () => {
const mockApiOrigin = 'http://localhost';
@@ -20,7 +20,6 @@ import { assertError } from '@backstage/errors';
import parseGitUrl from 'git-url-parse';
import limiterFactory from 'p-limit';
import { Logger } from 'winston';
import * as result from './results';
import {
CatalogProcessor,
CatalogProcessorCache,
@@ -29,7 +28,8 @@ import {
CatalogProcessorParser,
CatalogProcessorResult,
LocationSpec,
} from './types';
processingResult,
} from '../../api';
const CACHE_KEY = 'v1';
@@ -102,10 +102,10 @@ export class UrlReaderProcessor implements CatalogProcessor {
}
} else if (error.name === 'NotFoundError') {
if (!optional) {
emit(result.notFoundError(location, message));
emit(processingResult.notFoundError(location, message));
}
} else {
emit(result.generalError(location, message));
emit(processingResult.generalError(location, message));
}
}
@@ -14,20 +14,10 @@
* limitations under the License.
*/
import * as results from './results';
export { AnnotateLocationEntityProcessor } from './AnnotateLocationEntityProcessor';
export { AnnotateScmSlugEntityProcessor } from './AnnotateScmSlugEntityProcessor';
export { AwsS3DiscoveryProcessor } from './AwsS3DiscoveryProcessor';
export { BitbucketDiscoveryProcessor } from './BitbucketDiscoveryProcessor';
export { BuiltinKindsEntityProcessor } from './BuiltinKindsEntityProcessor';
export { CodeOwnersProcessor } from './CodeOwnersProcessor';
export { FileReaderProcessor } from './FileReaderProcessor';
export { GithubDiscoveryProcessor } from './GithubDiscoveryProcessor';
export { AzureDevOpsDiscoveryProcessor } from './AzureDevOpsDiscoveryProcessor';
export { GithubOrgReaderProcessor } from './GithubOrgReaderProcessor';
export { GithubMultiOrgReaderProcessor } from './GithubMultiOrgReaderProcessor';
export { GitLabDiscoveryProcessor } from './GitLabDiscoveryProcessor';
export { LocationEntityProcessor } from './LocationEntityProcessor';
export type { LocationEntityProcessorOptions } from './LocationEntityProcessor';
export { PlaceholderProcessor } from './PlaceholderProcessor';
@@ -39,10 +29,5 @@ export type {
PlaceholderResolverResolveUrl,
} from './PlaceholderProcessor';
export { StaticLocationProcessor } from './StaticLocationProcessor';
export * from './types';
export { UrlReaderProcessor } from './UrlReaderProcessor';
export { parseEntityYaml } from './util/parse';
export { results };
export type { BitbucketRepositoryParser } from './bitbucket';
export type { GithubMultiOrgConfig } from './github';
export { parseEntityYaml } from '../util/parse';
@@ -20,10 +20,12 @@ import {
GithubCredentialsProvider,
GitHubIntegrationConfig,
} from '@backstage/integration';
import { GitHubOrgEntityProvider } from '.';
import { EntityProviderConnection } from '../../providers';
import { withLocations } from './GitHubOrgEntityProvider';
import { graphql } from '@octokit/graphql';
import { EntityProviderConnection } from '../../api';
import {
GitHubOrgEntityProvider,
withLocations,
} from './GitHubOrgEntityProvider';
jest.mock('@octokit/graphql');
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
ANNOTATION_LOCATION,
ANNOTATION_ORIGIN_LOCATION,
@@ -29,13 +30,13 @@ import {
import { graphql } from '@octokit/graphql';
import { merge } from 'lodash';
import { Logger } from 'winston';
import { EntityProvider, EntityProviderConnection } from '../../providers';
import { EntityProvider, EntityProviderConnection } from '../../api';
import {
getOrganizationTeams,
getOrganizationUsers,
parseGitHubOrgUrl,
} from '../processors/github';
import { assignGroupsToUsers, buildOrgHierarchy } from '../processors/util/org';
} from './lib';
import { assignGroupsToUsers, buildOrgHierarchy } from '../util/org';
// TODO: Consider supporting an (optional) webhook that reacts on org changes
/** @public */
@@ -15,16 +15,16 @@
*/
import { getVoidLogger } from '@backstage/backend-common';
import { GithubDiscoveryProcessor, parseUrl } from './GithubDiscoveryProcessor';
import { getOrganizationRepositories } from './github';
import { LocationSpec } from './types';
import { ConfigReader } from '@backstage/config';
import {
ScmIntegrations,
DefaultGithubCredentialsProvider,
ScmIntegrations,
} from '@backstage/integration';
import { LocationSpec } from '../../api';
import { GithubDiscoveryProcessor, parseUrl } from './GithubDiscoveryProcessor';
import { getOrganizationRepositories } from './lib';
jest.mock('./github');
jest.mock('./lib');
const mockGetOrganizationRepositories =
getOrganizationRepositories as jest.MockedFunction<
typeof getOrganizationRepositories
@@ -23,9 +23,13 @@ import {
} from '@backstage/integration';
import { graphql } from '@octokit/graphql';
import { Logger } from 'winston';
import { getOrganizationRepositories } from './github';
import * as results from './results';
import { CatalogProcessor, CatalogProcessorEmit, LocationSpec } from './types';
import { getOrganizationRepositories } from './lib';
import {
CatalogProcessor,
CatalogProcessorEmit,
LocationSpec,
processingResult,
} from '../../api';
/**
* Extracts repositories out of a GitHub org.
@@ -141,7 +145,7 @@ export class GithubDiscoveryProcessor implements CatalogProcessor {
const path = `/blob/${branchName}${catalogPath}`;
emit(
results.location({
processingResult.location({
type: 'url',
target: `${repository.url}${path}`,
// Not all locations may actually exist, since the user defined them as a wildcard pattern.
@@ -30,10 +30,14 @@ import {
getOrganizationUsers,
GithubMultiOrgConfig,
readGithubMultiOrgConfig,
} from './github';
import * as results from './results';
import { CatalogProcessor, CatalogProcessorEmit, LocationSpec } from './types';
import { buildOrgHierarchy } from './util/org';
} from './lib';
import {
CatalogProcessor,
CatalogProcessorEmit,
LocationSpec,
processingResult,
} from '../../api';
import { buildOrgHierarchy } from '../util/org';
/**
* @alpha
@@ -158,7 +162,7 @@ export class GithubMultiOrgReaderProcessor implements CatalogProcessor {
buildOrgHierarchy(groups);
for (const group of groups) {
emit(results.entity(location, group));
emit(processingResult.entity(location, group));
}
} catch (e) {
this.logger.error(
@@ -169,7 +173,7 @@ export class GithubMultiOrgReaderProcessor implements CatalogProcessor {
const allUsers = Array.from(allUsersMap.values());
for (const user of allUsers) {
emit(results.entity(location, user));
emit(processingResult.entity(location, user));
}
return true;
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { getVoidLogger } from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import {
@@ -21,7 +22,7 @@ import {
} from '@backstage/integration';
import { graphql } from '@octokit/graphql';
import { GithubOrgReaderProcessor } from './GithubOrgReaderProcessor';
import { LocationSpec } from './types';
import { LocationSpec } from '../../api';
jest.mock('@octokit/graphql');
@@ -28,10 +28,14 @@ import {
getOrganizationTeams,
getOrganizationUsers,
parseGitHubOrgUrl,
} from './github';
import * as results from './results';
import { CatalogProcessor, CatalogProcessorEmit, LocationSpec } from './types';
import { assignGroupsToUsers, buildOrgHierarchy } from './util/org';
} from './lib';
import {
CatalogProcessor,
CatalogProcessorEmit,
LocationSpec,
processingResult,
} from '../../api';
import { assignGroupsToUsers, buildOrgHierarchy } from '../util/org';
type GraphQL = typeof graphql;
@@ -106,10 +110,10 @@ export class GithubOrgReaderProcessor implements CatalogProcessor {
// Done!
for (const group of groups) {
emit(results.entity(location, group));
emit(processingResult.entity(location, group));
}
for (const user of users) {
emit(results.entity(location, user));
emit(processingResult.entity(location, user));
}
return true;
@@ -0,0 +1,21 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { GithubDiscoveryProcessor } from './GithubDiscoveryProcessor';
export { GithubMultiOrgReaderProcessor } from './GithubMultiOrgReaderProcessor';
export { GitHubOrgEntityProvider } from './GitHubOrgEntityProvider';
export { GithubOrgReaderProcessor } from './GithubOrgReaderProcessor';
export type { GithubMultiOrgConfig } from './lib';
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { parseGitHubOrgUrl } from './util';
describe('parseGitHubOrgUrl', () => {
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export function parseGitHubOrgUrl(urlString: string): { org: string } {
const path = new URL(urlString).pathname.substr(1).split('/');
@@ -19,8 +19,8 @@ import { getVoidLogger } from '@backstage/backend-common';
import { GitLabDiscoveryProcessor, parseUrl } from './GitLabDiscoveryProcessor';
import { setupServer } from 'msw/node';
import { rest } from 'msw';
import { GitLabProject } from './gitlab';
import { LocationSpec } from './types';
import { GitLabProject } from './lib';
import { LocationSpec } from '../../api';
const server = setupServer();
@@ -20,9 +20,13 @@ import {
ScmIntegrations,
} from '@backstage/integration';
import { Logger } from 'winston';
import * as results from './results';
import { CatalogProcessor, CatalogProcessorEmit, LocationSpec } from './types';
import { GitLabClient, GitLabProject, paginated } from './gitlab';
import {
CatalogProcessor,
CatalogProcessorEmit,
LocationSpec,
processingResult,
} from '../../api';
import { GitLabClient, GitLabProject, paginated } from './lib';
import {
CacheClient,
CacheManager,
@@ -95,12 +99,12 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor {
page: 1,
});
const result: Result = {
const res: Result = {
scanned: 0,
matches: [],
};
for await (const project of projects) {
result.scanned++;
res.scanned++;
if (project.archived) {
continue;
@@ -110,14 +114,14 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor {
continue;
}
result.matches.push(project);
res.matches.push(project);
}
for (const project of result.matches) {
for (const project of res.matches) {
const project_branch = branch === '*' ? project.default_branch : branch;
emit(
results.location({
processingResult.location({
type: 'url',
// The format expected by the GitLabUrlReader:
// https://gitlab.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/filepath
@@ -133,7 +137,7 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor {
const duration = ((Date.now() - startTimestamp) / 1000).toFixed(1);
this.logger.debug(
`Read ${result.scanned} GitLab repositories in ${duration} seconds`,
`Read ${res.scanned} GitLab repositories in ${duration} seconds`,
);
return true;
@@ -0,0 +1,17 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { GitLabDiscoveryProcessor } from './GitLabDiscoveryProcessor';
@@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ConfigReader } from '@backstage/config';
import { setupRequestMockHandlers } from '@backstage/test-utils';
import { readGitLabIntegrationConfig } from '@backstage/integration';
import { getVoidLogger } from '@backstage/backend-common';
import { rest } from 'msw';
import { setupServer, SetupServerApi } from 'msw/node';
import { GitLabClient, paginated } from './client';
const server = setupServer();
@@ -0,0 +1,23 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './aws';
export * from './azure';
export * from './bitbucket';
export * from './codeowners';
export * from './core';
export * from './github';
export * from './gitlab';
@@ -15,7 +15,7 @@
*/
import { parseEntityYaml } from './parse';
import * as result from '../results';
import { processingResult } from '../../api';
const testLoc = {
target: 'my-loc-target',
@@ -47,7 +47,7 @@ describe('parseEntityYaml', () => {
);
expect(results).toEqual([
result.entity(testLoc, {
processingResult.entity(testLoc, {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
@@ -92,7 +92,7 @@ describe('parseEntityYaml', () => {
);
expect(results).toEqual([
result.entity(testLoc, {
processingResult.entity(testLoc, {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
@@ -102,7 +102,7 @@ describe('parseEntityYaml', () => {
type: 'website',
},
}),
result.entity(testLoc, {
processingResult.entity(testLoc, {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
@@ -137,7 +137,7 @@ describe('parseEntityYaml', () => {
);
expect(results).toEqual([
result.entity(testLoc, {
processingResult.entity(testLoc, {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
@@ -157,7 +157,7 @@ describe('parseEntityYaml', () => {
// Parse errors are always per document
expect(results).toEqual([
result.generalError(
processingResult.generalError(
testLoc,
'YAML error at my-loc-type:my-loc-target, YAMLSemanticError: Plain value cannot start with reserved character `',
),
@@ -186,7 +186,7 @@ describe('parseEntityYaml', () => {
);
expect(results).toEqual([
result.entity(testLoc, {
processingResult.entity(testLoc, {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
@@ -196,7 +196,7 @@ describe('parseEntityYaml', () => {
type: 'website',
},
}),
result.generalError(
processingResult.generalError(
testLoc,
'YAML error at my-loc-type:my-loc-target, YAMLSemanticError: Nested mappings are not allowed in compact mappings',
),
@@ -209,7 +209,10 @@ describe('parseEntityYaml', () => {
);
expect(results).toEqual([
result.generalError(testLoc, 'Expected object at root, got string'),
processingResult.generalError(
testLoc,
'Expected object at root, got string',
),
]);
});
});
@@ -17,12 +17,12 @@
import { Entity, stringifyLocationRef } from '@backstage/catalog-model';
import lodash from 'lodash';
import yaml from 'yaml';
import * as result from '../results';
import {
CatalogProcessorParser,
CatalogProcessorResult,
LocationSpec,
} from '../types';
processingResult,
} from '../../api';
/** @public */
export function* parseEntityYaml(
@@ -35,7 +35,7 @@ export function* parseEntityYaml(
} catch (e) {
const loc = stringifyLocationRef(location);
const message = `Failed to parse YAML at ${loc}, ${e}`;
yield result.generalError(location, message);
yield processingResult.generalError(location, message);
return;
}
@@ -43,17 +43,17 @@ export function* parseEntityYaml(
if (document.errors?.length) {
const loc = stringifyLocationRef(location);
const message = `YAML error at ${loc}, ${document.errors[0]}`;
yield result.generalError(location, message);
yield processingResult.generalError(location, message);
} else {
const json = document.toJSON();
if (lodash.isPlainObject(json)) {
yield result.entity(location, json as Entity);
yield processingResult.entity(location, json as Entity);
} else if (json === null) {
// Ignore null values, these happen if there is an empty document in the
// YAML file, for example if --- is added to the end of the file.
} else {
const message = `Expected object at root, got ${typeof json}`;
yield result.generalError(location, message);
yield processingResult.generalError(location, message);
}
}
}
@@ -29,11 +29,11 @@ import {
CatalogProcessorEmit,
CatalogProcessorParser,
LocationSpec,
results,
} from '../ingestion';
processingResult,
} from '../api';
import { CatalogRulesEnforcer } from '../ingestion/CatalogRules';
import { DefaultCatalogProcessingOrchestrator } from './DefaultCatalogProcessingOrchestrator';
import { defaultEntityDataParser } from '../ingestion/processors/util/parse';
import { defaultEntityDataParser } from '../modules/util/parse';
import { ConfigReader } from '@backstage/config';
class FooBarProcessor implements CatalogProcessor {
@@ -51,7 +51,7 @@ class FooBarProcessor implements CatalogProcessor {
) {
if (await cache.get('emit')) {
emit(
results.entity(
processingResult.entity(
{ type: 'url', target: './new-place' },
{
apiVersion: 'my-api/v1',
@@ -63,7 +63,7 @@ class FooBarProcessor implements CatalogProcessor {
),
);
emit(
results.relation({
processingResult.relation({
type: 'my-type',
source: { kind: 'foobar', name: 'my-source', namespace: 'default' },
target: { kind: 'foobar', name: 'my-target', namespace: 'default' },
@@ -211,7 +211,7 @@ describe('DefaultCatalogProcessingOrchestrator', () => {
getProcessorName: jest.fn(),
validateEntityKind: jest.fn(async () => true),
readLocation: jest.fn(async (_l, _o, emit) => {
emit(results.entity({ type: 't', target: 't' }, entity));
emit(processingResult.entity({ type: 't', target: 't' }, entity));
return true;
}),
};
@@ -36,8 +36,8 @@ import {
CatalogProcessor,
CatalogProcessorParser,
LocationSpec,
} from '../ingestion/processors';
import * as results from '../ingestion/processors/results';
processingResult,
} from '../api';
import {
CatalogProcessingOrchestrator,
EntityProcessingRequest,
@@ -178,13 +178,13 @@ export class DefaultCatalogProcessingOrchestrator
entity: Entity,
context: Context,
): Promise<Entity> {
let result = entity;
let res = entity;
for (const processor of this.options.processors) {
if (processor.preProcessEntity) {
try {
result = await processor.preProcessEntity(
result,
res = await processor.preProcessEntity(
res,
context.location,
context.collector.onEmit,
context.originLocation,
@@ -199,7 +199,7 @@ export class DefaultCatalogProcessingOrchestrator
}
}
return result;
return res;
}
/**
@@ -295,7 +295,7 @@ export class DefaultCatalogProcessingOrchestrator
for (const maybeRelativeTarget of targets) {
if (type === 'file' && maybeRelativeTarget.endsWith(path.sep)) {
context.collector.onEmit(
results.inputError(
processingResult.inputError(
context.location,
`LocationEntityProcessor cannot handle ${type} type location with target ${context.location.target} that ends with a path separator`,
),
@@ -351,13 +351,13 @@ export class DefaultCatalogProcessingOrchestrator
entity: Entity,
context: Context,
): Promise<Entity> {
let result = entity;
let res = entity;
for (const processor of this.options.processors) {
if (processor.postProcessEntity) {
try {
result = await processor.postProcessEntity(
result,
res = await processor.postProcessEntity(
res,
context.location,
context.collector.onEmit,
context.cache.forProcessor(processor),
@@ -371,6 +371,6 @@ export class DefaultCatalogProcessingOrchestrator
}
}
return result;
return res;
}
}

Some files were not shown because too many files have changed in this diff Show More