github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/internal/fs/client/action.templ (about)

     1  {{ GeneratedWarning }}
     2  
     3  package {{ .Package }}
     4  
     5  import (
     6  	"context"
     7  	"encoding/json"
     8  	"fmt"
     9  	"sync"
    10  	"io"
    11  	"github.com/choria-io/go-choria/protocol"
    12  	rpcclient "github.com/choria-io/go-choria/providers/agent/mcorpc/client"
    13  	"github.com/choria-io/go-choria/providers/agent/mcorpc/replyfmt"
    14  	"github.com/choria-io/go-choria/providers/agent/mcorpc/ddl/agent"
    15  )
    16  
    17  // {{ .ActionName | SnakeToCamel }}Requester performs a RPC request to {{ .AgentName | ToLower }}#{{ .ActionName | ToLower }}
    18  type {{ .ActionName | SnakeToCamel }}Requester struct {
    19  	r    *requester
    20  	outc chan *{{ .ActionName | SnakeToCamel }}Output
    21  }
    22  
    23  // {{ .ActionName | SnakeToCamel }}Output is the output from the {{ .ActionName | ToLower }} action
    24  type {{ .ActionName | SnakeToCamel }}Output struct {
    25  	details *ResultDetails
    26  	reply   map[string]any
    27  }
    28  
    29  // {{ .ActionName | SnakeToCamel }}Result is the result from a {{ .ActionName | ToLower }} action
    30  type {{ .ActionName | SnakeToCamel }}Result struct {
    31  	ddl *agent.DDL
    32  	stats   *rpcclient.Stats
    33  	outputs []*{{ .ActionName | SnakeToCamel }}Output
    34  	rpcreplies []*replyfmt.RPCReply
    35  	mu sync.Mutex
    36  }
    37  
    38  func (d *{{ .ActionName | SnakeToCamel }}Result) RenderResults(w io.Writer, format RenderFormat, displayMode DisplayMode, verbose bool, silent bool, colorize bool, log Log) error {
    39  	d.mu.Lock()
    40  	defer d.mu.Unlock()
    41  
    42  	if d.stats == nil {
    43  		return fmt.Errorf("result stats is not set, result was not completed")
    44  	}
    45  
    46  	results := &replyfmt.RPCResults{
    47  		Agent:   d.stats.Agent(),
    48  		Action:  d.stats.Action(),
    49  		Replies: d.rpcreplies,
    50  		Stats:   d.stats,
    51  	}
    52  
    53  	addl, err := d.ddl.ActionInterface(d.stats.Action())
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	switch format {
    59  	case JSONFormat:
    60  		return results.RenderJSON(w, addl)
    61  	case TableFormat:
    62  		return results.RenderTable(w, addl)
    63  	case TXTFooter:
    64  		results.RenderTXTFooter(w, verbose)
    65  		return nil
    66  	default:
    67  		return results.RenderTXT(w, addl, verbose, silent, replyfmt.DisplayMode(displayMode), colorize, log)
    68  	}
    69  }
    70  
    71  // Stats is the rpc request stats
    72  func (d *{{ .ActionName | SnakeToCamel }}Result) Stats() Stats {
    73  	return d.stats
    74  }
    75  
    76  // ResultDetails is the details about the request
    77  func (d *{{ .ActionName | SnakeToCamel }}Output) ResultDetails() *ResultDetails {
    78  	return d.details
    79  }
    80  
    81  // HashMap is the raw output data
    82  func (d *{{ .ActionName | SnakeToCamel }}Output) HashMap() map[string]any {
    83  	return d.reply
    84  }
    85  
    86  // JSON is the JSON representation of the output data
    87  func (d *{{ .ActionName | SnakeToCamel }}Output) JSON() ([]byte, error) {
    88  	return json.Marshal(d.reply)
    89  }
    90  
    91  // Parse{{ .ActionName | SnakeToCamel }}Output parses the result value from the {{ .ActionName | SnakeToCamel }} action into target
    92  func (d *{{ $.ActionName | SnakeToCamel }}Output) Parse{{ .ActionName | SnakeToCamel }}Output(target any) error {
    93  	j, err := d.JSON()
    94  	if err != nil {
    95  		return fmt.Errorf("could not access payload: %s", err)
    96  	}
    97  
    98  	err = json.Unmarshal(j, target)
    99  	if err != nil {
   100  		return fmt.Errorf("could not unmarshal JSON payload: %s", err)
   101  	}
   102  
   103  	return nil
   104  }
   105  
   106  // Do performs the request
   107  func (d *{{ .ActionName | SnakeToCamel }}Requester) Do(ctx context.Context) (*{{ .ActionName | SnakeToCamel }}Result, error) {
   108  	dres := &{{ .ActionName | SnakeToCamel }}Result{ddl: d.r.client.ddl}
   109  
   110  	addl, err := dres.ddl.ActionInterface(d.r.action)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	handler := func(pr protocol.Reply, r *rpcclient.RPCReply) {
   116  		// filtered by expr filter
   117  		if r ==nil {
   118  			return
   119  		}
   120  
   121  		output := &{{ .ActionName | SnakeToCamel }}Output{
   122  			reply: make(map[string]any),
   123  			details: &ResultDetails{
   124  				sender:  pr.SenderID(),
   125  				code:    int(r.Statuscode),
   126  				message: r.Statusmsg,
   127  				ts:      pr.Time(),
   128  			},
   129  		}
   130  
   131  		addl.SetOutputDefaults(output.reply)
   132  
   133  		err := json.Unmarshal(r.Data, &output.reply)
   134  		if err != nil {
   135  			d.r.client.errorf("Could not decode reply from %s: %s", pr.SenderID(), err)
   136  		}
   137  
   138  		// caller wants a channel so we dont return a resultset too (lots of memory etc)
   139  		// this is unused now, no support for setting a channel
   140  		if d.outc != nil {
   141  			d.outc <- output
   142  			return
   143  		}
   144  
   145  		// else prepare our result set
   146  		dres.mu.Lock()
   147  		dres.outputs = append(dres.outputs, output)
   148  		dres.rpcreplies = append(dres.rpcreplies, &replyfmt.RPCReply{
   149  			Sender:   pr.SenderID(),
   150  			RPCReply: r,
   151  		})
   152  		dres.mu.Unlock()
   153  	}
   154  
   155  	res, err := d.r.do(ctx, handler)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	dres.stats = res
   161  
   162  	return dres, nil
   163  }
   164  
   165  // AllOutputs provide access to all outputs
   166  func (d *{{ .ActionName | SnakeToCamel }}Result) AllOutputs() []*{{ .ActionName | SnakeToCamel }}Output {
   167  	return d.outputs
   168  }
   169  
   170  // EachOutput iterates over all results received
   171  func (d *{{ .ActionName | SnakeToCamel }}Result) EachOutput(h func(r *{{ .ActionName | SnakeToCamel }}Output)) {
   172  	for _, resp := range d.outputs {
   173  		h(resp)
   174  	}
   175  }
   176  
   177  {{ range $name, $input := .OptionalInputs }}
   178  // {{ $name | SnakeToCamel }} is an optional input to the {{ $.ActionName | ToLower }} action
   179  //
   180  // Description: {{ $input.Description }}
   181  func (d *{{ $.ActionName | SnakeToCamel }}Requester) {{ $name | SnakeToCamel }}(v {{ ChoriaTypeToGoType $input.Type }}) *{{ $.ActionName | SnakeToCamel }}Requester {
   182  	d.r.args["{{ $name | ToLower }}"] = v
   183  
   184  	return d
   185  }
   186  {{ end }}
   187  {{ range $name, $output := .Outputs }}
   188  {{- $return_type := $output.Type | ChoriaTypeToGoType -}}
   189  // {{ $name | SnakeToCamel }} is the value of the {{ $name }} output
   190  //
   191  // Description: {{ $output.Description }}
   192  func (d *{{ $.ActionName | SnakeToCamel }}Output) {{ $name | SnakeToCamel }}() {{ $return_type }} {
   193  {{- if eq "any" $return_type  -}}
   194  	val, ok := d.reply["{{ $name }}"]
   195  	if !ok || val == nil {
   196  		// we have to avoid returning nil.(any)
   197  		return nil
   198  	}
   199  {{- else -}}
   200  	val := d.reply["{{ $name }}"]
   201  {{ end }}
   202  
   203  {{ if eq "any" $return_type  -}}
   204      	return val
   205  {{ else -}}
   206  	return {{ ChoriaTypeToValOfType $output.Type }}
   207  {{ end }}
   208  }
   209  {{ end }}