fix(ui): Include application name in status badge (#17126)

* Added application name to badge

Signed-off-by: sshenoy6 <sonamkaup_shenoy@intuit.com>

* Rever svg change

Signed-off-by: sshenoy6 <sonamkaup_shenoy@intuit.com>

* Doc for disabling application name

Signed-off-by: sshenoy6 <sonamkaup_shenoy@intuit.com>

* Flag to not display application name

Signed-off-by: sshenoy6 <sonamkaup_shenoy@intuit.com>

* Added tests

Signed-off-by: sshenoy6 <sonamkaup_shenoy@intuit.com>

* Make no app name the default

Signed-off-by: sshenoy6 <sonamkaup_shenoy@intuit.com>

* Have enable app name as a query parameter

Signed-off-by: sshenoy6 <sonamkaup_shenoy@intuit.com>

* Have enable app name as a query parameter

Signed-off-by: sshenoy6 <sonamkaup_shenoy@intuit.com>

* argocd to original

Signed-off-by: sshenoy6 <sonamkaup_shenoy@intuit.com>

* Update docs/user-guide/status-badge.md

Signed-off-by: Dan Garfield <dan@codefresh.io>

Signed-off-by: Dan Garfield <dan@codefresh.io>

---------

Signed-off-by: sshenoy6 <sonamkaup_shenoy@intuit.com>
Signed-off-by: Dan Garfield <dan@codefresh.io>
Co-authored-by: sshenoy6 <sonamkaup_shenoy@intuit.com>
Co-authored-by: Dan Garfield <dan@codefresh.io>
This commit is contained in:
Sonam
2024-02-24 17:34:30 +05:30
committed by GitHub
parent 5bc1850aa1
commit 7fe1263300
6 changed files with 122 additions and 9 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -308,9 +308,9 @@ data:
# have either a permanent banner or a regular closeable banner, and NOT both. eg. A user can't dismiss a
# notification message (closeable) banner, to then immediately see a permanent banner.
# ui.bannerpermanent: "true"
# An option to specify the position of the banner, either the top or bottom of the page, or both. The valid values
# are: "top", "bottom" and "both". The default (if the option is not provided), is "top". If "both" is specified, then
# the content appears both at the top and the bottom of the page. Uncomment the following line to make the banner appear
# An option to specify the position of the banner, either the top or bottom of the page, or both. The valid values
# are: "top", "bottom" and "both". The default (if the option is not provided), is "top". If "both" is specified, then
# the content appears both at the top and the bottom of the page. Uncomment the following line to make the banner appear
# at the bottom of the page. Change the value as needed.
# ui.bannerposition: "bottom"
@@ -413,4 +413,4 @@ data:
# Mandatory if multiple services are specified.
cluster:
name: some-cluster
server: https://some-cluster
server: https://some-cluster

View File

@@ -9,7 +9,12 @@ To show this badge, use the following URL format `${argoCdBaseUrl}/api/badge?nam
The URLs for status image are available on application details page:
1. Navigate to application details page and click on 'Details' button.
1. Scroll down to 'Status Badge' section.
1. Select required template such as URL, Markdown etc.
2. Scroll down to 'Status Badge' section.
3. Select required template such as URL, Markdown etc.
for the status image URL in markdown, html, etc are available .
1. Copy the text and paste it into your README or website.
4. Copy the text and paste it into your README or website.
The application name may optionally be displayed in the status badge by adding the `?showAppName=true` query parameter.
For example, `${argoCdBaseUrl}/api/badge?name=${appName}&showAppName=true`.
To remove the application name from the badge, remove the query parameter from the URL or set it to `false`.

View File

@@ -42,10 +42,28 @@ var (
leftTextPattern = regexp.MustCompile(`id="leftText" [^>]*>([^<]*)`)
rightTextPattern = regexp.MustCompile(`id="rightText" [^>]*>([^<]*)`)
revisionTextPattern = regexp.MustCompile(`id="revisionText" [^>]*>([^<]*)`)
titleTextPattern = regexp.MustCompile(`id="titleText" [^>]*>([^<]*)`)
titleRectWidthPattern = regexp.MustCompile(`(id="titleRect" .* width=)("0")`)
rightRectWidthPattern = regexp.MustCompile(`(id="rightRect" .* width=)("\d*")`)
leftRectYCoodPattern = regexp.MustCompile(`(id="leftRect" .* y=)("\d*")`)
rightRectYCoodPattern = regexp.MustCompile(`(id="rightRect" .* y=)("\d*")`)
revisionRectYCoodPattern = regexp.MustCompile(`(id="revisionRect" .* y=)("\d*")`)
leftTextYCoodPattern = regexp.MustCompile(`(id="leftText" .* y=)("\d*")`)
rightTextYCoodPattern = regexp.MustCompile(`(id="rightText" .* y=)("\d*")`)
revisionTextYCoodPattern = regexp.MustCompile(`(id="revisionText" .* y=)("\d*")`)
svgHeightPattern = regexp.MustCompile(`^(<svg .* height=)("\d*")`)
logoYCoodPattern = regexp.MustCompile(`(<image .* y=)("\d*")`)
)
const (
svgWidthWithRevision = 192
svgWidthWithRevision = 192
svgWidthWithoutRevision = 131
svgHeightWithAppName = 40
badgeRowHeight = 20
statusRowYCoodWithAppName = 330
logoYCoodWithAppName = 22
leftRectWidth = 77
widthPerChar = 6
)
func replaceFirstGroupSubMatch(re *regexp.Regexp, str string, repl string) string {
@@ -71,9 +89,12 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
health := healthutil.HealthStatusUnknown
status := appv1.SyncStatusCodeUnknown
revision := ""
applicationName := ""
revisionEnabled := false
enabled := false
displayAppName := false
notFound := false
svgWidth := svgWidthWithoutRevision
if sets, err := h.settingsMgr.GetSettings(); err == nil {
enabled = sets.StatusBadgeEnabled
}
@@ -100,6 +121,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if app, err := h.appClientset.ArgoprojV1alpha1().Applications(reqNs).Get(context.Background(), name[0], v1.GetOptions{}); err == nil {
health = app.Status.Health.Status
status = app.Status.Sync.Status
applicationName = name[0]
if app.Status.OperationState != nil && app.Status.OperationState.SyncResult != nil {
revision = app.Status.OperationState.SyncResult.Revision
}
@@ -175,6 +197,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !notFound && revisionEnabled && revision != "" {
// Increase width of SVG and enable display of revision components
badge = svgWidthPattern.ReplaceAllString(badge, fmt.Sprintf(`<svg width="%d" $2`, svgWidthWithRevision))
svgWidth = svgWidthWithRevision
badge = displayNonePattern.ReplaceAllString(badge, `display="inline"`)
badge = revisionRectColorPattern.ReplaceAllString(badge, fmt.Sprintf(`id="revisionRect" fill="%s" $2`, rightColorString))
shortRevision := revision
@@ -184,6 +207,29 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
badge = replaceFirstGroupSubMatch(revisionTextPattern, badge, fmt.Sprintf("(%s)", shortRevision))
}
if showAppNameParam, ok := r.URL.Query()["showAppName"]; ok && enabled && strings.EqualFold(showAppNameParam[0], "true") {
displayAppName = true
}
if displayAppName && applicationName != "" {
titleRectWidth := len(applicationName) * widthPerChar
var longerWidth int = max(titleRectWidth, svgWidth)
rightRectWidth := longerWidth - leftRectWidth
fmt.Println(len(applicationName))
badge = titleRectWidthPattern.ReplaceAllString(badge, fmt.Sprintf(`$1"%d"`, longerWidth))
badge = rightRectWidthPattern.ReplaceAllString(badge, fmt.Sprintf(`$1"%d"`, rightRectWidth))
badge = replaceFirstGroupSubMatch(titleTextPattern, badge, applicationName)
badge = leftRectYCoodPattern.ReplaceAllString(badge, fmt.Sprintf(`$1"%d"`, badgeRowHeight))
badge = rightRectYCoodPattern.ReplaceAllString(badge, fmt.Sprintf(`$1"%d"`, badgeRowHeight))
badge = revisionRectYCoodPattern.ReplaceAllString(badge, fmt.Sprintf(`$1"%d"`, badgeRowHeight))
badge = leftTextYCoodPattern.ReplaceAllString(badge, fmt.Sprintf(`$1"%d"`, statusRowYCoodWithAppName))
badge = rightTextYCoodPattern.ReplaceAllString(badge, fmt.Sprintf(`$1"%d"`, statusRowYCoodWithAppName))
badge = revisionTextYCoodPattern.ReplaceAllString(badge, fmt.Sprintf(`$1"%d"`, statusRowYCoodWithAppName))
badge = svgHeightPattern.ReplaceAllString(badge, fmt.Sprintf(`$1"%d"`, svgHeightWithAppName))
badge = logoYCoodPattern.ReplaceAllString(badge, fmt.Sprintf(`$1"%d"`, logoYCoodWithAppName))
badge = svgWidthPattern.ReplaceAllString(badge, fmt.Sprintf(`<svg width="%d" $2`, longerWidth))
}
w.Header().Set("Content-Type", "image/svg+xml")
//Ask cache's to not cache the contents in order prevent the badge from becoming stale

View File

@@ -88,6 +88,7 @@ func TestHandlerFeatureIsEnabled(t *testing.T) {
assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
assert.NotContains(t, response, "test-app")
assert.NotContains(t, response, "(aa29b85)")
}
@@ -148,6 +149,7 @@ func TestHandlerFeatureProjectIsEnabled(t *testing.T) {
assert.Equal(t, toRGBString(tt.statusColor), rightRectColorPattern.FindStringSubmatch(response)[1])
assert.Equal(t, tt.health, leftTextPattern.FindStringSubmatch(response)[1])
assert.Equal(t, tt.status, rightTextPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "\"20\"", svgHeightPattern.FindStringSubmatch(response)[2])
}
}
}
@@ -170,6 +172,7 @@ func TestHandlerNamespacesIsEnabled(t *testing.T) {
assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
assert.NotContains(t, response, "test-app")
assert.NotContains(t, response, "(aa29b85)")
})
@@ -268,6 +271,7 @@ func TestHandlerFeatureIsEnabledRevisionIsEnabled(t *testing.T) {
assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
assert.NotContains(t, response, "test-app")
assert.Contains(t, response, "(aa29b85)")
}
@@ -291,6 +295,7 @@ func TestHandlerRevisionIsEnabledNoOperationState(t *testing.T) {
assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
assert.NotContains(t, response, "test-app")
assert.NotContains(t, response, "(aa29b85)")
}
@@ -331,4 +336,59 @@ func TestHandlerFeatureIsDisabled(t *testing.T) {
assert.Equal(t, toRGBString(Purple), rightRectColorPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "Unknown", leftTextPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "Unknown", rightTextPattern.FindStringSubmatch(response)[1])
assert.NotContains(t, response, "test-app")
assert.Equal(t, "\"20\"", svgHeightPattern.FindStringSubmatch(response)[2])
}
func TestHandlerApplicationNameInBadgeIsEnabled(t *testing.T) {
settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), "default")
handler := NewHandler(appclientset.NewSimpleClientset(&testApp), settingsMgr, "default", []string{})
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&showAppName=true", nil)
assert.NoError(t, err)
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
response := rr.Body.String()
assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
assert.NotContains(t, response, "(aa29b85)")
assert.Equal(t, "test-app", titleTextPattern.FindStringSubmatch(response)[1])
assert.Equal(t, fmt.Sprintf("\"%d\"", svgHeightWithAppName), svgHeightPattern.FindStringSubmatch(response)[2])
assert.Equal(t, fmt.Sprintf("\"%d\"", badgeRowHeight), leftRectYCoodPattern.FindStringSubmatch(response)[2])
assert.Equal(t, fmt.Sprintf("\"%d\"", badgeRowHeight), rightRectYCoodPattern.FindStringSubmatch(response)[2])
assert.Equal(t, fmt.Sprintf("\"%d\"", badgeRowHeight), revisionRectYCoodPattern.FindStringSubmatch(response)[2])
assert.Equal(t, fmt.Sprintf("\"%d\"", logoYCoodWithAppName), logoYCoodPattern.FindStringSubmatch(response)[2])
}
func TestHandlerApplicationNameInBadgeIsDisabled(t *testing.T) {
settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), "default")
handler := NewHandler(appclientset.NewSimpleClientset(&testApp), settingsMgr, "default", []string{})
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app", nil)
assert.NoError(t, err)
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
response := rr.Body.String()
assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
assert.Equal(t, "\"20\"", svgHeightPattern.FindStringSubmatch(response)[2])
assert.Equal(t, "\"0\"", leftRectYCoodPattern.FindStringSubmatch(response)[2])
assert.Equal(t, "\"0\"", rightRectYCoodPattern.FindStringSubmatch(response)[2])
assert.Equal(t, "\"0\"", revisionRectYCoodPattern.FindStringSubmatch(response)[2])
assert.Equal(t, "\"2\"", logoYCoodPattern.FindStringSubmatch(response)[2])
assert.NotContains(t, response, "test-app")
}

View File

@@ -19,7 +19,7 @@ export const BadgePanel = ({app, project, appNamespace, nsEnabled}: {app?: strin
let entityURL = '';
let alt = '';
if (app) {
badgeURL = `${root}api/badge?name=${app}&revision=true`;
badgeURL = `${root}api/badge?name=${app}&revision=true&showAppName=true`;
if (nsEnabled) {
badgeURL += `&namespace=${appNamespace}`;
}