Improve CARD enrichment to extract fields from card_flags

The owner endpoint doesn't return ncim_discovery (so EQUIP_INST_ID
is unavailable from that endpoint). Update extractGraniteFields to
pull hostname from CARD_HOSTNAME, ASN from CARD_ASN, CLLI from
CARD_CLLI, serial from CARD_DEVICE_ID, and vendor/model from
CARD_VENDOR_MODEL in the card_flags array.

Assets without EQUIP_INST_ID are not in Granite and should use
the Add operation in the loader sheet instead of Change.
This commit is contained in:
Jordan Ramos
2026-05-27 19:40:21 -06:00
parent 5e95e35d26
commit 3310f7fa22

View File

@@ -590,17 +590,28 @@ function createCardApiRouter() {
}
/**
* Extract Granite-relevant fields from a CARD asset record.
* Extract Granite-relevant fields from a CARD asset/owner record.
* The owner endpoint returns: owner (confirmed/unconfirmed/etc), card_flags, tmp.
* ncim_discovery is only available from the team assets endpoint.
*/
function extractGraniteFields(asset, ip) {
// card_flags can be an array or nested in the response differently
let flags = {};
if (asset.card_flags) {
flags = Array.isArray(asset.card_flags) ? (asset.card_flags[0] || {}) : asset.card_flags;
}
// Also check if flags are at the top level (owner endpoint format)
if (!flags.CARD_HOSTNAME && asset.CARD_HOSTNAME) {
flags = asset;
}
const ncim = asset.ncim_discovery || [];
const granite = asset.netops_granite_allips || [];
const iseGranite = asset.ise_granite_equipment || [];
const flags = (asset.card_flags && asset.card_flags[0]) || {};
const qualys = asset.qualys_hosts || [];
const ivanti = asset.ivanti_assets || [];
// EQUIP_INST_ID — primary from ncim_discovery, fallbacks
// EQUIP_INST_ID — from ncim_discovery or granite (only available from team assets endpoint)
let equip_inst_id = null;
let site_name = null;
let responsible_team = null;
@@ -623,25 +634,37 @@ function extractGraniteFields(asset, ip) {
equip_inst_id = iseGranite[0].EQUIP_INST_ID || null;
}
// Hostname fallbacks
if (!hostname) {
hostname = (flags.CARD_HOSTNAME && flags.CARD_HOSTNAME[0])
|| (qualys.length > 0 && qualys[0].HOSTNAME)
|| (ivanti.length > 0 && ivanti[0].hostName)
|| null;
// Hostname from card_flags (primary source from owner endpoint)
if (!hostname && flags.CARD_HOSTNAME) {
const hostnames = Array.isArray(flags.CARD_HOSTNAME) ? flags.CARD_HOSTNAME : [flags.CARD_HOSTNAME];
hostname = hostnames[0] || null;
}
if (!hostname && qualys.length > 0) hostname = qualys[0].HOSTNAME || null;
if (!hostname && ivanti.length > 0) hostname = ivanti[0].hostName || null;
// ASN
const mgmt_ip_asn = (flags.CARD_ASN) || null;
// ASN from card_flags
const mgmt_ip_asn = flags.CARD_ASN || null;
// CLLI → can be used as site hint
if (!site_name && flags.CARD_CLLI) {
site_name = flags.CARD_CLLI; // CLLI code, not full site name — user may need to map
}
// Equipment class — always S (Shelf) from CARD context
const equipment_class = 'S';
// Equip status from flags
const equip_status = (flags.status) || null;
// Device ID → maps to SERIALNUMBER in Granite
const serial_number = flags.CARD_DEVICE_ID || null;
// Equip template — not typically in CARD data, leave null
const equip_template = null;
// Equip status from flags
const equip_status = flags.status || null;
// Vendor/model from card_flags
let equip_template = null;
if (flags.CARD_VENDOR_MODEL && Array.isArray(flags.CARD_VENDOR_MODEL) && flags.CARD_VENDOR_MODEL.length > 0) {
const vm = flags.CARD_VENDOR_MODEL[0];
equip_template = typeof vm === 'string' ? vm : (vm.vendor_model || null);
}
// Confirmed team from owner record
const confirmed_team = asset.owner && asset.owner.confirmed
@@ -656,6 +679,7 @@ function extractGraniteFields(asset, ip) {
equipment_class,
equip_template,
equip_status,
serial_number,
};
}