From bea6705ed0e64fe010da1cf8662118b660bff704 Mon Sep 17 00:00:00 2001 From: Connor Lindsey Date: Wed, 27 Dec 2023 09:14:31 -0800 Subject: [PATCH] chore: port recent changes from client-js (#6) * chore: port feat: add preference overlay support and standardize methods * add changeset --- .changeset/famous-parents-type.md | 5 ++ packages/client/README.md | 35 ++++++---- .../client/src/clients/preferences/index.ts | 27 +++++++ .../src/clients/preferences/interfaces.ts | 12 +++- packages/client/src/clients/users/index.ts | 70 +++++++++++++++---- .../client/src/clients/users/interfaces.ts | 8 +++ packages/client/src/interfaces.ts | 5 ++ 7 files changed, 134 insertions(+), 28 deletions(-) create mode 100644 .changeset/famous-parents-type.md create mode 100644 packages/client/src/clients/users/interfaces.ts diff --git a/.changeset/famous-parents-type.md b/.changeset/famous-parents-type.md new file mode 100644 index 00000000..311388a4 --- /dev/null +++ b/.changeset/famous-parents-type.md @@ -0,0 +1,5 @@ +--- +"@knocklabs/client": patch +--- + +feat: add preference overlay support and standardize methods diff --git a/packages/client/README.md b/packages/client/README.md index 7ace8e78..7a83573e 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -43,10 +43,6 @@ knockClient.authenticate( ); ``` -## Usage - -You can find an example usage in a React application in the [example/App.js](../../examples/client-example/src/App.js) file, which is a plain-old Create React App. - ### Retrieving new items from the feed ```typescript @@ -159,7 +155,7 @@ feedClient.markAsUnarchived(feedItemOrItems); ```typescript // Set an entire preference set -await knockClient.preferences.set({ +await knockClient.user.setPreferences({ channel_types: { email: true, sms: false }, workflows: { "dinosaurs-loose": { @@ -169,16 +165,29 @@ await knockClient.preferences.set({ }); // Retrieve a whole preference set -const preferences = await knockClient.preferences.get(); +const preferences = await knockClient.user.getPreferences(); + +// Retrieve all preference sets for the user +const preferenceSets = await knockClient.user.getAllPreferences(); +``` + +### Managing the current user's channel data -// Granular preference setting for channel types -await knockClient.preferences.setChannelType("email", false); +```typescript +// Get user channel data +const channelData = await knockClient.user.getChannelData({ + channelId: "uuid-knock-channel-id", +}); +``` -// Granular preference setting for workflows -await knockClient.preferences.setWorkflow("dinosaurs-loose", { - channel_types: { - email: true, - in_app_feed: false, +```typescript +// Set push channel data for a user +await knockClient.user.setChannelData({ + channelId: "uuid-knock-channel-id", + channelData: { + tokens: ["apns-user-push-token"], }, }); ``` + +See provider requirements for setting channel data [here]("https://docs.knock.app/managing-recipients/setting-channel-data#provider-data-requirements"). diff --git a/packages/client/src/clients/preferences/index.ts b/packages/client/src/clients/preferences/index.ts index 48b38441..1cf6a898 100644 --- a/packages/client/src/clients/preferences/index.ts +++ b/packages/client/src/clients/preferences/index.ts @@ -27,6 +27,9 @@ class Preferences { this.instance = instance; } + /** + * @deprecated Use `user.getAllPreferences()` instead + */ async getAll() { const result = await this.instance.client().makeRequest({ method: "GET", @@ -36,6 +39,9 @@ class Preferences { return this.handleResponse(result); } + /** + * @deprecated Use `user.getPreferences()` instead + */ async get(options: PreferenceOptions = {}) { const preferenceSetId = options.preferenceSet || DEFAULT_PREFERENCE_SET_ID; @@ -47,6 +53,9 @@ class Preferences { return this.handleResponse(result); } + /** + * @deprecated Use `user.setPreferences(preferenceSet, options)` instead + */ async set( preferenceSet: SetPreferencesProperties, options: PreferenceOptions = {}, @@ -62,6 +71,9 @@ class Preferences { return this.handleResponse(result); } + /** + * @deprecated Use `user.setPreferences(preferenceSet, options)` instead + */ async setChannelTypes( channelTypePreferences: ChannelTypePreferences, options: PreferenceOptions = {}, @@ -77,6 +89,9 @@ class Preferences { return this.handleResponse(result); } + /** + * @deprecated Use `user.setPreferences(preferenceSet, options)` instead + */ async setChannelType( channelType: ChannelType, setting: boolean, @@ -93,6 +108,9 @@ class Preferences { return this.handleResponse(result); } + /** + * @deprecated Use `user.setPreferences(preferenceSet, options)` instead + */ async setWorkflows( workflowPreferences: WorkflowPreferences, options: PreferenceOptions = {}, @@ -108,6 +126,9 @@ class Preferences { return this.handleResponse(result); } + /** + * @deprecated Use `user.setPreferences(preferenceSet, options)` instead + */ async setWorkflow( workflowKey: string, setting: WorkflowPreferenceSetting, @@ -125,6 +146,9 @@ class Preferences { return this.handleResponse(result); } + /** + * @deprecated Use `user.setPreferences(preferenceSet, options)` instead + */ async setCategories( categoryPreferences: WorkflowPreferences, options: PreferenceOptions = {}, @@ -140,6 +164,9 @@ class Preferences { return this.handleResponse(result); } + /** + * @deprecated Use `user.setPreferences(preferenceSet, options)` instead + */ async setCategory( categoryKey: string, setting: WorkflowPreferenceSetting, diff --git a/packages/client/src/clients/preferences/interfaces.ts b/packages/client/src/clients/preferences/interfaces.ts index c5d6ebcb..c813d6f7 100644 --- a/packages/client/src/clients/preferences/interfaces.ts +++ b/packages/client/src/clients/preferences/interfaces.ts @@ -1,6 +1,12 @@ // Channel types supported in Knock // TODO: it would be great to pull this in from an external location -export type ChannelType = "email" | "in_app_feed" | "sms" | "push" | "chat" | "http"; +export type ChannelType = + | "email" + | "in_app_feed" + | "sms" + | "push" + | "chat" + | "http"; export type ChannelTypePreferences = { [K in ChannelType]?: boolean; @@ -30,3 +36,7 @@ export interface PreferenceSet { export interface PreferenceOptions { preferenceSet?: string; } + +export interface GetPreferencesOptions extends PreferenceOptions { + tenant?: string; +} diff --git a/packages/client/src/clients/users/index.ts b/packages/client/src/clients/users/index.ts index cf9d9880..0f4118af 100644 --- a/packages/client/src/clients/users/index.ts +++ b/packages/client/src/clients/users/index.ts @@ -1,14 +1,15 @@ import { ApiResponse } from "../../api"; +import { ChannelData } from "../../interfaces"; import Knock from "../../knock"; +import { + GetPreferencesOptions, + PreferenceOptions, + PreferenceSet, + SetPreferencesProperties, +} from "../preferences/interfaces"; +import { GetChannelDataInput, SetChannelDataInput } from "./interfaces"; -type SetChannelDataInput = { - channelId: string; - channelData: Record; -}; - -type GetChannelDataInput = { - channelId: string; -}; +const DEFAULT_PREFERENCE_SET_ID = "default"; class UserClient { private instance: Knock; @@ -17,31 +18,72 @@ class UserClient { this.instance = instance; } - async getChannelData(params: GetChannelDataInput) { + async getAllPreferences() { + const result = await this.instance.client().makeRequest({ + method: "GET", + url: `/v1/users/${this.instance.userId}/preferences`, + }); + + return this.handleResponse(result); + } + + async getPreferences( + options: GetPreferencesOptions = {}, + ): Promise { + const preferenceSetId = options.preferenceSet || DEFAULT_PREFERENCE_SET_ID; + + const result = await this.instance.client().makeRequest({ + method: "GET", + url: `/v1/users/${this.instance.userId}/preferences/${preferenceSetId}`, + params: { tenant: options.tenant }, + }); + + return this.handleResponse(result); + } + + async setPreferences( + preferenceSet: SetPreferencesProperties, + options: PreferenceOptions = {}, + ): Promise { + const preferenceSetId = options.preferenceSet || DEFAULT_PREFERENCE_SET_ID; + + const result = await this.instance.client().makeRequest({ + method: "PUT", + url: `/v1/users/${this.instance.userId}/preferences/${preferenceSetId}`, + data: preferenceSet, + }); + + return this.handleResponse(result); + } + + async getChannelData(params: GetChannelDataInput) { const result = await this.instance.client().makeRequest({ method: "GET", url: `/v1/users/${this.instance.userId}/channel_data/${params.channelId}`, }); - return this.handleResponse(result); + return this.handleResponse>(result); } - async setChannelData({ channelId, channelData }: SetChannelDataInput) { + async setChannelData({ + channelId, + channelData, + }: SetChannelDataInput) { const result = await this.instance.client().makeRequest({ method: "PUT", url: `/v1/users/${this.instance.userId}/channel_data/${channelId}`, data: { data: channelData }, }); - return this.handleResponse(result); + return this.handleResponse>(result); } - private handleResponse(response: ApiResponse) { + private handleResponse(response: ApiResponse) { if (response.statusCode === "error") { throw new Error(response.error || response.body); } - return response.body; + return response.body as T; } } diff --git a/packages/client/src/clients/users/interfaces.ts b/packages/client/src/clients/users/interfaces.ts new file mode 100644 index 00000000..dd95c7ab --- /dev/null +++ b/packages/client/src/clients/users/interfaces.ts @@ -0,0 +1,8 @@ +export interface SetChannelDataInput { + channelId: string; + channelData: Record; +} + +export interface GetChannelDataInput { + channelId: string; +} diff --git a/packages/client/src/interfaces.ts b/packages/client/src/interfaces.ts index 0a0ab8ad..296d59d4 100644 --- a/packages/client/src/interfaces.ts +++ b/packages/client/src/interfaces.ts @@ -41,3 +41,8 @@ export interface Activity { actor: Recipient | null; data: T | null; } + +export interface ChannelData { + channel_id: string; + data: T; +}