Rewrite enrich-batch to use team assets endpoint for full data
The owner endpoint only returns ownership info (no card_flags, ncim_discovery, or netops_granite_allips). Switch to fetching team assets (paginated) which returns the full enriched record with EQUIP_INST_ID, CARD_HOSTNAME, CARD_ASN, CARD_DEVICE_ID, CARD_VENDOR_MODEL, CARD_CLLI, and ncim_discovery data. Accepts optional 'team' parameter (defaults to NTS-AEO-STEAM). Paginates through confirmed and unconfirmed dispositions until all target IPs are found or pages are exhausted.
This commit is contained in:
@@ -518,7 +518,7 @@ function createCardApiRouter() {
|
|||||||
return res.status(503).json({ error: 'CARD API is not configured.', missingVars });
|
return res.status(503).json({ error: 'CARD API is not configured.', missingVars });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { ips } = req.body || {};
|
const { ips, team } = req.body || {};
|
||||||
if (!Array.isArray(ips) || ips.length === 0) {
|
if (!Array.isArray(ips) || ips.length === 0) {
|
||||||
return res.status(400).json({ error: 'ips must be a non-empty array of IP address strings.' });
|
return res.status(400).json({ error: 'ips must be a non-empty array of IP address strings.' });
|
||||||
}
|
}
|
||||||
@@ -526,59 +526,81 @@ function createCardApiRouter() {
|
|||||||
return res.status(400).json({ error: 'Maximum 200 IPs per request.' });
|
return res.status(400).json({ error: 'Maximum 200 IPs per request.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Known CARD asset ID suffixes to try
|
// Build a set of IPs we're looking for
|
||||||
const SUFFIXES = ['CTEC', 'NATL', 'CHTR', 'COML', 'RESI', 'WIFI', 'VOIP'];
|
const targetIps = new Set(ips.map(ip => (ip || '').trim()).filter(Boolean));
|
||||||
|
const resultMap = {};
|
||||||
|
|
||||||
|
// Strategy: fetch team assets (paginated) and match against our target IPs.
|
||||||
|
// The team assets endpoint returns the full enriched record with ncim_discovery,
|
||||||
|
// card_flags, netops_granite_allips, etc.
|
||||||
|
const teamName = team || 'NTS-AEO-STEAM';
|
||||||
|
const dispositions = ['confirmed', 'unconfirmed'];
|
||||||
|
let foundCount = 0;
|
||||||
|
|
||||||
|
for (const disposition of dispositions) {
|
||||||
|
if (foundCount >= targetIps.size) break;
|
||||||
|
|
||||||
|
let page = 1;
|
||||||
|
const pageSize = 200;
|
||||||
|
let hasMore = true;
|
||||||
|
|
||||||
|
while (hasMore && foundCount < targetIps.size) {
|
||||||
|
try {
|
||||||
|
const result = await getTeamAssets(teamName, { disposition, page, pageSize });
|
||||||
|
if (!result.ok) break;
|
||||||
|
|
||||||
|
const data = JSON.parse(result.body);
|
||||||
|
const assets = data.assets || data.results || (Array.isArray(data) ? data : []);
|
||||||
|
|
||||||
|
if (assets.length === 0) {
|
||||||
|
hasMore = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match assets against target IPs
|
||||||
|
for (const asset of assets) {
|
||||||
|
const assetId = asset._id || '';
|
||||||
|
// Extract IP from asset ID (e.g., "10.240.78.110-CTEC" → "10.240.78.110")
|
||||||
|
const assetIp = assetId.replace(/-[A-Z]+$/i, '');
|
||||||
|
|
||||||
|
if (targetIps.has(assetIp) && !resultMap[assetIp]) {
|
||||||
|
resultMap[assetIp] = extractGraniteFields(asset, assetIp);
|
||||||
|
foundCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there are more pages
|
||||||
|
const total = data.total || data.count || 0;
|
||||||
|
if (page * pageSize >= total || assets.length < pageSize) {
|
||||||
|
hasMore = false;
|
||||||
|
} else {
|
||||||
|
page++;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[card-api] enrich-batch: Error fetching ${teamName}/${disposition} page ${page}:`, err.message);
|
||||||
|
hasMore = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build results array in the same order as input IPs
|
||||||
const results = [];
|
const results = [];
|
||||||
let enrichedCount = 0;
|
let enrichedCount = 0;
|
||||||
let notFoundCount = 0;
|
let notFoundCount = 0;
|
||||||
|
|
||||||
for (const ip of ips) {
|
for (const ip of ips) {
|
||||||
if (!ip || typeof ip !== 'string') {
|
const trimmedIp = (ip || '').trim();
|
||||||
results.push({ ip: ip || '', found: false, equip_inst_id: null, hostname: null, error: 'Invalid IP' });
|
if (!trimmedIp) {
|
||||||
|
results.push({ ip: '', found: false, equip_inst_id: null, hostname: null, error: 'Invalid IP' });
|
||||||
notFoundCount++;
|
notFoundCount++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const trimmedIp = ip.trim();
|
if (resultMap[trimmedIp]) {
|
||||||
let found = false;
|
results.push({ ip: trimmedIp, found: true, ...resultMap[trimmedIp] });
|
||||||
let enriched = null;
|
|
||||||
|
|
||||||
// Try owner lookup with each suffix
|
|
||||||
for (const suffix of SUFFIXES) {
|
|
||||||
try {
|
|
||||||
const assetId = `${trimmedIp}-${suffix}`;
|
|
||||||
const ownerResult = await getOwner(assetId);
|
|
||||||
if (ownerResult.ok) {
|
|
||||||
const asset = JSON.parse(ownerResult.body);
|
|
||||||
enriched = extractGraniteFields(asset, trimmedIp);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (_) {
|
|
||||||
// Continue to next suffix
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try without suffix (bare IP)
|
|
||||||
if (!found) {
|
|
||||||
try {
|
|
||||||
const ownerResult = await getOwner(trimmedIp);
|
|
||||||
if (ownerResult.ok) {
|
|
||||||
const asset = JSON.parse(ownerResult.body);
|
|
||||||
enriched = extractGraniteFields(asset, trimmedIp);
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
} catch (_) {
|
|
||||||
// Not found
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found && enriched) {
|
|
||||||
results.push({ ip: trimmedIp, found: true, ...enriched });
|
|
||||||
enrichedCount++;
|
enrichedCount++;
|
||||||
} else {
|
} else {
|
||||||
results.push({ ip: trimmedIp, found: false, equip_inst_id: null, hostname: null, error: 'IP not found in CARD' });
|
results.push({ ip: trimmedIp, found: false, equip_inst_id: null, hostname: null, error: 'IP not found in CARD team assets' });
|
||||||
notFoundCount++;
|
notFoundCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user