Giter VIP home page Giter VIP logo

webexteamsapiclient's Introduction

Webex Teams API Client for .NET

nuget MIT license

Webex Teams API Client is a Library that wraps Cisco Webex Teams REST API.
Also, some useful features for developers are provided.

README in other language


By using Webex Teams API Client, you can invoke Cisco Webex Teams REST API easily.

  • A Simple example to post a message as markdown.
// Load encrypted bot token from storage.
ProtectedString token = LoadEncryptedBotToken();

// Create a TeamsAPIClient instance with retry option.
var teams = TeamsAPI.CreateVersion1Client(token, new TeamsRetryHandler(4));

// Build markdown.
var markdown = new MarkdownBuilder();
markdown.Append("Hi, ").AppendBold("Webex Teams").Append("!!");

// Post a message.
var message = (await teams.CreateDirectMessageAsync("[email protected]", markdown.ToString())).GetData();

Console.WriteLine("Message was posted: ID = {0}", message.Id);
  • A Simple example to post a message with Adaptive Cards created from Json string or object.
// Create From Json String.
var card = AdaptiveCardAttachment.FromJsonString(
@"
    {
        ""type"": ""AdaptiveCard"",
        ""version"": ""1.0"",
        ""body"": [
        {
            ""type"": ""TextBlock"",
            ""text"": ""Adaptive Cards"",
            ""size"": ""large""
        }
        ],
        ""actions"": [
        {
            ""type"": ""Action.OpenUrl"",
            ""url"": ""http://adaptivecards.io"",
            ""title"": ""Learn More""
        }
        ]
    }
");

// Post a message with card.
var message = (await teams.CreateDirectMessageAsync("[email protected]",
    "[Learn More](http://adaptivecards.io) about Adaptive Card.",
    card)).GetData();

Console.WriteLine("Message was posted: ID = {0}", message.Id);


// Also, you can create from Anonymous type or class instance.
var cardObj = new
{
    type    = "AdaptiveCard",
    version = "1.0",
    body    = new []
    {
        new
        {
            type = "TextBlock",
            text = "Adaptive Cards",
            size = "large",
        }
    },
    actions = new []
    {
        new
        {
            type  = "Action.OpenUrl",
            url   = "http://adaptivecards.io",
            title = "Learn More"
        }
    },
};

card = AdaptiveCardAttachment.FromObject(cardObj);

// Post a message with card.
message = (await teams.CreateDirectMessageAsync("[email protected]",
    "[Learn More](http://adaptivecards.io) about Adaptive Card.",
    card)).GetData();

Console.WriteLine("Message was posted: ID = {0}", message.Id);

// You can use teams.GetAttachmentActionAsync() to get action from users.
// Webhook listner, event handler in this APIClient has capablities to get these actions.
  • An example to use Guest Issuer

It is an example that facilitates Guest Issuer of Webex Teams.

// Load encrypted Guest Issuer secret from storage.
ProtectedString secret = LoadEncryptedGuestIssuerSecret();

// Create a GuestIssuerClient instance.
var guestIssuer = TeamsAPI.CreateVersion1GuestIssuerClient(secret, "your_guest_issuer_id");


// Create a Guest User.
var guest = (await guestIssuer.CreateGuestUserAsync("my-guest-id", "GuestUserName")).GetData();

// Create a TeamsAPIClient instance for the Guest User with retry option.
var teams = TeamsAPI.CreateVersion1Client(guest, new TeamsRetryHandler(4));

// Post a message from the Guest User.
var message = (await teams.CreateDirectMessageAsync("[email protected]", "Hello, I am a guest!!")).GetData();

Console.WriteLine("Message was posted: ID = {0}", message.Id);
  • An example to facilitate Pagination

It is an example that facilitates Pagination feature of Webex Teams REST API.
It demonstrates to get and iterate for each 50 spaces, then say "Hello" to the specific named space.

// Gets Enumerator to list all the Group spaces.
var e = (await teams.ListSpacesAsync(
                       type: SpaceType.Group,
                       max: 50)
        ).GetListResultEnumerator();

// Iterates until getting all the spaces.
while (await e.MoveNextAsync())
{
  var r = e.CurrentResult;

  if (r.IsSuccessStatus && r.Data.HasItems)
  {
    // Each result has space list.
    foreach (var space in r.Data.Items)
    {
      // Say "Hello" to the specific named space.
      if (space.Title == "Demo space for Webex Teams API Client(Thrzn41.WebexTeams)")
      {
        await teams.CreateMessageAsync(space, "Hello, Webex Teams!!");
      }
    }

  }
}

More samples are here.


Available Platforms

  • .NET Standard 1.3 or later
  • .NET Core 1.0 or later
  • .NET Framework 4.5.2 or later

NOTE: If you use Simple Webhook Listener/Server feature,
.NET Standard 2.0+, .NET Core 2.0+ or .NET Framework 4.5.2+ is required.


Samples

Samples for Webex Teams API Client is available on here.


Available Features

  • Basic Webex Teams APIs(List/Get/Create Message, Space, etc.).
  • Webex Teams Admin APIs(List/Get Event, License, etc.).
  • Encrypt/Decrypt Webex Teams token in storage.
  • AdaptiveCards attachment(Create Cards and Get AttachmentActions).
  • Pagination for list APIs. Enumerator to facilitate the pagination.
  • Retry-after value, Retry handler.
  • Markdown builder.
  • Error code, error description.
  • Webhook secret validator, Webhook notification manager, Webhook event handler.
  • Guest Issuer helper.
  • OAuth2 helper.
  • Simple Webhook Listener/Server(.NET Standard 2.0+, .NET Core 2.0+, .NET Framework 4.5.2+).

Basic Features

Teams Resource Available Feature Description
Person/People List/Get Available in v1.2.2. Get Me is also available
Space(Room) List/Create/Get/Update/Delete Available in v1.2.2. Room is called 'Space' in this API Client.
Space(Room) Meeting Info Get Available in v1.7.1.
SpaceMembership(Membership) List/Create/Get/Update/Delete Available in v1.2.2. Membership is called 'SpaceMembership' in this API Client.
Message List/Create/Get/Delete Available in v1.2.2. Attach file from local stream is also available
AdaptiveCards List/Create/Get/Delete Available in v1.7.1
AttachmentActions Create/Get Available in v1.7.1
Team List/Create/Get/Update/Delete Available in v1.2.2.
TeamMembership List/Create/Get/Update/Delete Available in v1.2.2.
Webhook List/Create/Get/Update/Delete Available in v1.2.2.
File GetInfo/GetData/Upload Available in v1.2.2.
Place/Device/xAPI - Planed in v1.8.1.

Admin Features

Teams Resource Available Feature Description
Person/People Create/Update/Delete Available in v1.2.2.
Event List/Get Available in v1.2.2.
Organization List/Get Available in v1.2.2.
License List/Get Available in v1.2.2.
Role List/Get Available in v1.2.2.
GroupResource List/Get Available in v1.2.2.
GroupResourceMembership List/Get/Update Available in v1.2.2.

Token encryption/decryption in storage

ProtectedString provides token encryption/decryption.
More details are described later.

Pagination

Cisco Webex Teams API pagination is described on here.

result.HasNext and result.ListNextAsync() are available in the Webex Teams API Client.
Also, TeamsListResultEnumerator is available.
More details are described later.

Gets retry-after

result.HasRetryAfter and result.RetryAfter are available in the Webex Teams API Client.
Also, TeamsRetryHandler and TeamsRetryOnErrorHandler are available.
More details are described later.

Gets HttpStatus code

result.HttpStatusCode is available in the Webex Teams API Client.
More details are described later.

Gets Error code/description

There are cases when Cisco Webex Teams API returns error with error code and description.
result.Data.HasErrors and result.Data.GetErrors() are available in the Webex Teams API Client.

Gets Partial Errors

There are cases when Cisco Webex Teams API returns partial errors.
This is described on here.
Item.HasErrors and Item.GetPartialErrors() are available in the Webex Teams API Client.

Gets trackingId

The trackingId may be used on technical support of Cisco Webex Teams API side.

result.TrackingId is available in the Webex Teams API Client.
More details are described later.

Validates webhook secret

Webhook.CreateEventValidator() is available in the Webex Teams API Client.
Also, WebhookNotificationManager is available to facilicate event handling.
More details are described later.

CreateWebhookAsync() method in the Webex Teams API Client generates webhook secret dynamically by default option.

Markdonw builder

MarkdownBuilder is available in the Webex Teams API Client.
More details are described later.

OAuth2 Helper

TeamsOauth2Client is available in the Webex Teams API Client.

Webhook Listener(.NET Standard 2.0+, .NET Core 2.0+, .NET Framework 4.5.2+)

Webhook listener feature provides simple Webhook server feature.

NOTE: This feature is intended to be used for quick testing purpose.
In production environment, more reliable server solution should be used.

WebhookListener is available in the Webex Teams API Client.
More details are described later.


Basic Usage

Install Webex Teams API Client

You can install Webex Teams API Client from NuGet package manager by any of the following methods.

  • NuGet Package Manager GUI
    Search "Thrzn41.WebexTeams" package and install.

  • NuGet Package Manager CLI

PM> Install-Package Thrzn41.WebexTeams
  • .NET Client
> dotnet add package Thrzn41.WebexTeams

using Directive to import Webex Teams API Client related types

If you want to use using directive, the following namespaces could be used.

using Thrzn41.Util
using Thrzn41.WebexTeams
using Thrzn41.WebexTeams.Version1

You can also use the following namespaces, if needed.

  • Thrzn41.WebexTeams.Version1.GuestIssuer to use Guest Issuer helper.
  • Thrzn41.WebexTeams.Version1.OAuth2 to use OAuth2 helper.
  • Thrzn41.WebexTeams.Version1.Admin to use Admin APIs.

Create a Webex Teams API Client instance

A Webex Teams API Client instance should be re-used as long as possible.

/// Basic APIs.
TeamsAPIClient teams = TeamsAPI.CreateVersion1Client(token);

If you use Admin APIs, an instance for Admin APIs is required.
TeamsAdminAPIClient has all the features of TeamsAPIClient and Admin Features.

/// Admin APIs.
TeamsAdminAPIClient teams = TeamsAPI.CreateVersion1AdminClient(token);

NOTE: The 'token' is very sensitive information for Cisco Webex Teams API.
You MUST protect the 'token' carefully.
NEVER put it in source code directly or NEVER save it in unsecure manner.
Webex Teams API Client provides some token encryption/decryption methods.
If you use your own token encryption/decryption/protection methods, you can create an instance with the decrypted token string.

Save encrypted token to storage

char[] tokens = GetBotTokenFromBotOwner();

var protectedToken = LocalProtectedString.FromChars(tokens);
LocalProtectedString.ClearChars(tokens);

Save("token.dat",   protectedToken.EncryptedData);
Save("entropy.dat", protectedToken.Entropy);

NOTE: LocalProtectedString does not provide in-memory protection.
This is intended to be used to save and load encrypted token.

Load encrypted token from storage and create a Webex Teams API Client

byte[] encryptedData = Load("token.dat");
byte[] entropy       = Load("entropy.dat");

var protectedToken = LocalProtectedString.FromEncryptedData(encryptedData, entropy);

/// Basic APIs.
TeamsAPIClient teams = TeamsAPI.CreateVersion1Client(protectedToken);

NOTE: The encrypted data can be decrypted only on the same local user or local machine as encrypted based on option parameter.

Post a message to a Cisco Webex Teams Space

var result = await teams.CreateMessageAsync("xyz_space_id", "Hello, Teams!");

if(result.IsSuccessStatus)
{
   Console.WriteLine("Message was posted: id = {0}", result.Data.Id);
}

Success, fail, error handling

You can use result.IsSuccessStatus to check if the request is succeeded or not. You can also use result.Data.HasErrors and result.Data.GetErrorMessage() to retrieve error code value of error description from Cisco Webex Teams API service.

var result = await teams.CreateMessageAsync("xyz_space_id", "Hello, Teams!");

if(result.IsSuccessStatus)
{
   Console.WriteLine("Message was posted: id = {0}", result.Data.Id);
}
else
{
  Console.WriteLine("Failed to post a message: status = {0}, trackingId = {1}", result.HttpStatusCode, result.TrackingId);

  if(result.Data.HasErrors)
  {
    Console.WriteLine( result.Data.GetErrorMessage() );
  }
}

If you preferred to catch Exception, you can use result.GetData() to get data.
The result.GetData() will throw TeamsResultException on request error.
(On the other hand, result.Data does not throw TeamsResultException.)

try
{
  var result = await teams.CreateMessageAsync("xyz_space_id", "Hello, Teams!");

  var message = result.GetData();

  Console.WriteLine("Message was posted: id = {0}", message.Id);
}
catch(TeamsResultException tre)
{
  Console.WriteLine("Failed to post a message: status = {0}, trackingId = {1}, description = {2}",
                      tre.HttpStatusCode, tre.TrackingId, tre.Message);
}

Post a message with attachment to a Cisco Webex Teams Space

using (var fs   = new FileStream("path/myfile.png", FileMode.Open, FileAccess.Read, FileShare.Read))
using (var data = new TeamsFileData(fs, "imagefile.png", TeamsMediaType.ImagePNG))
{
    var result = await teams.CreateMessageAsync("xyz_space_id", "Hello Teams with Attachment", data);

    if(result.IsSuccessStatus)
    {
       Console.WriteLine("Message was posted with attachment: id = {0}", result.Data.Id);
    }
}

Post a message to a Cisco Webex Teams 1:1 Space

var result = await teams.CreateDirectMessageAsync("[email protected]", "Hello, Teams!");

if(result.IsSuccessStatus)
{
   Console.WriteLine("Message was posted: id = {0}", result.Data.Id);
}

List spaces

var result = await teams.ListSpacesAsync();

if(result.IsSuccessStatus && result.Data.HasItems)
{
  foreach (var item in result.Data.Items) {
    Console.WriteLine("Space: title = {0}", item.Title);
  }  
}

Get File info or File data

Get File info without downloading the file.

var result = await teams.GetFileInfoAsync(new Uri("https://api.example.com/path/to/file.png"));

if(result.IsSuccessStatus)
{
  var file = result.Data;

  Console.WriteLine("File: Name = {0}, Size = {1}, Type = {2}", file.Name, file.Size?.Value, file.MediaType?.Name);
}

Download the file.
In this example, the file data will be downloaded to memory stream.

using(var stream = new MemoryStream())
{
  var result = await teams.CopyFileDataToStreamAsync(new Uri("https://api.example.com/path/to/file.png"), stream);

  if(result.IsSuccessStatus)
  {
    var file = result.Data;

    Console.WriteLine("File: Name = {0}, Size = {1}, Type = {2}", file.Name, file.Size?.Value, file.MediaType?.Name);
  }
}

Pagination

var result = await teams.ListSpacesAsync();

if(result.IsSuccessStatus)
{
  //
  // Do something...
  //

  if(result.HasNext)
  {
    // List next result.
    result = await result.ListNextAsync();

    if(result.IsSuccessStatus)
    {
      // ...
    }
  }
}

Enumerator for Pagination

// Gets Enumerator to list all the Group spaces.
var e = (await teams.ListSpacesAsync(
                       type: SpaceType.Group,
                       max: 50)
        ).GetListResultEnumerator();

// Iterates until getting all the spaces.
while (await e.MoveNextAsync())
{
  var r = e.CurrentResult;

  if (r.IsSuccessStatus && r.Data.HasItems)
  {
    // Each result has space list.
    foreach (var space in r.Data.Items)
    {
      Console.WriteLine("Title = {0}", space.Title);
    }

  }
}

Gets Http status code of the request

var result = await teams.ListSpacesAsync();

Console.WriteLine("Status is {0}", result.HttpStatusCode);

Gets retry after value

var result = await teams.ListSpacesAsync();

if(result.IsSuccessStatus)
{
  //
  // Do something...
  //
}
else if(result.HasRetryAfter)
{
  Console.WriteLine("Let's retry: {0}", result.RetryAfter.Delta);  
}

Retry Handler

TeamsRetryHandler and TeamsRetryOnErrorHandler facilitate retry.

// Creates instance with Retry option(Max retry is 4 in this case).
var teams = TeamsAPI.CreateVersion1Client(token, new TeamsRetryHandler(4));

// If the request received HTTP 429. the request will be retried.
var result = await teams.GetMeAsync();


// If you want the client retry also on HTTP 500, 502, 503, 504 error response, use TeamsRetryOnErrorHandler.
// If there is no Retry-After header, the request will be retried after 15 sec delay in this case.
var teams = TeamsAPI.CreateVersion1Client(token, new TeamsRetryOnErrorHandler(4, TimeSpan.FromSeconds(15.0f)));

Gets TrackingId

var result = await teams.ListSpacesAsync();

Console.WriteLine("Tracking id is {0}", result.TrackingId);  

Markdown Builder

var md = new MarkdownBuilder();

// Creates markdown with mention and ordered list.
md.Append("Hi ").AppendMentionToPerson("xyz_person_id", "PersonName").AppendLine();
md.AppendOrderedList("Item1");
md.AppendOrderedList("Item2");
md.AppendOrderedList("Item3");

var result = await teams.CreateMessageAsync("xyz_space_id", md.ToString());

Validates webhook event data

var webhook = await teams.GetWebhookAsync("xyz_webhook_id");

var validator = webhook.CreateEventValidator();

When an event is notified on webhook uri,
X-Spark-Signature will have hash code.

The validator can be used to validate the data.

byte[] webhookEventData = GetWebhookEventData();

if( validator.Validate(webhookEventData, "xyz_x_teams_signature_value") )
{
  Console.WriteLine("Notified data is valid!");
}

Webhook Notification manager

Webhook notification manager manages webhooks and event notification.

  • Create instance.
var notificationManager = new WebhookNotificationManager();
  • Then, add webhook to manager with notification function.
var webhook = await teams.GetWebhookAsync("xyz_webhook_id");

notificationManager.AddNotification(
  webhook,
  (eventData) =>
  {
    Console.WriteLine("Event is notified, id = {0}", eventData.Id);
  }
);
  • On receiving webhook event.
byte[] webhookEventData = GetWebhookEventData();

// Signature will be checked and notified to function which is added earlier.
notificationManager.ValidateAndNotify(webhookEventData, "xyz_x_teams_signature_value", encodingOfData);

Webhook Listener

  • Create webhook listener instance.
var listener = new WebhookListener();
  • Add listening host and port.

The TLS/https connection SHOULD be used for listening port.
To do this, a valid certificate for the listener SHOULD be bound in your environment first by netsh tools or related tools.

Add listening endpoint with the bound address and port.

var endpointUri = listener.AddListenerEndpoint("yourwebhookserver.example.com", 8443);
  • Create webhook for the listener.

The endpointUri returned by listener.AddListenerEndpoint() is Uri of the webhook.

var result = await teams.CreateWebhookAsync(
  "my webhook for test",
  endpointUri,
  EventResource.Message,
  EventType.Created);
  • Add webhook to listener with notification function.
var webhook = result.Data;

listener.AddNotification(
  webhook,
  async (eventData) =>
  {
    Console.WriteLine("Event is notified, id = {0}", eventData.Id);

    if(eventData.Resource == EventResource.Message)
    {
      Console.WriteLine("Message, id = {0}", eventData.MessageData.Id);
    }
  }
);
  • Start listener.

After starting listener, events will be notified to registered notification function.

listener.Start();

Webhook Listener with ngrok

If you do not have global ip address.
You may be able to use tunneling services such as ngrok.

  • Get ngrok and start it.

You can download ngrok command line tool from here.

The following command will start tunneling that will forward to port 8080 of localhost.

prompt> ngrok http 8080 --bind-tls=true
  • Create webhook listener instance.
var listener = new WebhookListener();
  • Add listening host and port.

The ngrok will forward to localhost.

var endpointUri = listener.AddListenerEndpoint("localhost", 8080, false);
  • Create webhook for the listener.

In this case, a tunneling service is used with ngrok,
The endpointUri returned by listener.AddListenerEndpoint() is Uri to be forwarded.

You should create webhook with ngrok uri.

If ngrok assigns https://ngrok-xyz.example.com,
You need to create webhook with String.Format("https://ngrok-xyz.example.com{0}", endpointUri.AbsolutePath).

var result = await teams.CreateWebhookAsync(
  "my webhook for test",
  new Uri(String.Format("https://ngrok-xyz.example.com{0}", endpointUri.AbsolutePath)),
  EventResource.Message,
  EventType.Created);
  • Add webhook to listener with notification function.
var webhook = result.Data;

listener.AddNotification(
  webhook,
  async (eventData) =>
  {
    Console.WriteLine("Event is notified, id = {0}", eventData.Id);

    if(eventData.Resource == EventResource.Message)
    {
      Console.WriteLine("Message, id = {0}", eventData.MessageData.Id);
    }
  }
);
  • Start listener.

After starting listener, events will be notified to registered notification function.

listener.Start();

webexteamsapiclient's People

Contributors

machner avatar thrzn41 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

webexteamsapiclient's Issues

avoid unnecessary await Task

There are some unnecessary await Tasks.

For example:
some await Stream.CopyToAsync() can be replaced with simply Stream.CopyTo().

For some methods, it will be better:

Stream.CopyTo();
return Task.CompletedTask;

Retrieve request line feature

This is mainly for logging.

Code snippet for this enhancement:

var r = await teams.ListSpaces();

// For example, "GET /v1/rooms HTTP/1.1 - OK" is output to log.
Log.Info("{0} - {1}", r.RequestLine, r.HttpStatusCode);

GetFileInfoAsync returns Unauthorzied - Webex API URI changed

The URI https://api.ciscospark.com has changed to https://webexapis.com. The code still calls the old API-URI api.ciscospark.com as stated in TeamsAPIClient.cs:

protected const string TEAMS_API_PATH = "https://api.ciscospark.com/v1/";

While this is still working for most of the scenarios, the File-URI's in messages return the new URI. Subsequent calls to GetFileInfoAsync with the new URI returned fail with 401 "Unauthorized", because the Authentication header will not be passed to the API due to this check in TeamsHttpClient.cs:

if (this.teamsAuthentication != null && !reusableRequest.HasAuthentication && this.teamsAPIUriPattern != null && this.teamsAPIUriPattern.IsMatch(reusableRequest.Uri.AbsoluteUri)) // <-- no Match!
{
     reusableRequest.Authentication = this.teamsAuthentication;
}

Improve retry feature

Currently, retry feature works per request.
For easy use, it is better that the feature works per Teams API Client.

Code snippet for this enhancement:

var teams = TeamsAPI.CreateVersion1Client(token, RetryExecutor.One);

// This request will be invoked with retry option.
var r = await teams.ListSpaces();

More easy way to list result by paging.

For now paging feature is supported by enumerating TeamsListResult:

// Gets Enumerator to list all the Group spaces.
var e = (await teams.ListSpacesAsync(
                       type: SpaceType.Group,
                       max: 50)
        ).GetListResultEnumerator();

// Iterates until getting all the spaces.
while (await e.MoveNextAsync())
{
  var r = e.CurrentResult;

  if (r.IsSuccessStatus && r.Data.HasItems)
  {
    // Each result has space list.
    foreach (var space in r.Data.Items)
    {
      // Say "Hello" to the specific named space.
      if (space.Title == "Demo space for Webex Teams API Client(Thrzn41.WebexTeams)")
      {
        await teams.CreateMessageAsync(space, "Hello, Webex Teams!!");
      }
    }

  }
}

To avoid nested while of for/foreach loop, it will be better there is enumeration feature of TeamsResult:

// Gets Enumerator to list all the Group spaces.
var e = (await teams.ListSpacesAsync(
                       type: SpaceType.Group,
                       max: 50)
        ).GetResultEnumerator();

// Iterates until getting all the spaces.
while (await e.MoveNextAsync())
{
  var r = e.CurrentResult;

  if (r.IsSuccessStatus)
  {
      var space = r.Data;

      // Say "Hello" to the specific named space.
      if (space.Title == "Demo space for Webex Teams API Client(Thrzn41.WebexTeams)")
      {
        await teams.CreateMessageAsync(space, "Hello, Webex Teams!!");
      }
    }
}

CopyFileDataToStreamAsync() throws OutOfMemoryException when the file is too large.

If a large file(example, 2 GB or 4 GB) will be downloaded, CopyFileDataToStreamAsync() throws OutOfMemoryException.

In this issue,
HttpClient.SendAsync(request) throws OutOfMemoryException after retrieving large Content-Length header value.
To use HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead) and stream download will avoid the exception.

