Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# This file lists all individuals having contributed content to the repository.
# For how it is generated, see `hack/generate-authors.sh`.

Lordwill1 <messagelordwill@gmail.com>
ejilistic <vitalisibeh85@gmail.com>
sadiq Abdulrasheed Bello <sadiqabello0@gmail.com>
Umar Ibrahim <@gmail.com>
79 changes: 73 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@

# GitHub Activity Fetcher

This is a simple C command-line tool to fetch and display a user's public GitHub activity.

## Prerequisites

- `libcurl`: For making HTTP requests.
- `jansson`: For parsing JSON.

On Debian/Ubuntu, you can install these using:
```bash
sudo apt-get update
sudo apt-get install -y libcurl4-openssl-dev libjansson-dev
```

## Building

A build script is provided:
```bash
chmod +x build.sh
./build.sh
```
This will compile `github_activity.c` and create an executable named `github_activity`.

## Usage

```bash
./github_activity <username>
=======
# 0x16. C - Simple Shell
**By Spencer Cheng, featuring Julien Barbier**
Project to be done in teams of 2 people (your team: Godswill Kalu, Vatalis Ibeh)
Expand Down Expand Up @@ -106,11 +136,48 @@ write (man 2 write)

## How to add Author file
`Bash script for generating the list of authors in git repo`

```
Example:
```bash
./github_activity octocat
```
#!/bin/sh

git shortlog -se \
| perl -spe 's/^\s+\d+\s+//' \
| sed -e '/^CommitSyncScript.*$/d' \
> AUTHORS
```
## Testing

First, ensure the program is compiled by running `./build.sh`.

### Automated Basic Test

A simple automated test script is provided to check basic functionality:
```bash
chmod +x test.sh
./test.sh
```
This script currently verifies the "No arguments" test case.

### Manual Test Cases

**Test Case 1: No arguments.**
* Command: `./github_activity`
* Expected Output: Error message "Usage: github_activity <username>" printed to stderr, and the program should exit with a non-zero status code (e.g., 1).

**Test Case 2: Valid username.**
* Command: `./github_activity octocat`
* Expected Output: A list of recent public events for 'octocat'. Each event should be displayed in the format: "Event Type: [Event Type], Repository: [Repository Name], Time: [Timestamp]".
* Example: `Fetching activity for user: octocat`
* `Event Type: PushEvent, Repository: octocat/Spoon-Knife, Time: 2023-10-27T12:34:56Z` (The exact events and timestamps will vary).

**Test Case 3: Invalid/Non-existent username.**
* Command: `./github_activity NonExistentUser12345xyzAbc`
* Expected Output: The program should indicate that no events were found or the user is invalid.
* Example: `Fetching activity for user: NonExistentUser12345xyzAbc`
* Followed by either no event output, or an API error message like "JSON error on line 1: '{' expected near end of file" if the API returns an error document (e.g. `{"message":"Not Found", ...}`).

**Test Case 4: Network Error Simulation.**
* Instruction: Disconnect your machine from the internet (e.g., turn off Wi-Fi or unplug the Ethernet cable).
* Command: `./github_activity octocat`
* Expected Output: A cURL error message should be printed to stderr.
* Example: `Fetching activity for user: octocat`
* `curl_easy_perform() failed: Couldn't resolve host name` (or similar network-related cURL error).
(Remember to reconnect to the internet after this test.)
8 changes: 8 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash

# This script compiles github_activity.c and links it with libcurl and jansson.
# -o github_activity: Specifies the output executable name.
# github_activity.c: The source file to compile.
# -lcurl: Links with the libcurl library.
# -ljansson: Links with the libjansson library.
gcc -o github_activity github_activity.c -lcurl -ljansson
130 changes: 130 additions & 0 deletions github_activity.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <jansson.h>

struct MemoryStruct {
char *memory;
size_t size;
};

