summaryrefslogtreecommitdiff
path: root/main.go
blob: e6ce9f65b559a75e122553c98192506350b5150d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Storehouse: HTTP object store server
//
// Licensed under CC0 1.0 Universal:
// https://creativecommons.org/publicdomain/zero/1.0/legalcode
package main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"

	"github.com/labstack/echo/v4"
	"github.com/pablombg/storehouse/objectstore"
)

type storehouse struct {
	objects objectstore.ObjectStore
}

func InitStorehouse(memory bool, dataDir string) *storehouse {
	var objects objectstore.ObjectStore

	if memory {
		objects = objectstore.NewMemBackend()
	} else {
		objects = objectstore.NewFileBackend(dataDir)
	}

	return &storehouse{objects: objects}
}

func (s *storehouse) putObject(c echo.Context) error {
	bucketId := c.Param("bucketId")
	objectId := c.Param("objectId")

	// Get request body
	bodyBytes, err := ioutil.ReadAll(c.Request().Body)
	if err != nil {
		log.Printf("Error reading body: %v", err)
		m := &errorJSON{Error: "Error reading body"}
		return c.JSON(http.StatusInternalServerError, m)
	}

	err = s.objects.CreateObject(bucketId, objectId, bodyBytes)
	// Check errors creating the object
	if err != nil {
		log.Printf("Error creating the object: %v", err)
		switch err.(type) {
		case *objectstore.DuplicateError:
			// Return "409 Conflict" if there
			// is a duplicate of the object
			m := &errorJSON{Error: err.Error()}
			return c.JSON(http.StatusConflict, m)
		default:
			m := &errorJSON{Error: "Internal error"}
			return c.JSON(http.StatusInternalServerError, m)
		}
	}

	// Return success
	m := &objectIdJSON{Id: objectId}
	return c.JSON(http.StatusCreated, m)
}

func (s *storehouse) getObject(c echo.Context) error {
	bucketId := c.Param("bucketId")
	objectId := c.Param("objectId")

	object, err := s.objects.GetObject(bucketId, objectId)
	if errors.Is(err, os.ErrNotExist) {
		msg := "Object not found"
		log.Println(msg)
		m := &errorJSON{Error: msg}
		return c.JSON(http.StatusNotFound, m)
	} else if err != nil {
		log.Println(err)
		m := &errorJSON{Error: "Internal error"}
		return c.JSON(http.StatusInternalServerError, m)
	}

	return c.String(http.StatusOK, object)
}

func (s *storehouse) deleteObject(c echo.Context) error {
	bucketId := c.Param("bucketId")
	objectId := c.Param("objectId")

	err := s.objects.DeleteObject(bucketId, objectId)
	if errors.Is(err, os.ErrNotExist) {
		msg := "Object not found"
		log.Println(msg)
		m := &errorJSON{Error: msg}
		return c.JSON(http.StatusNotFound, m)
	} else if err != nil {
		log.Println(err)
		m := &errorJSON{Error: "Internal error"}
		return c.JSON(http.StatusInternalServerError, m)
	}

	m := &infoJSON{Info: "Object deleted"}
	return c.JSON(http.StatusOK, m)
}

func main() {
	// Command-line options
	memHelp := "Memory backend with de-duplication (default: file backend w/o de-dup)"
	memory := flag.Bool("memory", false, memHelp)
	dataDirHelp := "The directory where the objects are stored with the file backend"
	dataDir := flag.String("datadir", "data", dataDirHelp)
	portHelp := "The port on which the server will listen for connections"
	port := flag.Int("port", 8080, portHelp)
	flag.Parse()

	// Initialization
	s := InitStorehouse(*memory, *dataDir)
	e := echo.New()

	// Routes
	e.GET("/objects/:bucketId/:objectId", s.getObject)
	e.PUT("/objects/:bucketId/:objectId", s.putObject)
	e.DELETE("/objects/:bucketId/:objectId", s.deleteObject)

	// Start server
	listenConf := fmt.Sprintf(":%v", *port)
	e.Start(listenConf)
}