catalog-model: Remove deprecations
Signed-off-by: Johan Haals <johan.haals@gmail.com>
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
---
|
||||
'@backstage/catalog-model': minor
|
||||
---
|
||||
|
||||
**Breaking**: The following changes are all breaking changes.
|
||||
|
||||
Removed `EDIT_URL_ANNOTATION` and `VIEW_URL_ANNOTATION`, `LOCATION_ANNOTATION`, `ORIGIN_LOCATION_ANNOTATION`, `LOCATION_ANNOTATION`, `SOURCE_LOCATION_ANNOTATION`. All of these constants have been prefixed with ANNOTATION to be easier to find meaning `SOURCE_LOCATION_ANNOTATION` is available as `ANNOTATION_SOURCE_LOCATION`.
|
||||
|
||||
Removed `parseLocationReference`, replaced by `parseLocationRef`.
|
||||
|
||||
Removed `stringifyLocationReference`, replaced by `stringifyLocationRef`.
|
||||
|
||||
Removed `Location` type which has been moved to `catalog-client`.
|
||||
|
||||
Removed `ENTITY_DEFAULT_NAMESPACE`, replaced by `DEFAULT_NAMESPACE`.
|
||||
|
||||
Removed `compareEntityToRef` compare using `stringifyEntityRef` instead.
|
||||
|
||||
Removed `JSONSchema` type which should be imported from `json-schema` package instead.
|
||||
|
||||
Removed utility methods: `entityHasChanges`, `generateEntityEtag`, `generateEntityUid`, `generateUpdatedEntity`.
|
||||
|
||||
Removed `ENTITY_META_GENERATED_FIELDS` and `EntityRefContext`.
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
```ts
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { SerializedError } from '@backstage/errors';
|
||||
|
||||
// @alpha
|
||||
@@ -66,23 +64,6 @@ export class CommonValidatorFunctions {
|
||||
static isValidUrl(value: unknown): boolean;
|
||||
}
|
||||
|
||||
// @public @deprecated
|
||||
export function compareEntityToRef(
|
||||
entity: Entity,
|
||||
ref:
|
||||
| string
|
||||
| {
|
||||
kind?: string;
|
||||
namespace?: string;
|
||||
name: string;
|
||||
}
|
||||
| EntityName,
|
||||
context?: {
|
||||
defaultKind?: string;
|
||||
defaultNamespace?: string;
|
||||
},
|
||||
): boolean;
|
||||
|
||||
// @public
|
||||
interface ComponentEntityV1alpha1 extends Entity {
|
||||
// (undocumented)
|
||||
@@ -134,9 +115,6 @@ export { DomainEntityV1alpha1 };
|
||||
// @public
|
||||
export const domainEntityV1alpha1Validator: KindValidator;
|
||||
|
||||
// @public @deprecated
|
||||
export const EDIT_URL_ANNOTATION = 'backstage.io/edit-url';
|
||||
|
||||
// @public
|
||||
export type Entity = {
|
||||
apiVersion: string;
|
||||
@@ -146,16 +124,6 @@ export type Entity = {
|
||||
relations?: EntityRelation[];
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export const ENTITY_DEFAULT_NAMESPACE = 'default';
|
||||
|
||||
// @public @deprecated
|
||||
export const ENTITY_META_GENERATED_FIELDS: readonly [
|
||||
'uid',
|
||||
'etag',
|
||||
'generation',
|
||||
];
|
||||
|
||||
// @public
|
||||
export type EntityEnvelope = {
|
||||
apiVersion: string;
|
||||
@@ -171,9 +139,6 @@ export function entityEnvelopeSchemaValidator<
|
||||
T extends EntityEnvelope = EntityEnvelope,
|
||||
>(schema?: unknown): (data: unknown) => T;
|
||||
|
||||
// @public @deprecated
|
||||
export function entityHasChanges(previous: Entity, next: Entity): boolean;
|
||||
|
||||
// @public
|
||||
export function entityKindSchemaValidator<T extends Entity>(
|
||||
schema: unknown,
|
||||
@@ -228,12 +193,6 @@ export type EntityRef =
|
||||
name: string;
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export type EntityRefContext = {
|
||||
defaultKind?: string;
|
||||
defaultNamespace?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type EntityRelation = {
|
||||
type: string;
|
||||
@@ -269,15 +228,6 @@ export class FieldFormatEntityPolicy implements EntityPolicy {
|
||||
enforce(entity: Entity): Promise<Entity>;
|
||||
}
|
||||
|
||||
// @public @deprecated
|
||||
export function generateEntityEtag(): string;
|
||||
|
||||
// @public @deprecated
|
||||
export function generateEntityUid(): string;
|
||||
|
||||
// @public @deprecated
|
||||
export function generateUpdatedEntity(previous: Entity, next: Entity): Entity;
|
||||
|
||||
// @public
|
||||
export function getEntityName(entity: Entity): EntityName;
|
||||
|
||||
@@ -312,11 +262,6 @@ export { GroupEntityV1alpha1 };
|
||||
// @public
|
||||
export const groupEntityV1alpha1Validator: KindValidator;
|
||||
|
||||
// @public @deprecated
|
||||
export type JSONSchema = JSONSchema7 & {
|
||||
[key in string]?: JsonValue;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type KindValidator = {
|
||||
check(entity: Entity): Promise<boolean>;
|
||||
@@ -342,15 +287,6 @@ export class KubernetesValidatorFunctions {
|
||||
static isValidObjectName(value: unknown): boolean;
|
||||
}
|
||||
|
||||
// @public @deprecated
|
||||
type Location_2 = {
|
||||
id: string;
|
||||
} & LocationSpec;
|
||||
export { Location_2 as Location };
|
||||
|
||||
// @public @deprecated
|
||||
export const LOCATION_ANNOTATION = 'backstage.io/managed-by-location';
|
||||
|
||||
// @public
|
||||
interface LocationEntityV1alpha1 extends Entity {
|
||||
// (undocumented)
|
||||
@@ -388,10 +324,6 @@ export class NoForeignRootFieldsEntityPolicy implements EntityPolicy {
|
||||
enforce(entity: Entity): Promise<Entity>;
|
||||
}
|
||||
|
||||
// @public @deprecated
|
||||
export const ORIGIN_LOCATION_ANNOTATION =
|
||||
'backstage.io/managed-by-origin-location';
|
||||
|
||||
// @public @deprecated
|
||||
export function parseEntityName(
|
||||
ref:
|
||||
@@ -428,12 +360,6 @@ export function parseLocationRef(ref: string): {
|
||||
target: string;
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export function parseLocationReference(ref: string): {
|
||||
type: string;
|
||||
target: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export const RELATION_API_CONSUMED_BY = 'apiConsumedBy';
|
||||
|
||||
@@ -503,9 +429,6 @@ export class SchemaValidEntityPolicy implements EntityPolicy {
|
||||
enforce(entity: Entity): Promise<Entity>;
|
||||
}
|
||||
|
||||
// @public @deprecated
|
||||
export const SOURCE_LOCATION_ANNOTATION = 'backstage.io/source-location';
|
||||
|
||||
// @public
|
||||
export function stringifyEntityRef(
|
||||
ref:
|
||||
@@ -523,12 +446,6 @@ export function stringifyLocationRef(ref: {
|
||||
target: string;
|
||||
}): string;
|
||||
|
||||
// @public @deprecated
|
||||
export function stringifyLocationReference(ref: {
|
||||
type: string;
|
||||
target: string;
|
||||
}): string;
|
||||
|
||||
// @public
|
||||
interface SystemEntityV1alpha1 extends Entity {
|
||||
// (undocumented)
|
||||
@@ -581,7 +498,4 @@ export type Validators = {
|
||||
isValidAnnotationValue(value: unknown): boolean;
|
||||
isValidTag(value: unknown): boolean;
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export const VIEW_URL_ANNOTATION = 'backstage.io/view-url';
|
||||
```
|
||||
|
||||
@@ -14,14 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The namespace that entities without an explicit namespace fall into.
|
||||
*
|
||||
* @public
|
||||
* @deprecated use {@link DEFAULT_NAMESPACE} instead.
|
||||
*/
|
||||
export const ENTITY_DEFAULT_NAMESPACE = 'default';
|
||||
|
||||
/**
|
||||
* The namespace that entities without an explicit namespace fall into.
|
||||
*
|
||||
@@ -29,34 +21,6 @@ export const ENTITY_DEFAULT_NAMESPACE = 'default';
|
||||
*/
|
||||
export const DEFAULT_NAMESPACE = 'default';
|
||||
|
||||
/**
|
||||
* The keys of EntityMeta that are auto-generated.
|
||||
*
|
||||
* @public
|
||||
* @deprecated will be removed in a future release.
|
||||
*/
|
||||
export const ENTITY_META_GENERATED_FIELDS = [
|
||||
'uid',
|
||||
'etag',
|
||||
'generation',
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Annotation for linking to entity page from catalog pages.
|
||||
*
|
||||
* @public
|
||||
* @deprecated use {@link ANNOTATION_VIEW_URL} instead.
|
||||
*/
|
||||
export const VIEW_URL_ANNOTATION = 'backstage.io/view-url';
|
||||
|
||||
/**
|
||||
* Annotation for linking to entity edit page from catalog pages.
|
||||
*
|
||||
* @public
|
||||
* @deprecated use {@link ANNOTATION_EDIT_URL} instead.
|
||||
*/
|
||||
export const EDIT_URL_ANNOTATION = 'backstage.io/edit-url';
|
||||
|
||||
/**
|
||||
* Annotation for linking to entity page from catalog pages.
|
||||
*
|
||||
|
||||
@@ -15,11 +15,7 @@
|
||||
*/
|
||||
|
||||
export {
|
||||
EDIT_URL_ANNOTATION,
|
||||
ENTITY_DEFAULT_NAMESPACE,
|
||||
DEFAULT_NAMESPACE,
|
||||
ENTITY_META_GENERATED_FIELDS,
|
||||
VIEW_URL_ANNOTATION,
|
||||
ANNOTATION_EDIT_URL,
|
||||
ANNOTATION_VIEW_URL,
|
||||
} from './constants';
|
||||
@@ -38,16 +34,8 @@ export type {
|
||||
} from './EntityStatus';
|
||||
export * from './policies';
|
||||
export {
|
||||
compareEntityToRef,
|
||||
getEntityName,
|
||||
parseEntityName,
|
||||
parseEntityRef,
|
||||
stringifyEntityRef,
|
||||
} from './ref';
|
||||
export type { EntityRefContext } from './ref';
|
||||
export {
|
||||
entityHasChanges,
|
||||
generateEntityEtag,
|
||||
generateEntityUid,
|
||||
generateUpdatedEntity,
|
||||
} from './util';
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
|
||||
import { DEFAULT_NAMESPACE } from './constants';
|
||||
import { Entity } from './Entity';
|
||||
import { compareEntityToRef, parseEntityName, parseEntityRef } from './ref';
|
||||
import { parseEntityName, parseEntityRef } from './ref';
|
||||
|
||||
describe('ref', () => {
|
||||
describe('parseEntityName', () => {
|
||||
@@ -313,320 +312,4 @@ describe('ref', () => {
|
||||
).toThrow(/namespace/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('compareEntityToRef', () => {
|
||||
const entityWithNamespace: Entity = {
|
||||
apiVersion: 'a',
|
||||
kind: 'K',
|
||||
metadata: {
|
||||
name: 'n',
|
||||
namespace: 'ns',
|
||||
},
|
||||
};
|
||||
const entityWithoutNamespace: Entity = {
|
||||
apiVersion: 'a',
|
||||
kind: 'K',
|
||||
metadata: {
|
||||
name: 'n',
|
||||
},
|
||||
};
|
||||
|
||||
it('handles matching string refs', () => {
|
||||
expect(compareEntityToRef(entityWithNamespace, 'K:ns/n')).toBe(true);
|
||||
expect(compareEntityToRef(entityWithNamespace, 'k:nS/N')).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(entityWithNamespace, 'K:n', {
|
||||
defaultNamespace: 'ns',
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(entityWithNamespace, 'K:n', {
|
||||
defaultNamespace: 'Ns',
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(entityWithNamespace, 'ns/n', { defaultKind: 'K' }),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(entityWithNamespace, 'n', {
|
||||
defaultKind: 'K',
|
||||
defaultNamespace: 'ns',
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(entityWithNamespace, 'N', {
|
||||
defaultKind: 'k',
|
||||
defaultNamespace: 'nS',
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
expect(compareEntityToRef(entityWithoutNamespace, 'K:default/n')).toBe(
|
||||
true,
|
||||
);
|
||||
expect(compareEntityToRef(entityWithoutNamespace, 'K:deFault/n')).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
compareEntityToRef(entityWithoutNamespace, 'K:n', {
|
||||
defaultNamespace: 'default',
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(entityWithoutNamespace, 'K:n', {
|
||||
defaultNamespace: 'deFault',
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(compareEntityToRef(entityWithoutNamespace, 'K:default/n')).toBe(
|
||||
true,
|
||||
);
|
||||
expect(compareEntityToRef(entityWithoutNamespace, 'K:n')).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(entityWithoutNamespace, 'default/n', {
|
||||
defaultKind: 'K',
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(entityWithoutNamespace, 'n', {
|
||||
defaultKind: 'K',
|
||||
defaultNamespace: 'default',
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(entityWithoutNamespace, 'n', {
|
||||
defaultKind: 'K',
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('handles mismatching string refs', () => {
|
||||
expect(compareEntityToRef(entityWithNamespace, 'X:ns/n')).toBe(false);
|
||||
expect(
|
||||
compareEntityToRef(entityWithoutNamespace, 'ns/n', {
|
||||
defaultKind: 'X',
|
||||
}),
|
||||
).toBe(false);
|
||||
|
||||
expect(compareEntityToRef(entityWithNamespace, 'K:xx/n')).toBe(false);
|
||||
expect(
|
||||
compareEntityToRef(entityWithoutNamespace, 'K:n', {
|
||||
defaultNamespace: 'xx',
|
||||
}),
|
||||
).toBe(false);
|
||||
|
||||
expect(compareEntityToRef(entityWithNamespace, 'K:ns/x')).toBe(false);
|
||||
expect(
|
||||
compareEntityToRef(entityWithoutNamespace, 'x', {
|
||||
defaultKind: 'K',
|
||||
defaultNamespace: 'ns',
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('handles matching compound refs', () => {
|
||||
expect(
|
||||
compareEntityToRef(entityWithNamespace, {
|
||||
kind: 'K',
|
||||
namespace: 'ns',
|
||||
name: 'n',
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(entityWithNamespace, {
|
||||
kind: 'k',
|
||||
namespace: 'Ns',
|
||||
name: 'N',
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(
|
||||
entityWithNamespace,
|
||||
{ kind: 'K', name: 'n' },
|
||||
{
|
||||
defaultNamespace: 'ns',
|
||||
},
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(
|
||||
entityWithNamespace,
|
||||
{ namespace: 'ns', name: 'n' },
|
||||
{ defaultKind: 'K' },
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(entityWithNamespace, 'n', {
|
||||
defaultKind: 'K',
|
||||
defaultNamespace: 'ns',
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(entityWithNamespace, 'N', {
|
||||
defaultKind: 'k',
|
||||
defaultNamespace: 'nS',
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
compareEntityToRef(entityWithoutNamespace, {
|
||||
kind: 'K',
|
||||
namespace: 'default',
|
||||
name: 'n',
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(entityWithoutNamespace, {
|
||||
kind: 'k',
|
||||
namespace: 'deFault',
|
||||
name: 'N',
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(
|
||||
entityWithoutNamespace,
|
||||
{ kind: 'K', name: 'n' },
|
||||
{
|
||||
defaultNamespace: 'default',
|
||||
},
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(entityWithoutNamespace, { kind: 'K', name: 'n' }),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(
|
||||
entityWithoutNamespace,
|
||||
{ namespace: 'default', name: 'n' },
|
||||
{
|
||||
defaultKind: 'K',
|
||||
},
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(
|
||||
entityWithoutNamespace,
|
||||
{ name: 'n' },
|
||||
{
|
||||
defaultKind: 'K',
|
||||
defaultNamespace: 'default',
|
||||
},
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(
|
||||
entityWithoutNamespace,
|
||||
{ name: 'N' },
|
||||
{
|
||||
defaultKind: 'k',
|
||||
defaultNamespace: 'defAult',
|
||||
},
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
compareEntityToRef(
|
||||
entityWithoutNamespace,
|
||||
{ name: 'n' },
|
||||
{
|
||||
defaultKind: 'K',
|
||||
},
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('handles mismatching compound refs', () => {
|
||||
expect(
|
||||
compareEntityToRef(entityWithNamespace, {
|
||||
kind: 'X',
|
||||
namespace: 'ns',
|
||||
name: 'n',
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
compareEntityToRef(
|
||||
entityWithNamespace,
|
||||
{
|
||||
namespace: 'ns',
|
||||
name: 'n',
|
||||
},
|
||||
{ defaultKind: 'X' },
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
compareEntityToRef(entityWithoutNamespace, {
|
||||
kind: 'X',
|
||||
namespace: 'default',
|
||||
name: 'n',
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
compareEntityToRef(
|
||||
entityWithoutNamespace,
|
||||
{
|
||||
namespace: 'default',
|
||||
name: 'n',
|
||||
},
|
||||
{ defaultKind: 'X' },
|
||||
),
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
compareEntityToRef(entityWithNamespace, {
|
||||
kind: 'K',
|
||||
namespace: 'xx',
|
||||
name: 'n',
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
compareEntityToRef(
|
||||
entityWithNamespace,
|
||||
{
|
||||
kind: 'K',
|
||||
name: 'n',
|
||||
},
|
||||
{ defaultNamespace: 'xx' },
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
compareEntityToRef(entityWithoutNamespace, {
|
||||
kind: 'K',
|
||||
namespace: 'xx',
|
||||
name: 'n',
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
compareEntityToRef(
|
||||
entityWithoutNamespace,
|
||||
{
|
||||
kind: 'K',
|
||||
name: 'n',
|
||||
},
|
||||
{ defaultNamespace: 'xx' },
|
||||
),
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
compareEntityToRef(entityWithNamespace, {
|
||||
kind: 'K',
|
||||
namespace: 'ns',
|
||||
name: 'x',
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
compareEntityToRef(entityWithoutNamespace, {
|
||||
kind: 'K',
|
||||
namespace: 'default',
|
||||
name: 'x',
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
compareEntityToRef(
|
||||
entityWithoutNamespace,
|
||||
{
|
||||
kind: 'K',
|
||||
name: 'x',
|
||||
},
|
||||
{ defaultNamespace: 'default' },
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -53,19 +53,6 @@ export function getEntityName(entity: Entity): EntityName {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The context of defaults that entity reference parsing happens within.
|
||||
*
|
||||
* @public
|
||||
* @deprecated type inlined, will be removed in a future release.
|
||||
*/
|
||||
export type EntityRefContext = {
|
||||
/** The default kind, if none is given in the reference */
|
||||
defaultKind?: string;
|
||||
/** The default namespace, if none is given in the reference */
|
||||
defaultNamespace?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses an entity reference, either on string or compound form, and always
|
||||
* returns a complete entity name including kind, namespace and name.
|
||||
@@ -203,67 +190,3 @@ export function stringifyEntityRef(
|
||||
'en-US',
|
||||
)}/${name.toLocaleLowerCase('en-US')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares an entity to either a string reference or a compound reference.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The comparison is case insensitive, and all of kind, namespace, and name
|
||||
* must match (after applying the optional context to the ref).
|
||||
*
|
||||
* @public
|
||||
* @param entity - The entity to match
|
||||
* @param ref - A string or compound entity ref
|
||||
* @param context - An optional context of default kind and namespace, that apply
|
||||
* to the ref if given
|
||||
* @returns True if matching, false otherwise
|
||||
* @deprecated compare using stringifyEntityRef instead.
|
||||
*/
|
||||
export function compareEntityToRef(
|
||||
entity: Entity,
|
||||
ref:
|
||||
| string
|
||||
| { kind?: string; namespace?: string; name: string }
|
||||
| EntityName,
|
||||
context?: {
|
||||
/** The default kind, if none is given in the reference */
|
||||
defaultKind?: string;
|
||||
/** The default namespace, if none is given in the reference */
|
||||
defaultNamespace?: string;
|
||||
},
|
||||
): boolean {
|
||||
const entityKind = entity.kind;
|
||||
const entityNamespace = entity.metadata.namespace || DEFAULT_NAMESPACE;
|
||||
const entityName = entity.metadata.name;
|
||||
|
||||
let refKind: string | undefined;
|
||||
let refNamespace: string | undefined;
|
||||
let refName: string;
|
||||
if (typeof ref === 'string') {
|
||||
const parsed = parseRefString(ref);
|
||||
refKind = parsed.kind || context?.defaultKind;
|
||||
refNamespace =
|
||||
parsed.namespace || context?.defaultNamespace || DEFAULT_NAMESPACE;
|
||||
refName = parsed.name;
|
||||
} else {
|
||||
refKind = ref.kind || context?.defaultKind;
|
||||
refNamespace =
|
||||
ref.namespace || context?.defaultNamespace || DEFAULT_NAMESPACE;
|
||||
refName = ref.name;
|
||||
}
|
||||
|
||||
if (!refKind || !refNamespace) {
|
||||
throw new Error(
|
||||
`Entity reference or context did not contain kind and namespace`,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
entityKind.toLocaleLowerCase('en-US') ===
|
||||
refKind.toLocaleLowerCase('en-US') &&
|
||||
entityNamespace.toLocaleLowerCase('en-US') ===
|
||||
refNamespace.toLocaleLowerCase('en-US') &&
|
||||
entityName.toLocaleLowerCase('en-US') === refName.toLocaleLowerCase('en-US')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
/*
|
||||
* 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 lodash from 'lodash';
|
||||
import {
|
||||
generateEntityEtag,
|
||||
generateEntityUid,
|
||||
entityHasChanges,
|
||||
generateUpdatedEntity,
|
||||
} from './util';
|
||||
import { Entity } from './Entity';
|
||||
|
||||
describe('util', () => {
|
||||
describe('generateEntityUid', () => {
|
||||
it('generates randomness', () => {
|
||||
expect(generateEntityUid()).not.toEqual('');
|
||||
expect(generateEntityUid()).not.toEqual(generateEntityUid());
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateEntityEtag', () => {
|
||||
it('generates randomness', () => {
|
||||
expect(generateEntityEtag()).not.toEqual('');
|
||||
expect(generateEntityEtag()).not.toEqual(generateEntityEtag());
|
||||
});
|
||||
});
|
||||
|
||||
describe('entityHasChanges', () => {
|
||||
let a: Entity;
|
||||
beforeEach(() => {
|
||||
a = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'name',
|
||||
custom: 'custom',
|
||||
labels: {
|
||||
labelKey: 'labelValue',
|
||||
},
|
||||
annotations: {
|
||||
annotationKey: 'annotationValue',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
a: 'a',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('happy path: clone has no changes', () => {
|
||||
const b = lodash.cloneDeep(a);
|
||||
expect(entityHasChanges(a, b)).toBe(false);
|
||||
});
|
||||
|
||||
it('detects root field changes', () => {
|
||||
let b: any = lodash.cloneDeep(a);
|
||||
b.apiVersion += 'a';
|
||||
expect(entityHasChanges(a, b)).toBe(true);
|
||||
b = lodash.cloneDeep(a);
|
||||
delete b.apiVersion;
|
||||
expect(entityHasChanges(a, b)).toBe(true);
|
||||
b = lodash.cloneDeep(a);
|
||||
b.kind += 'a';
|
||||
expect(entityHasChanges(a, b)).toBe(true);
|
||||
b = lodash.cloneDeep(a);
|
||||
delete b.kind;
|
||||
expect(entityHasChanges(a, b)).toBe(true);
|
||||
});
|
||||
|
||||
it('detects metadata changes', () => {
|
||||
let b: any = lodash.cloneDeep(a);
|
||||
b.metadata.name += 'a';
|
||||
expect(entityHasChanges(a, b)).toBe(true);
|
||||
b = lodash.cloneDeep(a);
|
||||
delete b.metadata.custom;
|
||||
expect(entityHasChanges(a, b)).toBe(true);
|
||||
b = lodash.cloneDeep(a);
|
||||
delete b.metadata.custom;
|
||||
expect(entityHasChanges(a, b)).toBe(true);
|
||||
b = lodash.cloneDeep(a);
|
||||
b.metadata.labels.n = 'n';
|
||||
expect(entityHasChanges(a, b)).toBe(true);
|
||||
b = lodash.cloneDeep(a);
|
||||
b.metadata.labels.labelKey += 'a';
|
||||
expect(entityHasChanges(a, b)).toBe(true);
|
||||
b = lodash.cloneDeep(a);
|
||||
b.metadata.annotations.annotationKey += 'a';
|
||||
expect(entityHasChanges(a, b)).toBe(true);
|
||||
b = lodash.cloneDeep(a);
|
||||
delete b.metadata.annotations.annotationKey;
|
||||
expect(entityHasChanges(a, b)).toBe(true);
|
||||
});
|
||||
|
||||
it('detects spec changes', () => {
|
||||
let b: any = lodash.cloneDeep(a);
|
||||
b.spec.a += 'a';
|
||||
expect(entityHasChanges(a, b)).toBe(true);
|
||||
b = lodash.cloneDeep(a);
|
||||
delete b.spec.a;
|
||||
expect(entityHasChanges(a, b)).toBe(true);
|
||||
b = lodash.cloneDeep(a);
|
||||
b.spec.n = 'n';
|
||||
expect(entityHasChanges(a, b)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateUpdatedEntity', () => {
|
||||
let a: Entity;
|
||||
let b: any;
|
||||
beforeEach(() => {
|
||||
a = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
uid: 'da921f56-f655-4e6e-9b8b-bb19a57818d8',
|
||||
etag: 'NzY5NDA5NzQtYmEwNC00MDY0LWFiYmItNTYxYzQxM2JhZDcx',
|
||||
generation: 2,
|
||||
name: 'name',
|
||||
custom: 'custom',
|
||||
labels: {
|
||||
labelKey: 'labelValue',
|
||||
},
|
||||
annotations: {
|
||||
annotationKey: 'annotationValue',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
a: 'a',
|
||||
},
|
||||
};
|
||||
b = lodash.cloneDeep(a);
|
||||
delete b.metadata.uid;
|
||||
delete b.metadata.etag;
|
||||
delete b.metadata.generation;
|
||||
});
|
||||
|
||||
it('happy path: running on itself leaves it unchanged', () => {
|
||||
const result = generateUpdatedEntity(a, b);
|
||||
expect(result).toEqual(a);
|
||||
});
|
||||
|
||||
it('bumps etag and generation when spec is changed', () => {
|
||||
b.spec.a += 'a';
|
||||
const result = generateUpdatedEntity(a, b);
|
||||
expect(result.metadata.uid).toEqual(a.metadata.uid);
|
||||
expect(result.metadata.etag).not.toEqual(a.metadata.etag);
|
||||
expect(result.metadata.generation).toEqual(a.metadata.generation! + 1);
|
||||
expect(result.spec).toEqual({ a: 'aa' });
|
||||
});
|
||||
|
||||
it('bumps only etag when other things than spec are changed', () => {
|
||||
b.metadata.n = 'n';
|
||||
const result = generateUpdatedEntity(a, b);
|
||||
expect(result.metadata.uid).toEqual(a.metadata.uid);
|
||||
expect(result.metadata.etag).not.toEqual(a.metadata.etag);
|
||||
expect(result.metadata.generation).toEqual(a.metadata.generation);
|
||||
expect(result.metadata.n).toEqual('n');
|
||||
});
|
||||
|
||||
it('retains new annotations', () => {
|
||||
b.metadata.annotations.annotationKey = 'changedValue';
|
||||
b.metadata.annotations.newKey = 'newValue';
|
||||
const result = generateUpdatedEntity(a, b);
|
||||
expect(result.metadata.uid).toEqual(a.metadata.uid);
|
||||
expect(result.metadata.etag).not.toEqual(a.metadata.etag);
|
||||
expect(result.metadata.generation).toEqual(a.metadata.generation);
|
||||
expect(result.metadata.annotations).toEqual({
|
||||
annotationKey: 'changedValue',
|
||||
newKey: 'newValue',
|
||||
});
|
||||
});
|
||||
|
||||
it('retains old annotations', () => {
|
||||
b.metadata.annotations.newKey = 'newValue';
|
||||
const result = generateUpdatedEntity(a, b);
|
||||
expect(result.metadata.uid).toEqual(a.metadata.uid);
|
||||
expect(result.metadata.etag).not.toEqual(a.metadata.etag);
|
||||
expect(result.metadata.generation).toEqual(a.metadata.generation);
|
||||
expect(result.metadata.annotations).toEqual({
|
||||
annotationKey: 'annotationValue',
|
||||
newKey: 'newValue',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* 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 lodash from 'lodash';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { Entity, AlphaEntity } from './Entity';
|
||||
|
||||
/**
|
||||
* Generates a new random UID for an entity.
|
||||
*
|
||||
* @public
|
||||
* @returns A string with enough randomness to uniquely identify an entity
|
||||
* @deprecated use `uuidv4()` instead.
|
||||
*/
|
||||
export function generateEntityUid(): string {
|
||||
return uuidv4();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new random Etag for an entity.
|
||||
*
|
||||
* @public
|
||||
* @returns A string with enough randomness to uniquely identify an entity
|
||||
* revision
|
||||
* @deprecated will be removed in a future release.
|
||||
*/
|
||||
export function generateEntityEtag(): string {
|
||||
return Buffer.from(uuidv4(), 'utf8').toString('base64').replace(/[^\w]/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether there are any significant changes going from the previous to
|
||||
* the next version of this entity.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Significance, in this case, means that we do not compare generated fields
|
||||
* such as uid, etag and generation.
|
||||
*
|
||||
* Note that this comparison does NOT take status, relations or similar into
|
||||
* account. It only compares the actual input entity data, i.e. metadata and
|
||||
* spec.
|
||||
*
|
||||
* @public
|
||||
* @param previous - The old state of the entity
|
||||
* @param next - The new state of the entity
|
||||
* @deprecated will be removed in a future release.
|
||||
*/
|
||||
export function entityHasChanges(previous: Entity, next: Entity): boolean {
|
||||
const e1 = lodash.cloneDeep(previous);
|
||||
const e2 = lodash.cloneDeep(next);
|
||||
|
||||
if (!e1.metadata.labels) {
|
||||
e1.metadata.labels = {};
|
||||
}
|
||||
if (!e2.metadata.labels) {
|
||||
e2.metadata.labels = {};
|
||||
}
|
||||
if (!e1.metadata.annotations) {
|
||||
e1.metadata.annotations = {};
|
||||
}
|
||||
if (!e2.metadata.annotations) {
|
||||
e2.metadata.annotations = {};
|
||||
}
|
||||
if (!e1.metadata.tags) {
|
||||
e1.metadata.tags = [];
|
||||
}
|
||||
if (!e2.metadata.tags) {
|
||||
e2.metadata.tags = [];
|
||||
}
|
||||
|
||||
// Remove generated fields
|
||||
delete e1.metadata.uid;
|
||||
delete e1.metadata.etag;
|
||||
delete e1.metadata.generation;
|
||||
delete e2.metadata.uid;
|
||||
delete e2.metadata.etag;
|
||||
delete e2.metadata.generation;
|
||||
|
||||
// Remove things that we explicitly do not compare
|
||||
delete e1.relations;
|
||||
delete (e1 as AlphaEntity).status;
|
||||
delete e2.relations;
|
||||
delete (e2 as AlphaEntity).status;
|
||||
|
||||
return !lodash.isEqual(e1, e2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an old revision of an entity and a new desired state, and merges
|
||||
* them into a complete new state.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The previous revision is expected to be a complete model loaded from the
|
||||
* catalog, including the uid, etag and generation fields.
|
||||
*
|
||||
* @public
|
||||
* @param previous - The old state of the entity
|
||||
* @param next - The new state of the entity
|
||||
* @returns An entity with the merged state of both
|
||||
* @deprecated will be removed in a future release.
|
||||
*/
|
||||
export function generateUpdatedEntity(previous: Entity, next: Entity): Entity {
|
||||
const { uid, etag, generation } = previous.metadata;
|
||||
if (!uid || !etag || !generation) {
|
||||
throw new Error('Previous entity must have uid, etag and generation');
|
||||
}
|
||||
|
||||
const result = lodash.cloneDeep(next);
|
||||
|
||||
// Generated fields are copied and updated
|
||||
const bumpEtag = entityHasChanges(previous, result);
|
||||
const bumpGeneration = !lodash.isEqual(previous.spec, result.spec);
|
||||
result.metadata.uid = uid;
|
||||
result.metadata.etag = bumpEtag ? generateEntityEtag() : etag;
|
||||
result.metadata.generation = bumpGeneration ? generation + 1 : generation;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -24,5 +24,5 @@ export * from './entity';
|
||||
export { EntityPolicies } from './EntityPolicies';
|
||||
export * from './kinds';
|
||||
export * from './location';
|
||||
export type { EntityName, EntityRef, JSONSchema } from './types';
|
||||
export type { EntityName, EntityRef } from './types';
|
||||
export * from './validation';
|
||||
|
||||
@@ -14,30 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constant storing location annotation.
|
||||
*
|
||||
* @public
|
||||
* @deprecated use {@link ANNOTATION_LOCATION} instead.
|
||||
* */
|
||||
export const LOCATION_ANNOTATION = 'backstage.io/managed-by-location';
|
||||
/**
|
||||
* Constant storing origin location annotation
|
||||
*
|
||||
* @public
|
||||
* @deprecated use {@link ANNOTATION_ORIGIN_LOCATION} instead.
|
||||
*/
|
||||
export const ORIGIN_LOCATION_ANNOTATION =
|
||||
'backstage.io/managed-by-origin-location';
|
||||
|
||||
/**
|
||||
* Contant storing source location annotation
|
||||
*
|
||||
* @public
|
||||
* @deprecated use {@link ANNOTATION_SOURCE_LOCATION} instead.
|
||||
* */
|
||||
export const SOURCE_LOCATION_ANNOTATION = 'backstage.io/source-location';
|
||||
|
||||
/**
|
||||
* Constant storing location annotation.
|
||||
*
|
||||
|
||||
@@ -18,18 +18,6 @@ import { ANNOTATION_SOURCE_LOCATION } from '.';
|
||||
import { Entity, stringifyEntityRef } from '../entity';
|
||||
import { ANNOTATION_LOCATION } from './annotation';
|
||||
|
||||
/**
|
||||
* Parses a string form location reference.
|
||||
*
|
||||
* @public
|
||||
* @param ref - A string-form location reference, e.g. `'url:https://host'`
|
||||
* @returns A location reference, e.g. `{ type: 'url', target: 'https://host' }`
|
||||
* @deprecated use {@link parseLocationRef} instead
|
||||
*/
|
||||
export function parseLocationReference(ref: string) {
|
||||
return parseLocationRef(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string form location reference.
|
||||
*
|
||||
@@ -72,21 +60,6 @@ export function parseLocationRef(ref: string): {
|
||||
return { type, target };
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a location reference into its string form.
|
||||
*
|
||||
* @public
|
||||
* @param ref - A location reference, e.g. `{ type: 'url', target: 'https://host' }`
|
||||
* @returns A string-form location reference, e.g. `'url:https://host'`
|
||||
* @deprecated use {@link stringifyLocationRef} instead
|
||||
*/
|
||||
export function stringifyLocationReference(ref: {
|
||||
type: string;
|
||||
target: string;
|
||||
}): string {
|
||||
return stringifyLocationRef(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a location ref into its string form.
|
||||
*
|
||||
|
||||
@@ -18,15 +18,10 @@ export {
|
||||
ANNOTATION_LOCATION,
|
||||
ANNOTATION_ORIGIN_LOCATION,
|
||||
ANNOTATION_SOURCE_LOCATION,
|
||||
LOCATION_ANNOTATION,
|
||||
ORIGIN_LOCATION_ANNOTATION,
|
||||
SOURCE_LOCATION_ANNOTATION,
|
||||
} from './annotation';
|
||||
export {
|
||||
getEntitySourceLocation,
|
||||
parseLocationRef,
|
||||
parseLocationReference,
|
||||
stringifyLocationRef,
|
||||
stringifyLocationReference,
|
||||
} from './helpers';
|
||||
export type { Location, LocationSpec } from './types';
|
||||
export type { LocationSpec } from './types';
|
||||
|
||||
@@ -31,13 +31,3 @@ export type LocationSpec = {
|
||||
target: string;
|
||||
presence?: 'optional' | 'required';
|
||||
};
|
||||
|
||||
/**
|
||||
* Entity location for a specific entity.
|
||||
*
|
||||
* @public
|
||||
* @deprecated import from {@link @backstage/catalog-client#Location} instead.
|
||||
*/
|
||||
export type Location = {
|
||||
id: string;
|
||||
} & LocationSpec;
|
||||
|
||||
@@ -14,17 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
|
||||
/**
|
||||
* JSONSchema extendable by arbitrary JSON attributes
|
||||
*
|
||||
* @public
|
||||
* @deprecated use JSONSchema7 from the json-schema package instead.
|
||||
*/
|
||||
export type JSONSchema = JSONSchema7 & { [key in string]?: JsonValue };
|
||||
|
||||
/**
|
||||
* A complete entity name, with the full kind-namespace-name triplet.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user