mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Timestamps on apps / routes (#614)
* route updated_at * add app created at, fix some route updated_at bugs * add app updated_at TODO need to add tests through front end TODO for validation we don't really want to use the validate wrapper since it's a programmer error and not a user error, hopefully tests block this. * add tests for timestamps to exist / change on apps&routes * route equals at done, fix tests wit dis * fix up the equals sugar * add swagger * fix rebase * precisely allocate maps in clone * vetted * meh * fix api tests
This commit is contained in:
@@ -1,8 +1,29 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
Name string `json:"name" db:"name"`
|
||||
Config Config `json:"config,omitempty" db:"config"`
|
||||
Name string `json:"name" db:"name"`
|
||||
Config Config `json:"config,omitempty" db:"config"`
|
||||
CreatedAt strfmt.DateTime `json:"created_at,omitempty" db:"created_at"`
|
||||
UpdatedAt strfmt.DateTime `json:"updated_at,omitempty" db:"updated_at"`
|
||||
}
|
||||
|
||||
func (a *App) SetDefaults() {
|
||||
if time.Time(a.CreatedAt).IsZero() {
|
||||
a.CreatedAt = strfmt.DateTime(time.Now())
|
||||
}
|
||||
if time.Time(a.UpdatedAt).IsZero() {
|
||||
a.UpdatedAt = strfmt.DateTime(time.Now())
|
||||
}
|
||||
if a.Config == nil {
|
||||
// keeps the json from being nil
|
||||
a.Config = map[string]string{}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) Validate() error {
|
||||
@@ -21,24 +42,42 @@ func (a *App) Validate() error {
|
||||
}
|
||||
|
||||
func (a *App) Clone() *App {
|
||||
var c App
|
||||
c.Name = a.Name
|
||||
clone := new(App)
|
||||
*clone = *a // shallow copy
|
||||
|
||||
// now deep copy the map
|
||||
if a.Config != nil {
|
||||
c.Config = make(Config)
|
||||
clone.Config = make(Config, len(a.Config))
|
||||
for k, v := range a.Config {
|
||||
c.Config[k] = v
|
||||
clone.Config[k] = v
|
||||
}
|
||||
}
|
||||
return &c
|
||||
return clone
|
||||
}
|
||||
|
||||
// UpdateConfig adds entries from patch to a.Config, and removes entries with empty values.
|
||||
func (a *App) UpdateConfig(patch Config) {
|
||||
if patch != nil {
|
||||
func (a1 *App) Equals(a2 *App) bool {
|
||||
// start off equal, check equivalence of each field.
|
||||
// the RHS of && won't eval if eq==false so config checking is lazy
|
||||
|
||||
eq := true
|
||||
eq = eq && a1.Name == a2.Name
|
||||
eq = eq && a1.Config.Equals(a2.Config)
|
||||
// NOTE: datastore tests are not very fun to write with timestamp checks,
|
||||
// and these are not values the user may set so we kind of don't care.
|
||||
//eq = eq && time.Time(a1.CreatedAt).Equal(time.Time(a2.CreatedAt))
|
||||
//eq = eq && time.Time(a1.UpdatedAt).Equal(time.Time(a2.UpdatedAt))
|
||||
return eq
|
||||
}
|
||||
|
||||
// Update adds entries from patch to a.Config, and removes entries with empty values.
|
||||
func (a *App) Update(src *App) {
|
||||
original := a.Clone()
|
||||
|
||||
if src.Config != nil {
|
||||
if a.Config == nil {
|
||||
a.Config = make(Config)
|
||||
}
|
||||
for k, v := range patch {
|
||||
for k, v := range src.Config {
|
||||
if v == "" {
|
||||
delete(a.Config, k)
|
||||
} else {
|
||||
@@ -46,6 +85,10 @@ func (a *App) UpdateConfig(patch Config) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !a.Equals(original) {
|
||||
a.UpdatedAt = strfmt.DateTime(time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
// AppFilter is the filter used for querying apps
|
||||
|
||||
@@ -14,6 +14,19 @@ func (c *Config) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c1 Config) Equals(c2 Config) bool {
|
||||
if len(c1) != len(c2) {
|
||||
return false
|
||||
}
|
||||
for k1, v1 := range c1 {
|
||||
v2, _ := c2[k1]
|
||||
if v1 != v2 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// implements sql.Valuer, returning a string
|
||||
func (c Config) Value() (driver.Value, error) {
|
||||
if len(c) < 1 {
|
||||
@@ -56,6 +69,24 @@ func (c *Config) Scan(value interface{}) error {
|
||||
// Headers is an http.Header that implements additional methods.
|
||||
type Headers http.Header
|
||||
|
||||
func (h1 Headers) Equals(h2 Headers) bool {
|
||||
if len(h1) != len(h2) {
|
||||
return false
|
||||
}
|
||||
for k1, v1s := range h1 {
|
||||
v2s, _ := h2[k1]
|
||||
if len(v2s) != len(v1s) {
|
||||
return false
|
||||
}
|
||||
for i, v1 := range v1s {
|
||||
if v2s[i] != v1 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// implements sql.Valuer, returning a string
|
||||
func (h Headers) Value() (driver.Value, error) {
|
||||
if len(h) < 1 {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
@@ -19,7 +20,7 @@ const (
|
||||
MaxIdleTimeout = MaxAsyncTimeout
|
||||
)
|
||||
|
||||
var RouteMaxMemory = uint64(8 * 1024) // 8GB TODO should probably be a var of machine max?
|
||||
var RouteMaxMemory = uint64(8 * 1024)
|
||||
|
||||
type Routes []*Route
|
||||
|
||||
@@ -35,6 +36,7 @@ type Route struct {
|
||||
IdleTimeout int32 `json:"idle_timeout" db:"idle_timeout"`
|
||||
Config Config `json:"config,omitempty" db:"config"`
|
||||
CreatedAt strfmt.DateTime `json:"created_at,omitempty" db:"created_at"`
|
||||
UpdatedAt strfmt.DateTime `json:"updated_at,omitempty" db:"updated_at"`
|
||||
}
|
||||
|
||||
// SetDefaults sets zeroed field to defaults.
|
||||
@@ -56,6 +58,7 @@ func (r *Route) SetDefaults() {
|
||||
}
|
||||
|
||||
if r.Config == nil {
|
||||
// keeps the json from being nil
|
||||
r.Config = map[string]string{}
|
||||
}
|
||||
|
||||
@@ -66,6 +69,14 @@ func (r *Route) SetDefaults() {
|
||||
if r.IdleTimeout == 0 {
|
||||
r.IdleTimeout = DefaultIdleTimeout
|
||||
}
|
||||
|
||||
if time.Time(r.CreatedAt).IsZero() {
|
||||
r.CreatedAt = strfmt.DateTime(time.Now())
|
||||
}
|
||||
|
||||
if time.Time(r.UpdatedAt).IsZero() {
|
||||
r.UpdatedAt = strfmt.DateTime(time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
// Validate validates all field values, returning the first error, if any.
|
||||
@@ -121,16 +132,54 @@ func (r *Route) Validate() error {
|
||||
}
|
||||
|
||||
func (r *Route) Clone() *Route {
|
||||
var clone Route
|
||||
clone.AppName = r.AppName
|
||||
clone.Path = r.Path
|
||||
clone.Update(r)
|
||||
return &clone
|
||||
clone := new(Route)
|
||||
*clone = *r // shallow copy
|
||||
|
||||
// now deep copy the maps
|
||||
if r.Config != nil {
|
||||
clone.Config = make(Config, len(r.Config))
|
||||
for k, v := range r.Config {
|
||||
clone.Config[k] = v
|
||||
}
|
||||
}
|
||||
if r.Headers != nil {
|
||||
clone.Headers = make(Headers, len(r.Headers))
|
||||
for k, v := range r.Headers {
|
||||
// TODO technically, we need to deep copy this slice...
|
||||
clone.Headers[k] = v
|
||||
}
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
// Update updates fields in r with non-zero field values from new.
|
||||
// 0-length slice Header values, and empty-string Config values trigger removal of map entry.
|
||||
func (r1 *Route) Equals(r2 *Route) bool {
|
||||
// start off equal, check equivalence of each field.
|
||||
// the RHS of && won't eval if eq==false so config/headers checking is lazy
|
||||
|
||||
eq := true
|
||||
eq = eq && r1.AppName == r2.AppName
|
||||
eq = eq && r1.Path == r2.Path
|
||||
eq = eq && r1.Image == r2.Image
|
||||
eq = eq && r1.Memory == r2.Memory
|
||||
eq = eq && r1.Headers.Equals(r2.Headers)
|
||||
eq = eq && r1.Type == r2.Type
|
||||
eq = eq && r1.Format == r2.Format
|
||||
eq = eq && r1.Timeout == r2.Timeout
|
||||
eq = eq && r1.IdleTimeout == r2.IdleTimeout
|
||||
eq = eq && r1.Config.Equals(r2.Config)
|
||||
// NOTE: datastore tests are not very fun to write with timestamp checks,
|
||||
// and these are not values the user may set so we kind of don't care.
|
||||
//eq = eq && time.Time(r1.CreatedAt).Equal(time.Time(r2.CreatedAt))
|
||||
//eq = eq && time.Time(r2.UpdatedAt).Equal(time.Time(r2.UpdatedAt))
|
||||
return eq
|
||||
}
|
||||
|
||||
// Update updates fields in r with non-zero field values from new, and sets
|
||||
// updated_at if any of the fields change. 0-length slice Header values, and
|
||||
// empty-string Config values trigger removal of map entry.
|
||||
func (r *Route) Update(new *Route) {
|
||||
original := r.Clone()
|
||||
|
||||
if new.Image != "" {
|
||||
r.Image = new.Image
|
||||
}
|
||||
@@ -173,6 +222,10 @@ func (r *Route) Update(new *Route) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !r.Equals(original) {
|
||||
r.UpdatedAt = strfmt.DateTime(time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
type RouteFilter struct {
|
||||
|
||||
Reference in New Issue
Block a user