Skip to content

Commit

Permalink
refactor(api): Refactor api package
Browse files Browse the repository at this point in the history
  • Loading branch information
elldritch committed Jul 12, 2018
1 parent fcec296 commit b0a8b72
Show file tree
Hide file tree
Showing 11 changed files with 184 additions and 162 deletions.
27 changes: 14 additions & 13 deletions api/api.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
// Package api provides low-level primitives for implementing interfaces to
// various HTTP APIs.
// Package api provides low-level primitives for HTTP APIs.
package api

import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"time"

"github.com/pkg/errors"

"github.com/fossas/fossa-cli/log"
)

var c = http.Client{
var defaultClient = http.Client{
Timeout: 60 * time.Second,
Transport: &http.Transport{
DisableKeepAlives: true,
Expand Down Expand Up @@ -56,9 +56,9 @@ func jsonAPIRequest(method string, endpoint *url.URL, APIKey string, body []byte
if err != nil {
return code, err
}
jsonErr := json.Unmarshal(res, v)
if jsonErr != nil {
return code, fmt.Errorf("could not unmarshal JSON API response: %s", jsonErr.Error())
err = json.Unmarshal(res, v)
if err != nil {
return code, errors.Wrap(err, "could not unmarshal JSON API response")
}
return code, nil
}
Expand All @@ -73,9 +73,10 @@ func isTimeout(err error) bool {
return false
}

// TimeoutError is an error caused by an HTTP request timeout.
type TimeoutError error

// MakeAPIRequest runs and logs a request backed by an `http.Client`.
// MakeAPIRequest runs and logs a request backed by a default `http.Client`.
func MakeAPIRequest(method string, endpoint *url.URL, APIKey string, body []byte) (res []byte, statusCode int, err error) {
log.Logger.Debug(log.Entry{
Message: "making API request",
Expand All @@ -90,26 +91,26 @@ func MakeAPIRequest(method string, endpoint *url.URL, APIKey string, body []byte
// Construct request.
req, err := http.NewRequest(method, endpoint.String(), bytes.NewReader(body))
if err != nil {
return nil, 0, fmt.Errorf("could not construct API HTTP request: %s", err.Error())
return nil, 0, errors.Wrap(err, "could not construct API HTTP request")
}
req.Close = true
req.Header.Set("Authorization", "token "+APIKey)
req.Header.Set("Content-Type", "application/json")

// Send request.
response, err := c.Do(req)
response, err := defaultClient.Do(req)
if err != nil {
if isTimeout(err) {
return nil, 0, TimeoutError(fmt.Errorf("API request timed out: %s", err.Error()))
return nil, 0, TimeoutError(errors.Wrap(err, "API request timed out"))
}
return nil, 0, fmt.Errorf("could not send API HTTP request: %s", err.Error())
return nil, 0, errors.Wrap(err, "could not send API HTTP request")
}
defer response.Body.Close()

// Read request.
res, err = ioutil.ReadAll(response.Body)
if err != nil {
return nil, 0, fmt.Errorf("could not read API HTTP response: %s", err.Error())
return nil, 0, errors.Wrap(err, "could not read API HTTP response")
}

log.Logger.Debugf("Got API response: %#v", string(res))
Expand Down
13 changes: 8 additions & 5 deletions api/fossa/fossa.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,20 @@ func mustParse(endpoint string) *url.URL {
return u
}

func Post(endpoint string, body []byte) (res string, statusCode int, err error) {
u := mustParse(endpoint)
return api.Post(u, apiKey, body)
}

// Get makes a GET request to a FOSSA API endpoint.
func Get(endpoint string) (res string, statusCode int, err error) {
u := mustParse(endpoint)
return api.Get(u, apiKey, nil)
}

// GetJSON makes a JSON GET request to a FOSSA API endpoint.
func GetJSON(endpoint string, v interface{}) (statusCode int, err error) {
u := mustParse(endpoint)
return api.GetJSON(u, apiKey, nil, v)
}

// Post makes a POST request to a FOSSA API endpoint.
func Post(endpoint string, body []byte) (res string, statusCode int, err error) {
u := mustParse(endpoint)
return api.Post(u, apiKey, body)
}
67 changes: 23 additions & 44 deletions api/fossa/locator.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/fossas/fossa-cli/pkg"
)

// Locator serializes FOSSA API locators.
type Locator struct {
Fetcher string `json:"fetcher"`
Project string `json:"package"`
Expand All @@ -20,18 +21,13 @@ func (l Locator) String() string {
return "git+" + NormalizeGitURL(l.Project) + "$" + l.Revision
}

func (l Locator) QueryString() string {
if l.Fetcher == "go" {
l.Fetcher = "git"
}
return l.String()
}

// NormalizeGitURL normalizes all forms of git remote URLs to a single standard
// form.
func NormalizeGitURL(project string) string {
// Remove fetcher prefix (in case project is derived from splitting a locator on '$')
// Remove fetcher prefix (in case project is derived from splitting a locator on '$').
noFetcherPrefix := strings.TrimPrefix(project, "git+")

// Normalize Git URL format
// Normalize Git URL format.
noGitExtension := strings.TrimSuffix(noFetcherPrefix, ".git")
handleGitHubSSH := strings.Replace(noGitExtension, "git@github.com:", "github.com/", 1)

Expand All @@ -42,55 +38,38 @@ func NormalizeGitURL(project string) string {
return noHTTPSPrefix
}

// IsResolved returns true only if a locator is resolved.
func (l Locator) IsResolved() bool {
return l.Revision != ""
}

func ReadLocator(s string) Locator {
r := regexp.MustCompile("^(.*?)\\+(.*?)\\$(.*?)$")
matches := r.FindStringSubmatch(s)
// ReadLocator parses a string locator into a Locator.
func ReadLocator(locator string) Locator {
locatorRegexp := regexp.MustCompile("^(.*?)\\+(.*?)\\$(.*?)$")
matches := locatorRegexp.FindStringSubmatch(locator)
return Locator{
Fetcher: matches[1],
Project: matches[2],
Revision: matches[3],
}
}

type ImportPath []Locator
type ImportPathString string

func (p ImportPath) String() ImportPathString {
var out []string
for _, locator := range p {
out = append(out, locator.String())
}
return ImportPathString(strings.Join(out, " "))
}

func ReadImportPath(s ImportPathString) ImportPath {
parts := strings.Split(string(s), " ")
var out []Locator
for _, part := range parts {
out = append(out, ReadLocator(part))
// LocatorOf returns the locator of a pkg.ID.
func LocatorOf(id pkg.ID) Locator {
// Normalize locator fetchers.
fetcher := id.Type.String()
switch id.Type {
case pkg.Gradle:
fetcher = "mvn"
case pkg.Ant:
fetcher = "mvn"
case pkg.Go:
fetcher = "git"
}
return out
}

func LocatorOf(id pkg.ID) *Locator {
return &Locator{
Fetcher: LocatorType(id.Type),
return Locator{
Fetcher: fetcher,
Project: id.Name,
Revision: id.Revision,
}
}

func LocatorType(t pkg.Type) string {
switch t {
case pkg.Gradle:
return "mvn"
case pkg.Ant:
return "mvn"
default:
return t.String()
}
}
26 changes: 15 additions & 11 deletions api/fossa/normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,28 @@ import (
"github.com/fossas/fossa-cli/pkg"
)

// SourceUnit is the basic module unit of the FOSSA API.
type SourceUnit struct {
Name string
Type string
Manifest string
Build Build
Build SourceUnitBuild
}

type Build struct {
// A SourceUnitBuild contains the build information of a SourceUnit.
type SourceUnitBuild struct {
Artifact string
Context interface{}

Succeeded bool
Error error `json:",omitempty"`

Imports []string
Dependencies []Dependency
Dependencies []SourceUnitDependency
}

type Dependency struct {
// A SourceUnitDependency contains the dependency information of a SourceUnit.
type SourceUnitDependency struct {
// Location
Locator string `json:"locator"`
Imports []string `json:"imports,omitempty"`
Expand All @@ -41,34 +44,35 @@ type Dependency struct {
UnresolvedLocators []string `json:"unresolved_locators,omitempty"`
}

func NormalizeType(t pkg.Type) (string, error) {
// TODO: handle more normalizations
// SourceUnitType normalizes pkg.Types into SourceUnit types.
func SourceUnitType(t pkg.Type) (string, error) {
// TODO: handle more normalizations.
switch t {
case pkg.NodeJS:
return "commonjspackage", nil
default:
return t.String(), nil
}
return "", errors.Errorf("unknown module type: %s", t.String())
}

// Normalize transforms module.Modules into SourceUnits.
func Normalize(modules []module.Module) ([]SourceUnit, error) {
var normalized []SourceUnit
for _, analyzed := range modules {
var deps []Dependency
var deps []SourceUnitDependency
for _, dep := range analyzed.Deps {
var imports []string
for _, i := range dep.Imports {
imports = append(imports, LocatorOf(i.Resolved).String())
}

deps = append(deps, Dependency{
deps = append(deps, SourceUnitDependency{
Locator: LocatorOf(dep.ID).String(),
Imports: imports,
})
}

normalizedType, err := NormalizeType(analyzed.Type)
normalizedType, err := SourceUnitType(analyzed.Type)
if err != nil {
return nil, errors.Wrap(err, "could not normalize analyzed module type")
}
Expand All @@ -81,7 +85,7 @@ func Normalize(modules []module.Module) ([]SourceUnit, error) {
Name: analyzed.Name,
Type: normalizedType,
Manifest: analyzed.BuildTarget,
Build: Build{
Build: SourceUnitBuild{
Artifact: "default",
Succeeded: true,
Dependencies: deps,
Expand Down

0 comments on commit b0a8b72

Please sign in to comment.