Skip to content

Commit 07a5e5f

Browse files
authored
Merge pull request #88 from eXist-db/feat/find-json
Feature: improve /find
2 parents 80a006c + 2ba9b2f commit 07a5e5f

File tree

2 files changed

+164
-52
lines changed

2 files changed

+164
-52
lines changed

modules/find.xq

Lines changed: 146 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ xquery version "3.1";
88
: The parameter name `version` is retained for backward compatibility, even though it's
99
: `versions` in the EXPath Package spec.
1010
:
11-
: The `info` parameter can be used for troubleshooting.
11+
: The `info` parameter can be used for troubleshooting and to query package availability
1212
:
1313
: The `zip` parameter forces the EXPath Package to be returned with a .xar.zip file extension.
1414
:
15+
: A client can set application/json in its accept header to receive packge information and errors
16+
: as JSON.
17+
:
1518
: @see http://expath.org/spec/pkg
1619
:)
1720

@@ -22,47 +25,149 @@ import module namespace versions="http://exist-db.org/apps/public-repo/versions"
2225
declare namespace request="http://exist-db.org/xquery/request";
2326
declare namespace response="http://exist-db.org/xquery/response";
2427

25-
let $abbrev := request:get-parameter("abbrev", ())
26-
let $name := request:get-parameter("name", ())
27-
let $exist-version-semver := request:get-parameter("processor", $config:default-exist-version)
28-
let $versions := request:get-parameter("version", ())
29-
let $semver := request:get-parameter("semver", ())
30-
let $semver-min := request:get-parameter("semver-min", ())
31-
let $semver-max := request:get-parameter("semver-max", ())
32-
let $zip := request:get-parameter("zip", ())
33-
let $info := request:get-parameter("info", ())
34-
let $app-root-absolute-url := request:get-parameter("app-root-absolute-url", ())
35-
36-
let $packages :=
37-
if ($name) then
28+
(: TODO shouldn't we get $abs-public from $config? - joewiz :)
29+
declare variable $app-root-absolute-url := request:get-parameter("app-root-absolute-url", ());
30+
declare variable $abs-public := $app-root-absolute-url || "/public/";
31+
32+
declare variable $exist-version-semver := request:get-parameter("processor", $config:default-exist-version);
33+
declare variable $abbrev := request:get-parameter("abbrev", ());
34+
declare variable $name := request:get-parameter("name", ());
35+
declare variable $versions := request:get-parameter("version", ());
36+
declare variable $semver := request:get-parameter("semver", ());
37+
declare variable $semver-min := request:get-parameter("semver-min", ());
38+
declare variable $semver-max := request:get-parameter("semver-max", ());
39+
40+
declare variable $zip := request:get-parameter("zip", ());
41+
declare variable $info := request:get-parameter("info", ());
42+
43+
declare variable $versions-or-version-range :=
44+
exists($versions)
45+
or exists($semver)
46+
or exists($semver-min)
47+
or exists($semver-max)
48+
;
49+
50+
(:
51+
input: "text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8"
52+
output: ("text/html", "application/json", "application/xml", "image/webp", "*/*")
53+
:)
54+
declare function local:parse-accept-header($accept as xs:string) as xs:string* {
55+
tokenize($accept, ",")
56+
! normalize-space() (: trim value :)
57+
! tokenize(., ";")[1] (: drop q :)
58+
};
59+
60+
declare function local:prefers-json($mime-types as xs:string*) as xs:boolean {
61+
let $json-index := index-of($mime-types, "application/json")
62+
let $xml-index := index-of($mime-types, "application/xml")
63+
return
64+
exists($json-index) and (empty($xml-index) or $xml-index > $json-index)
65+
};
66+
67+
declare variable $json-preferred := local:prefers-json(local:parse-accept-header(request:get-header("Accept")));
68+
69+
declare function local:render-semver-range($semver as xs:string?, $semver-min as xs:string?, $semver-max as xs:string?) as xs:string {
70+
if (exists($semver)) then (
71+
$semver
72+
) else (
73+
string-join((
74+
if (exists($semver-min)) then ``[>=`{$semver-min}`]`` else (),
75+
if (exists($semver-max)) then``[<=`{$semver-max}`]`` else ()
76+
))
77+
)
78+
};
79+
80+
declare function local:report-not-found ($message as xs:string) as item() {
81+
response:set-status-code(404),
82+
if ($json-preferred) then (
83+
response:set-header("content-type", "application/json"),
84+
serialize(map { "error": $message }, map{ "method": "json" })
85+
) else (
86+
(: could be changed to <error/> element :)
87+
<p>{$message}</p>
88+
)
89+
};
90+
91+
declare function local:render-version-query() as xs:string {
92+
if (exists($versions))
93+
then ("versions: " || string-join($versions, ', '))
94+
else if ($versions-or-version-range)
95+
then ("semver-range: " || local:render-semver-range($semver, $semver-min, $semver-max))
96+
else ("compatible with processor version " || $exist-version-semver)
97+
};
98+
99+
declare function local:render-package-query() as xs:string {
100+
if (exists($name)) then (
101+
"name: " || $name
102+
) else (
103+
"abbrev: " || $abbrev
104+
)
105+
};
106+
107+
declare variable $packages :=
108+
if (exists($name)) then (
38109
doc($config:package-groups-doc)//package-group[name eq $name]//package
39-
else
110+
) else (
40111
doc($config:package-groups-doc)//package-group[abbrev eq $abbrev]//package
112+
)
113+
;
41114

42-
let $package :=
43-
if (exists($versions) or exists($semver) or exists($semver-min) or exists($semver-max)) then
44-
versions:get-newest-package-satisfying-version-attributes($packages, $versions, $semver, $semver-min, $semver-max)
45-
else
46-
versions:get-newest-package-satisfying-exist-version($packages, $exist-version-semver)
47-
48-
return
49-
if ($package) then
50-
(: TODO shouldn't we get $abs-public from $config? - joewiz :)
51-
let $abs-public := $app-root-absolute-url || "/public/"
52-
let $xar-filename := $package/@path
53-
return
54-
if ($info) then
55-
element found {
56-
$package/@sha256,
57-
$package/version ! attribute version {.},
58-
$package/@path
59-
}
60-
else if ($zip) then
61-
app:redirect-to($abs-public || $xar-filename || ".zip")
62-
else
63-
app:redirect-to($abs-public || $xar-filename)
64-
else
65-
(
66-
response:set-status-code(404),
67-
<p>Package file not found!</p>
115+
declare variable $package :=
116+
try {
117+
if ($versions-or-version-range) then (
118+
versions:get-newest-package-satisfying-version-attributes(
119+
$packages, $versions, $semver, $semver-min, $semver-max)
120+
) else (
121+
versions:get-newest-package-satisfying-exist-version($packages, $exist-version-semver)
68122
)
123+
} catch * {
124+
util:log("info", "Error retrieving matching package in find.xq: " || $err:description)
125+
}
126+
;
127+
128+
(: util:log("info", map {
129+
"exist-version-semver" : $exist-version-semver,
130+
"abbrev" : $abbrev,
131+
"name" : $name,
132+
"versions" : $versions,
133+
"semver" : $semver,
134+
"semver-min" : $semver-min,
135+
"semver-max" : $semver-max,
136+
"zip" : $zip,
137+
"info" : $info,
138+
"json-preferred": $json-preferred
139+
}), :)
140+
if (empty($packages)) then (
141+
local:report-not-found(``[No package with `{local:render-package-query()}` found.]``)
142+
) else if (empty($package)) then (
143+
local:report-not-found(
144+
``[No matching version found for `{local:render-package-query()}`; `{local:render-version-query()}`.]``)
145+
) else if ($info and $json-preferred) then (
146+
response:set-header("content-type", "application/json"),
147+
serialize(
148+
map {
149+
"sha256" : string($package/@sha256),
150+
"path" : string($package/@path),
151+
"size" : xs:integer($package/@size),
152+
"name" : string($package/name),
153+
"abbrev" : string($package/abbrev),
154+
"version" : string($package/version),
155+
"url" : $abs-public || $package/@path
156+
},
157+
map { "method": "json" }
158+
)
159+
) else if ($info) then (
160+
element found {
161+
$package/@sha256,
162+
$package/@path,
163+
$package/@size,
164+
attribute name { $package/name },
165+
attribute abbrev { $package/abbrev },
166+
attribute version { $package/version },
167+
attribute url { $abs-public || $package/@path }
168+
}
169+
) else if ($zip) then (
170+
app:redirect-to($abs-public || $package/@path || ".zip")
171+
) else (
172+
app:redirect-to($abs-public || $package/@path)
173+
)

modules/versions.xqm

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,27 @@ import module namespace semver="http://exist-db.org/xquery/semver";
1515
:)
1616
declare function versions:get-packages-satisfying-version-attributes(
1717
$packages as element(package)*,
18-
$versions as xs:string?,
18+
$versions as xs:string*,
1919
$semver as xs:string?,
2020
$semver-min as xs:string?,
2121
$semver-max as xs:string?
2222
) as element(package)* {
23-
$packages[
24-
semver:satisfies-expath-package-dependency-versioning-attributes(
25-
./version,
26-
$versions,
27-
$semver,
28-
$semver-min,
29-
$semver-max
30-
)
31-
]
23+
if (exists($versions)) then (
24+
for-each($versions, function ($v as xs:string) {
25+
$packages[
26+
semver:satisfies-expath-package-dependency-versioning-attributes(./version, $v, (), (), ())]
27+
})
28+
) else (
29+
$packages[
30+
semver:satisfies-expath-package-dependency-versioning-attributes(
31+
./version,
32+
$versions,
33+
$semver,
34+
$semver-min,
35+
$semver-max
36+
)
37+
]
38+
)
3239
=> versions:sort-packages()
3340
};
3441

@@ -37,7 +44,7 @@ declare function versions:get-packages-satisfying-version-attributes(
3744
:)
3845
declare function versions:get-newest-package-satisfying-version-attributes(
3946
$packages as element(package)*,
40-
$versions as xs:string?,
47+
$versions as xs:string*,
4148
$semver as xs:string?,
4249
$semver-min as xs:string?,
4350
$semver-max as xs:string?

0 commit comments

Comments
 (0)