add parseLocationReference/stringifyLocationReference
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
@@ -19,15 +19,16 @@ import {
|
||||
EntityName,
|
||||
Location,
|
||||
LOCATION_ANNOTATION,
|
||||
stringifyLocationReference,
|
||||
} from '@backstage/catalog-model';
|
||||
import fetch from 'cross-fetch';
|
||||
import {
|
||||
AddLocationRequest,
|
||||
AddLocationResponse,
|
||||
CatalogRequestOptions,
|
||||
CatalogApi,
|
||||
CatalogEntitiesRequest,
|
||||
CatalogListResponse,
|
||||
CatalogRequestOptions,
|
||||
DiscoveryApi,
|
||||
} from './types';
|
||||
|
||||
@@ -135,7 +136,7 @@ export class CatalogClient implements CatalogApi {
|
||||
);
|
||||
return all
|
||||
.map(r => r.data)
|
||||
.find(l => locationCompound === `${l.type}:${l.target}`);
|
||||
.find(l => locationCompound === stringifyLocationReference(l));
|
||||
}
|
||||
|
||||
async removeEntityByUid(
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2021 Spotify AB
|
||||
*
|
||||
* 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 { parseLocationReference, stringifyLocationReference } from './helpers';
|
||||
|
||||
describe('parseLocationReference', () => {
|
||||
it('works for the simple case', () => {
|
||||
expect(parseLocationReference('url:https://www.google.com')).toEqual({
|
||||
type: 'url',
|
||||
target: 'https://www.google.com',
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects faulty inputs', () => {
|
||||
expect(() => parseLocationReference(7 as any)).toThrow(
|
||||
"Unable to parse location reference '7', unexpected argument number",
|
||||
);
|
||||
expect(() => parseLocationReference('')).toThrow(
|
||||
"Unable to parse location reference '', expected '<type>:<target>', e.g. 'url:https://host/path'",
|
||||
);
|
||||
expect(() => parseLocationReference('hello')).toThrow(
|
||||
"Unable to parse location reference 'hello', expected '<type>:<target>', e.g. 'url:https://host/path'",
|
||||
);
|
||||
expect(() => parseLocationReference(':hello')).toThrow(
|
||||
"Unable to parse location reference ':hello', expected '<type>:<target>', e.g. 'url:https://host/path'",
|
||||
);
|
||||
expect(() => parseLocationReference('hello:')).toThrow(
|
||||
"Unable to parse location reference 'hello:', expected '<type>:<target>', e.g. 'url:https://host/path'",
|
||||
);
|
||||
expect(() => parseLocationReference('http://blah')).toThrow(
|
||||
"Invalid location reference 'http://blah', please prefix it with 'url:', e.g. 'url:http://blah'",
|
||||
);
|
||||
expect(() => parseLocationReference('https://bleh')).toThrow(
|
||||
"Invalid location reference 'https://bleh', please prefix it with 'url:', e.g. 'url:https://bleh'",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('stringifyLocationReference', () => {
|
||||
it('works for the simple case', () => {
|
||||
expect(
|
||||
stringifyLocationReference({
|
||||
type: 'url',
|
||||
target: 'https://www.google.com',
|
||||
}),
|
||||
).toEqual('url:https://www.google.com');
|
||||
});
|
||||
|
||||
it('rejects faulty inputs', () => {
|
||||
expect(() =>
|
||||
stringifyLocationReference({ type: '', target: 'hello' }),
|
||||
).toThrow('Unable to stringify location reference, empty type');
|
||||
expect(() =>
|
||||
stringifyLocationReference({ type: 'hello', target: '' }),
|
||||
).toThrow('Unable to stringify location reference, empty target');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2021 Spotify AB
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parses a string form location reference.
|
||||
*
|
||||
* Note that the return type is not `LocationSpec`, because we do not want to
|
||||
* conflate the string form with the additional properties of that type.
|
||||
*
|
||||
* @param ref A string-form location reference, e.g. 'url:https://host'
|
||||
* @returns A location reference, e.g. { type: 'url', target: 'https://host' }
|
||||
*/
|
||||
export function parseLocationReference(
|
||||
ref: string,
|
||||
): { type: string; target: string } {
|
||||
if (typeof ref !== 'string') {
|
||||
throw new TypeError(
|
||||
`Unable to parse location reference '${ref}', unexpected argument ${typeof ref}`,
|
||||
);
|
||||
}
|
||||
|
||||
const splitIndex = ref.indexOf(':');
|
||||
if (splitIndex < 0) {
|
||||
throw new TypeError(
|
||||
`Unable to parse location reference '${ref}', expected '<type>:<target>', e.g. 'url:https://host/path'`,
|
||||
);
|
||||
}
|
||||
|
||||
const type = ref.substr(0, splitIndex).trim();
|
||||
const target = ref.substr(splitIndex + 1).trim();
|
||||
|
||||
if (!type || !target) {
|
||||
throw new TypeError(
|
||||
`Unable to parse location reference '${ref}', expected '<type>:<target>', e.g. 'url:https://host/path'`,
|
||||
);
|
||||
}
|
||||
|
||||
if (type === 'http' || type === 'https') {
|
||||
throw new TypeError(
|
||||
`Invalid location reference '${ref}', please prefix it with 'url:', e.g. 'url:${ref}'`,
|
||||
);
|
||||
}
|
||||
|
||||
return { type, target };
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a location reference into its string form.
|
||||
*
|
||||
* Note that the input type is not `LocationSpec`, because we do not want to
|
||||
* conflate the string form with the additional properties of that type.
|
||||
*
|
||||
* @param ref A location reference, e.g. { type: 'url', target: 'https://host' }
|
||||
* @returns A string-form location reference, e.g. 'url:https://host'
|
||||
*/
|
||||
export function stringifyLocationReference(ref: {
|
||||
type: string;
|
||||
target: string;
|
||||
}): string {
|
||||
const { type, target } = ref;
|
||||
|
||||
if (!type) {
|
||||
throw new TypeError(`Unable to stringify location reference, empty type`);
|
||||
} else if (!target) {
|
||||
throw new TypeError(`Unable to stringify location reference, empty target`);
|
||||
}
|
||||
|
||||
return `${type}:${target}`;
|
||||
}
|
||||
@@ -14,14 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type { Location, LocationSpec } from './types';
|
||||
export {
|
||||
locationSchema,
|
||||
locationSpecSchema,
|
||||
analyzeLocationSchema,
|
||||
} from './validation';
|
||||
export {
|
||||
LOCATION_ANNOTATION,
|
||||
ORIGIN_LOCATION_ANNOTATION,
|
||||
SOURCE_LOCATION_ANNOTATION,
|
||||
} from './annotation';
|
||||
export { parseLocationReference, stringifyLocationReference } from './helpers';
|
||||
export type { Location, LocationSpec } from './types';
|
||||
export {
|
||||
analyzeLocationSchema,
|
||||
locationSchema,
|
||||
locationSpecSchema,
|
||||
} from './validation';
|
||||
|
||||
@@ -105,7 +105,7 @@ describe('parseReferenceAnnotation', () => {
|
||||
'backstage.io/techdocs-ref',
|
||||
mockEntityWithBadAnnotation,
|
||||
);
|
||||
}).toThrow(/Failure to parse/);
|
||||
}).toThrow(/Unable to parse/);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { Git, InputError, UrlReader } from '@backstage/backend-common';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Entity, parseLocationReference } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import fs from 'fs-extra';
|
||||
import parseGitUrl from 'git-url-parse';
|
||||
@@ -36,28 +36,15 @@ export const parseReferenceAnnotation = (
|
||||
entity: Entity,
|
||||
): ParsedLocationAnnotation => {
|
||||
const annotation = entity.metadata.annotations?.[annotationName];
|
||||
|
||||
if (!annotation) {
|
||||
throw new InputError(
|
||||
`No location annotation provided in entity: ${entity.metadata.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
// split on the first colon for the protocol and the rest after the first split
|
||||
// is the location.
|
||||
const [type, target] = annotation.split(/:(.+)/) as [
|
||||
RemoteProtocol?,
|
||||
string?,
|
||||
];
|
||||
|
||||
if (!type || !target) {
|
||||
throw new InputError(
|
||||
`Failure to parse either protocol or location for entity: ${entity.metadata.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
const { type, target } = parseLocationReference(annotation);
|
||||
return {
|
||||
type,
|
||||
type: type as RemoteProtocol,
|
||||
target,
|
||||
};
|
||||
};
|
||||
@@ -77,8 +64,9 @@ export const getLocationForEntity = (
|
||||
case 'url':
|
||||
return { type, target };
|
||||
case 'dir':
|
||||
if (path.isAbsolute(target)) return { type, target };
|
||||
|
||||
if (path.isAbsolute(target)) {
|
||||
return { type, target };
|
||||
}
|
||||
return parseReferenceAnnotation(
|
||||
'backstage.io/managed-by-location',
|
||||
entity,
|
||||
|
||||
Reference in New Issue
Block a user