55bdebe8b2
* internal/engine/ooapi: auto-generated API client * feat: introduce the callers abstraction * feat: implement API caching on disk * feat: implement cloneWithToken when we require login * feat: implement login * fix: do not cache all APIs * feat: start making space for more tests * feat: implement caching policy * feat: write tests for caching layer * feat: add integration tests and fix some minor issues * feat: write much more unit tests * feat: add some more easy unit tests * feat: add tests that use a local server While there, make sure many fields we care about are OK. * doc: write basic documentation * fix: tweak sentence * doc: improve ooapi documentation * doc(ooapi): other documentation improvements * fix(ooapi): remove caching for most APIs We discussed this topic yesterday with @FedericoCeratto. The only place where we want LRU caching is MeasurementMeta. * feat(ooapi): improve handling of errors during login This was also discussed yesterday with @FedericoCeratto * fix(swaggerdiff_test.go): temporarily disable Before I work on this, I need to tend onto other tasks. * fix(ootest): add one more test case We're going towards 100% coverage of this package, as it ought to be. * feat(ooapi): test cases for when the probe clock is off * fix(ooapi): change test to have 100% unittest coverage * feat: sync server and client APIs definition Companion PR: https://github.com/ooni/api/pull/218 * fix(ooapi): start testing again against API * fix(ooapi): only generate each file once * chore: set version to 3.7.0-alpha While there, make sure we don't always skip a currently failing riseupvpn test, and slightly clarify the readme. * fix(kvstore): less scoped error message
164 lines
5.5 KiB
Go
164 lines
5.5 KiB
Go
// Package ooapi contains clients for the OONI API. We
|
|
// automatically generate the code in this package from
|
|
// the apimodel and internal/generator packages. For
|
|
// each OONI API, we define up to three data structures:
|
|
//
|
|
// 1. a data structure representing the API;
|
|
//
|
|
// 2. a caching data structure, if the API
|
|
// supports caching;
|
|
//
|
|
// 3. an auto-login data structure, if the API
|
|
// requires login.
|
|
//
|
|
// The rest of this documentation page describes these
|
|
// three data structures and the design and architecture
|
|
// of this package. Refer to subpackages for more
|
|
// information on how to specify an API.
|
|
//
|
|
// API data structure
|
|
//
|
|
// For each API, this package defines a data structure
|
|
// representing the API. For example, for the TorTargets API,
|
|
// we define the TorTargetsAPI data structure.
|
|
//
|
|
// The API data structure defines a method named Call that
|
|
// allows calling the specified API. Call takes as arguments
|
|
// a context and the request for the API and returns the
|
|
// API response or an error.
|
|
//
|
|
// Request and response messages live inside the apimodel
|
|
// subpackage. We name them after the API. Thus, for
|
|
// the TorTargets API, the request is TorTargetsRequest,
|
|
// and the response is TorTargetsResponse.
|
|
//
|
|
// API data structures are cheap to create and do not
|
|
// mutate. They should be used in place and then forgotten
|
|
// off once the API call is complete.
|
|
//
|
|
// Unless explicitly indicated, the zero value of every
|
|
// API data structure is a valid API data structure.
|
|
//
|
|
// In terms of dependencies, APIs certainly need an http.Client
|
|
// to communicate with the OONI backend. To represent such a
|
|
// client, we use the HTTPClient interface. If you do not tell
|
|
// an API which http.Client to use, we will default to the
|
|
// standard library's http.DefaultClient.
|
|
//
|
|
// An API also depends on a JSONCodec. That is, on a data
|
|
// structures that encodes data to/from JSON. If you do not
|
|
// specify explicitly a JSONCodec, we will use the Go
|
|
// standard library's JSON implementation.
|
|
//
|
|
// When an API requires authentication, you need to tell
|
|
// it which authentication token to use. This gives you
|
|
// control over obtaining the token and is the low-level
|
|
// way of interacting with authenticated APIs. We recommend
|
|
// using the auto-login wrappers instead (see below).
|
|
//
|
|
// Authenticated APIs also define the WithToken method. This
|
|
// method takes as argument a token and returns a copy of the
|
|
// original API using the given token. We use this method
|
|
// to implement auto-login wrappers.
|
|
//
|
|
// For each API, we also define two interfaces:
|
|
//
|
|
// 1. the Caller interface represents the possibility of
|
|
// calling a specific API with the correct arguments;
|
|
//
|
|
// 2. the Cloner interface represents the possibility of
|
|
// calling WithToken on the given API.
|
|
//
|
|
// They abstract the interaction between the API type and
|
|
// its caching and auto-login wrappers.
|
|
//
|
|
// Caching
|
|
//
|
|
// If an API supports caching, we define a type whose name
|
|
// ends in Cache. The TorTargets API cache, for example,
|
|
// is TorTargetsCache. These caching types wrap the API type
|
|
// and provide the caching functionality.
|
|
//
|
|
// Because the cache needs to read from and write to the
|
|
// disk, a caching type needs a KVStore. A KVStore is
|
|
// an interface that allow you to bind a specific key to
|
|
// a given blob of bytes and to retrieve such bytes later.
|
|
//
|
|
// Caches use the gob data format from the Go standard
|
|
// library (`encoding/gob`). We abstract this dependency
|
|
// using the GobCodec interface. By default, when you
|
|
// do not specify a GobCodec we use the implementation
|
|
// of gob from the Go standard library.
|
|
//
|
|
// See the example describing caching for more information
|
|
// on how to use caching.
|
|
//
|
|
// Auto-login
|
|
//
|
|
// If an API supports auto-login, we define a type whose
|
|
// name ends with WithLogin. The TorTargets auto-login struct,
|
|
// for example, is called TorTargetsAPIWithLogin.
|
|
//
|
|
// Auto-login wrappers need to store persistent data. We
|
|
// use a KVStore for that (see above). We encode login data
|
|
// using JSON. To this end, we use a JSONCodec (also
|
|
// described above).
|
|
//
|
|
// See the example describing auto-login for more information
|
|
// on how to use auto-login.
|
|
//
|
|
// Design
|
|
//
|
|
// Most of the code in this package is auto-generated from the
|
|
// data model in ./apimodel and the definition of APIs provided
|
|
// by ./internal/generator/spec.go.
|
|
//
|
|
// We keep the generated files up-to-date by running
|
|
//
|
|
// go generate ./...
|
|
//
|
|
// We have tests that ensure that the definition of the API
|
|
// used here is reasonably close to the server's one.
|
|
//
|
|
// Testing
|
|
//
|
|
// The following command
|
|
//
|
|
// go test ./...
|
|
//
|
|
// will, among other things, ensure that the our API spec
|
|
// is consistent with the server's one. Running
|
|
//
|
|
// go test -short ./...
|
|
//
|
|
// will exclude most (slow) integration tests.
|
|
//
|
|
// Architecture
|
|
//
|
|
// The ./apimodel package contains the definition of request
|
|
// and response messages. We rely on tagging to specify how
|
|
// we should encode and decode messages.
|
|
//
|
|
// The ./internal/generator contains code to generate most
|
|
// code in this package. In particular, the spec.go file is
|
|
// the specification of the APIs.
|
|
//
|
|
// Notable generated files
|
|
//
|
|
// - apis.go: contains APIs (e.g., TorTargetsAPI);
|
|
//
|
|
// - caching.go: contains caching wrappers for every API
|
|
// that declares that it needs a cache (e.g., TorTargetsCache);
|
|
//
|
|
// - callers.go: contains Callers;
|
|
//
|
|
// - cloners.go: contains the Cloners;
|
|
//
|
|
// - login.go: contains auto-login wrappers (e.g.,
|
|
// TorTargetsAPIWithLogin);
|
|
//
|
|
// - requests.go: contains code to generate http.Requests.
|
|
//
|
|
// - responses.go: code to parse http.Responses.
|
|
package ooapi
|