// Callback function to handle writing received data
static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) {
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;

char *ptr = realloc(mem->memory, mem->size + realsize + 1);
if(ptr == NULL) {
/* out of memory! */
fprintf(stderr, "not enough memory (realloc returned NULL)\n");
return 0;
}

mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;

return realsize;
}

void fetch_user_activity(const char *username) {
CURL *curl;
CURLcode res;
char *url = NULL;
const char *base_url = "https://api.github.com/users/";
const char *events_suffix = "/events/public";

struct MemoryStruct chunk;
chunk.memory = malloc(1); /* will be grown as needed by the realloc above */
if(chunk.memory == NULL) {
fprintf(stderr, "Failed to allocate memory for chunk\n");
return;
}
chunk.size = 0; /* no data at this point */
chunk.memory[0] = '\0';

// Calculate the length of the URL
size_t url_len = strlen(base_url) + strlen(username) + strlen(events_suffix) + 1;
url = malloc(url_len);
if (url == NULL) {
fprintf(stderr, "Failed to allocate memory for URL\n");
free(chunk.memory);
return;
}
// Construct the URL
snprintf(url, url_len, "%s%s%s", base_url, username, events_suffix);

curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "github-activity-c-tool/1.0");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);

res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
} else {
json_t *root;
json_error_t error;
root = json_loads(chunk.memory, 0, &error);

if (!root) {
fprintf(stderr, "JSON error on line %d: %s\n", error.line, error.text);
} else {
if (!json_is_array(root)) {
fprintf(stderr, "Error: root is not a JSON array.\n");
} else {
for (size_t i = 0; i < json_array_size(root); i++) {
json_t *event = json_array_get(root, i);
if (!event || !json_is_object(event)) {
fprintf(stderr, "Error: event is not a JSON object.\n");
continue;
}

const char *type_str = "N/A";
json_t *type_json = json_object_get(event, "type");
if (type_json && json_is_string(type_json)) {
type_str = json_string_value(type_json);
}

const char *repo_name_str = "N/A";
json_t *repo_obj = json_object_get(event, "repo");
if (repo_obj && json_is_object(repo_obj)) {
json_t *repo_name_json = json_object_get(repo_obj, "name");
if (repo_name_json && json_is_string(repo_name_json)) {
repo_name_str = json_string_value(repo_name_json);
}
}

const char *created_at_str = "N/A";
json_t *created_at_json = json_object_get(event, "created_at");
if (created_at_json && json_is_string(created_at_json)) {
created_at_str = json_string_value(created_at_json);
}
printf("Event Type: %s, Repository: %s, Time: %s\n", type_str, repo_name_str, created_at_str);
}
}
json_decref(root);
}
}
curl_easy_cleanup(curl);
} else {
fprintf(stderr, "Failed to initialize CURL\n");
}
free(url);
free(chunk.memory);
}

int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: github_activity <username>\n");
return 1;
}
char *username = argv[1];
printf("Fetching activity for user: %s\n", username);
fetch_user_activity(username);
return 0;
}
23 changes: 23 additions & 0 deletions test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash
echo "Running tests..."

# Ensure the program is built
./build.sh
if [ ! -f ./github_activity ]; then
echo "Build failed, cannot run tests."
exit 1
fi

echo "Test Case 1: No arguments"
OUTPUT_NO_ARGS=$(./github_activity 2>&1) # Capture both stdout and stderr
EXIT_CODE_NO_ARGS=$?

# Check exit code (should be non-zero, e.g. 1 as implemented)
# Check stderr for usage message (as implemented)
if [ $EXIT_CODE_NO_ARGS -ne 0 ] && echo "$OUTPUT_NO_ARGS" | grep -q "Usage: github_activity <username>"; then
echo " PASSED: Correct exit code and usage message."
else
echo " FAILED: Expected non-zero exit code and usage message. Got exit code $EXIT_CODE_NO_ARGS and output: $OUTPUT_NO_ARGS"
fi

echo "Basic tests complete. Please perform manual tests as described in README.md."