Skip to content

Commit be8f2ac

Browse files
authored
Merge pull request #51 from intersystems-community/stage
Proxy Registry
2 parents 4fb79b8 + 3e6ae4e commit be8f2ac

File tree

9 files changed

+784
-108
lines changed

9 files changed

+784
-108
lines changed

Dockerfile

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
FROM store/intersystems/iris-community:2020.1.0.215.0
1+
#FROM store/intersystems/iris-community:2020.1.0.215.0
2+
FROM intersystemsdc/iris-community:2020.4.0.524.0-zpm
23

34
USER root
45

6+
COPY zpm-registry.yaml /usr/irissys/
7+
58
WORKDIR /opt/zpm
69
RUN chown ${ISC_PACKAGE_MGRUSER}:${ISC_PACKAGE_IRISGROUP} .
710

@@ -15,7 +18,11 @@ SHELL ["/irissession.sh"]
1518

1619
RUN \
1720
do $SYSTEM.OBJ.Load("Installer.cls", "ck") \
18-
set sc = ##class(ZPM.Installer).setup()
21+
set sc = ##class(ZPM.Installer).setup() \
22+
zn "registry" \
23+
zpm "install yaml-utils"
1924

2025
# bringing the standard shell back
21-
SHELL ["/bin/bash", "-c"]
26+
SHELL ["/bin/bash", "-c"]
27+
28+

Installer.cls

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,31 @@ XData setup
2727
Recurse="1" />
2828

2929
</Namespace>
30-
3130
<Invoke Class="ZPM.Installer" Method="SetDispatchClass">
3231
<Arg Value="${CSPAPP}"/>
3332
<Arg Value="ZPM.Registry"/>
3433
</Invoke>
3534

35+
<Invoke Class="ZPM.Installer" Method="AddSSLConfiguration"></Invoke>
36+
3637
<Invoke Class="Security.SQLPrivileges" Method="Import" CheckStatus="true">
3738
<Arg Value="${APPPATH}SQLPriv.xml"/>
3839
</Invoke>
40+
3941
</Manifest>
4042
}
4143

