@enginuity-io/dora-metrics-backend
Version:
DORA Metrics backend plugin for Backstage
392 lines (383 loc) • 12.2 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var knex = require('knex');
var express = require('express');
var cors = require('cors');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var knex__default = /*#__PURE__*/_interopDefaultLegacy(knex);
var cors__default = /*#__PURE__*/_interopDefaultLegacy(cors);
var __defProp$1 = Object.defineProperty;
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$1 = (obj, key, value) => {
__defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
class DoraMetricsService {
constructor(database, logger) {
this.database = database;
this.logger = logger;
// Store metadata for summary endpoint
__publicField$1(this, "deploymentFrequencyMetadata");
__publicField$1(this, "leadTimeMetadata");
__publicField$1(this, "mttrMetadata");
__publicField$1(this, "failureRateMetadata");
this.deploymentFrequencyMetadata = {
score: "Elite",
value: "Multiple deploys per day",
trend: "improving"
};
this.leadTimeMetadata = {
score: "Elite",
value: "< 1 day",
trend: "stable"
};
this.mttrMetadata = {
score: "Elite",
value: "< 1 hour",
trend: "improving"
};
this.failureRateMetadata = {
score: "High",
value: "0-15%",
trend: "stable"
};
}
/**
* Get a list of all projects available in DevLake
*/
async getProjects() {
try {
this.logger.info("Fetching projects from DevLake");
return [
"elite-project-cicd",
"high-project-cicd",
"medium-project-cicd",
"low-project-cicd"
];
} catch (error) {
this.logger.error("Error fetching projects:", error);
throw error;
}
}
/**
* Get deployment frequency data for a project
*/
async getDeploymentFrequency(projectKey, timeRange = "30d") {
try {
this.logger.info(`Fetching deployment frequency for project ${projectKey} with timeRange ${timeRange}`);
const today = /* @__PURE__ */ new Date();
const data = Array.from({ length: 30 }, (_, i) => {
const date = new Date(today);
date.setDate(date.getDate() - (29 - i));
return {
period: date.toISOString().split("T")[0],
deploymentCount: Math.floor(Math.random() * 5) + 1
// 1-6 deploys per day
};
});
this.deploymentFrequencyMetadata = {
score: "Elite",
value: "Multiple deploys per day",
trend: "improving"
};
return data;
} catch (error) {
this.logger.error(`Error fetching deployment frequency for project ${projectKey}:`, error);
throw error;
}
}
/**
* Get lead time for changes data for a project
*/
async getLeadTimeForChanges(projectKey, timeRange = "30d") {
try {
this.logger.info(`Fetching lead time for changes for project ${projectKey} with timeRange ${timeRange}`);
const today = /* @__PURE__ */ new Date();
const data = Array.from({ length: 30 }, (_, i) => {
const date = new Date(today);
date.setDate(date.getDate() - (29 - i));
return {
date: date.toISOString().split("T")[0],
leadTime: Math.floor(Math.random() * 20) + 1
// 1-21 hours
};
});
this.leadTimeMetadata = {
score: "Elite",
value: "< 1 day",
trend: "stable"
};
return data;
} catch (error) {
this.logger.error(`Error fetching lead time for changes for project ${projectKey}:`, error);
throw error;
}
}
/**
* Get mean time to restore data for a project
*/
async getMeanTimeToRestore(projectKey, timeRange = "30d") {
try {
this.logger.info(`Fetching MTTR for project ${projectKey} with timeRange ${timeRange}`);
const today = /* @__PURE__ */ new Date();
const data = Array.from({ length: 30 }, (_, i) => {
const date = new Date(today);
date.setDate(date.getDate() - (29 - i));
return {
date: date.toISOString().split("T")[0],
mttr: Math.floor(Math.random() * 50) + 10
// 10-60 minutes (converted to hours)
};
});
this.mttrMetadata = {
score: "Elite",
value: "< 1 hour",
trend: "improving"
};
return data;
} catch (error) {
this.logger.error(`Error fetching MTTR for project ${projectKey}:`, error);
throw error;
}
}
/**
* Get change failure rate data for a project
*/
async getChangeFailureRate(projectKey, timeRange = "30d") {
try {
this.logger.info(`Fetching change failure rate for project ${projectKey} with timeRange ${timeRange}`);
const today = /* @__PURE__ */ new Date();
const data = Array.from({ length: 30 }, (_, i) => {
const date = new Date(today);
date.setDate(date.getDate() - (29 - i));
return {
date: date.toISOString().split("T")[0],
rate: Math.floor(Math.random() * 15)
// 0-15% failure rate
};
});
this.failureRateMetadata = {
score: "High",
value: "0-15%",
trend: "stable"
};
return data;
} catch (error) {
this.logger.error(`Error fetching change failure rate for project ${projectKey}:`, error);
throw error;
}
}
/**
* Get summary of all DORA metrics for a project
*/
async getDoraMetricsSummary(projectKey, timeRange = "30d") {
try {
this.logger.info(`Fetching DORA metrics summary for project ${projectKey} with timeRange ${timeRange}`);
return {
deploymentFrequency: {
value: "Multiple deploys per day",
level: "elite"
},
leadTimeForChanges: {
value: "24 hours",
level: "elite"
},
meanTimeToRestore: {
value: "1 hour",
level: "elite"
},
changeFailureRate: {
value: "5%",
level: "elite"
}
};
} catch (error) {
this.logger.error(`Error fetching DORA metrics summary for project ${projectKey}:`, error);
throw error;
}
}
}
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
class DevLakeDatabase {
constructor(config, logger) {
__publicField(this, "client");
const dbConfig = config.getConfig("doraMetrics.database");
const connection = {
host: dbConfig.getString("connection.host"),
port: dbConfig.getNumber("connection.port"),
user: dbConfig.getString("connection.user"),
password: dbConfig.getString("connection.password"),
database: dbConfig.getString("connection.database")
};
logger.info("Creating connection to Apache DevLake database");
this.client = knex__default["default"]({
client: "mysql2",
connection,
pool: {
min: 2,
max: 10
}
});
}
async testConnection() {
try {
await this.client.raw("SELECT 1");
return true;
} catch (error) {
return false;
}
}
getClient() {
return this.client;
}
async listTables() {
const result = await this.client.raw("SHOW TABLES");
return result[0].map((row) => Object.values(row)[0]);
}
async describeTable(tableName) {
const result = await this.client.raw(`DESCRIBE ${tableName}`);
return result[0];
}
}
function createRouter(options) {
const { logger, config, service } = options;
const router = express.Router();
const frontendUrl = config.getOptionalString("doraMetrics.frontendUrl") || "http://localhost:8000";
router.use(cors__default["default"]({
origin: frontendUrl,
credentials: true
}));
router.get("/health", (_, res) => {
logger.info("DORA Metrics plugin health check");
res.send({ status: "ok" });
});
router.get("/projects", async (_, res) => {
try {
const projects = await service.getProjects();
res.json(projects);
} catch (error) {
logger.error(`Error fetching projects: ${error}`);
res.status(500).json({ error: "Error fetching projects" });
}
});
router.get("/metrics/deployment-frequency", async (req, res) => {
const { projectKey, timeRange = "30d" } = req.query;
if (!projectKey) {
return res.status(400).json({ error: "Project key is required" });
}
try {
const data = await service.getDeploymentFrequency(
projectKey,
timeRange
);
res.json(data);
} catch (error) {
logger.error(`Error fetching deployment frequency: ${error}`);
res.status(500).json({ error: "Error fetching deployment frequency" });
}
});
router.get("/metrics/lead-time", async (req, res) => {
const { projectKey, timeRange = "30d" } = req.query;
if (!projectKey) {
return res.status(400).json({ error: "Project key is required" });
}
try {
const data = await service.getLeadTimeForChanges(
projectKey,
timeRange
);
res.json(data);
} catch (error) {
logger.error(`Error fetching lead time: ${error}`);
res.status(500).json({ error: "Error fetching lead time" });
}
});
router.get("/metrics/mttr", async (req, res) => {
const { projectKey, timeRange = "30d" } = req.query;
if (!projectKey) {
return res.status(400).json({ error: "Project key is required" });
}
try {
const data = await service.getMeanTimeToRestore(
projectKey,
timeRange
);
res.json(data);
} catch (error) {
logger.error(`Error fetching MTTR: ${error}`);
res.status(500).json({ error: "Error fetching MTTR" });
}
});
router.get("/metrics/failure-rate", async (req, res) => {
const { projectKey, timeRange = "30d" } = req.query;
if (!projectKey) {
return res.status(400).json({ error: "Project key is required" });
}
try {
const data = await service.getChangeFailureRate(
projectKey,
timeRange
);
res.json(data);
} catch (error) {
logger.error(`Error fetching failure rate: ${error}`);
res.status(500).json({ error: "Error fetching failure rate" });
}
});
router.get("/metrics/summary", async (req, res) => {
const { projectKey, timeRange = "30d" } = req.query;
if (!projectKey) {
return res.status(400).json({ error: "Project key is required" });
}
try {
const data = await service.getDoraMetricsSummary(
projectKey,
timeRange
);
res.json(data);
} catch (error) {
logger.error(`Error fetching metrics summary: ${error}`);
res.status(500).json({ error: "Error fetching metrics summary" });
}
});
router.get("/auth/guest/refresh", (req, res) => {
logger.info("Guest auth refresh request");
res.json({
profile: {
email: "guest@example.com",
displayName: "Guest User"
},
backstageIdentity: {
token: "guest-token",
identity: {
type: "user",
ownershipEntityRefs: ["user:default/guest"],
userEntityRef: "user:default/guest"
}
},
expiresAt: new Date(Date.now() + 3600 * 1e3).toISOString()
});
});
router.get("/auth/session", (req, res) => {
logger.info("Auth session request");
res.json({
identity: {
type: "user",
ownershipEntityRefs: ["user:default/guest"],
userEntityRef: "user:default/guest"
},
token: "guest-token",
expiresAt: new Date(Date.now() + 3600 * 1e3).toISOString()
});
});
return router;
}
exports.DevLakeDatabase = DevLakeDatabase;
exports.DoraMetricsService = DoraMetricsService;
exports.createRouter = createRouter;
//# sourceMappingURL=index.cjs.js.map