src/bll/utils/xmlparser.ts (116 lines of code) (raw):

import {Logger} from "./logger"; import {QueuedBuild} from "./queuedbuild"; import {Constants} from "./constants"; import {Summary} from "../entities/summary"; import {Build} from "../entities/build"; import {injectable} from "inversify"; import {Project} from "../entities/project"; import {BuildConfig} from "../entities/buildconfig"; import {parseString} from "xml2js"; import {TcNotificationMessage} from "../notifications/TcNotificationMessage"; @injectable() export class XmlParser { public async parseProjectsWithRelatedBuilds( buildsXml: string[], buildConfigFilter: (buildConfig: BuildConfig) => boolean): Promise<Project[]> { XmlParser.ensureNotEmpty(buildsXml); const projectMap: Map<string, Project> = new Map<string, Project>(); Logger.logDebug("XmlParser#parseBuilds: start collect projects"); for (const projectXml of buildsXml) { const projectData: any = await parseStringAsync(projectXml); const project: Project = XmlParser.parseProject(projectData, buildConfigFilter); projectMap.set(project.id, project); } XmlParser.buildProjectHierarchy(projectMap); const result: Project[] = projectMap.get(Constants.ROOT_PROJECT_ID).children; Logger.logDebug("XmlParser#parseBuilds: collected projects:"); Logger.LogObject(result); return result; } private static ensureNotEmpty(data?: string[]) { if (!data) { throw new Error("Related data were not found."); } } private static parseProject(projectData: any, filterBuildConfig: (buildConfig: BuildConfig) => boolean): Project { const parentId = projectData.Project.myParentProjectId ? projectData.Project.myParentProjectId[0] : undefined; const project = new Project(projectData.Project.myProjectId[0], parentId, projectData.Project.name[0]); if (projectData.Project.configs && projectData.Project.configs[0]) { const buildConfigs: BuildConfig[] = XmlParser.parseConfigurations(projectData.Project.configs[0].Configuration, filterBuildConfig); buildConfigs.forEach((config) => project.addChildBuildConfig(config)); } return project; } private static parseConfigurations(configDataArray: Array<any>, filterBuildConfig: (buildConfig: BuildConfig) => boolean): BuildConfig[] { const buildConfigs: BuildConfig[] = []; for (const configData of configDataArray) { const buildConfig = new BuildConfig(configData.id[0], configData.myExternalId[0], configData.name[0]); if (filterBuildConfig(buildConfig)) { buildConfigs.push(buildConfig); } } return buildConfigs; } private static buildProjectHierarchy(projectMap: Map<string, Project>) { projectMap.forEach((project) => { if (project.parentId && projectMap.get(project.parentId)) { projectMap.get(project.parentId).addChildProject(project); } }); } public async parseRestBuild(buildXml: string): Promise<Build> { if (buildXml === undefined) { Logger.logWarning("XmlParser#parseRestBuild: buildXml is empty"); return; } Logger.logDebug("XmlParser#parseRestBuild: start collect projects"); const buildObj: any = await parseStringAsync(buildXml); return Build.fromRestParsedObject(buildObj); } public async parseSummary(summeryXmlObj: string): Promise<Summary> { const dataObj: any = await parseStringAsync(summeryXmlObj); return Summary.fromXmlRpcObject(dataObj); } public async parseQueuedBuild(queuedBuildInfoXml: string): Promise<QueuedBuild> { const queuedBuildObj: any = await parseStringAsync(queuedBuildInfoXml); return queuedBuildObj.build.$; } public async parseBuildStatus(buildInfoXml: string): Promise<string> { const buildInfoObj: any = await parseStringAsync(buildInfoXml); if (!(buildInfoObj.build.$.state === "finished" && buildInfoObj.build.$.status)) { return undefined; } return buildInfoObj.build.$.status; } public async parseNotificationMessage(messageXml: string): Promise<TcNotificationMessage> { const wrapRoute: string = "jetbrains.buildServer.notification.NotificationMessage"; const resultWrapper: any = await parseStringAsync(messageXml, false); if (!resultWrapper.hasOwnProperty(wrapRoute)) { return Promise.reject("Result doesn't contain notification message"); } else { return resultWrapper[wrapRoute]; } } } /*exported only for tests*/ export async function parseStringAsync(data: string, explicitArray: boolean = true): Promise<any> { return new Promise<any>((resolve, reject) => { parseString(data, {explicitArray: explicitArray, validator: preparator}, (err, result) => { if (err) { return reject(err); } else { return resolve(result); } }); }); } function preparator(xpath, currentValue, newValue) { if (!isNaN(newValue)) { return Number(newValue); } else if (newValue === "false" || newValue === "true") { return newValue === "true"; } else { return newValue; } }