rettiwt-api 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/.dockerignore +2 -0
  2. package/.gitattributes +3 -0
  3. package/Dockerfile +9 -0
  4. package/README.md +38 -0
  5. package/dist/config/env.d.ts +5 -0
  6. package/dist/config/env.js +9 -0
  7. package/dist/config/env.js.map +1 -0
  8. package/dist/index.d.ts +10 -0
  9. package/dist/index.js +23 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/models/graphql/Global.d.ts +4 -0
  12. package/dist/models/graphql/Global.js +13 -0
  13. package/dist/models/graphql/Global.js.map +1 -0
  14. package/dist/models/graphql/TweetTypes.d.ts +6 -0
  15. package/dist/models/graphql/TweetTypes.js +156 -0
  16. package/dist/models/graphql/TweetTypes.js.map +1 -0
  17. package/dist/models/graphql/UserTypes.d.ts +3 -0
  18. package/dist/models/graphql/UserTypes.js +139 -0
  19. package/dist/models/graphql/UserTypes.js.map +1 -0
  20. package/dist/queries/RootQuery.d.ts +4 -0
  21. package/dist/queries/RootQuery.js +59 -0
  22. package/dist/queries/RootQuery.js.map +1 -0
  23. package/dist/resolvers/ResolverBase.d.ts +5 -0
  24. package/dist/resolvers/ResolverBase.js +11 -0
  25. package/dist/resolvers/ResolverBase.js.map +1 -0
  26. package/dist/resolvers/TweetResolver.d.ts +54 -0
  27. package/dist/resolvers/TweetResolver.js +328 -0
  28. package/dist/resolvers/TweetResolver.js.map +1 -0
  29. package/dist/resolvers/UserResolver.d.ts +47 -0
  30. package/dist/resolvers/UserResolver.js +302 -0
  31. package/dist/resolvers/UserResolver.js.map +1 -0
  32. package/dist/server.d.ts +1 -0
  33. package/dist/server.js +75 -0
  34. package/dist/server.js.map +1 -0
  35. package/dist/services/AuthService.d.ts +17 -0
  36. package/dist/services/AuthService.js +103 -0
  37. package/dist/services/AuthService.js.map +1 -0
  38. package/dist/services/CacheService.d.ts +25 -0
  39. package/dist/services/CacheService.js +141 -0
  40. package/dist/services/CacheService.js.map +1 -0
  41. package/dist/services/FetcherService.d.ts +30 -0
  42. package/dist/services/FetcherService.js +171 -0
  43. package/dist/services/FetcherService.js.map +1 -0
  44. package/dist/services/data/TweetService.d.ts +43 -0
  45. package/dist/services/data/TweetService.js +229 -0
  46. package/dist/services/data/TweetService.js.map +1 -0
  47. package/dist/services/data/UserAccountService.d.ts +49 -0
  48. package/dist/services/data/UserAccountService.js +250 -0
  49. package/dist/services/data/UserAccountService.js.map +1 -0
  50. package/dist/services/helper/Deserializers.d.ts +19 -0
  51. package/dist/services/helper/Deserializers.js +115 -0
  52. package/dist/services/helper/Deserializers.js.map +1 -0
  53. package/dist/services/helper/Extractors.d.ts +101 -0
  54. package/dist/services/helper/Extractors.js +409 -0
  55. package/dist/services/helper/Extractors.js.map +1 -0
  56. package/dist/services/helper/Headers.d.ts +9 -0
  57. package/dist/services/helper/Headers.js +47 -0
  58. package/dist/services/helper/Headers.js.map +1 -0
  59. package/dist/services/helper/Parser.d.ts +28 -0
  60. package/dist/services/helper/Parser.js +104 -0
  61. package/dist/services/helper/Parser.js.map +1 -0
  62. package/dist/services/helper/Urls.d.ts +74 -0
  63. package/dist/services/helper/Urls.js +114 -0
  64. package/dist/services/helper/Urls.js.map +1 -0
  65. package/dist/test/Test.js +2 -0
  66. package/dist/test/Test.js.map +1 -0
  67. package/dist/types/Authentication.d.ts +15 -0
  68. package/dist/types/Authentication.js +5 -0
  69. package/dist/types/Authentication.js.map +1 -0
  70. package/dist/types/HTTP.d.ts +22 -0
  71. package/dist/types/HTTP.js +30 -0
  72. package/dist/types/HTTP.js.map +1 -0
  73. package/dist/types/Service.d.ts +27 -0
  74. package/dist/types/Service.js +19 -0
  75. package/dist/types/Service.js.map +1 -0
  76. package/dist/types/Tweet.d.ts +40 -0
  77. package/dist/types/Tweet.js +5 -0
  78. package/dist/types/Tweet.js.map +1 -0
  79. package/dist/types/UserAccount.d.ts +19 -0
  80. package/dist/types/UserAccount.js +4 -0
  81. package/dist/types/UserAccount.js.map +1 -0
  82. package/dist/types/graphql/Errors.d.ts +15 -0
  83. package/dist/types/graphql/Errors.js +23 -0
  84. package/dist/types/graphql/Errors.js.map +1 -0
  85. package/dist/types/raw/auth/Cookie.d.ts +16 -0
  86. package/dist/types/raw/auth/Cookie.js +3 -0
  87. package/dist/types/raw/auth/Cookie.js.map +1 -0
  88. package/dist/types/raw/tweet/Favouriters.d.ts +164 -0
  89. package/dist/types/raw/tweet/Favouriters.js +3 -0
  90. package/dist/types/raw/tweet/Favouriters.js.map +1 -0
  91. package/dist/types/raw/tweet/Retweeters.d.ts +171 -0
  92. package/dist/types/raw/tweet/Retweeters.js +3 -0
  93. package/dist/types/raw/tweet/Retweeters.js.map +1 -0
  94. package/dist/types/raw/tweet/Tweet.d.ts +746 -0
  95. package/dist/types/raw/tweet/Tweet.js +3 -0
  96. package/dist/types/raw/tweet/Tweet.js.map +1 -0
  97. package/dist/types/raw/tweet/Tweets.d.ts +386 -0
  98. package/dist/types/raw/tweet/Tweets.js +3 -0
  99. package/dist/types/raw/tweet/Tweets.js.map +1 -0
  100. package/dist/types/raw/user/Followers.d.ts +176 -0
  101. package/dist/types/raw/user/Followers.js +3 -0
  102. package/dist/types/raw/user/Followers.js.map +1 -0
  103. package/dist/types/raw/user/Following.d.ts +176 -0
  104. package/dist/types/raw/user/Following.js +3 -0
  105. package/dist/types/raw/user/Following.js.map +1 -0
  106. package/dist/types/raw/user/Likes.d.ts +1059 -0
  107. package/dist/types/raw/user/Likes.js +3 -0
  108. package/dist/types/raw/user/Likes.js.map +1 -0
  109. package/dist/types/raw/user/Tweets.d.ts +2428 -0
  110. package/dist/types/raw/user/Tweets.js +3 -0
  111. package/dist/types/raw/user/Tweets.js.map +1 -0
  112. package/dist/types/raw/user/User.d.ts +117 -0
  113. package/dist/types/raw/user/User.js +3 -0
  114. package/dist/types/raw/user/User.js.map +1 -0
  115. package/dist/types/raw/user/Users.d.ts +120 -0
  116. package/dist/types/raw/user/Users.js +3 -0
  117. package/dist/types/raw/user/Users.js.map +1 -0
  118. package/environment.d.ts +11 -0
  119. package/package.json +40 -0
  120. package/src/config/env.ts +5 -0
  121. package/src/index.ts +19 -0
  122. package/src/models/graphql/Global.ts +10 -0
  123. package/src/models/graphql/TweetTypes.ts +154 -0
  124. package/src/models/graphql/UserTypes.ts +137 -0
  125. package/src/queries/RootQuery.ts +56 -0
  126. package/src/resolvers/ResolverBase.ts +12 -0
  127. package/src/resolvers/TweetResolver.ts +257 -0
  128. package/src/resolvers/UserResolver.ts +239 -0
  129. package/src/server.ts +36 -0
  130. package/src/services/AuthService.ts +58 -0
  131. package/src/services/CacheService.ts +70 -0
  132. package/src/services/FetcherService.ts +84 -0
  133. package/src/services/data/TweetService.ts +163 -0
  134. package/src/services/data/UserAccountService.ts +187 -0
  135. package/src/services/helper/Deserializers.ts +95 -0
  136. package/src/services/helper/Extractors.ts +455 -0
  137. package/src/services/helper/Headers.ts +45 -0
  138. package/src/services/helper/Parser.ts +108 -0
  139. package/src/services/helper/Urls.ts +109 -0
  140. package/src/types/Authentication.ts +16 -0
  141. package/src/types/HTTP.ts +23 -0
  142. package/src/types/Service.ts +36 -0
  143. package/src/types/Tweet.ts +44 -0
  144. package/src/types/UserAccount.ts +21 -0
  145. package/src/types/graphql/Errors.ts +16 -0
  146. package/src/types/raw/auth/Cookie.ts +16 -0
  147. package/src/types/raw/tweet/Favouriters.ts +193 -0
  148. package/src/types/raw/tweet/Retweeters.ts +201 -0
  149. package/src/types/raw/tweet/Tweet.ts +882 -0
  150. package/src/types/raw/tweet/Tweets.ts +444 -0
  151. package/src/types/raw/user/Followers.ts +208 -0
  152. package/src/types/raw/user/Following.ts +208 -0
  153. package/src/types/raw/user/Likes.ts +1247 -0
  154. package/src/types/raw/user/Tweets.ts +2847 -0
  155. package/src/types/raw/user/User.ts +135 -0
  156. package/tsconfig.json +95 -0
