Files
portabase/tests/api/helpers/discovery.ts
T

136 lines
3.9 KiB
TypeScript

function readIdArray(json: unknown, resourceName: string): Array<{ id: string }> {
if (!json || typeof json !== "object" || !("data" in json)) {
throw new Error(`Invalid ${resourceName} response: missing data array`);
}
const data = (json as { data?: unknown }).data;
if (!Array.isArray(data)) {
throw new Error(`Invalid ${resourceName} response: data is not an array`);
}
for (const item of data) {
if (!item || typeof item !== "object" || typeof (item as { id?: unknown }).id !== "string") {
throw new Error(
`Invalid ${resourceName} response: each item must include a string id`,
);
}
}
return data as Array<{ id: string }>;
}
function readBackupDetail(json: unknown) {
if (!json || typeof json !== "object" || !("data" in json)) {
throw new Error("Invalid backup detail response: missing data object");
}
const data = (json as { data?: unknown }).data;
if (!data || typeof data !== "object") {
throw new Error("Invalid backup detail response: data is not an object");
}
const id = (data as { id?: unknown }).id;
const storages = (data as { storages?: unknown }).storages;
if (typeof id !== "string") {
throw new Error("Invalid backup detail response: data.id must be a string");
}
if (!Array.isArray(storages)) {
throw new Error(
"Invalid backup detail response: data.storages is not an array",
);
}
for (const item of storages) {
if (
!item ||
typeof item !== "object" ||
typeof (item as { id?: unknown }).id !== "string" ||
typeof (item as { status?: unknown }).status !== "string"
) {
throw new Error(
"Invalid backup detail response: each storage must include string id and status",
);
}
}
return {
id,
storages: storages as Array<{ id: string; status: string }>,
};
}
export async function discoverAgentId(api: {
get: (path: string) => Promise<{ response: Response; json: unknown; text: string }>;
}) {
const result = await api.get("/api/v1/agents");
if (!result.response.ok) {
throw new Error(`Failed to list agents: ${result.response.status} ${result.text}`);
}
const agents = readIdArray(result.json, "agents");
if (agents.length === 0) {
throw new Error("No agent could be discovered from /api/v1/agents.");
}
return agents[0].id;
}
export async function discoverDatabaseId(api: {
get: (path: string) => Promise<{ response: Response; json: unknown; text: string }>;
}) {
const result = await api.get("/api/v1/databases");
if (!result.response.ok) {
throw new Error(
`Failed to list databases: ${result.response.status} ${result.text}`,
);
}
const databases = readIdArray(result.json, "databases");
if (databases.length === 0) {
throw new Error("No database could be discovered from /api/v1/databases.");
}
return databases[0].id;
}
export async function discoverBackupContext(
api: {
get: (path: string) => Promise<{ response: Response; json: unknown; text: string }>;
},
databaseId: string | null,
) {
if (!databaseId) {
throw new Error("Backup discovery requires a database id.");
}
const backups = await api.get(`/api/v1/databases/${databaseId}/backup`);
if (!backups.response.ok) {
throw new Error(
`Failed to list backups: ${backups.response.status} ${backups.text}`,
);
}
const backupList = readIdArray(backups.json, "backups");
for (const backup of backupList) {
const detail = await api.get(`/api/v1/databases/${databaseId}/backup/${backup.id}`);
if (!detail.response.ok) {
continue;
}
const payload = readBackupDetail(detail.json);
const storage = payload.storages.find((item) => item.status === "success");
if (storage) {
return {
backupId: payload.id,
backupStorageId: storage.id,
};
}
}
throw new Error(
`No successful backup storage could be discovered for database ${databaseId}.`,
);
}