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  }