| defmodule PlausibleWeb.Api.Internal.AnnotationsController do |
| @moduledoc """ |
| Internal API controller for annotations. |
| """ |
| use Plausible |
| use PlausibleWeb, :controller |
| use PlausibleWeb.Plugs.ErrorHandler |
| alias Plausible.Annotations |
| alias Plausible.ChangesetHelpers |
| alias Plausible.Stats.{ApiQueryParser, QueryPeriod} |
| alias PlausibleWeb.Api.Helpers, as: H |
|
|
| def index(conn, params) do |
| user = conn.assigns[:current_user] |
| site = conn.assigns.site |
| site_role = conn.assigns.site_role |
|
|
| with {:ok, input_date_range} <- parse_input_date_range(params), |
| {:ok, relative_date} <- parse_relative_date(params) do |
| now = DateTime.utc_now(:second) |
|
|
| range_in_site_tz = |
| QueryPeriod.build_range_for_site(input_date_range, site, relative_date, now) |
|
|
| case Annotations.get_all_for_site(site, site_role, user, range_in_site_tz) do |
| {:ok, result} -> |
| json(conn, result) |
|
|
| {:error, :not_enough_permissions} -> |
| json(conn, "Not enough permissions to get annotations") |
| end |
| else |
| {:error, message} -> H.bad_request(conn, message) |
| end |
| end |
|
|
| defp parse_input_date_range(%{ |
| "date_range_start" => start, |
| "date_range_end" => end_ |
| }) |
| when is_binary(start) and is_binary(end_) do |
| case ApiQueryParser.parse_input_date_range([start, end_]) do |
| {:ok, parsed} -> {:ok, parsed} |
| {:error, %{message: message}} -> {:error, message} |
| end |
| end |
|
|
| defp parse_input_date_range(%{"date_range" => "realtime"}), do: {:ok, :realtime} |
| defp parse_input_date_range(%{"date_range" => "realtime_30m"}), do: {:ok, :realtime_30m} |
|
|
| defp parse_input_date_range(%{"date_range" => date_range}) do |
| case ApiQueryParser.parse_input_date_range(date_range) do |
| {:ok, parsed} -> {:ok, parsed} |
| {:error, %{message: message}} -> {:error, message} |
| end |
| end |
|
|
| defp parse_input_date_range(_), do: {:error, "Required 'date_range' parameter missing"} |
|
|
| defp parse_relative_date(%{"relative_date" => date}) when is_binary(date) do |
| case Date.from_iso8601(date) do |
| {:ok, date} -> {:ok, date} |
| _ -> {:error, "Failed to convert '#{date}' to date"} |
| end |
| end |
|
|
| defp parse_relative_date(_), do: {:ok, nil} |
|
|
| def create(conn, params) do |
| user = conn.assigns[:current_user] |
| site = conn.assigns.site |
| site_role = conn.assigns.site_role |
|
|
| case Annotations.insert_one(user, site, site_role, params) do |
| {:error, :not_enough_permissions} -> |
| H.not_enough_permissions(conn, "Not enough permissions to create annotation") |
|
|
| {:error, :annotations_limit_reached} -> |
| H.not_enough_permissions(conn, "Annotations limit reached") |
|
|
| {:error, {:invalid_annotation, errors}} when is_list(errors) -> |
| conn |
| |> put_status(400) |
| |> json(%{ |
| error: ChangesetHelpers.serialize_first_error(errors) |
| }) |
|
|
| {:ok, annotation} -> |
| json(conn, annotation) |
| end |
| end |
|
|
| def update(conn, params) do |
| user = conn.assigns[:current_user] |
| site = conn.assigns.site |
| site_role = conn.assigns.site_role |
| annotation_id = normalize_annotation_id_param(params["annotation_id"]) |
|
|
| case Annotations.update_one(user, site, site_role, annotation_id, params) do |
| {:error, :not_enough_permissions} -> |
| H.not_enough_permissions(conn, "Not enough permissions to edit annotation") |
|
|
| {:error, :annotation_not_found} -> |
| annotation_not_found(conn, params["annotation_id"]) |
|
|
| {:error, {:invalid_annotation, errors}} when is_list(errors) -> |
| conn |
| |> put_status(400) |
| |> json(%{ |
| error: ChangesetHelpers.serialize_first_error(errors) |
| }) |
|
|
| {:ok, annotation} -> |
| json(conn, annotation) |
| end |
| end |
|
|
| def delete(conn, params) do |
| user = conn.assigns[:current_user] |
| site = conn.assigns.site |
| site_role = conn.assigns.site_role |
| annotation_id = normalize_annotation_id_param(params["annotation_id"]) |
|
|
| case Annotations.delete_one(user, site, site_role, annotation_id) do |
| {:error, :not_enough_permissions} -> |
| H.not_enough_permissions(conn, "Not enough permissions to delete annotation") |
|
|
| {:error, :annotation_not_found} -> |
| annotation_not_found(conn, params["annotation_id"]) |
|
|
| {:ok, annotation} -> |
| json(conn, annotation) |
| end |
| end |
|
|
| @spec normalize_annotation_id_param(any()) :: nil | pos_integer() |
| defp normalize_annotation_id_param(input) do |
| case Integer.parse(input) do |
| {int_value, ""} when int_value > 0 -> int_value |
| _ -> nil |
| end |
| end |
|
|
| defp annotation_not_found(%Plug.Conn{} = conn, annotation_id_param) do |
| H.not_found(conn, "Annotation not found with ID #{inspect(annotation_id_param)}") |
| end |
| end |
|
|