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 , buildConfigFilter ) { XmlParser . ensureNotEmpty ( buildsXml ) ; const projectMap = new Map < string , Project > ( ) ; Logger . logDebug ( "XmlParser#parseBuilds: start collect projects" ) ; for ( const projectXml of buildsXml ) { const projectData = await parseStringAsync ( projectXml ) ; const project = XmlParser . parseProject ( projectData , buildConfigFilter ) ; projectMap . set ( project . id , project ) ; } XmlParser . buildProjectHierarchy ( projectMap ) ; const result = projectMap . get ( Constants . ROOT_PROJECT_ID ) . children ; Logger . logDebug ( "XmlParser#parseBuilds: collected projects:" ) ; Logger . LogObject ( result ) ; return result ; } private static ensureNotEmpty ( data ? ) { if ( ! data ) { throw new Error ( "Related data were not found." ) ; } } private static parseProject ( projectData , filterBuildConfig ) { 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 = XmlParser . parseConfigurations ( projectData . Project . configs [ 0 ] . Configuration , filterBuildConfig ) ; buildConfigs . forEach ( ( config ) => project . addChildBuildConfig ( config ) ) ; } return project ; } private static parseConfigurations ( configDataArray , filterBuildConfig ) { const buildConfigs = [ ] ; 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 ) { projectMap . forEach ( ( project ) => { if ( project . parentId && projectMap . get ( project . parentId ) ) { projectMap . get ( project . parentId ) . addChildProject ( project ) ; } } ) ; } public async parseRestBuild ( buildXml ) { if ( buildXml === undefined ) { Logger . logWarning ( "XmlParser#parseRestBuild: buildXml is empty" ) ; return ; } Logger . logDebug ( "XmlParser#parseRestBuild: start collect projects" ) ; const buildObj = await parseStringAsync ( buildXml ) ; return Build . fromRestParsedObject ( buildObj ) ; } public async parseSummary ( summeryXmlObj ) { const dataObj = await parseStringAsync ( summeryXmlObj ) ; return Summary . fromXmlRpcObject ( dataObj ) ; } public async parseQueuedBuild ( queuedBuildInfoXml ) { const queuedBuildObj = await parseStringAsync ( queuedBuildInfoXml ) ; return queuedBuildObj . build . $ ; } public async parseBuildStatus ( buildInfoXml ) { const buildInfoObj = await parseStringAsync ( buildInfoXml ) ; if ( ! ( buildInfoObj . build . $ . state === "finished" && buildInfoObj . build . $ . status ) ) { return undefined ; } return buildInfoObj . build . $ . status ; } public async parseNotificationMessage ( messageXml ) { const wrapRoute = "jetbrains.buildServer.notification.NotificationMessage" ; const resultWrapper = await parseStringAsync ( messageXml , false ) ; if ( ! resultWrapper . hasOwnProperty ( wrapRoute ) ) { return Promise . reject ( "Result doesn't contain notification message" ) ; } else { return resultWrapper [ wrapRoute ] ; } } } export async function parseStringAsync ( data , explicitArray = true ) { 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 ; } }