Include %syConfig
Class MFT.Installer
{
Parameter DB = "MFTLIB";
/// write $System.Status.GetErrorText(##class(MFT.Installer).Install())
ClassMethod Install(namespace = {$Namespace}) As %Status
{
#Dim sc As %Status = $$$OK
$$$QuitOnError(..CreateDatabase())
$$$QuitOnError(..CreateMapping("%MFT.Addons"))
$$$QuitOnError(..CreateMapping("%SYS.MFT.Connection.Addons"))
$$$QuitOnError(..Load())
Quit sc
}
ClassMethod CreateDatabase() As %Status
{
#Dim sc As %Status = $$$OK
Set dir = ##class(%File).SubDirectoryName(##class(%File).ManagerDirectory(), ..#DB, $$$YES)
Set exists = ##class(%File).Exists(dir _ "CACHE.DAT")
Quit:exists $$$ERROR($$$GeneralError, ..#DB _ " directory exists and contains CACHE.DAT")
If '##class(%File).DirectoryExists(dir) {
// Make sure the directory exists first
If '##class(%File).CreateDirectoryChain(dir) {
Quit $$$ERROR($$$DirectoryCannotCreate, dir)
}
}
New $Namespace
Set $Namespace = "%SYS"
// Physically create the database
$$$QuitOnError($$CreateDatabase^%SYS.DATABASE(dir))
Kill properties
Set properties("Directory") = dir
$$$QuitOnError(##Class(Config.Databases).Create(..#DB, .properties))
$$$QuitOnError(##class(%EnsembleMgr).assignResourceToDB("%DB_" _ ..#DB, dir, "R"))
Quit sc
}
ClassMethod CreateMapping(package As %String, fromDB As %String = {..#DB}, toNS = "%SYS") As %Status
{
New $Namespace
Set $Namespace = "%SYS"
#Dim sc As %Status = $$$OK
Set toNS = $zcvt(toNS, "U")
If '##Class(Config.MapPackages).Exists(toNS, package) {
Kill properties
Set properties("Database") = fromDB
Set sc = ##Class(Config.MapPackages).Create(toNS, package, .properties)
}
Quit sc
}
/// write $System.Status.GetErrorText(##class(MFT.Installer).Load())
ClassMethod Load2()
{
New $Namespace
Set $Namespace = "%SYS"
#Dim sc As %Status = $$$OK
Set sc = $system.OBJ.LoadDir("C:\temp\MFTAdapters\", "cukb", .err, $$$YES)
Quit sc
}
/// 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/MFTAdapters
/// Owner - intersystems-ru, Repository - MFTAdapters
/// write $System.Status.GetErrorText(##class(MFT.Installer).Load())
ClassMethod Load(namespace As %String = {$Namespace}, owner As %String = "intersystems-ru", repository As %String = "MFTAdapters", branch As %String, username As %String, password As %String) As %Status
{
New $Namespace
Set $Namespace = "%SYS"
Set ssl = "GitHub"
Do:'##class(Security.SSLConfigs).Exists(ssl) ##class(Security.SSLConfigs).Create(ssl)
Set req = ##class(%Net.HttpRequest).%New()
Set req.Https = $$$YES
Set req.SSLConfiguration = ssl
Set req.Server = "api.github.com"
Set req.Location = "repos/" _ owner _ "/" _ repository _ "/contents" // as described in https://developer.github.com/v3/repos/
Do:$d(Branch) req.SetParam("ref", branch) // if omitted the repository’s default branch (usually master) would be used
Do req.SetHeader("Accept","application/vnd.github.v3+json") // we want to receive API v3
If ($d(username) && $d(password)) { // supply Username and Passwor, if both are provided. GitHub accept Basic Auth
Set req.Username = username // https://developer.github.com/v3/auth/
Set req.Password = password
}
Set links = ##class(%ListOfDataTypes).%New()
$$$QuitOnError(..ProcessDirectory("", req, links))
Quit ..DownloadFiles(links, req, namespace)
}
/// Process one directory of GitHub repository. Recursive.
/// path -Internal repository path. Root is empty string
/// request - Authenticated/Set %Net.HttpRequest object.
/// namespace - load non % code here
ClassMethod ProcessDirectory(path As %String = "", request As %Net.HttpRequest, ByRef links As %ListOfDataTypes) As %Status
{
New $Namespace
Set $Namespace = "%SYS"
Set location = request.Location
Set request.Location = $zcvt(request.Location _ path, "I", "URL")
$$$QuitOnError(request.Get(,,$$$NO))
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 '= 200) $$$ERROR($$$GeneralError,"Received " _ request.HttpResponse.StatusCode _ " status, expected 200")
#dim objects As List of %ZEN.proxyObject
#dim obj As %ZEN.proxyObject
$$$QuitOnError(##class(%ZEN.Auxiliary.jsonProvider).%ConvertJSONToObject(request.HttpResponse.Data,,.objects,1))
For i = 1:1:objects.Count() {
Set obj = objects.GetAt(i)
If (obj.type = "dir") {
$$$QuitOnError(..ProcessDirectory("/" _ obj.name, request, links))
} ElseIf (obj.type = "file") {
Do:..IsCacheFile(obj) links.Insert(obj."download_url")
}
}
Set request.Location = location // to keep track of where in the repository tree we are
Quit $$$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,"
Quit:($L(file.name,".")=1) 0 //no extension
Set file.Extension = $P(file.name,".",$L(file.name,"."))
Quit $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, namespace As %String = {$Namespace}) As %Status
{
New $Namespace
#Dim sc As %Status = $$$OK
Set nsItems = ""
Set file = ##class(%File).TempFilename("cls")
Set stream = ##class(%Stream.FileCharacter).%New()
Do stream.LinkToFile(file)
Set request.Server = "raw.githubusercontent.com"
Set request.ResponseStream = stream
For i = 1:1:links.Count() {
set link = $e(links.GetAt(i),35,*)
$$$QuitOnError(request.Get(link)) // Remove "https://raw.githubusercontent.com/" from URL.
If $find(link, "%25") {
Set $Namespace = "%SYS"
} Else {
Set $Namespace = namespace
}
$$$QuitOnError($system.OBJ.Load(file,"",.error,.items,,,,"UTF8"))
Merge nsItems = items
}
Set $Namespace = namespace
$$$QuitOnError($system.OBJ.CompileList(.nsItems))
Quit sc
}
}