197 lines
9.3 KiB
TypeScript
197 lines
9.3 KiB
TypeScript
import {isObjectType} from "graphql";
|
|
import {logger} from "../logging/logger.js";
|
|
import {Device, Host} from "../schema/generated/graphql.js";
|
|
|
|
export const isDevice = (value: Host): value is Device => !!(value as Device).deviceType;
|
|
|
|
/*
|
|
As a default all . - seperators within a key shall be replaced by a Capital letter of the following word
|
|
*/
|
|
function defaultKeyMappingFunction(key: string): string {
|
|
let words = key.split(".")
|
|
for (let i = 1; i < words.length; i++) {
|
|
if (words[i]) {
|
|
words[i] = words[i][0].toUpperCase() + words[i].substring(1);
|
|
}
|
|
}
|
|
return words.join("")
|
|
}
|
|
|
|
export function createHierarchicalValueFieldResolver(
|
|
schema: any, typename: string,
|
|
sourceFieldMapper: (fieldname: string, parent: any, objectTypeRequested: boolean) => { [p: string]: any } | null): {
|
|
[fieldname: string]: any
|
|
} {
|
|
let resolver: { [fieldname: string]: any } = {}
|
|
|
|
let type = schema.getType(typename)
|
|
if (isObjectType(type)) {
|
|
let fields = type.getFields();
|
|
for (let fieldsKey in fields) {
|
|
let field = fields[fieldsKey];
|
|
resolver[field.name] = (parent: any) => sourceFieldMapper(field.name, parent, isObjectType(field.type));
|
|
}
|
|
}
|
|
return resolver
|
|
}
|
|
|
|
export function zabbixItemValueSourceFieldMapper(
|
|
fieldname: string,
|
|
parent: {
|
|
items: [{ itemid: string, key_: string; name: string, lastvalue: any }],
|
|
[key: string]: any
|
|
},
|
|
objectTypeRequested: boolean
|
|
) {
|
|
let result: { [p: string]: any; } | any = parent[fieldname]
|
|
if (!parent.items) {
|
|
logger.debug(`No parent.items found: ${JSON.stringify(parent)}`)
|
|
return result
|
|
}
|
|
parent.items.forEach(
|
|
item => {
|
|
result = mapAttributeListToGraphQlType(result, objectTypeRequested, fieldname, {
|
|
key: item.key_,
|
|
value: item.lastvalue
|
|
}
|
|
)
|
|
}
|
|
)
|
|
logger.info(`Device data mapped: ${JSON.stringify(result)}`)
|
|
|
|
return result;
|
|
}
|
|
|
|
export function zabbixTagsValueSourceFieldMapper(
|
|
fieldname: string,
|
|
tags: [{ tag: string, value: any }],
|
|
objectTypeRequested: boolean
|
|
) {
|
|
let result: { [p: string]: any; } | any = {}
|
|
if (!tags) {
|
|
logger.debug(`No parent.tags or parent.inheritedTags found: ${JSON.stringify(tags)}`)
|
|
return result
|
|
}
|
|
tags.forEach(
|
|
tag => {
|
|
result = mapAttributeListToGraphQlType(result, objectTypeRequested, fieldname, {
|
|
key: tag.tag,
|
|
value: tag.value
|
|
}
|
|
)
|
|
}
|
|
)
|
|
logger.info(`Device tags mapped: ${JSON.stringify(result)}`)
|
|
|
|
return result;
|
|
}
|
|
|
|
function mapAttributeListToGraphQlType(result: {
|
|
[p: string]: any;
|
|
} | any, objectTypeRequested: boolean, fieldname: string, item: { value: any, key: string }) {
|
|
|
|
logger.debug(`Resolving ${objectTypeRequested ? "attributes of object" : "value of scalar"} field parent.${fieldname} (${result}), looking up key from item ${JSON.stringify(item)}`)
|
|
if (item.key) {
|
|
if (objectTypeRequested) {
|
|
function addRecursive(
|
|
result: { [x: string]: any; } | null,
|
|
fieldHierarchy: string[],
|
|
value: any
|
|
) {
|
|
if (!fieldHierarchy || fieldHierarchy.length == 0) {
|
|
return result
|
|
|
|
} else {
|
|
if (!result) {
|
|
result = {}
|
|
}
|
|
if (fieldHierarchy.length == 1) {
|
|
let fieldTokenName = fieldHierarchy[0];
|
|
const TOKEN_SEPERATOR = "_";
|
|
|
|
// As value is not typed we must parse the type in order to transform it to a strongly
|
|
// typed value which is expected by Graphql
|
|
// Example: Graphql does not accept a string "true" and empty string as false as boolean
|
|
// In order to facilitate this it is possible (but not mandatory) to provide typehints
|
|
// to item keys by prepending the fieldTokenName with a typehint, following by an underscore.
|
|
// I.e. if a key is prefixed with str_, bool_, float_ or json_ this will be stripped
|
|
// and the value will be cast to the appropriate type.
|
|
// If no typeHint is provided and the type is string it will be tried to create a float
|
|
// or a boolean out of it.
|
|
let typeHintToken = fieldTokenName.split(TOKEN_SEPERATOR);
|
|
let typeHint = undefined;
|
|
if (typeHintToken.length > 0) {
|
|
switch (typeHintToken[0]) {
|
|
case "str":
|
|
case "bool":
|
|
case "float":
|
|
case "json":
|
|
typeHint = typeHintToken[0];
|
|
// Remove typehint + token separator from field name - if the typehint
|
|
// is followed by another token. If not (e.g. fieldTokenName="str" only) the
|
|
// token is considered to be a valid typehint, but it is not stripped from the
|
|
// fieldTokenName (i.e. nothing happens to the fieldTokenName in that case)
|
|
if (typeHintToken.length > 1) {
|
|
fieldTokenName = fieldTokenName.substring(typeHint.length + 1);
|
|
}
|
|
}
|
|
}
|
|
let fieldValue = undefined;
|
|
|
|
if (typeof value === 'string' && (typeHint == "bool" || value.toLowerCase() === 'true' || value.toLowerCase() === 'false')) {
|
|
fieldValue = (value.toLowerCase() === 'true');
|
|
// logger.debug("Parsing attribute '" + fieldTokenName + "' as true/false string, type=" + typeof fieldValue + ", value=" + fieldValue);
|
|
} else if (typeof value === 'string' && value !== '' && !isNaN(Number(value))) {
|
|
fieldValue = Number(value);
|
|
// logger.debug("Parsing attribute '" + fieldTokenName + "' as number, type=" + typeof fieldValue + ", value=" + fieldValue);
|
|
} else if (typeof value === 'string' && typeHint == "json") {
|
|
logger.debug("Trying to parse attribute value as json, typeHint=" + typeHint + ", type=" + typeof value + ", value=" + value);
|
|
if (!value) {
|
|
// Empty string values will be considered to be an unset JSON-Object if there is a typeHin=="json"
|
|
fieldValue = undefined;
|
|
} else {
|
|
try {
|
|
fieldValue = JSON.parse(value);
|
|
// logger.debug("Parsing attribute '" + fieldTokenName + "' as json, type=" + typeof fieldValue + ", value=" + value);
|
|
} catch (e) {
|
|
logger.debug("Unable to parse attribute value as json, passing unmodified, type=" + typeof value + ", value=" + value);
|
|
fieldValue = value;
|
|
}
|
|
}
|
|
} else {
|
|
fieldValue = value;
|
|
// logger.debug("Passing attribute '" + fieldTokenName + "' unmodified, type=" + typeof fieldValue + ", value=" + fieldValue);
|
|
}
|
|
if (fieldValue !== undefined) {
|
|
// logger.debug(`length of parsed field hierarchy is 1: Setting fieldTokenName=${fieldTokenName} to ${value}`);
|
|
result[fieldTokenName] = fieldValue;
|
|
} /*else {
|
|
// logger.debug(`length of parsed field hierarchy is 1: Skipping to set fieldHierarchy=${fieldHierarchy} to ${value} (empty value)`);
|
|
}*/
|
|
} else {
|
|
result[fieldHierarchy[0]] = addRecursive(result[fieldHierarchy[0]], fieldHierarchy.slice(1), value)
|
|
}
|
|
}
|
|
logger.debug(`Adding attribute ${fieldHierarchy[0]}, result: ${JSON.stringify(result)}`)
|
|
|
|
return result
|
|
}
|
|
|
|
let fieldHierarchy = item.key.split(".");
|
|
if (fieldHierarchy[0] == fieldname) {
|
|
result = addRecursive(result, fieldHierarchy.slice(1), item.value);
|
|
logger.debug(`Detected matching item key ${fieldname} in item , result: ${JSON.stringify(result)}`)
|
|
} else {
|
|
logger.debug(`Item key ${fieldHierarchy[0]} not matched fieldname=${fieldname}, result: ${JSON.stringify(result)}`)
|
|
}
|
|
|
|
} else {
|
|
let keyInCamel = defaultKeyMappingFunction(item.key);
|
|
if (keyInCamel == fieldname) {
|
|
result = item.value
|
|
logger.debug(`Detected matching item key ${keyInCamel} in item , result: ${JSON.stringify(result)}`)
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|