github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/api/fossa/revisions.go (about) 1 package fossa 2 3 import ( 4 "fmt" 5 "net/url" 6 7 "github.com/apex/log" 8 ) 9 10 // A License holds the FOSSA API response for the license API. 11 type License struct { 12 ID int64 13 LicenseID string 14 RevisionID string 15 LicenseGroupID int64 16 Ignored bool 17 Title string 18 URL string 19 Copyright string 20 Text string 21 Attribution string 22 } 23 24 // A Revision holds the FOSSA API response for the revision API. 25 type Revision struct { 26 Locator *Locator `json:"loc"` 27 Licenses []License 28 Project *Project 29 Meta []RevisionMeta 30 Issues []Issue 31 } 32 33 // A RevisionMeta holds metadata about a FOSSA API revision. 34 type RevisionMeta struct { 35 LastScan string `json:"last_scan"` 36 } 37 38 // A Project holds the FOSSA API response for the project API. 39 type Project struct { 40 Title string 41 URL string 42 Public bool 43 Authors []string 44 } 45 46 // RevisionsAPI is the API endpoint for revisions. 47 const RevisionsAPI = "/api/revisions/%s" 48 49 // RevisionsDependenciesAPI is the API endpoint to retrieve transitive dependencies of a revision. 50 const RevisionsDependenciesAPI = "/api/revisions/%s/dependencies" 51 52 // GetRevisionDependencies returns all transitive dependencies for a project revision. 53 func GetRevisionDependencies(locator Locator, licenseText bool) ([]Revision, error) { 54 var revisions []Revision 55 licenseParams := url.Values{} 56 if licenseText { 57 licenseParams.Add("include_license_text", "true") 58 licenseParams.Add("generate_attribution", "true") 59 } 60 61 url := fmt.Sprintf(RevisionsDependenciesAPI, url.PathEscape(locator.OrgString())) + "?" + licenseParams.Encode() 62 _, err := GetJSON(url, &revisions) 63 return revisions, err 64 } 65 66 // GetRevision loads a single revision. 67 func GetRevision(locator Locator) (Revision, error) { 68 var revision Revision 69 _, err := GetJSON(fmt.Sprintf(RevisionsAPI, url.PathEscape(locator.String())), &revision) 70 if err != nil { 71 return Revision{}, err 72 } 73 74 if revision.Locator == nil { 75 revision.Locator = &locator 76 } 77 if len(revision.Licenses) == 0 { 78 revision.Licenses = append(revision.Licenses, License{ 79 LicenseID: "UNKNOWN", 80 }) 81 } 82 if revision.Project == nil { 83 revision.Project = &Project{ 84 Title: revision.Locator.Project, 85 URL: "UNKNOWN", 86 } 87 } 88 89 return revision, err 90 } 91 92 // GetRevisions loads many revisions in batched requests. 93 func GetRevisions(locators []Locator) (revs []Revision, err error) { 94 var locs []string 95 for _, loc := range locators { 96 locs = append(locs, loc.String()) 97 } 98 99 // Split locators into chunks of 20 (this is an API limitation). 100 chunks := make([][]string, 0) 101 chunkSize := 20 102 for i := 0; i < len(locs); i += chunkSize { 103 end := i + chunkSize 104 105 if end > len(locs) { 106 end = len(locs) 107 } 108 109 chunks = append(chunks, locs[i:end]) 110 } 111 112 // Make chunked API calls in parallel. 113 responses := make(chan []Revision, len(chunks)) 114 for _, chunk := range chunks { 115 qs := url.Values{} 116 for _, q := range chunk { 117 qs.Add("locator", q) 118 } 119 120 go func(endpoint string) { 121 var revisions []Revision 122 _, err := GetJSON(endpoint, &revisions) 123 if err != nil { 124 log.Warnf("Failed to get some revisions: %s", err.Error()) 125 responses <- []Revision{} 126 } else { 127 responses <- revisions 128 } 129 }(fmt.Sprintf(RevisionsAPI, "?"+qs.Encode())) 130 } 131 132 var revisions []Revision 133 for range chunks { 134 r := <-responses 135 revisions = append(revisions, r...) 136 } 137 138 return revisions, err 139 }