From cde9f8873e87d2d2211108239026884171dbcb92 Mon Sep 17 00:00:00 2001 From: Thomas Citharel <tcit@tcit.fr> Date: Fri, 26 Mar 2021 19:01:55 +0100 Subject: [PATCH] Expose personal tokened feeds Signed-off-by: Thomas Citharel <tcit@tcit.fr> --- js/src/components/Settings/SettingsMenu.vue | 2 +- js/src/graphql/user.ts | 14 ++ js/src/i18n/en_US.json | 10 +- js/src/i18n/fr_FR.json | 10 +- .../views/Account/children/EditIdentity.vue | 139 ++++++++++++++++ js/src/views/Settings/Notifications.vue | 151 +++++++++++++++++- lib/graphql/resolvers/feed_token.ex | 14 +- lib/graphql/schema/actors/person.ex | 10 +- lib/graphql/schema/events/feed_token.ex | 2 +- lib/graphql/schema/user.ex | 10 +- lib/service/export/common.ex | 5 +- lib/web/controllers/feed_controller.ex | 4 +- test/graphql/resolvers/feed_token_test.exs | 8 +- test/service/export/icalendar_test.exs | 2 +- test/web/controllers/feed_controller_test.exs | 8 +- 15 files changed, 363 insertions(+), 26 deletions(-) diff --git a/js/src/components/Settings/SettingsMenu.vue b/js/src/components/Settings/SettingsMenu.vue index e6aa18ba3..bbf96cdcb 100644 --- a/js/src/components/Settings/SettingsMenu.vue +++ b/js/src/components/Settings/SettingsMenu.vue @@ -14,7 +14,7 @@ :to="{ name: RouteName.PREFERENCES }" /> <SettingMenuItem - :title="this.$t('Email notifications')" + :title="this.$t('Notifications')" :to="{ name: RouteName.NOTIFICATIONS }" /> </SettingMenuSection> diff --git a/js/src/graphql/user.ts b/js/src/graphql/user.ts index b794ef1a8..1eba90fbb 100644 --- a/js/src/graphql/user.ts +++ b/js/src/graphql/user.ts @@ -241,3 +241,17 @@ export const UPDATE_USER_LOCALE = gql` } } `; + +export const FEED_TOKENS_LOGGED_USER = gql` + query { + loggedUser { + id + feedTokens { + token + actor { + id + } + } + } + } +`; diff --git a/js/src/i18n/en_US.json b/js/src/i18n/en_US.json index 0953e0c0b..a5410a33e 100644 --- a/js/src/i18n/en_US.json +++ b/js/src/i18n/en_US.json @@ -969,5 +969,13 @@ "You replied to a comment on the event {event}.": "You replied to a comment on the event {event}.", "{profile} replied to a comment on the event {event}.": "{profile} replied to a comment on the event {event}.", "New post": "New post", - "Comment text can't be empty": "Comment text can't be empty" + "Comment text can't be empty": "Comment text can't be empty", + "Notifications": "Notifications", + "Profile feeds": "Profile feeds", + "These feeds contain event data for the events for which this specific profile is a participant or creator. You should keep these private. You can find feeds for all of your profiles into your notification settings.": "These feeds contain event data for the events for which this specific profile is a participant or creator. You should keep these private. You can find feeds for all of your profiles into your notification settings.", + "Regenerate new links": "Regenerate new links", + "Create new links": "Create new links", + "You'll need to change the URLs where there were previously entered.": "You'll need to change the URLs where there were previously entered.", + "Personal feeds": "Personal feeds", + "These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page.": "These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page." } diff --git a/js/src/i18n/fr_FR.json b/js/src/i18n/fr_FR.json index 992fc10ea..cdbfda87c 100644 --- a/js/src/i18n/fr_FR.json +++ b/js/src/i18n/fr_FR.json @@ -1063,5 +1063,13 @@ "You replied to a comment on the event {event}.": "Vous avez répondu à un commentaire sur l'événement {event}.", "{profile} replied to a comment on the event {event}.": "{profile} a répondu à un commentaire sur l'événement {event}.", "New post": "Nouveau billet", - "Comment text can't be empty": "Le texte du commentaire ne peut être vide" + "Comment text can't be empty": "Le texte du commentaire ne peut être vide", + "Notifications": "Notifications", + "Profile feeds": "Flux du profil", + "These feeds contain event data for the events for which this specific profile is a participant or creator. You should keep these private. You can find feeds for all of your profiles into your notification settings.": "Ces flux contiennent des informations sur les événements pour lesquels ce profil spécifique est un⋅e participant⋅e ou un⋅e créateur⋅ice. Vous devriez les garder privés. Vous pouvez trouver des flux pour l'ensemble de vos profils dans vos paramètres de notification.", + "Regenerate new links": "Regénérer de nouveaux liens", + "Create new links": "Créer de nouveaux liens", + "You'll need to change the URLs where there were previously entered.": "Vous devrez changer les URLs là où vous les avez entrées précédemment.", + "Personal feeds": "Flux personnels", + "These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page.": "Ces flux contiennent des informations sur les événements pour lesquels n'importe lequel de vos profils est un⋅e participant⋅e ou un⋅e créateur⋅ice. Vous devriez les garder privés. Vous pouvez trouver des flux spécifiques à chaque profil sur la page d'édition des profils." } diff --git a/js/src/views/Account/children/EditIdentity.vue b/js/src/views/Account/children/EditIdentity.vue index bb6c44dc5..5bf8670a4 100644 --- a/js/src/views/Account/children/EditIdentity.vue +++ b/js/src/views/Account/children/EditIdentity.vue @@ -98,6 +98,77 @@ $t("Delete this identity") }}</span> </div> + + <section v-if="isUpdate"> + <div class="setting-title"> + <h2>{{ $t("Profile feeds") }}</h2> + </div> + <p> + {{ + $t( + "These feeds contain event data for the events for which this specific profile is a participant or creator. You should keep these private. You can find feeds for all of your profiles into your notification settings." + ) + }} + </p> + <div v-if="identity.feedTokens && identity.feedTokens.length > 0"> + <div + class="buttons" + v-for="feedToken in identity.feedTokens" + :key="feedToken.token" + > + <b-tooltip + :label="$t('URL copied to clipboard')" + :active="showCopiedTooltip.atom" + always + type="is-success" + position="is-left" + > + <b-button + tag="a" + icon-left="rss" + @click=" + (e) => copyURL(e, tokenToURL(feedToken.token, 'atom'), 'atom') + " + :href="tokenToURL(feedToken.token, 'atom')" + target="_blank" + >{{ $t("RSS/Atom Feed") }}</b-button + > + </b-tooltip> + <b-tooltip + :label="$t('URL copied to clipboard')" + :active="showCopiedTooltip.ics" + always + type="is-success" + position="is-left" + > + <b-button + tag="a" + @click=" + (e) => copyURL(e, tokenToURL(feedToken.token, 'ics'), 'ics') + " + icon-left="calendar-sync" + :href="tokenToURL(feedToken.token, 'ics')" + target="_blank" + >{{ $t("ICS/WebCal Feed") }}</b-button + > + </b-tooltip> + <b-button + icon-left="refresh" + type="is-text" + @click="openRegenerateFeedTokensConfirmation" + >{{ $t("Regenerate new links") }}</b-button + > + </div> + </div> + <div v-else> + <b-button + icon-left="refresh" + type="is-text" + @click="generateFeedTokens" + >{{ $t("Create new links") }}</b-button + > + </div> + </section> </div> </div> </template> @@ -131,6 +202,10 @@ h1 { .username-field + .field { margin-bottom: 0; } + +::v-deep .buttons > *:not(:last-child) .button { + margin-right: 0.5rem; +} </style> <script lang="ts"> @@ -151,6 +226,11 @@ import RouteName from "../../../router/name"; import { buildFileVariable } from "../../../utils/image"; import { changeIdentity } from "../../../utils/auth"; import identityEditionMixin from "../../../mixins/identityEdition"; +import { + CREATE_FEED_TOKEN_ACTOR, + DELETE_FEED_TOKEN, +} from "@/graphql/feed_tokens"; +import { IFeedToken } from "@/types/feedtoken.model"; @Component({ components: { @@ -191,6 +271,8 @@ export default class EditIdentity extends mixins(identityEditionMixin) { RouteName = RouteName; + showCopiedTooltip = { ics: false, atom: false }; + get message(): string | null { if (this.isUpdate) return null; return this.$t( @@ -353,6 +435,63 @@ export default class EditIdentity extends mixins(identityEditionMixin) { return MOBILIZON_INSTANCE_HOST; } + tokenToURL(token: string, format: string): string { + return `${window.location.origin}/events/going/${token}/${format}`; + } + + copyURL(e: Event, url: string, format: "ics" | "atom"): void { + if (navigator.clipboard) { + e.preventDefault(); + navigator.clipboard.writeText(url); + this.showCopiedTooltip[format] = true; + setTimeout(() => { + this.showCopiedTooltip[format] = false; + }, 2000); + } + } + + async generateFeedTokens(): Promise<void> { + const newToken = await this.createNewFeedToken(); + this.identity.feedTokens.push(newToken); + } + + async regenerateFeedTokens(): Promise<void> { + if (this.identity?.feedTokens.length < 1) return; + await this.deleteFeedToken(this.identity.feedTokens[0].token); + const newToken = await this.createNewFeedToken(); + this.identity.feedTokens.pop(); + this.identity.feedTokens.push(newToken); + } + + private async deleteFeedToken(token: string): Promise<void> { + await this.$apollo.mutate({ + mutation: DELETE_FEED_TOKEN, + variables: { token }, + }); + } + + private async createNewFeedToken(): Promise<IFeedToken> { + const { data } = await this.$apollo.mutate({ + mutation: CREATE_FEED_TOKEN_ACTOR, + variables: { actor_id: this.identity?.id }, + }); + + return data.createFeedToken; + } + + openRegenerateFeedTokensConfirmation(): void { + this.$buefy.dialog.confirm({ + type: "is-warning", + title: this.$t("Regenerate new links") as string, + message: this.$t( + "You'll need to change the URLs where there were previously entered." + ) as string, + confirmText: this.$t("Regenerate new links") as string, + cancelText: this.$t("Cancel") as string, + onConfirm: () => this.regenerateFeedTokens(), + }); + } + openDeleteIdentityConfirmation(): void { this.$buefy.dialog.prompt({ type: "is-danger", diff --git a/js/src/views/Settings/Notifications.vue b/js/src/views/Settings/Notifications.vue index c204471a9..c6e697b1d 100644 --- a/js/src/views/Settings/Notifications.vue +++ b/js/src/views/Settings/Notifications.vue @@ -9,7 +9,7 @@ </li> <li class="is-active"> <router-link :to="{ name: RouteName.NOTIFICATIONS }">{{ - $t("Email notifications") + $t("Notifications") }}</router-link> </li> </ul> @@ -118,23 +118,108 @@ </b-select> </div> </section> + <section> + <div class="setting-title"> + <h2>{{ $t("Personal feeds") }}</h2> + </div> + <p> + {{ + $t( + "These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page." + ) + }} + </p> + <div v-if="feedTokens && feedTokens.length > 0"> + <div + class="buttons" + v-for="feedToken in feedTokens" + :key="feedToken.token" + > + <b-tooltip + :label="$t('URL copied to clipboard')" + :active="showCopiedTooltip.atom" + always + type="is-success" + position="is-left" + > + <b-button + tag="a" + icon-left="rss" + @click=" + (e) => copyURL(e, tokenToURL(feedToken.token, 'atom'), 'atom') + " + :href="tokenToURL(feedToken.token, 'atom')" + target="_blank" + >{{ $t("RSS/Atom Feed") }}</b-button + > + </b-tooltip> + <b-tooltip + :label="$t('URL copied to clipboard')" + :active="showCopiedTooltip.ics" + always + type="is-success" + position="is-left" + > + <b-button + tag="a" + @click=" + (e) => copyURL(e, tokenToURL(feedToken.token, 'ics'), 'ics') + " + icon-left="calendar-sync" + :href="tokenToURL(feedToken.token, 'ics')" + target="_blank" + >{{ $t("ICS/WebCal Feed") }}</b-button + > + </b-tooltip> + <b-button + icon-left="refresh" + type="is-text" + @click="openRegenerateFeedTokensConfirmation" + >{{ $t("Regenerate new links") }}</b-button + > + </div> + </div> + <div v-else> + <b-button + icon-left="refresh" + type="is-text" + @click="generateFeedTokens" + >{{ $t("Create new links") }}</b-button + > + </div> + </section> </div> </template> <script lang="ts"> import { Component, Vue, Watch } from "vue-property-decorator"; import { INotificationPendingEnum } from "@/types/enums"; -import { USER_SETTINGS, SET_USER_SETTINGS } from "../../graphql/user"; +import { + USER_SETTINGS, + SET_USER_SETTINGS, + FEED_TOKENS_LOGGED_USER, +} from "../../graphql/user"; import { IUser } from "../../types/current-user.model"; import RouteName from "../../router/name"; +import { IFeedToken } from "@/types/feedtoken.model"; +import { CREATE_FEED_TOKEN, DELETE_FEED_TOKEN } from "@/graphql/feed_tokens"; @Component({ apollo: { loggedUser: USER_SETTINGS, + feedTokens: { + query: FEED_TOKENS_LOGGED_USER, + update: (data) => + data.loggedUser.feedTokens.filter( + (token: IFeedToken) => token.actor === null + ), + }, }, }) export default class Notifications extends Vue { loggedUser!: IUser; + feedTokens: IFeedToken[] = []; + notificationOnDay: boolean | undefined = true; notificationEachWeek: boolean | undefined = false; @@ -148,6 +233,8 @@ export default class Notifications extends Vue { RouteName = RouteName; + showCopiedTooltip = { ics: false, atom: false }; + mounted(): void { this.notificationPendingParticipationValues = { [INotificationPendingEnum.NONE]: this.$t("Do not receive any mail"), @@ -176,6 +263,62 @@ export default class Notifications extends Vue { refetchQueries: [{ query: USER_SETTINGS }], }); } + + tokenToURL(token: string, format: string): string { + return `${window.location.origin}/events/going/${token}/${format}`; + } + + copyURL(e: Event, url: string, format: "ics" | "atom"): void { + if (navigator.clipboard) { + e.preventDefault(); + navigator.clipboard.writeText(url); + this.showCopiedTooltip[format] = true; + setTimeout(() => { + this.showCopiedTooltip[format] = false; + }, 2000); + } + } + + openRegenerateFeedTokensConfirmation(): void { + this.$buefy.dialog.confirm({ + type: "is-warning", + title: this.$t("Regenerate new links") as string, + message: this.$t( + "You'll need to change the URLs where there were previously entered." + ) as string, + confirmText: this.$t("Regenerate new links") as string, + cancelText: this.$t("Cancel") as string, + onConfirm: () => this.regenerateFeedTokens(), + }); + } + + async regenerateFeedTokens(): Promise<void> { + if (this.feedTokens.length < 1) return; + await this.deleteFeedToken(this.feedTokens[0].token); + const newToken = await this.createNewFeedToken(); + this.feedTokens.pop(); + this.feedTokens.push(newToken); + } + + async generateFeedTokens(): Promise<void> { + const newToken = await this.createNewFeedToken(); + this.feedTokens.push(newToken); + } + + private async deleteFeedToken(token: string): Promise<void> { + await this.$apollo.mutate({ + mutation: DELETE_FEED_TOKEN, + variables: { token }, + }); + } + + private async createNewFeedToken(): Promise<IFeedToken> { + const { data } = await this.$apollo.mutate({ + mutation: CREATE_FEED_TOKEN, + }); + + return data.createFeedToken; + } } </script> @@ -193,4 +336,8 @@ export default class Notifications extends Vue { margin-left: 5px; } } + +::v-deep .buttons > *:not(:last-child) .button { + margin-right: 0.5rem; +} </style> diff --git a/lib/graphql/resolvers/feed_token.ex b/lib/graphql/resolvers/feed_token.ex index ed545ed3a..196da066c 100644 --- a/lib/graphql/resolvers/feed_token.ex +++ b/lib/graphql/resolvers/feed_token.ex @@ -22,7 +22,7 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedToken do ) do with {:is_owned, %Actor{}} <- User.owns_actor(user, actor_id), {:ok, feed_token} <- Events.create_feed_token(%{user_id: id, actor_id: actor_id}) do - {:ok, feed_token} + {:ok, to_short_uuid(feed_token)} else {:is_owned, nil} -> {:error, dgettext("errors", "Profile is not owned by authenticated user")} @@ -32,7 +32,7 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedToken do @spec create_feed_token(any, map, map) :: {:ok, FeedToken.t()} def create_feed_token(_parent, %{}, %{context: %{current_user: %User{id: id}}}) do with {:ok, feed_token} <- Events.create_feed_token(%{user_id: id}) do - {:ok, feed_token} + {:ok, to_short_uuid(feed_token)} end end @@ -50,7 +50,8 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedToken do %{token: token}, %{context: %{current_user: %User{id: id} = _user}} ) do - with {:ok, token} <- Ecto.UUID.cast(token), + with {:ok, token} <- ShortUUID.decode(token), + {:ok, token} <- Ecto.UUID.cast(token), {:no_token, %FeedToken{actor: actor, user: %User{} = user} = feed_token} <- {:no_token, Events.get_feed_token(token)}, {:token_from_user, true} <- {:token_from_user, id == user.id}, @@ -65,6 +66,9 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedToken do :error -> {:error, dgettext("errors", "Token is not a valid UUID")} + {:error, "Invalid input"} -> + {:error, dgettext("errors", "Token is not a valid UUID")} + {:no_token, _} -> {:error, dgettext("errors", "Token does not exist")} @@ -77,4 +81,8 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedToken do def delete_feed_token(_parent, _args, %{}) do {:error, dgettext("errors", "You are not allowed to delete a feed token if not connected")} end + + defp to_short_uuid(%FeedToken{token: token} = feed_token) do + %FeedToken{feed_token | token: ShortUUID.encode!(token)} + end end diff --git a/lib/graphql/schema/actors/person.ex b/lib/graphql/schema/actors/person.ex index fd8aedf03..42743316f 100644 --- a/lib/graphql/schema/actors/person.ex +++ b/lib/graphql/schema/actors/person.ex @@ -4,7 +4,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do """ use Absinthe.Schema.Notation - import Absinthe.Resolution.Helpers, only: [dataloader: 1] + import Absinthe.Resolution.Helpers, only: [dataloader: 2] alias Mobilizon.Events alias Mobilizon.GraphQL.Resolvers.{Media, Person} @@ -53,7 +53,13 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do ) field(:feed_tokens, list_of(:feed_token), - resolve: dataloader(Events), + resolve: + dataloader( + Events, + callback: fn feed_tokens, _parent, _args -> + {:ok, Enum.map(feed_tokens, &Map.put(&1, :token, ShortUUID.encode!(&1.token)))} + end + ), description: "A list of the feed tokens for this person" ) diff --git a/lib/graphql/schema/events/feed_token.ex b/lib/graphql/schema/events/feed_token.ex index 44ea96289..c50880574 100644 --- a/lib/graphql/schema/events/feed_token.ex +++ b/lib/graphql/schema/events/feed_token.ex @@ -31,7 +31,7 @@ defmodule Mobilizon.GraphQL.Schema.Events.FeedTokenType do description: "The actor that participates to the event" ) - field(:token, :string, description: "The role of this actor at this event") + field(:token, :string, description: "A ShortUUID private token") end @desc "Represents a deleted feed_token" diff --git a/lib/graphql/schema/user.ex b/lib/graphql/schema/user.ex index af62f798b..e8236c897 100644 --- a/lib/graphql/schema/user.ex +++ b/lib/graphql/schema/user.ex @@ -4,7 +4,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do """ use Absinthe.Schema.Notation - import Absinthe.Resolution.Helpers, only: [dataloader: 1] + import Absinthe.Resolution.Helpers, only: [dataloader: 2] alias Mobilizon.Events alias Mobilizon.GraphQL.Resolvers.{Media, User} @@ -43,7 +43,13 @@ defmodule Mobilizon.GraphQL.Schema.UserType do ) field(:feed_tokens, list_of(:feed_token), - resolve: dataloader(Events), + resolve: + dataloader( + Events, + callback: fn feed_tokens, _parent, _args -> + {:ok, Enum.map(feed_tokens, &Map.put(&1, :token, ShortUUID.encode!(&1.token)))} + end + ), description: "A list of the feed tokens for this user" ) diff --git a/lib/service/export/common.ex b/lib/service/export/common.ex index 26efe9b38..2b9a35692 100644 --- a/lib/service/export/common.ex +++ b/lib/service/export/common.ex @@ -25,8 +25,9 @@ defmodule Mobilizon.Service.Export.Common do # Only events, not posts @spec fetch_events_from_token(String.t()) :: String.t() def fetch_events_from_token(token) do - with {:ok, _uuid} <- Ecto.UUID.cast(token), - %FeedToken{actor: actor, user: %User{} = user} <- Events.get_feed_token(token) do + with {:ok, uuid} <- ShortUUID.decode(token), + {:ok, _uuid} <- Ecto.UUID.cast(uuid), + %FeedToken{actor: actor, user: %User{} = user} <- Events.get_feed_token(uuid) do case actor do %Actor{} = actor -> %{ diff --git a/lib/web/controllers/feed_controller.ex b/lib/web/controllers/feed_controller.ex index 87b8181df..4de8e61b4 100644 --- a/lib/web/controllers/feed_controller.ex +++ b/lib/web/controllers/feed_controller.ex @@ -26,7 +26,7 @@ defmodule Mobilizon.Web.FeedController do end def event(conn, %{"uuid" => uuid, "format" => "ics"}) do - return_data(conn, "ics", "event_" <> uuid, "event.ics") + return_data(conn, "ics", "event_" <> uuid, "event") end def event(_conn, _) do @@ -34,7 +34,7 @@ defmodule Mobilizon.Web.FeedController do end def going(conn, %{"token" => token, "format" => format}) when format in @formats do - return_data(conn, format, "token_" <> token, "events.#{format}") + return_data(conn, format, "token_" <> token, "events") end def going(_conn, _) do diff --git a/test/graphql/resolvers/feed_token_test.exs b/test/graphql/resolvers/feed_token_test.exs index 931c8c342..bc2d353ce 100644 --- a/test/graphql/resolvers/feed_token_test.exs +++ b/test/graphql/resolvers/feed_token_test.exs @@ -186,7 +186,7 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedTokenTest do %{ "feedTokens" => [ %{ - "token" => feed_token.token + "token" => ShortUUID.encode!(feed_token.token) } ] } @@ -194,7 +194,7 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedTokenTest do mutation = """ mutation { deleteFeedToken( - token: "#{feed_token.token}", + token: "#{ShortUUID.encode!(feed_token.token)}", ) { actor { id @@ -270,7 +270,7 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedTokenTest do mutation = """ mutation { deleteFeedToken( - token: "#{feed_token.token}", + token: "#{ShortUUID.encode!(feed_token.token)}", ) { actor { id @@ -320,7 +320,7 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedTokenTest do mutation = """ mutation { deleteFeedToken( - token: "#{uuid}" + token: "#{ShortUUID.encode!(uuid)}" ) { actor { id diff --git a/test/service/export/icalendar_test.exs b/test/service/export/icalendar_test.exs index c3aceed1c..b50351e12 100644 --- a/test/service/export/icalendar_test.exs +++ b/test/service/export/icalendar_test.exs @@ -58,7 +58,7 @@ defmodule Mobilizon.Service.ICalendarTest do event = insert(:event) insert(:participant, event: event, actor: actor, role: :participant) - {:commit, ics} = ICalendarService.create_cache("token_#{token}") + {:commit, ics} = ICalendarService.create_cache("token_#{ShortUUID.encode!(token)}") assert ics =~ event.title end end diff --git a/test/web/controllers/feed_controller_test.exs b/test/web/controllers/feed_controller_test.exs index 1b56dabc3..c4335dfbc 100644 --- a/test/web/controllers/feed_controller_test.exs +++ b/test/web/controllers/feed_controller_test.exs @@ -225,7 +225,7 @@ defmodule Mobilizon.Web.FeedControllerTest do conn |> get( Endpoint - |> Routes.feed_url(:going, feed_token.token, "atom") + |> Routes.feed_url(:going, ShortUUID.encode!(feed_token.token), "atom") |> URI.decode() ) @@ -260,7 +260,7 @@ defmodule Mobilizon.Web.FeedControllerTest do |> put_req_header("accept", "application/atom+xml") |> get( Endpoint - |> Routes.feed_url(:going, feed_token.token, "atom") + |> Routes.feed_url(:going, ShortUUID.encode!(feed_token.token), "atom") |> URI.decode() ) @@ -307,7 +307,7 @@ defmodule Mobilizon.Web.FeedControllerTest do |> put_req_header("accept", "text/calendar") |> get( Endpoint - |> Routes.feed_url(:going, feed_token.token, "ics") + |> Routes.feed_url(:going, ShortUUID.encode!(feed_token.token), "ics") |> URI.decode() ) @@ -338,7 +338,7 @@ defmodule Mobilizon.Web.FeedControllerTest do |> put_req_header("accept", "text/calendar") |> get( Endpoint - |> Routes.feed_url(:going, feed_token.token, "ics") + |> Routes.feed_url(:going, ShortUUID.encode!(feed_token.token), "ics") |> URI.decode() )