refactor(mlablocatev2): use interfaces, add missing tests, add docs (#384)

This is a very light refactoring of the mlablocatev2 package where we do
the following things:

1. use interfaces rather than depending on other pkgs where possible

2. add a missing test to the test suite

3. write more comprehensive docs (including todo-next comments)
This commit is contained in:
Simone Basso 2021-06-15 19:25:09 +02:00 committed by GitHub
commit d84cf5b69f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 89 additions and 29 deletions

View file

@ -1,4 +1,6 @@
// Package mlablocatev2 implements m-lab locate services API v2.
// Package mlablocatev2 implements m-lab locate services API v2. This
// API currently only allows you to get servers for ndt7. Use the
// mlablocate package for all other m-lab tools.
package mlablocatev2
import (
@ -10,7 +12,6 @@ import (
"net/url"
"regexp"
"github.com/ooni/probe-cli/v3/internal/engine/model"
"github.com/ooni/probe-cli/v3/internal/iox"
)
@ -27,17 +28,40 @@ var (
ErrEmptyResponse = errors.New("mlablocatev2: empty response")
)
// Client is a client for v2 of the locate services.
// Logger is the logger expected by this package.
type Logger interface {
// Debugf formats and emits a debug message.
Debugf(format string, v ...interface{})
}
// HTTPClient is anything that looks like an http.Client.
type HTTPClient interface {
// Do behaves like http.Client.Do.
Do(req *http.Request) (*http.Response, error)
}
// Client is a client for v2 of the locate services. Please use the
// NewClient factory to construct a new instance of client, otherwise
// you MUST fill all the fields marked as MANDATORY.
type Client struct {
HTTPClient *http.Client
Hostname string
Logger model.Logger
Scheme string
UserAgent string
// HTTPClient is the MANDATORY http client to use
HTTPClient HTTPClient
// Hostname is the MANDATORY hostname of the mlablocate API.
Hostname string
// Logger is the MANDATORY logger to use.
Logger Logger
// Scheme is the MANDATORY scheme to use (http or https).
Scheme string
// UserAgent is the MANDATORY user-agent to use.
UserAgent string
}
// NewClient creates a client for v2 of the locate services.
func NewClient(httpClient *http.Client, logger model.Logger, userAgent string) Client {
func NewClient(httpClient HTTPClient, logger Logger, userAgent string) Client {
return Client{
HTTPClient: httpClient,
Hostname: "locate.measurementlab.net",
@ -49,8 +73,8 @@ func NewClient(httpClient *http.Client, logger model.Logger, userAgent string) C
// entryRecord describes one of the boxes returned by v2 of
// the locate service. It gives you the FQDN of the specific
// box along with URLs for each experiment phase. Use the
// URLs directly because they contain access tokens.
// box along with URLs for each experiment phase. You MUST
// use the URLs directly because they contain access tokens.
type entryRecord struct {
Machine string `json:"machine"`
URLs map[string]string `json:"urls"`
@ -83,6 +107,8 @@ type resultRecord struct {
// query performs a locate.measurementlab.net query
// using v2 of the locate protocol.
func (c Client) query(ctx context.Context, path string) (resultRecord, error) {
// TODO(bassosimone): this code should probably be
// refactored to use the httpx package.
URL := &url.URL{
Scheme: c.Scheme,
Host: c.Hostname,
@ -116,10 +142,22 @@ func (c Client) query(ctx context.Context, path string) (resultRecord, error) {
// NDT7Result is the result of a v2 locate services query for ndt7.
type NDT7Result struct {
Hostname string
Site string
// Hostname is an informative field containing the hostname
// to which you're connected. Because there are access tokens,
// you cannot use this field directly.
Hostname string
// Site is an informative field containing the site
// to which the server belongs to.
Site string
// WSSDownloadURL is the WebSocket URL to be used for
// performing a download over HTTPS. Note that the URL
// typically includes the required access token.
WSSDownloadURL string
WSSUploadURL string
// WSSUploadURL is like WSSDownloadURL but for the upload.
WSSUploadURL string
}
// QueryNDT7 performs a v2 locate services query for ndt7.
@ -137,6 +175,9 @@ func (c Client) QueryNDT7(ctx context.Context) ([]NDT7Result, error) {
if r.WSSDownloadURL == "" || r.WSSUploadURL == "" {
continue
}
// Implementation note: we extract the hostname from the
// download URL, under the assumption that the download and
// the upload URLs have the same hostname.
url, err := url.Parse(r.WSSDownloadURL)
if err != nil {
continue