Skip to content

Commit

Permalink
Moderator notifications (#290)
Browse files Browse the repository at this point in the history
* Add mostly-working login

* Add sign-in form component, helper functions for storing auth token and current user

* Update fetcher to use dynamic headers

* Add register form with /register page

* Add password reset request form and route

* Update password reset form button text

* Rename SigninForm.tsx to SignInForm.tsx

* Add reset password form, mutation

* Move rest of backend auth routes to /admin prefix

* Move backend password reset up in router

* Add sign out page

* Move register and sign-in pages to top level

* Move auth page layout into separate component

* Move reports pages layout into separate component

* Refactor types for client

* Rename auth folder to lowercase

Auth isn't a component by itself

* Refactor SignInForm

* Use theme.palette.augmentColor for accent colors

* Use Container for auth forms

* Remove unnecessary color='primary'

* Merge props into sx in AuthLayout

* Update React imports, replace interfaces with types

* Remvoe a few more interfaces

* Remove FC type return

* Use mutation variable types for auth form annotations

* Use our Link components

* Remove token, use session auth

* Authorization policies (#283)

* Add currentUser GQL query, basic authorization policies for users, feeds, detections, candidates.

* Remove commented out token lifetime

* Add missing getCurrentUser gql definition

* Fix wrong form arg shape

* Underscore unused variables

* Revert router fallback

* Update authorization for auth actions

* Clear session on signOut graphql request

* Update dependencies

* Add moderator flag to users

* Add visible flags to candidates, detections. Add policies and mutations for updating visible flags.

* Add visible columns to detections, candidates

* Add actions for moderator hiding/showing detections and candidate

* Show visible/hidden chip for moderators on candidate row, modal, and page

* Remove setCandidateVisible mutation

* Revert router settings

* Add graphql to Notifications API, show notifying and canceling notifications for a candidate. Add auth policies for moderator

* Add cancel_candidate_notifications mutation for candidate

* Refactor to use auth layout

* Fix useEffect deps array

* Add indexes to meta columns in notifications, subscribers, subscriptions. Add new cancel, notify candidate mutations in frontend

* Invalidate candidate query on detection update

* Re-add missing sign out mutation

* Add confirmation, cancel, and notification submission

* Change invalidateQuery to refetch when creating new notification

---------

Co-authored-by: Paul Cretu <paul.s.cretu@gmail.com>
  • Loading branch information
skanderm and paulcretu authored Jan 3, 2024
1 parent 592e423 commit 0d185b4
Show file tree
Hide file tree
Showing 23 changed files with 1,343 additions and 81 deletions.
1 change: 1 addition & 0 deletions server/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ config :spark, :formatter,
:authentication,
:token,
:policies,
:field_policies,
:actions,
:admin,
:json_api,
Expand Down
5 changes: 4 additions & 1 deletion server/lib/orcasite/notifications.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
defmodule Orcasite.Notifications do
use Ash.Api, extensions: [AshAdmin.Api, AshJsonApi.Api]
use Ash.Api, extensions: [AshAdmin.Api, AshJsonApi.Api, AshGraphql.Api]

resources do
registry Orcasite.Notifications.Registry
Expand All @@ -12,4 +12,7 @@ defmodule Orcasite.Notifications do
json_api do
log_errors? true
end

graphql do
end
end
124 changes: 93 additions & 31 deletions server/lib/orcasite/notifications/resources/notification.ex
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
defmodule Orcasite.Notifications.Notification do
use Ash.Resource,
extensions: [AshAdmin.Resource],
data_layer: AshPostgres.DataLayer
extensions: [AshAdmin.Resource, AshGraphql.Resource],
data_layer: AshPostgres.DataLayer,
authorizers: [Ash.Policy.Authorizer]

alias Orcasite.Notifications.{Event, NotificationInstance, Subscription}

postgres do
table "notifications"
repo Orcasite.Repo

custom_indexes do
index [:meta], using: "gin"
end
end

attributes do
uuid_primary_key :id

attribute :meta, :map
attribute :meta, :map, default: %{}
attribute :active, :boolean, default: true

attribute :event_type, :atom do
constraints one_of: Event.types()
end

create_timestamp :inserted_at
create_timestamp :inserted_at, private?: false, writable?: false
update_timestamp :updated_at
end

Expand All @@ -34,24 +39,16 @@ defmodule Orcasite.Notifications.Notification do
end
end

code_interface do
define_for Orcasite.Notifications

define :notify_new_detection,
action: :notify_new_detection,
args: [:detection_id, :node, :description, :listener_count, :candidate_id]

define :notify_confirmed_candidate,
action: :notify_confirmed_candidate,
args: [:candidate_id, :node]
end
policies do
bypass actor_attribute_equals(:admin, true) do
authorize_if always()
end

resource do
description """
Notification for a specific event type. Once created, all Subscriptions that match this Notification's
event type (new detection, confirmed candidate, etc.) will be notified using the Subscription's particular
channel settings (email, browser notification, webhooks).
"""
bypass actor_attribute_equals(:moderator, true) do
authorize_if action(:notify_confirmed_candidate)
authorize_if action(:cancel_notification)
authorize_if action(:for_candidate)
end
end

actions do
Expand All @@ -64,7 +61,31 @@ defmodule Orcasite.Notifications.Notification do
manual Orcasite.Notifications.ManualReadNotificationsSince
end

read :for_candidate do
prepare build(sort: [inserted_at: :desc])
argument :candidate_id, :string, allow_nil?: false

argument :event_type, :atom do
constraints one_of: Event.types()
end

argument :active, :boolean

filter expr(
fragment("(? @> ?)", meta, expr(%{candidate_id: ^arg(:candidate_id)})) and
if(not is_nil(^arg(:event_type)),
do: event_type == ^arg(:event_type),
else: true
) and
if(not is_nil(^arg(:active)),
do: active == ^arg(:active),
else: true
)
)
end

update :cancel_notification do
accept []
change set_attribute(:active, false)

change fn changeset, _context ->
Expand All @@ -84,9 +105,8 @@ defmodule Orcasite.Notifications.Notification do

create :notify_confirmed_candidate do
description "Create a notification for confirmed candidate (i.e. detection group)"
accept [:candidate_id]
argument :candidate_id, :string
argument :node, :string, allow_nil?: false
accept [:candidate_id, :message]
argument :candidate_id, :string, allow_nil?: false

argument :message, :string do
description """
Expand All @@ -100,10 +120,19 @@ defmodule Orcasite.Notifications.Notification do
change set_attribute(:event_type, :confirmed_candidate)

change fn changeset, _context ->
candidate_id =
Ash.Changeset.get_argument(changeset, :candidate_id)
|> IO.inspect(label: "candidate id")

candidate =
Orcasite.Radio.Candidate
|> Orcasite.Radio.get(candidate_id)
|> Orcasite.Radio.load!(:feed)

changeset
|> Ash.Changeset.change_attribute(:meta, %{
candidate_id: Ash.Changeset.get_argument(changeset, :candidate_id),
node: Ash.Changeset.get_argument(changeset, :node),
candidate_id: candidate_id,
node: candidate.feed.slug,
message: Ash.Changeset.get_argument(changeset, :message)
})
end
Expand Down Expand Up @@ -134,6 +163,36 @@ defmodule Orcasite.Notifications.Notification do
end
end

code_interface do
define_for Orcasite.Notifications

define :notify_new_detection,
action: :notify_new_detection,
args: [:detection_id, :node, :description, :listener_count, :candidate_id]

define :notify_confirmed_candidate,
action: :notify_confirmed_candidate,
args: [:candidate_id]
end

resource do
description """
Notification for a specific event type. Once created, all Subscriptions that match this Notification's
event type (new detection, confirmed candidate, etc.) will be notified using the Subscription's particular
channel settings (email, browser notification, webhooks).
"""
end

admin do
table_columns [:id, :meta, :event_type, :inserted_at]

format_fields meta: {Jason, :encode!, []}

form do
field :event_type, type: :default
end
end

changes do
change fn changeset, _context ->
changeset
Expand Down Expand Up @@ -162,13 +221,16 @@ defmodule Orcasite.Notifications.Notification do
on: :create
end

admin do
table_columns [:id, :meta, :event_type, :inserted_at]
graphql do
type :notification

format_fields meta: {Jason, :encode!, []}
queries do
list :notifications_for_candidate, :for_candidate
end

form do
field :event_type, type: :default
mutations do
create :notify_confirmed_candidate, :notify_confirmed_candidate
update :cancel_notification, :cancel_notification
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule Orcasite.Notifications.NotificationInstance do
attributes do
uuid_primary_key :id

attribute :meta, :map
attribute :meta, :map, default: %{}

attribute :channel, :atom do
constraints one_of: [:email]
Expand Down
18 changes: 11 additions & 7 deletions server/lib/orcasite/notifications/resources/subscriber.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ defmodule Orcasite.Notifications.Subscriber do
postgres do
table "subscribers"
repo Orcasite.Repo

custom_indexes do
index [:meta], using: "gin"
end
end

identities do
Expand All @@ -24,7 +28,7 @@ defmodule Orcasite.Notifications.Subscriber do
constraints one_of: [:individual, :organization]
end

attribute :meta, :map
attribute :meta, :map, default: %{}

create_timestamp :inserted_at
update_timestamp :updated_at
Expand All @@ -39,12 +43,6 @@ defmodule Orcasite.Notifications.Subscriber do
define :by_email, args: [:email]
end

resource do
description """
A subscriber object. Can relate to an individual, an organization, a newsletter, or an admin.
"""
end

authentication do
api Orcasite.Notifications

Expand Down Expand Up @@ -78,6 +76,12 @@ defmodule Orcasite.Notifications.Subscriber do
end
end

resource do
description """
A subscriber object. Can relate to an individual, an organization, a newsletter, or an admin.
"""
end

actions do
defaults [:create, :read, :update, :destroy]

Expand Down
66 changes: 35 additions & 31 deletions server/lib/orcasite/notifications/resources/subscription.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,48 @@ defmodule Orcasite.Notifications.Subscription do
postgres do
table "subscriptions"
repo Orcasite.Repo

custom_indexes do
index [:meta], using: "gin"
end
end

identities do
# Needed by magic_token. Primary key doesn't show up as an identity otherwise
identity :id, [:id]
end

attributes do
uuid_primary_key :id

attribute :name, :string
attribute :meta, :map, default: %{}

attribute :active, :boolean, default: true

attribute :event_type, :atom do
constraints one_of: Event.types()
end

attribute :last_notified_at, :utc_datetime_usec

create_timestamp :inserted_at
update_timestamp :updated_at
end

relationships do
belongs_to :subscriber, Subscriber
has_many :notification_instances, NotificationInstance

many_to_many :notifications, Notification do
through NotificationInstance
source_attribute_on_join_resource :subscription_id
destination_attribute_on_join_resource :notification_id
end

belongs_to :last_notification, Notification
end

authentication do
api Orcasite.Notifications

Expand Down Expand Up @@ -48,37 +83,6 @@ defmodule Orcasite.Notifications.Subscription do
end
end

attributes do
uuid_primary_key :id

attribute :name, :string
attribute :meta, :map

attribute :active, :boolean, default: true

attribute :event_type, :atom do
constraints one_of: Event.types()
end

attribute :last_notified_at, :utc_datetime_usec

create_timestamp :inserted_at
update_timestamp :updated_at
end

relationships do
belongs_to :subscriber, Subscriber
has_many :notification_instances, NotificationInstance

many_to_many :notifications, Notification do
through NotificationInstance
source_attribute_on_join_resource :subscription_id
destination_attribute_on_join_resource :notification_id
end

belongs_to :last_notification, Notification
end

code_interface do
define_for Orcasite.Notifications

Expand Down
Loading

0 comments on commit 0d185b4

Please sign in to comment.