Sharper health probes: resolve stage option group via CustomField metadata; list near-match relationship types
This commit is contained in:
@@ -114,24 +114,55 @@ async function probeContactGet(): Promise<CheckResult> {
|
|||||||
|
|
||||||
async function probeRelationshipType(): Promise<CheckResult> {
|
async function probeRelationshipType(): Promise<CheckResult> {
|
||||||
try {
|
try {
|
||||||
const res = await civi<{ id: number; name_a_b: string; name_b_a: string }>(
|
const exact = await civi<{ id: number; name_a_b: string; name_b_a: string; label_a_b: string }>(
|
||||||
"RelationshipType",
|
"RelationshipType",
|
||||||
"get",
|
"get",
|
||||||
{
|
{
|
||||||
select: ["id", "name_a_b", "name_b_a"],
|
select: ["id", "name_a_b", "name_b_a", "label_a_b"],
|
||||||
where: [["name_a_b", "=", "Primary Form Contact of"]],
|
where: [["name_a_b", "=", "Primary Form Contact of"]],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const rows = res.values ?? [];
|
if ((exact.values ?? []).length === 1) {
|
||||||
|
const r = exact.values[0];
|
||||||
return {
|
return {
|
||||||
check: "relationship_type_primary_form_contact_of",
|
check: "relationship_type_primary_form_contact_of",
|
||||||
ok: rows.length === 1,
|
ok: true,
|
||||||
|
detail: `Found id=${r.id}, name_a_b="${r.name_a_b}", name_b_a="${r.name_b_a}".`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not found by exact name — list close matches so we know what's available.
|
||||||
|
const candidates = await civi<{ id: number; name_a_b: string; label_a_b: string; contact_type_a: string; contact_type_b: string }>(
|
||||||
|
"RelationshipType",
|
||||||
|
"get",
|
||||||
|
{
|
||||||
|
select: ["id", "name_a_b", "label_a_b", "contact_type_a", "contact_type_b"],
|
||||||
|
where: [
|
||||||
|
[
|
||||||
|
"OR",
|
||||||
|
[
|
||||||
|
["name_a_b", "LIKE", "%Form%"],
|
||||||
|
["name_a_b", "LIKE", "%Primary%"],
|
||||||
|
["name_a_b", "LIKE", "%Contact%"],
|
||||||
|
["label_a_b", "LIKE", "%Form%"],
|
||||||
|
["label_a_b", "LIKE", "%Primary%"],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
limit: 25,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const closeMatches = (candidates.values ?? [])
|
||||||
|
.map((r) => ` • id=${r.id} name_a_b="${r.name_a_b}" label="${r.label_a_b}" (${r.contact_type_a} ↔ ${r.contact_type_b})`)
|
||||||
|
.join("\n");
|
||||||
|
return {
|
||||||
|
check: "relationship_type_primary_form_contact_of",
|
||||||
|
ok: false,
|
||||||
detail:
|
detail:
|
||||||
rows.length === 1
|
"No relationship type with name_a_b='Primary Form Contact of' on this site.\n" +
|
||||||
? `Found id=${rows[0].id}, name_a_b="${rows[0].name_a_b}", name_b_a="${rows[0].name_b_a}".`
|
"Existing relationship types matching Form/Primary/Contact:\n" +
|
||||||
: rows.length === 0
|
(closeMatches || " (none)") +
|
||||||
? `Not found. Create a relationship type named "Primary Form Contact of" (Individual ↔ Organization).`
|
"\nFix: create a new RelationshipType (Individual A ↔ Organization B) with name_a_b='Primary Form Contact of', or change the expected name in /api/data and /api/submit to match an existing type.",
|
||||||
: `Multiple matches (${rows.length}). Should be exactly one.`,
|
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {
|
return {
|
||||||
@@ -174,17 +205,33 @@ async function probeActivityType(): Promise<CheckResult> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function probeStageOptionGroup(): Promise<CheckResult> {
|
async function probeStageOptionGroup(): Promise<CheckResult> {
|
||||||
|
// Resolve the option group via the Framework Stage field's metadata
|
||||||
|
// (custom field id=1 on Organization, in Food_Co_op_Organizing group).
|
||||||
|
// This is more robust than guessing the option group's machine name.
|
||||||
try {
|
try {
|
||||||
const res = await civi<{ value: string; label: string }>("OptionValue", "get", {
|
const fieldRes = await civi<{ option_group_id: number; name: string }>("CustomField", "get", {
|
||||||
|
select: ["option_group_id", "name"],
|
||||||
|
where: [["id", "=", 1]],
|
||||||
|
});
|
||||||
|
const fieldRow = fieldRes.values?.[0];
|
||||||
|
if (!fieldRow?.option_group_id) {
|
||||||
|
return {
|
||||||
|
check: "stage_option_group_values",
|
||||||
|
ok: false,
|
||||||
|
detail: `CustomField id=1 (Framework Stage) not found, or has no option_group_id.`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const ogId = fieldRow.option_group_id;
|
||||||
|
const valuesRes = await civi<{ value: string; label: string }>("OptionValue", "get", {
|
||||||
select: ["value", "label"],
|
select: ["value", "label"],
|
||||||
where: [
|
where: [
|
||||||
["option_group_id:name", "=", "Stage"],
|
["option_group_id", "=", ogId],
|
||||||
["is_active", "=", true],
|
["is_active", "=", true],
|
||||||
],
|
],
|
||||||
orderBy: { weight: "ASC" },
|
orderBy: { weight: "ASC" },
|
||||||
limit: 50,
|
limit: 100,
|
||||||
});
|
});
|
||||||
const rows = res.values ?? [];
|
const rows = valuesRes.values ?? [];
|
||||||
const expected = [
|
const expected = [
|
||||||
"Inquiry",
|
"Inquiry",
|
||||||
"Organizing",
|
"Organizing",
|
||||||
@@ -195,13 +242,14 @@ async function probeStageOptionGroup(): Promise<CheckResult> {
|
|||||||
];
|
];
|
||||||
const present = new Set(rows.map((r) => r.value));
|
const present = new Set(rows.map((r) => r.value));
|
||||||
const missing = expected.filter((v) => !present.has(v));
|
const missing = expected.filter((v) => !present.has(v));
|
||||||
|
const sample = rows.slice(0, 10).map((r) => `${r.value}`).join(", ");
|
||||||
return {
|
return {
|
||||||
check: "stage_option_group_values",
|
check: "stage_option_group_values",
|
||||||
ok: missing.length === 0,
|
ok: missing.length === 0,
|
||||||
detail:
|
detail:
|
||||||
missing.length === 0
|
missing.length === 0
|
||||||
? `All 6 in-scope stage values present (${expected.join(", ")}). Total active options: ${rows.length}.`
|
? `All 6 in-scope stage values present in option_group_id=${ogId}. Total active options: ${rows.length}.`
|
||||||
: `Missing expected stage value(s): ${missing.join(", ")}. Found ${rows.length} active options.`,
|
: `option_group_id=${ogId} has ${rows.length} active option(s) but missing expected: ${missing.join(", ")}. First few present values: ${sample || "(none)"}`,
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user