github.com/avenga/couper@v1.12.2/config/configload/endpoint_sequence.go (about)

     1  package configload
     2  
     3  import (
     4  	"sort"
     5  
     6  	"github.com/hashicorp/hcl/v2"
     7  	"github.com/hashicorp/hcl/v2/hclsyntax"
     8  
     9  	"github.com/avenga/couper/config"
    10  	"github.com/avenga/couper/config/body"
    11  	srvpkg "github.com/avenga/couper/config/runtime/server"
    12  	"github.com/avenga/couper/config/sequence"
    13  	"github.com/avenga/couper/eval/variables"
    14  )
    15  
    16  // buildSequences collects possible dependencies from 'backend_responses' variable.
    17  func buildSequences(names map[string]*hclsyntax.Body, endpoint *config.Endpoint) (err error) {
    18  	sequences := map[string]*sequence.Item{}
    19  
    20  	defer func() {
    21  		if rc := recover(); rc != nil {
    22  			err = rc.(error)
    23  		}
    24  	}()
    25  
    26  	var sortedNames sort.StringSlice
    27  	for name := range names {
    28  		sortedNames = append(sortedNames, name)
    29  	}
    30  	sortedNames.Sort()
    31  
    32  	for _, name := range sortedNames {
    33  		b := names[name]
    34  		refs := responseReferences(b)
    35  
    36  		if len(refs) == 0 {
    37  			continue
    38  		}
    39  
    40  		seq, exist := sequences[name]
    41  		if !exist {
    42  			seq = &sequence.Item{Name: name, BodyRange: getRange(b)}
    43  			sequences[name] = seq
    44  		}
    45  
    46  		for _, r := range refs {
    47  			ref, ok := sequences[r]
    48  			if !ok {
    49  				ref = &sequence.Item{Name: r, BodyRange: getRange(b)}
    50  				sequences[r] = ref
    51  			}
    52  			// Do not add ourselves
    53  			// Use case: modify response headers with current response
    54  			if seq != ref {
    55  				seq.Add(ref)
    56  			}
    57  		}
    58  	}
    59  
    60  	sortedSequences := srvpkg.SortDefault(sequences)
    61  	for _, name := range sortedSequences {
    62  		s := sequences[name]
    63  		if !s.HasParent() {
    64  			endpoint.Sequences = append(endpoint.Sequences, s)
    65  		}
    66  	}
    67  
    68  	return err
    69  }
    70  
    71  func responseReferences(b *hclsyntax.Body) []string {
    72  	var result []string
    73  	unique := map[string]struct{}{}
    74  
    75  	for _, expr := range body.CollectExpressions(b) {
    76  		for _, traversal := range expr.Variables() {
    77  			if traversal.RootName() != variables.BackendResponses || len(traversal) < 2 {
    78  				continue
    79  			}
    80  
    81  			tr, ok := traversal[1].(hcl.TraverseAttr)
    82  			if !ok {
    83  				continue
    84  			}
    85  
    86  			if _, ok = unique[tr.Name]; !ok {
    87  				unique[tr.Name] = struct{}{}
    88  				result = append(result, tr.Name)
    89  			}
    90  		}
    91  	}
    92  
    93  	return result
    94  }