diff --git a/package.json b/package.json index 90276bb..d8ff870 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "license": "MIT", "dependencies": { "@google-cloud/functions-framework": "^3.1.2", - "axios": "^0.27.2" + "axios": "^1.1.0" }, "scripts": { "start": "functions-framework --source=build/src/ --target=run --port=8088", @@ -21,12 +21,13 @@ "posttest": "npm run lint" }, "devDependencies": { - "@types/express": "^4.17.13", - "@types/node": "^14.11.2", + "@types/axios": "^0.14.0", + "@types/express": "^4.17.14", + "@types/node": "^18.11.0", "@types/node-fetch": "^2.6.2", "concurrently": "^7.2.2", - "gts": "^3.1.0", - "nodemon": "^2.0.18", - "typescript": "^4.0.3" + "gts": "^3.1.1", + "nodemon": "^2.0.20", + "typescript": "^4.8.4" } } diff --git a/src/api/getTweets.ts b/src/api/getTweets.ts new file mode 100644 index 0000000..b26b985 --- /dev/null +++ b/src/api/getTweets.ts @@ -0,0 +1,37 @@ +/** + * getTweets.ts + * + * /2/users/:id/tweets + * + * Returns a list of tweets while considering pagination + */ +import get from '../helpers/get'; + +interface ReturnI { + newNextToken?: string; + tweets: Array; +} + +type GetTweetsT = ( + myUserId: string, + perPage: number, + nextToken?: string +) => Promise; + +const getTweets: GetTweetsT = async (myUserId, perPage, nextToken) => { + let url = `https://api.twitter.com/2/users/${myUserId}/tweets?tweet.fields=entities&max_results=${perPage}`; + + // if there is another page, we'll have a token + if (nextToken?.length) { + url = `${url}&pagination_token=${nextToken}`; + } + + // collect tweets + const results = await get(url); + const tweets = results?.data?.data; + const newNextToken = results?.data?.meta?.next_token; + + return {newNextToken, tweets}; +}; + +export default getTweets; diff --git a/src/api/getUser.ts b/src/api/getUser.ts new file mode 100644 index 0000000..aa7727e --- /dev/null +++ b/src/api/getUser.ts @@ -0,0 +1,15 @@ +/** + * getUser.ts + * + * /2/users/by/username/:id + * + * Returns a particular user's info + */ +import get from '../helpers/get'; + +const getUser = async (username: string) => { + const url = `https://api.twitter.com/2/users/by/username/${username}`; + return get(url); +}; + +export default getUser; diff --git a/src/helpers/get.ts b/src/helpers/get.ts new file mode 100644 index 0000000..dd09b0f --- /dev/null +++ b/src/helpers/get.ts @@ -0,0 +1,16 @@ +/** + * get.ts + * + * GET endpoint using bearer token authorization + */ +import axios from 'axios'; + +const get = async (url: string) => { + return axios.get(url, { + headers: { + Authorization: `Bearer ${process.env.TWITTER_BEARER_TOKEN}`, + }, + }); +}; + +export default get; diff --git a/src/index.ts b/src/index.ts index 2e173ed..61405d1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,75 +1,44 @@ /** + * index.ts + * * This Google Cloud Function will connect to Twitter using * the provided Twitter Bearer token stored inside of Cloud * secrets and allow you to use the Twitter API! * - * Example #1 fetches the 20th Tweet ever recorded in history but @jack - * Example #2 lists all of your own Tweets using tail-call recursion + * This example lists all Tweets for the provider username using tail-call recursion. */ -import type {HttpFunction} from '@google-cloud/functions-framework/build/src/functions'; -import axios from 'axios'; - -const getTweets = async ( - myUserId: string, - perPage: number, - nextToken?: string -) => { - let url = `https://api.twitter.com/2/users/${myUserId}/tweets?tweet.fields=entities&max_results=${perPage}`; - if (nextToken?.length) { - url = `${url}&pagination_token=${nextToken}`; - } - const results = await axios.get(url, { - headers: { - Authorization: `Bearer ${process.env.TWITTER_BEARER_TOKEN}`, - }, - }); +import type {HttpFunction} from '@google-cloud/functions-framework/build/src/functions'; +import getUser from './api/getUser'; +import getTweets from './api/getTweets'; - // collect tweets - const tweets = results?.data?.data; - const newNextToken = results?.data?.meta?.next_token; - - return {newNextToken, tweets}; -}; +const twitterUsername = 'wesleylemahieu'; export const run: HttpFunction = async (req, res) => { - console.log('Connecting to Twitter API...'); - - // allow our local apps to interact with this gcf. (affects local only) res.set('Access-Control-Allow-Origin', '*'); - const {nextToken, rowsPerPage} = req.query; - - console.log({nextToken, rowsPerPage}); - try { - console.log('Fetching all my tweets...'); - const myUser = await axios.get( - 'https://api.twitter.com/2/users/by/username/WesleyLeMahieu', - { - headers: { - Authorization: `Bearer ${process.env.TWITTER_BEARER_TOKEN}`, - }, - } - ); + const rowsPerPage = parseInt((req.query.rowsPerPage as string) || '100', 10); + const nextToken = req.query.nextToken as string; - const myUserId = myUser.data?.data?.id; + const response = await getUser(twitterUsername); + const myUserId = response?.data?.data?.id; - if (myUserId) { - // const likes = await client.tweets.usersIdLikedTweets(myUser.data.id); - const {newNextToken, tweets} = await getTweets( - myUserId, - parseInt((rowsPerPage as string) || '100', 0), - (nextToken as string) || '' - ); - res.send({ - // likes: likes?.data, - nextToken: newNextToken, - tweets, - }); - } else { - res.send('No user found, check your Twitter secrets!'); - } - } catch (e) { - console.log(e); + if (!response?.data?.data?.id) { + throw { + status: 400, + message: 'No user found, check your Twitter secrets!', + }; } + + const {newNextToken, tweets} = await getTweets( + myUserId, + rowsPerPage, + nextToken + ); + + res.send({ + // likes: likes?.data, + nextToken: newNextToken, + tweets, + }); };