Skip to content

Commit

Permalink
Merge branch 'search-spike' of github.com:etalab/transport-site into …
Browse files Browse the repository at this point in the history
…search-spike
  • Loading branch information
thbar committed Jan 9, 2024
2 parents b848359 + 223b326 commit b2269ca
Show file tree
Hide file tree
Showing 57 changed files with 409 additions and 218 deletions.
3 changes: 1 addition & 2 deletions apps/gbfs/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ defmodule GBFS.MixProject do
lockfile: "../../mix.lock",
elixir: "~> 1.8",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix] ++ Mix.compilers(),
start_permanent: Mix.env() == :prod,
deps: deps(),
test_coverage: [tool: ExCoveralls],
Expand All @@ -38,7 +37,7 @@ defmodule GBFS.MixProject do
[
{:cachex, "~> 3.5"},
{:httpoison, "~> 2.1"},
{:phoenix, "~> 1.6.2"},
{:phoenix, "~> 1.7.0"},
{:sweet_xml, ">= 0.0.0"},
{:jason, ">= 0.0.0"},
{:cors_plug, "~> 3.0"},
Expand Down
9 changes: 0 additions & 9 deletions apps/shared/lib/helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,4 @@ defmodule Helpers do
dates -> dates |> Enum.max(DateTime) |> DateTime.to_iso8601()
end
end

@spec admin?(map | nil) :: boolean
def admin?(%{} = user) do
user
|> Map.get("organizations", [])
|> Enum.any?(fn org -> org["slug"] == "equipe-transport-data-gouv-fr" end)
end

def admin?(nil), do: false
end
2 changes: 1 addition & 1 deletion apps/shared/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ defmodule Shared.MixProject do
{:finch, "~> 0.8"},
# Required for the ConditionalJSONEncoder shared component, but
# there is probably a way to avoid that?
{:phoenix, "~> 1.6.2"},
{:phoenix, "~> 1.7.0"},
# The global app config references Sentry.LoggerBackend. We add it in "shared"
# as an implicit dependency, to ensure `Sentry.LoggerBackend` is always defined,
# otherwise running tests for an individual umbrella sub-app will raise error.
Expand Down
2 changes: 1 addition & 1 deletion apps/transport/client/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4041,7 +4041,7 @@ path-type@^4.0.0:
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==

"phoenix@file:../../../deps/phoenix":
version "1.6.16"
version "1.7.10"

"phoenix_html@file:../../../deps/phoenix_html":
version "3.3.3"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ defmodule Transport.Jobs.DatasetNowOnNAPNotificationJob do
Phoenix.View.render_to_string(TransportWeb.EmailView, "dataset_now_on_nap.html",
dataset_url: TransportWeb.Router.Helpers.dataset_url(TransportWeb.Endpoint, :details, dataset.slug),
dataset_custom_title: dataset.custom_title,
contact_email_address: Application.get_env(:transport, :contact_email),
espace_producteur_url: TransportWeb.Router.Helpers.page_url(TransportWeb.Endpoint, :espace_producteur)
contact_email_address: Application.get_env(:transport, :contact_email)
)
)

