Skip to content

Commit 7d9ac96

Browse files
committed
Added update_time to listapps output
1 parent 509083f commit 7d9ac96

File tree

7 files changed

+117
-12
lines changed

7 files changed

+117
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1010
### Added
1111

1212
- Support for passing `X-Openrun-Rbac-Enabled` header to the proxied downstream service. Value is true if RBAC is enabled for app, false otherwise.
13+
- Added update_time field to listapps output, app listing will display that info
1314

1415
## [0.15.9] - 2025-10-22
1516

internal/metadata/metadata.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ func (m *Metadata) GetAppsForDomain(domain string) ([]string, error) {
392392

393393
rows, err := stmt.Query(domain)
394394
if err != nil {
395-
return nil, fmt.Errorf("error querying apps: %w", err)
395+
return nil, fmt.Errorf("error querying domain apps: %w", err)
396396
}
397397

398398
paths := make([]string, 0)
@@ -401,7 +401,7 @@ func (m *Metadata) GetAppsForDomain(domain string) ([]string, error) {
401401
var path string
402402
err = rows.Scan(&path)
403403
if err != nil {
404-
return nil, fmt.Errorf("error querying apps: %w", err)
404+
return nil, fmt.Errorf("error scanning domain app: %w", err)
405405
}
406406
paths = append(paths, path)
407407
}
@@ -413,7 +413,7 @@ func (m *Metadata) GetAppsForDomain(domain string) ([]string, error) {
413413
}
414414

415415
func (m *Metadata) GetAllApps(includeInternal bool) ([]types.AppInfo, error) {
416-
sqlStr := `select domain, path, is_dev, id, main_app, settings, metadata, source_url from apps`
416+
sqlStr := `select domain, path, is_dev, id, main_app, settings, metadata, source_url, update_time from apps`
417417
if !includeInternal {
418418
sqlStr += ` where main_app = ''`
419419
}
@@ -427,17 +427,18 @@ func (m *Metadata) GetAllApps(includeInternal bool) ([]types.AppInfo, error) {
427427

428428
rows, err := stmt.Query()
429429
if err != nil {
430-
return nil, fmt.Errorf("error querying apps: %w", err)
430+
return nil, fmt.Errorf("error querying all apps: %w", err)
431431
}
432432
apps := make([]types.AppInfo, 0)
433433
defer rows.Close() //nolint:errcheck
434434
for rows.Next() {
435435
var path, domain, id, mainApp, sourceUrl string
436436
var isDev bool
437437
var settingsStr, metadataStr sql.NullString
438-
err = rows.Scan(&domain, &path, &isDev, &id, &mainApp, &settingsStr, &metadataStr, &sourceUrl)
438+
var updateTime *time.Time
439+
err = rows.Scan(&domain, &path, &isDev, &id, &mainApp, &settingsStr, &metadataStr, &sourceUrl, &updateTime)
439440
if err != nil {
440-
return nil, fmt.Errorf("error querying apps: %w", err)
441+
return nil, fmt.Errorf("error querying next app: %w", err)
441442
}
442443

443444
var metadata types.AppMetadata
@@ -460,7 +461,7 @@ func (m *Metadata) GetAllApps(includeInternal bool) ([]types.AppInfo, error) {
460461
apps = append(apps, types.CreateAppInfo(types.AppId(id), metadata.Name, path, domain, isDev,
461462
types.AppId(mainApp), settings.AuthnType, sourceUrl, metadata.Spec,
462463
metadata.VersionMetadata.Version, metadata.VersionMetadata.GitCommit, metadata.VersionMetadata.GitMessage,
463-
metadata.VersionMetadata.GitBranch, types.StripQuotes(metadata.AppConfig["star_base"])))
464+
metadata.VersionMetadata.GitBranch, types.StripQuotes(metadata.AppConfig["star_base"]), *updateTime))
464465
}
465466
if closeErr := rows.Close(); closeErr != nil {
466467
return nil, fmt.Errorf("error closing rows: %w", closeErr)
@@ -478,7 +479,7 @@ func (m *Metadata) GetLinkedApps(ctx context.Context, tx types.Transaction, main
478479

479480
rows, err := stmt.Query(mainAppId)
480481
if err != nil {
481-
return nil, fmt.Errorf("error querying apps: %w", err)
482+
return nil, fmt.Errorf("error querying linked apps: %w", err)
482483
}
483484
apps := make([]*types.AppEntry, 0)
484485
defer rows.Close() //nolint:errcheck
@@ -531,7 +532,7 @@ func (m *Metadata) UpdateAppMetadata(ctx context.Context, tx types.Transaction,
531532
return fmt.Errorf("error marshalling metadata: %w", err)
532533
}
533534

534-
_, err = tx.ExecContext(ctx, system.RebindQuery(m.dbType, `UPDATE apps set metadata = ? where path = ? and domain = ?`), string(metadataJson), app.Path, app.Domain)
535+
_, err = tx.ExecContext(ctx, system.RebindQuery(m.dbType, `UPDATE apps set metadata = ?, update_time = `+system.FuncNow(m.dbType)+` where path = ? and domain = ?`), string(metadataJson), app.Path, app.Domain)
535536
if err != nil {
536537
return fmt.Errorf("error updating app metadata: %w", err)
537538
}
@@ -552,7 +553,7 @@ func (m *Metadata) UpdateAppSettings(ctx context.Context, tx types.Transaction,
552553
return fmt.Errorf("error marshalling settings: %w", err)
553554
}
554555

555-
_, err = tx.ExecContext(ctx, system.RebindQuery(m.dbType, `UPDATE apps set settings = ? where path = ? and domain = ?`), string(settingsJson), app.Path, app.Domain)
556+
_, err = tx.ExecContext(ctx, system.RebindQuery(m.dbType, `UPDATE apps set settings = ?, update_time = `+system.FuncNow(m.dbType)+` where path = ? and domain = ?`), string(settingsJson), app.Path, app.Domain)
556557
if err != nil {
557558
return fmt.Errorf("error updating app settings: %w", err)
558559
}

internal/server/openrun_plugin.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ func (c *openrunPlugin) listAppsImpl(thread *starlark.Thread, _ *starlark.Builti
178178
v.SetKey(starlark.String("git_sha"), starlark.String(app.GitSha))
179179
v.SetKey(starlark.String("git_message"), starlark.String(app.GitMessage))
180180
v.SetKey(starlark.String("git_branch"), starlark.String(app.Branch))
181+
v.SetKey(starlark.String("update_age"), starlark.String(system.HumanDuration(time.Since(app.UpdateTime))))
182+
v.SetKey(starlark.String("update_time"), starlark.String(app.UpdateTime.Format(time.RFC3339)))
181183

182184
ret.Append(&v)
183185
}

internal/system/source.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
package system
55

6-
import "strings"
6+
import (
7+
"strings"
8+
)
79

810
// IsGit returns true if the sourceURL is a git URL
911
func IsGit(url string) bool {

internal/system/time.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) ClaceIO, LLC
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package system
5+
6+
import (
7+
"fmt"
8+
"strings"
9+
"time"
10+
)
11+
12+
// HumanDuration returns a human readable duration string
13+
func HumanDuration(d time.Duration) string {
14+
if d >= 0 && d < time.Second {
15+
return "recently"
16+
}
17+
if d < 0 {
18+
return "-" + HumanDuration(-d)
19+
}
20+
21+
// Round to whole seconds so we don't show sub-second noise.
22+
if d < time.Hour {
23+
d = d.Round(time.Second)
24+
} else if d < 6*time.Hour {
25+
d = d.Round(time.Minute)
26+
} else {
27+
d = d.Round(time.Hour)
28+
}
29+
30+
days := d / (24 * time.Hour)
31+
d -= days * 24 * time.Hour
32+
33+
hours := d / time.Hour
34+
d -= hours * time.Hour
35+
36+
minutes := d / time.Minute
37+
d -= minutes * time.Minute
38+
39+
seconds := d / time.Second
40+
41+
var parts []string
42+
add := func(n int64, singular, plural string) {
43+
if n == 0 {
44+
return
45+
}
46+
if n == 1 {
47+
parts = append(parts, fmt.Sprintf("%d %s", n, singular))
48+
} else {
49+
parts = append(parts, fmt.Sprintf("%d %s", n, plural))
50+
}
51+
}
52+
53+
add(int64(days), "day", "days")
54+
add(int64(hours), "hour", "hours")
55+
add(int64(minutes), "minute", "minutes")
56+
add(int64(seconds), "second", "seconds")
57+
58+
return strings.Join(parts, " ") + " ago"
59+
}

internal/system/time_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package system
2+
3+
import (
4+
"testing"
5+
"time"
6+
)
7+
8+
func TestHumanDuration(t *testing.T) {
9+
cases := []struct {
10+
name string
11+
d time.Duration
12+
want string
13+
}{
14+
{"recent_zero", 0, "recently"},
15+
{"recent_subsec", 500 * time.Millisecond, "recently"},
16+
{"one_second", 1 * time.Second, "1 second ago"},
17+
{"multi_seconds", 42 * time.Second, "42 seconds ago"},
18+
{"one_minute", 1 * time.Minute, "1 minute ago"},
19+
{"minute_and_seconds", 1*time.Minute + 1*time.Second, "1 minute 1 second ago"},
20+
{"hour_rounds_minute_precision_down", 1*time.Hour + 1*time.Second, "1 hour ago"},
21+
{"one_hour_one_minute", 1*time.Hour + 1*time.Minute + 1*time.Second, "1 hour 1 minute ago"},
22+
{"under_six_hours_round_up_to_hour", 5*time.Hour + 59*time.Minute + 31*time.Second, "6 hours ago"},
23+
{"six_hours_round_to_hour", 6*time.Hour + 1*time.Minute, "6 hours ago"},
24+
{"one_day", 24 * time.Hour, "1 day ago"},
25+
{"days_and_hours", 49 * time.Hour, "2 days 1 hour ago"},
26+
{"negative_seconds", -30 * time.Second, "-30 seconds ago"},
27+
{"negative_recently", -500 * time.Millisecond, "-recently"},
28+
}
29+
30+
for _, tc := range cases {
31+
t.Run(tc.name, func(t *testing.T) {
32+
got := HumanDuration(tc.d)
33+
if got != tc.want {
34+
t.Fatalf("HumanDuration(%v) = %q, want %q", tc.d, got, tc.want)
35+
}
36+
})
37+
}
38+
}

internal/types/types.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ type AppInfo struct {
306306
GitMessage string
307307
Branch string
308308
StarBase string
309+
UpdateTime time.Time
309310
}
310311

311312
func CreateAppPathDomain(path, domain string) AppPathDomain {
@@ -317,7 +318,7 @@ func CreateAppPathDomain(path, domain string) AppPathDomain {
317318

318319
func CreateAppInfo(id AppId, name, path, domain string, isDev bool, mainApp AppId,
319320
auth AppAuthnType, sourceUrl string, spec AppSpec,
320-
version int, gitSha, gitMessage, branch, starBase string) AppInfo {
321+
version int, gitSha, gitMessage, branch, starBase string, updatedAt time.Time) AppInfo {
321322
return AppInfo{
322323
AppPathDomain: AppPathDomain{
323324
Path: path,
@@ -335,6 +336,7 @@ func CreateAppInfo(id AppId, name, path, domain string, isDev bool, mainApp AppI
335336
GitMessage: gitMessage,
336337
Branch: branch,
337338
StarBase: starBase,
339+
UpdateTime: updatedAt,
338340
}
339341
}
340342

0 commit comments

Comments
 (0)