feat(export-import): add yaml export/import with id upsert for credentials, snippets, and scenarios
- all entities export a kind field (credential/snippet/scenario) for safe type checking on import - import upserts by id: overwrites if id exists, creates with explicit id otherwise - scenario export now includes id and step ids; import deletes old steps before recreating - add GET /:id/export and POST /import endpoints to credential and snippet controllers - add UuidBadge ui component: shortens uuid to first+last 4 hex chars, tooltip, clipboard copy with animated ClipboardCheck icon pop - apply UuidBadge across all entity id display sites (detail pages, card footers, table columns) - add export/import buttons to CredentialsPage, SnippetsPage, CredentialDetailPage, SnippetDetailPage
This commit is contained in:
@@ -33,14 +33,14 @@ interface BrowserHandle {
|
||||
@Injectable()
|
||||
export class ScenarioSchedulerService {
|
||||
private readonly logger = new TraceLogger(ScenarioSchedulerService.name);
|
||||
private readonly activeRuns = new Set<number>();
|
||||
private readonly runBrowsers = new Map<number, BrowserHandle>();
|
||||
private readonly activeRuns = new Set<string>();
|
||||
private readonly runBrowsers = new Map<string, BrowserHandle>();
|
||||
// Cache credential maps per run (built once when a run starts)
|
||||
private readonly runCredentials = new Map<number, Record<string, unknown>>();
|
||||
private readonly runCredentials = new Map<string, Record<string, unknown>>();
|
||||
// Cache environment URLs per run (resolved from the first login step)
|
||||
private readonly runEnvironments = new Map<number, EnvironmentUrls>();
|
||||
private readonly runEnvironments = new Map<string, EnvironmentUrls>();
|
||||
// Cache snippet code map per run (built once when a run starts)
|
||||
private readonly runSnippets = new Map<number, Record<string, string>>();
|
||||
private readonly runSnippets = new Map<string, Record<string, string>>();
|
||||
|
||||
constructor(
|
||||
@InjectRepository(ScenarioRunEntity)
|
||||
@@ -57,8 +57,8 @@ export class ScenarioSchedulerService {
|
||||
) {}
|
||||
|
||||
private persistLog(
|
||||
runId: number,
|
||||
stepRunId: number | null,
|
||||
runId: string,
|
||||
stepRunId: string | null,
|
||||
level: "log" | "warn" | "error",
|
||||
message: string,
|
||||
): void {
|
||||
@@ -67,7 +67,7 @@ export class ScenarioSchedulerService {
|
||||
);
|
||||
}
|
||||
|
||||
private stepLogger(stepRunId: number, runId: number): ScriptLogger {
|
||||
private stepLogger(stepRunId: string, runId: string): ScriptLogger {
|
||||
return (level, msg) => {
|
||||
this.logger[level](`StepRun #${stepRunId} script: ${msg}`);
|
||||
this.persistLog(runId, stepRunId, level, msg);
|
||||
@@ -98,7 +98,7 @@ export class ScenarioSchedulerService {
|
||||
* no login step or the environment cannot be found.
|
||||
*/
|
||||
private async resolveRunEnvironment(
|
||||
scenarioId: number,
|
||||
scenarioId: string,
|
||||
): Promise<EnvironmentUrls | null> {
|
||||
try {
|
||||
const scenario = await this.scenarioService.findOne(scenarioId);
|
||||
@@ -147,7 +147,7 @@ export class ScenarioSchedulerService {
|
||||
}
|
||||
}
|
||||
|
||||
private async processRunToCompletion(runId: number): Promise<void> {
|
||||
private async processRunToCompletion(runId: string): Promise<void> {
|
||||
try {
|
||||
let stepRun = await this.runStepRepo.findOne({
|
||||
where: { runId, status: "pending" },
|
||||
@@ -207,7 +207,7 @@ export class ScenarioSchedulerService {
|
||||
|
||||
// ── Shared browser per run ─────────────────────────────────────────────────
|
||||
|
||||
private async getOrCreateBrowserHandle(runId: number): Promise<BrowserHandle> {
|
||||
private async getOrCreateBrowserHandle(runId: string): Promise<BrowserHandle> {
|
||||
const existing = this.runBrowsers.get(runId);
|
||||
if (existing) return existing;
|
||||
|
||||
@@ -224,7 +224,7 @@ export class ScenarioSchedulerService {
|
||||
return handle;
|
||||
}
|
||||
|
||||
private async closeBrowserHandle(runId: number): Promise<void> {
|
||||
private async closeBrowserHandle(runId: string): Promise<void> {
|
||||
const handle = this.runBrowsers.get(runId);
|
||||
if (!handle) return;
|
||||
this.runBrowsers.delete(runId);
|
||||
|
||||
Reference in New Issue
Block a user