diff --git a/frontend/src/metabase-types/api/index.ts b/frontend/src/metabase-types/api/index.ts index b3e231edee4f8b397d2bde01028ab0cf4c115db6..d28fa6a5d7802f03c0fa95abf8ef5f8ef853376f 100644 --- a/frontend/src/metabase-types/api/index.ts +++ b/frontend/src/metabase-types/api/index.ts @@ -33,6 +33,7 @@ export * from "./setup"; export * from "./slack"; export * from "./snippets"; export * from "./store"; +export * from "./subscription"; export * from "./table"; export * from "./task"; export * from "./timeline"; diff --git a/frontend/src/metabase-types/api/subscription.ts b/frontend/src/metabase-types/api/subscription.ts new file mode 100644 index 0000000000000000000000000000000000000000..07d44c6ec1ec1ef35fcd7f4485143340255e872d --- /dev/null +++ b/frontend/src/metabase-types/api/subscription.ts @@ -0,0 +1,55 @@ +import type { Card } from "./card"; +import type { RegularCollectionId } from "./collection"; +import type { DashboardId } from "./dashboard"; +import type { Channel } from "./notifications"; +import type { Parameter } from "./parameters"; +import type { User } from "./user"; + +export interface ListSubscriptionsRequest { + archived?: boolean; + dashboard_id?: DashboardId; + creator_or_recipient?: boolean; +} + +export interface DashboardSubscription { + archived: boolean; + cards: Card[]; + channels: Channel[]; + collection_id: RegularCollectionId | null; + collection_position: number | null; + created_at: string; + creator: User; + creator_id: number; + dashboard_id: DashboardId; + entity_id: string; + id: number; + name: string; + parameters: Parameter[]; + skip_if_empty: boolean; + updated_at: string; +} + +export interface CreateSubscriptionRequest { + name: string; + cards: Card[]; + channels: Channel[]; + skip_if_empty?: boolean; + collection_id?: RegularCollectionId | null; + collection_position?: number | null; + dashboard_id?: DashboardId; + parameters?: Parameter[]; +} + +export interface UpdateSubscriptionRequest { + id: number; + name?: string; + cards?: Card[]; + channels?: Channel[]; + skip_if_empty?: boolean; + collection_id?: RegularCollectionId | null; + collection_position?: number | null; + dashboard_id?: DashboardId; + parameters?: Parameter[]; + archived?: boolean; + can_write?: boolean; +} diff --git a/frontend/src/metabase/api/index.ts b/frontend/src/metabase/api/index.ts index ea7e0c5b1c2b2d2bac8d78cf5068029adf27aed9..09bed25e5148c48f08ebbb63dfff8beac4834619 100644 --- a/frontend/src/metabase/api/index.ts +++ b/frontend/src/metabase/api/index.ts @@ -19,6 +19,7 @@ export * from "./search"; export * from "./segment"; export * from "./session"; export * from "./snippet"; +export * from "./subscription"; export * from "./table"; export * from "./task"; export * from "./timeline"; diff --git a/frontend/src/metabase/api/subscription.ts b/frontend/src/metabase/api/subscription.ts new file mode 100644 index 0000000000000000000000000000000000000000..35f4b48ec73dd6a4d5ec78584d46f80a6c45d7be --- /dev/null +++ b/frontend/src/metabase/api/subscription.ts @@ -0,0 +1,86 @@ +import type { + ListSubscriptionsRequest, + DashboardSubscription, + CreateSubscriptionRequest, + UpdateSubscriptionRequest, +} from "metabase-types/api"; + +import { Api } from "./api"; +import { + idTag, + invalidateTags, + listTag, + provideSubscriptionListTags, + provideSubscriptionTags, +} from "./tags"; + +export const subscriptionApi = Api.injectEndpoints({ + endpoints: builder => ({ + listSubscriptions: builder.query< + DashboardSubscription[], + ListSubscriptionsRequest + >({ + query: params => ({ + method: "GET", + url: "/api/pulse", + params, + }), + providesTags: (subscriptions = []) => + provideSubscriptionListTags(subscriptions), + }), + getSubscription: builder.query<DashboardSubscription, number>({ + query: id => ({ + method: "GET", + url: `/api/pulse/${id}`, + }), + providesTags: subscription => + subscription ? provideSubscriptionTags(subscription) : [], + }), + createSubscription: builder.mutation< + DashboardSubscription, + CreateSubscriptionRequest + >({ + query: body => ({ + method: "POST", + url: "/api/pulse", + body, + }), + invalidatesTags: (_, error) => + invalidateTags(error, [listTag("subscription")]), + }), + updateSubscription: builder.mutation< + DashboardSubscription, + UpdateSubscriptionRequest + >({ + query: ({ id, ...body }) => ({ + method: "PUT", + url: `/api/pulse/${id}`, + body, + }), + invalidatesTags: (_, error, { id }) => + invalidateTags(error, [ + listTag("subscription"), + idTag("subscription", id), + ]), + }), + unsubscribe: builder.mutation<void, number>({ + query: id => ({ + method: "DELETE", + url: `/api/pulse/${id}/subscription`, + }), + invalidatesTags: (_, error, id) => + invalidateTags(error, [ + listTag("subscription"), + idTag("subscription", id), + ]), + }), + }), +}); + +export const { + useListSubscriptionsQuery, + useGetSubscriptionQuery, + useCreateSubscriptionMutation, + useUpdateSubscriptionMutation, + useUnsubscribeMutation, +} = subscriptionApi; diff --git a/frontend/src/metabase/api/tags/constants.ts b/frontend/src/metabase/api/tags/constants.ts index db0db90805f165a36cdf949772967fa58d04febb..e558fa6e88188e11b71c158c3516ca8229dff1e0 100644 --- a/frontend/src/metabase/api/tags/constants.ts +++ b/frontend/src/metabase/api/tags/constants.ts @@ -18,8 +18,9 @@ export const TAG_TYPES = [ "persisted-model", "revision", "schema", - "snippet", "segment", + "snippet", + "subscription", "table", "task", "timeline", diff --git a/frontend/src/metabase/api/tags/utils.ts b/frontend/src/metabase/api/tags/utils.ts index 7679676ea0147a373446b2eb259e309faa7f0931..18b06a9e09e0601b5a6c001def05e31903a062fa 100644 --- a/frontend/src/metabase/api/tags/utils.ts +++ b/frontend/src/metabase/api/tags/utils.ts @@ -10,6 +10,7 @@ import type { CollectionItem, CollectionItemModel, Dashboard, + DashboardSubscription, Database, DatabaseCandidate, Field, @@ -375,6 +376,21 @@ export function provideSnippetTags( return [idTag("snippet", snippet.id)]; } +export function provideSubscriptionListTags( + subscriptions: DashboardSubscription[], +): TagDescription<TagType>[] { + return [ + listTag("subscription"), + ...subscriptions.flatMap(provideSubscriptionTags), + ]; +} + +export function provideSubscriptionTags( + subscription: DashboardSubscription, +): TagDescription<TagType>[] { + return [idTag("subscription", subscription.id)]; +} + export function provideTableListTags( tables: Table[], ): TagDescription<TagType>[] { diff --git a/frontend/src/metabase/entities/pulses.js b/frontend/src/metabase/entities/pulses.js index 978ff30c14027ba78e7c8aea5faa1d93a2a3fbd9..7befcf18c6706778f169665be2fc14f5b710ad87 100644 --- a/frontend/src/metabase/entities/pulses.js +++ b/frontend/src/metabase/entities/pulses.js @@ -1,12 +1,15 @@ import { t } from "ttag"; -import { canonicalCollectionId } from "metabase/collections/utils"; +import { subscriptionApi } from "metabase/api"; import { getCollectionType } from "metabase/entities/collections"; import { color } from "metabase/lib/colors"; -import { createEntity, undo } from "metabase/lib/entities"; +import { + createEntity, + undo, + entityCompatibleQuery, +} from "metabase/lib/entities"; import * as Urls from "metabase/lib/urls"; import { addUndo } from "metabase/redux/undo"; -import { PulseApi } from "metabase/services"; export const UNSUBSCRIBE = "metabase/entities/pulses/unsubscribe"; @@ -22,6 +25,36 @@ const Pulses = createEntity({ UNSUBSCRIBE, }, + api: { + list: (entityQuery, dispatch) => + entityCompatibleQuery( + entityQuery, + dispatch, + subscriptionApi.endpoints.listSubscriptions, + ), + get: (entityQuery, options, dispatch) => + entityCompatibleQuery( + entityQuery.id, + dispatch, + subscriptionApi.endpoints.getSubscription, + ), + create: (entityQuery, dispatch) => + entityCompatibleQuery( + entityQuery, + dispatch, + subscriptionApi.endpoints.createSubscription, + ), + update: (entityQuery, dispatch) => + entityCompatibleQuery( + entityQuery, + dispatch, + subscriptionApi.endpoints.updateSubscription, + ), + delete: () => { + throw new TypeError("Pulses.api.delete is not supported"); + }, + }, + objectActions: { setArchived: ({ id }, archived, opts) => { return Pulses.actions.update( @@ -31,37 +64,14 @@ const Pulses = createEntity({ ); }, - setChannels: ({ id }, channels, opts) => { - return Pulses.actions.update( - { id }, - { channels }, - undo(opts, t`subscription`, t`updated`), - ); - }, - - setCollection: ({ id }, collection, opts) => { - return Pulses.actions.update( - { id }, - { collection_id: canonicalCollectionId(collection && collection.id) }, - undo(opts, t`subscription`, t`moved`), - ); - }, - - setPinned: ({ id }, pinned, opts) => { - return Pulses.actions.update( - { id }, - { - collection_position: - typeof pinned === "number" ? pinned : pinned ? 1 : null, - }, - opts, - ); - }, - unsubscribe: ({ id }) => async dispatch => { - await PulseApi.unsubscribe({ id }); + await entityCompatibleQuery( + id, + dispatch, + subscriptionApi.endpoints.unsubscribe, + ); dispatch(addUndo({ message: t`Successfully unsubscribed` })); dispatch({ type: UNSUBSCRIBE, payload: { id } }); dispatch({ type: Pulses.actionTypes.INVALIDATE_LISTS_ACTION });