Description
Description
This issue continues on from a discussion that started in #2144, in regards to a lack of clarity in the definition or implementation of the Guild object.
In the new Template API, there exists a property called serialized_source_guild
which, ostensibly, contains a Guild object. However, the data in the property never contains an id
property, nor a significant number of other properties. Furthermore, several optional fields are strictly irrelevant for the property, as it represents a snapshot of a guild structure, and not an actual live guild. The reasoning behind this relates to Create Guild, since it accepts in its JSON request body a structure of the exact format of serialized_source_guild
(with an exception related to icon
and icon_hash
, which appear to be unused or deprecated).
This problem exists in other parts of the documentation as well, where a property may be marked as containing a "partial" instance of some object type, without any structured, well-established elaboration about what may or may not be present in this partial object. Therefore, if one were to simply blindly implement the type as it is written in the documentation, properties that are marked as non-optional may still be missing when an object of the given type is expected.
This poses a complexity issue for strongly-typed languages, and libraries wanting to provide a stable and predictable API for their end users. Without knowing exactly what can appear and when, implementing, for example, a Guild
type becomes difficult and problematic. Developers are left with the unenviable decision to either follow the documentation as it is written and accept that incoming data may unexpectedly break their expectations; to implement special cases whereever a partial object is documented as potentially occurring; or to simply make all properties optional as a rule and leave checking for data to the end user. Neither option is particularly appealing.
Without strongly pushing one way or the other, I'd like to at least bring up two potential solutions I can see.
Document Type Inheritance
The first option is for the internal inheritance chains between the various objects to be fully and thoroughly documented. According to @tpcstld, the property that sparked the discussion (serialized_source_guild
) is internally derived from Guild
, and I would expect that the story is similar where partial objects occur in other places.
I see the following pros and cons with this approach:
Pro
- Reflects Discord's internal structure better
- Makes it clear where data from one source is fully compatible with an input elsewhere
Con
- Discord's API often removes from rather than adds properties to a derived object. Few, if any, languages with polymorphism support this.
- If partial objects have different requirements in different places in the API, this would result in many new types being created
There already exists some precedent for this approach in the documentation, in regards to Unavailable Guild
- it is documented as a separate type, but is also referred to as a partial guild.
Document Optionality In the Strict Sense
The second option is to be much stricter in what makes a property optional in an object's documentation. In this scenario, any property which could potentially be missing from an object anywhere in the API would result in the property being marked optional at the base level - Guild
, for example, would have its id
property as being optional, and User
would have everything but id
as optional properties (due to Presence Update)
I see the following pros and cons with this approach:
Pro
- Simple and direct
- Requires no new types that are documented separately from the base type
Con
- Leads to inefficient checking for data presence in cases where you might not need to
- More frequent and breaking changes to the base type if Discord adds a new endpoint that returns a partial object with other requirements (of course, parsers which do not assume that anything could be missing would still break anyway)
There is also some precedent with this approach, in that many optional properties are only optional in data returned from some endpoints.
Hybrid
A hybrid approach between the two may also be viable, where a single new Partial <Object>
type is introduced for every type that has a partial representation, which is more liberal with its optionality (perhaps even making every single property optional) yet mirrors the structure of the base type. This reduces the amount of potential type bloat, while at the same time making intent clear. I haven't fully thought this out, however, and would appreciate input and discussion on all suggestions.
Scope
Currently, the following types are marked somewhere in the documentation as having a partial representation, as well as where they're presented as a partial object:
- Guild
- Objects
- Invite
- Template
- Endpoints
- GetCurrentUserGuilds
- RPC
- GET_GUILDS
- GUILD_STATUS
- Objects
- Channel
- Objects
- Invite
- Endpoints
- Create Guild
- RPC
- GET_CHANNELS
- Objects
- User
- Objects
- Invite
- Team Members
- Invite Create
- Presence Update (all properties mentioned as optional, save for
id
)
- RPC
- AUTHENTICATE
- READY
- ACTIVITY_JOIN_REQUEST
- Objects
- Integration
- Objects
- Audit Log
- Connection
- Objects
- Role
- Objects
- Audit Log Change
- Objects
- Voice State
- Objects
- Guild
- Objects
- Presence Update
- Objects
- Guild
- Objects
- Invite
- Endpoints
- Get Guild Vanity
- Endpoints
- Member
- Objects
- Message
- User (appears in
mentions
of message as a user with an additional member field)
- Objects
- Emoji
- Objects
- Reaction
- Message Reaction Add
- Message Reaction Remove
- Objects
This is obviously an issue without a simple, clear-cut solution, and whatever happens - if anything - it'll have rather significant effects on the structure and content of the API documentation. I hope that we can come to some sort of conclusion in regards to the best way forward.
CC original participants:
@ajpalkovic
@tpcstld
@advaith1