@@ -0,0 +1,56 @@
1
+ // PACKAGE
2
+ import { GraphQLBoolean, GraphQLInt, GraphQLList, GraphQLObjectType, GraphQLString } from 'graphql'
3
+
4
+ // TYPES
5
+ import { User } from '../models/graphql/UserTypes';
6
+ import { Tweet, TweetList } from '../models/graphql/TweetTypes';
7
+ import { TweetFilter } from '../types/Tweet';
8
+
9
+ // RESOLVERS
10
+ import UserResolver from '../resolvers/UserResolver';
11
+ import TweetResolver from '../resolvers/TweetResolver';
12
+
13
+ export const rootQuery = new GraphQLObjectType({
14
+ name: 'Root',
15
+ fields: {
16
+ test: {
17
+ type: GraphQLString,
18
+ resolve: () => "GraphQL Works!"
19
+ },
20
+ User: {
21
+ type: User,
22
+ description: "Returns the details of the twitter user with given user name",
23
+ args: {
24
+ userName: { type: GraphQLString },
25
+ id: { type: GraphQLString }
26
+ },
27
+ resolve: (parent, args, context) => new UserResolver(context).resolveUserDetails(args.userName, args.id)
28
+ },
29
+ Tweet: {
30
+ type: Tweet,
31
+ description: "Returns a single tweet given it's id",
32
+ args: {
33
+ id: { type: GraphQLString }
34
+ },
35
+ resolve: (parent, args, context) => new TweetResolver(context).resolveTweet(args.id)
36
+ },
37
+ Tweets: {
38
+ type: TweetList,
39
+ description: "Returns the list of tweets matching the given criteria",
40
+ args: {
41
+ fromUsers: { type: new GraphQLList(GraphQLString) },
42
+ toUsers: { type: new GraphQLList(GraphQLString) },
43
+ mentions: { type: new GraphQLList(GraphQLString) },
44
+ hashtags: { type: new GraphQLList(GraphQLString) },
45
+ words: { type: new GraphQLList(GraphQLString) },
46
+ startDate: { type: GraphQLString },
47
+ endDate: { type: GraphQLString },
48
+ quoted: { type: GraphQLString },
49
+ links: { type: GraphQLBoolean, defaultValue: false },
50
+ count: { type: GraphQLInt, defaultValue: 20 },
51
+ cursor: { type: GraphQLString, defaultValue: '' }
52
+ },
53
+ resolve: (parent, args, context) => new TweetResolver(context).resolveTweets(args as TweetFilter, args.count, args.cursor)
54
+ }
55
+ }
56
+ })
@@ -0,0 +1,12 @@
1
+ // TYPES
2
+ import { DataContext } from '../types/Service';
3
+
4
+ export default class ResolverBase {
5
+ // MEMBER DATA
6
+ protected context: DataContext; // To store the data context
7
+
8
+ // MEMBER METHODS
9
+ constructor(context: DataContext) {
10
+ this.context = context;
11
+ }
12
+ }
@@ -0,0 +1,257 @@
1
+ // RESOLVERS
2
+ import ResolverBase from './ResolverBase';
3
+
4
+ // TYPES
5
+ import { TweetFilter } from '../types/Tweet';
6
+ import { Cursor, DataContext } from '../types/Service';
7
+
8
+ // HELPERS
9
+ import { ValidationErrors } from '../types/graphql/Errors';
10
+
11
+ export default class TweetResolver extends ResolverBase {
12
+ // MEMBER METHODS
13
+ constructor(context: DataContext) {
14
+ super(context);
15
+ }
16
+
17
+ /**
18
+ * @returns The details of the tweet with the given id
19
+ * @param id The id of the tweet which is to be fetched
20
+ */
21
+ async resolveTweet(id: string): Promise<any> {
22
+ // Getting the data
23
+ let res = await this.context.tweets.getTweetById(id);
24
+
25
+ // Evaluating response
26
+ return res;
27
+ }
28
+
29
+ /**
30
+ * @returns The list of tweets matching the given filter
31
+ * @param filter The filter to be used for fetching matching tweets
32
+ * @param count The number of tweets to fetch
33
+ * @param cursor The cursor to the batch of tweets to fetch
34
+ */
35
+ async resolveTweets(filter: TweetFilter, count: number, cursor: string): Promise<any[]> {
36
+ let tweets: any[] = []; // To store the list of tweets
37
+ let next: Cursor = new Cursor(cursor); // To store cursor to next batch
38
+ let total: number = 0; // To store the total number of tweets fetched
39
+ let batchSize: number = 20; // To store the batchsize to use
40
+
41
+ // Checking if the given tweet filter is valid or not
42
+ if (!(filter.fromUsers || filter.toUsers || filter.words || filter.hashtags || filter.mentions || filter.quoted)) {
43
+ throw new Error(ValidationErrors.InvalidTweetFilter);
44
+ }
45
+
46
+ // If required count less than batch size, setting batch size to required count
47
+ batchSize = (count < batchSize) ? count : batchSize;
48
+
49
+ // Repeatedly fetching data as long as total data fetched is less than requried
50
+ while (total < count) {
51
+ // If this is the last batch, change batch size to number of remaining tweets
52
+ batchSize = ((count - total) < batchSize) ? (count - total) : batchSize;
53
+
54
+ // Getting the data
55
+ const res = await this.context.tweets.getTweets(filter, count, next.value);
56
+
57
+ // If data is available
58
+ if (res.list.length) {
59
+ // Adding fetched tweets to list of tweets
60
+ tweets = tweets.concat(res.list);
61
+
62
+ // Updating total tweets fetched
63
+ total = tweets.length;
64
+
65
+ // Getting cursor to next batch
66
+ next = res.next;
67
+ }
68
+ // If no more data is available
69
+ else {
70
+ break;
71
+ }
72
+ }
73
+
74
+ // Adding the cursor to the end of list of data
75
+ tweets.push(next);
76
+
77
+ return tweets;
78
+ }
79
+
80
+ /**
81
+ * @returns The list of quotes of the given tweet
82
+ * @param id The id of the tweet whose quotes are to be fetched
83
+ * @param count The number of quotes to be fetched
84
+ * @param all Whether to fetch all quotes or not
85
+ * @param cursor The cursor to the batch of tweet quotes to fetch
86
+ * @param quoteCount The total number of quotes of the given tweet
87
+ */
88
+ async resolveTweetQuotes(id: string, count: number, all: boolean, cursor: string, quoteCount: number): Promise<any[]> {
89
+ let quotes: any[] = []; // To store the list of quotes
90
+
91
+ // If all tweets are to be fetched
92
+ count = (all || count > quoteCount) ? quoteCount : count;
93
+
94
+ // Preparing the filter to use
95
+ let filter = {
96
+ words: [],
97
+ hashtags: [],
98
+ fromUsers: [],
99
+ toUsers: [],
100
+ mentions: [],
101
+ startDate: '',
102
+ endDate: '',
103
+ quoted: id
104
+ };
105
+
106
+ // Fetching the quotes using resolveTweets method
107
+ quotes = await this.resolveTweets(filter, count, cursor);
108
+
109
+ return quotes;
110
+ }
111
+
112
+ /**
113
+ * @returns The list of likers of the given tweet
114
+ * @param id The id of the tweet whose likers are to be fetched
115
+ * @param count The total number of likers to fetch
116
+ * @param all Whether to fetch all the likers of the tweet
117
+ * @param cursor The cursor to the batch of likers to fetch
118
+ * @param likesCount The total number of like of the tweet
119
+ */
120
+ async resolveTweetLikers(id: string, count: number, all: boolean, cursor: string, likesCount: number): Promise<any[]> {
121
+ let likers: any[] = []; // To store the list of likers
122
+ let next: Cursor = new Cursor(cursor); // To store cursor to next batch
123
+ let total: number = 0; // To store the total number of likers fetched
124
+ let batchSize: number = 20; // To store the batchsize to use
125
+
126
+ // If all likers are to be fetched
127
+ count = (all || count > likesCount) ? likesCount : count;
128
+
129
+ // If required count less than batch size, setting batch size to required count
130
+ batchSize = (count < batchSize) ? count : batchSize;
131
+
132
+ // Repeatedly fetching data as long as total data fetched is less than requried
133
+ while (total < count) {
134
+ // If this is the last batch, change batch size to number of remaining likers
135
+ batchSize = ((count - total) < batchSize) ? (count - total) : batchSize;
136
+
137
+ // Getting the data
138
+ const res = await this.context.tweets.getTweetLikers(id, count, next.value);
139
+
140
+ // If data is available
141
+ if (res.list.length) {
142
+ // Adding fetched likers to list of likers
143
+ likers = likers.concat(res.list);
144
+
145
+ // Updating total likers fetched
146
+ total = likers.length;
147
+
148
+ // Getting cursor to next batch
149
+ next = res.next;
150
+ }
151
+ // If no more data is available
152
+ else {
153
+ break;
154
+ }
155
+ }
156
+
157
+ // Adding the cursor to the end of list of data
158
+ likers.push(next);
159
+
160
+ return likers;
161
+ }
162
+
163
+ /**
164
+ * @returns The list of retweeters of the given tweet
165
+ * @param id The id of the tweet whose retweeters are to be fetched
166
+ * @param count The total number of retweeters to fetch
167
+ * @param all Whether to fetch all retweeters
168
+ * @param cursor The cursor to the batch of retweeters to fetch
169
+ * @param retweetsCount The total number of retweets of the
170
+ */
171
+ async resolveTweetRetweeters(id: string, count: number, all: boolean, cursor: string, retweetsCount: number): Promise<any[]> {
172
+ let retweeters: any[] = []; // To store the list of retweeters
173
+ let next: Cursor = new Cursor(cursor); // To store cursor to next batch
174
+ let total: number = 0; // To store the total number of retweeters fetched
175
+ let batchSize: number = 20; // To store the batchsize to use
176
+
177
+ // If all retweeters are to be fetched
178
+ count = (all || count > retweetsCount) ? retweetsCount : count;
179
+
180
+ // If required count less than batch size, setting batch size to required count
181
+ batchSize = (count < batchSize) ? count : batchSize;
182
+
183
+ // Repeatedly fetching data as long as total data fetched is less than requried
184
+ while (total < count) {
185
+ // If this is the last batch, change batch size to number of remaining retweeters
186
+ batchSize = ((count - total) < batchSize) ? (count - total) : batchSize;
187
+
188
+ // Getting the data
189
+ const res = await this.context.tweets.getTweetRetweeters(id, count, next.value);
190
+
191
+ // If data is available
192
+ if (res.list.length) {
193
+ // Adding fetched retweeters to list of retweeters
194
+ retweeters = retweeters.concat(res.list);
195
+
196
+ // Updating total retweeters fetched
197
+ total = retweeters.length;
198
+
199
+ // Getting cursor to next batch
200
+ next = res.next;
201
+ }
202
+ // If no more data is available
203
+ else {
204
+ break;
205
+ }
206
+ }
207
+
208
+ // Adding the cursor to the end of list of data
209
+ retweeters.push(next);
210
+
211
+ return retweeters;
212
+ }
213
+
214
+ /**
215
+ * @returns The list of replies of the given tweet
216
+ * @param id The id of the tweet whose replies are to be fetched
217
+ * @param count The total number of replies to fetch
218
+ * @param all Whether to fetch list of all replies
219
+ * @param cursor The cursor to the batch of replies to fetch
220
+ * @param repliesCount The total number of replies to the target tweet
221
+ */
222
+ async resolveTweetReplies(id: string, count: number, all: boolean, cursor: string, repliesCount: number): Promise<any[]> {
223
+ let replies: any[] = []; // To store the list of replies
224
+ let next: Cursor = new Cursor(cursor); // To store cursor to next batch
225
+ let total: number = 0; // To store the total number of replies fetched
226
+
227
+ // If all replies are to be fetched
228
+ count = (all || count > repliesCount) ? repliesCount : count;
229
+
230
+ // Repeatedly fetching data as long as total data fetched is less than requried
231
+ while (total < count) {
232
+ // Getting the data
233
+ const res = await this.context.tweets.getTweetReplies(id, next.value);
234
+
235
+ // If data is available
236
+ if (res.list.length) {
237
+ // Adding fetched replies to list of replies
238
+ replies = replies.concat(res.list);
239
+
240
+ // Updating total replies fetched
241
+ total = replies.length;
242
+
243
+ // Getting cursor to next batch
244
+ next = res.next;
245
+ }
246
+ // If no more data is available
247
+ else {
248
+ break;
249
+ }
250
+ }
251
+
252
+ // Adding the cursor to the end of list of data
253
+ replies.push(next);
254
+
255
+ return replies;
256
+ }
257
+ }
@@ -0,0 +1,239 @@
1
+ // RESOLVERS
2
+ import ResolverBase from './ResolverBase';
3
+
4
+ // TYPES
5
+ import { Cursor, DataContext } from '../types/Service';
6
+
7
+ // HELPERS
8
+ import { ValidationErrors } from '../types/graphql/Errors';
9
+
10
+ export default class UserResolver extends ResolverBase {
11
+ // MEMBER METHODS
12
+ constructor(context: DataContext) {
13
+ super(context);
14
+ }
15
+
16
+ /**
17
+ * @returns The details of the target twitter user
18
+ * @param userName The user name of the target twitter user
19
+ * @param id The id of the target twitter user
20
+ */
21
+ async resolveUserDetails(userName: string, id: string): Promise<any> {
22
+ // If user name is supplied
23
+ if (userName) {
24
+ return await this.context.users.getUserAccountDetails(userName);
25
+ }
26
+ // If id is supplied
27
+ else if (id) {
28
+ return await this.context.users.getUserAccountDetailsById(id);
29
+ }
30
+ // If neither userName nor id is supplied
31
+ else {
32
+ throw new Error(ValidationErrors.NoUserIdentification);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * @returns The list of tweets liked by the given user
38
+ * @param id The id of the user whose likes are to be fetched
39
+ * @param count The number of likes to fetch
40
+ * @param all Whether to fetch list of all tweets liked by user
41
+ * @param cursor The cursor to the batch of likes to fetch
42
+ * @param favouritesCount The total number of tweets liked by target user
43
+ */
44
+ async resolveUserLikes(id: string, count: number, all: boolean, cursor: string, favouritesCount: number): Promise<any> {
45
+ let likes: any[] = []; // To store the list of liked tweets
46
+ let next: Cursor = new Cursor(cursor); // To store cursor to next batch
47
+ let total: number = 0; // To store the total number of liked twets fetched
48
+ let batchSize: number = 20; // To store the batchsize to use
49
+
50
+ // If all liked tweets are to be fetched
51
+ count = all ? favouritesCount : count;
52
+
53
+ // If required count less than batch size, setting batch size to required count
54
+ batchSize = (count < batchSize) ? count : batchSize;
55
+
56
+ // Repeatedly fetching data as long as total data fetched is less than requried
57
+ while (total < count) {
58
+ // If this is the last batch, change batch size to number of remaining tweets
59
+ batchSize = ((count - total) < batchSize) ? (count - total) : batchSize;
60
+
61
+ // Getting the data
62
+ const res = await this.context.users.getUserLikes(id, count, next.value);
63
+
64
+ // If data is available
65
+ if (res.list.length) {
66
+ // Adding fetched tweets to list of tweets
67
+ likes = likes.concat(res.list);
68
+
69
+ // Updating total tweets fetched
70
+ total = likes.length;
71
+
72
+ // Getting cursor to next batch
73
+ next = res.next;
74
+ }
75
+ // If no more data is available
76
+ else {
77
+ break;
78
+ }
79
+ }
80
+
81
+ // Adding the cursor to the end of list of data
82
+ likes.push(next);
83
+
84
+ return likes;
85
+ }
86
+
87
+ /**
88
+ * @returns The list of followers of the given twiiter user
89
+ * @param id The id of the user whose followers are to be fetched
90
+ * @param count The number of followers to fetch
91
+ * @param all Whether to fetch all followers list
92
+ * @param cursor The cursor to the batch of followers to fetch
93
+ * @param followerCount The total number of followers of the target user
94
+ */
95
+ async resolveUserFollowers(id: string, count: number, all: boolean, cursor: string, followersCount: number): Promise<any> {
96
+ let followers: any[] = []; // To store the list of followers
97
+ let next: Cursor = new Cursor(cursor); // To store cursor to next batch
98
+ let total: number = 0; // To store the total number of followers fetched
99
+ let batchSize: number = 20; // To store the batchsize to use
100
+
101
+ // If all followers are to be fetched
102
+ count = (all || count > followersCount) ? followersCount : count;
103
+
104
+ // If required count less than batch size, setting batch size to required count
105
+ batchSize = (count < batchSize) ? count : batchSize;
106
+
107
+ // Repeatedly fetching data as long as total data fetched is less than requried
108
+ while (total < count) {
109
+ // If this is the last batch, change batch size to number of remaining followers
110
+ batchSize = ((count - total) < batchSize) ? (count - total) : batchSize;
111
+
112
+ // Getting the data
113
+ const res = await this.context.users.getUserFollowers(id, count, next.value);
114
+
115
+ // If data is available
116
+ if (res.list.length) {
117
+ // Adding fetched followers to list of followers
118
+ followers = followers.concat(res.list);
119
+
120
+ // Updating total followers fetched
121
+ total = followers.length;
122
+
123
+ // Getting cursor to next batch
124
+ next = res.next;
125
+ }
126
+ // If no more data is available
127
+ else {
128
+ break;
129
+ }
130
+ }
131
+
132
+ // Adding the cursor to the end of list of data
133
+ followers.push(next);
134
+
135
+ return followers;
136
+ }
137
+
138
+ /**
139
+ * @returns The list of following of the given twiiter user
140
+ * @param id The id of the user whose followings are to be fetched
141
+ * @param count The number of following to fetch
142
+ * @param all Whether to fetch list of all followings
143
+ * @param cursor The cursor to the batch of followings to fetch
144
+ * @param followingsCount The total number of followings of the target user
145
+ */
146
+ async resolveUserFollowing(id: string, count: number, all: boolean, cursor: string, followingsCount: number): Promise<any> {
147
+ let following: any[] = []; // To store the list of following
148
+ let next: Cursor = new Cursor(cursor); // To store cursor to next batch
149
+ let total: number = 0; // To store the total number of following fetched
150
+ let batchSize: number = 20; // To store the batchsize to use
151
+
152
+ // If all followings are to be fetched
153
+ count = (all || count > followingsCount) ? followingsCount : count;
154
+
155
+ // If required count less than batch size, setting batch size to required count
156
+ batchSize = (count < batchSize) ? count : batchSize;
157
+
158
+ // Repeatedly fetching data as long as total data fetched is less than requried
159
+ while (total < count) {
160
+ // If this is the last batch, change batch size to number of remaining following
161
+ batchSize = ((count - total) < batchSize) ? (count - total) : batchSize;
162
+
163
+ // Getting the data
164
+ const res = await this.context.users.getUserFollowing(id, count, next.value);
165
+
166
+ // If data is available
167
+ if (res.list.length) {
168
+ // Adding fetched following to list of following
169
+ following = following.concat(res.list);
170
+
171
+ // Updating total following fetched
172
+ total = following.length;
173
+
174
+ // Getting cursor to next batch
175
+ next = res.next;
176
+ }
177
+ // If no more data is available
178
+ else {
179
+ break;
180
+ }
181
+ }
182
+
183
+ // Adding the cursor to the end of list of data
184
+ following.push(next);
185
+
186
+ return following;
187
+ }
188
+
189
+ /**
190
+ * @returns The list of tweets made by the given user
191
+ * @param id The id of the user whose tweets are to be fetched
192
+ * @param count The number of tweets to fetch
193
+ * @param all Whether to fetch list of all tweets made by user
194
+ * @param cursor The cursor to the batch of tweets to fetch
195
+ * @param statusesCount The total number of tweets made by target user
196
+ */
197
+ async resolveUserTweets(id: string, count: number, all: boolean, cursor: string, statusesCount: number): Promise<any> {
198
+ let tweets: any[] = []; // To store the list of tweets
199
+ let next: Cursor = new Cursor(cursor); // To store cursor to next batch
200
+ let total: number = 0; // To store the total number of tweets fetched
201
+ let batchSize: number = 20; // To store the batchsize to use
202
+
203
+ // If all tweets are to be fetched
204
+ count = all ? statusesCount : count;
205
+
206
+ // If required count less than batch size, setting batch size to required count
207
+ batchSize = (count < batchSize) ? count : batchSize;
208
+
209
+ // Repeatedly fetching data as long as total data fetched is less than requried
210
+ while (total < count) {
211
+ // If this is the last batch, change batch size to number of remaining tweets
212
+ batchSize = ((count - total) < batchSize) ? (count - total) : batchSize;
213
+
214
+ // Getting the data
215
+ const res = await this.context.users.getUserTweets(id, count, next.value);
216
+
217
+ // If data is available
218
+ if (res.list.length) {
219
+ // Adding fetched tweets to list of tweets
220
+ tweets = tweets.concat(res.list);
221
+
222
+ // Updating total tweets fetched
223
+ total = tweets.length;
224
+
225
+ // Getting cursor to next batch
226
+ next = res.next;
227
+ }
228
+ // If no more data is available
229
+ else {
230
+ break;
231
+ }
232
+ }
233
+
234
+ // Adding the cursor to the end of list of data
235
+ tweets.push(next);
236
+
237
+ return tweets;
238
+ }
239
+ }
package/src/server.ts ADDED
@@ -0,0 +1,36 @@
1
+ // PACKAGE
2
+ import express from 'express';
3
+ import { graphqlHTTP } from 'express-graphql';
4
+ import { GraphQLSchema } from 'graphql';
5
+
6
+ // Services
7
+ import { UserAccountService } from './services/data/UserAccountService';
8
+ import { TweetService } from './services/data/TweetService';
9
+ import { AuthService } from './services/AuthService';
10
+
11
+ // SCHEMA
12
+ import { rootQuery } from './queries/RootQuery';
13
+
14
+ // CONFIGS
15
+ import { config } from './config/env';
16
+
17
+ // Initialising express instance
18
+ const app = express();
19
+
20
+ // Setting up graphql endpoint
21
+ app.use('/graphql', graphqlHTTP(req => ({
22
+ schema: new GraphQLSchema({
23
+ query: rootQuery
24
+ }),
25
+ context: {
26
+ users: new UserAccountService(new AuthService(req.headers.cookie as string)),
27
+ tweets: new TweetService(new AuthService(req.headers.cookie as string))
28
+ },
29
+ // If app is running in development environment, enable graphiql
30
+ graphiql: config.is_development
31
+ })));
32
+
33
+ // Setting up express server
34
+ app.listen(config.port, async () => {
35
+ console.log(`Listening on port ${config.port}`);
36
+ });
@@ -0,0 +1,58 @@
1
+ // PACKAGE
2
+ import axios from 'axios';
3
+
4
+ // URLS
5
+ import { guestTokenUrl } from './helper/Urls';
6
+
7
+ // TYPES
8
+ import { GuestCredentials, AuthCredentials } from '../types/Authentication';
9
+
10
+ // CONFIGS
11
+ import { config } from '../config/env';
12
+
13
+ /**
14
+ * @summary Handles authentication of http requests and other authentication related tasks
15
+ */
16
+ export class AuthService {
17
+ // MEMBER DATA
18
+ private authToken: string; // To store the common auth token
19
+ private credentials: AuthCredentials; // To store the current authentication credentials
20
+
21
+ // MEMBER METHODS
22
+ constructor(cookie: string = '') {
23
+ // Reading the auth token from the config, since it's always the same
24
+ this.authToken = config.twitter_auth_token;
25
+
26
+ // Setting up the authenticated credentials
27
+ /**
28
+ * The following regex pattern is used to extract the csrfToken from the cookie string.
29
+ * This is done by matching any string between the characters 'ct0=' and nearest enclosing ';'.
30
+ * (?<=pattern) starts matching after the given pattern.
31
+ * (?=pattern) stops matching just before the pattern
32
+ */
33
+ this.credentials = { authToken: this.authToken, csrfToken: cookie.match(/(?<=ct0=).+?(?=;)/) + '', cookie: cookie};
34
+
35
+ }
36
+
37
+ /**
38
+ * @returns The current authentication credentials. A different credential is returned each time this is invoked
39
+ */
40
+ async getAuthCredentials(): Promise<AuthCredentials> {
41
+ return this.credentials;
42
+ }
43
+
44
+ /**
45
+ * @returns The guest credentials fetched from twitter
46
+ */
47
+ async getGuestCredentials(): Promise<GuestCredentials> {
48
+ // Getting the guest credentials from twitter
49
+ return await axios.post<{ guest_token: string }>(guestTokenUrl(), null, {
50
+ headers: {
51
+ 'Authorization': this.authToken
52
+ }
53
+ }).then(res => ({
54
+ authToken: this.authToken,
55
+ guestToken: res.data.guest_token
56
+ }));
57
+ }
58
+ }