Copying azure-communication-common from the commit: 845592d241
This commit is contained in:
Родитель
921094ed16
Коммит
09da73632f
|
@ -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'
|
||||
|
|
Загрузка…
Ссылка в новой задаче