328 lines
11 KiB
TypeScript
328 lines
11 KiB
TypeScript
import {ApiError} from "../schema/generated/graphql.js";
|
|
import {ZabbixAPI} from "./zabbix-api.js";
|
|
import {ApiErrorCode} from "../model/model_enum_values.js";
|
|
import {logger} from "../logging/logger.js";
|
|
import {GraphQLError} from "graphql";
|
|
|
|
class ZabbixRequestBody {
|
|
public jsonrpc = "2.0"
|
|
public method
|
|
public id = 1
|
|
public params?: ZabbixParams
|
|
|
|
constructor(method: string) {
|
|
this.method = method;
|
|
}
|
|
|
|
}
|
|
|
|
export interface ZabbixResult {
|
|
}
|
|
|
|
export type ZabbixErrorResult = {
|
|
error: ApiError
|
|
}
|
|
export const isZabbixErrorResult = (value: any): value is ZabbixErrorResult => value instanceof Object && "error" in value && !!value.error;
|
|
|
|
export interface ZabbixParams {
|
|
}
|
|
|
|
export interface ZabbixWithTagsParams extends ZabbixParams {
|
|
tags?: { tag: string; operator: number; value: any; }[]
|
|
}
|
|
|
|
export class ParsedArgs {
|
|
public name_pattern?: string
|
|
public distinct_by_name?: boolean;
|
|
public zabbix_params: ZabbixParams[] | ZabbixParams
|
|
|
|
constructor(params?: any) {
|
|
if (Array.isArray(params)) {
|
|
this.zabbix_params = params.map(arg => this.parseArgObject(arg))
|
|
} else if (params instanceof Object) {
|
|
this.zabbix_params = this.parseArgObject(params)
|
|
}
|
|
}
|
|
|
|
getParam(paramName: string): any {
|
|
if (this.zabbix_params instanceof Array) {
|
|
return undefined
|
|
}
|
|
// @ts-ignore
|
|
return paramName in this.zabbix_params ? this.zabbix_params[paramName] : undefined
|
|
}
|
|
|
|
parseArgObject(args?: Object) {
|
|
let result: ZabbixParams
|
|
if (args) {
|
|
if ("name_pattern" in args && typeof args["name_pattern"] == "string") {
|
|
if (args["name_pattern"]) {
|
|
this.name_pattern = args["name_pattern"]
|
|
}
|
|
delete args["name_pattern"]
|
|
}
|
|
if ("distinct_by_name" in args) {
|
|
this.distinct_by_name = !(!args["distinct_by_name"])
|
|
delete args["distinct_by_name"]
|
|
}
|
|
if ("groupidsbase" in args) {
|
|
if (!("groupids" in args) || !args.groupids) {
|
|
// @ts-ignore
|
|
args["groupids"] = args.groupidsbase
|
|
}
|
|
delete args.groupidsbase
|
|
}
|
|
let filterTagStatements: { tag: string; operator: number; value: any; }[] = []
|
|
let filterStatements = {}
|
|
for (let argsKey in args) {
|
|
// @ts-ignore
|
|
let argsValue = args[argsKey]
|
|
if (argsKey.startsWith("tag_") && argsValue !== null) {
|
|
let argsArray = Array.isArray(argsValue) ? argsValue : [argsValue]
|
|
argsArray.forEach((tagValue) => {
|
|
filterTagStatements.push({
|
|
"tag": argsKey.slice(4),
|
|
"operator": 1,
|
|
"value": tagValue,
|
|
})
|
|
})
|
|
// @ts-ignore
|
|
delete args[argsKey]
|
|
}
|
|
if (argsKey.startsWith("filter_") && argsValue) {
|
|
// @ts-ignore
|
|
filterStatements[argsKey.slice(7)] = argsValue
|
|
// @ts-ignore
|
|
delete args[argsKey]
|
|
}
|
|
}
|
|
|
|
if (Object.keys(filterStatements).length) {
|
|
args = {
|
|
...args,
|
|
filter: filterStatements
|
|
}
|
|
}
|
|
if (filterTagStatements?.length) {
|
|
let tagsFilter: ZabbixWithTagsParams = {
|
|
tags: filterTagStatements
|
|
}
|
|
result = {
|
|
...tagsFilter,
|
|
...args,
|
|
inheritedTags: true,
|
|
}
|
|
} else {
|
|
result = args
|
|
}
|
|
} else {
|
|
result = {}
|
|
}
|
|
|
|
if (this.name_pattern) {
|
|
if ("search" in result) {
|
|
(<any> result.search).name = this.name_pattern
|
|
} else {
|
|
(<any> result).search = {
|
|
name: this.name_pattern,
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
}
|
|
|
|
export class ZabbixRequest<T extends ZabbixResult, A extends ParsedArgs = ParsedArgs> {
|
|
protected requestBodyTemplate: ZabbixRequestBody;
|
|
protected method: string
|
|
protected prepResult: T | ZabbixErrorResult | undefined = undefined
|
|
|
|
constructor(public path: string, public authToken?: string | null, public cookie?: string | null) {
|
|
this.method = path.split(".", 2).join(".");
|
|
this.requestBodyTemplate = new ZabbixRequestBody(this.method);
|
|
}
|
|
|
|
createZabbixParams(args?: A): ZabbixParams {
|
|
return args?.zabbix_params || {}
|
|
}
|
|
|
|
getRequestBody(args?: A, zabbixParams?: ZabbixParams): ZabbixRequestBody {
|
|
let params: ZabbixParams
|
|
if (Array.isArray(args?.zabbix_params)) {
|
|
params = args?.zabbix_params.map(paramsObj => {
|
|
return {...this.requestBodyTemplate.params, ...paramsObj}
|
|
})
|
|
} else {
|
|
params = {...this.requestBodyTemplate.params, ...zabbixParams ?? this.createZabbixParams(args)}
|
|
}
|
|
return params ? {
|
|
...this.requestBodyTemplate,
|
|
params: params
|
|
} : this.requestBodyTemplate
|
|
};
|
|
|
|
headers() {
|
|
let headers: {
|
|
"Content-Type": string
|
|
Accept: string
|
|
'Access-Control-Allow-Headers': string
|
|
Cookie?: string,
|
|
Authorization?: string
|
|
} = {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
'Access-Control-Allow-Headers': 'Content-Type'
|
|
};
|
|
if (this.cookie) {
|
|
headers.Cookie = this.cookie
|
|
}
|
|
if (this.authToken) {
|
|
headers.Authorization = `Bearer ${this.authToken}`
|
|
}
|
|
return headers
|
|
}
|
|
|
|
|
|
|
|
async prepare(zabbixAPI: ZabbixAPI, _args?: A): Promise<T | ZabbixErrorResult | undefined> {
|
|
// If prepare returns something else than undefined, the execution will be skipped and the
|
|
// result returned
|
|
return this.prepResult;
|
|
}
|
|
|
|
async executeRequestReturnError(zabbixAPI: ZabbixAPI, args?: A): Promise<T | ZabbixErrorResult> {
|
|
let prepareResult = await this.prepare(zabbixAPI, args);
|
|
if (prepareResult) {
|
|
return prepareResult;
|
|
}
|
|
let requestBody = this.getRequestBody(args);
|
|
|
|
try {
|
|
|
|
const result_promise = zabbixAPI.post<T | ZabbixErrorResult>(this.path, {
|
|
body: {...requestBody}, headers: this.headers()
|
|
});
|
|
|
|
return result_promise.then(response => {
|
|
if (isZabbixErrorResult(response)) {
|
|
return response as ZabbixErrorResult;
|
|
}
|
|
return response as T;
|
|
})
|
|
} catch (e) {
|
|
const msg = `Unable to execute zabbix request body=${JSON.stringify(requestBody)}: ${JSON.stringify(e)}`
|
|
logger.error(msg)
|
|
return {
|
|
error: {
|
|
code: -1,
|
|
message: msg,
|
|
data: e
|
|
}
|
|
} as ZabbixErrorResult
|
|
}
|
|
}
|
|
|
|
async executeRequestThrowError(zabbixApi: ZabbixAPI, args?: A): Promise<T> {
|
|
let response = await this.executeRequestReturnError(zabbixApi, args);
|
|
if (isZabbixErrorResult(response)) {
|
|
throw new GraphQLError(`Called Zabbix path ${this.path} with error: ${response.error.message || "Zabbix error."} ${response.error.data}`, {
|
|
extensions: {
|
|
path: response.error.path || this.path,
|
|
args: response.error.args || args,
|
|
code: response.error.code,
|
|
data: response.error.data
|
|
},
|
|
});
|
|
} else {
|
|
return response as unknown as T;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
export class ZabbixCreateOrUpdateParams extends ParsedArgs {
|
|
constructor(args: any, public dryRun = true) {
|
|
super(args);
|
|
}
|
|
}
|
|
|
|
export class ZabbixCreateOrUpdateRequest<
|
|
T extends ZabbixResult,
|
|
P extends ZabbixRequest<ZabbixResult>,
|
|
A extends ZabbixCreateOrUpdateParams = ZabbixCreateOrUpdateParams> extends ZabbixRequest<T, A> {
|
|
constructor(public entity: string,
|
|
public updateExistingIdFieldname: string,
|
|
private prepareType: new (authToken?: string | null, cookie?: string | null) => P, authToken?: string | null, cookie?: string | null) {
|
|
super(entity + ".create.orupdate", authToken, cookie);
|
|
}
|
|
|
|
public message: string = "";
|
|
|
|
async prepare(zabbixAPI: ZabbixAPI, args?: A): Promise<ZabbixErrorResult | T | undefined> {
|
|
let prepResult = await super.prepare(zabbixAPI, args);
|
|
let nameParam = args?.getParam("name");
|
|
if (prepResult || !nameParam) {
|
|
return prepResult;
|
|
}
|
|
let existingItems = await new this.prepareType(this.authToken, this.cookie)
|
|
.executeRequestReturnError(zabbixAPI, new ParsedArgs({
|
|
filter: {
|
|
name: nameParam
|
|
}
|
|
})) as Record<string, any>[] | ZabbixErrorResult;
|
|
if (isZabbixErrorResult(existingItems)) {
|
|
this.message = "Error getting existing " + this.entity + "(s)";
|
|
this.prepResult = existingItems;
|
|
return existingItems;
|
|
}
|
|
if (existingItems.length > 1) {
|
|
this.message = "Multiple existing " + this.entity + "(s) found for existing args";
|
|
this.prepResult = {
|
|
error: {
|
|
code: ApiErrorCode.ZABBIX_MULTIPLE_USERGROUPS_FOUND,
|
|
message: this.message,
|
|
data: args,
|
|
}
|
|
}
|
|
return this.prepResult;
|
|
}
|
|
if (existingItems.length == 1) {
|
|
this.message = "Updating existing user group";
|
|
if (args?.dryRun) {
|
|
this.prepResult = {
|
|
error: {
|
|
code: ApiErrorCode.OK,
|
|
message: "Not updating existing user group, dry run enabled",
|
|
data: args,
|
|
}
|
|
}
|
|
} else {
|
|
let updateParams: Record<string, any> = {
|
|
...args?.zabbix_params
|
|
}
|
|
updateParams[this.updateExistingIdFieldname] = existingItems[0][this.updateExistingIdFieldname];
|
|
this.prepResult = await new ZabbixRequest<any>(this.entity + ".update", this.authToken, this.cookie)
|
|
.executeRequestReturnError(zabbixAPI, new ParsedArgs(updateParams));
|
|
}
|
|
} else {
|
|
this.message = "Creating " + this.entity + " - name not found";
|
|
if (args?.dryRun) {
|
|
this.prepResult = {
|
|
error: {
|
|
code: ApiErrorCode.OK,
|
|
message: "Not creating " + this.entity + ", dry run enabled",
|
|
data: args,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return this.prepResult
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|