Initial commit: Extract base Zabbix GraphQl - API functionality from VCR Project and add dynamic schema samples
This commit is contained in:
commit
92ffe71684
42 changed files with 4234 additions and 0 deletions
194
src/api/resolver_helpers.ts
Normal file
194
src/api/resolver_helpers.ts
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
import {isObjectType} from "graphql";
|
||||
import {logger} from "../logging/logger.js";
|
||||
|
||||
/*
|
||||
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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue