From a0c8254ec9f3b1c6d7f2bb9af54aebd75535c329 Mon Sep 17 00:00:00 2001 From: Pravin Barton <9560941+isc-pbarton@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:32:42 -0500 Subject: [PATCH 1/3] fix: parsing of nested CDATA in PTD items --- CHANGELOG.md | 3 ++ cls/SourceControl/Git/Production.cls | 48 ++++++++++++++++------------ 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84749d85..68177c39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Menu option to export production to support migrating to production decomposition (#665) +### Fixed +- Fixed errors on production page when item settings need to be XML escaped (#667) + ## [2.8.0] - 2024-12-06 ### Added diff --git a/cls/SourceControl/Git/Production.cls b/cls/SourceControl/Git/Production.cls index 1ffbe553..23c48471 100644 --- a/cls/SourceControl/Git/Production.cls +++ b/cls/SourceControl/Git/Production.cls @@ -62,7 +62,7 @@ ClassMethod DeleteProductionDefinitionShards(productionClass As %String, deleteM while rs.%Next(.sc) { quit:$$$ISERR(sc) set ptdFilename = rs.Data("Name") - set sc = ##class(%Studio.SourceControl.Production).ParseExternalName(ptdFilename, .ptdInternalName) + set sc = ##class(SourceControl.Git.Production).ParseExternalName(ptdFilename, .ptdInternalName) quit:$$$ISERR(sc) // TODO: Consider reverting delete if any ptd is not editable by current user set sc = $method(%SourceControl, deleteMethod, ptdInternalName) @@ -371,26 +371,32 @@ ClassMethod IsProductionClass(className As %String, nameMethod As %String) As %B ClassMethod ParseExternalName(externalName, Output internalName = "", Output productionName = "") As %Status { set sc = $$$OK - set extNameNormalized = $replace(externalName, "\", "/") - set file = $piece(extNameNormalized, "/", *) - if $extract(file,1,9) = "ProdStgs-" { - set productionName = $replace($extract(file,10,*-4), "_", ".") - set internalName = ..CreateInternalName(productionName,,,1) - } else { - if ##class(%File).Exists(externalName) { - // Special case for Config Item Settings PTD, requires checking PTD CDATA for Item and Class name - set deployDoc = ##class(EnsLib.EDI.XML.Document).%New(externalName) - set exportNotesPTDText = $ZCVT(deployDoc.GetValueAt("/Export/Document[1]/1"),"I","XML") - set exportNotesPTD = ##class(EnsLib.EDI.XML.Document).%New(exportNotesPTDText) - set productionName = exportNotesPTD.GetValueAt("/Deployment/Creation/SourceProduction") - set settingsPTDText = $zconvert(deployDoc.GetValueAt("/Export/Document[2]/1"),"I","XML") - set settingsPTD = ##class(EnsLib.EDI.XML.Document).%New(settingsPTDText) - set itemClass = settingsPTD.GetValueAt("/Item/@ClassName") - set itemName = settingsPTD.GetValueAt("/Item/@Name") - set internalName = ..CreateInternalName(productionName, itemName, itemClass, 0) - } else { - set sc = $$$ERROR($$$GeneralError, "Item settings PTD file " _ externalName _ " does not exist. Cannot parse external name.") - } + try { + set extNameNormalized = $replace(externalName, "\", "/") + set file = $piece(extNameNormalized, "/", *) + if $extract(file,1,9) = "ProdStgs-" { + set productionName = $replace($extract(file,10,*-4), "_", ".") + set internalName = ..CreateInternalName(productionName,,,1) + } else { + if ##class(%File).Exists(externalName) { + // Special case for Config Item Settings PTD, requires checking PTD CDATA for Item and Class name + set deployDoc = ##class(EnsLib.EDI.XML.Document).%New(externalName) + set exportNotesPTDText = $ZCVT(deployDoc.GetValueAt("/Export/Document[1]/1"),"I","XML") + set exportNotesPTD = ##class(EnsLib.EDI.XML.Document).%New(exportNotesPTDText) + set productionName = exportNotesPTD.GetValueAt("/Deployment/Creation/SourceProduction") + set settingsPTDText = $zconvert(deployDoc.GetValueAt("/Export/Document[2]/1"),"I","XML") + // unquote embedded CDATA close markers - see Ens.Util.ProjectTextDocument StreamToGbl method + set settingsPTDText = $replace(settingsPTDText,"]*]>", "]]>") + set settingsPTD = ##class(EnsLib.EDI.XML.Document).%New(settingsPTDText) + set itemClass = settingsPTD.GetValueAt("/Item/@ClassName") + set itemName = settingsPTD.GetValueAt("/Item/@Name") + set internalName = ..CreateInternalName(productionName, itemName, itemClass, 0) + } else { + set sc = $$$ERROR($$$GeneralError, "Item settings PTD file " _ externalName _ " does not exist. Cannot parse external name.") + } + } + } catch err { + set sc = err.AsStatus() } return sc } From db1e65fede3d9957f9902fd5c7fc522f020bcad8 Mon Sep 17 00:00:00 2001 From: Pravin Barton <9560941+isc-pbarton@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:46:32 -0500 Subject: [PATCH 2/3] test: production decomp with escaped CDATA --- .../SourceControl/Git/ProductionDecomposition.cls | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/UnitTest/SourceControl/Git/ProductionDecomposition.cls b/test/UnitTest/SourceControl/Git/ProductionDecomposition.cls index 3ca53fe2..1a4d5fef 100644 --- a/test/UnitTest/SourceControl/Git/ProductionDecomposition.cls +++ b/test/UnitTest/SourceControl/Git/ProductionDecomposition.cls @@ -82,10 +82,10 @@ Method TestEditProduction() $$$ThrowOnError(%SourceControl.OnBeforeSave(..#ProductionName_".cls")) do ..ReplaceProductionDefinition("ProductionDefinition3") $$$ThrowOnError(%SourceControl.OnAfterSave(..#ProductionName_".cls")) - do $$$AssertTrue(##class(SourceControl.Git.Utils).IsInSourceControl("UnitTest.SampleProduction||Settings-b|Ens.Activity.Operation.Local.PTD")) + do $$$AssertTrue(##class(SourceControl.Git.Utils).IsInSourceControl("UnitTest.SampleProduction||Settings-b|EnsLib.SOAP.GenericOperation.PTD")) do ##class(SourceControl.Git.Utils).RunGitCommand("add",,,".") do ##class(SourceControl.Git.Utils).Commit("UnitTest.SampleProduction||Settings-a|Ens.Activity.Operation.Local.PTD") - do ##class(SourceControl.Git.Utils).Commit("UnitTest.SampleProduction||Settings-b|Ens.Activity.Operation.Local.PTD") + do ##class(SourceControl.Git.Utils).Commit("UnitTest.SampleProduction||Settings-b|EnsLib.SOAP.GenericOperation.PTD") $$$ThrowOnError(production.%Reload()) do $$$AssertEquals(production.Items.Count(), 2) do $$$AssertEquals(production.Items.GetAt(1).Settings.GetAt(1).Name, "RecordStatsInterval") @@ -135,7 +135,8 @@ XData ProductionDefinition2 61 - + + ]]> } @@ -147,7 +148,8 @@ XData ProductionDefinition3 71 - + + ]]> } From 13b474da912ce4f1d609f453f833f1d8b978d84a Mon Sep 17 00:00:00 2001 From: Pravin Barton <9560941+isc-pbarton@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:13:14 -0500 Subject: [PATCH 3/3] refactor: compatibility with %Studio.SourceControl.Production --- cls/SourceControl/Git/Production.cls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cls/SourceControl/Git/Production.cls b/cls/SourceControl/Git/Production.cls index 23c48471..1d2f98be 100644 --- a/cls/SourceControl/Git/Production.cls +++ b/cls/SourceControl/Git/Production.cls @@ -62,7 +62,7 @@ ClassMethod DeleteProductionDefinitionShards(productionClass As %String, deleteM while rs.%Next(.sc) { quit:$$$ISERR(sc) set ptdFilename = rs.Data("Name") - set sc = ##class(SourceControl.Git.Production).ParseExternalName(ptdFilename, .ptdInternalName) + set sc = ..ParseExternalName(ptdFilename, .ptdInternalName) quit:$$$ISERR(sc) // TODO: Consider reverting delete if any ptd is not editable by current user set sc = $method(%SourceControl, deleteMethod, ptdInternalName)