Expand Down
23 changes: 17 additions & 6 deletions apps/transport/lib/jobs/update_contacts_job.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,23 @@ defmodule Transport.Jobs.UpdateContactsJob do
DB.Contact.base_query()
|> where([contact: c], c.datagouv_user_id in ^ids)
|> DB.Repo.all()
|> Enum.each(fn %DB.Contact{datagouv_user_id: datagouv_user_id} = contact ->
{:ok, %{"organizations" => organizations}} = Datagouvfr.Client.User.get(datagouv_user_id)
|> Enum.each(&update_contact/1)
end

defp update_contact(%DB.Contact{datagouv_user_id: datagouv_user_id} = contact) do
# https://doc.data.gouv.fr/api/reference/#/users/get_user
# 404 status code: User not found
# 410 status code: User is not active or has been deleted
case Datagouvfr.Client.User.get(datagouv_user_id) do
{:ok, %{"organizations" => organizations}} ->
contact
|> DB.Contact.changeset(%{organizations: organizations})
|> DB.Repo.update!()

contact
|> DB.Contact.changeset(%{organizations: organizations})
|> DB.Repo.update!()
end)
{:error, reason} when reason in [:not_found, :gone] ->
contact
|> DB.Contact.changeset(%{organizations: [], datagouv_user_id: nil})
|> DB.Repo.update!()
end
end
end
19 changes: 19 additions & 0 deletions apps/transport/lib/transport_web.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ defmodule TransportWeb do
below. Instead, define any helper function in modules
and import those modules here.
"""
def static_paths,
do: ~w(js css fonts images data favicon.ico documents BingSiteAuth.xml google5be4b09db1274976.html demo_rt.html)

def controller do
quote do
Expand All @@ -26,6 +28,8 @@ defmodule TransportWeb do
import TransportWeb.PaginationHelpers
alias TransportWeb.ErrorView
import Phoenix.LiveView.Controller

unquote(verified_routes())
end
end

Expand Down Expand Up @@ -54,6 +58,12 @@ defmodule TransportWeb do
end
end

def view_helpers do
quote do
unquote(verified_routes())
end
end

def router do
quote do
use Phoenix.Router
Expand All @@ -78,6 +88,15 @@ defmodule TransportWeb do
end
end

def verified_routes do
quote do
use Phoenix.VerifiedRoutes,
endpoint: TransportWeb.Endpoint,
router: TransportWeb.Router,
statics: TransportWeb.static_paths()
end
end

@doc """
When used, dispatch to the appropriate controller/view/etc.
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,10 @@ defmodule TransportWeb.PageController do
{conn, []}
end

conn |> assign(:datasets, datasets) |> render("espace_producteur.html")
conn
|> assign(:datasets, datasets)
|> TransportWeb.Session.set_is_producer(datasets)
|> render("espace_producteur.html")
end

defp aoms_with_dataset do
Expand Down
25 changes: 9 additions & 16 deletions apps/transport/lib/transport_web/controllers/session_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -127,24 +127,17 @@ defmodule TransportWeb.SessionController do
end

def save_current_user(%Plug.Conn{} = conn, %{} = user_params) do
conn |> put_session(:current_user, user_params |> user_params_for_session())
conn
|> put_session(:current_user, user_params_for_session(user_params))
|> TransportWeb.Session.set_is_producer(user_params)
|> TransportWeb.Session.set_is_admin(user_params)
end

@doc """
iex> pan_org = %{"slug" => "equipe-transport-data-gouv-fr", "name" => "PAN"}
iex> other_org = %{"slug" => "foo-inc", "name" => "Foo Inc"}
iex> user_params_for_session(%{"foo" => "bar", "organizations" => [pan_org, other_org]})
%{"foo" => "bar", "organizations" => [pan_org]}
"""
def user_params_for_session(%{} = params) do
Map.put(
params,
"organizations",
Enum.filter(
params["organizations"],
&match?(%{"slug" => "equipe-transport-data-gouv-fr"}, &1)
)
)
defp user_params_for_session(%{} = params) do
# Remove the list of `organizations` from the final map: it's already stored in the database
# and maintained up-to-date by `Transport.Jobs.UpdateContactsJob`
# and it can be too big to be stored in a cookie
Map.delete(params, "organizations")
end

defp get_redirect_path(%Plug.Conn{} = conn) do
Expand Down
2 changes: 1 addition & 1 deletion apps/transport/lib/transport_web/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ defmodule TransportWeb.Endpoint do
at: "/",
from: :transport,
gzip: Mix.env() == :prod,
only: ~w(js css fonts images data favicon.ico documents BingSiteAuth.xml google5be4b09db1274976.html demo_rt.html)
only: TransportWeb.static_paths()
)

# Code reloading can be explicitly enabled under the
Expand Down
5 changes: 3 additions & 2 deletions apps/transport/lib/transport_web/live/backoffice/jobs_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ defmodule TransportWeb.Backoffice.JobsLive do
# https://hexdocs.pm/phoenix_live_view/security-model.html#disconnecting-all-instances-of-a-given-live-user
#
def ensure_admin_auth_or_redirect(socket, current_user, func) do
if current_user && TransportWeb.Router.is_transport_data_gouv_member?(current_user) do
socket = assign(socket, current_user: current_user)

