-
Notifications
You must be signed in to change notification settings - Fork 198
Library Architecture
The teams-js library provides a suite of APIs that encompass a broad range of functionality across multiple hosts. Different hosts will support different subsets of that functionality (e.g. Outlook may support different functionality than Teams).
The concept of a "capability" has been defined to organize APIs and provide a host-agnostic method for detecting supported functionality. All functionality in the SDK is grouped into these capabilities.
A capability is a logical grouping of APIs that provide similar functionality. A host supports a given capability only if it supports all the APIs defined within that capability. Hosts cannot partially implement a capability. Capabilities can be feature or content-based, such as mail
, calendar
, chat
, dialog
, authentication
, etc., but there may also be capabilities for application types such as pages
, or other potential groups not yet anticipated.
In teams-js, APIs are defined as functions in a JavaScript namespace whose name matches their required capability. If an app is running in a host that supports the calendar
capability, then the app can safely call APIs such as calendar.openCalendarItem
(as well as other calendar-related APIs defined in the namespace). Meanwhile, if an app attempts to call an API that's not supported in that host, the API will throw an exception.
There are two ways for an app to take a dependency on a given capability:
- The app will be able to declare the capability as required in its manifest. Hosts will only load apps if they support all the capabilities those apps require. The app will not be listed in the host's store if any of its required capabilities are unsupported.
- If the app doesn't declare a capability as required, then it needs to check for that capability at runtime by calling an
isSupported()
function on that capability and adjust its behavior as appropriate. This allows an app to enable optional UI and functionality in hosts that support it, while continuing to run (and appear in the store) for hosts that don't.
A subcapability is a child namespace of an existing capability namespace (for example, pages.tabs
: tabs
is a subcapability within the pages
capability). Subcapabilities can only be supported if their parent capability is supported. However, the reverse is not true. A host can choose to support only the parent capability without supporting all subcapabilities. For example, Outlook may support pages
but NOT pages.tabs
. However, if Outlook supports pages.tabs
it MUST support pages
. This can make it easier to add new host-specific functionality to common capabilities.
Since hosts must support all functionality in a capability to declare it as "supported," this generally means that new functions cannot be added to existing shipped capabilities. If new functions were added to shipped capabilities, older hosts would not have support for the new function and consequently the "all or nothing" capability promise would be violated. New functions can be added as a subcapability, if appropriate.
Since developing new capabilties necessitates some amount of iteration and support rollout time, new capabilities (and their functions) still under development should be TSDoc tagged with the @beta tag. This ensures that potential consumers are aware that any and all functionality in that capability can change in the future and that they should not use it in production apps.
It is strongly discouraged that private capabilities be used for this purpose. Develop new capabilities in the public folder with @beta tags. By default, public facing documentation will be automatically generated for all exported namespaces and functions in the public folder. If under development functionality is not yet ready for consumers to use, use the @hidden tag to prevent documentation from being auto-generated. Even things marked with @hidden should be correctly and thoroughly documented.
Private APIs are APIs that exist in the private folder and use the @internal tag. There are no private APIs in the public folder. Any capability or function in the public folder is either public or under development to become public. Private APIs are strongly discouraged and any new functionality or pull request that adds/modifies a private API will be heavily scrutinized. All private APIs should be decorated with the @internal tag, which signifies that the function in question should only be used by Microsoft developers and applications. We do not support or guarantee any functionality of these functions when called outside of those parameters.
This option should only be used for work that meets ALL of the below requirements:
- Features which have already been discussed with the TeamsJS owners and for which approval to use this approach has been granted,
- Feature implementation that has a requirement of running in host clients that have not onboarded to the new declarative capability support architecture
Here are the steps for adding an API that utilizes version checks (e.g. if (!isCurrentSDKVersionAtLeast(captureImageMobileSupportVersion)...
):
- Add the API as a new capability or subcapability rather than adding to an existing capability. Please look at other capabilities such as
calendar.ts
for examples of how to structure a capability. There must be an isSupported() function with every capability which is a simple boolean check for seeing ifruntime.supports
contains the capability.
e.g.
export function isSupported(): boolean {
return runtime.supports.newCapability? true : false;
}
- In
runtime.ts
, add an object describing the new capability and its compatibility requirements toversionConstants
. The version number your new capability should go under
e.g.
// Object key is type string, value is type Array<ICapabilityReqs>
'1.9.0': [
{
capability: { anAndroidCapability: {} },
hostClientTypes: [
HostClientType.android,
HostClientType.teamsRoomsAndroid,
HostClientType.teamsPhones,
HostClientType.teamsDisplays,
],
},
],
If you're adding a capability to an already existing version requirement, simply add your object to the existing array.
e.g.
// Object key is type string, value is type Array<ICapabilityReqs>
'1.9.0': [
{
capability: { anAndroidCapability: {} },
hostClientTypes: [
HostClientType.android,
HostClientType.teamsRoomsAndroid,
HostClientType.teamsPhones,
HostClientType.teamsDisplays,
],
},
{
capability: { aSecondCapability: {} },
hostClientTypes: v1HostClientTypes,
},
],
- And that's it! Our unit tests are designed to automatically integrate the new capability, so if the unit tests pass, you're good to go.
The TeamsJS SDK 2.0 requires that all asynchronous functions be added using Promises instead of callbacks. Promises are a more modern and flexible way of handling asynchronicity than callbacks. New API calls will be rejected if they use callbacks.
BAD
export function getFoo(callback: (foo: Foo, sdkError: SdkError) => void): void
{…}
GOOD
export function getFoo(): Promise<Foo>
{…}
All new functionality requires unit test coverage. Please review the unit test guidelines.
The yarn docs command can be run locally and will generate the documentation provided for developers locally using jsdoc. All exported functions should have documentation comments following this rough format:
/**
* Brief, clear description of exactly what this function is intended to do and
* any side effects it might have (like showing UI to the user)
* @param One per parameter, describing what the parameter is used for
* @returns Brief, clear description of what the function returns.
*/
For any functions not in the public folder, you must begin the comment with the @hidden tag so it does not show up in intellisense:
/**
* @hidden
* Brief, clear description of exactly what this function is intended to do and
* any side effects it might have (like showing UI to the user)
* @param One per parameter, describing what the parameter is used for
* @returns Brief, clear description of what the function returns.
*/