github.com/mhlo/force@v0.22.28-0.20150915022417-6d05ecfb0b47/packagebuilder.go (about) 1 package main 2 3 import ( 4 "encoding/xml" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "strings" 9 ) 10 11 // Structs for XML building 12 type Package struct { 13 Xmlns string `xml:"xmlns,attr"` 14 Types []MetaType `xml:"types"` 15 Version string `xml:"version"` 16 } 17 18 type MetaType struct { 19 Members []string `xml:"members"` 20 Name string `xml:"name"` 21 } 22 23 func createPackage() Package { 24 return Package{ 25 Version: strings.TrimPrefix(apiVersion, "v"), 26 Xmlns: "http://soap.sforce.com/2006/04/metadata", 27 } 28 } 29 30 type metapath struct { 31 path string 32 name string 33 } 34 35 var metapaths = []metapath{ 36 metapath{"applications", "CustomApplication"}, 37 metapath{"assignmentRules", "AssignmentRules"}, 38 metapath{"aura", "AuraDefinitionBundle"}, 39 metapath{"autoResponseRules", "AutoResponseRules"}, 40 metapath{"classes", "ApexClass"}, 41 metapath{"communities", "Community"}, 42 metapath{"components", "ApexComponent"}, 43 metapath{"connectedApps", "ConnectedApp"}, 44 metapath{"flexipages", "FlexiPage"}, 45 metapath{"homePageLayouts", "HomePageLayout"}, 46 metapath{"labels", "CustomLabels"}, 47 metapath{"layouts", "Layout"}, 48 metapath{"objects", "CustomObject"}, 49 metapath{"objectTranslations", "CustomObjectTranslation"}, 50 metapath{"pages", "ApexPage"}, 51 metapath{"permissionsets", "PermissionSet"}, 52 metapath{"profiles", "Profile"}, 53 metapath{"quickActions", "QuickAction"}, 54 metapath{"remoteSiteSettings", "RemoteSiteSetting"}, 55 metapath{"roles", "Role"}, 56 metapath{"staticresources", "StaticResource"}, 57 metapath{"tabs", "CustomTab"}, 58 metapath{"triggers", "ApexTrigger"}, 59 } 60 61 type PackageBuilder struct { 62 IsPush bool 63 Metadata map[string]MetaType 64 Files ForceMetadataFiles 65 } 66 67 func NewPushBuilder() PackageBuilder { 68 pb := PackageBuilder{IsPush: true} 69 pb.Metadata = make(map[string]MetaType) 70 pb.Files = make(ForceMetadataFiles) 71 72 return pb 73 } 74 75 func NewFetchBuilder() PackageBuilder { 76 pb := PackageBuilder{IsPush: false} 77 pb.Metadata = make(map[string]MetaType) 78 pb.Files = make(ForceMetadataFiles) 79 80 return pb 81 } 82 83 // Build and return package.xml 84 func (pb PackageBuilder) PackageXml() []byte { 85 p := createPackage() 86 87 for _, metaType := range pb.Metadata { 88 p.Types = append(p.Types, metaType) 89 } 90 91 byteXml, _ := xml.MarshalIndent(p, "", " ") 92 byteXml = append([]byte(xml.Header), byteXml...) 93 //if err := ioutil.WriteFile("mypackage.xml", byteXml, 0644); err != nil { 94 //ErrorAndExit(err.Error()) 95 //} 96 return byteXml 97 } 98 99 // Returns the full ForceMetadataFiles container 100 func (pb *PackageBuilder) ForceMetadataFiles() ForceMetadataFiles { 101 pb.Files["package.xml"] = pb.PackageXml() 102 return pb.Files 103 } 104 105 // Add a file to the builder 106 func (pb *PackageBuilder) AddFile(fpath string) (fname string, err error) { 107 fpath, err = filepath.Abs(fpath) 108 if err != nil { 109 return 110 } 111 _, err = os.Stat(fpath) 112 if err != nil { 113 return 114 } 115 116 metaName, fname := getMetaTypeFromPath(fpath) 117 if len(strings.Split(fname, ".")) == 1 { 118 pb.AddMetaToPackage(metaName, fname) 119 } 120 121 // If it's a push, we want to actually add the files 122 if pb.IsPush { 123 err = pb.addFileToWorkingDir(metaName, fpath) 124 } 125 126 return 127 } 128 129 // Adds the file to a temp directory for deploy 130 func (pb *PackageBuilder) addFileToWorkingDir(metaName string, fpath string) (err error) { 131 // Get relative dir from source 132 srcDir := filepath.Dir(filepath.Dir(fpath)) 133 frel, _ := filepath.Rel(srcDir, fpath) 134 135 // Try to find meta file 136 hasMeta := true 137 fmeta := fpath + "-meta.xml" 138 fmetarel := "" 139 if _, err = os.Stat(fmeta); err != nil { 140 if os.IsNotExist(err) { 141 hasMeta = false 142 } else { 143 // Has error 144 return 145 } 146 } else { 147 // Should be present since we worked back to srcDir 148 fmetarel, _ = filepath.Rel(srcDir, fmeta) 149 } 150 151 fdata, err := ioutil.ReadFile(fpath) 152 if err != nil { 153 return 154 } 155 156 if metaName == "AuraDefinitionBundle" { 157 frel = filepath.Join("aura", frel) 158 } 159 pb.Files[frel] = fdata 160 if hasMeta { 161 fdata, err = ioutil.ReadFile(fmeta) 162 pb.Files[fmetarel] = fdata 163 return 164 } 165 166 return 167 } 168 169 // Adds a metadata name to the pending package 170 func (pb *PackageBuilder) AddMetaToPackage(metaName string, name string) { 171 mt := pb.Metadata[metaName] 172 if mt.Name == "" { 173 mt.Name = metaName 174 } 175 176 mt.Members = append(mt.Members, name) 177 pb.Metadata[metaName] = mt 178 } 179 180 // Gets metadata type name and target name from a file path 181 func getMetaTypeFromPath(fpath string) (metaName string, name string) { 182 fpath, err := filepath.Abs(fpath) 183 if err != nil { 184 ErrorAndExit("Cound not find " + fpath) 185 } 186 if _, err := os.Stat(fpath); err != nil { 187 ErrorAndExit("Cound not open " + fpath) 188 } 189 190 // Get name of file 191 name = filepath.Base(fpath) 192 name = strings.TrimSuffix(name, filepath.Ext(name)) 193 194 // Get the directory containing the file 195 fdir := filepath.Dir(fpath) 196 197 // Get the meta type for that directory 198 metaName = getMetaForPath(fdir) 199 return 200 } 201 202 // Gets partial path based on a meta type name 203 func getPathForMeta(metaname string) string { 204 for _, mp := range metapaths { 205 if strings.EqualFold(mp.name, metaname) { 206 return mp.path 207 } 208 } 209 210 // Unknown, so use metaname 211 return metaname 212 } 213 214 // Gets meta type name based on a partial path 215 func getMetaForPath(path string) string { 216 mpath := filepath.Base(path) 217 for _, mp := range metapaths { 218 if mp.path == mpath { 219 return mp.name 220 } 221 } 222 223 // Check to see if this is aura/lightning 224 if strings.HasSuffix(filepath.Dir(path), "metadata/aura") { 225 return "AuraDefinitionBundle" 226 } 227 // Unknown, so use path 228 return mpath 229 }