github.com/GoogleCloudPlatform/testgrid@v0.0.174/util/links.go (about) 1 /* 2 Copyright 2023 The TestGrid Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package util 18 19 import ( 20 "net/url" 21 "regexp" 22 "strings" 23 24 configpb "github.com/GoogleCloudPlatform/testgrid/pb/config" 25 ) 26 27 // Utility functions to correctly parse link templates. 28 29 var ( 30 // All tokens. 31 tokenRe = regexp.MustCompile(`<[^<>]*>`) 32 33 // Find the raw encode token: `<encode:[text]>`. 34 encodeTokenRe = regexp.MustCompile(`<encode:[^<>]*>`) 35 encodeRe = regexp.MustCompile(`<encode:([^<>]*)>`) 36 37 // Substitute a particular custom column value for this token. 38 // TODO: Handle start/end columns (for custom columns and build IDs). 39 CustomColumnRe = regexp.MustCompile(`<custom-\d+>`) 40 41 // Simple tokens. 42 TestStatus = "<test-status>" 43 TestID = "<test-id>" 44 WorkflowID = "<workflow-id>" 45 WorkflowName = "<workflow-name>" 46 TestName = "<test-name>" 47 DisplayName = "<display-name>" 48 MethodName = "<method-name>" 49 TestURL = "<test-url>" 50 BuildID = "<build-id>" 51 BugComponent = "<bug-component>" 52 Owner = "<owner>" 53 Cc = "<cc>" 54 GcsPrefix = "<gcs-prefix>" 55 Environment = "<environment>" // dashboard tab name 56 ResultsExplorer = "<results-explorer>" 57 CodeSearchPath = "<cs-path>" 58 ) 59 60 // Tokens returns the unique list of all Tokens that could be replaced in this template. 61 func Tokens(template *configpb.LinkTemplate) []string { 62 allTokens := map[string]bool{} 63 for _, token := range tokenRe.FindAllString(template.GetUrl(), -1) { 64 allTokens[token] = true 65 } 66 for _, option := range template.GetOptions() { 67 for _, token := range tokenRe.FindAllString(option.GetKey(), -1) { 68 allTokens[token] = true 69 } 70 for _, token := range tokenRe.FindAllString(option.GetValue(), -1) { 71 allTokens[token] = true 72 } 73 } 74 var keys []string 75 for k := range allTokens { 76 keys = append(keys, k) 77 } 78 return keys 79 } 80 81 // ExpandTemplate expands the given link template with given parameters. 82 func ExpandTemplate(template *configpb.LinkTemplate, parameters map[string]string) (string, error) { 83 rawUrl := expandTemplateString(template.GetUrl(), parameters, false) 84 baseUrl, err := url.Parse(rawUrl) 85 if err != nil { 86 return "", err 87 } 88 options := url.Values{} 89 for _, optionTemplate := range template.GetOptions() { 90 k := expandTemplateString(optionTemplate.GetKey(), parameters, true) 91 v := expandTemplateString(optionTemplate.GetValue(), parameters, true) 92 options.Add(k, v) 93 } 94 baseUrl.RawQuery = options.Encode() 95 return baseUrl.String(), nil 96 } 97 98 // expandTemplateString substitutes given parameters into the given link template. 99 func expandTemplateString(template string, parameters map[string]string, isQuery bool) string { 100 // TODO: Handle start/end tokens. 101 // Substitute parameters for simple tokens. 102 for token, value := range parameters { 103 template = strings.ReplaceAll(template, token, value) 104 } 105 escape := url.PathEscape 106 if isQuery { 107 // Don't encode here; options.Encode() will do that. 108 escape = func(s string) string { return s } 109 } 110 // Encode specified parameters. 111 return encodeTokenRe.ReplaceAllStringFunc( 112 template, 113 func(token string) string { 114 submatches := encodeRe.FindStringSubmatch(token) 115 if len(submatches) <= 1 { 116 return "" 117 } 118 return escape(submatches[1]) 119 }, 120 ) 121 }