diff --git a/Makefile b/Makefile index e2d64a3..b97099a 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,11 @@ default: all .PHONY: all all: package-apex package-vf package-lightning -docset-gen: +vendor: dep ensure - go build -x -o docset-gen ./SFDashC/ + +docset-gen: vendor + go build -i -x -o docset-gen ./SFDashC/ .PHONY: run-apex run-apex: clean-index docset-gen @@ -55,3 +57,10 @@ clean: clean-index clean-package clean-archive .PHONY: clean-build clean-build: rm -fr ./build + +.PHONY: clean-vendor +clean-vendor: + rm -fr ./vendor + +.PHONY: clean-all +clean-all: clean clean-build clean-vendor diff --git a/README.md b/README.md index a8cf2e9..3bd8998 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,14 @@ Everything is wrapped with a Makefile and can be completely built by simply exec That's it! -It will generate 3 docsets: Salesforce Apex, Salesforce Visualforce, and Salesforce Combined +It will generate 3 docsets: Salesforce Apex, Salesforce Visualforce, and Salesforce Lightning Dependencies ------------ -Currently these are not auto resolved. You must install the following: +All dependencies are being managed by [dep](https://github.com/golang/dep). Dep must be installed for the vendor folder to be built. -* github.com/coopernurse/gorp -* github.com/mattn/go-sqlite3 +To Do +----- + + - [ ] Now that new `ForceCascadeType` is available, some of the entries in `./SFDashC/supportedtypes.go` can be simplified diff --git a/SFDashC/database.go b/SFDashC/database.go index 7de9e39..3d631c8 100644 --- a/SFDashC/database.go +++ b/SFDashC/database.go @@ -11,6 +11,7 @@ import ( var dbmap *gorp.DbMap var dbName = "docSet.dsidx" +// InitDb will initialize a new instance of a sqlite db for indexing func InitDb(buildDir string) *gorp.DbMap { dbPath := filepath.Join(buildDir, dbName) err := os.MkdirAll(filepath.Dir(dbPath), 0755) @@ -32,13 +33,14 @@ func InitDb(buildDir string) *gorp.DbMap { return dbmap } -func SaveSearchIndex(dbmap *gorp.DbMap, entry TOCEntry, entryType *SupportedType, toc *AtlasTOC) { - if entry.LinkAttr.Href == "" || entryType == nil { +// SaveSearchIndex will index a particular entry into the sqlite3 database +func SaveSearchIndex(dbmap *gorp.DbMap, entry TOCEntry, entryType SupportedType, toc *AtlasTOC) { + if entry.LinkAttr.Href == "" || !entryType.IsValidType() { return } relLink := entry.GetContentFilepath(toc, false) - name := entry.CleanTitle(*entryType) + name := entry.CleanTitle(entryType) if entryType.ShowNamespace && len(entryHierarchy) > 0 { // Show namespace for methods name = entryHierarchy[len(entryHierarchy)-1] + "." + name @@ -50,6 +52,8 @@ func SaveSearchIndex(dbmap *gorp.DbMap, entry TOCEntry, entryType *SupportedType Path: relLink, } - dbmap.Insert(&si) + err := dbmap.Insert(&si) + ExitIfError(err) + LogDebug("%s is indexed as a %s", entry.Text, entryType.TypeName) } diff --git a/SFDashC/main.go b/SFDashC/main.go index 3108e24..43498b2 100644 --- a/SFDashC/main.go +++ b/SFDashC/main.go @@ -46,7 +46,9 @@ func getTOC(locale string, deliverable string) (toc *AtlasTOC, err error) { ExitIfError(err) // Read the downloaded JSON - defer resp.Body.Close() + defer func() { + ExitIfError(resp.Body.Close()) + }() contents, err := ioutil.ReadAll(resp.Body) ExitIfError(err) @@ -91,7 +93,9 @@ func saveMainContent(toc *AtlasTOC) { ofile, err := os.Create(filePath) ExitIfError(err) - defer ofile.Close() + defer func() { + ExitIfError(ofile.Close()) + }() _, err = ofile.WriteString( "" + content, @@ -100,6 +104,7 @@ func saveMainContent(toc *AtlasTOC) { } } +// saveContentVersion will retrieve the version number from the TOC and save that to a text file func saveContentVersion(toc *AtlasTOC) { filePath := fmt.Sprintf("%s-version.txt", toc.Deliverable) // Prepend build dir @@ -110,15 +115,20 @@ func saveContentVersion(toc *AtlasTOC) { ofile, err := os.Create(filePath) ExitIfError(err) - defer ofile.Close() + defer func() { + ExitIfError(ofile.Close()) + }() _, err = ofile.WriteString(toc.Version.DocVersion) ExitIfError(err) } +// downloadCSS will download a CSS file using the CSS base URL func downloadCSS(fileName string, wg *sync.WaitGroup) { downloadFile(cssBaseURL+"/"+fileName, fileName, wg) } +// downloadFile will download n aribtrary file to a given file path +// It will also handle throttling if a WaitGroup is provided func downloadFile(url string, fileName string, wg *sync.WaitGroup) { if wg != nil { defer wg.Done() @@ -131,11 +141,15 @@ func downloadFile(url string, fileName string, wg *sync.WaitGroup) { ofile, err := os.Create(filePath) ExitIfError(err) - defer ofile.Close() + defer func() { + ExitIfError(ofile.Close()) + }() response, err := http.Get(url) ExitIfError(err) - defer response.Body.Close() + defer func() { + ExitIfError(response.Body.Close()) + }() _, err = io.Copy(ofile, response.Body) ExitIfError(err) @@ -146,27 +160,43 @@ func downloadFile(url string, fileName string, wg *sync.WaitGroup) { } } -func getEntryType(entry TOCEntry) (*SupportedType, error) { +// getEntryType will return an entry type that should be used for a given entry and it's parent's type +func getEntryType(entry TOCEntry, parentType SupportedType) (SupportedType, error) { + if parentType.ForceCascadeType { + return parentType.CreateChildType(), nil + } + + childType, err := lookupEntryType(entry) + if err != nil && parentType.CascadeType { + childType = parentType.CreateChildType() + err = nil + } + + return childType, err +} + +// lookupEntryType returns the matching SupportedType for a given entry or returns an error +func lookupEntryType(entry TOCEntry) (SupportedType, error) { for _, t := range SupportedTypes { if entry.IsType(t) { - return &t, nil + return t, nil } } - return nil, NewTypeNotFoundError(entry) + return SupportedType{}, NewTypeNotFoundError(entry) } // processEntryReference downloads html and indexes a toc item -func processEntryReference(entry TOCEntry, entryType *SupportedType, toc *AtlasTOC) { +func processEntryReference(entry TOCEntry, entryType SupportedType, toc *AtlasTOC) { LogDebug("Processing: %s", entry.Text) throttle <- 1 wg.Add(1) go downloadContent(entry, toc, &wg) - if entryType == nil { - LogDebug("No entry type for %s. Cannot index", entry.Text) - } else if entryType.IsContainer || entryType.IsHidden { + if entryType.ShouldSkipIndex() { LogDebug("%s is a container or is hidden. Do not index", entry.Text) + } else if !entryType.IsValidType() { + LogDebug("No entry type for %s. Cannot index", entry.Text) } else { SaveSearchIndex(dbmap, entry, entryType, toc) } @@ -176,33 +206,23 @@ func processEntryReference(entry TOCEntry, entryType *SupportedType, toc *AtlasT var entryHierarchy []string // processChildReferences iterates through all child toc items, cascading types, and indexes them -func processChildReferences(entry TOCEntry, entryType *SupportedType, toc *AtlasTOC) { - if entryType != nil && entryType.PushName { - entryHierarchy = append(entryHierarchy, entry.CleanTitle(*entryType)) +func processChildReferences(entry TOCEntry, entryType SupportedType, toc *AtlasTOC) { + if entryType.PushName { + entryHierarchy = append(entryHierarchy, entry.CleanTitle(entryType)) } for _, child := range entry.Children { LogDebug("Reading child: %s", child.Text) var err error - var childType *SupportedType + var childType SupportedType // Skip anything without an HTML page if child.LinkAttr.Href != "" { - childType, err = getEntryType(child) - if childType == nil && entryType != nil && (entryType.IsContainer || entryType.CascadeType) { - // No child type, and parent is set to cascade - LogDebug("Parent was container or cascade, using parent type of %s", entryType.TypeName) - childType = entryType - childType.IsContainer = false - } else if childType != nil && entryType != nil { - // We didn't cascade in full, but some features are still hereditary - if entryType.IsHidden { - childType.IsHidden = true - } - } - if childType == nil && err != nil { + childType, err = getEntryType(child, entryType) + if err == nil { + processEntryReference(child, childType, toc) + } else { WarnIfError(err) } - processEntryReference(child, childType, toc) } else { LogDebug("%s has no link. Skipping", child.Text) } @@ -212,7 +232,7 @@ func processChildReferences(entry TOCEntry, entryType *SupportedType, toc *Atlas } LogDebug("Done processing children for %s", entry.Text) - if entryType != nil && entryType.PushName { + if entryType.PushName { entryHierarchy = entryHierarchy[:len(entryHierarchy)-1] } } @@ -244,7 +264,9 @@ func downloadContent(entry TOCEntry, toc *AtlasTOC, wg *sync.WaitGroup) { } header += "" - defer ofile.Close() + defer func() { + ExitIfError(ofile.Close()) + }() _, err = ofile.WriteString( header + content.Content, ) @@ -286,8 +308,8 @@ func main() { // Download each entry for _, entry := range toc.TOCEntries { - entryType, err := getEntryType(entry) - if entryType != nil && err == nil { + entryType, err := lookupEntryType(entry) + if err == nil { processEntryReference(entry, entryType, toc) } processChildReferences(entry, entryType, toc) diff --git a/SFDashC/structs.go b/SFDashC/structs.go index 9e704e2..a2e74ac 100644 --- a/SFDashC/structs.go +++ b/SFDashC/structs.go @@ -78,10 +78,6 @@ type SupportedType struct { TypeName string // Not sure... AppendParents bool - // Indicates that this just contains other nodes and we don't want to index this node - IsContainer bool - // Indicates that this and all nodes underneith should be hidden - IsHidden bool // Skip trimming of suffix from title NoTrim bool // Not sure... @@ -90,8 +86,15 @@ type SupportedType struct { PushName bool // Should a namspace be prefixed to the database entry ShowNamespace bool - // Should cascade type downwards + // Indicates that this just contains other nodes and we don't want to index this node + // This is not hereditary + IsContainer bool + // Indicates that this and all nodes underneith should be hidden + IsHidden bool + // Should cascade type downwards unless the child has it's own type CascadeType bool + // Should cascade type downwards, even if children have their own type + ForceCascadeType bool } // Sqlite Struct @@ -103,17 +106,17 @@ type SearchIndex struct { Path string `db:path` } +// matchesTitle returns true if the title matches that of the specified type func (suppType SupportedType) matchesTitle(title string) bool { match := false - if suppType.TitlePrefix != "" { - match = match || strings.HasPrefix(title, suppType.TitlePrefix) - } - if suppType.TitleSuffix != "" { - match = match || strings.HasSuffix(title, suppType.TitleSuffix) - } + match = match || (suppType.TitlePrefix != "" && + strings.HasPrefix(title, suppType.TitlePrefix)) + match = match || (suppType.TitleSuffix != "" && + strings.HasSuffix(title, suppType.TitleSuffix)) return match } +// matchesID returns true if the ID matches that of the specified type func (suppType SupportedType) matchesID(id string) bool { if suppType.ID != "" && suppType.ID == id { return true @@ -124,6 +127,22 @@ func (suppType SupportedType) matchesID(id string) bool { return false } +// CreateChildType returns a child type inheriting the current type +func (suppType SupportedType) CreateChildType() SupportedType { + // Reset values that do not cascade + suppType.IsContainer = false + return suppType +} + +func (suppType SupportedType) ShouldSkipIndex() bool { + return suppType.IsContainer || suppType.IsHidden +} + +// IsValidType returns whether or not this is a valid type +func (suppType SupportedType) IsValidType() bool { + return suppType.TypeName != "" +} + // IsType indicates that the TOCEntry is of a given SupportedType // This is done by checking the suffix of the entry text func (entry TOCEntry) IsType(t SupportedType) bool { @@ -173,14 +192,15 @@ func (entry TOCEntry) GetContent(toc *AtlasTOC) (content *TOCContent, err error) toc.Version.DocVersion, ) - // fmt.Println(url) resp, err := http.Get(url) if err != nil { return } // Read the downloaded JSON - defer resp.Body.Close() + defer func() { + ExitIfError(resp.Body.Close()) + }() contents, err := ioutil.ReadAll(resp.Body) if err != nil { return diff --git a/SFDashC/supportedtypes.go b/SFDashC/supportedtypes.go index 0e4c8e1..9199dec 100644 --- a/SFDashC/supportedtypes.go +++ b/SFDashC/supportedtypes.go @@ -19,6 +19,43 @@ var SupportedTypes = []SupportedType{ ID: "apex_intro_get_started", CascadeType: true, }, + SupportedType{ + TypeName: "Guide", + ID: "pages_flows_customize_runtime_ui", + }, + SupportedType{ + TypeName: "Guide", + ID: "pages_quick_start_controller_shell", + }, + SupportedType{ + TypeName: "Guide", + ID: "pages_email_custom_controller", + }, + SupportedType{ + TypeName: "Guide", + IDPrefix: "apex_qs_", + CascadeType: true, + }, + SupportedType{ + TypeName: "Guide", + ID: "apex_process_plugin_using", + }, + SupportedType{ + TypeName: "Guide", + ID: "apex_platform_cache_builder", + }, + SupportedType{ + TypeName: "Guide", + ID: "apex_classes_restful_http_testing_httpcalloutmock", + }, + SupportedType{ + TypeName: "Guide", + ID: "apex_classes_namespaces_and_invoking_methods", + }, + SupportedType{ + TypeName: "Guide", + ID: "apex_classes_schema_namespace_using", + }, // Apex types SupportedType{ TypeName: "Method", @@ -48,6 +85,13 @@ var SupportedTypes = []SupportedType{ AppendParents: true, ShowNamespace: false, }, + SupportedType{ + TypeName: "Interface", + TitleSuffix: "Global Interface", + PushName: true, + AppendParents: true, + ShowNamespace: false, + }, SupportedType{ TypeName: "Interface", TitleSuffix: "Interface",