Hello everyone!
We are working on upgrading the tooling of our client libraries. We have run into a decision point where we would like your input!
For typed languages, we’re discussing how the models should look and interact with request functions. The main issue is that it’s difficult to have a single Model represent both the request object & the response object, when they have different properties and/or un-returned properties.
Asana’s API has asymmetric requests & responses. Take the Task object for example:
POST /tasks
{
name: “Example”,
assignee: “1234567890”,
workspace: “123457890"
}
GET /tasks/1234567890
{
gid: “1234567890”,
name: “Example”,
assignee: {
gid: “1234567890”,
name: “Ross Grambo”
},
workspace: {
gid: “1234567890”,
name: “Main Workspace”
}
}
For languages that are not using types, this is not an issue. However, when types are introduced, what is the type of assignee
? Is it Option<String>
or Option<User>
?
Additionally, does assignee: null
mean that the assignee
field was not returned in the response or does it mean that the assignee
is unassigned?
Our API allows for a lot of flexibility, which makes it hard to capture in a typed language.
Here’s some approaches we’ve discussed:
We have Request & Response models
- Developer experience takes a hit, as you’re now dealing with different types depending on the context.
- The response models would probably use something like
Option<Option<User>>
to represent
assignee
being unassigned vs not returned.
Requests & Responses use nested Dictionaries (or Gson)
- Developer experience takes a hit, as developers will need to convert to and from models. The model will only represent 1 of the forms (
assignee
probably being Option) - Assuming developers are converting to a Model, they are expected to look into the Dictionary to see if
assignee
was returned or not, and optionally convert Dictionary.assignee to a User object
We choose 1 model, but do magic under the hood to have it work for requests and responses
- The developer experience might be unintuitive (sometimes an assignee is a user, sometimes it’s just an id, and you’re not in charge of it).
- This approach would require more maintenance and one-off handling for things like custom fields.
- We might still need the
Option<Option<User>>
format. We might be able to due without it the first Option<> layer, if we include the raw_response on models.
Does anyone have a preference? Or an idea? Or agree with one approach vs another? I currently prefer a single model, with a raw_response field, where we try and handle the asymmetry gracefully.
Note: we will most likely move to the new tooling and keep the libraries the same until we’ve decided on a consistent methodology & justified the breaking changes it makes
cc: @Phil_Seeman & @Bastien_Siebman
Thanks all,
Ross