Skip to content

Commit

Permalink
chore: port recent changes from client-js (#6)
Browse files Browse the repository at this point in the history
* chore: port feat: add preference overlay support and standardize methods

* add changeset
  • Loading branch information
connorlindsey authored Dec 27, 2023
1 parent bcdbc86 commit bea6705
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 28 deletions.
5 changes: 5 additions & 0 deletions .changeset/famous-parents-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@knocklabs/client": patch
---

feat: add preference overlay support and standardize methods
35 changes: 22 additions & 13 deletions packages/client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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": {
Expand All @@ -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").
27 changes: 27 additions & 0 deletions packages/client/src/clients/preferences/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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;

Expand All @@ -47,6 +53,9 @@ class Preferences {
return this.handleResponse(result);
}

/**
* @deprecated Use `user.setPreferences(preferenceSet, options)` instead
*/
async set(
preferenceSet: SetPreferencesProperties,
options: PreferenceOptions = {},
Expand All @@ -62,6 +71,9 @@ class Preferences {
return this.handleResponse(result);
}

/**
* @deprecated Use `user.setPreferences(preferenceSet, options)` instead
*/
async setChannelTypes(
channelTypePreferences: ChannelTypePreferences,
options: PreferenceOptions = {},
Expand All @@ -77,6 +89,9 @@ class Preferences {
return this.handleResponse(result);
}

/**
* @deprecated Use `user.setPreferences(preferenceSet, options)` instead
*/
async setChannelType(
channelType: ChannelType,
setting: boolean,
Expand All @@ -93,6 +108,9 @@ class Preferences {
return this.handleResponse(result);
}

/**
* @deprecated Use `user.setPreferences(preferenceSet, options)` instead
*/
async setWorkflows(
workflowPreferences: WorkflowPreferences,
options: PreferenceOptions = {},
Expand All @@ -108,6 +126,9 @@ class Preferences {
return this.handleResponse(result);
}

/**
* @deprecated Use `user.setPreferences(preferenceSet, options)` instead
*/
async setWorkflow(
workflowKey: string,
setting: WorkflowPreferenceSetting,
Expand All @@ -125,6 +146,9 @@ class Preferences {
return this.handleResponse(result);
}

/**
* @deprecated Use `user.setPreferences(preferenceSet, options)` instead
*/
async setCategories(
categoryPreferences: WorkflowPreferences,
options: PreferenceOptions = {},
Expand All @@ -140,6 +164,9 @@ class Preferences {
return this.handleResponse(result);
}

/**
* @deprecated Use `user.setPreferences(preferenceSet, options)` instead
*/
async setCategory(
categoryKey: string,
setting: WorkflowPreferenceSetting,
Expand Down
12 changes: 11 additions & 1 deletion packages/client/src/clients/preferences/interfaces.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -30,3 +36,7 @@ export interface PreferenceSet {
export interface PreferenceOptions {
preferenceSet?: string;
}

export interface GetPreferencesOptions extends PreferenceOptions {
tenant?: string;
}
70 changes: 56 additions & 14 deletions packages/client/src/clients/users/index.ts
Original file line number Diff line number Diff line change
@@ -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<string, any>;
};

type GetChannelDataInput = {
channelId: string;
};
const DEFAULT_PREFERENCE_SET_ID = "default";

class UserClient {
private instance: Knock;
Expand All @@ -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<PreferenceSet[]>(result);
}

async getPreferences(
options: GetPreferencesOptions = {},
): Promise<PreferenceSet> {
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<PreferenceSet>(result);
}

async setPreferences(
preferenceSet: SetPreferencesProperties,
options: PreferenceOptions = {},
): Promise<PreferenceSet> {
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<PreferenceSet>(result);
}

async getChannelData<T = any>(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<ChannelData<T>>(result);
}

async setChannelData({ channelId, channelData }: SetChannelDataInput) {
async setChannelData<T = any>({
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<ChannelData<T>>(result);
}

private handleResponse(response: ApiResponse) {
private handleResponse<T>(response: ApiResponse) {
if (response.statusCode === "error") {
throw new Error(response.error || response.body);
}

return response.body;
return response.body as T;
}
}

Expand Down
8 changes: 8 additions & 0 deletions packages/client/src/clients/users/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface SetChannelDataInput {
channelId: string;
channelData: Record<string, any>;
}

export interface GetChannelDataInput {
channelId: string;
}
5 changes: 5 additions & 0 deletions packages/client/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,8 @@ export interface Activity<T = GenericData> {
actor: Recipient | null;
data: T | null;
}

export interface ChannelData<T = any> {
channel_id: string;
data: T;
}

0 comments on commit bea6705

Please sign in to comment.