@roadiehq/backstage-entity-validator-test-ci
Version:
Backstage entity validator
1,606 lines (1,553 loc) • 1.54 MB
JavaScript
require('./sourcemap-register.js');module.exports =
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ 2703:
/***/ ((module) => {
"use strict";
module.exports = JSON.parse("{\"$id\":\"https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#\",\"description\":\"Meta-schema for $data reference (JSON AnySchema extension proposal)\",\"type\":\"object\",\"required\":[\"$data\"],\"properties\":{\"$data\":{\"type\":\"string\",\"anyOf\":[{\"format\":\"relative-json-pointer\"},{\"format\":\"json-pointer\"}]}},\"additionalProperties\":false}");
/***/ }),
/***/ 4777:
/***/ ((module) => {
"use strict";
module.exports = JSON.parse("{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"$id\":\"http://json-schema.org/draft-07/schema#\",\"title\":\"Core schema meta-schema\",\"definitions\":{\"schemaArray\":{\"type\":\"array\",\"minItems\":1,\"items\":{\"$ref\":\"#\"}},\"nonNegativeInteger\":{\"type\":\"integer\",\"minimum\":0},\"nonNegativeIntegerDefault0\":{\"allOf\":[{\"$ref\":\"#/definitions/nonNegativeInteger\"},{\"default\":0}]},\"simpleTypes\":{\"enum\":[\"array\",\"boolean\",\"integer\",\"null\",\"number\",\"object\",\"string\"]},\"stringArray\":{\"type\":\"array\",\"items\":{\"type\":\"string\"},\"uniqueItems\":true,\"default\":[]}},\"type\":[\"object\",\"boolean\"],\"properties\":{\"$id\":{\"type\":\"string\",\"format\":\"uri-reference\"},\"$schema\":{\"type\":\"string\",\"format\":\"uri\"},\"$ref\":{\"type\":\"string\",\"format\":\"uri-reference\"},\"$comment\":{\"type\":\"string\"},\"title\":{\"type\":\"string\"},\"description\":{\"type\":\"string\"},\"default\":true,\"readOnly\":{\"type\":\"boolean\",\"default\":false},\"examples\":{\"type\":\"array\",\"items\":true},\"multipleOf\":{\"type\":\"number\",\"exclusiveMinimum\":0},\"maximum\":{\"type\":\"number\"},\"exclusiveMaximum\":{\"type\":\"number\"},\"minimum\":{\"type\":\"number\"},\"exclusiveMinimum\":{\"type\":\"number\"},\"maxLength\":{\"$ref\":\"#/definitions/nonNegativeInteger\"},\"minLength\":{\"$ref\":\"#/definitions/nonNegativeIntegerDefault0\"},\"pattern\":{\"type\":\"string\",\"format\":\"regex\"},\"additionalItems\":{\"$ref\":\"#\"},\"items\":{\"anyOf\":[{\"$ref\":\"#\"},{\"$ref\":\"#/definitions/schemaArray\"}],\"default\":true},\"maxItems\":{\"$ref\":\"#/definitions/nonNegativeInteger\"},\"minItems\":{\"$ref\":\"#/definitions/nonNegativeIntegerDefault0\"},\"uniqueItems\":{\"type\":\"boolean\",\"default\":false},\"contains\":{\"$ref\":\"#\"},\"maxProperties\":{\"$ref\":\"#/definitions/nonNegativeInteger\"},\"minProperties\":{\"$ref\":\"#/definitions/nonNegativeIntegerDefault0\"},\"required\":{\"$ref\":\"#/definitions/stringArray\"},\"additionalProperties\":{\"$ref\":\"#\"},\"definitions\":{\"type\":\"object\",\"additionalProperties\":{\"$ref\":\"#\"},\"default\":{}},\"properties\":{\"type\":\"object\",\"additionalProperties\":{\"$ref\":\"#\"},\"default\":{}},\"patternProperties\":{\"type\":\"object\",\"additionalProperties\":{\"$ref\":\"#\"},\"propertyNames\":{\"format\":\"regex\"},\"default\":{}},\"dependencies\":{\"type\":\"object\",\"additionalProperties\":{\"anyOf\":[{\"$ref\":\"#\"},{\"$ref\":\"#/definitions/stringArray\"}]}},\"propertyNames\":{\"$ref\":\"#\"},\"const\":true,\"enum\":{\"type\":\"array\",\"items\":true,\"minItems\":1,\"uniqueItems\":true},\"type\":{\"anyOf\":[{\"$ref\":\"#/definitions/simpleTypes\"},{\"type\":\"array\",\"items\":{\"$ref\":\"#/definitions/simpleTypes\"},\"minItems\":1,\"uniqueItems\":true}]},\"format\":{\"type\":\"string\"},\"contentMediaType\":{\"type\":\"string\"},\"contentEncoding\":{\"type\":\"string\"},\"if\":{\"$ref\":\"#\"},\"then\":{\"$ref\":\"#\"},\"else\":{\"$ref\":\"#\"},\"allOf\":{\"$ref\":\"#/definitions/schemaArray\"},\"anyOf\":{\"$ref\":\"#/definitions/schemaArray\"},\"oneOf\":{\"$ref\":\"#/definitions/schemaArray\"},\"not\":{\"$ref\":\"#\"}},\"default\":true}");
/***/ }),
/***/ 7351:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
const os = __importStar(__nccwpck_require__(2087));
const utils_1 = __nccwpck_require__(5278);
/**
* Commands
*
* Command Format:
* ::name key=value,key=value::message
*
* Examples:
* ::warning::This is the message
* ::set-env name=MY_VAR::some value
*/
function issueCommand(command, properties, message) {
const cmd = new Command(command, properties, message);
process.stdout.write(cmd.toString() + os.EOL);
}
exports.issueCommand = issueCommand;
function issue(name, message = '') {
issueCommand(name, {}, message);
}
exports.issue = issue;
const CMD_STRING = '::';
class Command {
constructor(command, properties, message) {
if (!command) {
command = 'missing.command';
}
this.command = command;
this.properties = properties;
this.message = message;
}
toString() {
let cmdStr = CMD_STRING + this.command;
if (this.properties && Object.keys(this.properties).length > 0) {
cmdStr += ' ';
let first = true;
for (const key in this.properties) {
if (this.properties.hasOwnProperty(key)) {
const val = this.properties[key];
if (val) {
if (first) {
first = false;
}
else {
cmdStr += ',';
}
cmdStr += `${key}=${escapeProperty(val)}`;
}
}
}
}
cmdStr += `${CMD_STRING}${escapeData(this.message)}`;
return cmdStr;
}
}
function escapeData(s) {
return utils_1.toCommandValue(s)
.replace(/%/g, '%25')
.replace(/\r/g, '%0D')
.replace(/\n/g, '%0A');
}
function escapeProperty(s) {
return utils_1.toCommandValue(s)
.replace(/%/g, '%25')
.replace(/\r/g, '%0D')
.replace(/\n/g, '%0A')
.replace(/:/g, '%3A')
.replace(/,/g, '%2C');
}
//# sourceMappingURL=command.js.map
/***/ }),
/***/ 2186:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
const command_1 = __nccwpck_require__(7351);
const file_command_1 = __nccwpck_require__(717);
const utils_1 = __nccwpck_require__(5278);
const os = __importStar(__nccwpck_require__(2087));
const path = __importStar(__nccwpck_require__(5622));
/**
* The code to exit an action
*/
var ExitCode;
(function (ExitCode) {
/**
* A code indicating that the action was successful
*/
ExitCode[ExitCode["Success"] = 0] = "Success";
/**
* A code indicating that the action was a failure
*/
ExitCode[ExitCode["Failure"] = 1] = "Failure";
})(ExitCode = exports.ExitCode || (exports.ExitCode = {}));
//-----------------------------------------------------------------------
// Variables
//-----------------------------------------------------------------------
/**
* Sets env variable for this action and future actions in the job
* @param name the name of the variable to set
* @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function exportVariable(name, val) {
const convertedVal = utils_1.toCommandValue(val);
process.env[name] = convertedVal;
const filePath = process.env['GITHUB_ENV'] || '';
if (filePath) {
const delimiter = '_GitHubActionsFileCommandDelimeter_';
const commandValue = `${name}<<${delimiter}${os.EOL}${convertedVal}${os.EOL}${delimiter}`;
file_command_1.issueCommand('ENV', commandValue);
}
else {
command_1.issueCommand('set-env', { name }, convertedVal);
}
}
exports.exportVariable = exportVariable;
/**
* Registers a secret which will get masked from logs
* @param secret value of the secret
*/
function setSecret(secret) {
command_1.issueCommand('add-mask', {}, secret);
}
exports.setSecret = setSecret;
/**
* Prepends inputPath to the PATH (for this action and future actions)
* @param inputPath
*/
function addPath(inputPath) {
const filePath = process.env['GITHUB_PATH'] || '';
if (filePath) {
file_command_1.issueCommand('PATH', inputPath);
}
else {
command_1.issueCommand('add-path', {}, inputPath);
}
process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`;
}
exports.addPath = addPath;
/**
* Gets the value of an input. The value is also trimmed.
*
* @param name name of the input to get
* @param options optional. See InputOptions.
* @returns string
*/
function getInput(name, options) {
const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
if (options && options.required && !val) {
throw new Error(`Input required and not supplied: ${name}`);
}
return val.trim();
}
exports.getInput = getInput;
/**
* Sets the value of an output.
*
* @param name name of the output to set
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function setOutput(name, value) {
process.stdout.write(os.EOL);
command_1.issueCommand('set-output', { name }, value);
}
exports.setOutput = setOutput;
/**
* Enables or disables the echoing of commands into stdout for the rest of the step.
* Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set.
*
*/
function setCommandEcho(enabled) {
command_1.issue('echo', enabled ? 'on' : 'off');
}
exports.setCommandEcho = setCommandEcho;
//-----------------------------------------------------------------------
// Results
//-----------------------------------------------------------------------
/**
* Sets the action status to failed.
* When the action exits it will be with an exit code of 1
* @param message add error issue message
*/
function setFailed(message) {
process.exitCode = ExitCode.Failure;
error(message);
}
exports.setFailed = setFailed;
//-----------------------------------------------------------------------
// Logging Commands
//-----------------------------------------------------------------------
/**
* Gets whether Actions Step Debug is on or not
*/
function isDebug() {
return process.env['RUNNER_DEBUG'] === '1';
}
exports.isDebug = isDebug;
/**
* Writes debug message to user log
* @param message debug message
*/
function debug(message) {
command_1.issueCommand('debug', {}, message);
}
exports.debug = debug;
/**
* Adds an error issue
* @param message error issue message. Errors will be converted to string via toString()
*/
function error(message) {
command_1.issue('error', message instanceof Error ? message.toString() : message);
}
exports.error = error;
/**
* Adds an warning issue
* @param message warning issue message. Errors will be converted to string via toString()
*/
function warning(message) {
command_1.issue('warning', message instanceof Error ? message.toString() : message);
}
exports.warning = warning;
/**
* Writes info to log with console.log.
* @param message info message
*/
function info(message) {
process.stdout.write(message + os.EOL);
}
exports.info = info;
/**
* Begin an output group.
*
* Output until the next `groupEnd` will be foldable in this group
*
* @param name The name of the output group
*/
function startGroup(name) {
command_1.issue('group', name);
}
exports.startGroup = startGroup;
/**
* End an output group.
*/
function endGroup() {
command_1.issue('endgroup');
}
exports.endGroup = endGroup;
/**
* Wrap an asynchronous function call in a group.
*
* Returns the same type as the function itself.
*
* @param name The name of the group
* @param fn The function to wrap in the group
*/
function group(name, fn) {
return __awaiter(this, void 0, void 0, function* () {
startGroup(name);
let result;
try {
result = yield fn();
}
finally {
endGroup();
}
return result;
});
}
exports.group = group;
//-----------------------------------------------------------------------
// Wrapper action state
//-----------------------------------------------------------------------
/**
* Saves state for current action, the state can only be retrieved by this action's post job execution.
*
* @param name name of the state to store
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function saveState(name, value) {
command_1.issueCommand('save-state', { name }, value);
}
exports.saveState = saveState;
/**
* Gets the value of an state set by this action's main execution.
*
* @param name name of the state to get
* @returns string
*/
function getState(name) {
return process.env[`STATE_${name}`] || '';
}
exports.getState = getState;
//# sourceMappingURL=core.js.map
/***/ }),
/***/ 717:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict";
// For internal use, subject to change.
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
// We use any as a valid input type
/* eslint-disable @typescript-eslint/no-explicit-any */
const fs = __importStar(__nccwpck_require__(5747));
const os = __importStar(__nccwpck_require__(2087));
const utils_1 = __nccwpck_require__(5278);
function issueCommand(command, message) {
const filePath = process.env[`GITHUB_${command}`];
if (!filePath) {
throw new Error(`Unable to find environment variable for file command ${command}`);
}
if (!fs.existsSync(filePath)) {
throw new Error(`Missing file at path: ${filePath}`);
}
fs.appendFileSync(filePath, `${utils_1.toCommandValue(message)}${os.EOL}`, {
encoding: 'utf8'
});
}
exports.issueCommand = issueCommand;
//# sourceMappingURL=file-command.js.map
/***/ }),
/***/ 5278:
/***/ ((__unused_webpack_module, exports) => {
"use strict";
// We use any as a valid input type
/* eslint-disable @typescript-eslint/no-explicit-any */
Object.defineProperty(exports, "__esModule", ({ value: true }));
/**
* Sanitizes an input into a string so it can be passed into issueCommand safely
* @param input input to sanitize into a string
*/
function toCommandValue(input) {
if (input === null || input === undefined) {
return '';
}
else if (typeof input === 'string' || input instanceof String) {
return input;
}
return JSON.stringify(input);
}
exports.toCommandValue = toCommandValue;
//# sourceMappingURL=utils.js.map
/***/ }),
/***/ 7777:
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
"use strict";
var __webpack_unused_export__;
__webpack_unused_export__ = ({ value: true });
var lodash = __nccwpck_require__(250);
var Ajv = __nccwpck_require__(6441);
var uuid = __nccwpck_require__(9521);
var yup = __nccwpck_require__(112);
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var lodash__default = /*#__PURE__*/_interopDefaultLegacy(lodash);
var Ajv__default = /*#__PURE__*/_interopDefaultLegacy(Ajv);
var yup__namespace = /*#__PURE__*/_interopNamespace(yup);
const ENTITY_DEFAULT_NAMESPACE = "default";
const ENTITY_META_GENERATED_FIELDS = [
"uid",
"etag",
"generation"
];
const VIEW_URL_ANNOTATION = "backstage.io/view-url";
const EDIT_URL_ANNOTATION = "backstage.io/edit-url";
class DefaultNamespaceEntityPolicy {
constructor(namespace = ENTITY_DEFAULT_NAMESPACE) {
this.namespace = namespace;
}
async enforce(entity) {
if (entity.metadata.namespace) {
return entity;
}
return lodash__default["default"].merge({ metadata: { namespace: this.namespace } }, entity);
}
}
class CommonValidatorFunctions {
static isValidPrefixAndOrSuffix(value, separator, isValidPrefix, isValidSuffix) {
if (typeof value !== "string") {
return false;
}
const parts = value.split(separator);
if (parts.length === 1) {
return isValidSuffix(parts[0]);
} else if (parts.length === 2) {
return isValidPrefix(parts[0]) && isValidSuffix(parts[1]);
}
return false;
}
static isJsonSafe(value) {
try {
return lodash__default["default"].isEqual(value, JSON.parse(JSON.stringify(value)));
} catch {
return false;
}
}
static isValidDnsSubdomain(value) {
return typeof value === "string" && value.length >= 1 && value.length <= 253 && value.split(".").every(CommonValidatorFunctions.isValidDnsLabel);
}
static isValidDnsLabel(value) {
return typeof value === "string" && value.length >= 1 && value.length <= 63 && /^[a-z0-9]+(\-[a-z0-9]+)*$/.test(value);
}
static isValidTag(value) {
return typeof value === "string" && value.length >= 1 && value.length <= 63 && /^[a-z0-9+#]+(\-[a-z0-9+#]+)*$/.test(value);
}
static isValidUrl(value) {
if (typeof value !== "string") {
return false;
}
try {
new URL(value);
return true;
} catch {
return false;
}
}
static isValidString(value) {
var _a;
return typeof value === "string" && ((_a = value == null ? void 0 : value.trim()) == null ? void 0 : _a.length) >= 1;
}
}
var $schema$c = "http://json-schema.org/draft-07/schema";
var $id$c = "EntityEnvelope";
var description$c = "The envelope skeleton parts of an entity - whatever is necessary to be able to give it a ref and pass to further validation / policy checking.";
var examples$b = [
{
apiVersion: "backstage.io/v1alpha1",
kind: "Component",
metadata: {
name: "LoremService"
}
}
];
var type$3 = "object";
var required$2 = [
"apiVersion",
"kind",
"metadata"
];
var additionalProperties$2 = true;
var properties$2 = {
apiVersion: {
type: "string",
description: "The version of specification format for this particular entity that this is written against.",
minLength: 1,
examples: [
"backstage.io/v1alpha1",
"my-company.net/v1",
"1.0"
]
},
kind: {
type: "string",
description: "The high level entity type being described.",
minLength: 1,
examples: [
"API",
"Component",
"Domain",
"Group",
"Location",
"Resource",
"System",
"Template",
"User"
]
},
metadata: {
type: "object",
required: [
"name"
],
additionalProperties: true,
properties: {
name: {
type: "string",
description: "The name of the entity. Must be unique within the catalog at any given point in time, for any given namespace + kind pair.",
examples: [
"metadata-proxy"
],
minLength: 1
},
namespace: {
type: "string",
description: "The namespace that the entity belongs to.",
"default": "default",
examples: [
"default",
"admin"
],
minLength: 1
}
}
}
};
var entityEnvelopeSchema = {
$schema: $schema$c,
$id: $id$c,
description: description$c,
examples: examples$b,
type: type$3,
required: required$2,
additionalProperties: additionalProperties$2,
properties: properties$2
};
var $schema$b = "http://json-schema.org/draft-07/schema";
var $id$b = "Entity";
var description$b = "The parts of the format that's common to all versions/kinds of entity.";
var examples$a = [
{
apiVersion: "backstage.io/v1alpha1",
kind: "Component",
metadata: {
name: "LoremService",
description: "Creates Lorems like a pro.",
labels: {
product_name: "Random value Generator"
},
annotations: {
docs: "https://github.com/..../tree/develop/doc"
}
},
spec: {
type: "service",
lifecycle: "production",
owner: "tools"
}
}
];
var type$2 = "object";
var required$1 = [
"apiVersion",
"kind",
"metadata"
];
var additionalProperties$1 = false;
var properties$1 = {
apiVersion: {
type: "string",
description: "The version of specification format for this particular entity that this is written against.",
minLength: 1,
examples: [
"backstage.io/v1alpha1",
"my-company.net/v1",
"1.0"
]
},
kind: {
type: "string",
description: "The high level entity type being described.",
minLength: 1,
examples: [
"API",
"Component",
"Domain",
"Group",
"Location",
"Resource",
"System",
"Template",
"User"
]
},
metadata: {
$ref: "EntityMeta"
},
spec: {
type: "object",
description: "The specification data describing the entity itself."
},
relations: {
type: "array",
description: "The relations that this entity has with other entities.",
items: {
$ref: "common#relation"
}
},
status: {
$ref: "common#status"
}
};
var entitySchema = {
$schema: $schema$b,
$id: $id$b,
description: description$b,
examples: examples$a,
type: type$2,
required: required$1,
additionalProperties: additionalProperties$1,
properties: properties$1
};
var $schema$a = "http://json-schema.org/draft-07/schema";
var $id$a = "EntityMeta";
var description$a = "Metadata fields common to all versions/kinds of entity.";
var examples$9 = [
{
uid: "e01199ab-08cc-44c2-8e19-5c29ded82521",
etag: "lsndfkjsndfkjnsdfkjnsd==",
generation: 13,
name: "my-component-yay",
namespace: "the-namespace",
labels: {
"backstage.io/custom": "ValueStuff"
},
annotations: {
"example.com/bindings": "are-secret"
},
tags: [
"java",
"data"
]
}
];
var type$1 = "object";
var required = [
"name"
];
var additionalProperties = true;
var properties = {
uid: {
type: "string",
description: "A globally unique ID for the entity. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, but the server is free to reject requests that do so in such a way that it breaks semantics.",
examples: [
"e01199ab-08cc-44c2-8e19-5c29ded82521"
],
minLength: 1
},
etag: {
type: "string",
description: "An opaque string that changes for each update operation to any part of the entity, including metadata. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, and the server will then reject the operation if it does not match the current stored value.",
examples: [
"lsndfkjsndfkjnsdfkjnsd=="
],
minLength: 1
},
generation: {
type: "integer",
description: "A positive nonzero number that indicates the current generation of data for this entity; the value is incremented each time the spec changes. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations.",
examples: [
1
],
minimum: 1
},
name: {
type: "string",
description: "The name of the entity. Must be unique within the catalog at any given point in time, for any given namespace + kind pair.",
examples: [
"metadata-proxy"
],
minLength: 1
},
namespace: {
type: "string",
description: "The namespace that the entity belongs to.",
"default": "default",
examples: [
"default",
"admin"
],
minLength: 1
},
title: {
type: "string",
description: "A display name of the entity, to be presented in user interfaces instead of the name property, when available.",
examples: [
"React SSR Template"
],
minLength: 1
},
description: {
type: "string",
description: "A short (typically relatively few words, on one line) description of the entity."
},
labels: {
type: "object",
description: "Key/value pairs of identifying information attached to the entity.",
additionalProperties: true,
patternProperties: {
"^.+$": {
type: "string"
}
}
},
annotations: {
type: "object",
description: "Key/value pairs of non-identifying auxiliary information attached to the entity.",
additionalProperties: true,
patternProperties: {
"^.+$": {
type: "string"
}
}
},
tags: {
type: "array",
description: "A list of single-valued strings, to for example classify catalog entities in various ways.",
items: {
type: "string",
minLength: 1
}
},
links: {
type: "array",
description: "A list of external hyperlinks related to the entity. Links can provide additional contextual information that may be located outside of Backstage itself. For example, an admin dashboard or external CMS page.",
items: {
type: "object",
required: [
"url"
],
properties: {
url: {
type: "string",
description: "A url in a standard uri format.",
examples: [
"https://admin.example-org.com"
],
minLength: 1
},
title: {
type: "string",
description: "A user friendly display name for the link.",
examples: [
"Admin Dashboard"
],
minLength: 1
},
icon: {
type: "string",
description: "A key representing a visual icon to be displayed in the UI.",
examples: [
"dashboard"
],
minLength: 1
}
}
}
}
};
var entityMetaSchema = {
$schema: $schema$a,
$id: $id$a,
description: description$a,
examples: examples$9,
type: type$1,
required: required,
additionalProperties: additionalProperties,
properties: properties
};
var $schema$9 = "http://json-schema.org/draft-07/schema";
var $id$9 = "common";
var type = "object";
var description$9 = "Common definitions to import from other schemas";
var definitions = {
reference: {
$id: "#reference",
type: "object",
description: "A reference by name to another entity.",
required: [
"kind",
"namespace",
"name"
],
additionalProperties: false,
properties: {
kind: {
type: "string",
description: "The kind field of the entity.",
minLength: 1
},
namespace: {
type: "string",
description: "The metadata.namespace field of the entity.",
minLength: 1
},
name: {
type: "string",
description: "The metadata.name field of the entity.",
minLength: 1
}
}
},
relation: {
$id: "#relation",
type: "object",
description: "A directed relation from one entity to another.",
required: [
"type",
"target"
],
additionalProperties: false,
properties: {
type: {
type: "string",
minLength: 1,
pattern: "^\\w+$",
description: "The type of relation."
},
target: {
$ref: "#reference"
}
}
},
status: {
$id: "#status",
type: "object",
description: "The current status of the entity, as claimed by various sources.",
required: [
],
additionalProperties: true,
properties: {
items: {
type: "array",
items: {
$ref: "#statusItem"
}
}
}
},
statusItem: {
$id: "#statusItem",
type: "object",
description: "A specific status item on a well known format.",
required: [
"type",
"level",
"message"
],
additionalProperties: true,
properties: {
type: {
type: "string",
minLength: 1
},
level: {
$ref: "#statusLevel",
description: "The status level / severity of the status item."
},
message: {
type: "string",
description: "A brief message describing the status, intended for human consumption."
},
error: {
$ref: "#error",
description: "An optional serialized error object related to the status."
}
}
},
statusLevel: {
$id: "#statusLevel",
type: "string",
description: "A status level / severity.",
"enum": [
"info",
"warning",
"error"
]
},
error: {
$id: "#error",
type: "object",
description: "A serialized error object.",
required: [
"name",
"message"
],
additionalProperties: true,
properties: {
name: {
type: "string",
examples: [
"Error",
"InputError"
],
description: "The type name of the error",
minLength: 1
},
message: {
type: "string",
description: "The message of the error"
},
code: {
type: "string",
description: "An error code associated with the error"
},
stack: {
type: "string",
description: "An error stack trace"
}
}
}
};
var commonSchema = {
$schema: $schema$9,
$id: $id$9,
type: type,
description: description$9,
definitions: definitions
};
const compiledSchemaCache = /* @__PURE__ */ new Map();
const refDependencyCandidates = [
entityEnvelopeSchema,
entitySchema,
entityMetaSchema,
commonSchema
];
function throwAjvError(errors) {
if (!(errors == null ? void 0 : errors.length)) {
throw new TypeError("Unknown error");
}
const error = errors[0];
throw new TypeError(`${error.dataPath || "<root>"} ${error.message}${error.params ? ` - ${Object.entries(error.params).map(([key, val]) => `${key}: ${val}`).join(", ")}` : ""}`);
}
function compileAjvSchema(schema, options = {}) {
var _a;
const disableCache = (_a = options == null ? void 0 : options.disableCache) != null ? _a : false;
const cacheKey = disableCache ? "" : JSON.stringify(schema);
if (!disableCache) {
const cached = compiledSchemaCache.get(cacheKey);
if (cached) {
return cached;
}
}
const extraSchemas = getExtraSchemas(schema);
const ajv = new Ajv__default["default"]({
allowUnionTypes: true,
allErrors: true,
validateSchema: true
});
if (extraSchemas.length) {
ajv.addSchema(extraSchemas, void 0, void 0, true);
}
const compiled = ajv.compile(schema);
if (!disableCache) {
compiledSchemaCache.set(cacheKey, compiled);
}
return compiled;
}
function getExtraSchemas(schema) {
if (typeof schema !== "object") {
return [];
}
const seen = /* @__PURE__ */ new Set();
if (schema.$id) {
seen.add(schema.$id);
}
const selected = new Array();
const todo = [schema];
while (todo.length) {
const current = todo.pop();
for (const ref of getAllRefs(current)) {
if (!seen.has(ref)) {
seen.add(ref);
const match = refDependencyCandidates.find((c) => c.$id === ref);
if (match) {
selected.push(match);
todo.push(match);
}
}
}
}
return selected;
}
function* getAllRefs(schema) {
const todo = [schema];
while (todo.length) {
const current = todo.pop();
if (typeof current === "object" && current) {
for (const [key, value] of Object.entries(current)) {
if (key === "$ref" && typeof value === "string") {
yield value.split("#")[0];
} else {
todo.push(value);
}
}
}
}
}
function entityEnvelopeSchemaValidator(schema) {
const validate = compileAjvSchema(schema ? schema : entityEnvelopeSchema);
return (data) => {
const result = validate(data);
if (result === true) {
return data;
}
throw throwAjvError(validate.errors);
};
}
function entityKindSchemaValidator(schema) {
const validate = compileAjvSchema(schema);
return (data) => {
var _a;
const result = validate(data);
if (result === true) {
return data;
}
const softCandidates = (_a = validate.errors) == null ? void 0 : _a.filter((e) => ["/kind", "/apiVersion"].includes(e.dataPath));
if ((softCandidates == null ? void 0 : softCandidates.length) && softCandidates.every((e) => e.keyword === "enum")) {
return false;
}
throw throwAjvError(validate.errors);
};
}
function entitySchemaValidator(schema) {
const validate = compileAjvSchema(schema ? schema : entitySchema);
return (data) => {
const result = validate(data);
if (result === true) {
return data;
}
throw throwAjvError(validate.errors);
};
}
class KubernetesValidatorFunctions {
static isValidApiVersion(value) {
return CommonValidatorFunctions.isValidPrefixAndOrSuffix(value, "/", CommonValidatorFunctions.isValidDnsSubdomain, (n) => n.length >= 1 && n.length <= 63 && /^[a-z0-9A-Z]+$/.test(n));
}
static isValidKind(value) {
return typeof value === "string" && value.length >= 1 && value.length <= 63 && /^[a-zA-Z][a-z0-9A-Z]*$/.test(value);
}
static isValidObjectName(value) {
return typeof value === "string" && value.length >= 1 && value.length <= 63 && /^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$/.test(value);
}
static isValidNamespace(value) {
return CommonValidatorFunctions.isValidDnsLabel(value);
}
static isValidLabelKey(value) {
return CommonValidatorFunctions.isValidPrefixAndOrSuffix(value, "/", CommonValidatorFunctions.isValidDnsSubdomain, KubernetesValidatorFunctions.isValidObjectName);
}
static isValidLabelValue(value) {
return value === "" || KubernetesValidatorFunctions.isValidObjectName(value);
}
static isValidAnnotationKey(value) {
return CommonValidatorFunctions.isValidPrefixAndOrSuffix(value, "/", CommonValidatorFunctions.isValidDnsSubdomain, KubernetesValidatorFunctions.isValidObjectName);
}
static isValidAnnotationValue(value) {
return typeof value === "string";
}
}
const defaultValidators = {
isValidApiVersion: KubernetesValidatorFunctions.isValidApiVersion,
isValidKind: KubernetesValidatorFunctions.isValidKind,
isValidEntityName: KubernetesValidatorFunctions.isValidObjectName,
isValidNamespace: KubernetesValidatorFunctions.isValidNamespace,
isValidLabelKey: KubernetesValidatorFunctions.isValidLabelKey,
isValidLabelValue: KubernetesValidatorFunctions.isValidLabelValue,
isValidAnnotationKey: KubernetesValidatorFunctions.isValidAnnotationKey,
isValidAnnotationValue: KubernetesValidatorFunctions.isValidAnnotationValue,
isValidTag: CommonValidatorFunctions.isValidTag
};
function makeValidator(overrides = {}) {
return {
...defaultValidators,
...overrides
};
}
class FieldFormatEntityPolicy {
constructor(validators = makeValidator()) {
this.validators = validators;
}
async enforce(entity) {
var _a, _b, _c, _d, _e, _f, _g;
function require(field, value, validator) {
if (value === void 0 || value === null) {
throw new Error(`${field} must have a value`);
}
let isValid;
try {
isValid = validator(value);
} catch (e) {
throw new Error(`${field} could not be validated, ${e}`);
}
if (!isValid) {
let expectation;
switch (validator.name) {
case "isValidLabelValue":
case "isValidObjectName":
expectation = "a string that is sequences of [a-zA-Z0-9] separated by any of [-_.], at most 63 characters in total";
break;
case "isValidLabelKey":
case "isValidApiVersion":
case "isValidAnnotationKey":
expectation = "a valid prefix and/or suffix";
break;
case "isValidNamespace":
case "isValidDnsLabel":
expectation = "a string that is sequences of [a-z0-9] separated by [-], at most 63 characters in total";
break;
case "isValidTag":
expectation = "a string that is sequences of [a-z0-9+#] separated by [-], at most 63 characters in total";
break;
case "isValidAnnotationValue":
expectation = "a string";
break;
case "isValidKind":
expectation = "a string that is a sequence of [a-zA-Z][a-z0-9A-Z], at most 63 characters in total";
break;
case "isValidUrl":
expectation = "a string that is a valid url";
break;
case "isValidString":
expectation = "a non empty string";
break;
default:
expectation = void 0;
break;
}
const message = expectation ? ` expected ${expectation} but found "${value}".` : "";
throw new Error(`"${field}" is not valid;${message} To learn more about catalog file format, visit: https://github.com/backstage/backstage/blob/master/docs/architecture-decisions/adr002-default-catalog-file-format.md`);
}
}
function optional(field, value, validator) {
return value === void 0 || require(field, value, validator);
}
require("apiVersion", entity.apiVersion, this.validators.isValidApiVersion);
require("kind", entity.kind, this.validators.isValidKind);
require("metadata.name", entity.metadata.name, this.validators.isValidEntityName);
optional("metadata.namespace", entity.metadata.namespace, this.validators.isValidNamespace);
for (const [k, v] of Object.entries((_a = entity.metadata.labels) != null ? _a : [])) {
require(`labels.${k}`, k, this.validators.isValidLabelKey);
require(`labels.${k}`, v, this.validators.isValidLabelValue);
}
for (const [k, v] of Object.entries((_b = entity.metadata.annotations) != null ? _b : [])) {
require(`annotations.${k}`, k, this.validators.isValidAnnotationKey);
require(`annotations.${k}`, v, this.validators.isValidAnnotationValue);
}
const tags = (_c = entity.metadata.tags) != null ? _c : [];
for (let i = 0; i < tags.length; ++i) {
require(`tags.${i}`, tags[i], this.validators.isValidTag);
}
const links = (_d = entity.metadata.links) != null ? _d : [];
for (let i = 0; i < links.length; ++i) {
require(`links.${i}.url`, (_e = links[i]) == null ? void 0 : _e.url, CommonValidatorFunctions.isValidUrl);
optional(`links.${i}.title`, (_f = links[i]) == null ? void 0 : _f.title, CommonValidatorFunctions.isValidString);
optional(`links.${i}.icon`, (_g = links[i]) == null ? void 0 : _g.icon, KubernetesValidatorFunctions.isValidObjectName);
}
return entity;
}
}
const defaultKnownFields = ["apiVersion", "kind", "metadata", "spec"];
class NoForeignRootFieldsEntityPolicy {
constructor(knownFields = defaultKnownFields) {
this.knownFields = knownFields;
}
async enforce(entity) {
for (const field of Object.keys(entity)) {
if (!this.knownFields.includes(field)) {
throw new Error(`Unknown field ${field}`);
}
}
return entity;
}
}
class SchemaValidEntityPolicy {
async enforce(entity) {
if (!this.validate) {
const ajv = new Ajv__default["default"]({ allowUnionTypes: true });
this.validate = ajv.addSchema([commonSchema, entityMetaSchema], void 0, void 0, true).compile(entitySchema);
}
const result = this.validate(entity);
if (result === true) {
return entity;
}
const [error] = this.validate.errors || [];
if (!error) {
throw new Error(`Malformed envelope, Unknown error`);
}
throw new Error(`Malformed envelope, ${error.dataPath || "<root>"} ${error.message}`);
}
}
function parseRefString(ref) {
var _a, _b;
const match = /^([^:/]+:)?([^:/]+\/)?([^:/]+)$/.exec(ref.trim());
if (!match) {
throw new TypeError(`Entity reference "${ref}" was not on the form [<kind>:][<namespace>/]<name>`);
}
return {
kind: (_a = match[1]) == null ? void 0 : _a.slice(0, -1),
namespace: (_b = match[2]) == null ? void 0 : _b.slice(0, -1),
name: match[3]
};
}
function getEntityName(entity) {
return {
kind: entity.kind,
namespace: entity.metadata.namespace || ENTITY_DEFAULT_NAMESPACE,
name: entity.metadata.name
};
}
function parseEntityName(ref, context = {}) {
const { kind, namespace, name } = parseEntityRef(ref, {
defaultNamespace: ENTITY_DEFAULT_NAMESPACE,
...context
});
if (!kind) {
throw new Error(`Entity reference ${namespace}/${name} did not contain a kind`);
}
return { kind, namespace, name };
}
function parseEntityRef(ref, context = {}) {
var _a, _b;
if (!ref) {
throw new Error(`Entity reference must not be empty`);
}
if (typeof ref === "string") {
const parsed = parseRefString(ref);
return {
kind: (_a = parsed.kind) != null ? _a : context.defaultKind,
namespace: (_b = parsed.namespace) != null ? _b : context.defaultNamespace,
name: parsed.name
};
}
const { kind, namespace, name } = ref;
if (kind === "") {
throw new Error("Entity reference kinds must not be empty");
} else if (namespace === "") {
throw new Error("Entity reference namespaces must not be empty");
} else if (!name) {
throw new Error("Entity references must contain a name");
}
return {
kind: kind != null ? kind : context.defaultKind,
namespace: namespace != null ? namespace : context.defaultNamespace,
name
};
}
function serializeEntityRef(ref) {
let kind;
let namespace;
let name;
if ("metadata" in ref) {
kind = ref.kind;
namespace = ref.metadata.namespace;
name = ref.metadata.name;
} else {
kind = ref.kind;
namespace = ref.namespace;
name = ref.name;
}
if ((kind == null ? void 0 : kind.includes(":")) || (kind == null ? void 0 : kind.includes("/")) || (namespace == null ? void 0 : namespace.includes(":")) || (namespace == null ? void 0 : namespace.includes("/")) || name.includes(":") || name.includes("/")) {
return { kind, namespace, name };
}
return `${kind ? `${kind}:` : ""}${namespace ? `${namespace}/` : ""}${name}`;
}
function stringifyEntityRef(ref) {
var _a, _b;
let kind;
let namespace;
let name;
if ("metadata" in ref) {
kind = ref.kind;
namespace = (_a = ref.metadata.namespace) != null ? _a : ENTITY_DEFAULT_NAMESPACE;
name = ref.metadata.name;
} else {
kind = ref.kind;
namespace = (_b = ref.namespace) != null ? _b : ENTITY_DEFAULT_NAMESPACE;
name = ref.name;
}
return `${kind.toLocaleLowerCase("en-US")}:${namespace.toLocaleLowerCase("en-US")}/${name.toLocaleLowerCase("en-US")}`;
}
function compareEntityToRef(entity, ref, context) {
const entityKind = entity.kind;
const entityNamespace = entity.metadata.namespace || ENTITY_DEFAULT_NAMESPACE;
const entityName = entity.metadata.name;
let refKind;
let refNamespace;
let refName;
if (typeof ref === "string") {
const parsed = parseRefString(ref);
refKind = parsed.kind || (context == null ? void 0 : context.defaultKind);
refNamespace = parsed.namespace || (context == null ? void 0 : context.defaultNamespace) || ENTITY_DEFAULT_NAMESPACE;
refName = parsed.name;
} else {
refKind = ref.kind || (context == null ? void 0 : context.defaultKind);
refNamespace = ref.namespace || (context == null ? void 0 : context.defaultNamespace) || ENTITY_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");
}
function generateEntityUid() {
return uuid.v4();
}
function generateEntityEtag() {
return Buffer.from(uuid.v4(), "utf8").toString("base64").replace(/[^\w]/g, "");
}
function entityHasChanges(previous, next) {
const e1 = lodash__default["default"].cloneDeep(previous);
const e2 = lodash__default["default"].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 = [];
}
delete e1.metadata.uid;
delete e1.metadata.etag;
delete e1.metadata.generation;
delete e2.metadata.uid;
delete e2.metadata.etag;
delete e2.metadata.generation;
delete e1.relations;
delete e1.status;
delete e2.relations;
delete e2.status;
return !lodash__default["default"].isEqual(e1, e2);
}
function generateUpdatedEntity(previous, next) {
const { uid, etag, generation } = previous.metadata;
if (!uid || !etag || !generation) {
throw new Error("Previous entity must have uid, etag and generation");
}
const result = lodash__default["default"].cloneDeep(next);
const bumpEtag = entityHasChanges(previous, result);
const bumpGeneration = !lodash__default["default"].isEqual(previous.spec, result.spec);
result.metadata.uid = uid;
result.metadata.etag = bumpEtag ? generateEntityEtag() : etag;
result.metadata.generation = bumpGeneration ? generation + 1 : generation;
return result;
}
class AllEntityPolicies {
constructor(policies) {
this.policies = policies;
}
async enforce(entity) {
let result = entity;
for (const policy of this.policies) {
const output = await policy.enforce(result);
if (!output) {
throw new Error(`Policy ${policy.constructor.name} did not return a result`);
}
result = output;
}
return result;
}
}
class AnyEntityPolicy {
constructor(policies) {
this.policies = policies;
}
async enforce(entity) {
for (const policy of this.policies) {
const output = await policy.enforce(entity);
if (output) {
return output;
}
}
throw new Error(`The entity did not match any known policy`);
}
}
const EntityPolicies = {
allOf(policies) {
return new AllEntityPolicies(policies);
},
oneOf(policies) {
return new AnyEntityPolicy(policies);
}
};
var $schema$8 = "http://json-schema.org/draft-07/schema";
var $id$8 = "ApiV1alpha1";
var description$8 = "An API describes an interface that can be exposed by a component. The API can be defined in different formats, like OpenAPI, AsyncAPI, GraphQL, gRPC, or other formats.";
var examples$8 = [
{
apiVersion: "backstage.io/v1alpha1",
kind: "API",
metadata: {
name: "artist-api",
description: "Retrieve artist details",
labels: {
product_name: "Random value Generator"
},
annotations: {
docs: "https://github.com/..../tree/develop/doc"
}
},
spec: {
type: "openapi",
lifecycle: "production",
owner: "artist-relations-team",
system: "artist-engagement-portal",
definition: "openapi: \"3.0.0\"\ninfo:..."
}
}
];
var allOf$8 = [
{
$ref: "Entity"
},
{
type: "object",
required: [
"spec"
],
properties: {
apiVersion: {
"enum": [
"backstage.io/v1alpha1",
"backstage.io/v1beta1"
]
},
kind: {
"enum": [
"API"
]
},
spec: {
type: "object",
required: [
"type",
"lifecycle",
"owner",
"definition"
],
properties: {
type: {
type: "string",
description: "The type of the API definition.",
examples: [
"openapi",
"asyncapi",
"graphql",
"grpc"
],
minLength: 1
},
lifecycle: {
type: "string",
description: "The lifecycle state of the API.",
examples: [
"experimental",
"production",
"deprecated"
],
minLength: 1
},
owner: {
type: "string",
description: "An entity reference to the owner of the API.",
examples: [
"artist-relations-team",
"user:john.johnson"
],
minLength: 1
},
system: {
type: "string",
description: "An entity reference to the system that the API belongs to.",
minLength: 1
},
definition: {
type: "string",
description: "The definition of the API, based on the format defined by the type.",
minLength: 1
}
}
}
}
}
];
var schema$8 = {
$schema: $schema$8,
$id: $id$8,
description: description$8,
examples: examples$8,
allOf: allOf$8
};
function ajvCompiledJsonSchemaValidator(schema) {
const validator = entityKindSchemaValidator(schema);
return {
async check(data) {
return validator(data) === data;
}
};
}
const apiEntityV1alpha1Validator = ajvCompiledJsonSchemaValidator(schema$8);
var $schem