From 7c7c18615da8ceeef8c57a68fcaf27c30a7cc11f Mon Sep 17 00:00:00 2001 From: Amin Mahboubi Date: Fri, 11 Feb 2022 15:16:00 +0100 Subject: [PATCH] BREAKING: Refactor Generics into Single Generic (#490) Co-authored-by: Amin Mahboubi --- README.md | 31 ++--- src/batch_operations.ts | 8 +- src/client.ts | 214 ++++++++++++-------------------- src/collections.ts | 138 ++++++++++----------- src/connect.ts | 23 ++-- src/feed.ts | 261 ++++++++++++++++------------------------ src/personalization.ts | 33 ++--- src/reaction.ts | 86 +++++-------- src/user.ts | 43 +++---- test/typescript/test.ts | 67 +++++------ 10 files changed, 363 insertions(+), 541 deletions(-) diff --git a/README.md b/README.md index 54caee8d..8ef168b5 100644 --- a/README.md +++ b/README.md @@ -267,7 +267,7 @@ client.feed('user', 'ken').updateActivityToTargets('foreign_id:1234', timestamp, ### Typescript ```ts -import { connect, EnrichedActivity, NotificationActivity } from getstream; +import { connect, UR, EnrichedActivity, NotificationActivity } from getstream; type User1Type = { name: string; username: string; image?: string }; type User2Type = { name: string; avatar?: string }; @@ -278,13 +278,16 @@ type Collection2Type = { branch: number; location: string }; type ReactionType = { text: string }; type ChildReactionType = { text?: string }; -const client = connect< - User1Type | User2Type, - ActivityType, - Collection1Type | Collection2Type, - ReactionType, - ChildReactionType ->('api_key', 'secret!', 'app_id'); +type StreamType = { + userType: User1Type | User2Type, + activityType: ActivityType, + collectionType: Collection1Type | Collection2Type, + reactionType: ReactionType, + childReactionType: ChildReactionType, + personalizationType: UR, +} + +const client = connect('api_key', 'secret!', 'app_id'); // if you have different union types like "User1Type | User2Type" you can use type guards as follow: // https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types @@ -301,21 +304,21 @@ client return id; }); -// notification: StreamFeed +// notification: StreamFeed const timeline = client.feed('timeline', 'feed_id'); timeline.get({ withOwnChildren: true, withOwnReactions: true }).then((response) => { - // response: FeedAPIResponse + // response: FeedAPIResponse if (response.next !== '') return response.next; - return (response.results as EnrichedActivity[]).map((activity) => { + return (response.results as EnrichedActivity[]).map((activity) => { return activity.id + activity.text + (activity.actor as User2Type).name; }); }); -// notification: StreamFeed +// notification: StreamFeed const notification = client.feed('notification', 'feed_id'); notification.get({ mark_read: true, mark_seen: true }).then((response) => { - // response: FeedAPIResponse + // response: FeedAPIResponse if (response.unread || response.unseen) return response.next; return (response.results as NotificationActivity[]).map((activityGroup) => { @@ -324,7 +327,7 @@ notification.get({ mark_read: true, mark_seen: true }).then((response) => { }); }); -client.collections.get('collection_1', 'taco').then((item: CollectionEntry) => { +client.collections.get('collection_1', 'taco').then((item: CollectionEntry) => { if (item.data.rating) return { [item.data.cid]: item.data.rating }; return item.id; }); diff --git a/src/batch_operations.ts b/src/batch_operations.ts index 02e72349..d708570a 100644 --- a/src/batch_operations.ts +++ b/src/batch_operations.ts @@ -1,4 +1,4 @@ -import { StreamClient, APIResponse } from './client'; +import { StreamClient, APIResponse, DefaultGenerics } from './client'; import utils from './utils'; type BaseFollowRelation = { @@ -24,7 +24,11 @@ export type UnfollowRelation = BaseFollowRelation & { * @param {string[]} feeds Array of feed id in form of `${feedSlug}:${feedId}` * @return {Promise} */ -function addToMany(this: StreamClient, activity: ActivityType, feeds: string[]) { +function addToMany( + this: StreamClient, + activity: StreamFeedGenerics['activityType'], + feeds: string[], +) { this._throwMissingApiSecret(); return this.post({ diff --git a/src/client.ts b/src/client.ts index b089e49d..c94d24f3 100644 --- a/src/client.ts +++ b/src/client.ts @@ -35,6 +35,15 @@ const pkg = require('../package.json'); export type UR = Record; export type UnknownRecord = UR; // alias to avoid breaking change +export type DefaultGenerics = { + activityType: UR; + childReactionType: UR; + collectionType: UR; + personalizationType: UR; + reactionType: UR; + userType: UR; +}; + export type APIResponse = { duration?: string }; export type FileUploadAPIResponse = APIResponse & { file: string }; @@ -107,23 +116,19 @@ export type HandlerCallback = (...args: unknown[]) => unknown; export type ForeignIDTimes = { foreign_id: string; time: Date | string } | { foreignID: string; time: Date | string }; -export type ActivityPartialChanges = Partial & { - id?: string; - set?: Partial; - unset?: Array>; -}; +export type ActivityPartialChanges = + Partial & { + id?: string; + set?: Partial; + unset?: Array>; + }; -export type RealTimeMessage< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, -> = { +export type RealTimeMessage = { deleted: Array; deleted_foreign_ids: Array<[id: string, time: string]>; new: Array< Omit< - EnrichedActivity, + EnrichedActivity, 'latest_reactions' | 'latest_reactions_extra' | 'own_reactions' | 'own_reactions_extra' | 'reaction_counts' > & { group?: string } >; @@ -138,14 +143,7 @@ export type RealTimeMessage< * Client to connect to Stream api * @class StreamClient */ -export class StreamClient< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, - PersonalizationType extends UR = UR, -> { +export class StreamClient { baseUrl: string; baseAnalyticsUrl: string; apiKey: string; @@ -162,7 +160,7 @@ export class StreamClient< group: string; expireTokens: boolean; location: string; - fayeClient: Faye.Client> | null; + fayeClient: Faye.Client> | null; browser: boolean; node: boolean; nodeOptions?: { httpAgent: http.Agent; httpsAgent: https.Agent }; @@ -174,40 +172,22 @@ export class StreamClient< >; handlers: Record; - currentUser?: StreamUser< - UserType, - ActivityType, - CollectionType, - ReactionType, - ChildReactionType, - PersonalizationType - >; - personalization: Personalization< - UserType, - ActivityType, - CollectionType, - ReactionType, - ChildReactionType, - PersonalizationType - >; - collections: Collections< - UserType, - ActivityType, - CollectionType, - ReactionType, - ChildReactionType, - PersonalizationType - >; + currentUser?: StreamUser; + personalization: Personalization; + collections: Collections; files: StreamFileStore; images: StreamImageStore; - reactions: StreamReaction; + reactions: StreamReaction; private _personalizationToken?: string; private _collectionsToken?: string; private _getOrCreateToken?: string; - // eslint-disable-next-line no-shadow - addToMany?: (this: StreamClient, activity: ActivityType, feeds: string[]) => Promise; // eslint-disable-line no-use-before-define + addToMany?: ( // eslint-disable-line no-shadow + this: StreamClient, // eslint-disable-line no-use-before-define + activity: StreamFeedGenerics['activityType'], + feeds: string[], + ) => Promise; // eslint-disable-line no-use-before-define followMany?: (this: StreamClient, follows: FollowRelation[], activityCopyLimit?: number) => Promise; // eslint-disable-line no-use-before-define unfollowMany?: (this: StreamClient, unfollows: UnfollowRelation[]) => Promise; // eslint-disable-line no-use-before-define createRedirectUrl?: (this: StreamClient, targetUrl: string, userId: string, events: unknown[]) => string; // eslint-disable-line no-use-before-define @@ -285,38 +265,17 @@ export class StreamClient< ...(this.nodeOptions || {}), }); - this.personalization = new Personalization< - UserType, - ActivityType, - CollectionType, - ReactionType, - ChildReactionType, - PersonalizationType - >(this); + this.personalization = new Personalization(this); if (this.browser && this.usingApiSecret) { throw new FeedError( 'You are publicly sharing your App Secret. Do not expose the App Secret in browsers, "native" mobile apps, or other non-trusted environments.', ); } - this.collections = new Collections< - UserType, - ActivityType, - CollectionType, - ReactionType, - ChildReactionType, - PersonalizationType - >(this, this.getOrCreateToken()); + this.collections = new Collections(this, this.getOrCreateToken()); this.files = new StreamFileStore(this as StreamClient, this.getOrCreateToken()); this.images = new StreamImageStore(this as StreamClient, this.getOrCreateToken()); - this.reactions = new StreamReaction< - UserType, - ActivityType, - CollectionType, - ReactionType, - ChildReactionType, - PersonalizationType - >(this, this.getOrCreateToken()); + this.reactions = new StreamReaction(this, this.getOrCreateToken()); // If we are in a node environment and batchOperations/createRedirectUrl is available add the methods to the prototype of StreamClient if (BatchOperations && !!createRedirectUrl) { @@ -496,11 +455,9 @@ export class StreamClient< */ feed( feedSlug: string, - userId?: - | string - | StreamUser, + userId?: string | StreamUser, token?: string, - ): StreamFeed { + ): StreamFeed { if (userId instanceof StreamUser) userId = userId.id; if (token === undefined) { @@ -511,12 +468,7 @@ export class StreamClient< } } - return new StreamFeed( - this, - feedSlug, - userId || (this.userId as string), - token, - ); + return new StreamFeed(this, feedSlug, userId || (this.userId as string), token); } /** @@ -619,12 +571,12 @@ export class StreamClient< getFayeAuthorization() { return { incoming: ( - message: Faye.Message>, - callback: Faye.Callback>, + message: Faye.Message>, + callback: Faye.Callback>, ) => callback(message), outgoing: ( - message: Faye.Message>, - callback: Faye.Callback>, + message: Faye.Message>, + callback: Faye.Callback>, ) => { if (message.subscription && this.subscriptions[message.subscription]) { const subscription = this.subscriptions[message.subscription]; @@ -651,10 +603,7 @@ export class StreamClient< */ getFayeClient(timeout = 10) { if (this.fayeClient === null) { - this.fayeClient = new Faye.Client>( - this.fayeUrl, - { timeout }, - ); + this.fayeClient = new Faye.Client>(this.fayeUrl, { timeout }); const authExtension = this.getFayeAuthorization(); this.fayeClient.addExtension(authExtension); } @@ -776,10 +725,10 @@ export class StreamClient< /** * Updates all supplied activities on the stream * @link https://getstream.io/activity-feeds/docs/node/adding_activities/?language=js#updating-activities - * @param {UpdateActivity[]} activities list of activities to update + * @param {UpdateActivity[]} activities list of activities to update * @return {Promise} */ - updateActivities(activities: UpdateActivity[]) { + updateActivities(activities: UpdateActivity[]) { this._throwMissingApiSecret(); if (!(activities instanceof Array)) { @@ -801,10 +750,10 @@ export class StreamClient< /** * Updates one activity on the stream * @link https://getstream.io/activity-feeds/docs/node/adding_activities/?language=js#updating-activities - * @param {UpdateActivity} activity The activity to update + * @param {UpdateActivity} activity The activity to update * @return {Promise} */ - updateActivity(activity: UpdateActivity) { + updateActivity(activity: UpdateActivity) { this._throwMissingApiSecret(); return this.updateActivities([activity]); @@ -863,7 +812,7 @@ export class StreamClient< this.replaceReactionOptions(params); const path = this.shouldUseEnrichEndpoint(params) ? 'enrich/activities/' : 'activities/'; - return this.get>({ + return this.get>({ url: path, qs: { ...params, ...extraParams }, token, @@ -880,14 +829,10 @@ export class StreamClient< } user(userId: string) { - return new StreamUser( - this, - userId, - this.getOrCreateToken(), - ); + return new StreamUser(this, userId, this.getOrCreateToken()); } - async setUser(data: UserType) { + async setUser(data: StreamFeedGenerics['userType']) { if (this.usingApiSecret) { throw new SiteError('This method can only be used client-side using a user token'); } @@ -895,16 +840,7 @@ export class StreamClient< const body = { ...data }; delete body.id; - const user = await ( - this.currentUser as StreamUser< - UserType, - ActivityType, - CollectionType, - ReactionType, - ChildReactionType, - PersonalizationType - > - ).getOrCreate(body); + const user = await (this.currentUser as StreamUser).getOrCreate(body); this.currentUser = user; return user; } @@ -918,9 +854,7 @@ export class StreamClient< } personalizedFeed(options: GetFeedOptions = {}) { - return this.get< - PersonalizationFeedAPIResponse - >({ + return this.get>({ url: 'enrich/personalization/feed/', qs: options, token: this.getOrCreateToken(), @@ -930,8 +864,8 @@ export class StreamClient< /** * Update a single activity with partial operations. * @link https://getstream.io/activity-feeds/docs/node/adding_activities/?language=js&q=partial+#activity-partial-update - * @param {ActivityPartialChanges} data object containing either the ID or the foreign_id and time of the activity and the operations to issue as set:{...} and unset:[...]. - * @return {Promise>} + * @param {ActivityPartialChanges} data object containing either the ID or the foreign_id and time of the activity and the operations to issue as set:{...} and unset:[...]. + * @return {Promise>} * @example * client.activityPartialUpdate({ * id: "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", @@ -960,8 +894,8 @@ export class StreamClient< * }) */ async activityPartialUpdate( - data: ActivityPartialChanges, - ): Promise> { + data: ActivityPartialChanges, + ): Promise> { const { activities, ...response } = await this.activitiesPartialUpdate([data]); const [activity] = activities; return { ...activity, ...response }; @@ -970,8 +904,8 @@ export class StreamClient< /** * Update multiple activities with partial operations. * @link https://getstream.io/activity-feeds/docs/node/adding_activities/?language=js&q=partial+#activity-partial-update - * @param {ActivityPartialChanges[]} changes array containing the changesets to be applied. Every changeset contains the activity identifier which is either the ID or the pair of of foreign ID and time of the activity. The operations to issue can be set:{...} and unset:[...]. - * @return {Promise<{ activities: Activity[] }>} + * @param {ActivityPartialChanges[]} changes array containing the changesets to be applied. Every changeset contains the activity identifier which is either the ID or the pair of of foreign ID and time of the activity. The operations to issue can be set:{...} and unset:[...]. + * @return {Promise<{ activities: Activity[] }>} * @example * client.activitiesPartialUpdate([ * { @@ -1021,27 +955,29 @@ export class StreamClient< * }, * ]) */ - activitiesPartialUpdate(changes: ActivityPartialChanges[]) { + activitiesPartialUpdate(changes: ActivityPartialChanges[]) { if (!(changes instanceof Array)) { throw new TypeError('changes should be an Array'); } - changes.forEach((item: ActivityPartialChanges & { foreign_id?: string; foreignID?: string }) => { - if (!(item instanceof Object)) { - throw new TypeError(`changeset should be and Object`); - } - if (item.foreignID) { - item.foreign_id = item.foreignID; - } - if (item.id === undefined && (item.foreign_id === undefined || item.time === undefined)) { - throw new TypeError('missing id or foreign_id and time'); - } - if (item.set && !(item.set instanceof Object)) { - throw new TypeError('set field should be an Object'); - } - if (item.unset && !(item.unset instanceof Array)) { - throw new TypeError('unset field should be an Array'); - } - }); + changes.forEach( + (item: ActivityPartialChanges & { foreign_id?: string; foreignID?: string }) => { + if (!(item instanceof Object)) { + throw new TypeError(`changeset should be and Object`); + } + if (item.foreignID) { + item.foreign_id = item.foreignID; + } + if (item.id === undefined && (item.foreign_id === undefined || item.time === undefined)) { + throw new TypeError('missing id or foreign_id and time'); + } + if (item.set && !(item.set instanceof Object)) { + throw new TypeError('set field should be an Object'); + } + if (item.unset && !(item.unset instanceof Array)) { + throw new TypeError('unset field should be an Array'); + } + }, + ); let token = this.userToken as string; if (this.usingApiSecret) { @@ -1051,7 +987,7 @@ export class StreamClient< }); } - return this.post[] }>({ + return this.post[] }>({ url: 'activity/', body: { changes }, token, diff --git a/src/collections.ts b/src/collections.ts index c78cd5d1..0d91187d 100644 --- a/src/collections.ts +++ b/src/collections.ts @@ -1,65 +1,62 @@ -import { StreamClient, APIResponse, UR } from './client'; +import { StreamClient, APIResponse, DefaultGenerics } from './client'; import { SiteError } from './errors'; -type BaseCollection = { +type BaseCollection = { collection: string; - data: CollectionType; + data: StreamFeedGenerics['collectionType']; id: string; }; -export type CollectionItem = CollectionType & { - id: string; - user_id?: string; -}; +export type CollectionItem = + StreamFeedGenerics['collectionType'] & { + id: string; + user_id?: string; + }; -export type CollectionResponse = BaseCollection & { - created_at: string; - foreign_id: string; - updated_at: string; -}; +export type CollectionResponse = + BaseCollection & { + created_at: string; + foreign_id: string; + updated_at: string; + }; -export type NewCollectionEntry = BaseCollection & { - user_id?: string; -}; +export type NewCollectionEntry = + BaseCollection & { + user_id?: string; + }; -export type CollectionAPIResponse = APIResponse & CollectionResponse; +export type CollectionAPIResponse = APIResponse & + CollectionResponse; -export type SelectCollectionAPIResponse = APIResponse & { +export type SelectCollectionAPIResponse = APIResponse & { response: { - data: CollectionResponse[]; + data: CollectionResponse[]; }; }; -export type UpsertCollectionAPIResponse = APIResponse & { +export type UpsertCollectionAPIResponse = APIResponse & { data: { - [key: string]: CollectionItem[]; + [key: string]: CollectionItem[]; }; }; -export type UpsertManyCollectionRequest = { - [collection: string]: CollectionItem[]; +export type UpsertManyCollectionRequest = { + [collection: string]: CollectionItem[]; }; -export class CollectionEntry< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, - PersonalizationType extends UR = UR, -> { +export class CollectionEntry { id: string; collection: string; - store: Collections; // eslint-disable-line no-use-before-define - data: CollectionType | null; + store: Collections; // eslint-disable-line no-use-before-define + data: StreamFeedGenerics['collectionType'] | null; full?: unknown; constructor( // eslint-disable-next-line no-use-before-define - store: Collections, + store: Collections, collection: string, id: string, - data: CollectionType, + data: StreamFeedGenerics['collectionType'], ) { this.collection = collection; this.store = store; @@ -75,7 +72,7 @@ export class CollectionEntry< * get item from collection and sync data * @method get * @memberof CollectionEntry.prototype - * @return {Promise>} + * @return {Promise>} * @example collection.get("0c7db91c-67f9-11e8-bcd9-fe00a9219401") */ async get() { @@ -90,11 +87,11 @@ export class CollectionEntry< * @link https://getstream.io/activity-feeds/docs/node/collections_introduction/?language=js#adding-collections * @method add * @memberof CollectionEntry.prototype - * @return {Promise>} + * @return {Promise>} * @example collection.add("cheese101", {"name": "cheese burger","toppings": "cheese"}) */ async add() { - const response = await this.store.add(this.collection, this.id, this.data as CollectionType); + const response = await this.store.add(this.collection, this.id, this.data as StreamFeedGenerics['collectionType']); this.data = response.data; this.full = response; return response; @@ -105,12 +102,16 @@ export class CollectionEntry< * @link https://getstream.io/activity-feeds/docs/node/collections_introduction/?language=js#updating-collections * @method update * @memberof CollectionEntry.prototype - * @return {Promise>} + * @return {Promise>} * @example store.update("0c7db91c-67f9-11e8-bcd9-fe00a9219401", {"name": "cheese burger","toppings": "cheese"}) * @example store.update("cheese101", {"name": "cheese burger","toppings": "cheese"}) */ async update() { - const response = await this.store.update(this.collection, this.id, this.data as CollectionType); + const response = await this.store.update( + this.collection, + this.id, + this.data as StreamFeedGenerics['collectionType'], + ); this.data = response.data; this.full = response; return response; @@ -132,15 +133,8 @@ export class CollectionEntry< } } -export class Collections< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, - PersonalizationType extends UR = UR, -> { - client: StreamClient; +export class Collections { + client: StreamClient; token: string; /** @@ -150,10 +144,7 @@ export class Collections< * @param {StreamCloudClient} client Stream client this collection is constructed from * @param {string} token JWT token */ - constructor( - client: StreamClient, - token: string, - ) { + constructor(client: StreamClient, token: string) { this.client = client; this.token = token; } @@ -163,15 +154,8 @@ export class Collections< return itemId === undefined ? url : `${url}${itemId}/`; }; - entry(collection: string, itemId: string, itemData: CollectionType) { - return new CollectionEntry< - UserType, - ActivityType, - CollectionType, - ReactionType, - ChildReactionType, - PersonalizationType - >(this, collection, itemId, itemData); + entry(collection: string, itemId: string, itemData: StreamFeedGenerics['collectionType']) { + return new CollectionEntry(this, collection, itemId, itemData); } /** @@ -181,11 +165,11 @@ export class Collections< * @memberof Collections.prototype * @param {string} collection collection name * @param {string} itemId id for this entry - * @return {Promise>} + * @return {Promise>} * @example collection.get("food", "0c7db91c-67f9-11e8-bcd9-fe00a9219401") */ async get(collection: string, itemId: string) { - const response = await this.client.get>({ + const response = await this.client.get>({ url: this.buildURL(collection, itemId), token: this.token, }); @@ -203,11 +187,11 @@ export class Collections< * @param {string} collection collection name * @param {string | null} itemId entry id, if null a random id will be assigned to the item * @param {CollectionType} itemData ObjectStore data - * @return {Promise>} + * @return {Promise>} * @example collection.add("food", "cheese101", {"name": "cheese burger","toppings": "cheese"}) */ - async add(collection: string, itemId: string | null, itemData: CollectionType) { - const response = await this.client.post>({ + async add(collection: string, itemId: string | null, itemData: StreamFeedGenerics['collectionType']) { + const response = await this.client.post>({ url: this.buildURL(collection), body: { id: itemId === null ? undefined : itemId, @@ -229,12 +213,12 @@ export class Collections< * @param {string} collection collection name * @param {string} entryId Collection object id * @param {CollectionType} data ObjectStore data - * @return {Promise>} + * @return {Promise>} * @example store.update("0c7db91c-67f9-11e8-bcd9-fe00a9219401", {"name": "cheese burger","toppings": "cheese"}) * @example store.update("food", "cheese101", {"name": "cheese burger","toppings": "cheese"}) */ - async update(collection: string, entryId: string, data: CollectionType) { - const response = await this.client.put>({ + async update(collection: string, entryId: string, data: StreamFeedGenerics['collectionType']) { + const response = await this.client.put>({ url: this.buildURL(collection, entryId), body: { data }, token: this.token, @@ -268,17 +252,17 @@ export class Collections< * @method upsert * @memberof Collections.prototype * @param {string} collection collection name - * @param {NewCollectionEntry | NewCollectionEntry[]} data - A single json object or an array of objects - * @return {Promise>} + * @param {NewCollectionEntry | NewCollectionEntry[]} data - A single json object or an array of objects + * @return {Promise>} */ - upsert(collection: string, data: CollectionItem | CollectionItem[]) { + upsert(collection: string, data: CollectionItem | CollectionItem[]) { if (!this.client.usingApiSecret) { throw new SiteError('This method can only be used server-side using your API Secret'); } if (!Array.isArray(data)) data = [data]; - return this.client.post>({ + return this.client.post>({ url: 'collections/', serviceName: 'api', body: { data: { [collection]: data } }, @@ -293,14 +277,14 @@ export class Collections< * @memberof Collections.prototype * @param {string} collection collection name * @param {UpsertManyCollectionRequest} items - A single json object that contains information of many collections - * @return {Promise>} + * @return {Promise>} */ upsertMany(items: UpsertManyCollectionRequest) { if (!this.client.usingApiSecret) { throw new SiteError('This method can only be used server-side using your API Secret'); } - return this.client.post>({ + return this.client.post>({ url: 'collections/', serviceName: 'api', body: { data: items }, @@ -315,7 +299,7 @@ export class Collections< * @memberof Collections.prototype * @param {string} collection collection name * @param {string | string[]} ids - A single object id or an array of ids - * @return {Promise>} + * @return {Promise>} */ select(collection: string, ids: string | string[]) { if (!this.client.usingApiSecret) { @@ -324,7 +308,7 @@ export class Collections< if (!Array.isArray(ids)) ids = [ids]; - return this.client.get>({ + return this.client.get>({ url: 'collections/', serviceName: 'api', qs: { foreign_ids: ids.map((id) => `${collection}:${id}`).join(',') }, diff --git a/src/connect.ts b/src/connect.ts index 029c82db..988bb270 100644 --- a/src/connect.ts +++ b/src/connect.ts @@ -1,4 +1,4 @@ -import { StreamClient, UR, ClientOptions } from './client'; +import { StreamClient, ClientOptions, DefaultGenerics } from './client'; /** * Create StreamClient @@ -23,14 +23,12 @@ import { StreamClient, UR, ClientOptions } from './client'; * @example where streamURL looks like * "https://thierry:pass@gestream.io/?app=1" */ -export function connect< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, - PersonalizationType extends UR = UR, ->(apiKey: string, apiSecret: string | null, appId?: string, options?: ClientOptions) { +export function connect( + apiKey: string, + apiSecret: string | null, + appId?: string, + options?: ClientOptions, +) { if (typeof process !== 'undefined' && process.env?.STREAM_URL && !apiKey) { const parts = /https:\/\/(\w+):(\w+)@([\w-]*).*\?app_id=(\d+)/.exec(process.env.STREAM_URL) || []; apiKey = parts[1]; @@ -46,10 +44,5 @@ export function connect< } } - return new StreamClient( - apiKey, - apiSecret, - appId, - options, - ); + return new StreamClient(apiKey, apiSecret, appId, options); } diff --git a/src/feed.ts b/src/feed.ts index 14a2366a..f2f7ed13 100644 --- a/src/feed.ts +++ b/src/feed.ts @@ -1,7 +1,7 @@ /// import * as Faye from 'faye'; -import { StreamClient, APIResponse, UR, RealTimeMessage } from './client'; +import { StreamClient, APIResponse, UR, RealTimeMessage, DefaultGenerics } from './client'; import { StreamUser, EnrichedUser } from './user'; import { FeedError, SiteError } from './errors'; import utils from './utils'; @@ -76,78 +76,70 @@ type BaseActivity = { to?: string[]; }; -export type NewActivity = ActivityType & - BaseActivity & { - actor: string | StreamUser; - object: string | unknown; - foreign_id?: string; - time?: string; - }; +export type NewActivity = + StreamFeedGenerics['activityType'] & + BaseActivity & { + actor: string | StreamUser; + object: string | unknown; + foreign_id?: string; + time?: string; + }; -export type UpdateActivity = ActivityType & - BaseActivity & { - actor: string; - foreign_id: string; - object: string | unknown; - time: string; - }; +export type UpdateActivity = + StreamFeedGenerics['activityType'] & + BaseActivity & { + actor: string; + foreign_id: string; + object: string | unknown; + time: string; + }; -export type Activity = ActivityType & - BaseActivity & { - actor: string; - foreign_id: string; - id: string; - object: string | unknown; - time: string; - analytics?: Record; // ranked feeds only - extra_context?: UR; - origin?: string; - score?: number; // ranked feeds only - // ** Add new fields to EnrichedActivity as well ** - }; +export type Activity = + StreamFeedGenerics['activityType'] & + BaseActivity & { + actor: string; + foreign_id: string; + id: string; + object: string | unknown; + time: string; + analytics?: Record; // ranked feeds only + extra_context?: UR; + origin?: string; + score?: number; // ranked feeds only + // ** Add new fields to EnrichedActivity as well ** + }; -export type ReactionsRecords< - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, - UserType extends UR = UR, -> = Record[]>; - -export type EnrichedActivity< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, -> = ActivityType & - BaseActivity & - Pick & { - actor: EnrichedUser | string; - // Object should be casted based on the verb - object: - | string - | unknown - | EnrichedActivity - | EnrichedReaction - | CollectionResponse; - latest_reactions?: ReactionsRecords; - latest_reactions_extra?: Record; - own_reactions?: ReactionsRecords; - own_reactions_extra?: Record; - // Reaction posted to feed - reaction?: EnrichedReaction; - // enriched reactions - reaction_counts?: Record; - }; +export type ReactionsRecords = Record< + string, + EnrichedReaction[] +>; + +export type EnrichedActivity = + StreamFeedGenerics['activityType'] & + BaseActivity & + Pick & { + actor: EnrichedUser | string; + // Object should be casted based on the verb + object: + | string + | unknown + | EnrichedActivity + | EnrichedReaction + | CollectionResponse; + latest_reactions?: ReactionsRecords; + latest_reactions_extra?: Record; + own_reactions?: ReactionsRecords; + own_reactions_extra?: Record; + // Reaction posted to feed + reaction?: EnrichedReaction; + // enriched reactions + reaction_counts?: Record; + }; -export type FlatActivity = Activity; +export type FlatActivity = Activity; -export type FlatActivityEnriched< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, -> = EnrichedActivity; +export type FlatActivityEnriched = + EnrichedActivity; type BaseAggregatedActivity = { activity_count: number; @@ -160,79 +152,50 @@ type BaseAggregatedActivity = { score?: number; }; -export type AggregatedActivity = BaseAggregatedActivity & { - activities: Activity[]; -}; +export type AggregatedActivity = + BaseAggregatedActivity & { + activities: Activity[]; + }; -export type AggregatedActivityEnriched< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, -> = BaseAggregatedActivity & { - activities: EnrichedActivity[]; -}; +export type AggregatedActivityEnriched = + BaseAggregatedActivity & { + activities: EnrichedActivity[]; + }; type BaseNotificationActivity = { is_read: boolean; is_seen: boolean }; -export type NotificationActivity = AggregatedActivity & - BaseNotificationActivity; - -export type NotificationActivityEnriched< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, -> = BaseNotificationActivity & - AggregatedActivityEnriched; - -export type FeedAPIResponse< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, -> = APIResponse & { +export type NotificationActivity = + AggregatedActivity & BaseNotificationActivity; + +export type NotificationActivityEnriched = + BaseNotificationActivity & AggregatedActivityEnriched; + +export type FeedAPIResponse = APIResponse & { next: string; results: - | FlatActivity[] - | FlatActivityEnriched[] - | AggregatedActivity[] - | AggregatedActivityEnriched[] - | NotificationActivity[] - | NotificationActivityEnriched[]; + | FlatActivity[] + | FlatActivityEnriched[] + | AggregatedActivity[] + | AggregatedActivityEnriched[] + | NotificationActivity[] + | NotificationActivityEnriched[]; // Notification Feed only unread?: number; unseen?: number; }; -export type PersonalizationFeedAPIResponse< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, -> = APIResponse & { - limit: number; - next: string; - offset: number; - results: FlatActivityEnriched[]; - version: string; -}; +export type PersonalizationFeedAPIResponse = + APIResponse & { + limit: number; + next: string; + offset: number; + results: FlatActivityEnriched[]; + version: string; + }; -export type GetActivitiesAPIResponse< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, -> = APIResponse & { - results: - | FlatActivity[] - | FlatActivityEnriched[]; +export type GetActivitiesAPIResponse = APIResponse & { + results: FlatActivity[] | FlatActivityEnriched[]; }; /** @@ -240,15 +203,8 @@ export type GetActivitiesAPIResponse< * The feed object contains convenience functions such add activity, remove activity etc * @class StreamFeed */ -export class StreamFeed< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, - PersonalizationType extends UR = UR, -> { - client: StreamClient; +export class StreamFeed { + client: StreamClient; token: string; id: string; slug: string; @@ -267,12 +223,7 @@ export class StreamFeed< * @param {string} userId - The user id * @param {string} [token] - The authentication token */ - constructor( - client: StreamClient, - feedSlug: string, - userId: string, - token: string, - ) { + constructor(client: StreamClient, feedSlug: string, userId: string, token: string) { if (!feedSlug || !userId) { throw new FeedError('Please provide a feed slug and user id, ie client.feed("user", "1")'); } @@ -307,16 +258,16 @@ export class StreamFeed< * @link https://getstream.io/activity-feeds/docs/node/adding_activities/?language=js#adding-activities-basic * @method addActivity * @memberof StreamFeed.prototype - * @param {NewActivity} activity - The activity to add - * @return {Promise>} + * @param {NewActivity} activity - The activity to add + * @return {Promise>} */ - addActivity(activity: NewActivity) { + addActivity(activity: NewActivity) { activity = utils.replaceStreamObjects(activity); if (!activity.actor && this.client.currentUser) { activity.actor = this.client.currentUser.ref(); } - return this.client.post>({ + return this.client.post>({ url: `feed/${this.feedUrl}/`, body: activity, token: this.token, @@ -350,11 +301,11 @@ export class StreamFeed< * @link https://getstream.io/activity-feeds/docs/node/add_many_activities/?language=js#batch-add-activities * @method addActivities * @memberof StreamFeed.prototype - * @param {NewActivity[]} activities Array of activities to add - * @return {Promise[]>} + * @param {NewActivity[]} activities Array of activities to add + * @return {Promise[]>} */ - addActivities(activities: NewActivity[]) { - return this.client.post[]>({ + addActivities(activities: NewActivity[]) { + return this.client.post[]>({ url: `feed/${this.feedUrl}/`, body: { activities: utils.replaceStreamObjects(activities) }, token: this.token, @@ -518,7 +469,7 @@ export class StreamFeed< const path = this.client.shouldUseEnrichEndpoint(options) ? 'enrich/feed/' : 'feed/'; - return this.client.get>({ + return this.client.get>({ url: `${path}${this.feedUrl}/`, qs: { ...options, ...extraOptions }, token: this.token, @@ -563,14 +514,14 @@ export class StreamFeed< * @link https://getstream.io/activity-feeds/docs/node/web_and_mobile/?language=js#subscribe-to-realtime-updates-via-api-client * @method subscribe * @memberof StreamFeed.prototype - * @param {function} Faye.Callback> Callback to call on completion + * @param {function} Faye.Callback> Callback to call on completion * @return {Promise} * @example * feed.subscribe(callback).then(function(){ * console.log('we are now listening to changes'); * }); */ - subscribe(callback: Faye.SubscribeCallback>) { + subscribe(callback: Faye.SubscribeCallback>) { if (!this.client.appId) { throw new SiteError( 'Missing app id, which is needed to subscribe, use var client = stream.connect(key, secret, appId);', @@ -651,7 +602,7 @@ export class StreamFeed< if (addedTargets) body.added_targets = addedTargets; if (removedTargets) body.removed_targets = removedTargets; - return this.client.post & { added?: string[]; removed?: string[] }>({ + return this.client.post & { added?: string[]; removed?: string[] }>({ url: `feed_targets/${this.feedUrl}/activity_to_targets/`, token: this.token, body, diff --git a/src/personalization.ts b/src/personalization.ts index aad003f9..1c879bf0 100644 --- a/src/personalization.ts +++ b/src/personalization.ts @@ -1,4 +1,4 @@ -import { StreamClient, APIResponse, UR } from './client'; +import { StreamClient, APIResponse, UR, DefaultGenerics } from './client'; /** * Manage api calls for personalization @@ -6,24 +6,17 @@ import { StreamClient, APIResponse, UR } from './client'; * @class Personalization */ -export type PersonalizationAPIResponse = APIResponse & { +export type PersonalizationAPIResponse = APIResponse & { app_id: string; next: string; - results: PersonalizationType[]; + results: Array; limit?: number; offset?: number; version?: string; }; -export class Personalization< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, - PersonalizationType extends UR = UR, -> { - client: StreamClient; +export class Personalization { + client: StreamClient; /** * Initialize the Personalization class @@ -32,9 +25,7 @@ export class Personalization< * @memberof Personalization.prototype * @param {StreamClient} client - The stream client */ - constructor( - client: StreamClient, - ) { + constructor(client: StreamClient) { this.client = client; } @@ -45,11 +36,11 @@ export class Personalization< * @memberof Personalization.prototype * @param {string} resource - personalized resource endpoint i.e "follow_recommendations" * @param {object} options Additional options - * @return {Promise>} Promise object. Personalized feed + * @return {Promise>} Promise object. Personalized feed * @example client.personalization.get('follow_recommendations', {foo: 'bar', baz: 'qux'}) */ get(resource: string, options: Record & { token?: string } = {}) { - return this.client.get>({ + return this.client.get>({ url: `${resource}/`, serviceName: 'personalization', qs: options, @@ -65,11 +56,11 @@ export class Personalization< * @param {string} resource - personalized resource endpoint i.e "follow_recommendations" * @param {object} options - Additional options * @param {object} data - Data to send in the payload - * @return {Promise>} Promise object. Data that was posted if successful, or an error. + * @return {Promise>} Promise object. Data that was posted if successful, or an error. * @example client.personalization.post('follow_recommendations', {foo: 'bar', baz: 'qux'}) */ post(resource: string, options: Record = {}, data: UR = {}) { - return this.client.post>({ + return this.client.post>({ url: `${resource}/`, serviceName: 'personalization', qs: options, @@ -85,11 +76,11 @@ export class Personalization< * @memberof Personalization.prototype * @param {object} resource - personalized resource endpoint i.e "follow_recommendations" * @param {object} options - Additional options - * @return {Promise>} Promise object. Data that was deleted if successful, or an error. + * @return {Promise>} Promise object. Data that was deleted if successful, or an error. * @example client.personalization.delete('follow_recommendations', {foo: 'bar', baz: 'qux'}) */ delete(resource: string, options: Record = {}) { - return this.client.delete>({ + return this.client.delete>({ url: `${resource}/`, serviceName: 'personalization', qs: options, diff --git a/src/reaction.ts b/src/reaction.ts index cd1e7e31..c70478b9 100644 --- a/src/reaction.ts +++ b/src/reaction.ts @@ -1,4 +1,4 @@ -import { StreamClient, APIResponse, UR } from './client'; +import { StreamClient, APIResponse, UR, DefaultGenerics } from './client'; import { StreamFeed } from './feed'; import { SiteError } from './errors'; import { EnrichedUser } from './user'; @@ -36,41 +36,29 @@ export type Reaction = { export type ReactionAPIResponse = APIResponse & Reaction; export type ChildReactionsRecords< - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, - UserType extends UR = UR, + StreamFeedGenerics extends DefaultGenerics = DefaultGenerics, // eslint-disable-next-line no-use-before-define -> = Record[]>; +> = Record[]>; -export type EnrichedReaction< - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, - UserType extends UR = UR, -> = Reaction & { +export type EnrichedReaction = Reaction< + StreamFeedGenerics['reactionType'] | StreamFeedGenerics['childReactionType'] +> & { children_counts: Record; - latest_children: ChildReactionsRecords; + latest_children: ChildReactionsRecords; latest_children_extra?: Record; - own_children?: ChildReactionsRecords; - user?: EnrichedUser; + own_children?: ChildReactionsRecords; + user?: EnrichedUser; }; -export type EnrichedReactionAPIResponse< - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, - UserType extends UR = UR, -> = APIResponse & EnrichedReaction; +export type EnrichedReactionAPIResponse = APIResponse & + EnrichedReaction; -export type ReactionFilterAPIResponse< - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, - ActivityType extends UR = UR, - UserType extends UR = UR, -> = APIResponse & { +export type ReactionFilterAPIResponse = APIResponse & { next: string; results: - | ReactionAPIResponse[] - | EnrichedReactionAPIResponse[]; - activity?: ActivityType; + | ReactionAPIResponse[] + | EnrichedReactionAPIResponse[]; + activity?: StreamFeedGenerics['childReactionType']; }; export type ReactionFilterConditions = { @@ -101,15 +89,8 @@ export type ReactionAddChildOptions = ReactionUpdateOptions & { userId?: string; }; -export class StreamReaction< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, - PersonalizationType extends UR = UR, -> { - client: StreamClient; +export class StreamReaction { + client: StreamClient; token: string; /** @@ -121,10 +102,7 @@ export class StreamReaction< * @param {string} token JWT token * @example new StreamReaction(client, "eyJhbGciOiJIUzI1...") */ - constructor( - client: StreamClient, - token: string, - ) { + constructor(client: StreamClient, token: string) { this.client = client; this.token = token; } @@ -157,10 +135,10 @@ export class StreamReaction< add( kind: string, activity: string | { id: string }, - data?: ReactionType, + data?: StreamFeedGenerics['reactionType'], { id, targetFeeds = [], userId, targetFeedsExtraData }: ReactionAddOptions = {}, ) { - const body: ReactionBody = { + const body: ReactionBody = { id, activity_id: activity instanceof Object ? (activity as { id: string }).id : activity, kind, @@ -171,7 +149,7 @@ export class StreamReaction< if (targetFeedsExtraData != null) { body.target_feeds_extra_data = targetFeedsExtraData; } - return this.client.post>({ + return this.client.post>({ url: this.buildURL(), body, token: this.token, @@ -197,10 +175,10 @@ export class StreamReaction< addChild( kind: string, reaction: string | { id: string }, - data?: ChildReactionType, + data?: StreamFeedGenerics['childReactionType'], { targetFeeds = [], userId, targetFeedsExtraData }: ReactionAddChildOptions = {}, ) { - const body: ReactionBody = { + const body: ReactionBody = { parent: reaction instanceof Object ? (reaction as { id: string }).id : reaction, kind, data: data || {}, @@ -210,7 +188,7 @@ export class StreamReaction< if (targetFeedsExtraData != null) { body.target_feeds_extra_data = targetFeedsExtraData; } - return this.client.post>({ + return this.client.post>({ url: this.buildURL(), body, token: this.token, @@ -223,11 +201,11 @@ export class StreamReaction< * @method get * @memberof StreamReaction.prototype * @param {string} id Reaction Id - * @return {Promise>} + * @return {Promise>} * @example reactions.get("67b3e3b5-b201-4697-96ac-482eb14f88ec") */ get(id: string) { - return this.client.get>({ + return this.client.get>({ url: this.buildURL(id), token: this.token, }); @@ -244,7 +222,7 @@ export class StreamReaction< * @method filter * @memberof StreamReaction.prototype * @param {ReactionFilterConditions} conditions Reaction Id {activity_id|user_id|reaction_id:string, kind:string, limit:integer} - * @return {Promise>} + * @return {Promise>} * @example reactions.filter({activity_id: "0c7db91c-67f9-11e8-bcd9-fe00a9219401", kind:"like"}) * @example reactions.filter({user_id: "john", kinds:"like"}) */ @@ -265,7 +243,7 @@ export class StreamReaction< ? this.buildURL(lookupType as string, value as string, conditions.kind) : this.buildURL(lookupType as string, value as string); - return this.client.get>({ + return this.client.get>({ url, qs: qs as { [key: string]: unknown }, token: this.token, @@ -288,17 +266,19 @@ export class StreamReaction< */ update( id: string, - data?: ReactionType | ChildReactionType, + data?: StreamFeedGenerics['reactionType'] | StreamFeedGenerics['childReactionType'], { targetFeeds = [], targetFeedsExtraData }: ReactionUpdateOptions = {}, ) { - const body: ReactionBody = { + const body: ReactionBody = { data, target_feeds: this._convertTargetFeeds(targetFeeds), }; if (targetFeedsExtraData != null) { body.target_feeds_extra_data = targetFeedsExtraData; } - return this.client.put>({ + return this.client.put< + ReactionAPIResponse + >({ url: this.buildURL(id), body, token: this.token, diff --git a/src/user.ts b/src/user.ts index bc771511..e0258b89 100644 --- a/src/user.ts +++ b/src/user.ts @@ -1,32 +1,25 @@ -import { StreamClient, APIResponse, UR } from './client'; +import { StreamClient, APIResponse, DefaultGenerics } from './client'; -export type EnrichedUser = { +export type EnrichedUser = { created_at: string; - data: UserType; + data: StreamFeedGenerics['userType']; id: string; updated_at: string; }; -export type UserAPIResponse = APIResponse & - EnrichedUser & { +export type UserAPIResponse = APIResponse & + EnrichedUser & { // present only in profile response followers_count?: number; following_count?: number; }; -export class StreamUser< - UserType extends UR = UR, - ActivityType extends UR = UR, - CollectionType extends UR = UR, - ReactionType extends UR = UR, - ChildReactionType extends UR = UR, - PersonalizationType extends UR = UR, -> { - client: StreamClient; +export class StreamUser { + client: StreamClient; token: string; id: string; - data?: UserType; - full?: UserAPIResponse; + data?: StreamFeedGenerics['userType']; + full?: UserAPIResponse; private url: string; /** @@ -39,11 +32,7 @@ export class StreamUser< * @param {string} userAuthToken JWT token * @example new StreamUser(client, "123", "eyJhbGciOiJIUzI1...") */ - constructor( - client: StreamClient, - userId: string, - userAuthToken: string, - ) { + constructor(client: StreamClient, userId: string, userAuthToken: string) { this.client = client; this.id = userId; this.data = undefined; @@ -79,7 +68,7 @@ export class StreamUser< * @return {Promise} */ async get(options?: { with_follow_counts?: boolean }) { - const response = await this.client.get>({ + const response = await this.client.get>({ url: this.url, token: this.token, qs: options, @@ -98,8 +87,8 @@ export class StreamUser< * @param {boolean} [options.get_or_create] if user already exists return it * @return {Promise} */ - async create(data?: UserType, options?: { get_or_create?: boolean }) { - const response = await this.client.post>({ + async create(data?: StreamFeedGenerics['userType'], options?: { get_or_create?: boolean }) { + const response = await this.client.post>({ url: 'user/', body: { id: this.id, @@ -121,8 +110,8 @@ export class StreamUser< * @param {object} data user date stored in stream * @return {Promise} */ - async update(data?: Partial) { - const response = await this.client.put>({ + async update(data?: Partial) { + const response = await this.client.put>({ url: this.url, body: { data: data || this.data || {}, @@ -142,7 +131,7 @@ export class StreamUser< * @param {object} data user date stored in stream * @return {Promise} */ - getOrCreate(data: UserType) { + getOrCreate(data: StreamFeedGenerics['userType']) { return this.create(data, { get_or_create: true }); } diff --git a/test/typescript/test.ts b/test/typescript/test.ts index e144a331..f6c08c58 100644 --- a/test/typescript/test.ts +++ b/test/typescript/test.ts @@ -16,6 +16,7 @@ import { StreamFeed, FeedAPIResponse, FlatActivity, + RealTimeMessage, } from '../..'; type UserType = { name: string; image?: string }; @@ -25,23 +26,22 @@ type ReactionType = { rText: string }; type ChildReactionType = { cText: string }; type T = {}; +type Generics = { + userType: UserType; + activityType: ActivityType; + collectionType: CollectionType; + reactionType: ReactionType; + childReactionType: ChildReactionType; + personalizationType: Faye.UR; +}; + let voidReturn: void; let voidPromise: Promise; let emptyAPIPromise: Promise; -let client: StreamClient = connect< - UserType, - ActivityType, - CollectionType, - ReactionType, - ChildReactionType ->('api_key', 'secret!', 'app_id'); +let client: StreamClient = connect('api_key', 'secret!', 'app_id'); -client = new StreamClient( - 'api_key', - 'secret!', - 'app_id', -); +client = new StreamClient('api_key', 'secret!', 'app_id'); connect('', null); connect('', null, '', {}); @@ -105,7 +105,7 @@ client.shouldUseEnrichEndpoint({ // @ts-expect-error client.shouldUseEnrichEndpoint({ enrich: '' }); -const faye: Faye.Client = client.getFayeClient(); +const faye: Faye.Client> = client.getFayeClient(); client.getFayeClient(100); const upload: Promise = client.upload('/file', 'uri'); @@ -158,7 +158,7 @@ client.updateActivity(emptyActivity); // @ts-expect-error client.updateActivities([emptyActivity]); -const partialUpdatePromise: Promise> = client.activityPartialUpdate({ +const partialUpdatePromise: Promise> = client.activityPartialUpdate({ id: '', set: { aText: '' }, unset: ['attachments'], @@ -170,7 +170,7 @@ client.activityPartialUpdate({ unset: ['missing'] }); // @ts-expect-error client.activityPartialUpdate({ set: { missing: '' } }); -const partialUpdatesPromise: Promise<{ activities: Activity[] }> = client.activitiesPartialUpdate([ +const partialUpdatesPromise: Promise<{ activities: Activity[] }> = client.activitiesPartialUpdate([ { id: '', set: { aText: '' }, @@ -184,9 +184,7 @@ client.activityPartialUpdate([{ unset: ['missing'] }]); // @ts-expect-error client.activityPartialUpdate([{ set: { missing: '' } }]); -const activitiesPromise: Promise< - GetActivitiesAPIResponse -> = client.getActivities({ ids: ['', ''] }); +const activitiesPromise: Promise> = client.getActivities({ ids: ['', ''] }); activitiesPromise.then(({ results }) => { results[0].id as string; results[0].time as string; @@ -202,9 +200,7 @@ client.getActivities({}); // @ts-expect-error client.getActivities(); -const pFeedPromise: Promise< - PersonalizationFeedAPIResponse -> = client.personalizedFeed({ enrich: true }); +const pFeedPromise: Promise> = client.personalizedFeed({ enrich: true }); pFeedPromise.then((pFeed) => { pFeed.results as Array; pFeed.version as string; @@ -213,31 +209,26 @@ pFeedPromise.then((pFeed) => { pFeed.offset as number; }); -const userPromise: Promise> = client.setUser({ name: '' }); +const userPromise: Promise> = client.setUser({ name: '' }); // @ts-expect-error client.setUser({ username: '' }); -const user: StreamUser = client.user('user_id'); -const userGet: Promise> = client.user('user_id').get(); +const user: StreamUser = client.user('user_id'); +const userGet: Promise> = client.user('user_id').get(); client.user('user_id').get({ with_follow_counts: true }); // @ts-expect-error client.user('user_id').get({ with_follow_counts: 1 }); // @ts-expect-error client.user('user_id').get({ list: true }); -const timeline: StreamFeed = client.feed( - 'timeline', - 'feed_id', -); +const timeline: StreamFeed = client.feed('timeline', 'feed_id'); -timeline - .get({ withOwnChildren: true, withOwnReactions: true }) - .then((response: FeedAPIResponse) => { - response.next as string; - response.unread as number; - response.unseen as number; - response.results as FlatActivity[]; - }); +timeline.get({ withOwnChildren: true, withOwnReactions: true }).then((response: FeedAPIResponse) => { + response.next as string; + response.unread as number; + response.unseen as number; + response.results as FlatActivity[]; +}); client .feed('notification', 'feed_id') @@ -246,10 +237,10 @@ client response.next as string; response.unread as number; response.unseen as number; - response.results as NotificationActivity[]; + response.results as NotificationActivity[]; }); -const collection: Promise> = client.collections.get('collection_1', 'taco'); +const collection: Promise> = client.collections.get('collection_1', 'taco'); collection.then((item) => { item.id as string;