diff --git a/CacheUpdater/Task.cls.xml b/CacheUpdater/Task.cls.xml deleted file mode 100644 index e0da71c..0000000 --- a/CacheUpdater/Task.cls.xml +++ /dev/null @@ -1,697 +0,0 @@ - - - -%SYS.Task.Definition,CacheUpdater.UDL -64173,65712.36031 -63603,52252.541311 - - -GitHub Update - - - - -Repository URL, like https://github.com/intersystems-ru/Cache-MDX2JSON -Increased to 500 to support long urls -%String - - - - - -Note, that with Username/Password, you can make up to 5,000 requests per hour. -For unauthenticated requests, the rate limit allows to make up to 60 requests per hour. -Unauthenticated requests are associated with an IP address.
-Required, if you want to create webhooks]]>
-%String -
- - - -GitHub password, corresponding to Username. Optional for public repositories. -%String - - - - -Namespace, where to download and compile repository -%String -$Namespace - - - - -Repository branch, usually master. Leave empty, if you want to receive default branch. -%String -"master" - - - -%Status - - - - - -Owner - The name of the repository owner.
-Repository - The name of the repository.
-Branch - The name of the commit/branch/tag. If skipped the repository’s default branch (usually master) would be used.
-Username - GitHub user, who has access to repository. Optional for public repositories.
-Password - GitHub password, corresponding to Username. Optional for public repositories.
-Note, that with Username, you can make up to 5,000 requests per hour. -For unauthenticated requests, the rate limit allows to make up to 60 requests per hour. -Unauthenticated requests are associated with an IP address.
-Namespace - Namespace, where to download and compile repository.
- -For example in the repository: https://github.com/intersystems-ru/Cache-MDX2JSON
-Owner - intersystems-ru, Repository - Cache-MDX2JSON.
]]>
-1 -Owner:%String,Repository:%String,Branch:%String="",Username:%String="",Password:%String="",Namespace=$Namespace -%Status - -
- - - -Path -Internal repository path. Root is empty string
-Request - Authenticated/Set %Net.HttpRequest object.
-Links - List of links to raw files (which satisfy IsCacheFile conditions) from repository.
]]>
-1 - -%Status - -
- - - -Check that incoming file is the one you need. -1 -File:%ZEN.proxyObject -%Boolean - - - - - -Links - List of links to raw files.
-Request - Authenticated/Set %Net.HttpRequest object.
-loadedlist - Returns an array of the items loaded. ]]>
-1 -Links:%ListOfDataTypes,Request:%Net.HttpRequest,*Items -%Status - -
- - -1 -Username:%String,Password:%String -%Net.HttpRequest - - -
- - - -%RegisteredObject -64173,65465.415142 -64161,48850.325593 - - -stream - stream which contains file definition]]> -1 -stream:%GlobalCharacterStream -%Boolean -= 10 { - quit - } - set line = stream.ReadLine() - if $find(line, " - - - -line - any string.
]]>
-1 -line:%String -%String -W") - return $piece(trimmed, " ") -]]> -
- - -stream - stream which contains a class definition
-name - name which contains the name of class
]]>
-1 - -%Status - -
- - -stream - stream which contains a routine definition
-name - name which contains the name of routine
-type - type of file {1 - mac, 2 - inc, 3 - int}]]>
-1 - -%Status - -
- - -stream - stream which contains a dfi definition
-name - name which contains the name of dfi
]]>
-1 - -%Status - -
- - -url - the url where the file is located in the web.
]]>
-1 -url:%String -%String - -
- - -ext - extensions of the file
]]>
-1 -ext:%String -%String - -
- - -contentStream - the stream which contains the source code in udl format.
-url - the url where the file is located in the web.
-list - array of files to compile
]]>
-1 -contentStream:%GlobalCharacterStream,binaryStream:%Stream.FileCharacterGzip,url:%String,list:%String -%Status - -
- - -className - name of the class.
]]>
-1 -className:%String -%Boolean - 0) { - Return $$$YES - } - Return $$$NO -]]> -
- - -contentStream - the stream which contains the source code in udl format.
-url - the url where the file is located in the web.
-list - array of files to compile
]]>
-1 - -%Status - -
- - -contentStream - the stream which contains the source code in udl format.
-list - array of files to compile
]]>
-1 - -%Status - -
- - -contentStream - the stream which contains the source code in udl format.
-url - the url where the file is located in the web.
-list - array of files to compile
]]>
-1 - -%Status - -
- - -contentStream - the stream which contains the source code in udl format.
-url - the url where the file is located in the web.
-ext - extension of the file
-list - array of files to compile
]]>
-1 - -%Status - -
-
-
diff --git a/README.md b/README.md index dfa1536..b717ba5 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,33 @@ Sync GitHub repositories into InterSystems Cache. Installation ----------- -1. Download Task.cls.xml and import it into Caché (any namespace, further referred to as {Namespace}). +1. Download the latest Release .xml file and import it into Caché (any namespace, further referred to as {Namespace}). Usage ----------- -To create task for syncing GitHub repository → Cache instance do the following: +Call + + w ##class(CacheUpdater.Task).Update("Owner", "Repository", "Branch", "Username", "Password", "Namespace") + +to download and compile classes from the Github repo. + +Where
+Owner - The name of the repository owner.
+Repository - The name of the repository.
+Branch - The name of the commit/branch/tag. If skipped the repository’s default branch (usually master) would be used.
+Username - GitHub user, who has access to repository. Optional for public repositories.
+Password - GitHub password, corresponding to Username. Optional for public repositories.
+Note, that with Username, you can make up to 5,000 requests per hour. +For unauthenticated requests, the rate limit allows to make up to 60 requests per hour. +Unauthenticated requests are associated with an IP address.
+Namespace - Namespace, where to download and compile repository.
+For example in the repository: https://github.com/intersystems-ru/Cache-MDX2JSON
+Owner - intersystems-ru, Repository - Cache-MDX2JSON.
+ +OR + +create task for syncing GitHub repository → Cache instance do the following: 1. Go to SMP → System Operation → Task Manager → New Task 2. Set Name as desired @@ -24,10 +45,6 @@ To create task for syncing GitHub repository → Cache instance do the followin After task runs at least once you will get GitHubURL repository contents in Namespace -Note that in order to sync CLS, MAC, INT, INC and DFI files they need to be stored
-at corresponding folders. For example:
-for CLS: cls/packagename/file.cls
-for MAC: mac/file.mac Continuous Integration ----------- diff --git a/sc-list.txt b/sc-list.txt deleted file mode 100644 index b0e7fa9..0000000 --- a/sc-list.txt +++ /dev/null @@ -1,2 +0,0 @@ -CacheUpdater.Task.CLS -GitHubUpdater.Task.CLS diff --git a/src/cls/CacheUpdater/Task.cls b/src/cls/CacheUpdater/Task.cls new file mode 100755 index 0000000..3b442b5 --- /dev/null +++ b/src/cls/CacheUpdater/Task.cls @@ -0,0 +1,246 @@ +Class CacheUpdater.Task Extends (%SYS.Task.Definition, CacheUpdater.UDL) +{ + +Parameter TaskName = "GitHub Update"; + +/// Repository URL, like https://github.com/intersystems-ru/Cache-MDX2JSON +/// Increased to 500 to support long urls +Property GitHubURL As %String(MAXLEN = 500); + +/// GitHub user, who has access to repository. Optional for public repositories.
+/// Note, that with Username/Password, you can make up to 5,000 requests per hour. +/// For unauthenticated requests, the rate limit allows to make up to 60 requests per hour. +/// Unauthenticated requests are associated with an IP address.
+/// Required, if you want to create webhooks +Property Username As %String; + +/// GitHub password, corresponding to Username. Optional for public repositories. +Property Password As %String; + +/// Namespace, where to download and compile repository +Property Namespace As %String [ InitialExpression = {$Namespace} ]; + +/// Repository branch, usually master. Leave empty, if you want to receive default branch. +Property Branch As %String [ InitialExpression = "master" ]; + +Method OnTask() As %Status +{ + Return:'##class(%SYS.Namespace).Exists(..Namespace) $$$ERROR($$$NamespaceUnavailable,..Namespace) + + Set Owner = $p(..GitHubURL,"/",4) + Set Repository = $p(..GitHubURL,"/",5) + + Return ..Update(Owner, Repository, ..Branch, ..Username, ..Password, ..Namespace) +} + +/// Downloads and compiles GitHub repository.
+/// Owner - The name of the repository owner.
+/// Repository - The name of the repository.
+/// Branch - The name of the commit/branch/tag. If skipped the repository’s default branch (usually master) would be used.
+/// Username - GitHub user, who has access to repository. Optional for public repositories.
+/// Password - GitHub password, corresponding to Username. Optional for public repositories.
+/// Note, that with Username, you can make up to 5,000 requests per hour. +/// For unauthenticated requests, the rate limit allows to make up to 60 requests per hour. +/// Unauthenticated requests are associated with an IP address.
+/// Namespace - Namespace, where to download and compile repository.
+/// +/// For example in the repository: https://github.com/intersystems-ru/Cache-MDX2JSON
+/// Owner - intersystems-ru, Repository - Cache-MDX2JSON.
+ClassMethod Update(Owner As %String, Repository As %String, Branch As %String = "", Username As %String = "", Password As %String = "", Namespace = {$Namespace}) As %Status +{ + #dim req As %Net.HttpRequest + Set req = ..CreateRequest(Username, Password) + Set req.Location = "repos/" _ Owner _ "/" _ Repository _ "/contents" // as described in https://developer.github.com/v3/repos/ + + Set links = ##class(%ListOfDataTypes).%New() + Set st = ..ProcessDirectory("",.req,Branch,.links) + Return:$$$ISERR(st) st + + Set namespace = $Namespace + Zn Namespace + Set st = ..DownloadFiles(links,req,.list) + zw list + Set st2 = $system.OBJ.CompileList(.list,"cuk /checkuptodate=expandedonly") + Zn namespace + + Return $$$ADDSC(st, st2) +} + +/// Process one directory of GitHub repository. Recursive.
+/// Path -Internal repository path. Root is empty string
+/// Request - Authenticated/Set %Net.HttpRequest object.
+/// Links - List of links to raw files (which satisfy IsCacheFile conditions) from repository.
+ClassMethod ProcessDirectory(Path As %String = "", Request As %Net.HttpRequest, Branch As %String = "", ByRef Links As %ListOfDataTypes) As %Status +{ + Set location = Request.Location + Set Request.Location = Request.Location _ Path + Do:(Branch'="") Request.SetParam("ref",Branch) + + Set st = Request.Get() + + Return:$$$ISERR(st) st + Return:(Request.HttpResponse.StatusCode = 404) $$$ERROR($$$GeneralError,"Repository doesn't exist OR you don't have access") + Return:((Request.HttpResponse.StatusCode = 403) && (Request.HttpResponse.GetHeader("X-RATELIMIT-REMAINING")=0)) $$$ERROR($$$GeneralError,"API rate limit exceeded. Try logging in.") + Return:(Request.HttpResponse.StatusCode = 401) $$$ERROR($$$GeneralError,"We couldn't find that combination of username and password") + Return:(Request.HttpResponse.StatusCode '= 200) $$$ERROR($$$GeneralError,"Received " _ Request.HttpResponse.StatusCode _ " expected 200") + + #dim objects As List of %ZEN.proxyObject + #dim obj As %ZEN.proxyObject + Set st = ##class(%ZEN.Auxiliary.jsonProvider).%ConvertJSONToObject(Request.HttpResponse.Data,,.objects,1) + Return:$$$ISERR(st) st + + For i = 1:1:objects.Count() { + Set obj = objects.GetAt(i) + If (obj.type = "dir") { + Set st = ..ProcessDirectory("/"_obj.name,Request,Branch,.Links) + Return:$$$ISERR(st) st + } ElseIf (obj.type = "file") { + //Do:..IsCacheFile(obj) Links.Insert(obj."download_url") + Do Links.Insert($LB(obj."download_url",..IsCacheFile(obj))) + } Else { + // obj.type = "symlink" or obj.type = "submodule" + } + } + Set Request.Location = location // to keep track of where in the repository tree we are + Return $$$OK +} + +/// Check that incoming file is the one you need. +ClassMethod IsCacheFile(File As %ZEN.proxyObject) As %Boolean +{ + Set extensions = ",xml,cls,csp,csr,mac,int,bas,inc,gbl,prj,obj,pkg,gof,dfi,pivot,dashboard,html,css,js,ts,scss," + Return:($L(File.name,".")=1) 0 //no extension + Set File.Extension = $P(File.name,".",$L(File.name,".")) + Return $F(extensions,","_$ZCVT(File.Extension,"l")_",") +} + +/// Download list of files on https://raw.githubusercontent.com/ server.
+/// Links - List of links to raw files.
+/// Request - Authenticated/Set %Net.HttpRequest object.
+/// loadedlist - Returns an array of the items loaded. +ClassMethod DownloadFiles(Links As %ListOfDataTypes, Request As %Net.HttpRequest, Output Items) As %Status +{ + Kill Items + Set Request.Server = "raw.githubusercontent.com" + Set st = $$$OK + Try + { + For i = 1:1:Links.Count() + { + Set link = $ListGet(Links.GetAt(i),1) + Set bIsCacheFile = $ListGet(Links.GetAt(i),2) + Set ^gitfiles(i,"link")=link + Set ^gitfiles(i,"bIsCacheFile")=bIsCacheFile + + Set streq = Request.Get($e(link,35,*)) // Remove "https://raw.githubusercontent.com/" from URL. + If $$$ISERR(streq) + { + Set st=$$$ADDSC(st, streq) + Set ^gitfiles(i,"streq")=streq + Continue + } + + Set ^gitfiles(i,"stream")="starting..." + Set binarystream = Request.HttpResponse.Data + + Do binarystream.Rewind() // just in case + + Set characterStream=##class(%GlobalCharacterStream).%New() //translating binary stream into character stream + Set stTranslate=$$$OK + Try + { + While 'binarystream.AtEnd + { + //Use eol to prevent breaking lines larger than 32Kb + Set line=binarystream.ReadLine(, .stTranslate, .eol) + Quit:$System.Status.IsError(stTranslate) + + If eol + { + Set stTranslate=characterStream.WriteLine(line) + } + Else + { + Set stTranslate=characterStream.Write(line) + } + Quit:$System.Status.IsError(stTranslate) + } + Quit:$System.Status.IsError(stTranslate) + + Do characterStream.Rewind() + } + Catch (oTranslateStreamException) + { + Set stTranslate=oTranslateStreamException.AsStatus() + } + + If $System.Status.IsError(stTranslate) + { + //Could not convert binary stream to character stream + //It is probably a binary file anyway + Set characterStream="" + Set st=$$$ADDSC(st, stTranslate) + Set ^gitfiles(i,"stTranslate")=stTranslate + } + Set ^gitfiles(i,"stream")="Done" + + Do binarystream.Rewind() + + Set stload = $$$OK + + set items = "" + If ('$IsObject(characterStream)) || (..IsUDLFile(characterStream)) + { + Set ^gitfiles(i,"IsUDLFile")="1" + Set stload = ..LoadUDLFile(characterStream, binarystream, link, .items) + } + ElseIf bIsCacheFile + { + Set ^gitfiles(i,"IsUDLFile")="0" + Set stload = $system.OBJ.LoadStream(characterStream,"",.error,.items,,,,"UTF8") + } + Set ^gitfiles(i,"stload")=stload + If $$$ISERR(stload) + { + Set st=$$$ADDSC(st, stload) + Continue + } + Merge Items = items // Does not overwrite existing array keys: Items(itemname)="" + } + + Set Request.Server="api.github.com" + } + Catch (oException) + { + Set st = oException.AsStatus() + If $D(i) Set ^gitfiles(i,"st final")=st + } + + Quit st +} + +ClassMethod CreateRequest(Username As %String, Password As %String) As %Net.HttpRequest +{ + Set namespace = $Namespace + Set SSLConfig = "GitHub" + + Zn "%SYS" + Do:'##class(Security.SSLConfigs).Exists(SSLConfig) ##class(Security.SSLConfigs).Create(SSLConfig) + Zn namespace + + Set req=##class(%Net.HttpRequest).%New() + Set req.Https=1 + Set req.SSLConfiguration=SSLConfig + Set req.Server="api.github.com" + Do req.SetHeader("Accept","application/vnd.github.v3+json") // we want 3rd version of api + + If ($d(Username) && $d(Password) && (Username'="") && (Password'="")) { // supply Username and Password, if both are provided. GitHub accept Basic Auth + Set req.Username = Username // https://developer.github.com/v3/auth/ + Set req.Password = Password + } + + Return req +} + +} + diff --git a/src/cls/CacheUpdater/TextServices.cls b/src/cls/CacheUpdater/TextServices.cls new file mode 100644 index 0000000..64cd75e --- /dev/null +++ b/src/cls/CacheUpdater/TextServices.cls @@ -0,0 +1,48 @@ +Class CacheUpdater.TextServices Extends %Compiler.UDL.TextServices +{ + +/// This method takes a namespace an integer subscripted array containing lines of text which represent a +/// class definition in the UDL class definition language. Subscript value 0 should contain a count +/// of lines of text which are defined as subscript value 1 ... n in the array +/// +/// Unlike %Compiler.UDL.TextServices saves classes with grammar errors. Taken from Atelier API +/// +/// It is important to realize that this method will replace the existing class definition if present and therefore +/// must contain a full representation of the class as can be obtained by calling the GetClassXXX() method(s) in +/// this class. Note: The name of the class is derived from the name of the class defined within the text +ClassMethod SetTextFromArray(pNamespace As %String = {$namespace}, pClassname As %String, ByRef pDocumentArray As %String) As %Status +{ + #dim tSC,tStatus As %Status = $$$OK + #dim e As %Exception.AbstractException + + #dim tErrList,tOneErr As %String + #dim tResultCode,tI As %Integer + + Try { + #; TODO: make sure pClassname and classname within the text match, else throw an error + #; Remember pClassname has .cls extension! + + #; Swap namespace if necessary + If pNamespace'=$namespace new $namespace Set $namespace=pNamespace + + #; Save the definition (just saves, doesn't compile) + Set tFlags=16777216 ; 0x01000000 = IPARSE_UDL_SAVEWITHERRORS save even if parse errors + + Set tResultCode=$compile(pDocumentArray,128,tErrList,,,tFlags) + If tResultCode { + For tI=1:1:$ll(tErrList) { + Set tOneErr = $list(tErrList,tI),tStatus=$$$ERROR($$$ClassSaveError,$li(tOneErr,4),$li(tOneErr,1),$li(tOneErr,2),$li(tOneErr,6)) + If tSC=$$$OK { + Set tSC=tStatus + } else { + Set tSC=$$$ADDSC(tSC,tStatus) + } + } + } + } Catch (e) { + Set tSC=e.AsStatus() + } + Quit tSC +} + +} diff --git a/src/cls/CacheUpdater/UDL.cls b/src/cls/CacheUpdater/UDL.cls new file mode 100755 index 0000000..aa7c61c --- /dev/null +++ b/src/cls/CacheUpdater/UDL.cls @@ -0,0 +1,338 @@ +Class CacheUpdater.UDL Extends %RegisteredObject +{ + +Parameter NamespacePar As %String = {$Namespace}; + +/// Checks whether this file is in UDL format +/// stream - stream which contains file definition +ClassMethod IsUDLFile(stream As %GlobalCharacterStream) As %Boolean +{ + // probably 10 lines is enough + set counter = 0 + while 'stream.AtEnd { + if counter >= 10 { + quit + } + set line = stream.ReadLine() + if $find(line, "line - any string.
+ClassMethod ReadName(line As %String) As %String +{ + set trimmed = $zstrip(line, "<>W") + return $piece(trimmed, " ") +} + +/// Finds a name of a class +/// stream - stream which contains a class definition
+/// name - name which contains the name of class
+ClassMethod GetClassName(stream As %GlobalCharacterStream, ByRef name As %String) As %Status +{ + while 'stream.AtEnd { + set line = stream.ReadLine() + + if $extract(line, 1, 3) = "///" { // check for inline comments + continue + } elseif $zconvert($extract(line, 1, 5), "l") = "class" { + set line = $extract(line, 6, *) + set name = ..ReadName(line) + if name = "" { + return '$$$OK + } else { + return $$$OK + } + } + } + return '$$$OK +} + +/// Finds a name of a routine +/// stream - stream which contains a routine definition
+/// name - name which contains the name of routine
+/// type - type of file {1 - mac, 2 - inc, 3 - int} +ClassMethod GetRoutineName(stream As %GlobalCharacterStream, ByRef name As %String, ByRef type As %Integer) As %Status +{ + while 'stream.AtEnd { + set line = stream.ReadLine() + set index = $find(line, "ROUTINE") + // TODO - check whether the name on the next line + // or something is between ROUTINE and name + if index { + if $find(line, "[Type=INC]") { + set type = 2 + } + elseif $find(line, "[Type=INT,Generated]") { + set type = 3 + } + else { + set type = 1 + } + set line = $extract(line, index, *) + set name = ..ReadName(line) + if name = "" { + return '$$$OK + } else { + return $$$OK + } + } + } + return '$$$OK +} + +/// Finds a name of a dfi +/// stream - stream which contains a dfi definition
+/// name - name which contains the name of dfi
+ClassMethod GetDFIName(stream As %GlobalCharacterStream, ByRef name As %String) As %Status +{ + #dim textreader As %XML.TextReader + set dfiContent = "" + + // I don't know why but if i just parse stream it doesn't work + while 'stream.AtEnd { + set dfiContent = dfiContent _ stream.Read() + } + + set st = ##class(%XML.TextReader).ParseString(dfiContent, .textreader) + return:$$$ISERR(st) st + + while textreader.Read() { + set node = textreader.Name + if (node = "pivot") || (node = "dashboard") { + do textreader.MoveToAttributeName("folderName") + // set dfiFolderName = $translate(textreader.Value, " ", "-") + set dfiFolderName=textreader.Value + + do textreader.MoveToAttributeName("name") + // set dfiName = $translate(textreader.Value, " ", "-") + set dfiName=textreader.Value + set name = dfiFolderName _ "-" _ dfiName _ "." _ node _ ".dfi" + return $$$OK + } + } + return '$$$OK +} + +/// Get extension of the file by url +/// url - the url where the file is located in the web.
+ClassMethod GetExt(url As %String) As %String +{ + //return $zconvert($piece(url, ".", *), "l") + //AMIR: There are parameters after the extension that are not part of the extension + return $zconvert($piece($piece(url, ".", *),"?"), "l") +} + +/// Check whether a file is a web file +/// ext - extensions of the file
+ClassMethod IsWebFile(ext As %String) As %String +{ + set webExts = "csp,html,css,js,ts,scss" + return $find(webExts, ext) +} + +/// Imports the file in UDL file in the project +/// contentStream - the stream which contains the source code in udl format.
+/// url - the url where the file is located in the web.
+/// list - array of files to compile
+ClassMethod LoadUDLFile(contentStream As %GlobalCharacterStream, binaryStream As %Stream.FileCharacterGzip, url As %String, list As %String) As %Status +{ + set st = $$$OK + + set ext = ..GetExt(url) + + if ext = "cls" { + set st = ..CreateClass(contentStream, url, .list) + } + elseif ext = "dfi" { + set st = ..CreateDFI(contentStream, url, .list) + } + elseif (ext = "inc") || (ext = "mac") { + set st = ..CreateRoutine(contentStream, url, .list) + } + else + { + set st = ..CreateWebFile(contentStream, binaryStream, url, ext, .list) + } + return st +} + +/// Checks whether the class exists +/// className - name of the class.
+ClassMethod DoesClassExist(className As %String) As %Boolean +{ + Set query = "SELECT TOP 1 COUNT(ID) FROM %Dictionary.ClassDefinition WHERE ID = ?" + Set statement = ##class(%SQL.Statement).%New() + Set st = statement.%Prepare(query) + Set rset = statement.%Execute(className) + If (rset.%Next()) && (rset.%ROWCOUNT > 0) { + Return $$$YES + } + Return $$$NO +} + +/// Creates and imports the class into the project from stream +/// contentStream - the stream which contains the source code in udl format.
+/// url - the url where the file is located in the web.
+/// list - array of files to compile
+ClassMethod CreateClass(contentStream As %CharacterStream, url As %String, ByRef list As %String) As %Status +{ + Set st = ..GetClassName(contentStream, .className) + Return:$$$ISERR(st) st + + set list(className _ ".cls") = "" + + Do contentStream.Rewind() + + if '(##class(%Dictionary.ClassDefinition).%ExistsId(className)) { + Set clsDef = ##class(%Dictionary.ClassDefinition).%New() + Set clsDef.Name = className + Set st = clsDef.%Save() + Return:$$$ISERR(st) st + } + + + Set namespace = $namespace + Set $namespace = ..#NamespacePar + Set st = ##class(CacheUpdater.TextServices).SetTextFromStream(namespace,className, contentStream) + Set $namespace = namespace + + if st { + w !, "Imported " _ className, ! + } + + Return st +} + +/// Creates and imports the dfi file into the project from stream +/// contentStream - the stream which contains the source code in udl format.
+/// list - array of files to compile
+ClassMethod CreateDFI(contentStream As %CharacterStream, url As %String, ByRef list As %String) As %Status +{ + Set st = $$$OK + Try { + Set st = ..GetDFIName(contentStream, .name) + Return:$$$ISERR(st) st + + set list(name) = "" + + Set tDoc = ##class(%DeepSee.UI.FolderItemDocument).%New(name) + Set st = tDoc.ImportFromXML(contentStream) + Return:$$$ISERR(st) st + + Set st = tDoc.Save() + if st { + w !, "Imported " _ name, ! + } + Return:$$$ISERR(st) st + } Catch e { + Set st = e.AsStatus() + } + Return st +} + +/// Creates and imports mac, int, inc files into the project from stream +/// contentStream - the stream which contains the source code in udl format.
+/// url - the url where the file is located in the web.
+/// list - array of files to compile
+ClassMethod CreateRoutine(contentStream As %GlobalCharacterStream, url As %String, ByRef list As %String) As %Status +{ + Set st = ..GetRoutineName(contentStream, .name, .type) + do contentStream.Rewind() + + return:$$$ISERR(st) st + + if type = 1 { + set name = name _ ".mac" + } + elseif type = 2 { + set name = name _ ".inc" + } + elseif type = 3 { + set name = name _ ".int" + } + + set list(name) = "" + + Set rtn = ##class(%Routine).%New(name) + While 'contentStream.AtEnd { + Set line = contentStream.ReadLine() + If $Find(line, "ROUTINE") { + Continue + } + Do rtn.WriteLine(line) + } + + Set st = rtn.Save() + Return:$$$ISERR(st) st + + if st { + w !, "Imported " _ name, ! + } + Return st +} + +/// Creates and imports mac, int, inc files into the project from stream +/// contentStream - the stream which contains the source code in udl format.
+/// url - the url where the file is located in the web.
+/// ext - extension of the file
+/// list - array of files to compile
+ClassMethod CreateWebFile(contentStream As %GlobalCharacterStream, binaryStream As %Stream.FileCharacterGzip, url As %String, ext As %String, ByRef list As %String) As %Status +{ + Set st = $$$OK + Try + { + Set tCSPRootPath = $system.CSP.GetFileName($system.CSP.GetDefaultApp($namespace)_"/") + + Set tFileName = $Piece($Piece(url,"?",1),"/",*) + Set tCSPSubPath = $Piece(url,"/",7,*-1)_"/" + + set tFileDirectory = tCSPRootPath_tCSPSubPath + Set tFullFileName = tFileDirectory_tFileName + + //On Windows, tFullFileName will contain \ and / but CreateDirectoryChain() and + //LinkToFile() already normalize the paths accordingly to the OS for us so + //we don't have to worry about it. + If '##class(%File).CreateDirectoryChain(tFileDirectory) + { + Set st = $System.Status.Error(5001,"Could nor create path chain '"_tFileDirectory_"'") + Quit + } + + Set filestream = ##class(%Stream.FileCharacter).%New() + set st = filestream.LinkToFile(tFullFileName) + Quit:$System.Status.IsError(st) + + If $IsObject(contentStream) && ..IsWebFile(ext) + { + Set st=filestream.CopyFrom(contentStream) + } + Else + { + Set st=filestream.CopyFrom(binaryStream) + } + Quit:$System.Status.IsError(st) + + set st = filestream.%Save() + Quit:$System.Status.IsError(st) + + Write !, "Imported " _ tFullFileName, ! + } + Catch (oException) + { + Set st = oException.AsStatus() + } + + Quit st +} + +} +