Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
store.json
95 changes: 83 additions & 12 deletions api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
## Usage
Launch the API the following way:
```
go build -o main src/*.go
cd src
go build -o main .
./main
```
or
```
go run src/*.go
cd src
go run .
```

# Endpoints
Expand All @@ -26,18 +28,87 @@ Method: **POST**

**JSON** Body:
```json
{"guildId": "656234546654"}
{"guildId": 636145886279237652}
```

**Responses**:
# **Responses**:

Code | Response Header | JSON Response | Info
--- | --- | --- | ---
**200** | `200 - Ok` | `{"guildId":65654,"timestamp":16504}` | **guildId** successfully bumped!
**200** | `200 - Added` | `{"guildId":65654,"timestamp":16504}` | **guildId** added to database and successfully bumped!
**425** | `425 - TooEarly` | `{"guildId":65654,"timestamp":16504}` | **guildId** bump delta hasn't exceeded the bumping interval. Try again later.
**400** | `400 - Bad Request` | *None* | Bad request, make sure there are no strings in **guildId**
**500** | `500 - InternalServerError` | *None* | Internal Server Error, try again later!

### **200**
- *200 - Ok* | **guildId** successfully bumped!
```json
{
"code": 200,
"message": "Guild bumped",
"payload": {
"guildId": 636145886279237652,
"timestamp": 1601832221
}
}
```

- *200 - Added* | **guildId** successfully bumped!
```json
{
"code": 200,
"message": "Guild added and bumped",
"payload": {
"guildId": 636145886279237152,
"timestamp": 1601832254
}
}
```

### **400**
- *400 - BadRequest* | Request body contains invalid character, most likely a string or a non number
allowed characters: 0-9
```json
{
"code": 400,
"message": "Request body contains invalid character",
"payload": {
"guildId": 0,
"timestamp": 0
}
}
```
- *400 - BadRequest* | **guildId** needs to be 18 characters long
```json
{
"code": 400,
"message": "GuildId does not conform to 18 character long integer requirement",
"payload": {
"guildId": 636149237152,
"timestamp": 0
}
}
```

### **425**
- *425 - TooEarly* | **guildId** bump delta hasn't exceeded the bumping interval. Try again later
```json
{
"code": 425,
"message": "Guild bumped too early",
"payload": {
"guildId": 636145886279237152,
"timestamp": 1601832360
}
}
```

### **500**
- *500 - InternalServerError* | Unrecoverable internal server error. Try again later
```json
{
"code": 425,
"message": "",
"payload": {
"guildId": 0,
"timestamp": 0
}
}
```

*Additional note*:
The **JSON Response** is always a direct and latest representation of the stored guild in the database. That means when getting a `200` or `425` status code, the `timestamp` attribute represents the time that guild was last bumped in a UNIX timestamp.
The **payload** is always a direct and latest representation of the stored guild in the database. That means when getting a `200` or `425` status code, the `timestamp` attribute represents the time that guild was last bumped in a UNIX timestamp.
5 changes: 0 additions & 5 deletions api/go.mod

This file was deleted.

70 changes: 70 additions & 0 deletions api/src/bump.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"encoding/json"
"io/ioutil"
"log"
"net/http"
"strconv"
"strings"
"time"
)

func BumpGuild(w http.ResponseWriter, r *http.Request) {
var guild Guild

// Read request body
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Print(err)
WriteBumpResponse(w, http.StatusInternalServerError, "Unable to read request body", guild)
return
}

// Unmarshal body into guild object
err = json.Unmarshal(body, &guild)
if err != nil {
log.Print(err)
if strings.Contains(err.Error(), "invalid character") {
WriteBumpResponse(w, http.StatusBadRequest, "Request body contains invalid character", guild)
return
}
WriteBumpResponse(w, http.StatusBadRequest, "Unable to process request body", guild)
return
}

// check if GuildId's length is 18
if len(strconv.Itoa(guild.GuildId)) != 18 {
errString := "GuildId does not conform to 18 character long integer requirement"
log.Print(errString)
WriteBumpResponse(w, http.StatusBadRequest, errString, guild)
return
}

// Adding timestamp to guild object
ts := time.Now().Unix()
guild.Timestamp = ts

