Copying azure-communication-common from the commit: 845592d241

This commit is contained in:
Anu Thomas Chandy 2021-02-25 14:42:54 -08:00
Родитель 921094ed16
Коммит 09da73632f
33 изменённых файлов: 2381 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,39 @@
# Release History
## 1.0.0-beta.6 (Unreleased)
### Breaking Changes
- Removed `CommunicationTokenCredential(Callable<String> tokenRefresher)`, ` CommunicationTokenCredential(Callable<String> tokenRefresher, String initialToken)`, `CommunicationTokenCredential(Callable<String> tokenRefresher, boolean refreshProactively)`, `CommunicationTokenCredential(Callable<String> tokenRefresher, boolean refreshProactively, String initialToken)`, and added `CommunicationTokenCredential(CommunicationTokenRefreshOptions tokenRefreshOptions)`
## 1.0.0-beta.5 (2021-02-08)
### Breaking Changes
- Removed 'CallingApplicationIdentifier'.
- Removed 'getId' method in 'CommunicationIdentifier' class.
### New Features
- Added a new 'MicrosoftTeamsUserIdentifier' constructor that takes a non-null CommunicationCloudEnvironment parameter.
- Added class 'CommunicationCloudEnvironment'.
- Added class 'CommunicationCloudEnvironmentModel'.
- Added class 'CommunicationIdentifierSerializer'.
- Added class 'CommunicationIdentifierModel'.
- Added class 'MicrosoftTeamsUserIdentifierModel'.
- Added class 'PhoneNumberIdentifierModel'.
- Added class 'CommunicationUserIdentifierModel'.
## 1.0.0-beta.4 (2021-01-28)
### Breaking Changes
- Renamed `CommunicationUserCredential` to `CommunicationTokenCredential`
- Renamed `PhoneNumber` to `PhoneNumberIdentifier`
- Renamed `CommunicationUser` to `CommunicationUserIdentifier `
- Renamed `CallingApplication` to `CallingApplicationIdentifier`
### Added
- Added class `MicrosoftTeamsUserIdentifier`
## 1.0.0-beta.1 (2020-09-22)
This package contains common code for Azure Communication Service libraries. For more information, please see the [README][read_me] and [documentation][documentation].
This is a Public Preview version, so breaking changes are possible in subsequent releases as we improve the product. To provide feedback, please submit an issue in our [Azure SDK for Java GitHub repo](https://github.com/Azure/azure-sdk-for-java/issues).
<!-- LINKS -->
[read_me]: https://github.com/Azure/azure-sdk-for-android/blob/master/sdk/communication/azure-communication-common/README.md
[documentation]: https://docs.microsoft.com/azure/communication-services/quickstarts/chat/get-started?pivots=programming-language-java

Просмотреть файл

@ -0,0 +1,154 @@
# Azure Communication Common client library for Android
This package contains common code for Azure Communication Service libraries.
[Source code](https://github.com/Azure/azure-sdk-for-android/tree/master/sdk/communication/azure-communication-common)
| [API reference documentation](https://azure.github.io/azure-sdk-for-android/sdk/communication/azure-communication-common/azure-communication-common/index.html)
| [Product documentation](https://docs.microsoft.com/azure/communication-services/overview)
## Getting started
### Prerequisites
* You must have an [Azure subscription](https://azure.microsoft.com/free/) and a
[Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource) to use this library.
* The client libraries natively target Android API level 21. Your application's `minSdkVersion` must be set to 21 or
higher to use this library.
* The library is written in Java 8. Your application must be built with Android Gradle Plugin 3.0.0 or later, and must
be configured to
[enable Java 8 language desugaring](https://developer.android.com/studio/write/java8-support.html#supported_features)
to use this library. Java 8 language features that require a target API level >21 are not used, nor are any Java 8+
APIs that would require the Java 8+ API desugaring provided by Android Gradle plugin 4.0.0.
### Versions available
The current version of this library is **1.0.0-beta.5**.
> Note: The SDK is currently in **beta**. The API surface and feature sets are subject to change at any time before they become generally available. We do not currently recommend them for production use.
### Install the library
To install the Azure client libraries for Android, add them as dependencies within your
[Gradle](#add-a-dependency-with-gradle) or
[Maven](#add-a-dependency-with-maven) build scripts.
#### Add a dependency with Gradle
To import the library into your project using the [Gradle](https://gradle.org/) build system, follow the instructions in [Add build dependencies](https://developer.android.com/studio/build/dependencies):
Add an `implementation` configuration to the `dependencies` block of your app's `build.gradle` or `build.gradle.kts` file, specifying the library's name and the version you wish to use:
```gradle
// build.gradle
dependencies {
...
implementation "com.azure.android:azure-communication-common:1.0.0-beta.5"
}
// build.gradle.kts
dependencies {
...
implementation("com.azure.android:azure-communication-common:1.0.0-beta.5")
}
```
#### Add a dependency with Maven
To import the library into your project using the [Maven](https://maven.apache.org/) build system, add it to the `dependencies` section of your app's `pom.xml` file, specifying its artifact ID and the version you wish to use:
```xml
<dependency>
<groupId>com.azure.android</groupId>
<artifactId>azure-communication-common</artifactId>
<version>1.0.0-beta.5</version>
</dependency>
```
### Key concepts
### CommunicationTokenCredential
A `CommunicationTokenCredential` authenticates a user with Communication Services, such as Chat or Calling. It optionally
provides an auto-refresh mechanism to ensure a continuously stable authentication state during communications. User
tokens are created by the application developer using the Communication Administration SDK - once created, they are
provided to the various Communication Services client libraries by way of a `CommunicationTokenCredential` object.
## Examples
The following sections provide several code snippets showing different ways to use a `CommunicationTokenCredential`:
* [Creating a credential with a static token](#creating-a-credential-with-a-static-token)
* [Creating a credential that refreshes with a Callable](#creating-a-credential-that-refreshes-with-a-callable)
* [Creating a credential that refreshes proactively](#creating-a-credential-that-refreshes-proactively)
* [Creating a credential with an initial value that refreshes proactively](#creating-a-credential-with-an-initial-value-that-refreshes-proactively)
* [Getting a token asynchronously](#getting-a-token-asynchronously)
* [Invalidating a credential](#invalidating-a-credential)
### Creating a credential with a static token
```java
CommunicationTokenCredential userCredential = new CommunicationTokenCredential("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjM2MDB9.adM-ddBZZlQ1WlN3pdPBOF5G4Wh9iZpxNP_fSvpF4cWs");
```
### Creating a credential that refreshes with a Callable
Here we pass an imagined `Callable<String>` that makes a network request to retrieve a token string for user Bob. It will be called on a background thread.
```java
Callable<String> tokenRefresher = () -> {
return fetchtoken();
};
CommunicationTokenCredential userCredential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(tokenRefresher, false));
```
### Creating a credential that refreshes proactively
Setting `refreshProactively` to true will call your `Callable<String> tokenRefresher` when the token is close to expiry.
```java
CommunicationTokenCredential userCredential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(tokenRefresher, true));
```
### Creating a credential with an initial value that refreshes proactively
Passing `initialToken` is an optional optimization to skip the first call to `Callable<String> tokenRefresher`. You can use this to separate the boot from your application from subsequent token refresh cycles.
```java
CommunicationTokenCredential userCredential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(tokenRefresher, true, "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjM2MDB9.adM-ddBZZlQ1WlN3pdPBOF5G4Wh9iZpxNP_fSvpF4cWs"));
```
### Getting a token asynchronously
Calling `getToken()` will return a `Future<AccessToken>`
```java
CommunicationTokenCredential userCredential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(tokenRefresher, false));
Future<AccessToken> accessTokenFuture = userCredential.getToken();
```
### Invalidating a credential
Each `CommunicationTokenCredential` instance uses a background thread for refreshing the cached token. To free up resources and facilitate garbage collection, `dispose()` must be called when the `CommunicationTokenCredential` instance is no longer used.
```java
userCredential.dispose();
```
## Troubleshooting
If you run into issues while using this library, please feel free to
[file an issue](https://github.com/Azure/azure-sdk-for-android/issues/new).
## Next steps
* Read more about Communication [user access tokens](https://docs.microsoft.com/azure/communication-services/concepts/authentication).
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License
Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For
details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate
the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to
do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-android%2Fsdk%2Fcommunication%2Fazure-communication-common%2FREADME.png)

Просмотреть файл

@ -0,0 +1,19 @@
ext.publishName = "Microsoft Azure Android Client Library common code For Communication Service"
description = "This package contains the Android client library common code for Microsoft Azure Communication Service."
version = "1.0.0-beta.6"
ext.versionCode = 1
android {
defaultConfig {
versionCode project.versionCode
versionName project.version
}
}
dependencies {
api "com.azure.android:azure-core:1.0.0-beta.3"
implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
implementation "com.jakewharton.threetenabp:threetenabp:$threeTenAbpVersion"
testImplementation "junit:junit:$jUnitVersion"
testImplementation "org.threeten:threetenbp:$threeTenBpVersion"
}

Просмотреть файл

@ -0,0 +1,3 @@
# Module azure-communication-common
This package contains common code for Azure Communication Service libraries.

Просмотреть файл

@ -0,0 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.azure.android.communication.common">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>

Просмотреть файл

@ -0,0 +1,183 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import com.azure.android.core.credential.AccessToken;
import com.azure.android.core.util.logging.ClientLogger;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
class AutoRefreshUserCredential extends UserCredential {
private static final int ON_DEMAND_REFRESH_BUFFER_SECS = 120;
private static final int PROACTIVE_REFRESH_BUFFER_SECS = 600;
private final ClientLogger logger = ClientLogger.getDefault(AutoRefreshUserCredential.class);
private Callable<String> tokenRefresher;
private Callable<AccessToken> accessTokenCallable;
private FutureTask<AccessToken> tokenFuture;
private Timer timer;
private TimerTask proactiveRefreshTask;
AutoRefreshUserCredential(Callable<String> tokenRefresher) {
this(tokenRefresher, false);
}
AutoRefreshUserCredential(Callable<String> tokenRefresher, String initialToken) {
this(tokenRefresher, false, initialToken);
}
AutoRefreshUserCredential(Callable<String> tokenRefresher, boolean refreshProactively) {
this(tokenRefresher, refreshProactively, null);
}
AutoRefreshUserCredential(Callable<String> tokenRefresher, boolean refreshProactively, String initialToken) {
this.tokenRefresher = tokenRefresher;
this.timer = new Timer();
this.accessTokenCallable = setupAccessTokenCallable(refreshProactively);
AccessToken initialAccessToken = null;
if (initialToken != null) {
initialAccessToken = TokenParser.createAccessToken(initialToken);
this.tokenFuture = setupInitialTokenFuture(initialAccessToken);
}
if (refreshProactively) {
this.scheduleProactiveRefresh(initialAccessToken);
}
}
@Override
public Future<AccessToken> getToken() {
if (shouldRefreshTokenOnDemand()) {
this.updateTokenFuture();
}
return this.tokenFuture;
}
@Override
public void dispose() {
if (this.tokenFuture != null) {
this.tokenFuture.cancel(true);
}
if (this.proactiveRefreshTask != null) {
this.proactiveRefreshTask.cancel();
}
this.timer.cancel();
this.timer.purge();
this.tokenRefresher = null;
this.tokenFuture = null;
this.proactiveRefreshTask = null;
super.dispose();
}
private FutureTask<AccessToken> setupInitialTokenFuture(AccessToken initialAccessToken) {
FutureTask<AccessToken> initialTokenFuture = new FutureTask<>(() -> initialAccessToken);
initialTokenFuture.run();
return initialTokenFuture;
}
private Callable<AccessToken> setupAccessTokenCallable(boolean refreshProactively) {
if (!refreshProactively) {
return this::refreshAccessToken;
}
return () -> {
AccessToken accessToken = this.refreshAccessToken();
this.scheduleProactiveRefresh(accessToken);
return accessToken;
};
}
private AccessToken refreshAccessToken() throws Exception {
String tokenStr = this.tokenRefresher.call();
AccessToken accessToken = TokenParser.createAccessToken(tokenStr);
return accessToken;
}
private boolean shouldRefreshTokenOnDemand() {
boolean shouldRefreshTokenOnDemand = false;
if (this.tokenFuture == null || this.tokenFuture.isCancelled()) {
shouldRefreshTokenOnDemand = true;
} else if (this.tokenFuture.isDone()) {
try {
AccessToken accessToken = this.tokenFuture.get();
long refreshEpochSecond = accessToken.getExpiresAt().toEpochSecond() - ON_DEMAND_REFRESH_BUFFER_SECS;
long currentEpochSecond = System.currentTimeMillis() / 1000;
shouldRefreshTokenOnDemand = currentEpochSecond >= refreshEpochSecond;
} catch (ExecutionException | InterruptedException e) {
shouldRefreshTokenOnDemand = true;
}
}
return shouldRefreshTokenOnDemand;
}
private synchronized void updateTokenFuture() {
// Ignore update if disposed
if (this.isDisposed()) {
return;
}
// Ignore update if tokenFuture in progress
if (this.tokenFuture != null && !this.tokenFuture.isDone() && !this.tokenFuture.isCancelled()) {
return;
}
FutureTask<AccessToken> futureTask = new FutureTask<>(this.accessTokenCallable);
this.tokenFuture = futureTask;
ScheduledTask scheduledTask = new ScheduledTask(futureTask::run);
try {
this.timer.schedule(scheduledTask, 0);
} catch (IllegalStateException e) {
logger.warning("AutoRefreshUserCredential has been disposed. Unable to schedule token refresh.", e);
}
}
private synchronized void scheduleProactiveRefresh(AccessToken accessToken) {
if (this.isDisposed()) {
return;
}
long delayMs = 0;
if (accessToken != null) {
long refreshEpochSecond = accessToken.getExpiresAt().toEpochSecond() - PROACTIVE_REFRESH_BUFFER_SECS;
long currentEpochSecond = System.currentTimeMillis() / 1000;
delayMs = Math.max((refreshEpochSecond - currentEpochSecond) * 1000, 0);
}
if (this.proactiveRefreshTask != null) {
this.proactiveRefreshTask.cancel();
}
this.proactiveRefreshTask = new ScheduledTask(this::updateTokenFuture);
this.timer.schedule(this.proactiveRefreshTask, delayMs);
}
private final class ScheduledTask extends TimerTask {
private Runnable runnable;
ScheduledTask(Runnable runnable) {
this.runnable = runnable;
}
@Override
public void run() {
this.runnable.run();
}
}
}

Просмотреть файл

@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import java.util.Objects;
/**
* The cloud that the identifier belongs to.
*/
public class CommunicationCloudEnvironment {
private static final String PUBLIC_VALUE = "public";
private static final String DOD_VALUE = "dod";
private static final String GCCH_VALUE = "gcch";
private final String environmentValue;
/**
* Create CommunicationCloudEnvironment with name string
* @param environmentValue name of hte cloud environment
*/
public CommunicationCloudEnvironment(String environmentValue) {
Objects.requireNonNull(environmentValue);
this.environmentValue = environmentValue;
}
static CommunicationCloudEnvironment fromModel(CommunicationCloudEnvironmentModel environmentModel) {
return new CommunicationCloudEnvironment(environmentModel.toString());
}
/**
* Represent Azure public cloud
*/
public static final CommunicationCloudEnvironment PUBLIC = new CommunicationCloudEnvironment(PUBLIC_VALUE);
/**
* Represent Azure Dod cloud
*/
public static final CommunicationCloudEnvironment DOD = new CommunicationCloudEnvironment(DOD_VALUE);
/**
* Represent Azure Gcch cloud
*/
public static final CommunicationCloudEnvironment GCCH = new CommunicationCloudEnvironment(GCCH_VALUE);
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
return that != null && this.environmentValue.equals(that.toString());
}
@Override
public String toString() {
return environmentValue;
}
@Override
public int hashCode() {
return toString().hashCode();
}
}

Просмотреть файл

@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Code generated by Microsoft (R) AutoRest Code Generator.
package com.azure. android.communication.common;
import com.azure.android.core.util.ExpandableStringEnum;
import com.fasterxml.jackson.annotation.JsonCreator;
import java.util.Collection;
/** Defines values for CommunicationCloudEnvironmentModel. */
public final class CommunicationCloudEnvironmentModel extends ExpandableStringEnum<CommunicationCloudEnvironmentModel> {
/** Static value public for CommunicationCloudEnvironmentModel. */
public static final CommunicationCloudEnvironmentModel PUBLIC = fromString("public");
/** Static value dod for CommunicationCloudEnvironmentModel. */
public static final CommunicationCloudEnvironmentModel DOD = fromString("dod");
/** Static value gcch for CommunicationCloudEnvironmentModel. */
public static final CommunicationCloudEnvironmentModel GCCH = fromString("gcch");
/**
* Creates or finds a CommunicationCloudEnvironmentModel from its string representation.
*
* @param name a name to look for.
* @return the corresponding CommunicationCloudEnvironmentModel.
*/
@JsonCreator
public static CommunicationCloudEnvironmentModel fromString(String name) {
return fromString(name, CommunicationCloudEnvironmentModel.class);
}
/** @return known CommunicationCloudEnvironmentModel values. */
public static Collection<CommunicationCloudEnvironmentModel> values() {
return values(CommunicationCloudEnvironmentModel.class);
}
}

Просмотреть файл

@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
/**
* Common communication identifier for Communication Services
*/
public abstract class CommunicationIdentifier {
}

Просмотреть файл

@ -0,0 +1,116 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Code generated by Microsoft (R) AutoRest Code Generator.
package com.azure.android.communication.common;
import com.azure.android.core.annotation.Fluent;
import com.fasterxml.jackson.annotation.JsonProperty;
/** The CommunicationIdentifierModel model. */
@Fluent
public final class CommunicationIdentifierModel {
/*
* Raw Id of the identifier. Optional in requests, required in responses.
*/
@JsonProperty(value = "rawId")
private String rawId;
/*
* The communication user.
*/
@JsonProperty(value = "communicationUser")
private CommunicationUserIdentifierModel communicationUser;
/*
* The phone number.
*/
@JsonProperty(value = "phoneNumber")
private PhoneNumberIdentifierModel phoneNumber;
/*
* The Microsoft Teams user.
*/
@JsonProperty(value = "microsoftTeamsUser")
private MicrosoftTeamsUserIdentifierModel microsoftTeamsUser;
/**
* Get the rawId property: Raw Id of the identifier. Optional in requests, required in responses.
*
* @return the rawId value.
*/
public String getRawId() {
return this.rawId;
}
/**
* Set the rawId property: Raw Id of the identifier. Optional in requests, required in responses.
*
* @param rawId the rawId value to set.
* @return the CommunicationIdentifierModel object itself.
*/
public CommunicationIdentifierModel setRawId(String rawId) {
this.rawId = rawId;
return this;
}
/**
* Get the communicationUser property: The communication user.
*
* @return the communicationUser value.
*/
public CommunicationUserIdentifierModel getCommunicationUser() {
return this.communicationUser;
}
/**
* Set the communicationUser property: The communication user.
*
* @param communicationUser the communicationUser value to set.
* @return the CommunicationIdentifierModel object itself.
*/
public CommunicationIdentifierModel setCommunicationUser(CommunicationUserIdentifierModel communicationUser) {
this.communicationUser = communicationUser;
return this;
}
/**
* Get the phoneNumber property: The phone number.
*
* @return the phoneNumber value.
*/
public PhoneNumberIdentifierModel getPhoneNumber() {
return this.phoneNumber;
}
/**
* Set the phoneNumber property: The phone number.
*
* @param phoneNumber the phoneNumber value to set.
* @return the CommunicationIdentifierModel object itself.
*/
public CommunicationIdentifierModel setPhoneNumber(PhoneNumberIdentifierModel phoneNumber) {
this.phoneNumber = phoneNumber;
return this;
}
/**
* Get the microsoftTeamsUser property: The Microsoft Teams user.
*
* @return the microsoftTeamsUser value.
*/
public MicrosoftTeamsUserIdentifierModel getMicrosoftTeamsUser() {
return this.microsoftTeamsUser;
}
/**
* Set the microsoftTeamsUser property: The Microsoft Teams user.
*
* @param microsoftTeamsUser the microsoftTeamsUser value to set.
* @return the CommunicationIdentifierModel object itself.
*/
public CommunicationIdentifierModel setMicrosoftTeamsUser(MicrosoftTeamsUserIdentifierModel microsoftTeamsUser) {
this.microsoftTeamsUser = microsoftTeamsUser;
return this;
}
}

Просмотреть файл

@ -0,0 +1,112 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.Objects;
class CommunicationIdentifierSerializer {
/**
* Deserialize CommunicationIdentifierModel into CommunicationIdentifier
* @param identifier CommunicationIdentifierModel to be deserialized
* @return deserialized CommunicationIdentifier
*/
public static CommunicationIdentifier deserialize(CommunicationIdentifierModel identifier) {
Objects.requireNonNull(identifier, "'identifier' cannot be null");
assertSingleType(identifier);
String rawId = identifier.getRawId();
if (identifier.getCommunicationUser() != null) {
Objects.requireNonNull(identifier.getCommunicationUser().getId());
return new CommunicationUserIdentifier(identifier.getCommunicationUser().getId());
}
if (identifier.getPhoneNumber() != null) {
PhoneNumberIdentifierModel phoneNumberModel = identifier.getPhoneNumber();
Objects.requireNonNull(phoneNumberModel.getValue(), "'phoneNumber.value' cannot be null");
Objects.requireNonNull(rawId, "'rawId' cannot be null");
return new PhoneNumberIdentifier(phoneNumberModel.getValue()).setRawId(rawId);
}
if (identifier.getMicrosoftTeamsUser() != null) {
MicrosoftTeamsUserIdentifierModel teamsUserIdentifierModel = identifier.getMicrosoftTeamsUser();
Objects.requireNonNull(teamsUserIdentifierModel.getUserId());
Objects.requireNonNull(teamsUserIdentifierModel.getCloud());
Objects.requireNonNull(rawId);
return new MicrosoftTeamsUserIdentifier(teamsUserIdentifierModel.getUserId(),
teamsUserIdentifierModel.isAnonymous())
.setRawId(rawId)
.setCloudEnvironment(CommunicationCloudEnvironment.fromModel(teamsUserIdentifierModel.getCloud()));
}
Objects.requireNonNull(rawId);
return new UnknownIdentifier(rawId);
}
private static void assertSingleType(CommunicationIdentifierModel identifier) {
CommunicationUserIdentifierModel communicationUser = identifier.getCommunicationUser();
PhoneNumberIdentifierModel phoneNumber = identifier.getPhoneNumber();
MicrosoftTeamsUserIdentifierModel microsoftTeamsUser = identifier.getMicrosoftTeamsUser();
ArrayList<String> presentProperties = new ArrayList<String>();
if (communicationUser != null) {
presentProperties.add(communicationUser.getClass().getName());
}
if (phoneNumber != null) {
presentProperties.add(phoneNumber.getClass().getName());
}
if (microsoftTeamsUser != null) {
presentProperties.add(microsoftTeamsUser.getClass().getName());
}
if (presentProperties.size() > 1) {
throw new IllegalArgumentException(String.format("Only one of the identifier models in %s should be present.",
TextUtils.join(", ", presentProperties)));
}
}
/**
* Serialize CommunicationIdentifier into CommunicationIdentifierModel
* @param identifier CommunicationIdentifier object to be serialized
* @return CommunicationIdentifierModel
* @throws IllegalArgumentException when identifier is an unknown class derived from
* CommunicationIdentifier
*/
public static CommunicationIdentifierModel serialize(CommunicationIdentifier identifier)
throws IllegalArgumentException {
if (identifier instanceof CommunicationUserIdentifier) {
return new CommunicationIdentifierModel()
.setCommunicationUser(
new CommunicationUserIdentifierModel().setId(((CommunicationUserIdentifier) identifier).getId()));
}
if (identifier instanceof PhoneNumberIdentifier) {
PhoneNumberIdentifier phoneNumberIdentifier = (PhoneNumberIdentifier) identifier;
return new CommunicationIdentifierModel()
.setRawId(phoneNumberIdentifier.getRawId())
.setPhoneNumber(new PhoneNumberIdentifierModel().setValue(phoneNumberIdentifier.getPhoneNumber()));
}
if (identifier instanceof MicrosoftTeamsUserIdentifier) {
MicrosoftTeamsUserIdentifier teamsUserIdentifier = (MicrosoftTeamsUserIdentifier) identifier;
return new CommunicationIdentifierModel()
.setRawId(teamsUserIdentifier.getRawId())
.setMicrosoftTeamsUser(new MicrosoftTeamsUserIdentifierModel()
.setIsAnonymous(teamsUserIdentifier.isAnonymous())
.setUserId(teamsUserIdentifier.getUserId())
.setCloud(CommunicationCloudEnvironmentModel.fromString(
teamsUserIdentifier.getCloudEnvironment().toString())));
}
if (identifier instanceof UnknownIdentifier) {
return new CommunicationIdentifierModel()
.setRawId(((UnknownIdentifier) identifier).getId());
}
throw new IllegalArgumentException(String.format("Unknown identifier class '%s'", identifier.getClass().getName()));
}
}

Просмотреть файл

@ -0,0 +1,105 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import com.azure.android.core.credential.AccessToken;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* The Azure Communication Services User token credential.
* <p>
* This class is used to cache/refresh the access token required by Azure Communication Services.
*/
public class CommunicationTokenCredential {
private UserCredential userCredential;
/**
* Creates a {@link CommunicationTokenCredential} from the provided token string.
* <p>
* The same token will be returned whenever {@link #getToken()} is called.
*
* @param userToken token string for initialization
*/
public CommunicationTokenCredential(String userToken) {
this.userCredential = new StaticUserCredential(userToken);
}
/**
* Creates a {@link CommunicationTokenCredential} that automatically refreshes the token
* with a provided {@link java.util.concurrent.Callable} on a background thread.
* <p>
* The cached token is updated if {@link #getToken()} is called and if the difference between the current time and token expiry time is less than 120s.
* <p>
* If {@code refreshProactively} is {@code true}:
* <ul>
* <li>The cached token will be updated in the background when the difference between the current time and token expiry time is less than 600s.</li>
* <li>The cached token will be updated immediately when the constructor is invoked and <code>initialToken</code> is expired</li>
* </ul>
*
* @param tokenRefreshOptions Options object that contains token refresher, initial token string, and refreshProactively
*/
public CommunicationTokenCredential(CommunicationTokenRefreshOptions tokenRefreshOptions) {
this.userCredential = new AutoRefreshUserCredential(
tokenRefreshOptions.getTokenRefresher(),
tokenRefreshOptions.getRefreshProactively(),
tokenRefreshOptions.getToken());
}
/**
* Get Azure core access token from credential
* <p>
* This method returns an asynchronous {@link java.util.concurrent.Future} with the AccessToken.
* When the {@link CommunicationTokenCredential} is constructed with a <code>tokenRefresher</code> {@link java.util.concurrent.Callable},
* the AccessToken will automatically be updated as part of the {@link java.util.concurrent.Future} if the cached token exceeds the expiry threshold.
* <p>
* If this method is called after {@link #dispose()} has been invoked, a cancelled {@link java.util.concurrent.Future} will be returned.
*
* @return Asynchronous {@link java.util.concurrent.Future} with the AccessToken
*/
public Future<AccessToken> getToken() {
if (this.userCredential.isDisposed()) {
return new CancelledTokenFuture();
}
return this.userCredential.getToken();
}
/**
* Invalidates the {@link CommunicationTokenCredential} instance to free up resources for garbage collection.
*/
public void dispose() {
this.userCredential.dispose();
}
private final class CancelledTokenFuture implements Future<AccessToken> {
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return true;
}
@Override
public boolean isDone() {
return true;
}
@Override
public AccessToken get() {
throw new CancellationException();
}
@Override
public AccessToken get(long timeout, TimeUnit unit) {
throw new CancellationException();
}
}
}

Просмотреть файл

@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import java.util.concurrent.Callable;
/**
* Options for refreshing CommunicationTokenCredential
* <p>
* This class is used to define how CommunicationTokenCredential should be refreshed
* </p>
*/
public class CommunicationTokenRefreshOptions {
private final Callable<String> tokenRefresher;
private final boolean refreshProactively;
private final String initialToken;
/**
* Creates a {@link CommunicationTokenRefreshOptions} object
* <p>
* Access token will be fetched on demand and may optionally enable proactive refreshing
* </p>
*
* @param tokenRefresher the token refresher to provide capacity to fetch fresh token, cannot be null
* @param refreshProactively when set to true, turn on proactive fetching to call
* tokenRefresher before token expiry by minutes set
* with setCallbackOffsetMinutes or default value of
* two minutes
*/
public CommunicationTokenRefreshOptions(Callable<String> tokenRefresher, boolean refreshProactively) {
if (tokenRefresher == null) {
throw new IllegalArgumentException("Missing required parameters 'tokenRefresher'.");
}
this.tokenRefresher = tokenRefresher;
this.refreshProactively = refreshProactively;
this.initialToken = null;
}
/**
* Creates a {@link CommunicationTokenRefreshOptions} object
* <p>
* A valid token is supplied and may optionally enable proactive refreshing
* </p>
*
* @param tokenRefresher the token refresher to provide capacity to fetch fresh token, cannot be null
* @param refreshProactively when set to true, turn on proactive fetching to call
* tokenRefresher before token expiry by minutes set
* with setCallbackOffsetMinutes or default value of
* two minutes
* @param token the serialized JWT token, cannot be null
*/
public CommunicationTokenRefreshOptions(Callable<String> tokenRefresher, boolean refreshProactively, String initialToken) {
if (tokenRefresher == null) {
throw new IllegalArgumentException("Missing required parameters 'tokenRefresher'.");
}
if (initialToken == null) {
throw new IllegalArgumentException("Missing required parameters 'initialToken'.");
}
this.tokenRefresher = tokenRefresher;
this.refreshProactively = refreshProactively;
this.initialToken = initialToken;
}
/**
* @return the token refresher to provide capacity to fetch fresh token
*/
public Callable<String> getTokenRefresher() {
return tokenRefresher;
}
/**
* @return whether or not to refresh token proactively
*/
public boolean getRefreshProactively() {
return refreshProactively;
}
/**
* @return the serialized JWT token
*/
public String getToken() {
return initialToken;
}
}

Просмотреть файл

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import com.azure.android.core.util.CoreUtil;
/**
* Communication identifier for Communication Services Users
*/
public class CommunicationUserIdentifier extends CommunicationIdentifier {
private final String id;
/**
* Creates a CommunicationUserIdentifier object
*
* @param id identifier of the communication user.
* @throws IllegalArgumentException thrown if id parameter fail the validation.
*/
public CommunicationUserIdentifier(String id) {
if (CoreUtil.isNullOrEmpty(id)) {
throw new IllegalArgumentException("The initialization parameter [id] cannot be null or empty.");
}
this.id = id;
}
/**
* Get id of the communication user.
* @return id of the communication user.
*/
public String getId() {
return id;
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (!(that instanceof CommunicationUserIdentifier)) {
return false;
}
return ((CommunicationUserIdentifier) that).getId().equals(id);
}
@Override
public int hashCode() {
return getId().hashCode();
}
}

Просмотреть файл

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Code generated by Microsoft (R) AutoRest Code Generator.
package com.azure.android.communication.common;
import com.azure.android.core.annotation.Fluent;
import com.fasterxml.jackson.annotation.JsonProperty;
/** The CommunicationUserIdentifierModel model. */
@Fluent
public final class CommunicationUserIdentifierModel {
/*
* The Id of the communication user.
*/
@JsonProperty(value = "id", required = true)
private String id;
/**
* Get the id property: The Id of the communication user.
*
* @return the id value.
*/
public String getId() {
return this.id;
}
/**
* Set the id property: The Id of the communication user.
*
* @param id the id value to set.
* @return the CommunicationUserIdentifierModel object itself.
*/
public CommunicationUserIdentifierModel setId(String id) {
this.id = id;
return this;
}
}

Просмотреть файл

@ -0,0 +1,149 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import com.azure.android.core.util.CoreUtil;
import java.util.Objects;
/**
* Communication identifier for Microsoft Teams User
*/
public class MicrosoftTeamsUserIdentifier extends CommunicationIdentifier {
private final String userId;
private final boolean isAnonymous;
private CommunicationCloudEnvironment cloudEnvironment = CommunicationCloudEnvironment.PUBLIC;
private String rawId;
/**
* Creates a MicrosoftTeamsUserIdentifier object
*
* @param userId Id of the Microsoft Teams user. If the user isn't anonymous, the id is the AAD object id of the user.
* @param isAnonymous set this to true if the user is anonymous,
* for example when joining a meeting with a share link
* @param cloudEnvironment the cloud environment in which this identifier is created
* @throws IllegalArgumentException thrown if userId parameter fail the validation.
*/
public MicrosoftTeamsUserIdentifier(String userId, boolean isAnonymous, CommunicationCloudEnvironment cloudEnvironment) {
this(userId, isAnonymous);
Objects.requireNonNull(cloudEnvironment);
this.cloudEnvironment = cloudEnvironment;
}
/**
* Creates a MicrosoftTeamsUserIdentifier object
*
* @param userId Id of the Microsoft Teams user. If the user isn't anonymous, the id is the AAD object id of the user.
* @param isAnonymous set this to true if the user is anonymous,
* for example when joining a meeting with a share link
* @throws IllegalArgumentException thrown if userId parameter fail the validation.
*/
public MicrosoftTeamsUserIdentifier(String userId, boolean isAnonymous) {
if (CoreUtil.isNullOrEmpty(userId)) {
throw new IllegalArgumentException("The initialization parameter [userId] cannot be null or empty.");
}
this.userId = userId;
this.isAnonymous = isAnonymous;
}
/**
* Creates a MicrosoftTeamsUserIdentifier object
*
* @param userId Id of the Microsoft Teams user. If the user isn't anonymous, the id is the AAD object id of the user.
* @throws IllegalArgumentException thrown if userId parameter fail the validation.
*/
public MicrosoftTeamsUserIdentifier(String userId) {
this(userId, false);
}
/**
* Get Teams User Id
* @return userId Id of the Microsoft Teams user. If the user isn't anonymous, the id is the AAD object id of the user.
*/
public String getUserId() {
return this.userId;
}
/**
* @return True if the user is anonymous, for example when joining a meeting with a share link.
*/
public boolean isAnonymous() {
return this.isAnonymous;
}
/**
* Set cloud environment of the Teams user identifier
* @param cloudEnvironment the cloud environment in which this identifier is created
* @return this object
*/
public MicrosoftTeamsUserIdentifier setCloudEnvironment(CommunicationCloudEnvironment cloudEnvironment) {
this.cloudEnvironment = cloudEnvironment;
return this;
}
/**
* Get cloud environment of the Teams user identifier
* @return cloud environment in which this identifier is created
*/
public CommunicationCloudEnvironment getCloudEnvironment() {
return cloudEnvironment;
}
/**
* Get full id of the identifier. This id is optional.
* @return full id of the identifier
*/
public String getRawId() {
return rawId;
}
/**
* Set full id of the identifier
* @param rawId full id of the identifier
* @return CommunicationIdentifier object itself
*/
public MicrosoftTeamsUserIdentifier setRawId(String rawId) {
this.rawId = rawId;
return this;
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (!(that instanceof MicrosoftTeamsUserIdentifier)) {
return false;
}
MicrosoftTeamsUserIdentifier thatId = (MicrosoftTeamsUserIdentifier) that;
if (!thatId.getUserId().equals(this.getUserId())
|| thatId.isAnonymous != this.isAnonymous) {
return false;
}
if (cloudEnvironment != null && !cloudEnvironment.equals(thatId.cloudEnvironment)) {
return false;
}
if (thatId.cloudEnvironment != null && !thatId.cloudEnvironment.equals(this.cloudEnvironment)) {
return false;
}
return getRawId() == null
|| thatId.getRawId() == null
|| thatId.getRawId().equals(this.getRawId());
}
@Override
public int hashCode() {
return userId.hashCode();
}
}

Просмотреть файл

@ -0,0 +1,95 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Code generated by Microsoft (R) AutoRest Code Generator.
package com.azure.android.communication.common;
import com.azure.android.core.annotation.Fluent;
import com.fasterxml.jackson.annotation.JsonProperty;
/** The MicrosoftTeamsUserIdentifierModel model. */
@Fluent
public final class MicrosoftTeamsUserIdentifierModel {
/*
* The Id of the Microsoft Teams user. If not anonymous, this is the AAD
* object Id of the user.
*/
@JsonProperty(value = "userId", required = true)
private String userId;
/*
* True if the Microsoft Teams user is anonymous. By default false if
* missing.
*/
@JsonProperty(value = "isAnonymous")
private Boolean isAnonymous;
/*
* The cloud that the Microsoft Teams user belongs to. By default 'public'
* if missing.
*/
@JsonProperty(value = "cloud")
private CommunicationCloudEnvironmentModel cloud;
/**
* Get the userId property: The Id of the Microsoft Teams user. If not anonymous, this is the AAD object Id of the
* user.
*
* @return the userId value.
*/
public String getUserId() {
return this.userId;
}
/**
* Set the userId property: The Id of the Microsoft Teams user. If not anonymous, this is the AAD object Id of the
* user.
*
* @param userId the userId value to set.
* @return the MicrosoftTeamsUserIdentifierModel object itself.
*/
public MicrosoftTeamsUserIdentifierModel setUserId(String userId) {
this.userId = userId;
return this;
}
/**
* Get the isAnonymous property: True if the Microsoft Teams user is anonymous. By default false if missing.
*
* @return the isAnonymous value.
*/
public Boolean isAnonymous() {
return this.isAnonymous;
}
/**
* Set the isAnonymous property: True if the Microsoft Teams user is anonymous. By default false if missing.
*
* @param isAnonymous the isAnonymous value to set.
* @return the MicrosoftTeamsUserIdentifierModel object itself.
*/
public MicrosoftTeamsUserIdentifierModel setIsAnonymous(Boolean isAnonymous) {
this.isAnonymous = isAnonymous;
return this;
}
/**
* Get the cloud property: The cloud that the Microsoft Teams user belongs to. By default 'public' if missing.
*
* @return the cloud value.
*/
public CommunicationCloudEnvironmentModel getCloud() {
return this.cloud;
}
/**
* Set the cloud property: The cloud that the Microsoft Teams user belongs to. By default 'public' if missing.
*
* @param cloud the cloud value to set.
* @return the MicrosoftTeamsUserIdentifierModel object itself.
*/
public MicrosoftTeamsUserIdentifierModel setCloud(CommunicationCloudEnvironmentModel cloud) {
this.cloud = cloud;
return this;
}
}

Просмотреть файл

@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import com.azure.android.core.util.CoreUtil;
/**
* Communication identifier for Communication Services Phone Numbers
*/
public class PhoneNumberIdentifier extends CommunicationIdentifier {
private final String phoneNumber;
private String rawId;
/**
* Creates a PhoneNumberIdentifier object
*
* @param phoneNumber the string identifier representing the PhoneNumber in E.164 format.
* E.164 is a phone number formatted as +[CountryCode][AreaCode][LocalNumber] eg. "+18005555555"
* @throws IllegalArgumentException thrown if phoneNumber parameter fail the validation.
*/
public PhoneNumberIdentifier(String phoneNumber) {
if (CoreUtil.isNullOrEmpty(phoneNumber)) {
throw new IllegalArgumentException("The initialization parameter [phoneNumber] cannot be null to empty.");
}
this.phoneNumber = phoneNumber;
}
/**
* @return the string identifier representing the object identity
*/
public String getPhoneNumber() {
return phoneNumber;
}
/**
* Get full id of the identifier. This id is optional.
* @return full id of the identifier
*/
public String getRawId() {
return rawId;
}
/**
* Set full id of the identifier
* @param rawId full id of the identifier
* @return PhoneNumberIdentifier object itself
*/
public PhoneNumberIdentifier setRawId(String rawId) {
this.rawId = rawId;
return this;
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (!(that instanceof PhoneNumberIdentifier)) {
return false;
}
PhoneNumberIdentifier phoneId = (PhoneNumberIdentifier) that;
if (!phoneNumber.equals(phoneId.phoneNumber)) {
return false;
}
return getRawId() == null
|| phoneId.getRawId() == null
|| getRawId().equals(phoneId.getRawId());
}
@Override
public int hashCode() {
return phoneNumber.hashCode();
}
}

Просмотреть файл

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Code generated by Microsoft (R) AutoRest Code Generator.
package com.azure.android.communication.common;
import com.azure.android.core.annotation.Fluent;
import com.fasterxml.jackson.annotation.JsonProperty;
/** The PhoneNumberIdentifierModel model. */
@Fluent
public final class PhoneNumberIdentifierModel {
/*
* The phone number in E.164 format.
*/
@JsonProperty(value = "value", required = true)
private String value;
/**
* Get the value property: The phone number in E.164 format.
*
* @return the value value.
*/
public String getValue() {
return this.value;
}
/**
* Set the value property: The phone number in E.164 format.
*
* @param value the value value to set.
* @return the PhoneNumberIdentifierModel object itself.
*/
public PhoneNumberIdentifierModel setValue(String value) {
this.value = value;
return this;
}
}

Просмотреть файл

@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import com.azure.android.core.credential.AccessToken;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
class StaticUserCredential extends UserCredential {
private Future<AccessToken> tokenFuture;
StaticUserCredential(String userToken) {
AccessToken accessToken = TokenParser.createAccessToken(userToken);
this.tokenFuture = new CompletedTokenFuture(accessToken);
}
@Override
public Future<AccessToken> getToken() {
return this.tokenFuture;
}
private final class CompletedTokenFuture implements Future<AccessToken> {
private final AccessToken accessToken;
CompletedTokenFuture(AccessToken accessToken) {
this.accessToken = accessToken;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return true;
}
@Override
public AccessToken get() {
return this.accessToken;
}
@Override
public AccessToken get(long timeout, TimeUnit unit) {
return accessToken;
}
}
}

Просмотреть файл

@ -0,0 +1,55 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import android.util.Base64;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.threeten.bp.Instant;
import org.threeten.bp.OffsetDateTime;
import org.threeten.bp.ZoneId;
import com.azure.android.core.credential.AccessToken;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Utility for Handling Access Tokens.
*/
final class TokenParser {
private static final ObjectMapper jsonMapper = new ObjectMapper();
private TokenParser() {
// Empty constructor to prevent instantiation of this class.
}
/**
* Create AccessToken object from Token string
*
* @param tokenStr token string
* @return AccessToken instance
*/
static AccessToken createAccessToken(String tokenStr) {
try {
Objects.requireNonNull(tokenStr, "'tokenStr' cannot be null.");
String[] tokenParts = tokenStr.split("\\.");
String tokenPayload = tokenParts[1];
byte[] decodedBytes = Base64.decode(tokenPayload, Base64.DEFAULT);
String decodedPayloadJson = new String(decodedBytes, StandardCharsets.UTF_8);
ObjectNode payloadObj = jsonMapper.readValue(decodedPayloadJson, ObjectNode.class);
long expire = payloadObj.get("exp").longValue();
OffsetDateTime offsetExpiry = OffsetDateTime.ofInstant(Instant.ofEpochMilli(expire * 1000), ZoneId.of("UTC"));
return new AccessToken(tokenStr, offsetExpiry);
} catch (Exception e) {
throw new IllegalArgumentException("'tokenStr' is not a valid token string", e);
}
}
}

Просмотреть файл

@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import com.azure.android.core.util.CoreUtil;
/**
* Catch-all for all other Communication identifiers for Communication Services
*/
public class UnknownIdentifier extends CommunicationIdentifier {
private final String id;
/**
* Creates an UnknownIdentifier object
*
* @param id the string identifier representing the identity
* @throws IllegalArgumentException thrown if id parameter fail the validation.
*/
public UnknownIdentifier(String id) {
if (CoreUtil.isNullOrEmpty(id)) {
throw new IllegalArgumentException("The initialization parameter [id] cannot be null or empty.");
}
this.id = id;
}
/**
* Get id of this identifier
* @return id of this identifier
*/
public String getId() {
return id;
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (!(that instanceof UnknownIdentifier)) {
return false;
}
UnknownIdentifier thatId = (UnknownIdentifier) that;
return this.id.equals(thatId.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
}

Просмотреть файл

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import java.util.concurrent.Future;
import com.azure.android.core.credential.AccessToken;
abstract class UserCredential {
private boolean isDisposed = false;
abstract Future<AccessToken> getToken();
void dispose() {
this.isDisposed = true;
}
boolean isDisposed() {
return this.isDisposed;
}
}

Просмотреть файл

@ -0,0 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/**
* Common code for all Azure Communication Service libraries.
*/
package com.azure.android.communication.common;

Просмотреть файл

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package android.util;
/**
* Mock for android.util.Base64
*/
public class Base64 {
public static final int DEFAULT = 0;
public static String encodeToString(byte[] input, int flags) {
return java.util.Base64.getEncoder().encodeToString(input);
}
public static byte[] decode(String input, int flags) {
return java.util.Base64.getDecoder().decode(input);
}
}

Просмотреть файл

@ -0,0 +1,97 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.util.Arrays;
import static com.azure.android.communication.common.CommunicationCloudEnvironmentModel.PUBLIC;
import static junit.framework.TestCase.assertNotNull;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
public class CommunicationIdentifierSerializerTests {
final String someId = "some id";
final String teamsUserId = "Teams user id";
final String rawId = "some lengthy id string";
final String testPhoneNumber = "+12223334444";
@Test
public void serializeCommunicationUser() {
CommunicationIdentifierModel model = CommunicationIdentifierSerializer.serialize(
new CommunicationUserIdentifier(someId));
assertNotNull(model.getCommunicationUser());
assertEquals(someId, model.getCommunicationUser().getId());
}
@Test
public void deserializeCommunicationUser() {
CommunicationIdentifier identifier = CommunicationIdentifierSerializer.deserialize(
new CommunicationIdentifierModel()
.setCommunicationUser(new CommunicationUserIdentifierModel().setId(someId)));
assertEquals(identifier.getClass(), CommunicationUserIdentifier.class);
assertEquals(someId, ((CommunicationUserIdentifier) identifier).getId());
}
@Test
public void serializeUnknown() {
CommunicationIdentifierModel model = CommunicationIdentifierSerializer.serialize(
new UnknownIdentifier(someId));
assertEquals(someId, model.getRawId());
}
@Test
public void deserializeUnknown() {
CommunicationIdentifier unknownIdentifier = CommunicationIdentifierSerializer.deserialize(
new CommunicationIdentifierModel()
.setRawId(rawId));
assertEquals(UnknownIdentifier.class, unknownIdentifier.getClass());
assertEquals(rawId, ((UnknownIdentifier) unknownIdentifier).getId());
}
@Test
public void serializeFutureTypeShouldThrow() {
assertThrows(IllegalArgumentException.class,
() -> {
CommunicationIdentifierSerializer.serialize(
new CommunicationIdentifier() {
public String getId() {
return someId;
}
});
});
}
@Test
public void serializePhoneNumber() {
final String phoneNumber = "+12223334444";
CommunicationIdentifierModel model = CommunicationIdentifierSerializer.serialize(
new PhoneNumberIdentifier(phoneNumber).setRawId(rawId));
assertNotNull(model.getPhoneNumber());
assertEquals(phoneNumber, model.getPhoneNumber().getValue());
assertEquals(rawId, model.getRawId());
}
@Test
public void deserializePhoneNumber() {
CommunicationIdentifier identifier = CommunicationIdentifierSerializer.deserialize(
new CommunicationIdentifierModel()
.setRawId(rawId)
.setPhoneNumber(new PhoneNumberIdentifierModel().setValue(testPhoneNumber)));
assertEquals(PhoneNumberIdentifier.class, identifier.getClass());
assertEquals(testPhoneNumber, ((PhoneNumberIdentifier) identifier).getPhoneNumber());
assertEquals(rawId, ((PhoneNumberIdentifier) identifier).getRawId());
}
}

Просмотреть файл

@ -0,0 +1,431 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import com.azure.android.core.credential.AccessToken;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.*;
public class CommunicationTokenCredentialTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test()
public void constructor_withStaticInitialTokenInvalid() {
expectedException.expect(is(instanceOf(IllegalArgumentException.class)));
new CommunicationTokenCredential("This is an invalid token string");
}
@Test
public void constructor_withTokenRefresher_withInitialTokenInvalid() {
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
expectedException.expect(is(instanceOf(IllegalArgumentException.class)));
new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, false, "This is an invalid token string"));
}
@Test
public void constructor_withTokenRefresher_proactiveRefresh_noInitialToken_refresh() throws InterruptedException, ExecutionException {
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
String refreshedToken = TokenStubHelper.createTokenStringForOffset(900);
mockTokenRefresher.setToken(refreshedToken);
CountDownLatch countDownLatch = new CountDownLatch(1);
mockTokenRefresher.setOnCallReturn(countDownLatch::countDown);
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, true));
countDownLatch.await();
AccessToken accessToken = credential.getToken().get();
assertEquals(refreshedToken, accessToken.getToken());
assertEquals(1, mockTokenRefresher.getCallCount());
}
@Test
public void constructor_withTokenRefresher_proactiveRefresh_initialTokenPastThreshold_refresh() throws InterruptedException {
String tokenString = TokenStubHelper.createTokenStringForOffset(600);
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
CountDownLatch countDownLatch = new CountDownLatch(1);
mockTokenRefresher.setOnCallReturn(countDownLatch::countDown);
new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, true, tokenString));
countDownLatch.await();
assertEquals(1, mockTokenRefresher.getCallCount());
}
@Test
public void constructor_withTokenRefresher_proactiveRefresh_initialTokenWithinThreshold_notRefreshed() throws InterruptedException, ExecutionException {
String tokenString = TokenStubHelper.createTokenStringForOffset(700);
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, true, tokenString));
AccessToken accessToken = credential.getToken().get();
assertEquals(tokenString, accessToken.getToken());
assertEquals(0, mockTokenRefresher.getCallCount());
}
@Test
public void constructor_withTokenRefresher_proactiveRefresh_withInitialTokenInvalid() {
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
expectedException.expect(is(instanceOf(IllegalArgumentException.class)));
new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, true, "This is an invalid token string"));
}
@Test
public void constructor_withTokenRefresher_proactiveRefresh_repeats() throws InterruptedException {
// Set up MockTokenRefresher to return token with expiry that immediately needs to be refreshed
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
String tokenPastThreshold = TokenStubHelper.createTokenStringForOffset(600);
mockTokenRefresher.setToken(tokenPastThreshold);
// Limit testing repeats
int numRepeats = 10;
CountDownLatch countDownLatch = new CountDownLatch(numRepeats);
mockTokenRefresher.setOnCallReturn(() -> {
if (countDownLatch.getCount() == 1) {
// Last token returned will not require immediate refresh
String tokenWithinThreshold = TokenStubHelper.createTokenStringForOffset(1200);
mockTokenRefresher.setToken(tokenWithinThreshold);
}
countDownLatch.countDown();
});
new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, true));
countDownLatch.await();
assertEquals(numRepeats, mockTokenRefresher.getCallCount());
}
@Test
public void getToken_staticInitialTokenActive() throws ExecutionException, InterruptedException {
long expiryEpochSecond = System.currentTimeMillis() / 1000 + 60;
String tokenString = TokenStubHelper.createTokenString(expiryEpochSecond);
CommunicationTokenCredential credential = new CommunicationTokenCredential(tokenString);
AccessToken accessToken = credential.getToken().get();
assertEquals(tokenString, accessToken.getToken());
assertEquals(expiryEpochSecond, accessToken.getExpiresAt().toEpochSecond());
assertFalse(accessToken.isExpired());
}
@Test
public void getToken_staticInitialTokenExpired() throws ExecutionException, InterruptedException {
long expiryEpochSecond = System.currentTimeMillis() / 1000 - 60;
String tokenString = TokenStubHelper.createTokenString(expiryEpochSecond);
CommunicationTokenCredential credential = new CommunicationTokenCredential(tokenString);
AccessToken accessToken = credential.getToken().get();
assertEquals(tokenString, accessToken.getToken());
assertEquals(expiryEpochSecond, accessToken.getExpiresAt().toEpochSecond());
assertTrue(accessToken.isExpired());
}
@Test
public void getToken_staticInitialToken_isDisposed_cancelledFuture() throws ExecutionException, InterruptedException {
String tokenString = TokenStubHelper.createTokenStringForOffset(1200);
CommunicationTokenCredential credential = new CommunicationTokenCredential(tokenString);
credential.dispose();
Future<AccessToken> accessTokenFuture = credential.getToken();
assertTrue(accessTokenFuture.isDone());
assertTrue(accessTokenFuture.isCancelled());
expectedException.expect(is(instanceOf(CancellationException.class)));
accessTokenFuture.get();
}
@Test
public void getToken_onDemandAutoRefresh_noInitialToken_firstFetch() throws ExecutionException, InterruptedException {
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
String refreshedToken = TokenStubHelper.createTokenStringForOffset(300);
mockTokenRefresher.setToken(refreshedToken);
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, false));
AccessToken accessToken = credential.getToken().get();
assertEquals(refreshedToken, accessToken.getToken());
assertEquals(1, mockTokenRefresher.getCallCount());
assertFalse(accessToken.isExpired());
}
@Test
public void getToken_onDemandAutoRefresh_noInitialToken_firstFetch_exception() throws ExecutionException, InterruptedException {
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
String refreshedToken = TokenStubHelper.createTokenStringForOffset(300);
mockTokenRefresher.setToken(refreshedToken);
RuntimeException mockTokenRefresherException = new RuntimeException("Mock Token Refresh Exception");
mockTokenRefresher.setOnCallReturn(() -> {
throw mockTokenRefresherException;
});
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, false));
try {
expectedException.expectCause(is(mockTokenRefresherException));
credential.getToken().get();
} finally {
assertEquals(1, mockTokenRefresher.getCallCount());
}
}
@Test
public void getToken_onDemandAutoRefresh_noInitialToken_firstFetch_multithreadedSameResult() throws ExecutionException, InterruptedException {
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
String refreshedToken = TokenStubHelper.createTokenStringForOffset(300);
mockTokenRefresher.setToken(refreshedToken);
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, false));
// Set up blocked multithreaded calls
Runnable blockedRefresh = this.arrangeBlockedRefresh(mockTokenRefresher);
int numCalls = 5;
List<Future<AccessToken>> accessTokenFutures = new ArrayList<>();
ExecutorService executorService = Executors.newFixedThreadPool(numCalls);
for (int i = 0; i < numCalls; i++) {
Future<AccessToken> accessTokenFuture = executorService.submit(() -> credential.getToken().get());
accessTokenFutures.add(accessTokenFuture);
}
// Unblock refresh and wait for results
blockedRefresh.run();
Set<AccessToken> accessTokenResults = new HashSet<>();
for (Future<AccessToken> accessTokenFuture : accessTokenFutures) {
accessTokenResults.add(accessTokenFuture.get());
}
executorService.shutdown();
assertEquals(1, accessTokenResults.size());
assertEquals(1, mockTokenRefresher.getCallCount());
}
@Test
public void getToken_onDemandAutoRefresh_tokenWithinThresholdNotRefreshed_singleCall() throws ExecutionException, InterruptedException {
String tokenString = TokenStubHelper.createTokenStringForOffset(130);
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, false, tokenString));
AccessToken accessToken = credential.getToken().get();
assertEquals(tokenString, accessToken.getToken());
assertEquals(0, mockTokenRefresher.getCallCount());
}
@Test
public void getToken_onDemandAutoRefresh_tokenWithinThresholdNotRefreshed_multithreadedCalls() throws ExecutionException, InterruptedException {
String tokenString = TokenStubHelper.createTokenStringForOffset(130);
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, false, tokenString));
// Set up blocked multithreaded calls
Runnable blockedRefresh = this.arrangeBlockedRefresh(mockTokenRefresher);
int numCalls = 5;
List<Future<AccessToken>> accessTokenFutures = new ArrayList<>();
ExecutorService executorService = Executors.newFixedThreadPool(numCalls);
for (int i = 0; i < numCalls; i++) {
Future<AccessToken> accessTokenFuture = executorService.submit(() -> credential.getToken().get());
accessTokenFutures.add(accessTokenFuture);
}
// Unblock refresh and wait for results
blockedRefresh.run();
Set<AccessToken> accessTokenResults = new HashSet<>();
for (Future<AccessToken> accessTokenFuture : accessTokenFutures) {
accessTokenResults.add(accessTokenFuture.get());
}
executorService.shutdown();
assertEquals(1, accessTokenResults.size());
assertEquals(tokenString, accessTokenResults.iterator().next().getToken());
assertEquals(0, mockTokenRefresher.getCallCount());
}
@Test
public void getToken_onDemandAutoRefresh_tokenPastThresholdRefreshed_singleCall() throws ExecutionException, InterruptedException {
String tokenString = TokenStubHelper.createTokenStringForOffset(120);
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
String refreshedToken = TokenStubHelper.createTokenStringForOffset(300);
mockTokenRefresher.setToken(refreshedToken);
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, false, tokenString));
AccessToken accessToken = credential.getToken().get();
assertNotEquals(tokenString, accessToken.getToken());
assertEquals(refreshedToken, accessToken.getToken());
assertEquals(1, mockTokenRefresher.getCallCount());
}
@Test
public void getToken_onDemandAutoRefresh_tokenPastThresholdRefreshed_exception() throws ExecutionException, InterruptedException {
String tokenString = TokenStubHelper.createTokenStringForOffset(120);
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
RuntimeException mockTokenRefresherException = new RuntimeException("Mock Token Refresh Exception");
mockTokenRefresher.setOnCallReturn(() -> {
throw mockTokenRefresherException;
});
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, false, tokenString));
try {
expectedException.expectCause(is(mockTokenRefresherException));
credential.getToken().get();
} finally {
assertEquals(1, mockTokenRefresher.getCallCount());
}
}
@Test
public void getToken_onDemandAutoRefresh_tokenPastThresholdRefreshed_multipleCalls() throws ExecutionException, InterruptedException {
String tokenString = TokenStubHelper.createTokenStringForOffset(120);
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
String refreshedToken = TokenStubHelper.createTokenStringForOffset(300);
mockTokenRefresher.setToken(refreshedToken);
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, false, tokenString));
Set<AccessToken> accessTokens = new HashSet<>();
int numCalls = 3;
for (int i = 0; i < numCalls; i++) {
AccessToken accessToken = credential.getToken().get();
accessTokens.add(accessToken);
}
assertEquals(1, accessTokens.size());
assertNotEquals(tokenString, accessTokens.iterator().next().getToken());
assertEquals(refreshedToken, accessTokens.iterator().next().getToken());
assertEquals(1, mockTokenRefresher.getCallCount());
}
@Test
public void getToken_onDemandAutoRefresh_tokenPastThresholdRefreshed_multithreadedCalls() throws ExecutionException, InterruptedException {
String tokenString = TokenStubHelper.createTokenStringForOffset(120);
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
String refreshedToken = TokenStubHelper.createTokenStringForOffset(300);
mockTokenRefresher.setToken(refreshedToken);
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, false, tokenString));
// Set up blocked multithreaded calls
Runnable blockedRefresh = this.arrangeBlockedRefresh(mockTokenRefresher);
int numCalls = 5;
List<Future<AccessToken>> accessTokenFutures = new ArrayList<>();
ExecutorService executorService = Executors.newFixedThreadPool(numCalls);
for (int i = 0; i < numCalls; i++) {
Future<AccessToken> accessTokenFuture = executorService.submit(() -> credential.getToken().get());
accessTokenFutures.add(accessTokenFuture);
}
// Unblock refresh and wait for results
blockedRefresh.run();
Set<AccessToken> accessTokenResults = new HashSet<>();
for (Future<AccessToken> accessTokenFuture : accessTokenFutures) {
accessTokenResults.add(accessTokenFuture.get());
}
executorService.shutdown();
assertEquals(1, accessTokenResults.size());
assertNotEquals(tokenString, accessTokenResults.iterator().next().getToken());
assertEquals(refreshedToken, accessTokenResults.iterator().next().getToken());
assertEquals(1, mockTokenRefresher.getCallCount());
}
@Test
public void getToken_onDemandAutoRefresh_isDisposed_cancelledFuture() throws ExecutionException, InterruptedException {
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, false));
credential.dispose();
Future<AccessToken> accessTokenFuture = credential.getToken();
assertTrue(accessTokenFuture.isDone());
assertTrue(accessTokenFuture.isCancelled());
expectedException.expect(is(instanceOf(CancellationException.class)));
accessTokenFuture.get();
}
@Test
public void getToken_whileProactiveRefresh_singleResult() throws ExecutionException, InterruptedException {
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
String refreshedToken = TokenStubHelper.createTokenStringForOffset(1200);
mockTokenRefresher.setToken(refreshedToken);
Runnable blockedRefresh = this.arrangeBlockedRefresh(mockTokenRefresher);
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, true));
Future<AccessToken> accessTokenFuture = credential.getToken();
blockedRefresh.run();
AccessToken accessToken = accessTokenFuture.get();
assertEquals(refreshedToken, accessToken.getToken());
assertEquals(1, mockTokenRefresher.getCallCount());
}
@Test
public void dispose_onDemandAutoRefresh_inProgressCancelled() throws ExecutionException, InterruptedException {
String tokenString = TokenStubHelper.createTokenStringForOffset(120);
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
String refreshedToken = TokenStubHelper.createTokenStringForOffset(300);
mockTokenRefresher.setToken(refreshedToken);
Runnable blockedRefresh = this.arrangeBlockedRefresh(mockTokenRefresher);
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, false, tokenString));
Future<AccessToken> accessTokenFuture = credential.getToken();
credential.dispose();
blockedRefresh.run();
assertTrue(accessTokenFuture.isCancelled());
assertTrue(accessTokenFuture.isDone());
expectedException.expect(is(instanceOf(CancellationException.class)));
accessTokenFuture.get();
}
@Test
public void dispose_proactiveRefresh_inProgressCancelled() throws ExecutionException, InterruptedException {
MockTokenRefresher mockTokenRefresher = new MockTokenRefresher();
String refreshedToken = TokenStubHelper.createTokenStringForOffset(1200);
mockTokenRefresher.setToken(refreshedToken);
Runnable blockedRefresh = this.arrangeBlockedRefresh(mockTokenRefresher);
CommunicationTokenCredential credential = new CommunicationTokenCredential(new CommunicationTokenRefreshOptions(mockTokenRefresher, true));
Future<AccessToken> accessTokenFuture = credential.getToken();
credential.dispose();
blockedRefresh.run();
assertTrue(accessTokenFuture.isCancelled());
assertTrue(accessTokenFuture.isDone());
expectedException.expect(is(instanceOf(CancellationException.class)));
accessTokenFuture.get();
}
private Runnable arrangeBlockedRefresh(MockTokenRefresher mockTokenRefresher) {
CountDownLatch countDownLatch = new CountDownLatch(1);
mockTokenRefresher.setOnCallReturn(() -> {
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
return countDownLatch::countDown;
}
}

Просмотреть файл

@ -0,0 +1,62 @@
package com.azure.android.communication.common;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.List;
import static com.azure.android.communication.common.CommunicationCloudEnvironmentModel.PUBLIC;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
@RunWith(Parameterized.class)
public class IdentifierDeserializationExceptionTests {
static final String someId = "some id";
static final String teamsUserId = "Teams user id";
static final String rawId = "some lengthy id string";
static final String testPhoneNumber = "+12223334444";
@Rule
public ExpectedException expectedException = ExpectedException.none();
private final CommunicationIdentifierModel identifierModel;
public IdentifierDeserializationExceptionTests(CommunicationIdentifierModel identifierModel) {
this.identifierModel = identifierModel;
}
@Parameterized.Parameters
public static List<CommunicationIdentifierModel> cases() {
return Arrays.asList(new CommunicationIdentifierModel()
.setRawId(rawId)
.setCommunicationUser(new CommunicationUserIdentifierModel()
.setId(someId))
.setPhoneNumber(new PhoneNumberIdentifierModel()
.setValue(testPhoneNumber)),
new CommunicationIdentifierModel()
.setRawId(rawId)
.setCommunicationUser(new CommunicationUserIdentifierModel()
.setId(someId))
.setMicrosoftTeamsUser(new MicrosoftTeamsUserIdentifierModel()
.setUserId(teamsUserId)
.setIsAnonymous(true)
.setCloud(PUBLIC)),
new CommunicationIdentifierModel()
.setRawId(rawId)
.setPhoneNumber(new PhoneNumberIdentifierModel()
.setValue(testPhoneNumber))
.setMicrosoftTeamsUser(new MicrosoftTeamsUserIdentifierModel()
.setUserId(teamsUserId)
.setIsAnonymous(true)
.setCloud(PUBLIC)));
}
@Test
public void throwsOnMoreThanOneNestedObject() {
expectedException.expect(is(instanceOf(IllegalArgumentException.class)));
CommunicationIdentifierSerializer.deserialize(identifierModel);
}
}

Просмотреть файл

@ -0,0 +1,52 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.List;
import static com.azure.android.communication.common.CommunicationCloudEnvironmentModel.PUBLIC;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
@RunWith(Parameterized.class)
public class IdentifierSerialzationExceptionTests {
static final String teamsUserId = "Teams user id";
static final String rawId = "some lengthy id string";
@Rule
public ExpectedException expectedException = ExpectedException.none();
private final CommunicationIdentifierModel identifierModel;
public IdentifierSerialzationExceptionTests(CommunicationIdentifierModel identifierModel) {
this.identifierModel = identifierModel;
}
@Parameterized.Parameters
public static List<CommunicationIdentifierModel> cases() {
return Arrays.asList(
new CommunicationIdentifierModel(), // Missing RawId
new CommunicationIdentifierModel().setRawId(rawId).setCommunicationUser(new CommunicationUserIdentifierModel()), // Missing Id
new CommunicationIdentifierModel().setRawId(rawId).setPhoneNumber(new PhoneNumberIdentifierModel()), // Missing PhoneNumber
new CommunicationIdentifierModel().setRawId(rawId).setMicrosoftTeamsUser(
new MicrosoftTeamsUserIdentifierModel().setCloud(PUBLIC)), // Missing userId
new CommunicationIdentifierModel().setRawId(rawId).setMicrosoftTeamsUser(
new MicrosoftTeamsUserIdentifierModel().setIsAnonymous(true).setCloud(CommunicationCloudEnvironmentModel.DOD)), // Missing UserId
new CommunicationIdentifierModel().setRawId(rawId).setMicrosoftTeamsUser(
new MicrosoftTeamsUserIdentifierModel().setUserId(teamsUserId).setIsAnonymous(true))
);
}
@Test
public void throwsOnMissingProperty() {
expectedException.expect(is(instanceOf(NullPointerException.class)));
CommunicationIdentifierSerializer.deserialize(identifierModel);
}
}

Просмотреть файл

@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import java.util.concurrent.Callable;
class MockTokenRefresher implements Callable<String> {
private String tokenString;
private Runnable onCallReturn;
private int callCount;
public void setToken(String tokenString) {
this.tokenString = tokenString;
}
public void setOnCallReturn(Runnable onCallReturn) {
this.onCallReturn = onCallReturn;
}
public int getCallCount() {
return callCount;
}
@Override
public String call() {
this.incrementCallCount();
if (this.onCallReturn != null) {
this.onCallReturn.run();
}
return this.tokenString;
}
private synchronized void incrementCallCount() {
this.callCount++;
}
}

Просмотреть файл

@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import java.util.Arrays;
import java.util.List;
import static junit.framework.TestCase.assertNotNull;
import static org.junit.Assert.assertEquals;
@RunWith(Parameterized.class)
public class TeamsUserIdentifierSerializationTests {
final String someId = "some id";
final String teamsUserId = "Teams user id";
final String rawId = "some lengthy id string";
private final boolean isAnonymous;
public TeamsUserIdentifierSerializationTests(boolean isAnonymous) {
this.isAnonymous = isAnonymous;
}
@Parameters
public static List<Boolean> cases() {
return Arrays.asList(true, false);
}
@Test
public void serializeMicrosoftTeamsUser() {
CommunicationIdentifierModel model = CommunicationIdentifierSerializer.serialize(
new MicrosoftTeamsUserIdentifier(teamsUserId, isAnonymous)
.setRawId(rawId)
.setCloudEnvironment(CommunicationCloudEnvironment.DOD));
assertNotNull(model.getMicrosoftTeamsUser());
assertEquals(teamsUserId, model.getMicrosoftTeamsUser().getUserId());
assertEquals(rawId, model.getRawId());
assertEquals(CommunicationCloudEnvironmentModel.DOD, model.getMicrosoftTeamsUser().getCloud());
assertEquals(isAnonymous, model.getMicrosoftTeamsUser().isAnonymous());
}
@Test
public void deserializeMicrosoftTeamsUser() {
MicrosoftTeamsUserIdentifier identifier = (MicrosoftTeamsUserIdentifier) CommunicationIdentifierSerializer.deserialize(
new CommunicationIdentifierModel()
.setRawId(rawId)
.setMicrosoftTeamsUser(new MicrosoftTeamsUserIdentifierModel()
.setUserId(teamsUserId).setIsAnonymous(isAnonymous).setCloud(CommunicationCloudEnvironmentModel.GCCH)));
assertEquals(MicrosoftTeamsUserIdentifier.class, identifier.getClass());
assertEquals(teamsUserId, identifier.getUserId());
assertEquals(rawId, identifier.getRawId());
assertEquals(CommunicationCloudEnvironment.GCCH, identifier.getCloudEnvironment());
assertEquals(isAnonymous, identifier.isAnonymous());
}
}

Просмотреть файл

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.common;
import android.util.Base64;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
final class TokenStubHelper {
private static final String STUB_TOKEN_TEMPLATE =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.%s.adM-ddBZZlQ1WlN3pdPBOF5G4Wh9iZpxNP_fSvpF4cWs";
private static final ObjectMapper jsonMapper = new ObjectMapper();
static String createTokenString(long expiryEpochSecond) {
Map<String, Object> payload = new HashMap<>();
payload.put("exp", expiryEpochSecond);
payload.put("jti", UUID.randomUUID());
try {
String payloadJson = jsonMapper.writeValueAsString(payload);
String encodedPayload = Base64.encodeToString(payloadJson.getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
return String.format(STUB_TOKEN_TEMPLATE, encodedPayload);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
static String createTokenStringForOffset(long secondsFromNow) {
return createTokenString(System.currentTimeMillis() / 1000 + secondsFromNow);
}
}

Просмотреть файл

@ -8,3 +8,4 @@ include ":sdk:core:azure-core-http-httpurlconnection"
include ":sdk:core:azure-core-rest"
include ":sdk:core:azure-core-test"
include ":eng:code-quality-reports"
include ':sdk:communication:azure-communication-common'