const fileInput = document.getElementById('local-file');
const pathInput = document.getElementById('findings_path');
let currentRawData = null;
let isJson = false;
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
isJson = file.name.toLowerCase().endsWith('.json');
const reader = new FileReader();
reader.onload = (event) => {
currentRawData = event.target.result;
document.getElementById('preview-placeholder').style.display = 'none';
if (isJson) {
try {
const parsed = JSON.parse(currentRawData);
const guessedPath = autoDetectArrayPath(parsed);
if (guessedPath) {
pathInput.value = guessedPath;
}
} catch (e) {
console.error("Auto-detect failed:", e);
}
}
processPreview();
};
reader.readAsText(file);
});
pathInput.addEventListener('input', () => {
if (currentRawData && isJson) processPreview();
});
function autoDetectArrayPath(obj) {
if (Array.isArray(obj)) return ".";
let bestPath = "";
let maxLen = -1;
function search(currentObj, currentPath) {
if (Array.isArray(currentObj)) {
if (currentObj.length > 0 && typeof currentObj[0] === 'object') {
if (currentObj.length > maxLen) {
maxLen = currentObj.length;
bestPath = currentPath;
}
}
return;
}
if (currentObj !== null && typeof currentObj === 'object') {
for (let key in currentObj) {
let nextPath = currentPath ? currentPath + "." + key : key;
search(currentObj[key], nextPath);
}
}
}
search(obj, "");
return bestPath || ".";
}
function processPreview() {
let headers = [];
let rows = [];
if (isJson) {
try {
const parsed = JSON.parse(currentRawData);
const findings = getNestedValue(parsed, pathInput.value);
if (!Array.isArray(findings) || findings.length === 0) {
const rawPreview = JSON.stringify(parsed, null, 2).substring(0, 1500) + "\n\n... (file truncated for preview)";
document.getElementById('preview-table-container').innerHTML =
`
⚠️ Path "${pathInput.value}" is not an array.
Here is the structure of your file to help you find the correct path:
${rawPreview}
`;
document.getElementById('save-btn').classList.add('disabled');
return;
}
document.getElementById('save-btn').classList.remove('disabled');
headers = Object.keys(findings[0]);
rows = findings.slice(0, 5).map(obj => headers.map(h => formatCell(obj[h])));
} catch(e) {
document.getElementById('preview-table-container').innerHTML = `JSON Parse Error: ${e.message}
`;
return;
}
} else {
const lines = currentRawData.split('\n').filter(l => l.trim() !== '');
headers = lines[0].split(',').map(h => h.trim());
rows = lines.slice(1, 6).map(line => line.split(',').map(c => c.trim()));
document.getElementById('save-btn').classList.remove('disabled');
}
renderTable(headers, rows);
populateDropdowns(headers);
}
function getNestedValue(obj, path) {
if (path === '' || path === '.') return obj;
return path.split('.').reduce((acc, part) => acc && acc[part], obj);
}
function formatCell(val) {
if (typeof val === 'object') return JSON.stringify(val);
if (val === undefined || val === null) return "";
const str = String(val);
return str.length > 50 ? str.substring(0, 47) + "..." : str;
}
function renderTable(headers, rows) {
let html = '';
html += '' + headers.map(h => `| ${h} | `).join('') + '
';
rows.forEach(row => {
html += '' + row.map(cell => `| ${cell} | `).join('') + '
';
});
html += '
';
document.getElementById('preview-table-container').innerHTML = html;
}
function populateDropdowns(headers) {
const selects = document.querySelectorAll('.source-header');
selects.forEach(select => {
select.innerHTML = '';
headers.forEach(h => {
const opt = document.createElement('option');
opt.value = h;
opt.textContent = h;
select.appendChild(opt);
});
});
}
document.getElementById('adapter-form').onsubmit = async (e) => {
e.preventDefault();
const data = {
name: document.getElementById('name').value,
source_name: document.getElementById('source_name').value,
findings_path: document.getElementById('findings_path').value,
mapping_title: document.getElementById('mapping_title').value,
mapping_asset: document.getElementById('mapping_asset').value,
mapping_severity: document.getElementById('mapping_severity').value,
mapping_description: document.getElementById('mapping_description').value,
mapping_remediation: document.getElementById('mapping_remediation').value
};
const resp = await fetch('/api/adapters', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (resp.ok) {
alert("Adapter Saved! Taking you back to the Landing Zone.");
window.location.href = "/ingest";
} else {
alert("Failed to save adapter: " + await resp.text());
}
};
window.exportAdapterJSON = function() {
const name = document.getElementById("adapterName").value.trim();
const sourceName = document.getElementById("sourceName").value.trim();
const rootPath = document.getElementById("rootPath").value.trim();
if (!name || !sourceName) {
return alert("Adapter Name and Source Name are required to export.");
}
const payload = {
name: name,
source_name: sourceName,
findings_path: rootPath,
mapping_title: document.getElementById("mapTitle").value.trim(),
mapping_asset: document.getElementById("mapAsset").value.trim(),
mapping_severity: document.getElementById("mapSeverity").value.trim(),
mapping_description: document.getElementById("mapDesc").value.trim(),
mapping_remediation: document.getElementById("mapRem").value.trim()
};
// Create a downloadable JSON blob
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(payload, null, 4));
const downloadAnchorNode = document.createElement('a');
downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute("download", `${sourceName.toLowerCase().replace(/\s+/g, '_')}_adapter.json`);
document.body.appendChild(downloadAnchorNode);
downloadAnchorNode.click();
downloadAnchorNode.remove();
};