commit bd996eec0a07992ae60d86323e47ba3b485ba11b
parent 8d4945b5fa3063cd3c629a2355be9ffa7142ac0b
Author: Vetle Haflan <vetle.haflan@luxsave.com>
Date: Sun, 24 May 2020 20:44:31 +0200
Service deletion and minor fixes
Closes #4
Diffstat:
8 files changed, 82 insertions(+), 19 deletions(-)
diff --git a/internal/events/events.go b/internal/events/events.go
@@ -43,7 +43,7 @@ func GetAll() []*Event {
return b.ForEach(func(id, _ []byte) error {
eb := b.Bucket(id)
event := new(Event)
- if err := event.fromBucket(eb); err != nil {
+ if err := event.FromBucket(eb); err != nil {
return err
}
events = append(events, event)
@@ -125,7 +125,7 @@ func (event *Event) toBucket(eb *bbolt.Bucket) error {
// Reads data from the given bucket into the fields of event
// Returns error if any of the fields cannot be found
-func (event *Event) fromBucket(eb *bbolt.Bucket) error {
+func (event *Event) FromBucket(eb *bbolt.Bucket) error {
var id, service, timestamp []byte
var status, title, details []byte
err := errors.New("missing field from database")
diff --git a/internal/http/events.go b/internal/http/events.go
@@ -109,6 +109,7 @@ func reportEvent(w http.ResponseWriter, r *http.Request) {
events.Delete(firstEventID)
log.Printf("MaxNumberEvents reached for service %v. Deleting first event, %v", service.ID, firstEventID)
}
+ w.WriteHeader(http.StatusCreated)
}
func generateLateEvent(s *services.Service) *events.Event {
diff --git a/internal/http/services.go b/internal/http/services.go
@@ -3,6 +3,7 @@ package http
import (
"encoding/json"
"io/ioutil"
+ "log"
"net/http"
"sermoni/internal/services"
"strconv"
@@ -16,6 +17,7 @@ func getServices(w http.ResponseWriter, r *http.Request) {
check(err)
w.Write(data)
}
+
func postService(w http.ResponseWriter, r *http.Request) {
content, err := ioutil.ReadAll(r.Body)
check(err)
@@ -24,13 +26,16 @@ func postService(w http.ResponseWriter, r *http.Request) {
err = json.Unmarshal(content, service)
check(err)
services.Add(service)
+ w.WriteHeader(http.StatusCreated)
}
+
func deleteService(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.ParseUint(vars["id"], 10, 64)
err := services.Delete(id)
if err != nil {
- // TODO: Non-existing error is not an internal server error
+ // TODO: "Non-existing" error is not an internal server error
+ log.Printf("deleteService error: %v\n", err)
w.WriteHeader(http.StatusInternalServerError)
}
}
diff --git a/internal/services/services.go b/internal/services/services.go
@@ -2,8 +2,10 @@ package services
import (
"errors"
+ "fmt"
"log"
"sermoni/internal/database"
+ "sermoni/internal/events"
"strconv"
"go.etcd.io/bbolt"
@@ -73,26 +75,54 @@ func GetAll() []*Service {
return services
}
-// Delete deletes the given service if it exists
+// Delete deletes the given service if it exists, and all events for said service, if any
func Delete(intID uint64) error {
db := database.GetDB()
serviceID := []byte(strconv.FormatUint(intID, 10))
- return db.Update(func(tx *bbolt.Tx) error {
- var b, stb *bbolt.Bucket
- if b = tx.Bucket(database.BucketKeyServices); b == nil {
+ return db.Update(func(tx *bbolt.Tx) (err error) {
+ sb := tx.Bucket(database.BucketKeyServices)
+ stb := tx.Bucket(database.BucketKeyServiceTokens)
+ eb := tx.Bucket(database.BucketKeyEvents)
+ if sb == nil {
log.Panic("The services bucket does not exist")
}
- if stb = tx.Bucket(database.BucketKeyServiceTokens); b == nil {
+ if stb == nil {
log.Panic("The service-tokens bucket does not exist")
}
+ if eb == nil {
+ log.Panic("The event bucket does not exist")
+ }
// Delete the entry from root services bucket
- if b.Bucket(serviceID) == nil {
+ if sb.Bucket(serviceID) == nil {
return errors.New("no service for the given id")
}
- if err := b.DeleteBucket(serviceID); err != nil {
+ if err = sb.DeleteBucket(serviceID); err != nil {
+ return err
+ }
+
+ // Delete all events for the service
+ var eventsToDeleteIDs [][]byte
+ err = eb.ForEach(func(id, _ []byte) error {
+ evb := eb.Bucket(id)
+ event := new(events.Event)
+ if err := event.FromBucket(evb); err != nil {
+ return err
+ }
+ if event.Service == intID {
+ eventsToDeleteIDs = append(eventsToDeleteIDs, id)
+ }
+ return nil
+ })
+ if err != nil {
return err
}
+ for _, id := range eventsToDeleteIDs {
+ if err = eb.DeleteBucket(id); err != nil {
+ fmt.Printf("Error deleting event with ID %v\n", id)
+ return err
+ }
+ }
// Find the token entry and delete it from service-tokens bucket
c := stb.Cursor()
diff --git a/ui/src/Events.vue b/ui/src/Events.vue
@@ -1,6 +1,6 @@
<template>
<div class="events-wrapper">
- <div v-for="e in events">
+ <div v-for="e in events" :key="e.id">
<div class="event"
style="display: flex;"
:style="e.style">
@@ -50,7 +50,7 @@
);
},
error => {
- console.error(error)
+ console.error(error);
this.$emit("error");
}
);
diff --git a/ui/src/Services.vue b/ui/src/Services.vue
@@ -17,6 +17,9 @@
<span>Expectation period</span>
<time-picker :value="service.period"/> <br/>
+
+ <button @click="deletionID = service.id">Delete</button>
+ <button @click="updateService(service.id)">Update</button>
</div>
<input :type="showPasswords ? 'text' : 'password'" v-model="newService.token" placeholder="Token"> <br/>
@@ -26,6 +29,10 @@
<time-picker v-model="newService.period" placeholder="Expectation Period"/> <br/>
<button @click="addService">Add service</button>
+ <div v-show="deletionID" style="position: fixed; bottom: 15px; right: 15px;">
+ <button @click="deletionID = 0">Cancel</button>
+ <button @click="deleteService()">Confirm</button>
+ </div>
</div>
</template>
@@ -45,7 +52,8 @@
period: {"number": 0, "scalar": 0},
maxevents: 0
},
- showPasswords: true
+ showPasswords: true,
+ deletionID: 0,
}
},
methods: {
@@ -69,6 +77,23 @@
}
);
},
+ updateService(id) {
+ alert("Not implemented!");
+ },
+ deleteService() {
+ api.deleteService(this.deletionID,
+ success => {
+ this.services = this.services.filter(
+ s => s.id !== this.deletionID
+ );
+ this.deletionID = 0;
+ },
+ error => {
+ console.error(error);
+ this.$emit("error");
+ }
+ );
+ },
getExpectations(unixMilliTime) {
const units = [
{ "unit": "weeks", "scalar": 604800000 },
diff --git a/ui/src/TimePicker.vue b/ui/src/TimePicker.vue
@@ -43,7 +43,6 @@
},
methods: {
update(prop, e) {
- console.log(e)
const newValue = this.value;
newValue[prop] = e.target.value;
this.$emit("input", newValue);
diff --git a/ui/src/requests.js b/ui/src/requests.js
@@ -58,25 +58,28 @@ function getServices(successHandler, errorHandler) {
});
}
-// TODO: Expected status 201 (and set 201 server-side)
function postService(service, successHandler, errorHandler) {
request({
url: "/services",
method: "POST",
+ expectedStatus: 201,
data: service,
success: successHandler,
error: errorHandler
});
}
-function deleteService(id) {
-
+function deleteService(id, successHandler, errorHandler) {
+ request({
+ url: "/services/" + id,
+ method: "DELETE",
+ success: successHandler,
+ error: errorHandler
+ });
}
/***** Event management *****/
-// (TODO)
-
function getEvents(successHandler, errorHandler) {
request({
url: "/events",