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  }