1- defmodule Plausible.Stats.QueryParser do
1+ defmodule Plausible.Stats.ApiQueryParser do
22 @ moduledoc false
33
44 use Plausible
55
66 alias Plausible.Stats . { Filters , Metrics , DateTimeRange , JSONSchema }
77
8- def parse ( site , schema_type , params , now \\ nil ) when is_map ( params ) do
9- now = now || Plausible.Stats.Query.Test . get_fixed_now ( )
10- date = now |> DateTime . shift_zone! ( site . timezone ) |> DateTime . to_date ( )
8+ def parse ( schema_type , params ) when is_map ( params ) do
9+ now = Plausible.Stats.Query.Test . get_fixed_now ( )
10+ input_date_range = Map . get ( params , "date_range" )
1111
1212 with :ok <- JSONSchema . validate ( schema_type , params ) ,
13- { :ok , date , now } <- parse_date ( site , Map . get ( params , "date" ) , date , now ) ,
14- { :ok , raw_time_range } <-
15- parse_time_range ( site , Map . get ( params , "date_range" ) , date , now ) ,
16- utc_time_range = raw_time_range |> DateTimeRange . to_timezone ( "Etc/UTC" ) ,
13+ { :ok , input_date_range } <- parse_input_date_range ( input_date_range ) ,
1714 { :ok , metrics } <- parse_metrics ( Map . fetch! ( params , "metrics" ) ) ,
1815 { :ok , filters } <- parse_filters ( params [ "filters" ] ) ,
1916 { :ok , dimensions } <- parse_dimensions ( params [ "dimensions" ] ) ,
2017 { :ok , order_by } <- parse_order_by ( params [ "order_by" ] ) ,
2118 { :ok , pagination } <- parse_pagination ( params [ "pagination" ] ) ,
22- { :ok , include } <- parse_include ( params [ "include" ] , site ) do
19+ { :ok , include } <- parse_include ( params [ "include" ] ) do
2320 { :ok ,
2421 Plausible.Stats.ParsedQueryParams . new! ( % {
2522 now: now ,
26- utc_time_range: utc_time_range ,
23+ input_date_range: input_date_range ,
2724 metrics: metrics ,
2825 filters: filters ,
2926 dimensions: dimensions ,
@@ -42,6 +39,13 @@ defmodule Plausible.Stats.QueryParser do
4239
4340 def parse_date_range_pair ( _site , unknown ) , do: { :error , "Invalid date_range '#{ i ( unknown ) } '." }
4441
42+ defp date_range_from_date_strings ( site , from , to ) do
43+ with { :ok , from_date } <- Date . from_iso8601 ( from ) ,
44+ { :ok , to_date } <- Date . from_iso8601 ( to ) do
45+ { :ok , DateTimeRange . new! ( from_date , to_date , site . timezone ) }
46+ end
47+ end
48+
4549 defp parse_metrics ( metrics ) when is_list ( metrics ) do
4650 parse_list ( metrics , & parse_metric / 1 )
4751 end
@@ -155,94 +159,43 @@ defmodule Plausible.Stats.QueryParser do
155159 { :ok , [ ] }
156160 end
157161
158- defp parse_date ( site , date_string , _date , _now ) when is_binary ( date_string ) do
159- with { :ok , date } <- Date . from_iso8601 ( date_string ) ,
160- { :ok , datetime } <- DateTime . new ( date , ~T[ 00:00:00] , site . timezone ) do
161- { :ok , date , datetime }
162- else
163- { :gap , just_before , _just_after } -> just_before
164- { :ambiguous , first_datetime , _second_datetime } -> first_datetime
165- _ -> { :error , "Invalid date '#{ date_string } '." }
166- end
167- end
168-
169- defp parse_date ( _site , _date_string , date , now ) do
170- { :ok , date , now }
171- end
172-
173- defp parse_time_range ( _site , date_range , _date , now ) when date_range in [ "realtime" , "30m" ] do
174- duration_minutes =
175- case date_range do
176- "realtime" -> 5
177- "30m" -> 30
178- end
179-
180- first_datetime = DateTime . shift ( now , minute: - duration_minutes )
181- last_datetime = DateTime . shift ( now , second: 5 )
182-
183- { :ok , DateTimeRange . new! ( first_datetime , last_datetime ) }
184- end
185-
186- defp parse_time_range ( site , "day" , date , _now ) do
187- { :ok , DateTimeRange . new! ( date , date , site . timezone ) }
188- end
189-
190- defp parse_time_range ( site , "month" , date , _now ) do
191- last = date |> Date . end_of_month ( )
192- first = last |> Date . beginning_of_month ( )
193- { :ok , DateTimeRange . new! ( first , last , site . timezone ) }
194- end
195-
196- defp parse_time_range ( site , "year" , date , _now ) do
197- last = date |> Plausible.Times . end_of_year ( )
198- first = last |> Plausible.Times . beginning_of_year ( )
199- { :ok , DateTimeRange . new! ( first , last , site . timezone ) }
200- end
201-
202- defp parse_time_range ( site , "all" , date , _now ) do
203- start_date = Plausible.Sites . stats_start_date ( site ) || date
162+ defp parse_input_date_range ( "realtime" ) , do: { :ok , :realtime }
163+ defp parse_input_date_range ( "30m" ) , do: { :ok , :realtime_30m }
164+ defp parse_input_date_range ( "day" ) , do: { :ok , :day }
165+ defp parse_input_date_range ( "month" ) , do: { :ok , :month }
166+ defp parse_input_date_range ( "year" ) , do: { :ok , :year }
167+ defp parse_input_date_range ( "all" ) , do: { :ok , :all }
204168
205- { :ok , DateTimeRange . new! ( start_date , date , site . timezone ) }
206- end
207-
208- defp parse_time_range ( site , shorthand , date , _now ) when is_binary ( shorthand ) do
169+ defp parse_input_date_range ( shorthand ) when is_binary ( shorthand ) do
209170 case Integer . parse ( shorthand ) do
210- { n , "d" } when n > 0 and n <= 5_000 ->
211- last = date |> Date . add ( - 1 )
212- first = date |> Date . add ( - n )
213- { :ok , DateTimeRange . new! ( first , last , site . timezone ) }
214-
215- { n , "mo" } when n > 0 and n <= 100 ->
216- last = date |> Date . shift ( month: - 1 ) |> Date . end_of_month ( )
217- first = date |> Date . shift ( month: - n ) |> Date . beginning_of_month ( )
218- { :ok , DateTimeRange . new! ( first , last , site . timezone ) }
219-
220- _ ->
221- { :error , "Invalid date_range #{ i ( shorthand ) } " }
171+ { n , "d" } when n > 0 and n <= 5_000 -> { :ok , { :last_n_days , n } }
172+ { n , "mo" } when n > 0 and n <= 100 -> { :ok , { :last_n_months , n } }
173+ _ -> { :error , "Invalid date_range #{ i ( shorthand ) } " }
222174 end
223175 end
224176
225- defp parse_time_range ( site , [ from , to ] , _date , _now ) when is_binary ( from ) and is_binary ( to ) do
226- case date_range_from_date_strings ( site , from , to ) do
227- { :ok , date_range } -> { :ok , date_range }
228- { :error , _ } -> date_range_from_timestamps ( from , to )
177+ defp parse_input_date_range ( [ from , to ] ) when is_binary ( from ) and is_binary ( to ) do
178+ case parse_date_strings ( from , to ) do
179+ { :ok , dates } -> { :ok , dates }
180+ { :error , _ } -> parse_timestamp_strings ( from , to )
229181 end
230182 end
231183
232- defp parse_time_range ( _site , unknown , _date , _now ) ,
233- do: { :error , "Invalid date_range #{ i ( unknown ) } " }
184+ defp parse_input_date_range ( unknown ) do
185+ { :error , "Invalid date_range #{ i ( unknown ) } " }
186+ end
234187
235- defp date_range_from_date_strings ( site , from , to ) do
188+ defp parse_date_strings ( from , to ) do
236189 with { :ok , from_date } <- Date . from_iso8601 ( from ) ,
237190 { :ok , to_date } <- Date . from_iso8601 ( to ) do
238- { :ok , DateTimeRange . new! ( from_date , to_date , site . timezone ) }
191+ { :ok , { :date_range , from_date , to_date } }
239192 end
240193 end
241194
242- defp date_range_from_timestamps ( from , to ) do
195+ defp parse_timestamp_strings ( from , to ) do
243196 with { :ok , from_datetime , _offset } <- DateTime . from_iso8601 ( from ) ,
244197 { :ok , to_datetime , _offset } <- DateTime . from_iso8601 ( to ) do
245- { :ok , DateTimeRange . new! ( from_datetime , to_datetime ) }
198+ { :ok , { :datetime_range , from_datetime , to_datetime } }
246199 else
247200 _ -> { :error , "Invalid date_range '#{ i ( [ from , to ] ) } '." }
248201 end
@@ -307,14 +260,14 @@ defmodule Plausible.Stats.QueryParser do
307260 defp parse_order_direction ( [ _ , "desc" ] ) , do: { :ok , :desc }
308261 defp parse_order_direction ( entry ) , do: { :error , "Invalid order_by entry '#{ i ( entry ) } '." }
309262
310- def parse_include ( include , site ) when is_map ( include ) do
263+ def parse_include ( include ) when is_map ( include ) do
311264 with { :ok , include } <- atomize_include_keys ( include ) do
312- update_comparisons_date_range ( include , site )
265+ parse_comparison_date_range ( include )
313266 end
314267 end
315268
316- def parse_include ( nil , _site ) , do: { :ok , nil }
317- def parse_include ( include , _site ) , do: { :error , "Invalid include '#{ i ( include ) } '." }
269+ def parse_include ( nil ) , do: { :ok , nil }
270+ def parse_include ( include ) , do: { :error , "Invalid include '#{ i ( include ) } '." }
318271
319272 defp atomize_include_keys ( map ) do
320273 expected_keys =
@@ -329,13 +282,13 @@ defmodule Plausible.Stats.QueryParser do
329282 end
330283 end
331284
332- defp update_comparisons_date_range ( % { comparisons: % { date_range: date_range } } = include , site ) do
333- with { :ok , parsed_date_range } <- parse_time_range ( site , date_range , nil , nil ) do
285+ defp parse_comparison_date_range ( % { comparisons: % { date_range: date_range } } = include ) do
286+ with { :ok , parsed_date_range } <- parse_input_date_range ( date_range ) do
334287 { :ok , put_in ( include , [ :comparisons , :date_range ] , parsed_date_range ) }
335288 end
336289 end
337290
338- defp update_comparisons_date_range ( include , _site ) , do: { :ok , include }
291+ defp parse_comparison_date_range ( include ) , do: { :ok , include }
339292
340293 defp parse_pagination ( pagination ) when is_map ( pagination ) do
341294 { :ok ,
0 commit comments