Guid(Transaction Id) per request even there are retries

For logging in apps,
it is better that there are consistent Guid per request even there are retries.

Code snippet for this enhancement:

// RetryExecutor.One requests with 1 time retry at most.
var result = await RetryExecutor.One.ListAsync(
  () =>
  {
      // This function will be executed again if needed.
      return teams.ListSpacesAsync();
  },

  (r, retryCount) =>
  {
      // This function will be executed before evry retry request.

      // You can output logs or other things at this point.
      Log.Info("Retry is required: delta = {0}, counter = {1}, guid = {2}", r.RetryAfter.Delta, retryCount, r.Guid);

      // Return 'true' when you want to proceed retry.
      return true;
  }
);
  
  // the Guid is the same as logging on a retry.
  Log.Info("guid = {0}",result.Guid);

Examples for oAuth

Do you have any example on how to use this API with the Cisco oAuth workflow?

Retrieve resource and operation enum feature

Enums for Resources or Operations.

For examples:

TeamsResource.Message;
TeamsResource.Space;
TeamsOperation.List;
TeamsOperation.Get;
TeamsOperation.Create;

In addition, this resource and operation can be retrieved from result.

Code snippet for this enhancement:

var r = await teams.ListSpaces();

// For example, "Operation: ListSpace" is output to log.
Log.Info("Operation: {0}", r.ResourceOperation);

TeamsDataProtect to encrypt and/or decrypt TeamsData.

Features to encrypt and/or decrypt TeamsData again and again.
Especially for token related data.

Code sinipped(may be changed):

using(var ltdp = TeamsLocalDataProtect.Create())
{
    var encrypted = ltdp.Encrypt(teamsdata);

    // some stream ops.
    await ltdp.WriteToStreamAsync(stream);
}

using(var ptdp = TeamsPBEDataProtect.Create())
{
    var encrypted = ptdp.Encrypt(teamsdata);

    // some stream ops.
    await ptdp.WriteToStreamAsync(stream);
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.