44+
ClassMethod AddSSLConfiguration()
45+
{
46+
Set host = "zpmregistry"
47+
New $NAMESPACE
48+
Set $NAMESPACE = "%SYS"
49+
If '##class(Security.SSLConfigs).Exists(host) {
50+
Do ##class(Security.SSLConfigs).Create(host)
51+
}
52+
Return host
53+
}
54+
4255
ClassMethod setup(ByRef pVars, pLogLevel As %Integer = 3, pInstaller As %Installer.Installer, pLogger As %Installer.AbstractLogger) As %Status [ CodeMode = objectgenerator, Internal ]
4356
{
4457
#; Let XGL document generate code for this method.

SQLPriv.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,12 @@
1616
<Grantor>_SYSTEM</Grantor>
1717
<Grantable>0</Grantable>
1818
</SQLPrivileges>
19+
<SQLPrivileges>
20+
<Namespace>REGISTRY</Namespace>
21+
<SQLObject>1,ZPM.UpLink</SQLObject>
22+
<Privilege>s</Privilege>
23+
<Grantee>_PUBLIC</Grantee>
24+
<Grantor>_SYSTEM</Grantor>
25+
<Grantable>0</Grantable>
26+
</SQLPrivileges>
1927
</SQLPrivilegesExport>

module.xml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,26 @@
44
<Module>
55
<Name>zpm-registry</Name>
66
<Description>Registry server for ZPM</Description>
7-
<Version>1.0.10</Version>
7+
<Version>1.1.0</Version>
88
<Packaging>module</Packaging>
9+
<Dependencies>
10+
<ModuleReference>
11+
<Name>yaml-utils</Name>
12+
<Version>0.1.*</Version>
13+
</ModuleReference>
14+
</Dependencies>
915
<SourcesRoot>src</SourcesRoot>
1016
<Resource Name="ZPM.PKG"/>
11-
17+
<Invoke Class="ZPM.Utils" Method="SQLsetup"></Invoke>
18+
<Invoke Class="ZPM.Utils" Method="AddSSLConfiguration"></Invoke>
1219
<CSPApplication
1320
Url="/registry"
1421
Path="/src"
1522
Recurse="1"
1623
Directory="{$cspdir}/registry"
1724
MatchRoles=":{$dbrole}"
1825
PasswordAuthEnabled="1"
19-
UnauthenticatedEnabled="0"
26+
UnauthenticatedEnabled="1"
2027
DispatchClass="ZPM.Registry"
2128
ServeFiles="1"
2229
CookiePath="/registry"

src/cls/ZPM/Package.cls

Lines changed: 151 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ Index Repository On repository;
2929

3030
Property dependencies As array Of %String;
3131

32-
Property package As %Stream.GlobalBinary(%JSONINCLUDE = "INPUTONLY") [ Required ];
32+
Property package As %Stream.GlobalBinary(%JSONINCLUDE = "INPUTONLY");
3333

3434
Property size As %Integer(%JSONINCLUDE = "OUTPUTONLY");
3535

36-
Property manifest As %Stream.GlobalCharacter(%JSONINCLUDE = "INPUTONLY") [ Required ];
36+
Property manifest As %Stream.GlobalCharacter(%JSONINCLUDE = "INPUTONLY");
3737

3838
Property installer As %Stream.GlobalCharacter(%JSONINCLUDE = "INPUTONLY");
3939

@@ -55,6 +55,12 @@ Property versionPrerelease As %String [ SqlComputeCode = {Set {*} = $Piece($Piec
5555

5656
Property versionBuildmetadata As %String [ SqlComputeCode = {Set {*} = $Piece({version},"+",2)}, SqlComputed, SqlComputeOnChange = %%INSERT ];
5757

58+
Property UpLink As ZPM.UpLink(%JSONINCLUDE = "NONE");
59+
60+
ForeignKey UpLinkFK(UpLink) References ZPM.UpLink() [ OnDelete = cascade ];
61+
62+
Index NameUpLink On (name, UpLink);
63+
5864
Method nameSet(value As %String) As %Status
5965
{
6066
set i%name = $$$lcase(value)
@@ -68,8 +74,9 @@ Method sizeGet() As %Integer
6874

6975
ClassMethod GetPackageHash(pStream As %Stream.Object) As %String
7076
{
71-
If '$isobject(pStream) Set pStream = ..packageOpen(pStream)
72-
set hash = $system.Encryption.SHA1HashStream(pStream)
77+
If (pStream="") { Return "" }
78+
If '$isobject(pStream) { Set pStream = ..packageOpen(pStream) }
79+
Set hash = $system.Encryption.SHA1HashStream(pStream)
7380
Set str=""
7481
For i=1:1:$length(hash) {
7582
Set str=str_$translate($justify($zhex($ascii(hash, i)),2)," ",0)
@@ -79,18 +86,19 @@ ClassMethod GetPackageHash(pStream As %Stream.Object) As %String
7986

8087
ClassMethod ServerURL() As %String
8188
{
82-
SET host = ""
83-
IF $ISOBJECT($GET(%request)) {
84-
set tSecure = %request.Secure
85-
SET host = %request.GetCgiEnv("HTTP_X_FORWARDED_HOST", %request.GetCgiEnv("SERVER_NAME"))
86-
set proto = %request.GetCgiEnv("HTTP_X_FORWARDED_PROTO", $SELECT(tSecure: "https", 1: "http"))
87-
SET port = %request.GetCgiEnv("HTTP_X_FORWARDED_PORT", %request.GetCgiEnv("SERVER_PORT"))
88-
SET host = proto _ "://" _ host
89-
IF '$LISTFIND($LISTBUILD(80, 443), port) {
90-
SET host = host_":"_port
89+
Set host = ""
90+
If $ISOBJECT($Get(%request)) {
91+
Set tSecure = %request.Secure
92+
Set host = %request.GetCgiEnv("HTTP_X_FORWARDED_HOST", %request.GetCgiEnv("SERVER_NAME"))
93+
Set proto = %request.GetCgiEnv("HTTP_X_FORWARDED_PROTO", $SELECT(tSecure: "https", 1: "http"))
94+
Set port = %request.GetCgiEnv("HTTP_X_FORWARDED_PORT", %request.GetCgiEnv("SERVER_PORT"))
95+
Set host = proto _ "://" _ host
96+
If '$LISTFIND($LISTBUILD(80, 443), port) {
97+
Set host = host_":"_port
9198
}
9299
}
93-
RETURN host
100+
//Set host = host_$p(%request.Application,"/",1,*-1)
101+
Return host
94102
}
95103

96104
Method urlGet() As %String
@@ -135,6 +143,62 @@ Method versionsGet() As %ListOfDataTypes
135143
return tList
136144
}
137145

146+
ClassMethod VersionFind(pkg As %String = "", version As %String = "") As %String
147+
{
148+
If (version = "") || (version = "latest") || (version = "*") {
149+
// package was published directly in this registry - return the last version
150+
&sql(SELECT TOP 1 Version INTO :version FROM ZPM.Package WHERE Name = :pkg AND UpLink IS NULL
151+
ORDER BY versionMajor DESC, versionMinor DESC, versionPatch DESC, versionPrerelease DESC, versionBuildmetadata DESC
152+
)
153+
If SQLCODE=0 {
154+
// found
155+
Return version
156+
} Else {
157+
// find the latest version in UpLinks
158+
Do ##class(ZPM.UpLink).LoadPackageFromAllUpLinks(pkg, "latest")
159+
&sql(SELECT TOP 1 Version INTO :version FROM ZPM.Package WHERE Name = :pkg
160+
ORDER BY versionMajor DESC, versionMinor DESC, versionPatch DESC, versionPrerelease DESC, versionBuildmetadata DESC
161+
)
162+
If SQLCODE=0 {
163+
// if manifest doesn''t exists - find in uplinks
164+
Set package = ##class(ZPM.Package).%OpenId(pkg_"||"_version)
165+
If ('$IsObject(package)) { Return ""}
166+
If (package.manifest.Size) { Return version }
167+
Do ##class(ZPM.UpLink).LoadPackageFromAllUpLinks(pkg, version)
168+
Return version
169+
}
170+
Return ""
171+
}
172+
} Else {
173+
If ( ##class(ZPM.Package).NameUpLinkExists(pkg, "") ) {
174+
If ..%ExistsId(pkg _ "||" _ version) {
175+
Return version
176+
}
177+
Return ""
178+
} Else {
179+
If ..%ExistsId(pkg _ "||" _ version) {
180+
Set package = ##class(ZPM.Package).%OpenId(pkg_"||"_version)
181+
If ('$IsObject(package)) { Return ""}
182+
If (package.manifest.Size) { Return version }
183+
}
184+
Do ##class(ZPM.UpLink).LoadPackageFromAllUpLinks(pkg, version)
185+
If ..%ExistsId(pkg _ "||" _ version) {
186+
Return version
187+
}
188+
Return ""
189+
}
190+
}
191+
}
192+
193+
ClassMethod DeleteExistingPackages(pkg, version) As %Status
194+
{
195+
// delete all "proxy" packages
196+
&sql(DELETE FROM ZPM.Package WHERE Name = :pkg AND UpLink IS NOT NULL)
197+
198+
Do ..NameVersionDelete(pkg, version)
199+
Return 1
200+
}
201+
138202
ClassMethod versionValidate(pkg As %String = "", version As %String = "") As %String
139203
{
140204
if (pkg="") {
@@ -250,7 +314,7 @@ ClassMethod LoadPackage(Path As %String, repository As %String = "", silent As %
250314
WRITE "ArchiveSize: " _ archive.Size
251315
}
252316

253-
do ..%DeleteId(name _ "||" _version)
317+
do ..DeleteExistingPackages(name,version)
254318
SET package = ..%New()
255319
SET package.name = name
256320
Set package.description = description
@@ -281,7 +345,7 @@ ClassMethod GetDefaultBranch(path As %String) As %String
281345
Set branch = "main"
282346
Set ht = ##class(%Net.HttpRequest).%New()
283347
Set ht.Server = "api.github.com"
284-
Set ht.SSLConfiguration = ..GetSSLConfiguration(ht.Server)
348+
Set ht.SSLConfiguration = ##class(ZPM.Utils).GetSSLConfiguration()
285349
Set ht.Https = 1
286350
Set ht.Location = "/repos"_path
287351
$$$ThrowOnError(ht.Get())
@@ -299,7 +363,7 @@ ClassMethod DownloadPackageFromGitHub(url As %String, Output branch As %String)
299363
Set ht = ##class(%Net.HttpRequest).%New()
300364
Set ht.Server = tComponents("host")
301365
If $get(tComponents("scheme"))="https" {
302-
Set ht.SSLConfiguration = ..GetSSLConfiguration(ht.Server)
366+
Set ht.SSLConfiguration = ##class(ZPM.Utils).GetSSLConfiguration()
303367
Set ht.Https = 1
304368
}
305369
If $data(tComponents("port"), port), port'="" {
@@ -330,7 +394,7 @@ ClassMethod GetModuleMeta(url As %String) As %DynamicObject
330394
Set ht = ##class(%Net.HttpRequest).%New()
331395
Set ht.Server = "raw.githubusercontent.com"
332396
If $get(tComponents("scheme"))="https" {
333-
Set ht.SSLConfiguration = ..GetSSLConfiguration(ht.Server)
397+
Set ht.SSLConfiguration = ##class(ZPM.Utils).GetSSLConfiguration()
334398
Set ht.Https = 1
335399
}
336400
If $data(tComponents("port"), port), port'="" {
@@ -341,24 +405,13 @@ ClassMethod GetModuleMeta(url As %String) As %DynamicObject
341405
If (ht.HttpResponse.StatusCode'=200) {
342406
$$$ThrowStatus($$$ERROR(5001,"Unable to download XML "_ht.Location))
343407
}
344-
Set tmpFolder = $$$FileTempDir
345-
346-
Set stream=##class(%Stream.FileCharacter).%New()
347-
Do stream.LinkToFile(tmpFolder_"/module.xml")
348-
Set stream.TranslateTable = "UTF8"
349-
Do stream.CopyFrom(ht.HttpResponse.Data)
350-
Do stream.%Save()
351408

352-
set binaryStream=##class(%Stream.FileBinary).%New()
353-
do binaryStream.LinkToFile(tmpFolder_"/module.xml")
409+
Set xPathArray("Module/Version")=""
410+
Set xPathArray("Module/Name")=""
411+
$$$ThrowOnError(##class(ZPM.Utils).ReadXMLElements(ht.HttpResponse.Data, .xPathArray))
354412

355-
$$$ThrowOnError(##class(%XML.XPATH.Document).CreateFromStream(binaryStream, .xpathdoc))
356-
$$$ThrowOnError(xpathdoc.EvaluateExpression("/","Export/Document/Module/Version/text()", .tResults))
357-
set jo.version = tResults.GetAt(1).ValueGet()
358-
$$$ThrowOnError(xpathdoc.EvaluateExpression("/","Export/Document/Module/Name/text()", .tResults))
359-
set jo.name = $$$lcase(tResults.GetAt(1).ValueGet())
360-
361-
Do ##class(%File).RemoveDirectoryTree(tmpFolder)
413+
Set jo.name = $Get(xPathArray("Module/Name","text"))
414+
Set jo.version = $Get(xPathArray("Module/Version","text"))
362415

363416
return jo
364417
}
@@ -377,6 +430,9 @@ ClassMethod UpdatePackage(jo As %DynamicObject) As %Status
377430
If (meta.name = "") { $$$ThrowStatus($$$ERROR(5001, "Package name is empty")) }
378431
Set tName = meta.name
379432
Set id = ""
433+
434+
&sql(DELETE FROM ZPM.Package WHERE Name = :tName AND UpLink IS NOT NULL) // delete all "proxy" packages
435+
380436
&sql(SELECT ID into :id FROM ZPM.Package WHERE name=:tName ORDER BY versionMajor DESC, versionMinor DESC, versionPatch DESC, versionPrerelease DESC)
381437
If (id'="") {
382438
If (..repositoryGetStored(id)'=url) {
@@ -432,15 +488,66 @@ ClassMethod LoadFromGitHub(Url = "")
432488
do ##class(%File).RemoveDirectoryTree(outputFolder)
433489
}
434490

435-
ClassMethod GetSSLConfiguration(host) As %String
491+
ClassMethod GetTopVersion(pkg As %String) As %String
436492
{
437-
NEW $NAMESPACE
438-
SET $NAMESPACE = "%SYS"
439-
440-
IF '##class(Security.SSLConfigs).Exists(host) {
441-
DO ##class(Security.SSLConfigs).Create(host)
493+
Set version = ""
494+
&sql(SELECT TOP 1 version INTO :version FROM ZPM.Package
495+
WHERE name = :pkg
496+
ORDER BY versionMajor DESC, versionMinor DESC, versionPatch DESC, versionPrerelease DESC, versionBuildmetadata DESC )
497+
Return version
498+
}
499+
500+
/// returns latest versions of packages
501+
ClassMethod GetLatest(searchTerms As %DynamicArray = "", Output pStatus As %Status) As %DynamicArray
502+
{
503+
Set pStatus = 1
504+
If ( '($CLASSNAME(searchTerms)="%Library.DynamicArray" ) || (searchTerms.%Size()=0) ) {
505+
// empty query
506+
Set searchCondition = " ( 1=1 ) "
507+
} Else {
508+
Set searchCondition = " ( "
509+
Set iter = searchTerms.%GetIterator()
510+
Set params = 0
511+
While iter.%GetNext(.key , .value ) {
512+
if (key=0) {
513+
Set searchCondition = searchCondition _ " ((name %MATCHES ?) OR (description %MATCHES ?) OR (keywords %MATCHES ?)) "
514+
} else {
515+
Set searchCondition = searchCondition _ " OR ((name %MATCHES ?) OR (description %MATCHES ?) OR (keywords %MATCHES ?))"
516+
}
517+
Set params(params+1) = value
518+
Set params(params+2) = value
519+
Set params(params+3) = value
520+
Set params = params + 3
521+
}
522+
Set searchCondition = searchCondition_ " ) "
523+
}
524+
Set sql = "SELECT name, repository, description "_
525+
"FROM ZPM.Package p1 "_
526+
"WHERE "_ searchCondition _
527+
"GROUP BY name "_
528+
"ORDER BY name"
529+
Set tStatement = ##class(%SQL.Statement).%New()
530+
Set tStatus = tStatement.%Prepare(sql)
531+
If ($$$ISERR(tStatus)) {
532+
Set pStatus = tStatus
533+
Return []
534+
}
535+
Set tResult = tStatement.%Execute(params...)
536+
If (tResult.%SQLCODE<0) {
537+
Set pStatus = $$$ERROR(5001, "Error executing sql statement")
538+
Return []
539+
}
540+
Set tList = []
541+
While tResult.%Next() {
542+
Set tPkgInfo = {
543+
"name": (tResult.name),
544+
"description": (tResult.Description),
545+
"repository": (tResult.repository),
546+
"versions": [(..GetTopVersion(tResult.name))]
547+
}
548+
do tList.%Push(tPkgInfo)
442549
}
443-
QUIT host
550+
Return tList
444551
}
445552

446553
Query ListLatest(searchTerm As %String = "") As %SQLQuery(ROWSPEC = "name:%String,description:%String,repository:%String,version:%String")
@@ -517,6 +624,9 @@ Storage Default
517624
<Value name="18">
518625
<Value>versionBuildmetadata</Value>
519626
</Value>
627+
<Value name="19">
628+
<Value>UpLink</Value>
629+
</Value>
520630
</Data>
521631
<Data name="dependencies">
522632
<Attribute>dependencies</Attribute>

0 commit comments

Comments
 (0)