Added counting

Changed Count post URL
Archiving crashes now removes crashes that match the archive
This commit is contained in:
Caleb Gardner
2024-06-12 02:17:26 -05:00
parent df3fe83c5f
commit e3af23873f
6 changed files with 157 additions and 16 deletions
+17 -2
View File
@@ -118,11 +118,26 @@ If an error status code is returned then the body will be as follows.
### Count ### Count
API Key must have the `Count` permission. API Key must have the `count` permission.
Request: Request:
> POST: /count/{uuid} > POST: /count
```json
{
id: "uuid", // Should be an empty string on first request. If invalid or too old, a new UUID will be returned.
platform: "web"
}
```
Returns:
```json
{
id: "uuid"
}
```
### User Count ### User Count
+34 -1
View File
@@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"log" "log"
"net/http" "net/http"
"strings"
) )
type ArchivedCrash struct { type ArchivedCrash struct {
@@ -163,6 +164,38 @@ func (b *Backend) actualCrashArchive(w http.ResponseWriter, ap App, toArchive Ar
} }
err := crash.Archive(toArchive) err := crash.Archive(toArchive)
if err != nil { if err != nil {
log.Println() log.Println("error archive crash:", err)
return
}
first, _, _ := strings.Cut(toArchive.Stack, "\n")
crashes, err := crash.Find(map[string]any{"error": toArchive.Error, "firstLine": first})
if err == ErrNotFound {
return
} else if err != nil {
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
return
}
for _, c := range crashes {
ogLen := len(c.Individual)
for i := 0; i < len(c.Individual); i++ {
ind := c.Individual[i]
if ind.Stack == toArchive.Stack {
if toArchive.Platform == "all" || toArchive.Platform == ind.Platform {
c.Individual = append(c.Individual[:i], c.Individual[i+1:]...)
i--
}
}
}
if len(c.Individual) == 0 {
err = crash.Remove(c.ID)
if err != nil {
log.Println("error removing empty crash report:", err)
}
} else if len(c.Individual) < ogLen {
err = crash.PartUpdate(c.ID, map[string]any{"individual": c.Individual})
if err != nil {
log.Println("error updating individual crash reports:", err)
}
}
} }
} }
+6 -3
View File
@@ -45,7 +45,7 @@ func NewBackend(keyTable Table[ApiKey], apps ...App) (*Backend, error) {
} }
} }
if hasLog { if hasLog {
b.m.HandleFunc("POST /count/{uuid}", b.countLog) b.m.HandleFunc("POST /count", b.countLog)
b.m.HandleFunc("GET /count", b.getCount) b.m.HandleFunc("GET /count", b.getCount)
} }
if hasCrash { if hasCrash {
@@ -59,8 +59,7 @@ func NewBackend(keyTable Table[ApiKey], apps ...App) (*Backend, error) {
func (b *Backend) cleanupLoop() { func (b *Backend) cleanupLoop() {
for range time.Tick(24 * time.Hour) { for range time.Tick(24 * time.Hour) {
oldTim := time.Now().Add(-30 * 24 * time.Hour) old := getDate(time.Now().Add(-30 * 24 * time.Hour))
old := (oldTim.Year() * 10000) + (int(oldTim.Month()) * 100) + oldTim.Day()
for _, a := range b.apps { for _, a := range b.apps {
tab := a.CountTable() tab := a.CountTable()
if tab == nil { if tab == nil {
@@ -71,6 +70,10 @@ func (b *Backend) cleanupLoop() {
} }
} }
func getDate(t time.Time) int {
return (t.Year() * 10000) + (int(t.Month()) * 100) + t.Day()
}
func (b *Backend) EnableManagementKey(managementID string) { func (b *Backend) EnableManagementKey(managementID string) {
b.managementKeyID = managementID b.managementKeyID = managementID
b.m.HandleFunc("DELETE /{appID}/crash/{crashID}", b.managementDeleteCrash) b.m.HandleFunc("DELETE /{appID}/crash/{crashID}", b.managementDeleteCrash)
+16 -3
View File
@@ -1,10 +1,23 @@
package darkstorm package darkstorm
import ( import (
"os" "fmt"
"testing" "testing"
"time"
"github.com/google/uuid"
) )
func TestMain(t *testing.M) { func TestStuff(t *testing.T) {
os.Exit(t.Run()) for i := 0; i < 50; i++ {
go func() {
id, err := uuid.NewV7()
if err != nil {
fmt.Println(err)
}
fmt.Println(id.String())
}()
}
time.Sleep(3 * time.Second)
t.Fatal("end")
} }
+2 -2
View File
@@ -23,13 +23,13 @@ type CountTable interface {
Table[CountLog] Table[CountLog]
// Remove all Log items that have a CountLog.Date value less then the given value. // Remove all Log items that have a CountLog.Date value less then the given value.
RemoveOldLogs(date int) RemoveOldLogs(date int)
// Get count. If platform is an empty string or "all", the full count should be given
Count(platform string) int Count(platform string) int
} }
type CrashTable interface { type CrashTable interface {
Table[CrashReport] Table[CrashReport]
// Move a crash type to archive. All instances that perfectly match that appear in CrashReport.Individual should be deleted. // Move a crash type to archive. Crashes that match the archived crash will be automatically removed from the CrashTable.
// If a CrashReport ends up with an empty Individual array it should also be deleted.
Archive(ArchivedCrash) error Archive(ArchivedCrash) error
IsArchived(IndividualCrash) bool IsArchived(IndividualCrash) bool
// Add the IndividualCrash report to the crash table. If a CrashReport exists that matches, then it gets added to CrashReport.Individual. // Add the IndividualCrash report to the crash table. If a CrashReport exists that matches, then it gets added to CrashReport.Individual.
+82 -5
View File
@@ -1,20 +1,29 @@
package darkstorm package darkstorm
import ( import (
"encoding/json"
"log" "log"
"net/http" "net/http"
"time"
"github.com/google/uuid"
) )
type CountLog struct { type CountLog struct {
ID string ID string `json:"id" bson:"_id"`
Platform string Platform string `json:"platform" bson:"platform"`
Date int Date int `json:"date" bson:"date"`
} }
func (c CountLog) GetID() string { func (c CountLog) GetID() string {
return c.ID return c.ID
} }
type countLogReq struct {
ID string
Platform string
}
func (b *Backend) countLog(w http.ResponseWriter, r *http.Request) { func (b *Backend) countLog(w http.ResponseWriter, r *http.Request) {
hdr, err := b.VerifyHeader(w, r, "count", false) hdr, err := b.VerifyHeader(w, r, "count", false)
if hdr == nil { if hdr == nil {
@@ -23,7 +32,69 @@ func (b *Backend) countLog(w http.ResponseWriter, r *http.Request) {
} }
return return
} }
//TODO defer r.Body.Close()
var req countLogReq
err = json.NewDecoder(r.Body).Decode(&req)
if err != nil || req.Platform == "" {
ReturnError(w, http.StatusBadRequest, "invalidBody", "Bad request")
return
}
ap := b.GetApp(hdr.Key)
count := ap.CountTable()
if count == nil {
ReturnError(w, http.StatusInternalServerError, "misconfigured", "Server Misconfigured")
return
}
curDate := getDate(time.Now())
if req.ID == "" {
err = addToCountTable(w, count, req.Platform, curDate)
if err != nil {
log.Println("error adding to count table:", err)
}
return
}
l, err := count.Get(req.ID)
if err == ErrNotFound {
err = addToCountTable(w, count, req.Platform, curDate)
if err != nil {
log.Println("error adding to count table:", err)
}
return
}
if l.Date >= curDate {
json.NewEncoder(w).Encode(map[string]string{"id": req.ID})
w.WriteHeader(http.StatusCreated)
return
}
err = count.PartUpdate(req.ID, map[string]any{"date": curDate})
if err != nil {
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
return
}
json.NewEncoder(w).Encode(map[string]string{"id": req.ID})
w.WriteHeader(http.StatusCreated)
}
func addToCountTable(w http.ResponseWriter, c CountTable, platform string, curDate int) error {
id, err := uuid.NewV7()
if err != nil {
log.Println("error generating new log UUID:", err)
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
return err
}
err = c.Insert(CountLog{
ID: id.String(),
Platform: platform,
Date: curDate,
})
if err != nil {
log.Println("error inserting new count log:", err)
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
return err
}
json.NewEncoder(w).Encode(map[string]string{"id": id.String()})
w.WriteHeader(http.StatusCreated)
return nil
} }
func (b *Backend) getCount(w http.ResponseWriter, r *http.Request) { func (b *Backend) getCount(w http.ResponseWriter, r *http.Request) {
@@ -44,5 +115,11 @@ func (b *Backend) getCount(w http.ResponseWriter, r *http.Request) {
} else { } else {
ap = b.GetApp(hdr.Key) ap = b.GetApp(hdr.Key)
} }
//TODO count := ap.CountTable()
if count == nil {
ReturnError(w, http.StatusBadRequest, "badRequest", "Trying to get user count on app that doesn't have a count table")
return
}
out := count.Count(r.URL.Query().Get("platform"))
json.NewEncoder(w).Encode(map[string]int{"count": out})
} }