if TransportWeb.Session.is_admin?(socket) do
# We track down the current admin so that it can be used by next actions
socket = assign(socket, current_admin_user: current_user)
# Then call the remaining code, which is expected to return the socket
func.(socket)
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule TransportWeb.Backoffice.ProxyConfigLive do
"""
use Phoenix.LiveView
alias Transport.Telemetry
import TransportWeb.Backoffice.JobsLive, only: [ensure_admin_auth_or_redirect: 3]
import TransportWeb.Router.Helpers

# The number of past days we want to report on (as a positive integer).
Expand All @@ -20,25 +21,6 @@ defmodule TransportWeb.Backoffice.ProxyConfigLive do
end)}
end

#
# If one calls "redirect" and does not leave immediately, the remaining code will
# be executed, opening security issues. This method goal is to minimize this risk.
# See https://hexdocs.pm/phoenix_live_view/security-model.html for overall docs.
#
# Also, disconnect will have to be handled:
# https://hexdocs.pm/phoenix_live_view/security-model.html#disconnecting-all-instances-of-a-given-live-user
#
defp ensure_admin_auth_or_redirect(socket, current_user, func) do
if current_user && TransportWeb.Router.is_transport_data_gouv_member?(current_user) do
# We track down the current admin so that it can be used by next actions
socket = assign(socket, current_admin_user: current_user)
# Then call the remaining code, which is expected to return the socket
func.(socket)
else
redirect(socket, to: "/login")
end
end

defp schedule_next_update_data do
Process.send_after(self(), :update_data, 1000)
end
Expand All @@ -57,8 +39,7 @@ defmodule TransportWeb.Backoffice.ProxyConfigLive do
end

def handle_event("refresh_proxy_config", _value, socket) do
if socket.assigns.current_admin_user, do: config_module().clear_config_cache!()

config_module().clear_config_cache!()
{:noreply, socket}
end

Expand Down
11 changes: 3 additions & 8 deletions apps/transport/lib/transport_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ defmodule TransportWeb.Router do
plug(:canonical_host)
plug(:accepts, ["html"])
plug(:fetch_session)
plug(:fetch_flash)
plug(:fetch_live_flash)
plug(:protect_from_forgery)
plug(TransportWeb.Plugs.PutLocale)
Expand Down Expand Up @@ -314,6 +315,7 @@ defmodule TransportWeb.Router do
end

defp assign_current_user(conn, _) do
# `current_user` is set by TransportWeb.SessionController.user_params_for_session/1
assign(conn, :current_user, get_session(conn, :current_user))
end

Expand Down Expand Up @@ -352,13 +354,6 @@ defmodule TransportWeb.Router do
end
end

# NOTE: method visibility set to public because we need to call the same logic from LiveView
def is_transport_data_gouv_member?(current_user) do
current_user
|> Map.get("organizations", [])
|> Enum.any?(fn org -> org["slug"] == "equipe-transport-data-gouv-fr" end)
end

# Check that a secret key is passed in the URL in the `export_key` query parameter
defp check_export_secret_key(%Plug.Conn{params: params} = conn, _) do
export_key_value = Map.get(params, "export_key", "")
Expand All @@ -375,7 +370,7 @@ defmodule TransportWeb.Router do
end

defp transport_data_gouv_member(%Plug.Conn{} = conn, _) do
if is_transport_data_gouv_member?(conn.assigns[:current_user]) do
if TransportWeb.Session.is_admin?(conn) do
conn
else
conn
Expand Down
64 changes: 64 additions & 0 deletions apps/transport/lib/transport_web/session.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
defmodule TransportWeb.Session do
@moduledoc """
Web session getters and setters.
"""
import Ecto.Query
import Plug.Conn

@is_admin_key_name "is_admin"
@is_producer_key_name "is_producer"

@doc """
Are you a data producer?
You're a data producer if you're a member of an organization with an active dataset
on transport.data.gouv.fr.
This is set when you log in and refreshed when you visit your "Espace producteur".
"""
@spec set_is_producer(Plug.Conn.t(), map() | [DB.Dataset.t()]) :: Plug.Conn.t()
def set_is_producer(%Plug.Conn{} = conn, %{"organizations" => _} = params) do
set_session_attribute_attribute(conn, @is_producer_key_name, is_producer?(params))
end

def set_is_producer(%Plug.Conn{} = conn, datasets_for_user) when is_list(datasets_for_user) do
is_producer = not Enum.empty?(datasets_for_user)
set_session_attribute_attribute(conn, @is_producer_key_name, is_producer)
end

@doc """
Are you a transport.data.gouv.fr admin?
You're an admin if you're a member of the PAN organization on data.gouv.fr.
"""
def set_is_admin(%Plug.Conn{} = conn, %{"organizations" => _} = params) do
set_session_attribute_attribute(conn, @is_admin_key_name, is_admin?(params))
end

def is_admin?(%{"organizations" => orgs}) do
Enum.any?(orgs, &(&1["slug"] == "equipe-transport-data-gouv-fr"))
end

def is_admin?(%Plug.Conn{} = conn) do
conn |> current_user() |> Map.get(@is_admin_key_name, false)
end

def is_admin?(%Phoenix.LiveView.Socket{assigns: %{current_user: current_user}}) do
Map.get(current_user, @is_admin_key_name, false)
end

def is_producer?(%Plug.Conn{} = conn) do
conn |> current_user() |> Map.get(@is_producer_key_name, false)
end

def is_producer?(%{"organizations" => orgs}) do
org_ids = Enum.map(orgs, & &1["id"])
DB.Dataset.base_query() |> where([dataset: d], d.organization_id in ^org_ids) |> DB.Repo.exists?()
end

@spec set_session_attribute_attribute(Plug.Conn.t(), binary(), boolean()) :: Plug.Conn.t()
defp set_session_attribute_attribute(%Plug.Conn{} = conn, key, value) do
current_user = current_user(conn)
conn |> put_session(:current_user, Map.put(current_user, key, value))
end

defp current_user(%Plug.Conn{} = conn), do: get_session(conn, :current_user, %{})
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<section :if={admin?(@conn.assigns[:current_user])} class="pt-48">
<section :if={TransportWeb.Session.is_admin?(@conn)} class="pt-48">
<h2><%= dgettext("page-dataset-details", "Dataset scores") %></h2>
<div class="panel" id="scores-chart">
<div id="vega-vis"></div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<h1>
<%= @dataset.custom_title %>
</h1>
<%= if admin?(assigns[:current_user]) do %>
<%= if TransportWeb.Session.is_admin?(@conn) do %>
<i class="fa fa-external-link-alt"></i>
<%= link("backoffice", to: backoffice_page_path(@conn, :edit, @dataset.id)) %>
<%= render("_dataset_scores.html", dataset_scores: @dataset_scores, locale: locale) %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@
<a href={dataset_path(@conn, :details, dataset.slug)}>
<%= dataset.custom_title %>
</a>
<%= if admin?(assigns[:current_user]) do %>
<%= if TransportWeb.Session.is_admin?(@conn) do %>
<span class="dataset-backoffice-link">
<i class="fa fa-external-link-alt"></i>
<%= link("backoffice", to: backoffice_page_path(@conn, :edit, dataset.id)) %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Bonjour,

Votre jeu de données [<%= @dataset_custom_title %>](<%= @dataset_url %>) a bien été référencé sur le Point d’Accès National aux données de transport, [transport.data.gouv.fr](https://transport.data.gouv.fr).

Rendez-vous sur votre [Espace Producteur](<%= @espace_producteur_url %>) pour mettre à jour vos données ou vous inscrire à des notifications en cas de péremption, d’indisponibilité ou d’erreurs bloquantes sur votre jeu de données.
Rendez-vous sur votre <%= link_for_espace_producteur(:dataset_now_on_nap) %> pour mettre à jour vos données ou vous inscrire à des notifications en cas de péremption, d’indisponibilité ou d’erreurs bloquantes sur votre jeu de données.

Si vous avez des questions, n’hésitez pas à contacter notre équipe à l’adresse : <%= @contact_email_address %>.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Vous êtes inscrit à des notifications concernant les jeux de données suivants
</ul>
<% end %>

Les notifications vous permettent d’être alerté en cas d’expiration, d’indisponibilité et d’erreurs de vos données. Rendez-vous sur votre [Espace Producteur](<%= TransportWeb.Router.Helpers.page_url(TransportWeb.Endpoint, :espace_producteur) %>) pour les gérer de manière autonome.
Les notifications vous permettent d’être alerté en cas d’expiration, d’indisponibilité et d’erreurs de vos données. Rendez-vous sur votre <%= link_for_espace_producteur(:periodic_reminder_producer_with_subscriptions) %> pour les gérer de manière autonome.

<%= if @has_other_producers_subscribers do %>
Les autres personnes inscrites à ces notifications sont : <%= @other_producers_subscribers %>.
Expand Down
Loading

0 comments on commit b2269ca

Please sign in to comment.