This commit is contained in:
znetsixe
2026-01-29 09:16:41 +01:00
parent a536c6ed5e
commit 15c33d650b
6 changed files with 332 additions and 12 deletions

View File

@@ -1,3 +1,191 @@
export function getAssetVariables() {
const http = require('node:http');
const https = require('node:https');
const { URL } = require('node:url');
const { assetCategoryManager } = require('../../datasets/assetData');
}
function toNumber(value, fallback = 1) {
const result = Number(value);
return Number.isFinite(result) && result > 0 ? result : fallback;
}
function toArray(value = []) {
if (Array.isArray(value)) {
return value.filter((item) => typeof item !== 'undefined' && item !== null);
}
if (typeof value === 'string' && value.trim()) {
return [value.trim()];
}
if (typeof value === 'number') {
return [value];
}
return [];
}
function findModelMetadata(selection = {}) {
if (!selection) {
return null;
}
const categoryKey = selection.softwareType || 'measurement';
if (!assetCategoryManager.hasCategory(categoryKey)) {
return null;
}
const suppliers = assetCategoryManager.getCategory(categoryKey).suppliers || [];
const supplierMatch = (entry, value) => {
if (!entry || !value) return false;
const key = value.toString().toLowerCase();
return (
(entry.id && entry.id.toLowerCase() === key) ||
(entry.name && entry.name.toLowerCase() === key)
);
};
const supplier = suppliers.find((item) => supplierMatch(item, selection.supplier));
const types = supplier?.types || [];
const type = types.find((item) => supplierMatch(item, selection.assetType));
const models = type?.models || [];
const model = models.find((item) => supplierMatch(item, selection.model));
return model || null;
}
function buildAssetPayload({ assetSelection = {}, registrationDefaults = {} }) {
const defaults = {
profileId: 1,
locationId: 1,
processId: 1,
status: 'actief',
childAssets: [],
...registrationDefaults
};
const metadata = assetSelection.modelMetadata || findModelMetadata(assetSelection) || {};
const rawName = assetSelection.assetName || assetSelection.name || assetSelection.assetType || assetSelection.model;
const assetName = (rawName || 'Measurement asset').toString();
const assetDescription = (assetSelection.assetDescription || assetSelection.description || assetName).toString();
const payload = {
profile_id: toNumber(defaults.profileId, 1),
location_id: toNumber(defaults.locationId, 1),
process_id: toNumber(defaults.processId, 1),
asset_name: assetName,
asset_description: assetDescription,
asset_status: (assetSelection.assetStatus || defaults.status || 'actief').toString(),
product_model_id: metadata.product_model_id || metadata.id || assetSelection.model || null,
product_model_uuid: metadata.product_model_uuid || metadata.uuid || metadata.id || assetSelection.model || null,
child_assets: toArray(defaults.childAssets)
};
const tagNumber = typeof assetSelection.tagNumber === 'string' && assetSelection.tagNumber.trim()
? assetSelection.tagNumber.trim()
: null;
return {
payload,
tagNumber,
isUpdate: Boolean(tagNumber)
};
}
function normalizeHeaders(headers = {}, body = '') {
const normalized = { ...headers };
if (!Object.prototype.hasOwnProperty.call(normalized, 'Content-Length')) {
normalized['Content-Length'] = Buffer.byteLength(body);
}
return normalized;
}
function prepareUrl(baseUrl = '', path = '') {
const trimmedBase = (baseUrl || '').replace(/\/+$/g, '').replace(/\\/g, '/');
const trimmedPath = path.startsWith('/') ? path : `/${path}`;
if (!trimmedBase) {
return trimmedPath;
}
return `${trimmedBase}${trimmedPath}`;
}
function sendHttpRequest(url, method, headers = {}, body = '') {
const parsedUrl = new URL(url, 'http://localhost');
const agent = parsedUrl.protocol === 'https:' ? https : http;
const requestOptions = {
method,
hostname: parsedUrl.hostname,
port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
path: `${parsedUrl.pathname}${parsedUrl.search}`,
headers: normalizeHeaders(headers, body)
};
return new Promise((resolve, reject) => {
const req = agent.request(requestOptions, (res) => {
let raw = '';
res.setEncoding('utf8');
res.on('data', (chunk) => { raw += chunk; });
res.on('end', () => resolve({ status: res.statusCode, body: raw }));
});
req.on('error', reject);
if (body) {
req.write(body);
}
req.end();
});
}
function parseApiResponse(raw, status) {
try {
const parsed = JSON.parse(raw);
return {
success: parsed.success === true,
data: parsed.data || null,
message: parsed.message || (status >= 400 ? `HTTP ${status}` : 'Result returned')
};
} catch (error) {
return {
success: false,
data: raw,
message: `Unable to decode asset API response: ${error.message}`
};
}
}
async function syncAsset({ assetSelection = {}, registrationDefaults = {}, apiConfig = {}, nodeContext = {} }) {
const { payload, tagNumber, isUpdate } = buildAssetPayload({ assetSelection, registrationDefaults });
if (!apiConfig || !apiConfig.baseUrl) {
const message = 'Asset API configuration is missing';
console.warn('[assetUtils] ' + message, { nodeContext });
return { success: false, data: null, message };
}
const path = isUpdate && tagNumber && typeof apiConfig.updatePath === 'function'
? apiConfig.updatePath(tagNumber)
: apiConfig.registerPath;
const url = prepareUrl(apiConfig.baseUrl, path);
const method = isUpdate ? 'PUT' : 'POST';
const headers = apiConfig.headers || {};
console.info('[assetUtils] Sending asset update', { nodeContext, method, url });
try {
const response = await sendHttpRequest(url, method, headers, JSON.stringify(payload));
const parsed = parseApiResponse(response.body, response.status);
return {
success: parsed.success,
data: parsed.data,
message: parsed.message
};
} catch (error) {
console.error('[assetUtils] Asset API request failed', error, { nodeContext });
return {
success: false,
data: null,
message: `Asset API request error: ${error.message}`
};
}
}
module.exports = {
syncAsset,
buildAssetPayload,
findModelMetadata
};