Skip to content

Commit e2825e8

Browse files
authored
Merge pull request #21 from evolvedbinary/hotfix/invalid-query-error-messages
Invalid queries should return nice error messages to the API caller
2 parents 72395c7 + a89dcfd commit e2825e8

File tree

5 files changed

+361
-27
lines changed

5 files changed

+361
-27
lines changed

pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@
9292
<scope>test</scope>
9393
</dependency>
9494

95+
<dependency>
96+
<groupId>com.evolvedbinary.j8fu</groupId>
97+
<artifactId>j8fu</artifactId>
98+
<version>1.23.0</version>
99+
<scope>test</scope>
100+
</dependency>
101+
95102
</dependencies>
96103

97104
<build>

src/main/xar-resources/api.xqm

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
2525
declare namespace http = "http://expath.org/ns/http-client";
2626
import module namespace sm = "http://exist-db.org/xquery/securitymanager";
2727
import module namespace system = "http://exist-db.org/xquery/system";
28+
import module namespace util = "http://exist-db.org/xquery/util";
2829

2930
import module namespace config = "http://fusiondb.com/ns/studio/api/config" at "modules/config.xqm";
3031
import module namespace col = "http://fusiondb.com/ns/studio/api/collection" at "modules/collection.xqm";
@@ -638,7 +639,10 @@ function api:query($range-header, $body) {
638639
"code": $hsc:bad-request,
639640
"reason": "Missing request body"
640641
},
641-
()
642+
map {
643+
"code": 1,
644+
"description": "Missing request body"
645+
}
642646
)
643647
else if (ut:is-guest())
644648
then
@@ -652,38 +656,81 @@ function api:query($range-header, $body) {
652656
else
653657
let $json-txt := util:base64-decode($body)
654658
let $query-data := fn:parse-json($json-txt)
659+
let $range :=
660+
if (fn:empty($range-header) or fn:not(fn:starts-with($range-header, "items=")))
661+
then
662+
()
663+
else
664+
let $res := fn:analyze-string($range-header, "items=([0-9]+)-([0-9]+)?")
665+
return
666+
($res//fn:group[@nr eq "1"]/xs:integer(.), $res//fn:group[@nr eq "2"]/xs:integer(.))
655667
return
656-
let $range :=
657-
if (fn:empty($range-header) or fn:not(fn:starts-with($range-header, "items=")))
658-
then
659-
()
660-
else
661-
let $res := fn:analyze-string($range-header, "items=([0-9]+)-([0-9]+)?")
662-
return
663-
($res//fn:group[@nr eq "1"]/xs:integer(.), $res//fn:group[@nr eq "2"]/xs:integer(.))
664-
665-
let $query-results := qry:execute($query-data, $range[1], $range[2])
666-
667-
let $content-range-header :=
668-
if (not(empty($range)))
669-
then
670-
map {
671-
"Content-Range": "items " || $range[1] || "-" || count($query-results) || "/*"
672-
}
673-
else
674-
map {}
675-
return
668+
if ($query-data?query-uri and not(util:binary-doc-available($query-data?query-uri)))
669+
then
676670
api:cors-allow(
677671
map {
678-
"code": if (not(empty($range))) then $hsc:partial-content else $hsc:ok,
679-
"headers": map:merge((map {
680-
"Accept-Ranges": "items"
681-
}, $content-range-header))
672+
"code": $hsc:bad-request,
673+
"reason": "Stored query: " || $query-data?query-uri || " does not exist!"
682674
},
683675
map {
684-
"results": $query-results
676+
"code": 2,
677+
"description": "Stored query: " || $query-data?query-uri || " does not exist!"
685678
}
686679
)
680+
else
681+
let $range-start := $range[1]
682+
let $range-end := $range[2]
683+
let $query-results :=
684+
try {
685+
qry:execute($query-data, $range-start, $range-end)
686+
} catch * {
687+
let $error-map := map {
688+
"code": $err:code,
689+
"description": $err:description,
690+
"value": $err:value,
691+
"module": $err:module,
692+
"line-number": $err:line-number,
693+
"column-number": $err:column-number,
694+
"additional": $err:additional
695+
}
696+
return
697+
map {
698+
"code": 3,
699+
"description": "An error occurred whilst evaluating the query",
700+
"xquery-error": ut:filter-map($error-map, ut:filter-entry-value-empty-sequence#2)
701+
}
702+
}
703+
return
704+
if ($query-results instance of map(*))
705+
then
706+
api:cors-allow(
707+
map {
708+
"code": $hsc:bad-request,
709+
"reason": "query raised an error"
710+
},
711+
$query-results
712+
)
713+
else
714+
let $content-range-header :=
715+
if (not(empty($range)))
716+
then
717+
map {
718+
"Content-Range": "items " || $range[1] || "-" || count($query-results) || "/*"
719+
}
720+
else
721+
map {}
722+
return
723+
api:cors-allow(
724+
map {
725+
"code": if (not(empty($range))) then $hsc:partial-content else $hsc:ok,
726+
"headers": map:merge((map {
727+
"Accept-Ranges": "items"
728+
}, $content-range-header))
729+
},
730+
map {
731+
"results": $query-results
732+
}
733+
)
687734
};
688735

689736
declare

src/test/java/com/fusiondb/studio/api/API.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,13 @@
1717
*/
1818
package com.fusiondb.studio.api;
1919

20+
import java.util.Collections;
21+
import java.util.HashMap;
22+
import java.util.Map;
2023
import java.util.function.Function;
2124

25+
import com.evolvedbinary.j8fu.tuple.Tuple2;
26+
2227
public class API {
2328

2429
/**
@@ -31,6 +36,16 @@ public class API {
3136
*/
3237
public static int DEFAULT_PORT = 4059;
3338

39+
/**
40+
* Default admin username
41+
*/
42+
public static String DEFAULT_ADMIN_USERNAME = "admin";
43+
44+
/**
45+
* Default admin password
46+
*/
47+
public static String DEFAULT_ADMIN_PASSWORD = "";
48+
3449
/**
3550
* Default API Endpoint for Fusion Studio API
3651
*/
@@ -49,7 +64,7 @@ public class API {
4964
/**
5065
* Get the Base URI for the Fusion Studio API.
5166
*
52-
* The URI can be overriden by environment variables
67+
* The URI can be overridden by environment variables
5368
* see {@link #ENV_VAR_FS_API_HOST} and {@link #ENV_VAR_FS_API_PORT}.
5469
*
5570
* @return the base URI
@@ -70,6 +85,30 @@ public static String getApiBaseUri() {
7085
return "http://" + host + ":" + port + "/exist/restxq/" + DEFAULT_ENDPOINT;
7186
}
7287

88+
/**
89+
* Get the Base URI for the eXist-db REST API.
90+
*
91+
* The URI can be overridden by environment variables
92+
* see {@link #ENV_VAR_FS_API_HOST} and {@link #ENV_VAR_FS_API_PORT}.
93+
*
94+
* @return the REST base URI
95+
*/
96+
public static String getRestApiBaseUri() {
97+
final String host = envVarOrDefault(ENV_VAR_FS_API_HOST, DEFAULT_HOST, envVarValue -> envVarValue);
98+
final int port = envVarOrDefault(ENV_VAR_FS_API_PORT, DEFAULT_PORT, envVarValue -> {
99+
try {
100+
return Integer.parseInt(envVarValue);
101+
} catch (final NumberFormatException e) {
102+
// invalid number
103+
System.err.println("ENV.FS_API_PORT=" + envVarValue + ", is not a valid TCP port number. Using default: " + DEFAULT_PORT);
104+
e.printStackTrace();
105+
return DEFAULT_PORT;
106+
}
107+
});
108+
109+
return "http://" + host + ":" + port + "/exist/rest";
110+
}
111+
73112
/**
74113
* Gets a value from an Environment variable or uses the default
75114
* if there is no such variable.
@@ -89,4 +128,16 @@ private static <T> T envVarOrDefault(final String envVarName, final T defaultVal
89128
return defaultValue;
90129
}
91130
}
131+
132+
static <K, V> Map<K, V> mapOf(final Tuple2<K, V>... entries) {
133+
if (entries == null) {
134+
return Collections.emptyMap();
135+
}
136+
137+
final Map<K, V> map = new HashMap<>(entries.length);
138+
for (final Tuple2<K, V> entry : entries) {
139+
map.put(entry._1, entry._2);
140+
}
141+
return map;
142+
}
92143
}

0 commit comments

Comments
 (0)