docs(flows): DoD obligatorio con user-facing surface + abrir issues 0100-0103 (taxonomia, frontmatter migration, dev_console, work dashboard)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-17 00:07:04 +02:00
parent 6337d93491
commit 8c5152fca4
12 changed files with 765 additions and 170 deletions
+92
View File
@@ -102,6 +102,17 @@ static void parse_run(const json& j, Run& r) {
r.duration_ms = get_int64(j, "duration_ms");
r.trigger = get_str(j, "trigger");
r.error = get_str(j, "error");
r.storage_db_id = get_str(j, "storage_db_id");
r.storage_table = get_str(j, "storage_table");
}
static void parse_table_entry(const json& j, TableEntry& t) {
t.database_id = get_str(j, "database_id");
t.database_label = get_str(j, "database_label");
t.database_kind = get_str(j, "database_kind");
t.table_name = get_str(j, "table_name");
t.row_count = get_int64(j, "row_count");
t.error = get_str(j, "error");
}
static void parse_db(const json& j, DatabaseInfo& d) {
@@ -191,6 +202,30 @@ bool list_databases_http(const std::string& api_url,
return true;
}
bool list_tables_http(const std::string& api_url,
std::vector<TableEntry>& out) {
std::string host;
int port;
if (!parse_url(api_url, host, port)) return false;
HttpClient cli(host, port);
auto res = cli.get("/api/datafactory/tables");
if (!res.ok()) {
fprintf(stderr, "[df_http] list_tables failed: status=%d\n", res.status);
return false;
}
auto j = json::parse(res.body, nullptr, false);
if (!j.is_object() || !j.contains("tables") || !j["tables"].is_array()) {
return false;
}
out.clear();
for (auto& item : j["tables"]) {
TableEntry t;
parse_table_entry(item, t);
out.push_back(std::move(t));
}
return true;
}
bool get_function_http(const std::string& api_url,
const std::string& function_id,
FnInfo& out) {
@@ -219,4 +254,61 @@ bool get_function_http(const std::string& api_url,
return true;
}
bool get_table_preview_http(const std::string& api_url,
const std::string& database_id,
const std::string& table,
int limit, int offset,
TablePreview& out) {
std::string host;
int port;
if (!parse_url(api_url, host, port)) return false;
if (database_id.empty() || table.empty()) return false;
HttpClient cli(host, port);
// Build query string manually (no URL encoding needed — IDs/table names
// are alphanumeric per the server-side validation regex).
char path[512];
std::snprintf(path, sizeof(path),
"/api/datafactory/preview?database_id=%s&table=%s&limit=%d&offset=%d",
database_id.c_str(), table.c_str(), limit, offset);
auto res = cli.get(path);
if (!res.ok()) {
fprintf(stderr, "[df_http] get_table_preview(%s.%s) failed: status=%d body=%s\n",
database_id.c_str(), table.c_str(), res.status, res.body.c_str());
return false;
}
auto j = json::parse(res.body, nullptr, false);
if (!j.is_object()) return false;
out.database_id = get_str(j, "database_id");
out.table_name = get_str(j, "table_name");
out.total_rows = get_int64(j, "total_rows");
out.limit = get_int64(j, "limit");
out.offset = get_int64(j, "offset");
out.columns.clear();
if (j.contains("columns") && j["columns"].is_array()) {
for (auto& c : j["columns"]) {
std::string name = get_str(c, "name");
std::string type = get_str(c, "type");
out.columns.emplace_back(name, type);
}
}
out.rows.clear();
if (j.contains("rows") && j["rows"].is_array()) {
for (auto& row : j["rows"]) {
if (!row.is_array()) continue;
std::vector<std::string> r;
r.reserve(row.size());
for (auto& cell : row) {
if (cell.is_string()) r.push_back(cell.get<std::string>());
else if (cell.is_null()) r.push_back("");
else r.push_back(cell.dump());
}
out.rows.push_back(std::move(r));
}
}
return true;
}
} // namespace data_factory