getstream
Version:
The official low-level GetStream.io client for Node.js and the browser.
324 lines (294 loc) • 12.8 kB
text/typescript
import { StreamClient, APIResponse, UR, DefaultGenerics } from './client';
import { StreamFeed } from './feed';
import { SiteError } from './errors';
import { EnrichedUser } from './user';
export type TargetFeeds = (string | StreamFeed)[];
export type TargetFeed = string | StreamFeed;
export type TargetFeedsExtraData = Record<string, unknown>;
type ReactionBody<T> = {
activity_id?: string; // only required for reactions
data?: T | UR;
id?: string; // api will generate an id if it's missing
kind?: string; // required only for add/addChile, not update
parent?: string; // only required for child reactions
target_feeds?: string[];
target_feeds_extra_data?: TargetFeedsExtraData;
user_id?: string; // optional when using client tokens
};
export type Reaction<T extends UR = UR> = {
activity_id: string;
created_at: string;
data: T;
id: string;
kind: string;
parent: string;
updated_at: string;
user_id: string;
target_feeds?: string[];
target_feeds_extra_data?: TargetFeedsExtraData;
};
export type ReactionAPIResponse<T extends UR = UR> = APIResponse & Reaction<T>;
export type ChildReactionsRecords<
StreamFeedGenerics extends DefaultGenerics = DefaultGenerics,
// eslint-disable-next-line no-use-before-define
> = Record<string, EnrichedReaction<StreamFeedGenerics>[]>;
export type EnrichedReaction<StreamFeedGenerics extends DefaultGenerics = DefaultGenerics> = Reaction<
StreamFeedGenerics['reactionType'] | StreamFeedGenerics['childReactionType']
> & {
children_counts: Record<string, number>;
latest_children: ChildReactionsRecords<StreamFeedGenerics>;
latest_children_extra?: Record<string, { next?: string }>;
own_children?: ChildReactionsRecords<StreamFeedGenerics>;
user?: EnrichedUser<StreamFeedGenerics>;
};
export type EnrichedReactionAPIResponse<StreamFeedGenerics extends DefaultGenerics = DefaultGenerics> = APIResponse &
EnrichedReaction<StreamFeedGenerics>;
export type ReactionFilterAPIResponse<StreamFeedGenerics extends DefaultGenerics = DefaultGenerics> = APIResponse & {
next: string;
results:
| ReactionAPIResponse<StreamFeedGenerics['reactionType'] | StreamFeedGenerics['childReactionType']>[]
| EnrichedReactionAPIResponse<StreamFeedGenerics>[];
activity?: StreamFeedGenerics['childReactionType'];
};
export type ReactionFilterConditions = {
activity_id?: string;
children_user_id?: string;
filter_user_id?: string;
id_gt?: string;
id_gte?: string;
id_lt?: string;
id_lte?: string;
kind?: string;
limit?: number;
reaction_id?: string;
user_id?: string;
with_activity_data?: boolean;
with_own_children?: boolean;
};
export type ReactionUpdateOptions = {
targetFeeds?: TargetFeeds;
targetFeedsExtraData?: TargetFeedsExtraData;
};
export type ReactionAddOptions = ReactionUpdateOptions & {
id?: string;
userId?: string;
};
export type ReactionAddChildOptions = ReactionUpdateOptions & {
userId?: string;
};
export class StreamReaction<StreamFeedGenerics extends DefaultGenerics = DefaultGenerics> {
client: StreamClient<StreamFeedGenerics>;
token: string;
/**
* Initialize a reaction object
* @link https://getstream.io/activity-feeds/docs/node/reactions_introduction/?language=js
* @method constructor
* @memberof StreamReaction.prototype
* @param {StreamClient} client Stream client this feed is constructed from
* @param {string} token JWT token
* @example new StreamReaction(client, "eyJhbGciOiJIUzI1...")
*/
constructor(client: StreamClient<StreamFeedGenerics>, token: string) {
this.client = client;
this.token = token;
}
buildURL = (...args: string[]) => {
return `${['reaction', ...args].join('/')}/`;
};
_convertTargetFeeds = (targetFeeds: TargetFeeds = []): string[] => {
return targetFeeds.map((elem: TargetFeed) => (typeof elem === 'string' ? elem : (elem as StreamFeed).id));
};
/**
* add reaction
* @link https://getstream.io/activity-feeds/docs/node/reactions_introduction/?language=js#adding-reactions
* @method add
* @memberof StreamReaction.prototype
* @param {string} kind kind of reaction
* @param {string} activity Activity or an ActivityID
* @param {ReactionType} data data related to reaction
* @param {ReactionAddOptions} [options]
* @param {string} [options.id] id associated with reaction
* @param {string[]} [options.targetFeeds] an array of feeds to which to send an activity with the reaction
* @param {string} [options.userId] useful for adding reaction with server token
* @param {object} [options.targetFeedsExtraData] extra data related to target feeds
* @return {Promise<ReactionAPIResponse<ReactionType>>}
* @example reactions.add("like", "0c7db91c-67f9-11e8-bcd9-fe00a9219401")
* @example reactions.add("comment", "0c7db91c-67f9-11e8-bcd9-fe00a9219401", {"text": "love it!"},)
*/
add(
kind: string,
activity: string | { id: string },
data?: StreamFeedGenerics['reactionType'],
{ id, targetFeeds = [], userId, targetFeedsExtraData }: ReactionAddOptions = {},
) {
const body: ReactionBody<StreamFeedGenerics['reactionType']> = {
id,
activity_id: activity instanceof Object ? (activity as { id: string }).id : activity,
kind,
data: data || {},
target_feeds: this._convertTargetFeeds(targetFeeds),
user_id: userId,
};
if (targetFeedsExtraData != null) {
body.target_feeds_extra_data = targetFeedsExtraData;
}
return this.client.post<ReactionAPIResponse<StreamFeedGenerics['reactionType']>>({
url: this.buildURL(),
body,
token: this.token,
});
}
/**
* add child reaction
* @link https://getstream.io/activity-feeds/docs/node/reactions_add_child/?language=js
* @method addChild
* @memberof StreamReaction.prototype
* @param {string} kind kind of reaction
* @param {string} reaction Reaction or a ReactionID
* @param {ChildReactionType} data data related to reaction
* @param {ReactionAddChildOptions} [options]
* @param {string[]} [options.targetFeeds] an array of feeds to which to send an activity with the reaction
* @param {string} [options.userId] useful for adding reaction with server token
* @param {object} [options.targetFeedsExtraData] extra data related to target feeds
* @return {Promise<ReactionAPIResponse<ChildReactionType>>}
* @example reactions.add("like", "0c7db91c-67f9-11e8-bcd9-fe00a9219401")
* @example reactions.add("comment", "0c7db91c-67f9-11e8-bcd9-fe00a9219401", {"text": "love it!"},)
*/
addChild(
kind: string,
reaction: string | { id: string },
data?: StreamFeedGenerics['childReactionType'],
{ targetFeeds = [], userId, targetFeedsExtraData }: ReactionAddChildOptions = {},
) {
const body: ReactionBody<StreamFeedGenerics['childReactionType']> = {
parent: reaction instanceof Object ? (reaction as { id: string }).id : reaction,
kind,
data: data || {},
target_feeds: this._convertTargetFeeds(targetFeeds),
user_id: userId,
};
if (targetFeedsExtraData != null) {
body.target_feeds_extra_data = targetFeedsExtraData;
}
return this.client.post<ReactionAPIResponse<StreamFeedGenerics['childReactionType']>>({
url: this.buildURL(),
body,
token: this.token,
});
}
/**
* get reaction
* @link https://getstream.io/activity-feeds/docs/node/reactions_introduction/?language=js#retrieving-reactions
* @method get
* @memberof StreamReaction.prototype
* @param {string} id Reaction Id
* @return {Promise<EnrichedReactionAPIResponse<StreamFeedGenerics>>}
* @example reactions.get("67b3e3b5-b201-4697-96ac-482eb14f88ec")
*/
get(id: string) {
return this.client.get<EnrichedReactionAPIResponse<StreamFeedGenerics>>({
url: this.buildURL(id),
token: this.token,
});
}
/**
* retrieve reactions by activity_id, user_id or reaction_id (to paginate children reactions), pagination can be done using id_lt, id_lte, id_gt and id_gte parameters
* id_lt and id_lte return reactions order by creation descending starting from the reaction with the ID provided, when id_lte is used
* the reaction with ID equal to the value provided is included.
* id_gt and id_gte return reactions order by creation ascending (oldest to newest) starting from the reaction with the ID provided, when id_gte is used
* the reaction with ID equal to the value provided is included.
* results are limited to 25 at most and are ordered newest to oldest by default.
* @link https://getstream.io/activity-feeds/docs/node/reactions_introduction/?language=js#retrieving-reactions
* @method filter
* @memberof StreamReaction.prototype
* @param {ReactionFilterConditions} conditions Reaction Id {activity_id|user_id|reaction_id:string, kind:string, limit:integer}
* @return {Promise<ReactionFilterAPIResponse<StreamFeedGenerics>>}
* @example reactions.filter({activity_id: "0c7db91c-67f9-11e8-bcd9-fe00a9219401", kind:"like"})
* @example reactions.filter({user_id: "john", kinds:"like"})
*/
filter(conditions: ReactionFilterConditions) {
const { user_id: userId, activity_id: activityId, reaction_id: reactionId, ...qs } = conditions;
if (!qs.limit) {
qs.limit = 10;
}
if ((userId ? 1 : 0) + (activityId ? 1 : 0) + (reactionId ? 1 : 0) !== 1) {
throw new SiteError('Must provide exactly one value for one of these params: user_id, activity_id, reaction_id');
}
const lookupType = (userId && 'user_id') || (activityId && 'activity_id') || (reactionId && 'reaction_id');
const value = userId || activityId || reactionId;
const url = conditions.kind
? this.buildURL(lookupType as string, value as string, conditions.kind)
: this.buildURL(lookupType as string, value as string);
return this.client.get<ReactionFilterAPIResponse<StreamFeedGenerics>>({
url,
qs: qs as { [key: string]: unknown },
token: this.token,
});
}
/**
* update reaction
* @link https://getstream.io/activity-feeds/docs/node/reactions_introduction/?language=js#updating-reactions
* @method update
* @memberof StreamReaction.prototype
* @param {string} id Reaction Id
* @param {ReactionType | ChildReactionType} data Data associated to reaction or childReaction
* @param {ReactionUpdateOptions} [options]
* @param {string[]} [options.targetFeeds] Optional feeds to post the activity to. If you sent this before and don't set it here it will be removed.
* @param {object} [options.targetFeedsExtraData] extra data related to target feeds
* @return {Promise<ReactionAPIResponse<ReactionType | ChildReactionType>>}
* @example reactions.update("67b3e3b5-b201-4697-96ac-482eb14f88ec", "0c7db91c-67f9-11e8-bcd9-fe00a9219401", "like")
* @example reactions.update("67b3e3b5-b201-4697-96ac-482eb14f88ec", "0c7db91c-67f9-11e8-bcd9-fe00a9219401", "comment", {"text": "love it!"},)
*/
update(
id: string,
data?: StreamFeedGenerics['reactionType'] | StreamFeedGenerics['childReactionType'],
{ targetFeeds = [], targetFeedsExtraData }: ReactionUpdateOptions = {},
) {
const body: ReactionBody<StreamFeedGenerics['reactionType'] | StreamFeedGenerics['childReactionType']> = {
data,
target_feeds: this._convertTargetFeeds(targetFeeds),
};
if (targetFeedsExtraData != null) {
body.target_feeds_extra_data = targetFeedsExtraData;
}
return this.client.put<
ReactionAPIResponse<StreamFeedGenerics['reactionType'] | StreamFeedGenerics['childReactionType']>
>({
url: this.buildURL(id),
body,
token: this.token,
});
}
/**
* delete reaction
* @link https://getstream.io/activity-feeds/docs/node/reactions_introduction/?language=js#removing-reactions
* @method delete
* @memberof StreamReaction.prototype
* @param {string} id Reaction Id
* @param {bool} soft Soft delete
* @return {Promise<APIResponse>}
* @example reactions.delete("67b3e3b5-b201-4697-96ac-482eb14f88ec")
*/
delete(id: string, soft = false) {
return this.client.delete({
url: this.buildURL(id),
token: this.token,
qs: soft ? { soft: 'true' } : undefined,
});
}
/**
* restore deleted reaction
* @link https://getstream.io/activity-feeds/docs/node/reactions_introduction/?language=js#removing-reactions
* @method restore
* @memberof StreamReaction.prototype
* @param {string} id Reaction Id
* @return {Promise<APIResponse>}
* @example reactions.restore("67b3e3b5-b201-4697-96ac-482eb14f88ec")
*/
restore(id: string) {
return this.client.put({
url: this.buildURL(id, 'restore'),
token: this.token,
});
}
}