feat(types): semilla con fields para 11 tipos + CLI --test-types-yaml
- examples/types.yaml: principal_field + fields para Person, Email,
Domain, Phone, Org, IBAN, Account, Document, Address, Url, Table.
44 fields totales. Documentacion del formato en cabecera.
- project_manager.cpp: seed con fields para los tipos basicos (fallback
cuando no se encuentra examples/types.yaml).
- main.cpp:
- Log de carga incluye conteo de schemas y total de fields.
- --test-types-yaml <path>: smoke test que carga, serializa a temp y
recarga. Compara entidades/relaciones/fields field-a-field. Salida
PASS/FAIL con exit code 0/1. Permite verificar round-trip sin
framework de tests.
Verificado: examples/types.yaml round-trip estable (11 entities, 44
fields, 6 relations).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+74
-5
@@ -1,57 +1,126 @@
|
|||||||
# Ejemplo OSINT — tipos comunes en investigacion de entidades.
|
# Ejemplo OSINT — tipos comunes en investigacion de entidades.
|
||||||
# Color como "#RRGGBB" (con o sin alpha "#RRGGBBAA").
|
# Color como "#RRGGBB" (con o sin alpha "#RRGGBBAA").
|
||||||
# Shapes: el campo `shape` se ignora — todos los nodos son circulo, salvo
|
# Shapes: campo `shape` ignorado — todos los nodos son circulo, salvo
|
||||||
# el tipo "Table" que es cuadrado (regla de forma aplicada en
|
# el tipo "Table" que es cuadrado (regla en types_registry.cpp::apply_types_yaml).
|
||||||
# types_registry.cpp::apply_types_yaml).
|
# Iconos: nombres ti-* mapeados en tabler_codepoint_by_name.
|
||||||
# Iconos: nombres ti-* mapeados en types_registry.cpp::tabler_codepoint_by_name
|
# Fields: schema por tipo, formato inline-map.
|
||||||
# Estilos de relacion: solid | dashed | dotted
|
# tipos: string | int | float | bool | date | url | enum
|
||||||
|
# enum requiere `values: [...]`
|
||||||
|
# `principal_field` (opcional) es el nombre del field que sirve de label
|
||||||
|
# visible en el grafo; default = "name".
|
||||||
|
|
||||||
entities:
|
entities:
|
||||||
- name: Person
|
- name: Person
|
||||||
color: "#5B8DEF"
|
color: "#5B8DEF"
|
||||||
icon: ti-user
|
icon: ti-user
|
||||||
|
principal_field: name
|
||||||
|
fields:
|
||||||
|
- { name: name, type: string, required: true }
|
||||||
|
- { name: first_name, type: string }
|
||||||
|
- { name: last_name, type: string }
|
||||||
|
- { name: age, type: int }
|
||||||
|
- { name: nationality, type: string }
|
||||||
|
- { name: birth_date, type: date }
|
||||||
|
- { name: gender, type: enum, values: [male, female, other] }
|
||||||
|
- { name: occupation, type: string }
|
||||||
|
|
||||||
- name: Email
|
- name: Email
|
||||||
color: "#58CA8C"
|
color: "#58CA8C"
|
||||||
icon: ti-mail
|
icon: ti-mail
|
||||||
|
principal_field: address
|
||||||
|
fields:
|
||||||
|
- { name: address, type: string, required: true }
|
||||||
|
- { name: provider, type: string }
|
||||||
|
- { name: verified, type: bool }
|
||||||
|
|
||||||
- name: Domain
|
- name: Domain
|
||||||
color: "#F4B860"
|
color: "#F4B860"
|
||||||
icon: ti-world
|
icon: ti-world
|
||||||
|
principal_field: name
|
||||||
|
fields:
|
||||||
|
- { name: name, type: string, required: true }
|
||||||
|
- { name: registrar, type: string }
|
||||||
|
- { name: created_at, type: date }
|
||||||
|
- { name: expires_at, type: date }
|
||||||
|
|
||||||
- name: Phone
|
- name: Phone
|
||||||
color: "#E36AC0"
|
color: "#E36AC0"
|
||||||
icon: ti-phone
|
icon: ti-phone
|
||||||
|
principal_field: number
|
||||||
|
fields:
|
||||||
|
- { name: number, type: string, required: true }
|
||||||
|
- { name: country_code, type: string }
|
||||||
|
- { name: kind, type: enum, values: [mobile, landline, voip] }
|
||||||
|
|
||||||
- name: Org
|
- name: Org
|
||||||
color: "#C780E8"
|
color: "#C780E8"
|
||||||
icon: ti-building
|
icon: ti-building
|
||||||
|
principal_field: name
|
||||||
|
fields:
|
||||||
|
- { name: name, type: string, required: true }
|
||||||
|
- { name: country, type: string }
|
||||||
|
- { name: kind, type: enum, values: [company, ngo, gov, other] }
|
||||||
|
- { name: website, type: url }
|
||||||
|
|
||||||
- name: IBAN
|
- name: IBAN
|
||||||
color: "#52CDF2"
|
color: "#52CDF2"
|
||||||
icon: ti-building-bank
|
icon: ti-building-bank
|
||||||
|
principal_field: iban
|
||||||
|
fields:
|
||||||
|
- { name: iban, type: string, required: true }
|
||||||
|
- { name: country, type: string }
|
||||||
|
- { name: bank_name, type: string }
|
||||||
|
|
||||||
- name: Account
|
- name: Account
|
||||||
color: "#7FD3A0"
|
color: "#7FD3A0"
|
||||||
icon: ti-id
|
icon: ti-id
|
||||||
|
principal_field: handle
|
||||||
|
fields:
|
||||||
|
- { name: handle, type: string, required: true }
|
||||||
|
- { name: platform, type: string }
|
||||||
|
- { name: url, type: url }
|
||||||
|
- { name: verified, type: bool }
|
||||||
|
|
||||||
- name: Document
|
- name: Document
|
||||||
color: "#C9C9C9"
|
color: "#C9C9C9"
|
||||||
icon: ti-file
|
icon: ti-file
|
||||||
|
principal_field: title
|
||||||
|
fields:
|
||||||
|
- { name: title, type: string, required: true }
|
||||||
|
- { name: url, type: url }
|
||||||
|
- { name: kind, type: string }
|
||||||
|
- { name: created, type: date }
|
||||||
|
|
||||||
- name: Address
|
- name: Address
|
||||||
color: "#FFB870"
|
color: "#FFB870"
|
||||||
icon: ti-map-pin
|
icon: ti-map-pin
|
||||||
|
principal_field: line1
|
||||||
|
fields:
|
||||||
|
- { name: line1, type: string, required: true }
|
||||||
|
- { name: city, type: string }
|
||||||
|
- { name: country, type: string }
|
||||||
|
- { name: postcode, type: string }
|
||||||
|
|
||||||
- name: Url
|
- name: Url
|
||||||
color: "#89E0FC"
|
color: "#89E0FC"
|
||||||
icon: ti-link
|
icon: ti-link
|
||||||
|
principal_field: url
|
||||||
|
fields:
|
||||||
|
- { name: url, type: url, required: true }
|
||||||
|
- { name: title, type: string }
|
||||||
|
- { name: domain, type: string }
|
||||||
|
|
||||||
# Nodo tabla — cuadrado (regla de forma). Issue 0010: contenedor con
|
# Nodo tabla — cuadrado (regla de forma). Issue 0010: contenedor con
|
||||||
# filas que son nodos del grafo.
|
# filas que son nodos del grafo.
|
||||||
- name: Table
|
- name: Table
|
||||||
color: "#0EA5E9"
|
color: "#0EA5E9"
|
||||||
icon: ti-database
|
icon: ti-database
|
||||||
|
principal_field: name
|
||||||
|
fields:
|
||||||
|
- { name: name, type: string, required: true }
|
||||||
|
- { name: row_type, type: string }
|
||||||
|
- { name: columns, type: string }
|
||||||
|
- { name: expanded, type: bool }
|
||||||
|
|
||||||
relations:
|
relations:
|
||||||
- name: owns
|
- name: owns
|
||||||
|
|||||||
@@ -189,9 +189,17 @@ static bool load_input() {
|
|||||||
g_atlas_bound = false;
|
g_atlas_bound = false;
|
||||||
if (g_atlas) { graph_icons_destroy(g_atlas); g_atlas = nullptr; }
|
if (g_atlas) { graph_icons_destroy(g_atlas); g_atlas = nullptr; }
|
||||||
g_atlas = ge::build_icon_atlas(codepoints);
|
g_atlas = ge::build_icon_atlas(codepoints);
|
||||||
|
int total_fields = 0;
|
||||||
|
int with_schema = 0;
|
||||||
|
for (const auto& e : pt.entities) {
|
||||||
|
total_fields += (int)e.fields.size();
|
||||||
|
if (!e.fields.empty()) ++with_schema;
|
||||||
|
}
|
||||||
std::fprintf(stdout,
|
std::fprintf(stdout,
|
||||||
"[graph_explorer] types.yaml: %zu entities, %zu relations, %zu icons\n",
|
"[graph_explorer] types.yaml: %zu entities (%d con schema, %d fields totales),"
|
||||||
pt.entities.size(), pt.relations.size(), codepoints.size());
|
" %zu relations, %zu icons\n",
|
||||||
|
pt.entities.size(), with_schema, total_fields,
|
||||||
|
pt.relations.size(), codepoints.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -765,7 +773,75 @@ static void usage() {
|
|||||||
" graph_explorer --input operations <path>\n"
|
" graph_explorer --input operations <path>\n"
|
||||||
" graph_explorer --types <types.yaml>\n"
|
" graph_explorer --types <types.yaml>\n"
|
||||||
" graph_explorer --layout force|grid|circular|radial|hierarchical|fixed\n"
|
" graph_explorer --layout force|grid|circular|radial|hierarchical|fixed\n"
|
||||||
" graph_explorer --project <slug>\n");
|
" graph_explorer --project <slug>\n"
|
||||||
|
" graph_explorer --test-types-yaml <path> (load+save+reload smoke test)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smoke test del parser+writer (issue 0005 round-trip): carga `path`,
|
||||||
|
// serializa a un temporal y vuelve a cargar. Compara campos clave de
|
||||||
|
// ParsedTypes. Devuelve exit code 0 si OK, 1 si discrepancia, 2 si error.
|
||||||
|
static int test_types_yaml_roundtrip(const char* path) {
|
||||||
|
ge::ParsedTypes pt1;
|
||||||
|
std::string err;
|
||||||
|
if (!ge::types_load_yaml(path, &pt1, &err)) {
|
||||||
|
std::fprintf(stderr, "[test] load1 fail: %s\n", err.c_str());
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
std::string tmp = std::string(path) + ".roundtrip.yaml";
|
||||||
|
if (!ge::types_save_yaml(tmp.c_str(), pt1, &err)) {
|
||||||
|
std::fprintf(stderr, "[test] save fail: %s\n", err.c_str());
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
ge::ParsedTypes pt2;
|
||||||
|
if (!ge::types_load_yaml(tmp.c_str(), &pt2, &err)) {
|
||||||
|
std::fprintf(stderr, "[test] load2 fail: %s\n", err.c_str());
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cmp = [&]() -> bool {
|
||||||
|
if (pt1.entities.size() != pt2.entities.size()) return false;
|
||||||
|
if (pt1.relations.size() != pt2.relations.size()) return false;
|
||||||
|
for (size_t i = 0; i < pt1.entities.size(); ++i) {
|
||||||
|
const auto& a = pt1.entities[i];
|
||||||
|
const auto& b = pt2.entities[i];
|
||||||
|
if (a.name != b.name) return false;
|
||||||
|
if (a.color != b.color) return false;
|
||||||
|
if (a.icon_name != b.icon_name) return false;
|
||||||
|
if (a.principal_field != b.principal_field) return false;
|
||||||
|
if (a.fields.size() != b.fields.size()) return false;
|
||||||
|
for (size_t j = 0; j < a.fields.size(); ++j) {
|
||||||
|
const auto& fa = a.fields[j];
|
||||||
|
const auto& fb = b.fields[j];
|
||||||
|
if (fa.name != fb.name) return false;
|
||||||
|
if (fa.kind != fb.kind) return false;
|
||||||
|
if (fa.required != fb.required) return false;
|
||||||
|
if (fa.enum_values != fb.enum_values) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < pt1.relations.size(); ++i) {
|
||||||
|
const auto& a = pt1.relations[i];
|
||||||
|
const auto& b = pt2.relations[i];
|
||||||
|
if (a.name != b.name) return false;
|
||||||
|
if (a.color != b.color) return false;
|
||||||
|
if (a.style != b.style) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
int total_fields = 0;
|
||||||
|
for (const auto& e : pt1.entities) total_fields += (int)e.fields.size();
|
||||||
|
|
||||||
|
if (cmp()) {
|
||||||
|
std::fprintf(stdout,
|
||||||
|
"[test] PASS — %zu entities, %d fields, %zu relations (round-trip estable)\n",
|
||||||
|
pt1.entities.size(), total_fields, pt1.relations.size());
|
||||||
|
std::remove(tmp.c_str());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
std::fprintf(stderr,
|
||||||
|
"[test] FAIL — discrepancia tras round-trip. dump preservado en %s\n",
|
||||||
|
tmp.c_str());
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
@@ -790,6 +866,8 @@ int main(int argc, char** argv) {
|
|||||||
g_layout_initial = argv[++i];
|
g_layout_initial = argv[++i];
|
||||||
} else if (std::strcmp(a, "--project") == 0 && i + 1 < argc) {
|
} else if (std::strcmp(a, "--project") == 0 && i + 1 < argc) {
|
||||||
project_arg = argv[++i];
|
project_arg = argv[++i];
|
||||||
|
} else if (std::strcmp(a, "--test-types-yaml") == 0 && i + 1 < argc) {
|
||||||
|
return test_types_yaml_roundtrip(argv[++i]);
|
||||||
} else if (std::strcmp(a, "--help") == 0 || std::strcmp(a, "-h") == 0) {
|
} else if (std::strcmp(a, "--help") == 0 || std::strcmp(a, "-h") == 0) {
|
||||||
usage();
|
usage();
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+15
-8
@@ -123,29 +123,33 @@ CREATE TABLE IF NOT EXISTS logs (
|
|||||||
);
|
);
|
||||||
)SQL";
|
)SQL";
|
||||||
|
|
||||||
// Semilla types.yaml minima si examples/types.yaml no se encuentra.
|
// Semilla types.yaml minima si examples/types.yaml no se encuentra. Incluye
|
||||||
|
// `fields` (issue 0005) y el tipo Table como cuadrado (issue 0010 preview).
|
||||||
static const char* k_seed_types_yaml = R"YAML(# types.yaml — generado al crear el proyecto. Editable desde el Type Editor.
|
static const char* k_seed_types_yaml = R"YAML(# types.yaml — generado al crear el proyecto. Editable desde el Type Editor.
|
||||||
# Todos los nodos son circulo, salvo "Table" que es cuadrado.
|
# Todos los nodos son circulo, salvo "Table" que es cuadrado.
|
||||||
# Iconos: nombres ti-* mapeados en types_registry.cpp
|
# Tipos de campo: string | int | float | bool | date | url | enum.
|
||||||
# Estilos de relacion: solid | dashed | dotted
|
|
||||||
|
|
||||||
entities:
|
entities:
|
||||||
- name: Person
|
- name: Person
|
||||||
color: "#5B8DEF"
|
color: "#5B8DEF"
|
||||||
icon: ti-user
|
icon: ti-user
|
||||||
|
principal_field: name
|
||||||
|
fields:
|
||||||
|
- { name: name, type: string, required: true }
|
||||||
|
- { name: first_name, type: string }
|
||||||
|
- { name: last_name, type: string }
|
||||||
|
|
||||||
- name: Email
|
- name: Email
|
||||||
color: "#58CA8C"
|
color: "#58CA8C"
|
||||||
icon: ti-mail
|
icon: ti-mail
|
||||||
|
principal_field: address
|
||||||
|
fields:
|
||||||
|
- { name: address, type: string, required: true }
|
||||||
|
|
||||||
- name: Domain
|
- name: Domain
|
||||||
color: "#F4B860"
|
color: "#F4B860"
|
||||||
icon: ti-world
|
icon: ti-world
|
||||||
|
|
||||||
- name: Phone
|
|
||||||
color: "#E36AC0"
|
|
||||||
icon: ti-phone
|
|
||||||
|
|
||||||
- name: Org
|
- name: Org
|
||||||
color: "#C780E8"
|
color: "#C780E8"
|
||||||
icon: ti-building
|
icon: ti-building
|
||||||
@@ -154,10 +158,13 @@ entities:
|
|||||||
color: "#C9C9C9"
|
color: "#C9C9C9"
|
||||||
icon: ti-file
|
icon: ti-file
|
||||||
|
|
||||||
# Nodo tabla — cuadrado (issue 0010).
|
|
||||||
- name: Table
|
- name: Table
|
||||||
color: "#0EA5E9"
|
color: "#0EA5E9"
|
||||||
icon: ti-database
|
icon: ti-database
|
||||||
|
principal_field: name
|
||||||
|
fields:
|
||||||
|
- { name: name, type: string, required: true }
|
||||||
|
- { name: row_type, type: string }
|
||||||
|
|
||||||
relations:
|
relations:
|
||||||
- name: owns
|
- name: owns
|
||||||
|
|||||||
Reference in New Issue
Block a user