if !gs.GuildInStore(guild) {
// guild is not yet present in GuildStore
gs.AddToStore(guild)
WriteBumpResponse(w, http.StatusOK, "Guild added and bumped", guild)
} else {
// guild is present in GuildStore
if gs.PastInterval(guild) {
// guild.Timestamp has exceeded BUMP_INTERVAL, Timestamp has been updated (bumped)
WriteBumpResponse(w, http.StatusOK, "Guild bumped", guild)
} else {
// guild.Timestamp has not exceeded BUMP_INTERVAL, not updated
guild.Timestamp = gs.GetTimestamp(guild)
WriteBumpResponse(w, http.StatusTooEarly, "Guild bumped too early", guild)
}
}

err = gs.WriteStore()
if err != nil {
log.Panic(err)
WriteBumpResponse(w, http.StatusInternalServerError, "Could not store changes", guild)
return
}
}
5 changes: 5 additions & 0 deletions api/src/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module api

go 1.15

require github.com/gorilla/mux v1.8.0
File renamed without changes.
123 changes: 27 additions & 96 deletions api/src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,44 @@ import (
"encoding/json"
"fmt"
"github.com/gorilla/mux"
"io/ioutil"
"net/http"
"strconv"
"sync"
"time"

"log"
)

const (
PORT = ":8080"
BUMP_INTERVAL = 60 // 1 minute in seconds
PORT = ":8080"
BUMP_INTERVAL = 60 // 1 minute in seconds
STORE_FILE_NAME = "store.json"
)

var gs GuildStore

type BumpRaw struct {
GuildId string `json:"guildId"`
}

type Guild struct {
GuildId int `json:"guildId"`
Timestamp int64 `json:"timestamp"`
}

type GuildStore struct {
Guilds []Guild `json:"guilds"`
mutex sync.Mutex
mutex sync.Mutex
}

type BumpResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Payload Guild `json:"payload"`
}

func init() {
var err error
var err error
gs.Guilds, err = LoadStore()
if err != nil {
log.Print(err)
} else {
log.Print("'Database' successfully restored!")
log.Print(fmt.Sprintf("%v guilds in 'database'", len(gs.Guilds)))
log.Print(fmt.Sprintf("%v guilds in 'database'", len(gs.Guilds)))
}
}

Expand All @@ -53,97 +52,29 @@ func middleware(f http.HandlerFunc) http.HandlerFunc {
}
}


func BumpGuild(w http.ResponseWriter, r *http.Request) {
// Read request body
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Print(err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("500 - InternalServerError"))
return
}

// Unmarshal body into BumpRaw
var br BumpRaw
err = json.Unmarshal(body, &br)
if err != nil {
log.Print(err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("500 - InternalServerError"))
return
}

// Convert BumpRaw.Guild_id into int64 from string
guildId, err := strconv.Atoi(br.GuildId)
func HandleRequests() {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/V1/bump", middleware(BumpGuild)).Methods("POST")
log.Print(fmt.Sprintf("Now serving: localhost%s", PORT))
err := http.ListenAndServe(PORT, router)
if err != nil {
log.Print(err)
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("400 - BadRequest"))
return
}
}

// Constructing actual Bump object
ts := time.Now().Unix()
var guild = Guild{
GuildId: guildId,
Timestamp: ts,
// makes BumpResponse object and writes it to ResponseWriter
func WriteBumpResponse(w http.ResponseWriter, code int, message string, guild Guild) {
br := BumpResponse{
Code: code,
Message: message,
Payload: guild,
}

m, err := json.Marshal(guild)
if err != nil {
log.Print(err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("500 - InternalServerError"))
return
}

if !gs.GuildInStore(guild) {
// guild is not yet present in GuildStore
gs.AddToStore(guild)
w.WriteHeader(http.StatusOK)
w.Write(m)
} else {

if gs.PastInterval(guild) {
// guild.Timestamp has exceeded BUMP_INTERVAL, Timestamp has been updated (bumped)
w.WriteHeader(http.StatusOK)
w.Write(m)
} else {
// guild.Timestamp has not exceeded BUMP_INTERVAL, not updated
guild.Timestamp = gs.GetTimestamp(guild)

// update m with new timestamp
m, err := json.Marshal(guild)
if err != nil {
log.Print(err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("500 - InternalServerError"))
return
}

w.WriteHeader(http.StatusTooEarly)
w.Write(m)
}
}

err = gs.WriteStore()
if err != nil {
log.Panic(err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("500 - InternalServerError"))
return
}
}
payloadByte, _ := json.Marshal(br)

func HandleRequests() {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/V1/bump", middleware(BumpGuild)).Methods("POST")
log.Print(fmt.Sprintf("Now serving: localhost%s", PORT))
err := http.ListenAndServe(PORT, router)
if err != nil {
log.Print(err)
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
w.Write(payloadByte)
}

func main() {
Expand Down
Loading