@@ -3,7 +3,7 @@ defmodule Plausible.Stats.ApiQueryParser do
33
44 use Plausible
55
6- alias Plausible.Stats . { Filters , Metrics , JSONSchema }
6+ alias Plausible.Stats . { Filters , Metrics , JSONSchema , QueryError }
77
88 @ default_include % Plausible.Stats.QueryInclude {
99 imports: false ,
@@ -50,8 +50,12 @@ defmodule Plausible.Stats.ApiQueryParser do
5050
5151 defp parse_metric ( metric_str ) do
5252 case Metrics . from_string ( metric_str ) do
53- { :ok , metric } -> { :ok , metric }
54- _ -> { :error , "Unknown metric '#{ i ( metric_str ) } '." }
53+ { :ok , metric } ->
54+ { :ok , metric }
55+
56+ _ ->
57+ { :error ,
58+ % QueryError { code: :invalid_metrics , message: "Unknown metric '#{ i ( metric_str ) } '." } }
5559 end
5660 end
5761
@@ -82,7 +86,11 @@ defmodule Plausible.Stats.ApiQueryParser do
8286 defp parse_operator ( [ "not" | _rest ] ) , do: { :ok , :not }
8387 defp parse_operator ( [ "has_done" | _rest ] ) , do: { :ok , :has_done }
8488 defp parse_operator ( [ "has_not_done" | _rest ] ) , do: { :ok , :has_not_done }
85- defp parse_operator ( filter ) , do: { :error , "Unknown operator for filter '#{ i ( filter ) } '." }
89+
90+ defp parse_operator ( filter ) ,
91+ do:
92+ { :error ,
93+ % QueryError { code: :invalid_filters , message: "Unknown operator for filter '#{ i ( filter ) } '." } }
8694
8795 def parse_filter_second ( operator , [ _ , filters | _rest ] ) when operator in [ :and , :or ] ,
8896 do: parse_filters ( filters )
@@ -97,7 +105,8 @@ defmodule Plausible.Stats.ApiQueryParser do
97105 parse_filter_dimension_string ( filter_dimension , "Invalid filter '#{ i ( filter ) } '." )
98106 end
99107
100- defp parse_filter_dimension ( filter ) , do: { :error , "Invalid filter '#{ i ( filter ) } '." }
108+ defp parse_filter_dimension ( filter ) ,
109+ do: { :error , % QueryError { code: :invalid_filters , message: "Invalid filter '#{ i ( filter ) } '." } }
101110
102111 defp parse_filter_rest ( operator , filter )
103112 when operator in [
@@ -133,7 +142,11 @@ defmodule Plausible.Stats.ApiQueryParser do
133142 { :ok , list }
134143 else
135144 { :error ,
136- "Invalid visit:country filter, visit:country needs to be a valid 2-letter country code." }
145+ % QueryError {
146+ code: :invalid_filters ,
147+ message:
148+ "Invalid visit:country filter, visit:country needs to be a valid 2-letter country code."
149+ } }
137150 end
138151
139152 { "segment" , _ } when all_integers? ->
@@ -143,11 +156,12 @@ defmodule Plausible.Stats.ApiQueryParser do
143156 { :ok , list }
144157
145158 _ ->
146- { :error , "Invalid filter '#{ i ( filter ) } '." }
159+ { :error , % QueryError { code: :invalid_filters , message: "Invalid filter '#{ i ( filter ) } '." } }
147160 end
148161 end
149162
150- defp parse_clauses_list ( filter ) , do: { :error , "Invalid filter '#{ i ( filter ) } '." }
163+ defp parse_clauses_list ( filter ) ,
164+ do: { :error , % QueryError { code: :invalid_filters , message: "Invalid filter '#{ i ( filter ) } '." } }
151165
152166 defp parse_filter_modifiers ( modifiers ) when is_map ( modifiers ) do
153167 { :ok , [ atomize_keys ( modifiers ) ] }
@@ -164,9 +178,15 @@ defmodule Plausible.Stats.ApiQueryParser do
164178
165179 defp parse_input_date_range ( shorthand ) when is_binary ( shorthand ) do
166180 case Integer . parse ( shorthand ) do
167- { n , "d" } when n > 0 and n <= 5_000 -> { :ok , { :last_n_days , n } }
168- { n , "mo" } when n > 0 and n <= 100 -> { :ok , { :last_n_months , n } }
169- _ -> { :error , "Invalid date_range #{ i ( shorthand ) } " }
181+ { n , "d" } when n > 0 and n <= 5_000 ->
182+ { :ok , { :last_n_days , n } }
183+
184+ { n , "mo" } when n > 0 and n <= 100 ->
185+ { :ok , { :last_n_months , n } }
186+
187+ _ ->
188+ { :error ,
189+ % QueryError { code: :invalid_date_range , message: "Invalid date_range #{ i ( shorthand ) } " } }
170190 end
171191 end
172192
@@ -178,7 +198,7 @@ defmodule Plausible.Stats.ApiQueryParser do
178198 end
179199
180200 defp parse_input_date_range ( unknown ) do
181- { :error , "Invalid date_range #{ i ( unknown ) } " }
201+ { :error , % QueryError { code: :invalid_date_range , message: "Invalid date_range #{ i ( unknown ) } " } }
182202 end
183203
184204 defp parse_date_strings ( from , to ) do
@@ -193,7 +213,9 @@ defmodule Plausible.Stats.ApiQueryParser do
193213 { :ok , to_datetime , _offset } <- DateTime . from_iso8601 ( to ) do
194214 { :ok , { :datetime_range , from_datetime , to_datetime } }
195215 else
196- _ -> { :error , "Invalid date_range '#{ i ( [ from , to ] ) } '." }
216+ _ ->
217+ { :error ,
218+ % QueryError { code: :invalid_date_range , message: "Invalid date_range '#{ i ( [ from , to ] ) } '." } }
197219 end
198220 end
199221
@@ -211,7 +233,11 @@ defmodule Plausible.Stats.ApiQueryParser do
211233 end
212234
213235 def parse_order_by ( nil ) , do: { :ok , nil }
214- def parse_order_by ( order_by ) , do: { :error , "Invalid order_by '#{ i ( order_by ) } '." }
236+
237+ def parse_order_by ( order_by ) ,
238+ do:
239+ { :error ,
240+ % QueryError { code: :invalid_order_by , message: "Invalid order_by '#{ i ( order_by ) } '." } }
215241
216242 defp parse_order_by_entry ( entry ) do
217243 with { :ok , value } <- parse_metric_or_dimension ( entry ) ,
@@ -227,7 +253,7 @@ defmodule Plausible.Stats.ApiQueryParser do
227253 } do
228254 { { :ok , time } , _ } -> { :ok , time }
229255 { _ , { :ok , dimension } } -> { :ok , dimension }
230- _ -> { :error , error_message }
256+ _ -> { :error , % QueryError { code: :invalid_dimensions , message: error_message } }
231257 end
232258 end
233259
@@ -237,10 +263,18 @@ defmodule Plausible.Stats.ApiQueryParser do
237263 parse_metric ( value ) ,
238264 parse_filter_dimension_string ( value )
239265 } do
240- { { :ok , time } , _ , _ } -> { :ok , time }
241- { _ , { :ok , metric } , _ } -> { :ok , metric }
242- { _ , _ , { :ok , dimension } } -> { :ok , dimension }
243- _ -> { :error , "Invalid order_by entry '#{ i ( entry ) } '." }
266+ { { :ok , time } , _ , _ } ->
267+ { :ok , time }
268+
269+ { _ , { :ok , metric } , _ } ->
270+ { :ok , metric }
271+
272+ { _ , _ , { :ok , dimension } } ->
273+ { :ok , dimension }
274+
275+ _ ->
276+ { :error ,
277+ % QueryError { code: :invalid_order_by , message: "Invalid order_by entry '#{ i ( entry ) } '." } }
244278 end
245279 end
246280
@@ -254,15 +288,19 @@ defmodule Plausible.Stats.ApiQueryParser do
254288
255289 defp parse_order_direction ( [ _ , "asc" ] ) , do: { :ok , :asc }
256290 defp parse_order_direction ( [ _ , "desc" ] ) , do: { :ok , :desc }
257- defp parse_order_direction ( entry ) , do: { :error , "Invalid order_by entry '#{ i ( entry ) } '." }
291+
292+ defp parse_order_direction ( entry ) ,
293+ do:
294+ { :error ,
295+ % QueryError { code: :invalid_order_by , message: "Invalid order_by entry '#{ i ( entry ) } '." } }
258296
259297 def parse_include ( include ) when is_map ( include ) do
260298 parsed_include_params_or_error =
261299 include
262300 |> Enum . reduce_while ( { :ok , [ ] } , fn { key , value } , { :ok , acc } ->
263301 case parse_include_entry ( key , value ) do
264302 { :ok , parsed_tuple } -> { :cont , { :ok , acc ++ [ parsed_tuple ] } }
265- { :error , msg } -> { :halt , { :error , msg } }
303+ { :error , query_error } -> { :halt , { :error , query_error } }
266304 end
267305 end )
268306
@@ -272,15 +310,18 @@ defmodule Plausible.Stats.ApiQueryParser do
272310 end
273311
274312 def parse_include ( nil ) , do: { :ok , @ default_include }
275- def parse_include ( include ) , do: { :error , "Invalid include '#{ i ( include ) } '." }
313+
314+ def parse_include ( include ) ,
315+ do: { :error , % QueryError { code: :invalid_include , message: "Invalid include '#{ i ( include ) } '." } }
276316
277317 @ allowed_include_keys Enum . map ( Map . keys ( @ default_include ) , & Atom . to_string / 1 )
278318
279319 defp parse_include_entry ( key , value ) when key in @ allowed_include_keys do
280320 { :ok , { String . to_existing_atom ( key ) , value } }
281321 end
282322
283- defp parse_include_entry ( key , _value ) , do: { :error , "Invalid include key'#{ i ( key ) } '." }
323+ defp parse_include_entry ( key , _value ) ,
324+ do: { :error , % QueryError { code: :invalid_include , message: "Invalid include key'#{ i ( key ) } '." } }
284325
285326 defp parse_pagination ( pagination ) when is_map ( pagination ) do
286327 { :ok , Map . merge ( @ default_pagination , atomize_keys ( pagination ) ) }
@@ -300,28 +341,28 @@ defmodule Plausible.Stats.ApiQueryParser do
300341 if String . length ( property_name ) > 0 do
301342 { :ok , dimension }
302343 else
303- { :error , error_message }
344+ { :error , % QueryError { code: :invalid_filters , message: error_message } }
304345 end
305346
306347 "event:" <> key ->
307348 if key in Filters . event_props ( ) do
308349 { :ok , dimension }
309350 else
310- { :error , error_message }
351+ { :error , % QueryError { code: :invalid_filters , message: error_message } }
311352 end
312353
313354 "visit:" <> key ->
314355 if key in Filters . visit_props ( ) do
315356 { :ok , dimension }
316357 else
317- { :error , error_message }
358+ { :error , % QueryError { code: :invalid_filters , message: error_message } }
318359 end
319360
320361 "segment" ->
321362 { :ok , dimension }
322363
323364 _ ->
324- { :error , error_message }
365+ { :error , % QueryError { code: :invalid_filters , message: error_message } }
325366 end
326367 end
327368
0 commit comments