diff --git a/api/datastore/bolt/bolt.go b/api/datastore/bolt/bolt.go index ad5f28501..68ebcb9d6 100644 --- a/api/datastore/bolt/bolt.go +++ b/api/datastore/bolt/bolt.go @@ -208,13 +208,13 @@ func (ds *BoltDatastore) StoreRoute(route *models.Route) (*models.Route, error) return route, nil } -func (ds *BoltDatastore) RemoveRoute(appName, routeName string) error { +func (ds *BoltDatastore) RemoveRoute(appName, routePath string) error { if appName == "" { return models.ErrDatastoreEmptyAppName } - if routeName == "" { - return models.ErrDatastoreEmptyRouteName + if routePath == "" { + return models.ErrDatastoreEmptyRoutePath } err := ds.db.Update(func(tx *bolt.Tx) error { @@ -223,7 +223,7 @@ func (ds *BoltDatastore) RemoveRoute(appName, routeName string) error { return err } - err = b.Delete([]byte(routeName)) + err = b.Delete([]byte(routePath)) if err != nil { return err } @@ -235,13 +235,13 @@ func (ds *BoltDatastore) RemoveRoute(appName, routeName string) error { return nil } -func (ds *BoltDatastore) GetRoute(appName, routeName string) (*models.Route, error) { +func (ds *BoltDatastore) GetRoute(appName, routePath string) (*models.Route, error) { if appName == "" { return nil, models.ErrDatastoreEmptyAppName } - if routeName == "" { - return nil, models.ErrDatastoreEmptyRouteName + if routePath == "" { + return nil, models.ErrDatastoreEmptyRoutePath } var route *models.Route @@ -251,7 +251,7 @@ func (ds *BoltDatastore) GetRoute(appName, routeName string) (*models.Route, err return err } - v := b.Get([]byte(routeName)) + v := b.Get([]byte(routePath)) if v != nil { err = json.Unmarshal(v, &route) } diff --git a/api/datastore/bolt_test.go b/api/datastore/bolt_test.go index e4a7263e0..d1c354f57 100644 --- a/api/datastore/bolt_test.go +++ b/api/datastore/bolt_test.go @@ -118,7 +118,7 @@ func TestBolt(t *testing.T) { t.Fatalf("Test GetRoute: error: %s", err) } if route.Path != testRoute.Path { - t.Fatalf("Test GetRoute: expected `route.Name` to be `%s` but it was `%s`", route.Path, testRoute.Path) + t.Fatalf("Test GetRoute: expected `route.Path` to be `%s` but it was `%s`", route.Path, testRoute.Path) } // Testing list routes diff --git a/api/datastore/mock.go b/api/datastore/mock.go index 960d6c46f..bb5919c6c 100644 --- a/api/datastore/mock.go +++ b/api/datastore/mock.go @@ -9,46 +9,73 @@ type Mock struct { FakeRoutes []*models.Route } -func (m *Mock) GetApp(app string) (*models.App, error) { - return m.FakeApp, nil +func (m *Mock) GetApp(appName string) (*models.App, error) { + app := m.FakeApp + if app == nil && m.FakeApps != nil { + for _, a := range m.FakeApps { + if a.Name == appName { + app = a + } + } + } + + return app, nil } func (m *Mock) GetApps(appFilter *models.AppFilter) ([]*models.App, error) { + // TODO: improve this mock method return m.FakeApps, nil } func (m *Mock) StoreApp(app *models.App) (*models.App, error) { + // TODO: improve this mock method return m.FakeApp, nil } -func (m *Mock) RemoveApp(app string) error { +func (m *Mock) RemoveApp(appName string) error { + // TODO: improve this mock method return nil } -func (m *Mock) GetRoute(app, route string) (*models.Route, error) { - return m.FakeRoute, nil +func (m *Mock) GetRoute(appName, routePath string) (*models.Route, error) { + route := m.FakeRoute + if route == nil && m.FakeRoutes != nil { + for _, r := range m.FakeRoutes { + if r.AppName == appName && r.Path == routePath { + route = r + } + } + } + + return route, nil } func (m *Mock) GetRoutes(routeFilter *models.RouteFilter) ([]*models.Route, error) { + // TODO: improve this mock method return m.FakeRoutes, nil } func (m *Mock) GetRoutesByApp(appName string, routeFilter *models.RouteFilter) ([]*models.Route, error) { + // TODO: improve this mock method return m.FakeRoutes, nil } func (m *Mock) StoreRoute(route *models.Route) (*models.Route, error) { + // TODO: improve this mock method return m.FakeRoute, nil } -func (m *Mock) RemoveRoute(app, route string) error { +func (m *Mock) RemoveRoute(appName, routePath string) error { + // TODO: improve this mock method return nil } func (m *Mock) Put(key, value []byte) error { + // TODO: improve this mock method return nil } func (m *Mock) Get(key []byte) ([]byte, error) { + // TODO: improve this mock method return []byte{}, nil } diff --git a/api/datastore/postgres/postgres.go b/api/datastore/postgres/postgres.go index 060de4579..babc0d302 100644 --- a/api/datastore/postgres/postgres.go +++ b/api/datastore/postgres/postgres.go @@ -17,11 +17,13 @@ CREATE TABLE IF NOT EXISTS routes ( path text NOT NULL, image character varying(256) NOT NULL, headers text NOT NULL, + config text NOT NULL, PRIMARY KEY (app_name, path) );` const appsTableCreate = `CREATE TABLE IF NOT EXISTS apps ( name character varying(256) NOT NULL PRIMARY KEY + config text NOT NULL, );` const extrasTableCreate = `CREATE TABLE IF NOT EXISTS extras ( @@ -73,13 +75,20 @@ func New(url *url.URL) (models.Datastore, error) { } func (ds *PostgresDatastore) StoreApp(app *models.App) (*models.App, error) { - _, err := ds.db.Exec(` - INSERT INTO apps (name) - VALUES ($1) - ON CONFLICT (name) DO NOTHING - RETURNING name; - `, app.Name) - // todo: after we support headers, the conflict should update the headers. + cbyte, err := json.Marshal(app.Config) + if err != nil { + return nil, err + } + + _, err = ds.db.Exec(` + INSERT INTO apps (name, config) + VALUES ($1, $2) + ON CONFLICT (app_name) DO UPDATE SET + config = $2; + `, + app.Name, + string(cbyte), + ) if err != nil { return nil, err @@ -105,12 +114,15 @@ func (ds *PostgresDatastore) GetApp(name string) (*models.App, error) { row := ds.db.QueryRow("SELECT name FROM apps WHERE name=$1", name) var resName string - err := row.Scan(&resName) + var config string + err := row.Scan(&resName, &config) res := &models.App{ Name: resName, } + json.Unmarshal([]byte(config), &res.Config) + if err != nil { return nil, err } @@ -155,32 +167,36 @@ func (ds *PostgresDatastore) GetApps(filter *models.AppFilter) ([]*models.App, e } func (ds *PostgresDatastore) StoreRoute(route *models.Route) (*models.Route, error) { - var headers string - hbyte, err := json.Marshal(route.Headers) if err != nil { return nil, err } - headers = string(hbyte) + cbyte, err := json.Marshal(route.Config) + if err != nil { + return nil, err + } _, err = ds.db.Exec(` INSERT INTO routes ( app_name, path, image, - headers + headers, + config ) VALUES ($1, $2, $3, $4) ON CONFLICT (app_name, path) DO UPDATE SET path = $2, image = $3, headers = $4; + config = $5; `, route.AppName, route.Path, route.Image, - headers, + string(hbyte), + string(cbyte), ) if err != nil { @@ -189,11 +205,11 @@ func (ds *PostgresDatastore) StoreRoute(route *models.Route) (*models.Route, err return route, nil } -func (ds *PostgresDatastore) RemoveRoute(appName, routeName string) error { +func (ds *PostgresDatastore) RemoveRoute(appName, routePath string) error { _, err := ds.db.Exec(` DELETE FROM routes - WHERE name = $1 - `, routeName) + WHERE path = $1 + `, routePath) if err != nil { return err @@ -203,27 +219,30 @@ func (ds *PostgresDatastore) RemoveRoute(appName, routeName string) error { func scanRoute(scanner rowScanner, route *models.Route) error { var headerStr string + var configStr string + err := scanner.Scan( - // &route.Name, &route.AppName, &route.Path, &route.Image, &headerStr, + &configStr, ) if headerStr == "" { return models.ErrRoutesNotFound } - err = json.Unmarshal([]byte(headerStr), &route.Headers) + json.Unmarshal([]byte(headerStr), &route.Headers) + json.Unmarshal([]byte(configStr), &route.Config) return err } -func getRoute(qr rowQuerier, routeName string) (*models.Route, error) { +func getRoute(qr rowQuerier, routePath string) (*models.Route, error) { var route models.Route - row := qr.QueryRow(fmt.Sprintf("%s WHERE name=$1", routeSelector), routeName) + row := qr.QueryRow(fmt.Sprintf("%s WHERE name=$1", routeSelector), routePath) err := scanRoute(row, &route) if err == sql.ErrNoRows { @@ -234,8 +253,8 @@ func getRoute(qr rowQuerier, routeName string) (*models.Route, error) { return &route, nil } -func (ds *PostgresDatastore) GetRoute(appName, routeName string) (*models.Route, error) { - return getRoute(ds.db, routeName) +func (ds *PostgresDatastore) GetRoute(appName, routePath string) (*models.Route, error) { + return getRoute(ds.db, routePath) } func (ds *PostgresDatastore) GetRoutes(filter *models.RouteFilter) ([]*models.Route, error) { diff --git a/api/models/app.go b/api/models/app.go index 811f60510..83780ce54 100644 --- a/api/models/app.go +++ b/api/models/app.go @@ -22,6 +22,7 @@ var ( type App struct { Name string `json:"name"` Routes Routes `json:"routes,omitempty"` + Config `json:"config"` } const ( diff --git a/api/models/config.go b/api/models/config.go index 82fd0bb41..4a9dc6290 100644 --- a/api/models/config.go +++ b/api/models/config.go @@ -1,7 +1,6 @@ package models -type Config struct { -} +type Config map[string]string func (c *Config) Validate() error { return nil diff --git a/api/models/datastore.go b/api/models/datastore.go index 0c195e80d..448e11681 100644 --- a/api/models/datastore.go +++ b/api/models/datastore.go @@ -8,11 +8,11 @@ type Datastore interface { StoreApp(*App) (*App, error) RemoveApp(appName string) error - GetRoute(appName, routeName string) (*Route, error) + GetRoute(appName, routePath string) (*Route, error) GetRoutes(*RouteFilter) (routes []*Route, err error) GetRoutesByApp(string, *RouteFilter) (routes []*Route, err error) StoreRoute(*Route) (*Route, error) - RemoveRoute(appName, routeName string) error + RemoveRoute(appName, routePath string) error // The following provide a generic key value store for arbitrary data, can be used by extensions to store extra data // todo: should we namespace these by app? Then when an app is deleted, it can delete any of this extra data too. @@ -22,7 +22,7 @@ type Datastore interface { var ( ErrDatastoreEmptyAppName = errors.New("Missing app name") - ErrDatastoreEmptyRouteName = errors.New("Missing route name") + ErrDatastoreEmptyRoutePath = errors.New("Missing route name") ErrDatastoreEmptyApp = errors.New("Missing app") ErrDatastoreEmptyRoute = errors.New("Missing route") ) diff --git a/api/models/route.go b/api/models/route.go index 810eb8cc0..b83566290 100644 --- a/api/models/route.go +++ b/api/models/route.go @@ -25,6 +25,7 @@ type Route struct { Path string `json:"path,omitempty"` Image string `json:"image,omitempty"` Headers http.Header `json:"headers,omitempty"` + Config `json:"config"` } var ( diff --git a/api/server/apps_create.go b/api/server/apps_create.go index 54795a6db..ffbbbac91 100644 --- a/api/server/apps_create.go +++ b/api/server/apps_create.go @@ -56,5 +56,5 @@ func handleAppCreate(c *gin.Context) { return } - c.JSON(http.StatusCreated, appResponse{"App successfully created", wapp}) + c.JSON(http.StatusCreated, appResponse{"App successfully created", wapp}) } diff --git a/api/server/apps_test.go b/api/server/apps_test.go index c9a18efed..e05a2bb59 100644 --- a/api/server/apps_test.go +++ b/api/server/apps_test.go @@ -11,7 +11,7 @@ import ( ) func TestAppCreate(t *testing.T) { - New(&models.Config{}, &datastore.Mock{}, testRunner(t)) + New(&datastore.Mock{}, testRunner(t)) router := testRouter() for i, test := range []struct { @@ -52,7 +52,7 @@ func TestAppCreate(t *testing.T) { } func TestAppDelete(t *testing.T) { - New(&models.Config{}, &datastore.Mock{}, testRunner(t)) + New(&datastore.Mock{}, testRunner(t)) router := testRouter() for i, test := range []struct { @@ -83,7 +83,7 @@ func TestAppDelete(t *testing.T) { } func TestAppList(t *testing.T) { - New(&models.Config{}, &datastore.Mock{}, testRunner(t)) + New(&datastore.Mock{}, testRunner(t)) router := testRouter() for i, test := range []struct { @@ -113,7 +113,7 @@ func TestAppList(t *testing.T) { } func TestAppGet(t *testing.T) { - New(&models.Config{}, &datastore.Mock{}, testRunner(t)) + New(&datastore.Mock{}, testRunner(t)) router := testRouter() for i, test := range []struct { @@ -143,7 +143,7 @@ func TestAppGet(t *testing.T) { } func TestAppUpdate(t *testing.T) { - New(&models.Config{}, &datastore.Mock{}, testRunner(t)) + New(&datastore.Mock{}, testRunner(t)) router := testRouter() for i, test := range []struct { diff --git a/api/server/apps_update.go b/api/server/apps_update.go index 88ccad6e2..22192321a 100644 --- a/api/server/apps_update.go +++ b/api/server/apps_update.go @@ -14,34 +14,30 @@ func handleAppUpdate(c *gin.Context) { ctx := c.MustGet("ctx").(context.Context) log := titancommon.Logger(ctx) - app := &models.App{} + wapp := models.AppWrapper{} - err := c.BindJSON(app) + err := c.BindJSON(&wapp) if err != nil { log.WithError(err).Debug(models.ErrInvalidJSON) c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON)) return } - if app == nil { + if wapp.App == nil { log.Debug(models.ErrAppsMissingNew) c.JSON(http.StatusBadRequest, simpleError(models.ErrAppsMissingNew)) return } - if err := app.Validate(); err != nil { - log.Error(err) - c.JSON(http.StatusInternalServerError, simpleError(err)) + app, err := Api.Datastore.StoreApp(wapp.App) + if err != nil { + log.WithError(err).Debug(models.ErrAppsCreate) + c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsCreate)) return } - // app, err := Api.Datastore.StoreApp(wapp.App) - // if err != nil { - // log.WithError(err).Debug(models.ErrAppsCreate) - // c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsCreate)) - // return - // } + wapp.App = app // Nothing to update right now in apps - c.JSON(http.StatusOK, simpleError(models.ErrAppsNothingToUpdate)) + c.JSON(http.StatusOK, appResponse{"App successfully updated", wapp}) } diff --git a/api/server/routes_delete.go b/api/server/routes_delete.go index 2981a2018..5a1a55968 100644 --- a/api/server/routes_delete.go +++ b/api/server/routes_delete.go @@ -15,8 +15,8 @@ func handleRouteDelete(c *gin.Context) { log := titancommon.Logger(ctx) appName := c.Param("app") - routeName := c.Param("route") - err := Api.Datastore.RemoveRoute(appName, routeName) + routePath := c.Param("route") + err := Api.Datastore.RemoveRoute(appName, routePath) if err != nil { log.WithError(err).Debug(models.ErrRoutesRemoving) diff --git a/api/server/routes_get.go b/api/server/routes_get.go index 94b0aeb05..9273a6531 100644 --- a/api/server/routes_get.go +++ b/api/server/routes_get.go @@ -16,9 +16,9 @@ func handleRouteGet(c *gin.Context) { log := titancommon.Logger(ctx) appName := c.Param("app") - routeName := c.Param("route") + routePath := c.Param("route") - route, err := Api.Datastore.GetRoute(appName, routeName) + route, err := Api.Datastore.GetRoute(appName, routePath) if err != nil { log.WithError(err).Error(models.ErrRoutesGet) c.JSON(http.StatusInternalServerError, simpleError(models.ErrRoutesGet))