From 940b1e9279610f963f30c2454f85c3b21a6fe77f Mon Sep 17 00:00:00 2001 From: Minnie Liu Date: Fri, 4 Dec 2020 19:22:27 -0800 Subject: [PATCH 1/5] Renaming CommunicationUserCredential to CommunicationTokenCredential --- .../azure-communication-chat/README.md | 4 +- .../communication/chat/ChatClientBuilder.java | 18 +- .../communication/chat/ReadmeSamples.java | 4 +- .../chat/ChatClientTestBase.java | 6 +- .../azure-communication-common/README.md | 6 +- .../common/CommunicationTokenCredential.java | 278 ++++++++++++++++-- .../common/CommunicationUserCredential.java | 272 ----------------- .../CommunicationBearerTokenCredential.java | 42 +++ .../src/main/java/module-info.java | 1 + ...municationBearerTokenCredentialTests.java} | 17 +- ...=> CommunicationTokenCredentialTests.java} | 36 +-- 11 files changed, 346 insertions(+), 338 deletions(-) delete mode 100644 sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/CommunicationUserCredential.java create mode 100644 sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/implementation/CommunicationBearerTokenCredential.java rename sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/{CommunicationTokenCrendentialTests.java => CommunicationBearerTokenCredentialTests.java} (75%) rename sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/{CommunicationUserCredentialTests.java => CommunicationTokenCredentialTests.java} (89%) diff --git a/sdk/communication/azure-communication-chat/README.md b/sdk/communication/azure-communication-chat/README.md index e1d24c13d2bc3..81d24c6dfe5c1 100644 --- a/sdk/communication/azure-communication-chat/README.md +++ b/sdk/communication/azure-communication-chat/README.md @@ -80,7 +80,7 @@ HttpClient httpClient = httpClientBuilder.build(); // Your user access token retrieved from your trusted service String token = "SECRET"; -CommunicationUserCredential credential = new CommunicationUserCredential(token); +communicationTokenCredential credential = new communicationTokenCredential(token); // Initialize the chat client final ChatClientBuilder builder = new ChatClientBuilder(); @@ -94,7 +94,7 @@ ChatClient chatClient = builder.buildClient(); #### Create a chat thread -To create a chat client, you will use the Communications Service endpoint and the access token that was generated as part of pre-requisite steps. User access tokens enable you to build client applications that directly authenticate to Azure Communication Services. Once you generate these tokens on your server, pass them back to a client device. You need to use the CommunicationUserCredential class from the Common SDK to pass the token to your chat client. +To create a chat client, you will use the Communications Service endpoint and the access token that was generated as part of pre-requisite steps. User access tokens enable you to build client applications that directly authenticate to Azure Communication Services. Once you generate these tokens on your server, pass them back to a client device. You need to use the CommunicationTokenCredential class from the Common SDK to pass the token to your chat client. Use the `createChatThread` method to create a chat thread. `createChatThreadOptions` is used to describe the thread request, an example is shown in the code snippet below. diff --git a/sdk/communication/azure-communication-chat/src/main/java/com/azure/communication/chat/ChatClientBuilder.java b/sdk/communication/azure-communication-chat/src/main/java/com/azure/communication/chat/ChatClientBuilder.java index 243a735754dda..ebb2d7058a062 100644 --- a/sdk/communication/azure-communication-chat/src/main/java/com/azure/communication/chat/ChatClientBuilder.java +++ b/sdk/communication/azure-communication-chat/src/main/java/com/azure/communication/chat/ChatClientBuilder.java @@ -5,7 +5,7 @@ import com.azure.communication.chat.implementation.AzureCommunicationChatServiceImplBuilder; import com.azure.communication.common.CommunicationTokenCredential; -import com.azure.communication.common.CommunicationUserCredential; +import com.azure.communication.common.implementation.CommunicationBearerTokenCredential; import java.util.ArrayList; import java.util.Map; @@ -33,7 +33,7 @@ public final class ChatClientBuilder { private String endpoint; private HttpClient httpClient; - private CommunicationUserCredential communicationUserCredential; + private CommunicationTokenCredential communicationTokenCredential; private final List customPolicies = new ArrayList(); private HttpLogOptions logOptions = new HttpLogOptions(); private HttpPipeline httpPipeline; @@ -68,12 +68,12 @@ public ChatClientBuilder httpClient(HttpClient httpClient) { /** * Set a token credential for authorization * - * @param communicationUserCredential valid token credential as a string + * @param communicationTokenCredential valid token credential as a string * @return the updated ChatClientBuilder object */ - public ChatClientBuilder credential(CommunicationUserCredential communicationUserCredential) { - this.communicationUserCredential = Objects.requireNonNull( - communicationUserCredential, "'communicationUserCredential' cannot be null."); + public ChatClientBuilder credential(CommunicationTokenCredential communicationTokenCredential) { + this.communicationTokenCredential = Objects.requireNonNull( + communicationTokenCredential, "'communicationTokenCredential' cannot be null."); return this; } @@ -166,10 +166,10 @@ public ChatAsyncClient buildAsyncClient() { if (httpPipeline != null) { pipeline = httpPipeline; } else { - Objects.requireNonNull(communicationUserCredential); + Objects.requireNonNull(communicationTokenCredential); Objects.requireNonNull(httpClient); - CommunicationTokenCredential tokenCredential = - new CommunicationTokenCredential(communicationUserCredential); + CommunicationBearerTokenCredential tokenCredential = + new CommunicationBearerTokenCredential(communicationTokenCredential); pipeline = createHttpPipeline(httpClient, new BearerTokenAuthenticationPolicy(tokenCredential, ""), diff --git a/sdk/communication/azure-communication-chat/src/samples/java/com/azure/communication/chat/ReadmeSamples.java b/sdk/communication/azure-communication-chat/src/samples/java/com/azure/communication/chat/ReadmeSamples.java index 2755e36295e71..7ea1b6419d173 100644 --- a/sdk/communication/azure-communication-chat/src/samples/java/com/azure/communication/chat/ReadmeSamples.java +++ b/sdk/communication/azure-communication-chat/src/samples/java/com/azure/communication/chat/ReadmeSamples.java @@ -15,7 +15,7 @@ import com.azure.communication.chat.models.UpdateChatMessageOptions; import com.azure.communication.chat.models.UpdateChatThreadOptions; import com.azure.communication.common.CommunicationUser; -import com.azure.communication.common.CommunicationUserCredential; +import com.azure.communication.common.CommunicationTokenCredential; import com.azure.core.http.HttpClient; import com.azure.core.http.netty.NettyAsyncHttpClientBuilder; import com.azure.core.http.rest.PagedIterable; @@ -47,7 +47,7 @@ public ChatClient createChatClient() { // Your user access token retrieved from your trusted service String token = "SECRET"; - CommunicationUserCredential credential = new CommunicationUserCredential(token); + CommunicationTokenCredential credential = new CommunicationTokenCredential(token); // Initialize the chat client final ChatClientBuilder builder = new ChatClientBuilder(); diff --git a/sdk/communication/azure-communication-chat/src/test/java/com/azure/communication/chat/ChatClientTestBase.java b/sdk/communication/azure-communication-chat/src/test/java/com/azure/communication/chat/ChatClientTestBase.java index d5bd5b4ec7649..b2f83c00dd84d 100644 --- a/sdk/communication/azure-communication-chat/src/test/java/com/azure/communication/chat/ChatClientTestBase.java +++ b/sdk/communication/azure-communication-chat/src/test/java/com/azure/communication/chat/ChatClientTestBase.java @@ -9,7 +9,7 @@ import com.azure.communication.administration.CommunicationIdentityClientBuilder; import com.azure.communication.chat.models.ErrorException; import com.azure.communication.chat.models.*; -import com.azure.communication.common.CommunicationUserCredential; +import com.azure.communication.common.CommunicationTokenCredential; import com.azure.core.exception.HttpResponseException; import com.azure.core.http.HttpClient; import com.azure.core.test.TestBase; @@ -48,10 +48,10 @@ protected ChatClientBuilder getChatClientBuilder(String token, HttpClient httpCl .httpClient(httpClient == null ? interceptorManager.getPlaybackClient() : httpClient); if (interceptorManager.isPlaybackMode()) { - builder.credential(new CommunicationUserCredential(generateRawToken())); + builder.credential(new CommunicationTokenCredential(generateRawToken())); return builder; } else { - builder.credential(new CommunicationUserCredential(token)); + builder.credential(new CommunicationTokenCredential(token)); } if (getTestMode() == TestMode.RECORD) { diff --git a/sdk/communication/azure-communication-common/README.md b/sdk/communication/azure-communication-common/README.md index 3b7bb70a5a745..315e3ee45e696 100644 --- a/sdk/communication/azure-communication-common/README.md +++ b/sdk/communication/azure-communication-common/README.md @@ -32,11 +32,11 @@ Azure Communication Service supports HMAC authentication with resource access ke apply HMAC authentication, construct CommunicationClientCredential with the access key and instantiate a CommunicationIdentityClient to manage users and tokens. -### CommunicationUserCredential +### CommunicationTokenCredential -It is up to you the developer to first create valid user tokens with the Communication Administration SDK. Then you use these tokens with the `CommunicationUserCredential`. +It is up to you the developer to first create valid user tokens with the Communication Administration SDK. Then you use these tokens with the `CommunicationTokenCredential`. -`CommunicationUserCredential` 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. +`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. ## Contributing diff --git a/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/CommunicationTokenCredential.java b/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/CommunicationTokenCredential.java index f65a78966c487..51a26bc34a560 100644 --- a/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/CommunicationTokenCredential.java +++ b/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/CommunicationTokenCredential.java @@ -1,41 +1,277 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. - package com.azure.communication.common; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import com.azure.core.util.logging.ClientLogger; import com.azure.core.credential.AccessToken; -import com.azure.core.credential.TokenCredential; -import com.azure.core.credential.TokenRequestContext; -import reactor.core.publisher.Mono; +import java.io.IOException; +import java.time.OffsetDateTime; +import java.util.Date; +import java.util.Objects; +import java.util.Timer; +import java.util.TimerTask; + +import com.azure.communication.common.implementation.TokenParser; /** - * This class serves as a CommunicationUserCredential wrapper that - * allows using BearerAuthenticationPolicy in different clients + * Provide user credential for Communication service user */ -public class CommunicationTokenCredential implements TokenCredential { - private final CommunicationUserCredential credential; +public final class CommunicationTokenCredential implements AutoCloseable { + private static final int DEFAULT_EXPIRING_OFFSET_MINUTES = 10; + + private final ClientLogger logger = new ClientLogger(CommunicationTokenCredential.class); + + private AccessToken accessToken; + private Future tokenFuture; + private final TokenParser tokenParser = new TokenParser(); + private TokenRefresher refresher; + private FetchingTask fetchingTask; + private boolean isClosed = false; + + private static class TokenImmediate implements Future { + private final AccessToken accessToken; + + TokenImmediate(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() throws InterruptedException, ExecutionException { + return this.accessToken; + } + + @Override + public AccessToken get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return this.accessToken; + } + } + + /** + * Create with serialized JWT token + * + * @param initialToken serialized JWT token + */ + public CommunicationTokenCredential(String initialToken) { + Objects.requireNonNull(initialToken, "'initialToken' cannot be null."); + setToken(initialToken); + tokenFuture = new TokenImmediate(accessToken); + } + + /** + * Create with a tokenRefresher + * + * @param tokenRefresher implementation to supply fresh token when reqested + */ + public CommunicationTokenCredential(TokenRefresher tokenRefresher) { + Objects.requireNonNull(tokenRefresher, "'tokenRefresher' cannot be null."); + refresher = tokenRefresher; + } /** - * Creates a CommunicationTokenCredential - * - * @param communicationUserCredential The {@link CommunicationUserCredential} to use - * in the BearerAuthenticationPolicy. + * Create with serialized JWT token and a token supplier to auto-refresh the + * token before it expires. Callback function tokenRefresher will be called ahead + * of the token expiry by the number of minutes specified by + * CallbackOffsetMinutes defaulted to two minutes. To modify this default, call + * setCallbackOffsetMinutes after construction + * + * @param tokenRefresher implementation to supply fresh token when reqested + * @param initialToken serialized JWT token + * @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 CommunicationTokenCredential(CommunicationUserCredential communicationUserCredential) { - credential = communicationUserCredential; + public CommunicationTokenCredential( + TokenRefresher tokenRefresher, + String initialToken, + boolean refreshProactively) + { + this(tokenRefresher); + Objects.requireNonNull(initialToken, "'initialToken' cannot be null."); + setToken(initialToken); + tokenFuture = new TokenImmediate(accessToken); + if (refreshProactively) { + OffsetDateTime nextFetchTime = accessToken.getExpiresAt().minusMinutes(DEFAULT_EXPIRING_OFFSET_MINUTES); + fetchingTask = new FetchingTask(this, nextFetchTime); + } + } + + /** + * Get Azure core access token from credential + * + * @return Asynchronous call to fetch actual token + * @throws ExecutionException when supplier throws this exception + * @throws InterruptedException when supplier throws this exception + */ + public Future getToken() throws InterruptedException, ExecutionException { + if (isClosed) { + throw logger.logExceptionAsError( + new RuntimeException("getToken called on closed CommunicationTokenCredential object")); + } + if ((accessToken == null || accessToken.isExpired()) // no valid token to return + && refresher != null // can refresh + && (tokenFuture == null || tokenFuture.isDone())) { // no fetching in progress, proactive or on-demand + fetchFreshToken(); + } + + return tokenFuture; } @Override - public Mono getToken(TokenRequestContext request) { - try { - return Mono.just(credential.getToken().get()); - } catch (InterruptedException ex) { - return Mono.error(ex); - } catch (ExecutionException ex) { - return Mono.error(ex); + public void close() throws IOException { + isClosed = true; + if (fetchingTask != null) { + fetchingTask.stopTimer(); + fetchingTask = null; } + refresher = null; + } + + // For test verification usage only + boolean hasProactiveFetcher() { + return fetchingTask != null; + } + + private void setToken(String freshToken) { + accessToken = tokenParser.parseJWTToken(freshToken); + + if (fetchingTask != null) { + OffsetDateTime nextFetchTime = accessToken.getExpiresAt().minusMinutes(DEFAULT_EXPIRING_OFFSET_MINUTES); + fetchingTask.setNextFetchTime(nextFetchTime); + } + } + + private Future fetchFreshToken() { + Future fetchFuture = refresher.getFetchTokenFuture(); + if (fetchFuture == null) { + throw logger.logExceptionAsError( + new RuntimeException("TokenRefresher returned null when getFetchTokenFuture is called")); + } + tokenFuture = new TokenFuture(fetchFuture); + return fetchFuture; + } + + private class TokenFuture implements Future { + private final Future clientTokenFuture; + + TokenFuture(Future tokenStringFuture) { + this.clientTokenFuture = tokenStringFuture; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return clientTokenFuture.cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return clientTokenFuture.isCancelled(); + } + + @Override + public boolean isDone() { + return clientTokenFuture.isDone(); + } + + @Override + public AccessToken get() throws InterruptedException, ExecutionException { + String freshToken = clientTokenFuture.get(); + setToken(freshToken); + return accessToken; + } + + @Override + public AccessToken get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + String freshToken = clientTokenFuture.get(timeout, unit); + setToken(freshToken); + return accessToken; + } + } + + private static class FetchingTask { + private final CommunicationTokenCredential host; + private Timer expiringTimer; + private OffsetDateTime nextFetchTime; + + FetchingTask(CommunicationTokenCredential tokenHost, + OffsetDateTime nextFetchAt) { + host = tokenHost; + nextFetchTime = nextFetchAt; + startTimer(); + } + + private synchronized void setNextFetchTime(OffsetDateTime newFetchTime) { + nextFetchTime = newFetchTime; + stopTimer(); + startTimer(); + } + + private synchronized void startTimer() { + expiringTimer = new Timer(); + Date expiring = Date.from(nextFetchTime.toInstant()); + expiringTimer.schedule(new TokenExpiringTask(this), expiring); + } + + private synchronized void stopTimer() { + if (expiringTimer == null) { + return; + } + + expiringTimer.cancel(); + expiringTimer.purge(); + expiringTimer = null; + } + + private Future fetchFreshToken() { + return host.fetchFreshToken(); + } + + private void setToken(String freshTokenString) { + host.setToken(freshTokenString); + } + + private class TokenExpiringTask extends TimerTask { + private final ClientLogger logger = new ClientLogger(TokenExpiringTask.class); + private final FetchingTask tokenCache; + + TokenExpiringTask(FetchingTask host) { + tokenCache = host; + } + + @Override + public void run() { + try { + Future tokenStringFuture = tokenCache.fetchFreshToken(); + tokenCache.setToken(tokenStringFuture.get()); + } catch (Exception exception) { + logger.logExceptionAsError(new RuntimeException(exception)); + } + + } + } } } diff --git a/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/CommunicationUserCredential.java b/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/CommunicationUserCredential.java deleted file mode 100644 index b13121cc46a0d..0000000000000 --- a/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/CommunicationUserCredential.java +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.communication.common; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import com.azure.core.util.logging.ClientLogger; -import com.azure.core.credential.AccessToken; - -import java.io.IOException; -import java.time.OffsetDateTime; -import java.util.Date; -import java.util.Objects; -import java.util.Timer; -import java.util.TimerTask; - -import com.azure.communication.common.implementation.TokenParser; - -/** - * Provide user credential for Communication service user - */ -public final class CommunicationUserCredential implements AutoCloseable { - private static final int DEFAULT_EXPIRING_OFFSET_MINUTES = 10; - - private final ClientLogger logger = new ClientLogger(CommunicationUserCredential.class); - - private AccessToken accessToken; - private Future tokenFuture; - private final TokenParser tokenParser = new TokenParser(); - private TokenRefresher refresher; - private FetchingTask fetchingTask; - private boolean isClosed = false; - - private static class TokenImmediate implements Future { - private final AccessToken accessToken; - - TokenImmediate(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() throws InterruptedException, ExecutionException { - return this.accessToken; - } - - @Override - public AccessToken get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - return this.accessToken; - } - } - - /** - * Create with serialized JWT token - * - * @param initialToken serialized JWT token - */ - public CommunicationUserCredential(String initialToken) { - Objects.requireNonNull(initialToken, "'initialToken' cannot be null."); - setToken(initialToken); - tokenFuture = new TokenImmediate(accessToken); - } - - /** - * Create with a tokenRefresher - * - * @param tokenRefresher implementation to supply fresh token when reqested - */ - public CommunicationUserCredential(TokenRefresher tokenRefresher) { - Objects.requireNonNull(tokenRefresher, "'tokenRefresher' cannot be null."); - refresher = tokenRefresher; - } - - /** - * Create with serialized JWT token and a token supplier to auto-refresh the - * token before it expires. Callback function tokenRefresher will be called ahead - * of the token expiry by the number of minutes specified by - * CallbackOffsetMinutes defaulted to two minutes. To modify this default, call - * setCallbackOffsetMinutes after construction - * - * @param tokenRefresher implementation to supply fresh token when reqested - * @param initialToken serialized JWT token - * @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 CommunicationUserCredential(TokenRefresher tokenRefresher, String initialToken, boolean refreshProactively) { - this(tokenRefresher); - Objects.requireNonNull(initialToken, "'initialToken' cannot be null."); - setToken(initialToken); - tokenFuture = new TokenImmediate(accessToken); - if (refreshProactively) { - OffsetDateTime nextFetchTime = accessToken.getExpiresAt().minusMinutes(DEFAULT_EXPIRING_OFFSET_MINUTES); - fetchingTask = new FetchingTask(this, nextFetchTime); - } - } - - /** - * Get Azure core access token from credential - * - * @return Asynchronous call to fetch actual token - * @throws ExecutionException when supplier throws this exception - * @throws InterruptedException when supplier throws this exception - */ - public Future getToken() throws InterruptedException, ExecutionException { - if (isClosed) { - throw logger.logExceptionAsError( - new RuntimeException("getToken called on closed CommunicationUserCredential object")); - } - if ((accessToken == null || accessToken.isExpired()) // no valid token to return - && refresher != null // can refresh - && (tokenFuture == null || tokenFuture.isDone())) { // no fetching in progress, proactive or on-demand - fetchFreshToken(); - } - - return tokenFuture; - } - - @Override - public void close() throws IOException { - isClosed = true; - if (fetchingTask != null) { - fetchingTask.stopTimer(); - fetchingTask = null; - } - refresher = null; - } - - // For test verification usage only - boolean hasProactiveFetcher() { - return fetchingTask != null; - } - - private void setToken(String freshToken) { - accessToken = tokenParser.parseJWTToken(freshToken); - - if (fetchingTask != null) { - OffsetDateTime nextFetchTime = accessToken.getExpiresAt().minusMinutes(DEFAULT_EXPIRING_OFFSET_MINUTES); - fetchingTask.setNextFetchTime(nextFetchTime); - } - } - - private Future fetchFreshToken() { - Future fetchFuture = refresher.getFetchTokenFuture(); - if (fetchFuture == null) { - throw logger.logExceptionAsError( - new RuntimeException("TokenRefresher returned null when getFetchTokenFuture is called")); - } - tokenFuture = new TokenFuture(fetchFuture); - return fetchFuture; - } - - private class TokenFuture implements Future { - private final Future clientTokenFuture; - - TokenFuture(Future tokenStringFuture) { - this.clientTokenFuture = tokenStringFuture; - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - return clientTokenFuture.cancel(mayInterruptIfRunning); - } - - @Override - public boolean isCancelled() { - return clientTokenFuture.isCancelled(); - } - - @Override - public boolean isDone() { - return clientTokenFuture.isDone(); - } - - @Override - public AccessToken get() throws InterruptedException, ExecutionException { - String freshToken = clientTokenFuture.get(); - setToken(freshToken); - return accessToken; - } - - @Override - public AccessToken get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - String freshToken = clientTokenFuture.get(timeout, unit); - setToken(freshToken); - return accessToken; - } - } - - private static class FetchingTask { - private final CommunicationUserCredential host; - private Timer expiringTimer; - private OffsetDateTime nextFetchTime; - - FetchingTask(CommunicationUserCredential tokenHost, - OffsetDateTime nextFetchAt) { - host = tokenHost; - nextFetchTime = nextFetchAt; - startTimer(); - } - - private synchronized void setNextFetchTime(OffsetDateTime newFetchTime) { - nextFetchTime = newFetchTime; - stopTimer(); - startTimer(); - } - - private synchronized void startTimer() { - expiringTimer = new Timer(); - Date expiring = Date.from(nextFetchTime.toInstant()); - expiringTimer.schedule(new TokenExpiringTask(this), expiring); - } - - private synchronized void stopTimer() { - if (expiringTimer == null) { - return; - } - - expiringTimer.cancel(); - expiringTimer.purge(); - expiringTimer = null; - } - - private Future fetchFreshToken() { - return host.fetchFreshToken(); - } - - private void setToken(String freshTokenString) { - host.setToken(freshTokenString); - } - - private class TokenExpiringTask extends TimerTask { - private final ClientLogger logger = new ClientLogger(TokenExpiringTask.class); - private final FetchingTask tokenCache; - - TokenExpiringTask(FetchingTask host) { - tokenCache = host; - } - - @Override - public void run() { - try { - Future tokenStringFuture = tokenCache.fetchFreshToken(); - tokenCache.setToken(tokenStringFuture.get()); - } catch (Exception exception) { - logger.logExceptionAsError(new RuntimeException(exception)); - } - - } - } - } -} diff --git a/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/implementation/CommunicationBearerTokenCredential.java b/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/implementation/CommunicationBearerTokenCredential.java new file mode 100644 index 0000000000000..d1f80f12e4a34 --- /dev/null +++ b/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/implementation/CommunicationBearerTokenCredential.java @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.common.implementation; + +import java.util.concurrent.ExecutionException; + +import com.azure.communication.common.CommunicationTokenCredential; +import com.azure.core.credential.AccessToken; +import com.azure.core.credential.TokenCredential; +import com.azure.core.credential.TokenRequestContext; + +import reactor.core.publisher.Mono; + +/** + * This class serves as a CommunicationTokenCredential wrapper that + * allows using BearerAuthenticationPolicy in different clients + */ +public class CommunicationBearerTokenCredential implements TokenCredential { + private final CommunicationTokenCredential credential; + + /** + * Creates a CommunicationTokenCredential + * + * @param communicationTokenCredential The {@link CommunicationTokenCredential} to use + * in the BearerAuthenticationPolicy. + */ + public CommunicationBearerTokenCredential(CommunicationTokenCredential communicationTokenCredential) { + credential = communicationTokenCredential; + } + + @Override + public Mono getToken(TokenRequestContext request) { + try { + return Mono.just(credential.getToken().get()); + } catch (InterruptedException ex) { + return Mono.error(ex); + } catch (ExecutionException ex) { + return Mono.error(ex); + } + } +} diff --git a/sdk/communication/azure-communication-common/src/main/java/module-info.java b/sdk/communication/azure-communication-common/src/main/java/module-info.java index 50f60e4558a53..2056ded810e7e 100644 --- a/sdk/communication/azure-communication-common/src/main/java/module-info.java +++ b/sdk/communication/azure-communication-common/src/main/java/module-info.java @@ -7,4 +7,5 @@ // public API surface area exports com.azure.communication.common; + exports com.azure.communication.common.implementation; } diff --git a/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationTokenCrendentialTests.java b/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationBearerTokenCredentialTests.java similarity index 75% rename from sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationTokenCrendentialTests.java rename to sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationBearerTokenCredentialTests.java index 484735b29efd6..6aa1e332b3014 100644 --- a/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationTokenCrendentialTests.java +++ b/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationBearerTokenCredentialTests.java @@ -2,6 +2,7 @@ // Licensed under the MIT License. package com.azure.communication.common; +import com.azure.communication.common.implementation.CommunicationBearerTokenCredential; import com.azure.communication.common.implementation.JwtTokenMocker; import com.azure.core.credential.AccessToken; import com.azure.core.credential.TokenRequestContext; @@ -30,24 +31,24 @@ public TokenRequestContext mockTokenRequestContext() { @Test public void constructWithValidTokenWithoutFresher() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", 3 * 60); - CommunicationUserCredential userCredential = new CommunicationUserCredential(tokenStr); - CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(userCredential); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(tokenStr); + CommunicationBearerTokenCredential bearerTokenCredential = new CommunicationBearerTokenCredential(tokenCredential); - AccessToken token = tokenCredential.getToken(mockTokenRequestContext()).block(); + AccessToken token = bearerTokenCredential.getToken(mockTokenRequestContext()).block(); assertFalse(token.isExpired(), "Statically cached AccessToken should not expire when expiry is set to 3 minutes later"); assertEquals(tokenStr, token.getToken()); - userCredential.close(); + tokenCredential.close(); } @Test public void constructWithExpiredTokenWithoutRefresher() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", -3 * 60); - CommunicationUserCredential userCredential = new CommunicationUserCredential(tokenStr); - CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(userCredential); - AccessToken token = tokenCredential.getToken(mockTokenRequestContext()).block(); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(tokenStr); + CommunicationBearerTokenCredential bearerTokenCredential = new CommunicationBearerTokenCredential(tokenCredential); + AccessToken token = bearerTokenCredential.getToken(mockTokenRequestContext()).block(); assertTrue(token.isExpired(), "Statically cached AccessToken should expire when expiry is set to 3 minutes before"); - userCredential.close(); + tokenCredential.close(); } } diff --git a/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationUserCredentialTests.java b/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationTokenCredentialTests.java similarity index 89% rename from sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationUserCredentialTests.java rename to sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationTokenCredentialTests.java index bf5b4fff8cce9..657b22057932d 100644 --- a/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationUserCredentialTests.java +++ b/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationTokenCredentialTests.java @@ -19,13 +19,13 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -public class CommunicationUserCredentialTests { +public class CommunicationTokenCredentialTests { private final JwtTokenMocker tokenMocker = new JwtTokenMocker(); @Test public void constructWithValidTokenWithoutFresher() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", 3 * 60); - CommunicationUserCredential userCredential = new CommunicationUserCredential(tokenStr); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(tokenStr); AccessToken token = userCredential.getToken().get(); assertFalse(token.isExpired(), "Statically cached AccessToken should not expire when expiry is set to 3 minutes later"); @@ -37,14 +37,14 @@ public void constructWithValidTokenWithoutFresher() throws InterruptedException, public void constructWithInvalidTokenStringShouldThrow() { String tokenStr = "IAmNotAToken"; assertThrows(Exception.class, () -> { - new CommunicationUserCredential(tokenStr); + new CommunicationTokenCredential(tokenStr); }, "Should throw on invalid token"); } @Test public void constructWithExpiredTokenWithoutRefresher() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", -3 * 60); - CommunicationUserCredential userCredential = new CommunicationUserCredential(tokenStr); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(tokenStr); AccessToken token = userCredential.getToken().get(); assertTrue(token.isExpired(), "Statically cached AccessToken should expire when expiry is set to 3 minutes before"); @@ -112,7 +112,7 @@ public String get(long timeout, TimeUnit unit) public void fresherShouldNotBeCalledBeforeExpiringTime() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", 15 * 60); immediateFresher.resetCallCount(); - CommunicationUserCredential userCredential = new CommunicationUserCredential(immediateFresher, tokenStr, true); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); AccessToken token = userCredential.getToken().get(); assertFalse(token.isExpired(), "Refreshable AccessToken should not expire when expiry is set to 5 minutes later"); @@ -127,7 +127,7 @@ public void fresherShouldBeCalledAfterExpiringTime() throws InterruptedException immediateFresher.resetCallCount(); CountDownLatch countDownLatch = new CountDownLatch(1); immediateFresher.setOnCallReturn(countDownLatch::countDown); - CommunicationUserCredential userCredential = new CommunicationUserCredential(immediateFresher, tokenStr, true); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); countDownLatch.await(); assertEquals(1, immediateFresher.numCalls()); @@ -143,7 +143,7 @@ public void refresherShouldBeCalledImmediatelyWithExpiredToken() throws Interrup immediateFresher.resetCallCount(); CountDownLatch countDownLatch = new CountDownLatch(1); immediateFresher.setOnCallReturn(countDownLatch::countDown); - CommunicationUserCredential userCredential = new CommunicationUserCredential(immediateFresher, tokenStr, true); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); countDownLatch.await(); assertEquals(1, immediateFresher.numCalls()); @@ -158,7 +158,7 @@ public void refresherShouldBeCalledAgainAfterFirstRefreshCall() throws Interrupt immediateFresher.resetCallCount(); CountDownLatch firstCountDownLatch = new CountDownLatch(1); immediateFresher.setOnCallReturn(firstCountDownLatch::countDown); - CommunicationUserCredential userCredential = new CommunicationUserCredential(immediateFresher, tokenStr, true); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); firstCountDownLatch.await(); assertEquals(1, immediateFresher.numCalls()); @@ -179,7 +179,7 @@ public void refresherShouldBeCalledAgainAfterFirstRefreshCall() throws Interrupt public void shouldNotCallRefresherWhenTokenStillValid() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", 15 * 60); immediateFresher.resetCallCount(); - CommunicationUserCredential userCredential = new CommunicationUserCredential(immediateFresher, tokenStr, false); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, false); AccessToken token = userCredential.getToken().get(); assertFalse(token.isExpired()); assertEquals(tokenStr, token.getToken()); @@ -196,7 +196,7 @@ public void shouldNotCallRefresherWhenTokenStillValid() throws InterruptedExcept public void expiredTokenShouldBeRefreshedOnDemandWithoutProactiveFetch() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", -5 * 60); immediateFresher.resetCallCount(); - CommunicationUserCredential userCredential = new CommunicationUserCredential(immediateFresher, tokenStr, false); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, false); assertEquals(0, immediateFresher.numCalls()); AccessToken token = userCredential.getToken().get(); assertFalse(token.isExpired(), @@ -214,7 +214,7 @@ public void expiredTokenShouldBeRefreshedOnDemandWithoutProactiveFetch() throws @Test public void shouldCallbackOnDemandWithoutRefresher() throws InterruptedException, ExecutionException, IOException { immediateFresher.resetCallCount(); - CommunicationUserCredential userCredential = new CommunicationUserCredential(immediateFresher); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher); AccessToken accessToken = userCredential.getToken().get(); assertEquals(1, immediateFresher.numCalls()); assertFalse(accessToken.isExpired(), "On demand fetching case, should be still valid"); @@ -225,7 +225,7 @@ public void shouldCallbackOnDemandWithoutRefresher() throws InterruptedException @Test public void shouldStopRefreshTimerWhenClosed() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", 12 * 60); - CommunicationUserCredential userCredential = new CommunicationUserCredential(immediateFresher, tokenStr, true); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); assertTrue(userCredential.hasProactiveFetcher()); userCredential.close(); assertFalse(userCredential.hasProactiveFetcher()); @@ -262,7 +262,7 @@ public Future getFetchTokenFuture() { @Test public void shouldNotModifyTokenWhenRefresherThrows() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", 601); - CommunicationUserCredential userCredential = new CommunicationUserCredential(exceptionRefresher, tokenStr, true); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(exceptionRefresher, tokenStr, true); CountDownLatch countDownLatch = new CountDownLatch(1); exceptionRefresher.setOnCallReturn(countDownLatch::countDown); @@ -277,7 +277,7 @@ public void shouldNotModifyTokenWhenRefresherThrows() throws InterruptedExceptio public void doNotSwallowExceptionWithoutProactiveFetching() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", -5 * 60); exceptionRefresher.resetCallCount(); - CommunicationUserCredential userCredential = new CommunicationUserCredential(exceptionRefresher, tokenStr, false); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(exceptionRefresher, tokenStr, false); assertThrows(Exception.class, () -> { userCredential.getToken(); }, "Should not swallow exception when client throws"); @@ -359,7 +359,7 @@ public void shouldCallRefresherOnlyOnceWhileRefreshingIsInProgress() longRunningRefresher.setOnCallReturn(countDownLatch::countDown); String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", -5 * 60); - CommunicationUserCredential userCredential = new CommunicationUserCredential(longRunningRefresher, tokenStr, true); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(longRunningRefresher, tokenStr, true); countDownLatch.await(); assertEquals(1, longRunningRefresher.numCalls()); @@ -374,7 +374,7 @@ public void shouldCallRefresherOnlyOnceWhileRefreshingIsInProgress() public void withoutInitialTokenShouldCallFresherOnlyOnceWhileRefreshingIsInProgress() throws InterruptedException, ExecutionException, IOException { longRunningRefresher.resetCallCount(); - CommunicationUserCredential userCredential = new CommunicationUserCredential(longRunningRefresher); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(longRunningRefresher); userCredential.getToken(); assertEquals(1, longRunningRefresher.numCalls()); for (int i = 0; i < 3; i++) { @@ -439,7 +439,7 @@ public String get(long timeout, TimeUnit unit) public void shouldRefreshAgainAfterClientFutureThrows() throws InterruptedException, ExecutionException, IOException { exceptionFutureRefresher.resetCallCount(); - CommunicationUserCredential userCredential = new CommunicationUserCredential(exceptionFutureRefresher); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(exceptionFutureRefresher); assertEquals(0, exceptionFutureRefresher.numCalls()); Future accessToken = userCredential.getToken(); assertEquals(1, exceptionFutureRefresher.numCalls()); @@ -461,7 +461,7 @@ public void shouldRefreshAgainAfterClientFutureThrows() throws InterruptedExcept @Test public void shouldThrowWhenGetTokenCalledOnClosedObject() throws IOException { - CommunicationUserCredential userCredential = new CommunicationUserCredential(exceptionFutureRefresher); + CommunicationTokenCredential userCredential = new CommunicationTokenCredential(exceptionFutureRefresher); userCredential.close(); assertThrows(RuntimeException.class, () -> { userCredential.getToken(); From 1c6c6dc7915bcc7773f14b6d4cff59ba0f465664 Mon Sep 17 00:00:00 2001 From: Minnie Liu Date: Mon, 7 Dec 2020 15:51:46 -0800 Subject: [PATCH 2/5] Fixing build --- .../communication/chat/ChatClientBuilder.java | 3 +- .../CommunicationBearerTokenCredential.java | 2 +- ...mmunicationBearerTokenCredentialTests.java | 54 ------------------- 3 files changed, 3 insertions(+), 56 deletions(-) rename sdk/communication/{azure-communication-common/src/main/java/com/azure/communication/common => azure-communication-chat/src/main/java/com/azure/communication/chat}/implementation/CommunicationBearerTokenCredential.java (96%) delete mode 100644 sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationBearerTokenCredentialTests.java diff --git a/sdk/communication/azure-communication-chat/src/main/java/com/azure/communication/chat/ChatClientBuilder.java b/sdk/communication/azure-communication-chat/src/main/java/com/azure/communication/chat/ChatClientBuilder.java index ebb2d7058a062..2bc6eeca35898 100644 --- a/sdk/communication/azure-communication-chat/src/main/java/com/azure/communication/chat/ChatClientBuilder.java +++ b/sdk/communication/azure-communication-chat/src/main/java/com/azure/communication/chat/ChatClientBuilder.java @@ -4,8 +4,9 @@ package com.azure.communication.chat; import com.azure.communication.chat.implementation.AzureCommunicationChatServiceImplBuilder; +import com.azure.communication.chat.implementation.CommunicationBearerTokenCredential; + import com.azure.communication.common.CommunicationTokenCredential; -import com.azure.communication.common.implementation.CommunicationBearerTokenCredential; import java.util.ArrayList; import java.util.Map; diff --git a/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/implementation/CommunicationBearerTokenCredential.java b/sdk/communication/azure-communication-chat/src/main/java/com/azure/communication/chat/implementation/CommunicationBearerTokenCredential.java similarity index 96% rename from sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/implementation/CommunicationBearerTokenCredential.java rename to sdk/communication/azure-communication-chat/src/main/java/com/azure/communication/chat/implementation/CommunicationBearerTokenCredential.java index d1f80f12e4a34..6479cefdec98b 100644 --- a/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/implementation/CommunicationBearerTokenCredential.java +++ b/sdk/communication/azure-communication-chat/src/main/java/com/azure/communication/chat/implementation/CommunicationBearerTokenCredential.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.communication.common.implementation; +package com.azure.communication.chat.implementation; import java.util.concurrent.ExecutionException; diff --git a/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationBearerTokenCredentialTests.java b/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationBearerTokenCredentialTests.java deleted file mode 100644 index 6aa1e332b3014..0000000000000 --- a/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationBearerTokenCredentialTests.java +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.communication.common; - -import com.azure.communication.common.implementation.CommunicationBearerTokenCredential; -import com.azure.communication.common.implementation.JwtTokenMocker; -import com.azure.core.credential.AccessToken; -import com.azure.core.credential.TokenRequestContext; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutionException; - -public class CommunicationTokenCrendentialTests { - private final JwtTokenMocker tokenMocker = new JwtTokenMocker(); - - public TokenRequestContext mockTokenRequestContext() { - String scope = "the scope value"; - List scopes = new ArrayList<>(); - scopes.add(scope); - TokenRequestContext tokenRequestContext = new TokenRequestContext().setScopes(scopes); - return tokenRequestContext; - } - @Test - public void constructWithValidTokenWithoutFresher() throws InterruptedException, ExecutionException, IOException { - String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", 3 * 60); - CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(tokenStr); - CommunicationBearerTokenCredential bearerTokenCredential = new CommunicationBearerTokenCredential(tokenCredential); - - AccessToken token = bearerTokenCredential.getToken(mockTokenRequestContext()).block(); - assertFalse(token.isExpired(), - "Statically cached AccessToken should not expire when expiry is set to 3 minutes later"); - assertEquals(tokenStr, token.getToken()); - tokenCredential.close(); - } - - @Test - public void constructWithExpiredTokenWithoutRefresher() throws InterruptedException, ExecutionException, IOException { - String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", -3 * 60); - CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(tokenStr); - CommunicationBearerTokenCredential bearerTokenCredential = new CommunicationBearerTokenCredential(tokenCredential); - AccessToken token = bearerTokenCredential.getToken(mockTokenRequestContext()).block(); - assertTrue(token.isExpired(), - "Statically cached AccessToken should expire when expiry is set to 3 minutes before"); - tokenCredential.close(); - } -} From 56851a58555365e10574adba590911afbf615a1d Mon Sep 17 00:00:00 2001 From: Minnie Liu Date: Mon, 7 Dec 2020 16:15:15 -0800 Subject: [PATCH 3/5] update read me --- sdk/communication/azure-communication-chat/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/communication/azure-communication-chat/README.md b/sdk/communication/azure-communication-chat/README.md index 81d24c6dfe5c1..97578f0d5db58 100644 --- a/sdk/communication/azure-communication-chat/README.md +++ b/sdk/communication/azure-communication-chat/README.md @@ -80,7 +80,7 @@ HttpClient httpClient = httpClientBuilder.build(); // Your user access token retrieved from your trusted service String token = "SECRET"; -communicationTokenCredential credential = new communicationTokenCredential(token); +CommunicationTokenCredential credential = new CommunicationTokenCredential(token); // Initialize the chat client final ChatClientBuilder builder = new ChatClientBuilder(); From 80a5e0ab8d78d629ea6523983175b13d724509b8 Mon Sep 17 00:00:00 2001 From: Minnie Liu Date: Mon, 7 Dec 2020 16:39:57 -0800 Subject: [PATCH 4/5] Remove unneeded export --- .../azure-communication-common/src/main/java/module-info.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/communication/azure-communication-common/src/main/java/module-info.java b/sdk/communication/azure-communication-common/src/main/java/module-info.java index 2056ded810e7e..50f60e4558a53 100644 --- a/sdk/communication/azure-communication-common/src/main/java/module-info.java +++ b/sdk/communication/azure-communication-common/src/main/java/module-info.java @@ -7,5 +7,4 @@ // public API surface area exports com.azure.communication.common; - exports com.azure.communication.common.implementation; } From 4e66d689da4a0b28c872e8307a3fad87071911c0 Mon Sep 17 00:00:00 2001 From: Minnie Liu Date: Mon, 7 Dec 2020 19:33:11 -0800 Subject: [PATCH 5/5] Addressing PR comments --- .../azure-communication-common/CHANGELOG.md | 3 +- .../CommunicationTokenCredentialTests.java | 110 +++++++++--------- 2 files changed, 57 insertions(+), 56 deletions(-) diff --git a/sdk/communication/azure-communication-common/CHANGELOG.md b/sdk/communication/azure-communication-common/CHANGELOG.md index 94b3e0d44d9cc..a780b49318d8e 100644 --- a/sdk/communication/azure-communication-common/CHANGELOG.md +++ b/sdk/communication/azure-communication-common/CHANGELOG.md @@ -1,7 +1,8 @@ # Release History ## 1.0.0-beta.4 (Unreleased) - +### Breaking Changes +- Renamed `CommunicationUserCredential` to `CommunicationTokenCredential` ## 1.0.0-beta.3 (2020-11-16) Updated `azure-communication-common` version diff --git a/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationTokenCredentialTests.java b/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationTokenCredentialTests.java index 657b22057932d..72bf799f6669c 100644 --- a/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationTokenCredentialTests.java +++ b/sdk/communication/azure-communication-common/src/test/java/com/azure/communication/common/CommunicationTokenCredentialTests.java @@ -25,12 +25,12 @@ public class CommunicationTokenCredentialTests { @Test public void constructWithValidTokenWithoutFresher() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", 3 * 60); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(tokenStr); - AccessToken token = userCredential.getToken().get(); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(tokenStr); + AccessToken token = tokenCredential.getToken().get(); assertFalse(token.isExpired(), "Statically cached AccessToken should not expire when expiry is set to 3 minutes later"); assertEquals(tokenStr, token.getToken()); - userCredential.close(); + tokenCredential.close(); } @Test @@ -44,11 +44,11 @@ public void constructWithInvalidTokenStringShouldThrow() { @Test public void constructWithExpiredTokenWithoutRefresher() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", -3 * 60); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(tokenStr); - AccessToken token = userCredential.getToken().get(); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(tokenStr); + AccessToken token = tokenCredential.getToken().get(); assertTrue(token.isExpired(), "Statically cached AccessToken should expire when expiry is set to 3 minutes before"); - userCredential.close(); + tokenCredential.close(); } class MockImmediateRefresher implements TokenRefresher { @@ -112,13 +112,13 @@ public String get(long timeout, TimeUnit unit) public void fresherShouldNotBeCalledBeforeExpiringTime() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", 15 * 60); immediateFresher.resetCallCount(); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); - AccessToken token = userCredential.getToken().get(); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); + AccessToken token = tokenCredential.getToken().get(); assertFalse(token.isExpired(), "Refreshable AccessToken should not expire when expiry is set to 5 minutes later"); assertEquals(tokenStr, token.getToken()); assertEquals(0, immediateFresher.numCalls()); - userCredential.close(); + tokenCredential.close(); } @Test @@ -127,13 +127,13 @@ public void fresherShouldBeCalledAfterExpiringTime() throws InterruptedException immediateFresher.resetCallCount(); CountDownLatch countDownLatch = new CountDownLatch(1); immediateFresher.setOnCallReturn(countDownLatch::countDown); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); countDownLatch.await(); assertEquals(1, immediateFresher.numCalls()); - AccessToken token = userCredential.getToken().get(); + AccessToken token = tokenCredential.getToken().get(); assertFalse(token.isExpired(), "Refreshable AccessToken should not expire after refresh"); - userCredential.close(); + tokenCredential.close(); } @Test @@ -143,13 +143,13 @@ public void refresherShouldBeCalledImmediatelyWithExpiredToken() throws Interrup immediateFresher.resetCallCount(); CountDownLatch countDownLatch = new CountDownLatch(1); immediateFresher.setOnCallReturn(countDownLatch::countDown); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); countDownLatch.await(); assertEquals(1, immediateFresher.numCalls()); - AccessToken token = userCredential.getToken().get(); + AccessToken token = tokenCredential.getToken().get(); assertFalse(token.isExpired(), "Refreshable AccessToken should not expire after refresh"); - userCredential.close(); + tokenCredential.close(); } @Test @@ -158,11 +158,11 @@ public void refresherShouldBeCalledAgainAfterFirstRefreshCall() throws Interrupt immediateFresher.resetCallCount(); CountDownLatch firstCountDownLatch = new CountDownLatch(1); immediateFresher.setOnCallReturn(firstCountDownLatch::countDown); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); firstCountDownLatch.await(); assertEquals(1, immediateFresher.numCalls()); - AccessToken token = userCredential.getToken().get(); + AccessToken token = tokenCredential.getToken().get(); assertFalse(token.isExpired(), "Refreshable AccessToken should not expire after refresh"); CountDownLatch secondCountDownLatch = new CountDownLatch(1); @@ -170,65 +170,65 @@ public void refresherShouldBeCalledAgainAfterFirstRefreshCall() throws Interrupt secondCountDownLatch.await(); assertEquals(2, immediateFresher.numCalls()); - token = userCredential.getToken().get(); + token = tokenCredential.getToken().get(); assertFalse(token.isExpired(), "Refreshable AccessToken should not expire after second refresh"); - userCredential.close(); + tokenCredential.close(); } @Test public void shouldNotCallRefresherWhenTokenStillValid() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", 15 * 60); immediateFresher.resetCallCount(); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, false); - AccessToken token = userCredential.getToken().get(); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, false); + AccessToken token = tokenCredential.getToken().get(); assertFalse(token.isExpired()); assertEquals(tokenStr, token.getToken()); assertEquals(0, immediateFresher.numCalls()); for (int i = 0; i < 3; i++) { - userCredential.getToken(); + tokenCredential.getToken(); assertEquals(0, immediateFresher.numCalls()); } - userCredential.close(); + tokenCredential.close(); } @Test public void expiredTokenShouldBeRefreshedOnDemandWithoutProactiveFetch() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", -5 * 60); immediateFresher.resetCallCount(); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, false); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, false); assertEquals(0, immediateFresher.numCalls()); - AccessToken token = userCredential.getToken().get(); + AccessToken token = tokenCredential.getToken().get(); assertFalse(token.isExpired(), "Expired AccessToken should have been refreshed on demand"); assertEquals(1, immediateFresher.numCalls()); for (int i = 0; i < 3; i++) { - userCredential.getToken(); + tokenCredential.getToken(); assertEquals(1, immediateFresher.numCalls()); } - userCredential.close(); + tokenCredential.close(); } @Test public void shouldCallbackOnDemandWithoutRefresher() throws InterruptedException, ExecutionException, IOException { immediateFresher.resetCallCount(); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher); - AccessToken accessToken = userCredential.getToken().get(); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(immediateFresher); + AccessToken accessToken = tokenCredential.getToken().get(); assertEquals(1, immediateFresher.numCalls()); assertFalse(accessToken.isExpired(), "On demand fetching case, should be still valid"); - userCredential.close(); + tokenCredential.close(); } @Test public void shouldStopRefreshTimerWhenClosed() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", 12 * 60); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); - assertTrue(userCredential.hasProactiveFetcher()); - userCredential.close(); - assertFalse(userCredential.hasProactiveFetcher()); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(immediateFresher, tokenStr, true); + assertTrue(tokenCredential.hasProactiveFetcher()); + tokenCredential.close(); + assertFalse(tokenCredential.hasProactiveFetcher()); } class ExceptionRefresher implements TokenRefresher { @@ -262,27 +262,27 @@ public Future getFetchTokenFuture() { @Test public void shouldNotModifyTokenWhenRefresherThrows() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", 601); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(exceptionRefresher, tokenStr, true); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(exceptionRefresher, tokenStr, true); CountDownLatch countDownLatch = new CountDownLatch(1); exceptionRefresher.setOnCallReturn(countDownLatch::countDown); countDownLatch.await(); assertEquals(1, exceptionRefresher.numCalls(), "Refresher was called once"); - AccessToken token = userCredential.getToken().get(); + AccessToken token = tokenCredential.getToken().get(); assertFalse(token.isExpired(), "When failed to refresh, token remains valid since it is not expired yet"); - userCredential.close(); + tokenCredential.close(); } @Test public void doNotSwallowExceptionWithoutProactiveFetching() throws InterruptedException, ExecutionException, IOException { String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", -5 * 60); exceptionRefresher.resetCallCount(); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(exceptionRefresher, tokenStr, false); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(exceptionRefresher, tokenStr, false); assertThrows(Exception.class, () -> { - userCredential.getToken(); + tokenCredential.getToken(); }, "Should not swallow exception when client throws"); assertEquals(1, exceptionRefresher.numCalls()); - userCredential.close(); + tokenCredential.close(); } class MockLongRunningRefresher implements TokenRefresher { @@ -359,29 +359,29 @@ public void shouldCallRefresherOnlyOnceWhileRefreshingIsInProgress() longRunningRefresher.setOnCallReturn(countDownLatch::countDown); String tokenStr = tokenMocker.generateRawToken("resourceId", "userIdentity", -5 * 60); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(longRunningRefresher, tokenStr, true); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(longRunningRefresher, tokenStr, true); countDownLatch.await(); assertEquals(1, longRunningRefresher.numCalls()); for (int i = 0; i < 3; i++) { - userCredential.getToken(); + tokenCredential.getToken(); assertEquals(1, longRunningRefresher.numCalls(), "No additional call is made by getToken"); } - userCredential.close(); + tokenCredential.close(); } @Test public void withoutInitialTokenShouldCallFresherOnlyOnceWhileRefreshingIsInProgress() throws InterruptedException, ExecutionException, IOException { longRunningRefresher.resetCallCount(); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(longRunningRefresher); - userCredential.getToken(); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(longRunningRefresher); + tokenCredential.getToken(); assertEquals(1, longRunningRefresher.numCalls()); for (int i = 0; i < 3; i++) { - userCredential.getToken(); + tokenCredential.getToken(); assertEquals(1, longRunningRefresher.numCalls(), "No additional calls made by getToken call"); } - userCredential.close(); + tokenCredential.close(); } class MockExceptionFutureRefresher implements TokenRefresher { @@ -439,13 +439,13 @@ public String get(long timeout, TimeUnit unit) public void shouldRefreshAgainAfterClientFutureThrows() throws InterruptedException, ExecutionException, IOException { exceptionFutureRefresher.resetCallCount(); - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(exceptionFutureRefresher); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(exceptionFutureRefresher); assertEquals(0, exceptionFutureRefresher.numCalls()); - Future accessToken = userCredential.getToken(); + Future accessToken = tokenCredential.getToken(); assertEquals(1, exceptionFutureRefresher.numCalls()); for (int i = 0; i < 3; i++) { - userCredential.getToken(); + tokenCredential.getToken(); assertEquals(1, exceptionFutureRefresher.numCalls()); } @@ -453,18 +453,18 @@ public void shouldRefreshAgainAfterClientFutureThrows() throws InterruptedExcept accessToken.get(); }); - userCredential.getToken(); + tokenCredential.getToken(); assertEquals(2, exceptionFutureRefresher.numCalls()); - userCredential.close(); + tokenCredential.close(); } @Test public void shouldThrowWhenGetTokenCalledOnClosedObject() throws IOException { - CommunicationTokenCredential userCredential = new CommunicationTokenCredential(exceptionFutureRefresher); - userCredential.close(); + CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(exceptionFutureRefresher); + tokenCredential.close(); assertThrows(RuntimeException.class, () -> { - userCredential.getToken(); + tokenCredential.getToken(); }); } }