github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/generators/client/client.go (about) 1 // Copyright (c) 2019-2021, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package client 6 7 import ( 8 "bytes" 9 "encoding/json" 10 "fmt" 11 "os" 12 "path" 13 "path/filepath" 14 "strings" 15 "text/template" 16 17 "github.com/choria-io/go-choria/internal/fs" 18 addl "github.com/choria-io/go-choria/providers/agent/mcorpc/ddl/agent" 19 "github.com/choria-io/go-choria/providers/agent/mcorpc/ddl/common" 20 21 "github.com/sirupsen/logrus" 22 "golang.org/x/tools/imports" 23 ) 24 25 type Generator struct { 26 agent *agent 27 28 DDLFile string 29 OutDir string 30 PackageName string 31 } 32 33 type agent struct { 34 Package string 35 DDL *addl.DDL 36 RawDDL string // raw text of the JSON DDL file 37 } 38 39 func (a *agent) ActionRequiredInputs(act string) map[string]*common.InputItem { 40 inputs := make(map[string]*common.InputItem) 41 42 for _, act := range a.DDL.Actions { 43 for name, input := range act.Input { 44 if !input.Optional { 45 inputs[name] = input 46 } 47 } 48 } 49 50 return inputs 51 } 52 53 func (g *Generator) writeActions() error { 54 code, err := fs.FS.ReadFile("client/action.templ") 55 if err != nil { 56 return err 57 } 58 59 type action struct { 60 Agent *agent 61 Package string 62 AgentName string 63 ActionName string 64 ActionDescription string 65 OutputNames []string 66 InputNames []string 67 RequiredInputs map[string]*common.InputItem 68 OptionalInputs map[string]*common.InputItem 69 Outputs map[string]*common.OutputItem 70 } 71 72 for _, actname := range g.agent.DDL.ActionNames() { 73 actint, err := g.agent.DDL.ActionInterface(actname) 74 if err != nil { 75 return err 76 } 77 78 outfile := filepath.Join(g.OutDir, fmt.Sprintf("action_%s.go", actint.Name)) 79 logrus.Infof("Writing %s for action %s", outfile, actint.Name) 80 81 out, err := os.Create(outfile) 82 if err != nil { 83 return err 84 } 85 defer out.Close() 86 87 act := &action{ 88 Agent: g.agent, 89 Package: g.agent.Package, 90 AgentName: g.agent.DDL.Metadata.Name, 91 ActionName: actint.Name, 92 ActionDescription: actint.Description, 93 InputNames: actint.InputNames(), 94 OutputNames: actint.OutputNames(), 95 RequiredInputs: g.optionalInputSelect(actint, false), 96 OptionalInputs: g.optionalInputSelect(actint, true), 97 Outputs: actint.Output, 98 } 99 100 tpl := template.Must(template.New(actint.Name).Funcs(g.funcMap()).Parse(string(code))) 101 err = tpl.Execute(out, act) 102 if err != nil { 103 return err 104 } 105 106 err = FormatGoSource(outfile) 107 if err != nil { 108 return err 109 } 110 } 111 112 ddlPath := filepath.Join(g.OutDir, "ddl.json") 113 cDDL := bytes.NewBuffer([]byte{}) 114 json.Compact(cDDL, []byte(g.agent.RawDDL)) 115 logrus.Infof("Writing %s", ddlPath) 116 err = os.WriteFile(filepath.Join(g.OutDir, "ddl.json"), cDDL.Bytes(), 0644) 117 if err != nil { 118 return err 119 } 120 121 return nil 122 } 123 124 func (g *Generator) writeBasics() error { 125 dir, err := fs.FS.ReadDir("client") 126 if err != nil { 127 return err 128 } 129 130 for _, file := range dir { 131 t := strings.TrimSuffix(filepath.Base(file.Name()), filepath.Ext(file.Name())) 132 if t == "action" { 133 continue 134 } 135 136 outfile := path.Join(g.OutDir, t+".go") 137 logrus.Infof("Writing %s", outfile) 138 out, err := os.Create(outfile) 139 if err != nil { 140 return err 141 } 142 143 code, err := fs.FS.ReadFile(filepath.Join("client", file.Name())) 144 if err != nil { 145 return err 146 } 147 148 tpl := template.Must(template.New(t).Funcs(g.funcMap()).Parse(string(code))) 149 150 err = tpl.Execute(out, g.agent) 151 if err != nil { 152 return err 153 } 154 155 err = FormatGoSource(outfile) 156 if err != nil { 157 return err 158 } 159 } 160 161 return nil 162 } 163 164 func FormatGoSource(f string) error { 165 bs, err := os.ReadFile(f) 166 if err != nil { 167 return err 168 } 169 opt := imports.Options{ 170 Comments: true, 171 FormatOnly: true, 172 } 173 bs, err = imports.Process(f, bs, &opt) 174 if err != nil { 175 return err 176 } 177 return os.WriteFile(f, bs, os.ModePerm) 178 } 179 180 func (g *Generator) GenerateClient() error { 181 var err error 182 183 g.agent = &agent{} 184 g.agent.DDL, err = addl.New(g.DDLFile) 185 if err != nil { 186 return err 187 } 188 189 if g.agent.DDL == nil { 190 return fmt.Errorf("could not find any DDL") 191 } 192 193 raw, err := os.ReadFile(g.DDLFile) 194 if err != nil { 195 return err 196 } 197 198 g.agent.RawDDL = string(raw) 199 g.agent.Package = g.PackageName 200 201 if g.PackageName == "" { 202 g.agent.Package = strings.ToLower(g.agent.DDL.Metadata.Name) + "client" 203 } 204 205 logrus.Infof("Writing Choria Client for Agent %s Version %s to %s", g.agent.DDL.Metadata.Name, g.agent.DDL.Metadata.Version, g.OutDir) 206 err = g.writeActions() 207 if err != nil { 208 return err 209 } 210 211 err = g.writeBasics() 212 if err != nil { 213 return err 214 } 215 216 return nil 217 } 218 219 func (g *Generator) optionalInputSelect(action *addl.Action, opt bool) map[string]*common.InputItem { 220 inputs := make(map[string]*common.InputItem) 221 222 for name, act := range action.Input { 223 if act.Optional == opt { 224 inputs[name] = act 225 } 226 } 227 228 return inputs 229 }