Compare commits

...

11 Commits

  1. 5
      .gitignore
  2. 25
      main.go
  3. 7
      main_test.go
  4. 61
      settings/settings.go
  5. 48
      settings/settings_test.go
  6. 27
      storage/storage.go
  7. 4
      storage/storage_test.go

5
.gitignore

@ -12,4 +12,7 @@
*.out
# Go workspace file
go.work
go.work
# Lithium specific
settings.json

25
main.go

@ -2,10 +2,13 @@ package main
import (
"encoding/json"
"github.com/geplauder/lithium/pipelines"
"net/http"
"github.com/geplauder/lithium/pipelines"
"github.com/geplauder/lithium/settings"
"github.com/geplauder/lithium/storage"
"github.com/gorilla/mux"
"github.com/spf13/afero"
)
const Name string = "Lithium"
@ -16,7 +19,7 @@ type Metadata struct {
Version string
}
func PipelineHandler(pipeline pipelines.IPipeline, w http.ResponseWriter, r *http.Request) {
func PipelineHandler(pipeline pipelines.IPipeline, storageProvider storage.IStorageProvider, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(pipeline)
if err != nil {
@ -32,23 +35,33 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
}
}
func RegisterPipelineRoutes(r *mux.Router, pipelines []pipelines.IPipeline) {
func RegisterPipelineRoutes(r *mux.Router, pipelines []pipelines.IPipeline, storageProvider storage.IStorageProvider) {
for _, pipeline := range pipelines {
r.HandleFunc("/"+pipeline.GetSlug(), func(w http.ResponseWriter, r *http.Request) {
PipelineHandler(pipeline, w, r)
PipelineHandler(pipeline, storageProvider, w, r)
})
}
}
func main() {
settings := settings.LoadSettings(afero.NewOsFs())
var storageProvider storage.IStorageProvider
if settings.StorageProvider.Type == 0 {
storageProvider = storage.GetFileSystemStorageProvider(settings.StorageProvider.BasePath)
} else {
panic("Invalid file system provided!")
}
pipes := pipelines.LoadPipelines()
r := mux.NewRouter()
r.HandleFunc("/", IndexHandler)
RegisterPipelineRoutes(r, pipes)
RegisterPipelineRoutes(r, pipes, storageProvider)
err := http.ListenAndServe(":8000", r)
err := http.ListenAndServe(settings.Endpoint, r)
if err != nil {
panic(err)
}

7
main_test.go

@ -3,12 +3,13 @@ package main
import (
"encoding/json"
"fmt"
"github.com/geplauder/lithium/pipelines"
"net/http"
"net/http/httptest"
"testing"
"github.com/bxcodec/faker/v3"
"github.com/geplauder/lithium/pipelines"
"github.com/geplauder/lithium/storage"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
)
@ -34,7 +35,9 @@ func TestEndpointRoute(t *testing.T) {
t.Run("Registered pipelines are valid routes", func(t *testing.T) {
router := mux.NewRouter()
RegisterPipelineRoutes(router, []pipelines.IPipeline{data})
fs := storage.GetMemoryStorageProvider()
RegisterPipelineRoutes(router, []pipelines.IPipeline{data}, fs)
request, _ := http.NewRequest("GET", "/"+data.Slug, nil)
responseRecorder := httptest.NewRecorder()

61
settings/settings.go

@ -0,0 +1,61 @@
package settings
import (
"encoding/json"
"os"
"path/filepath"
"github.com/spf13/afero"
)
const (
Local FileSystemType = iota
)
type FileSystemType int
type Settings struct {
Endpoint string `json:"endpoint"`
Token string `json:"token"`
StorageProvider StorageSettings `json:"storage_provider"`
}
type StorageSettings struct {
Type FileSystemType `json:"type"`
BasePath string `json:"base_path"`
}
func parseSettings(data []byte) Settings {
settings := Settings{}
json.Unmarshal(data, &settings)
return settings
}
func LoadSettings(fileSystem afero.Fs) Settings {
workingDirectory, _ := os.Getwd()
path := filepath.Join(workingDirectory, "settings.json")
// Load file and parse file
data, err := afero.ReadFile(fileSystem, path)
if err == nil {
return parseSettings(data)
}
// If file does not exist, create default settings
defaultSettings := Settings{
Endpoint: "127.0.0.1:8000",
Token: "changeme",
StorageProvider: StorageSettings{
Type: Local,
BasePath: "assets",
},
}
serializedSettings, err := json.MarshalIndent(defaultSettings, "", "\t")
afero.WriteFile(fileSystem, path, serializedSettings, os.ModePerm)
return defaultSettings
}

48
settings/settings_test.go

@ -0,0 +1,48 @@
package settings
import (
"os"
"path/filepath"
"testing"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
)
func TestSettingsParsing(t *testing.T) {
const file string = `{
"endpoint": "0.0.0.0:8000",
"token": "foobar",
"storage_provider": {
"type": 0,
"base_path": "assets"
}
}`
t.Run("Settings parsing is successful", func(t *testing.T) {
settings := parseSettings([]byte(file))
assert.Equal(t, "0.0.0.0:8000", settings.Endpoint)
assert.Equal(t, "foobar", settings.Token)
assert.Equal(t, "assets", settings.StorageProvider.BasePath)
})
}
func TestSettingsLoading(t *testing.T) {
t.Run("Settings loading creates default settings.json when none is present", func(t *testing.T) {
fileSystem := afero.NewMemMapFs()
workingDirectory, _ := os.Getwd()
path := filepath.Join(workingDirectory, "settings.json")
// Settings file does not exist in the beginning
doesFileExist, _ := afero.Exists(fileSystem, path)
assert.False(t, doesFileExist)
LoadSettings(fileSystem)
// Settings file should be present after calling LoadSettings
doesFileExist, _ = afero.Exists(fileSystem, path)
assert.True(t, doesFileExist)
})
}

27
storage/storage.go

@ -8,8 +8,8 @@ import (
)
type IStorageProvider interface {
storeRaw(bucketName string, objectName string, data []byte) string
storeExisting(bucketName string, objectName string, existingFilePath string) string
StoreRaw(bucketName string, objectName string, data []byte) string
StoreExisting(bucketName string, objectName string, existingFilePath string) string
}
type FileSystemStorageProvider struct {
@ -17,7 +17,7 @@ type FileSystemStorageProvider struct {
basePath string
}
func (sp FileSystemStorageProvider) storeRaw(bucketName string, objectName string, data []byte) string {
func (sp FileSystemStorageProvider) StoreRaw(bucketName string, objectName string, data []byte) string {
directoryPath := filepath.Join(sp.basePath, bucketName)
sp.fileSystem.MkdirAll(directoryPath, os.ModePerm)
@ -29,8 +29,25 @@ func (sp FileSystemStorageProvider) storeRaw(bucketName string, objectName strin
return filePath
}
func (sp FileSystemStorageProvider) storeExisting(bucketName string, objectName string, existingFilePath string) string {
func (sp FileSystemStorageProvider) StoreExisting(bucketName string, objectName string, existingFilePath string) string {
bytesRead, _ := afero.ReadFile(sp.fileSystem, existingFilePath)
return sp.storeRaw(bucketName, objectName, bytesRead)
return sp.StoreRaw(bucketName, objectName, bytesRead)
}
func GetFileSystemStorageProvider(basePath string) FileSystemStorageProvider {
wd, _ := os.Getwd()
return FileSystemStorageProvider{
fileSystem: afero.NewBasePathFs(afero.NewOsFs(), filepath.Join(wd, "files")),
basePath: basePath,
}
}
// TODO: Move this out of this file
func GetMemoryStorageProvider() FileSystemStorageProvider {
return FileSystemStorageProvider{
fileSystem: afero.NewBasePathFs(afero.NewMemMapFs(), "/"),
basePath: "/tmp/foo/bar",
}
}

4
storage/storage_test.go

@ -19,7 +19,7 @@ func TestFileSystemStorageProvider(t *testing.T) {
basePath: "/tmp/foo/bar",
}
finalPath := provider.storeRaw("test", "test.bin", dummyData)
finalPath := provider.StoreRaw("test", "test.bin", dummyData)
assert.Equal(t, "/tmp/foo/bar/test/test.bin", finalPath)
exists, _ := afero.Exists(fileSystem, "/tmp/foo/bar/test/test.bin")
@ -39,7 +39,7 @@ func TestFileSystemStorageProvider(t *testing.T) {
basePath: "/tmp/foo/bar",
}
finalPath := provider.storeExisting("test", "test.bin", "/tmp/existing.bin")
finalPath := provider.StoreExisting("test", "test.bin", "/tmp/existing.bin")
assert.Equal(t, "/tmp/foo/bar/test/test.bin", finalPath)
exists, _ := afero.Exists(fileSystem, "/tmp/foo/bar/test/test.bin")

Loading…
Cancel
Save