Problem Statement: GitHub portal is centered around organizations and repositories. Each organization has many repositories and each repository has many contributors. Your goal is to create an endpoint that given the name of the organization will return a list of contributors sorted by the number of contributions.
Few Other Requirements:
- respond to a GET request at port 8080 and address /org/{org_name}/contributors.
- Change default port in build.sbt PlayKeys.playDefaultPort := 8080
- handle GitHub’s API rate limit restriction using a token that can be set as an environment variable of name GH_TOKEN.
- Set GH_Token as env variable or pass token in header (with key - Authorization) (Token value currently used is GitHub Personal token - can be extended later)
- GitHub API returns paginated responses. You should take it into account if you want the result to be accurate for larger organizations.
- We make 1 extra call to fetch number of pages and then async-iterate through all pages from 1 to max page number.
- We also fetch the maximum records possible for a single api call (i.e page_size=100)
Project: The project is build on Play framework to utilize the efficiency in creating/consuming API's and allowing smooth plug and play access of Play supported libraries.
Libraries:
- scala-logging -> To utilize lazy logging
- ehcache-jcache -> For API caching
- play ws -> WebService Client
- salatestplus-play -> For Unit Testing
- mockito-scala -> For mock objects
You need to download and install sbt for this application to run.
Once you have sbt installed, type/run the following command in the terminal, this will start up Play in development mode:
sbt runPlay will start up on the HTTP port at http://localhost:8080/. You don't need to deploy or reload anything -- changing any source code while the server is running will automatically recompile and hot-reload the application on the next HTTP request.
Current Possible Routes
GET /
GET /rate/limit
GET /org/:orgName/contributors
Route definitions:
GET /
Index route -> Starts when application is run -> "Welcome to Github Rank Challenge"
GET /rate/limit
Utility API created only for testing purpose, to identify number of API requests still available in rate limit and also the next rate limit refresh date time.
Note: Test using 'Authorization <git_personal_token>' in header or GH_TOKEN as env_variable.
Response sample:
{
"core": {
"limit": 5000,
"used": 0,
"remaining": 5000,
"reset": 1653335771
},
"next reset date time": "2022-05-24 01:26:11"
}
GET /org/:orgName/contributors
Fetch Array of contributors of a specific organization, sorted based on number of contributions made to all repositories worked on.
Request : orgName in url
Header: Optional Authorization header.
Response sample:
example url: localhost:8080/org/io/contributors
[
{
"login": "jgilliam",
"contributions": 409
},
{
"login": "rbjarnason",
"contributions": 347
},
{
"login": "fannar",
"contributions": 83
},
{
"login": "aldavigdis",
"contributions": 58
},
{
"login": "simmix1",
"contributions": 8
}
]
Request => Routes => Controller => Cache => Service
- GithubService calls OrgServices to fetch list of all repos in an Organization.
- List of repos is passed to RepositoryService to make sequential async calls to all repos to fetch list of Contributors.
- List of contributors are than grouped by login and their contributions are added.
- The final list is then sorted based on Contribution value of each login.
- Final result is responded back to the called.
- Error are handled with appropriate error codes (disclaimer: Not all http status codes are managed at present)
- Integration tests.
- Configuration for different environments.
- Constant file to be added.
- At the present any error while fetching the api data returns an error response, this can be further extended to respond with a list of both contributions and list of errors for each individual url called.
Example: localhost:8080/org/zio/contributors
Response: 204 No Content. (This was found while API - testing) 1 of the repos in ZIO does not have any content. Hence, 204 response.
- We can ignore urls having errors, at the moment we don't.
I have added TODO in multiple parts of the program, listing possible extensions and enhancement that can be added.