The API for all the things managed in UpCloud.
Together with the Resource class, this is the most core part of the Groovy UpCloud library. Although seldom used directly, all the UpCloud API calls go through this class.
Basically, this class provides a method for each of the HTTP "verbs" that the server side knows about:
GET(...)
for fetching information about something.DELETE(...)
for deleting something.POST(...)
for creating new things.PUT(...)
for updating those things.
The arguments above are shown as ...
because the interfaces are somewhat flexible. This is Groovy
after all. We'll unravel them bit by bit below.
There's one argument that is most prominent and must always be provided by the application: the default request callback. When sending a request to the server, it is sent asynchronously, and a callback is invoked when the response is received from the server.
As said, the HTTP methods are typically called indirectly. For example, to create a server, one could do as follows:
def server = createMyServerResource()
upcloud.create server, { response ->
// Do something with the response object.
}
Above upcloud.create(...)
will call session.POST(..., Closure<Void> cb)
.
The default request callback is a Closure that doesn't return anything. All of those HTTP methods mentioned above take the default request callback as their last argument.
The response object above is an instance of Resource class. You can read about it later, but for example, to inspect the actual HTTP response status code, one could use the response.META.status property. But that is not typically necessary because there's a better way.
As the name "default request callback" might imply, there are other callbacks that can be attached to the HTTP exchange: additional request callbacks. This is a Map that maps HTTP status codes to callbacks.
For example, to handle the case where the username or password is mistyped, or they get revoked, one might write the server creation code like this:
def server = createMyServerResource()
upcloud.create(server,
401: { response ->
// The username/password pair were bogus. Deal with it here.
},
{ response ->
// Handle all the other cases here.
})
Above code snippet is taking advantage of the way Groovy deals with keyword arguments: it collects them to one
Map and puts that map
at the beginning of the argument list before calling the method.
In other words, the default request callback doesn't actually have to be the last argument, it can be followed
by keyword arguments.
Additional request callbacks are similar to default request callback: they are
Closures with void
return type. They get called with one argument, the
Resource instance representing the HTTP response. All of those HTTP methods mentioned above (GET
,
POST
, and so on) take the Map of additional request callbacks as their first argument.
Dealing with common HTTP error responses like above can get boring after awhile. Worry not, there's another way.
One can attach session callbacks to the Session
instance. These are just like additional request
callbacks, except that these are considered for all future HTTP requests made through this instance of the
Session
.
For example, the above 401 response can be handled once and for all like this:
session.callback 401: { log.fatal("configuration error: the username/password is no good") }
From that point on, all the requests made through this Session
instance would have that callback in
their set of callbacks.
As you may know, all the HTTP response status codes are three digit numbers and can be divided into five categories. The categories by range of status codes are listed below, with category names as known by this library:
info
success
redirect
client_error
server_error
This library also knows about error
category, which is just more generic and covers both
client_error
and server_error
categories, i.e. status codes 400-599.
These categories are useful when one wants to deal with a range of responses in the same way. For example, as
far as I know, at the time of this writing, the UpCloud API never responds with info
or
redirect
type of status codes. So, I might recommend adding this to the beginning of scripts:
session.callback info: { log.fatal "oh my, assumptions are all broken" },
redirect: { log.fatal "dear dear, but I don't want to go elsewhere" }
Of course, in reality, one would log some details from the passed in Resource instance.
Since each HTTP exchange may have multiple sources of callbacks, this library chooses the callback in the following way:
error
category, it is tried last
For example, if the server responds with a 200 OK
, the resolution path looks like this:
200
, that is invoked200
attached to the Session
instance, that is invokedsuccess
, that is invokedsuccess
attached to the Session
instance, that is invoked
On the other hand, for an HTTP response 400 Bad Request
, the resolution path is a bit longer due to the more
generic error
category:
400
400
client_error
client_error
error
error
In all examples above, remember that only one callback is invoked for one HTTP exchange: the first one that matches.
The additional request callbacks and the session callbacks attached to the Session
instance, are all by
definition tied to the HTTP response status code or the status category. However, networks are unreliable and
the communication with the server may not always work. The server might be unreachable, or there might be an
I/O error talking to the server, or the request might be cancelled.
The default request callback can take a second optional argument: an instance of Throwable class.
This is null
whenever a response from the server is available, even if the response represents an error,
and non-null
when the communication fails:
def server = createMyServerResource()
upcloud.create server, { response, error ->
if (error) {
// Handle the network error here.
assert error instanceof Throwable
assert response == null
} else {
// Handle all the other cases here.
assert response instanceof Resource
assert error == null
})
Alternatively, one can use network_error
category for a callback. This callback is different from
additional request callbacks in that it takes an instance of Throwable instead of a Resource
instance as their only parameter. Otherwise it is the same: Closures with
void
return type.
def server = createMyServerResource()
upcloud.create(server,
network_error: { error ->
// Handle the network error here.
assert error instanceof Throwable
},
{ response ->
// Handle all the other cases here.
})
Naturally, the network_error
callback can be attached to the Session
instance, too.
Also, similar to additional request callbacks, it takes precedence over default request callback (in case the
default request callback would take two arguments).
Injectable constructor.
http
- HTTP implementation.json
- JSON implementation.username
- UpCloud API username. This is not the one you use to login to the control panel.password
- UpCloud API password. This is not the one you use to login to the control panel.Set or clear session callbacks.
The set of session callbacks is empty by default. Application can store common callbacks in this map. The keys in the given map are HTTP response status codes like 500 or 404, or HTTP status categories: "info", "success", "redirect", "client_error", "server_error", "error". If the key is not recognized as either a status code or category, an {@ IllegalAgumentException} is thrown.
Note that the old callbacks are not removed, unless their corresponding key is set to null
in
{
cbs}.
-
Note also that the given map is copied while checking the keys, so any later modifications to the map itself are not picked up by this class.
cbs
- Session callbacks to set or clear. Note that any previously set callbacks are not cleared unless
they are explicitly set to null
in this argument.Perform a HTTP request.
cbs
- Additional request callbacks.method
- HTTP method.path
- Resource path relative to the API context path, i.e. without leading slash.resource
- Resource to send or null.cb
- Default request callback.