github.com/r2d2-ai/cli@v1.20.0/api/create.go (about) 1 package api 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "strings" 10 11 "github.com/r2d2-ai/cli/common" 12 "github.com/r2d2-ai/cli/util" 13 ) 14 15 var fileSampleEngineMain = filepath.Join("examples", "engine", "main.go") 16 17 func CreateProject(basePath, appName, appCfgPath, coreVersion string) (common.AppProject, error) { 18 19 var err error 20 var appJson string 21 22 if appCfgPath != "" { 23 24 if util.IsRemote(appCfgPath) { 25 26 appJson, err = util.LoadRemoteFile(appCfgPath) 27 if err != nil { 28 return nil, fmt.Errorf("unable to load remote app file '%s' - %s", appCfgPath, err.Error()) 29 } 30 } else { 31 appJson, err = util.LoadLocalFile(appCfgPath) 32 if err != nil { 33 return nil, fmt.Errorf("unable to load app file '%s' - %s", appCfgPath, err.Error()) 34 } 35 } 36 } else { 37 if len(appName) == 0 { 38 return nil, fmt.Errorf("app name not specified") 39 } 40 } 41 42 appName, err = getAppName(appName, appJson) 43 if err != nil { 44 return nil, err 45 } 46 47 fmt.Printf("Creating Flogo App: %s\n", appName) 48 49 appDir, err := createAppDirectory(basePath, appName) 50 if err != nil { 51 return nil, err 52 } 53 54 srcDir := filepath.Join(appDir, "src") 55 dm := util.NewDepManager(srcDir) 56 57 if Verbose() { 58 fmt.Printf("Setting up app directory: %s\n", appDir) 59 } 60 61 err = setupAppDirectory(dm, appDir, coreVersion) 62 if err != nil { 63 return nil, err 64 } 65 66 if Verbose() { 67 if appJson == "" { 68 fmt.Println("Adding sample flogo.json") 69 } 70 } 71 err = createAppJson(dm, appDir, appName, appJson) 72 if err != nil { 73 return nil, err 74 } 75 76 err = createMain(dm, appDir) 77 if err != nil { 78 return nil, err 79 } 80 81 project := NewAppProject(appDir) 82 83 if Verbose() { 84 fmt.Println("Importing Dependencies...") 85 } 86 87 err = importDependencies(project) 88 if err != nil { 89 return nil, err 90 } 91 92 if Verbose() { 93 fmt.Printf("Created App: %s\n", appName) 94 } 95 96 return project, nil 97 } 98 99 // createAppDirectory creates the flogo app directory 100 func createAppDirectory(basePath, appName string) (string, error) { 101 102 var err error 103 104 if basePath == "." { 105 basePath, err = os.Getwd() 106 if err != nil { 107 return "", err 108 } 109 } 110 111 appPath := filepath.Join(basePath, appName) 112 err = os.Mkdir(appPath, os.ModePerm) 113 if err != nil { 114 return "", err 115 } 116 117 return appPath, nil 118 } 119 120 //setupAppDirectory sets up the flogo app directory 121 func setupAppDirectory(dm util.DepManager, appPath, coreVersion string) error { 122 123 err := os.Mkdir(filepath.Join(appPath, dirBin), os.ModePerm) 124 if err != nil { 125 return err 126 } 127 128 srcDir := filepath.Join(appPath, dirSrc) 129 err = os.Mkdir(srcDir, os.ModePerm) 130 if err != nil { 131 return err 132 } 133 134 _, err = os.Create(filepath.Join(srcDir, fileImportsGo)) 135 if err != nil { 136 return err 137 } 138 err = ioutil.WriteFile(filepath.Join(srcDir, fileImportsGo), []byte("package main\n"), 0644) 139 if err != nil { 140 return err 141 } 142 143 err = dm.Init() 144 if err != nil { 145 return err 146 } 147 148 flogoCoreImport := util.NewFlogoImport(flogoCoreRepo, "", coreVersion, "") 149 150 //todo get the actual version installed from the go.mod file 151 if coreVersion == "" { 152 fmt.Printf("Installing: %s@latest\n", flogoCoreImport.CanonicalImport()) 153 } else { 154 fmt.Printf("Installing: %s\n", flogoCoreImport.CanonicalImport()) 155 } 156 157 // add & fetch the core library 158 err = dm.AddDependency(flogoCoreImport) 159 if err != nil { 160 return err 161 } 162 163 return nil 164 } 165 166 // createAppJson create the flogo app json 167 func createAppJson(dm util.DepManager, appDir, appName, appJson string) error { 168 169 updatedJson, err := getAndUpdateAppJson(dm, appName, appJson) 170 if err != nil { 171 return err 172 } 173 174 err = ioutil.WriteFile(filepath.Join(appDir, fileFlogoJson), []byte(updatedJson), 0644) 175 if err != nil { 176 return err 177 } 178 179 return nil 180 } 181 182 // importDependencies import all dependencies 183 func importDependencies(project common.AppProject) error { 184 185 ai, err := util.GetAppImports(filepath.Join(project.Dir(), fileFlogoJson), project.DepManager(), true) 186 if err != nil { 187 return err 188 } 189 190 imports := ai.GetAllImports() 191 192 if len(imports) == 0 { 193 return nil 194 } 195 196 err = project.AddImports(true, false, imports...) 197 if err != nil { 198 return err 199 } 200 201 legacySupportRequired := false 202 203 for _, details := range ai.GetAllImportDetails() { 204 205 path, err := project.GetPath(details.Imp) 206 if err != nil { 207 return err 208 } 209 210 desc, err := util.GetContribDescriptor(path) 211 212 if err != nil { 213 return err 214 } 215 216 if desc != nil { 217 218 cType := desc.GetContribType() 219 if desc.IsLegacy { 220 legacySupportRequired = true 221 cType = "legacy " + desc.GetContribType() 222 err := CreateLegacyMetadata(path, desc.GetContribType(), details.Imp.GoImportPath()) 223 if err != nil { 224 return err 225 } 226 } 227 228 fmt.Printf("Installed %s: %s\n", cType, details.Imp) 229 //instStr := fmt.Sprintf("Installed %s:", cType) 230 //fmt.Printf("%-20s %s\n", instStr, imp) 231 } 232 } 233 234 if legacySupportRequired { 235 err := InstallLegacySupport(project) 236 return err 237 } 238 239 return nil 240 } 241 242 func createMain(dm util.DepManager, appDir string) error { 243 244 flogoCoreImport, err := util.NewFlogoImportFromPath(flogoCoreRepo) 245 if err != nil { 246 return err 247 } 248 249 corePath, err := dm.GetPath(flogoCoreImport) 250 if err != nil { 251 return err 252 } 253 254 bytes, err := ioutil.ReadFile(filepath.Join(corePath, fileSampleEngineMain)) 255 if err != nil { 256 return err 257 } 258 259 err = ioutil.WriteFile(filepath.Join(appDir, dirSrc, fileMainGo), bytes, 0644) 260 if err != nil { 261 return err 262 } 263 264 return nil 265 } 266 267 func getAndUpdateAppJson(dm util.DepManager, appName, appJson string) (string, error) { 268 269 if len(appJson) == 0 { 270 appJson = emptyFlogoJson 271 } 272 273 descriptor, err := util.ParseAppDescriptor(appJson) 274 if err != nil { 275 return "", err 276 } 277 278 if appName != "" { 279 // override the application name 280 281 altJson := strings.Replace(appJson, `"`+descriptor.Name+`"`, `"`+appName+`"`, 1) 282 altDescriptor, err := util.ParseAppDescriptor(altJson) 283 284 //see if we can get away with simple replace so we don't reorder the existing json 285 if err == nil && altDescriptor.Name == appName { 286 appJson = altJson 287 } else { 288 //simple replace didn't work so we have to unmarshal & re-marshal the supplied json 289 var appObj map[string]interface{} 290 err := json.Unmarshal([]byte(appJson), &appObj) 291 if err != nil { 292 return "", err 293 } 294 295 appObj["name"] = appName 296 297 updApp, err := json.MarshalIndent(appObj, "", " ") 298 if err != nil { 299 return "", err 300 } 301 appJson = string(updApp) 302 } 303 304 descriptor.Name = appName 305 } else { 306 appName = descriptor.Name 307 } 308 309 return appJson, nil 310 } 311 312 func getAppName(appName, appJson string) (string, error) { 313 314 if appJson != "" && appName == "" { 315 descriptor, err := util.ParseAppDescriptor(appJson) 316 if err != nil { 317 return "", err 318 } 319 320 return descriptor.Name, nil 321 } 322 323 return appName, nil 324 } 325 func GetTempDir() (string, error) { 326 327 tempDir, err := ioutil.TempDir("", "flogo") 328 if err != nil { 329 return "", err 330 } 331 return tempDir, nil 332 } 333 334 var emptyFlogoJson = ` 335 { 336 "name": "{{.AppName}}", 337 "type": "flogo:app", 338 "version": "0.0.1", 339 "description": "My Flogo Application Description", 340 "appModel": "1.1.0", 341 "imports": [], 342 "triggers": [], 343 "resources":[] 344 } 345 `