Skip to content

Commit 4a5b1af

Browse files
committed
Remove superfluous feature, add behaviour that diverges from segments
1 parent e3d170f commit 4a5b1af

9 files changed

Lines changed: 106 additions & 24 deletions

File tree

assets/js/dashboard/annotations/routeless-annotations-modals.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export const RoutelessAnnotationModals = () => {
117117
{modal?.type === 'update-annotation' && (
118118
<UpdateAnnotationModal
119119
user={user}
120-
siteAnnotationsAvailable={site.siteSegmentsAvailable}
120+
siteAnnotationsAvailable={site.siteAnnotationsAvailable}
121121
annotation={modal.annotation}
122122
notePlaceholder={''}
123123
onClose={() => {
@@ -139,7 +139,7 @@ export const RoutelessAnnotationModals = () => {
139139
{modal?.type === 'create-annotation' && (
140140
<CreateAnnotationModal
141141
user={user}
142-
siteAnnotationsAvailable={site.siteSegmentsAvailable}
142+
siteAnnotationsAvailable={site.siteAnnotationsAvailable}
143143
notePlaceholder={modal.annotation.note}
144144
initialType={modal.annotation.type}
145145
initialDatetime={modal.annotation.datetime}

assets/js/dashboard/site-context.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ describe('parseSiteFromDataset', () => {
1717
data-funnels-available="true"
1818
data-exploration-available="false"
1919
data-site-segments-available="true"
20+
data-site-annotations-available="true"
2021
data-props-available="true"
2122
data-revenue-goals='[{"currency":"USD","display_name":"Purchase"}]'
2223
data-funnels='[{"id":1,"name":"From homepage to login","steps_count":3}]'
@@ -46,6 +47,7 @@ describe('parseSiteFromDataset', () => {
4647
propsAvailable: true,
4748
explorationAvailable: false,
4849
siteSegmentsAvailable: true,
50+
siteAnnotationsAvailable: true,
4951
revenueGoals: [{ currency: 'USD', display_name: 'Purchase' }],
5052
funnels: [{ id: 1, name: 'From homepage to login', steps_count: 3 }],
5153
hasProps: true,

assets/js/dashboard/site-context.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export function parseSiteFromDataset(dataset: DOMStringMap): PlausibleSite {
1010
propsAvailable: dataset.propsAvailable === 'true',
1111
explorationAvailable: dataset.explorationAvailable === 'true',
1212
siteSegmentsAvailable: dataset.siteSegmentsAvailable === 'true',
13+
siteAnnotationsAvailable: dataset.siteAnnotationsAvailable === 'true',
1314
conversionsOptedOut: dataset.conversionsOptedOut === 'true',
1415
funnelsOptedOut: dataset.funnelsOptedOut === 'true',
1516
propsOptedOut: dataset.propsOptedOut === 'true',
@@ -39,6 +40,7 @@ export const siteContextDefaultValue = {
3940
explorationAvailable: false,
4041
propsAvailable: false,
4142
siteSegmentsAvailable: false,
43+
siteAnnotationsAvailable: false,
4244
conversionsOptedOut: false,
4345
funnelsOptedOut: false,
4446
propsOptedOut: false,

assets/test-utils/app-context-providers.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export const DEFAULT_SITE: PlausibleSite = {
3434
explorationAvailable: false,
3535
propsAvailable: false,
3636
siteSegmentsAvailable: false,
37+
siteAnnotationsAvailable: false,
3738
conversionsOptedOut: false,
3839
funnelsOptedOut: false,
3940
propsOptedOut: false,

lib/plausible/annotations/annotations.ex

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ defmodule Plausible.Annotations do
1717

1818
@max_annotations 500
1919

20-
def get_all_for_site(%Plausible.Site{} = site, site_role) do
20+
@spec get_all_for_site(Plausible.Site.t(), atom(), pos_integer()) ::
21+
{:error, :not_enough_permissions} | {:ok, list(Annotation.t())}
22+
def get_all_for_site(%Plausible.Site{} = site, site_role, user_id) do
2123
fields = [:id, :note, :type, :datetime, :granularity, :inserted_at, :updated_at]
2224

2325
cond do
@@ -27,21 +29,25 @@ defmodule Plausible.Annotations do
2729
from(annotation in Annotation,
2830
select: ^fields,
2931
where: annotation.site_id == ^site.id,
32+
where: annotation.type == :site,
3033
order_by: [desc: annotation.updated_at, desc: annotation.id]
3134
)
3235
)
3336

3437
{:ok, Enum.map(annotations, &localize_annotation(&1, site.timezone))}
3538

36-
site_role in @roles_with_personal_annotations or
37-
site_role in @roles_with_maybe_site_annotations ->
39+
site_role in roles_with_personal_annotations() or
40+
site_role in roles_with_maybe_site_annotations() ->
3841
fields = fields ++ [:owner_id]
3942

4043
annotations =
4144
Repo.all(
4245
from(annotation in Annotation,
4346
select: ^fields,
4447
where: annotation.site_id == ^site.id,
48+
where:
49+
annotation.type == :site or
50+
(annotation.type == :personal and annotation.owner_id == ^user_id),
4551
order_by: [desc: annotation.updated_at, desc: annotation.id],
4652
preload: [:owner]
4753
)
@@ -90,7 +96,8 @@ defmodule Plausible.Annotations do
9096
%Annotation{},
9197
Map.merge(params, %{"site_id" => site.id, "owner_id" => user_id})
9298
) do
93-
{:ok, changeset |> Repo.insert!() |> Repo.preload(:owner) |> localize_annotation(site.timezone)}
99+
{:ok,
100+
changeset |> Repo.insert!() |> Repo.preload(:owner) |> localize_annotation(site.timezone)}
94101
else
95102
%{valid?: false, errors: errors} ->
96103
{:error, {:invalid_annotation, errors}}
@@ -124,7 +131,8 @@ defmodule Plausible.Annotations do
124131
) do
125132
Repo.update!(changeset)
126133

127-
{:ok, Repo.reload!(annotation) |> Repo.preload(:owner) |> localize_annotation(site.timezone)}
134+
{:ok,
135+
Repo.reload!(annotation) |> Repo.preload(:owner) |> localize_annotation(site.timezone)}
128136
else
129137
%{valid?: false, errors: errors} ->
130138
{:error, {:invalid_annotation, errors}}
@@ -223,7 +231,9 @@ defmodule Plausible.Annotations do
223231
from(annotation in Annotation,
224232
where: annotation.site_id == ^site_id,
225233
where: annotation.id == ^annotation_id,
226-
where: annotation.type == :site or annotation.owner_id == ^user_id,
234+
where:
235+
annotation.type == :site or
236+
(annotation.type == :personal and annotation.owner_id == ^user_id),
227237
preload: [:owner]
228238
)
229239

@@ -282,7 +292,7 @@ defmodule Plausible.Annotations do
282292
def roles_with_maybe_site_annotations(), do: @roles_with_maybe_site_annotations
283293

284294
def site_annotations_available?(%Plausible.Site{} = site),
285-
do: Plausible.Billing.Feature.SiteAnnotations.check_availability(site.team) == :ok
295+
do: Plausible.Billing.Feature.SiteSegments.check_availability(site.team) == :ok
286296

287297
@doc """
288298
iex> serialize_first_error([{"name", {"should be at most %{count} byte(s)", [count: 255]}}])

lib/plausible/billing/feature.ex

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ defmodule Plausible.Billing.Feature do
6969
Plausible.Billing.Feature.Goals,
7070
Plausible.Billing.Feature.RevenueGoals,
7171
Plausible.Billing.Feature.SiteSegments,
72-
Plausible.Billing.Feature.SiteAnnotations,
7372
Plausible.Billing.Feature.SitesAPI,
7473
Plausible.Billing.Feature.StatsAPI,
7574
Plausible.Billing.Feature.SSO,
@@ -204,13 +203,6 @@ defmodule Plausible.Billing.Feature.SiteSegments do
204203
display_name: "Shared Segments"
205204
end
206205

207-
defmodule Plausible.Billing.Feature.SiteAnnotations do
208-
@moduledoc false
209-
use Plausible.Billing.Feature,
210-
name: :site_annotations,
211-
display_name: "Shared Annotations"
212-
end
213-
214206
defmodule Plausible.Billing.Feature.StatsAPI do
215207
use Plausible
216208

lib/plausible_web/controllers/api/internal/annotations_controller.ex

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,21 @@ defmodule PlausibleWeb.Api.Internal.AnnotationsController do
1010

1111
def index(
1212
%Plug.Conn{
13-
assigns: %{
14-
site: site,
15-
site_role: site_role
16-
}
13+
assigns:
14+
%{
15+
site: site,
16+
site_role: site_role
17+
} = assigns
1718
} = conn,
1819
%{} = _params
1920
) do
20-
case Annotations.get_all_for_site(site, site_role) do
21+
user_id =
22+
case assigns[:current_user] do
23+
%{id: id} -> id
24+
nil -> nil
25+
end
26+
27+
case Annotations.get_all_for_site(site, site_role, user_id) do
2128
{:ok, result} -> json(conn, result)
2229
{:error, :not_enough_permissions} -> json(conn, "Not enough permissions to get annotations")
2330
end

lib/plausible_web/templates/stats/stats.html.heex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@
2626
data-props-available={
2727
to_string(Plausible.Billing.Feature.Props.check_availability(@site.team) == :ok)
2828
}
29-
data-site-segments-available={
30-
to_string(Plausible.Billing.Feature.SiteSegments.check_availability(@site.team) == :ok)
29+
data-site-segments-available={to_string(Plausible.Segments.site_segments_available?(@site))}
30+
data-site-annotations-available={
31+
to_string(Plausible.Annotations.site_annotations_available?(@site))
3132
}
3233
data-revenue-goals={Jason.encode!(@revenue_goals)}
3334
data-funnels={Jason.encode!(@funnels)}

test/plausible_web/controllers/api/internal_controller/annotations_controller_test.exs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,73 @@ defmodule PlausibleWeb.Api.Internal.AnnotationsControllerTest do
22
use PlausibleWeb.ConnCase, async: true
33
use Plausible.Repo
44

5+
describe "GET /api/:domain/annotations - index" do
6+
setup [:create_user, :log_in]
7+
8+
test "public role sees only site annotations, not personal ones", %{conn: conn} do
9+
public_site = new_site(public: true)
10+
site_owner = new_user()
11+
insert(:annotation, site: public_site, owner: site_owner, type: :site, note: "site note")
12+
insert(:annotation, site: public_site, owner: site_owner, type: :personal, note: "private")
13+
14+
conn = get(conn, "/api/#{public_site.domain}/annotations")
15+
16+
assert [result] = json_response(conn, 200)
17+
assert result["type"] == "site"
18+
assert result["note"] == "site note"
19+
end
20+
21+
test "public role response has null owner info", %{conn: conn} do
22+
public_site = new_site(public: true)
23+
site_owner = new_user()
24+
insert(:annotation, site: public_site, owner: site_owner, type: :site, note: "deploy")
25+
26+
conn = get(conn, "/api/#{public_site.domain}/annotations")
27+
28+
assert [result] = json_response(conn, 200)
29+
assert result["owner_id"] == nil
30+
assert result["owner_name"] == nil
31+
end
32+
33+
test "authenticated viewer sees their own personal annotations and all site annotations",
34+
%{conn: conn, user: user} do
35+
site = new_site()
36+
other_user = new_user()
37+
add_guest(site, user: user, role: :viewer)
38+
insert(:annotation, site: site, owner: user, type: :personal, note: "mine")
39+
insert(:annotation, site: site, owner: other_user, type: :personal, note: "not mine")
40+
insert(:annotation, site: site, owner: other_user, type: :site, note: "shared")
41+
42+
conn = get(conn, "/api/#{site.domain}/annotations")
43+
44+
assert results = json_response(conn, 200)
45+
assert length(results) == 2
46+
notes = Enum.map(results, & &1["note"])
47+
assert "mine" in notes
48+
assert "shared" in notes
49+
refute "not mine" in notes
50+
end
51+
52+
test "authenticated owner response includes owner info", %{conn: conn, user: user} do
53+
site = new_site(owner: user)
54+
insert(:annotation, site: site, owner: user, type: :site, note: "deploy")
55+
56+
conn = get(conn, "/api/#{site.domain}/annotations")
57+
58+
assert [result] = json_response(conn, 200)
59+
assert result["owner_id"] == user.id
60+
assert result["owner_name"] == user.name
61+
end
62+
63+
test "private site returns 404 for non-member", %{conn: conn} do
64+
private_site = new_site()
65+
66+
conn = get(conn, "/api/#{private_site.domain}/annotations")
67+
68+
assert json_response(conn, 404)
69+
end
70+
end
71+
572
describe "POST /api/:domain/annotations - datetime coercion" do
673
setup [:create_user, :log_in, :create_site]
774

0 commit comments

Comments
 (0)