/TwitterAPIChallenge

This program creates a java servlet with embedded tomcat. A request to this servlet will retrieve a public user's or multiple users' twitter feed.

Primary LanguageJava

The program takes in a HTTP GET request and returns the latest statuses from the users in the 
request in order from most recent statuses to least recent.

I allow the parameters for the GET request to be case insensitive. I also assume that if a user 
issues a request gets is returned a next_cursor, and then issues a new request 
(with new params without the cursor param set), we are starting a new series of requests. 
See comments and design discussion below for full design limitations and assumptions.

To run:
I assume that the user running the code has java installed. If not, please install java 1.8.
To run the program, please go into the target folder and change the extension of 
vinechallenge-app-0.0.1-SNAPSHOT-jar-with-dependencies from .jar.x to .jar.
Then, return to the vine-api-servlet folder and execute run.sh. 

Once run.sh has been ran, go to a browser and you can start entering requests. Requests will look 
like: localhost:8080/statuses?screen_names=user&count=3

To stop the application, hit Ctrl C.

Summary: 
For this challenge, I used Java and Apache Tomcat to create a Java Servlet to 
handle HTTP Get requests. I used Maven to embed tomcat into my java application.

In my design, I use the following libraries:
1. Jackson - For mapping java objects to JSON
2. Twitter4j - For accessing twitter API calls
3. Twitter-text - For their entity object class

Design and implementation:
I used the provided tokens for authentication.
My program consists of 4 packages.

1. src\main\java\com\vinechallenge\servlet\vine_api_servlet\ServletDriver.java 
This class creates an is our programs driver and creates an instance of tomcat.

2. src\main\java\com\vinechallenge\servlet\vine_api_servlet\VineChallengeServlet.java 
This class is the main program that takes in a HTTP Get request.
This code checks our request and ensures that our required params are in our request and that our
params are of the correct form. It also checks for invalid parameters. Finally, it contains a method
that creates a ObjectMapper for mapping a java object to JSON for our response.

3. src\main\java\vinechallenge\model\Response.java
 This class contains the response java object that we map to JSON. 

4. src\main\java\vinechallenge\statuses\StatusRetrieval.java - 
This class is responsible for retrieving the statuses and returning a 
response object to the caller. It creates a list of statuses from multiple users by using the 
twitter search API. 
Finally, this class also has a global hashmap which is used for handling the next_cursor option.

5. src\main\java\vinechallenge\utils\TweetParser.java
This class is based off of twitter text's implementation. However, Twitter text had a lot of extra 
stuff that I felt wasn't needed, and didn't have the exact formatting to meet this assignments 
specs. So I used the entity clas from Twitter Text and based my methods off of theirs, 
while making the necessary changes to meet the required specs.

How the program runs:
ServletDriver creates a new instance of tomcat and our VineChallengeServlet
 -> VineChallengeServlet gets a HTTP Request 
 -> calls StatusRetrieval.java to get the statuses back
 -> calls TweetParser to get the statuses into the correct html formatting for the return
 -> Creates a Response object to return.

Testing:
Due to the relatively small size of the project and methods, I did not utilize unit and 
integration testing. Had this project needed to scale up and be maintained, I would have used an 
integration and unit test suite to test my program.
Some cases I tested for manually:
- Submitting a request where count is less than the number of statuses returned and thus the
next_cursor had to be used
- Submitting a request using the cursor returned from the response
- Submitting a requst where the count was greater than the number of statuses returned
- Submitting a request to retrieve statuses from multiple users
- Submitting a request and getting a cursor back. Then removing screen names from request 
while using cursor.
- Submitting a request with missing parameters, unknown parameters or invalid values for the 
parameters.

Comments / Noteworthy Design decisions:
1. There were two designs I considered in doing this problem. The first was to use the Twitter 
Search API, which did not retrieve tweets older than a week. The second design was to call 
getUserTimeline for each user in the request and concatenate the statuses together and then sort 
them. In this way,I would not be limited to statuses from only the past week. Because the specs say 
we should return statuses as though we were looking at a twitter account that was following these 
users, and this is not limited to only statuses in the past week when you use Twitter.com, I went 
with the second design.

2. The second design uses more space and twitter API calls than the first design because the first 
design can just return a list of sorted tweets from all users you want in one call.
The second design however must call getUserTimeLine for each user and then sort the list. 
In the second design (the design I implemented), I use a global pageNo, list of statuses, and 
cursor. The global list of statuses holds all the statuses that have been returned. If count is 
less than the number of statuses in this list, the next call will first take from this list 
before making another call to retrieve more statuses. 

3. Everytime a request comes in with a null cursor, I assume this is the start of a new series of 
requests and I reset my global variables. When I send a null next_cursor, this indicates that we 
have reached the last batch of tweets.

Had I needed to support a user's ability to submit multiple requests and user the next_cursor for 
all of them, I would have done something similar to the following:
Created a global hashmap where the key is the cursor, the value is another hashmap where the key is 
the userId, and the value is the tweetId of the last tweet retrieved for that user. When a request
with a cursor comes in, we retrieve the last status of the users in that request from our hashmap 
and make another API call to retrieve statuses older than those statuses in our hashmap for each 
respective user. Every method that accesses this global hashmap will be locked so only one thread 
can access and change the hashmap at a time.

4. In order to implement the next_cursor, I utilized global variables. Because each Tomcat request 
is executed on its own thread, my methods that touch my global variables are synchronization safe. 

I also assume we have enough memory to hold all statuses retrieved in memory 
(In reality, if the user request has a huge number of screen names, we may run out of memory to hold
 all the statuses).

5. I limit the user to a count of 100. This is to try to prevent very large counts in the request 
that can lead to problems (holding too many statuses in memory at once, going past Twitter API's 
limit for a page for status retrieval, etc...). I do not limit the number of screen names a user can
enter for a request however. I assume a count of 0 is not supported.

6. For simplicity, I assume that when users add a cursor to their request, they don't change the 
screen_names in the request. If they did, there could be an issue where the user removes 
screen_name1 from their request but the global statuses list still contains statuses from 
screen_name1. These would appear in the response. In order to solve this problem, I would have 
kept an tweetId for the last tweet that was included in the last batch per user and make a request 
to get tweets after this tweetId in every subsequent request.

7. This design only remembers one cursor value, so the user can only enter the next_cursor from the 
latest response in their next request. This would not work if we wanted users to be able to page 
back and forth or if we had a previous cursor value. I would have done this differently if we wanted
 to allow the user to page forward and backward.