diff --git a/eng/.docsettings.yml b/eng/.docsettings.yml index da01b44b34666..50b913ab73d33 100644 --- a/eng/.docsettings.yml +++ b/eng/.docsettings.yml @@ -124,6 +124,7 @@ known_content_issues: - ['sdk/core/README.md', '#3113'] - ['sdk/core/azure-core-experimental/README.md', '#3113'] - ['sdk/cognitiveservices/ms-azure-cs-textanalytics/README.md', '#3113'] + - ['sdk/communication/azure-communication-callingserver/swagger/README.md', '#3113'] - ['sdk/communication/azure-communication-chat/swagger/README.md', '#3113'] - ['sdk/communication/azure-communication-sms/swagger/README.md', '#3113'] - ['sdk/communication/azure-communication-identity/swagger/README.md', '#3113'] diff --git a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml index dd6f6ae5eb993..d0173db14e35e 100755 --- a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml +++ b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml @@ -598,4 +598,8 @@ the main ServiceBusClientBuilder. --> + + + + diff --git a/eng/jacoco-test-coverage/pom.xml b/eng/jacoco-test-coverage/pom.xml index decdd7c8cce26..03b9c438960af 100644 --- a/eng/jacoco-test-coverage/pom.xml +++ b/eng/jacoco-test-coverage/pom.xml @@ -81,6 +81,11 @@ azure-communication-phonenumbers 1.1.0-beta.1 + + com.azure + azure-communication-callingserver + 1.0.0-beta.1 + com.azure azure-containers-containerregistry diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt index f25d3cf4b708c..223b98aa5a417 100644 --- a/eng/versioning/version_client.txt +++ b/eng/versioning/version_client.txt @@ -52,6 +52,7 @@ com.azure:azure-analytics-synapse-spark;1.0.0-beta.3;1.0.0-beta.4 com.azure:azure-analytics-synapse-managedprivateendpoints;1.0.0-beta.3;1.0.0-beta.4 com.azure:azure-analytics-synapse-monitoring;1.0.0-beta.3;1.0.0-beta.4 com.azure:azure-communication-chat;1.0.0;1.1.0-beta.1 +com.azure:azure-communication-callingserver;1.0.0-beta.1;1.0.0-beta.1 com.azure:azure-communication-common;1.0.2;1.1.0-beta.1 com.azure:azure-communication-sms;1.0.3;1.1.0-beta.1 com.azure:azure-communication-identity;1.1.1;1.2.0-beta.1 diff --git a/sdk/communication/azure-communication-callingserver/CHANGELOG.md b/sdk/communication/azure-communication-callingserver/CHANGELOG.md new file mode 100644 index 0000000000000..b8532b23f4860 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/CHANGELOG.md @@ -0,0 +1,19 @@ +# Release History + +## 1.0.0-beta.1 (Unreleased) +This is the first release of Azure Communication Calling Server SDK. For more information, please see the [README][read_me]. + +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 .NET GitHub repo](https://github.com/Azure/azure-sdk-for-net/issues). + +### Features Added +- Create outbound call to an Azure Communication Service user or a phone number. +- Hangup the existing call. +- Play audio in the call. +- Outbound APIs for call recording including start, pause, resume and stop. +- Subscribe to tone and receive tone selections via events. +- Invite and remove participants from the call. + + +[read_me]: https://github.com/Azure/azure-sdk-for-java/blob/feature/communication-ServerCalling/sdk/communication/azure-communication-callingserver/README.md + + diff --git a/sdk/communication/azure-communication-callingserver/README.md b/sdk/communication/azure-communication-callingserver/README.md new file mode 100644 index 0000000000000..28ad4c684c2ea --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/README.md @@ -0,0 +1,176 @@ +# Azure Communication CallingServer Service client library for Java + +This package contains a Java SDK for Azure Communication CallingServer Service. + +[Source code][source] | [Package (Maven)][package] | [API reference documentation][api_documentation] +| [Product documentation][product_docs] + +## Getting started + +### Prerequisites + +- An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). +- [Java Development Kit (JDK)](https://docs.microsoft.com/java/azure/jdk/?view=azure-java-stable) version 8 or above. +- [Apache Maven](https://maven.apache.org/download.cgi). +- A deployed Communication Services resource. You can use the [Azure Portal](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp) or the [Azure PowerShell](https://docs.microsoft.com/powershell/module/az.communication/new-azcommunicationservice) to set it up. + +### Include the package + +[//]: # ({x-version-update-start;com.azure:azure-communication-callingserver;current}) +```xml + + com.azure + azure-communication-callingserver + 1.0.0-beta.1 + +``` +[//]: # ({x-version-update-end}) + +## Key concepts + +At a high level the Azure Communication CallingServer API will support two kinds of scenarios: + +- In-call app: Contoso server app is a participant in the call. + +- Out-call app: Contoso server app is not a participant in the call - Server app can subscribe to events for calls between specific users or even all users belonging to the ACS azure resource. + +Based on if the Contoso app join a call or not, APIs can be divided into two categories: + +- In-call APIs: Contoso app is one of the participant in a call. It can be applicable for app to person (A2P) or person to app (P2A) case, or multi-party/group calls that server apps joined as a participant to provide audio/prompt. + +- Out-of-call APIs: Contoso app can invoke these set of APIs without joining a call. It is applicable for actions on P2P calls, A2P calls, P2A calls and group calls. + +## Examples + +### Authenticate the client + + +You can provide the connection string using the connectionString() function of `CallingServerClientBuilder`. Once you initialized a `CallingServerClient` class, you can do the different server calling operations. + +```java +// Your connectionString retrieved from your Azure Communication Service +String connectionString = "endpoint=https://.communication.azure.com/;accesskey="; + +// Initialize the calling server client +final CallingServerClientBuilder builder = new CallingServerClientBuilder(); +builder.connectionString(connectionString); +CallingServerClient callingServerClient = builder.buildClient(); +``` + +### Create call, Add participant and Hangup a call + +#### Create a Call: + +```java +CommunicationIdentifier source = new CommunicationUserIdentifier(""); +CommunicationIdentifier firstCallee = new CommunicationUserIdentifier(""); +CommunicationIdentifier secondCallee = new CommunicationUserIdentifier(""); + +CommunicationIdentifier[] targets = new CommunicationIdentifier[] { firstCallee, secondCallee }; + +String callbackUri = ""; + +CallModality[] requestedMediaTypes = new CallModality[] { CallModality.AUDIO, CallModality.VIDEO }; + +EventSubscriptionType[] requestedCallEvents = new EventSubscriptionType[] { + EventSubscriptionType.DTMF_RECEIVED, + EventSubscriptionType.PARTICIPANTS_UPDATED }; + +CreateCallOptions createCallOptions = new CreateCallOptions( + callbackUri, + requestedMediaTypes, + requestedCallEvents); + +CallConnection callConnection = callingServerClient.createCallConnection(source, targets, createCallOptions); +``` + +#### Add a participant to a Call: + +```java +CommunicationIdentifier thirdCallee = new CommunicationUserIdentifier(""); +callConnection.addParticipant(thirdCallee, "ACS User 3", ""); +``` + +#### Hangup a Call: + +```java +callConnection.hangup(); +``` + +### Start, Pause, Resume, Stop and Get a recording + +#### Start a Recording: + +```java +String serverCallId = ""; +String recordingStateCallbackUri = ""; +ServerCall serverCall = callingServerClient.initializeServerCall(serverCallId); +StartCallRecordingResponse response = serverCall.startRecording(recordingStateCallbackUri); +String recordingId = response.getRecordingId(); +``` + +#### Pause a Recording: + +```java +serverCall.pauseRecording(recordingId); +``` + +#### Resume a Recording: + +```java +serverCall.resumeRecording(recordingId); +``` + +#### Stop a Recording: + +```java +serverCall.stopRecording(recordingId); +``` + +#### Get the Recording State: + +```java +CallRecordingStateResult callRecordingStateResult = serverCall.getRecordingState(recordingId); +``` + +### Play Audio in Call + +#### Play Audio: + +```java +String audioFileUri = ""; +String audioFileId = ""; +String callbackUri = ""; +String context = ""; +PlayAudioResponse playAudioResponse = serverCall.playAudio(audioFileUri, audioFileId, callbackUri, context); +``` + +## Troubleshooting + +If you recieve a CommunicationErrorException with the messagae: "Action is invalid when call is not in Established state." This usually means the call has ended. This can occur if the participants all leave +the call, or participants did not accept the call before the call timed out. + +If you fail to start a call because of an HMAC validation error, be sure your access key is correct, and +that you are passing in a valid conversation id. + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a [Contributor License Agreement (CLA)][cla] declaring that you have the right to, and actually do, grant us the rights to use your contribution. + +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][coc]. For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments. + +## Next steps + +Check out other client libraries for Azure Communication Services + + +[cla]: https://cla.microsoft.com +[coc]: https://opensource.microsoft.com/codeofconduct/ +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ +[coc_contact]: mailto:opencode@microsoft.com +[product_docs]: https://docs.microsoft.com/azure/communication-services/ +[package]: https://search.maven.org/artifact/com.azure/azure-communication-callingserver +[api_documentation]: https://aka.ms/java-docs +[source]: https://github.com/Azure/azure-sdk-for-java/tree/feature/communication-ServerCalling/sdk/communication/azure-communication-callingserver/src diff --git a/sdk/communication/azure-communication-callingserver/pom.xml b/sdk/communication/azure-communication-callingserver/pom.xml new file mode 100644 index 0000000000000..71903ce125dca --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/pom.xml @@ -0,0 +1,154 @@ + + + 4.0.0 + + + com.azure + azure-client-sdk-parent + 1.7.0 + ../../parents/azure-client-sdk-parent + + + com.azure + azure-communication-callingserver + jar + 1.0.0-beta.1 + + Microsoft Azure client library for CallingServer service + + This package contains clients and data structures used to make call with Azure Communication CallingServer Service. + For this release, see notes - https://github.com/Azure/azure-sdk-for-java/blob/master/sdk/communication/azure-communication-callingserver/README.md and https://github.com/Azure/azure-sdk-for-java/blob/master/sdk/communication/azure-communication-callingserver/CHANGELOG.md. + + https://github.com/Azure/azure-sdk-for-java + + + + azure-java-build-docs + ${site.url}/site/${project.artifactId} + + + + + https://github.com/Azure/azure-sdk-for-java + scm:git:git@github.com:Azure/azure-sdk-for-java.git + HEAD + + + + src/main + src/test + 0.70 + 0.70 + true + + + + + com.azure + azure-core + 1.17.0 + + + com.azure + azure-communication-common + 1.1.0-beta.1 + + + com.azure + azure-communication-identity + 1.1.1 + test + + + com.azure + azure-core-test + 1.6.3 + test + + + com.nimbusds + nimbus-jose-jwt + 9.8.1 + test + + + org.junit.jupiter + junit-jupiter-api + 5.7.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.7.2 + test + + + org.junit.jupiter + junit-jupiter-params + 5.7.2 + test + + + org.hamcrest + hamcrest-all + 1.3 + test + + + org.mockito + mockito-core + 3.9.0 + test + + + io.projectreactor + reactor-test + 3.4.6 + test + + + com.azure + azure-core-http-okhttp + 1.7.0 + test + + + com.azure + azure-identity + 1.3.1 + test + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.5 + + + com/azure/communication/callingserver/*.class + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M3 + + + + + com.azure:* + + + + + + + + diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallConnection.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallConnection.java new file mode 100644 index 0000000000000..c7efc1b675e50 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallConnection.java @@ -0,0 +1,234 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.models.CancelAllMediaOperationsResult; +import com.azure.communication.callingserver.models.PlayAudioOptions; +import com.azure.communication.callingserver.models.PlayAudioResult; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.core.annotation.ReturnType; +import com.azure.core.annotation.ServiceMethod; +import com.azure.core.http.rest.Response; +import com.azure.core.util.Context; +import com.azure.core.util.logging.ClientLogger; + +/** + * Sync Client that supports call connection operations. + */ +public final class CallConnection { + private final CallConnectionAsync callConnectionAsync; + private final ClientLogger logger = new ClientLogger(CallConnection.class); + + CallConnection(CallConnectionAsync callConnectionAsync) { + this.callConnectionAsync = callConnectionAsync; + } + + /** + * Get the call connection id property + * + * @return the id value. + */ + public String getCallConnectionId() { + return callConnectionAsync.getCallConnectionId(); + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param loop The flag indicating whether audio file needs to be played in loop or not. + * @param audioFileId An id for the media in the AudioFileUri, using which we cache the media. + * @param callbackUri call back uri to receive notifications. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public PlayAudioResult playAudio( + String audioFileUri, + boolean loop, + String audioFileId, + String callbackUri, + String operationContext) { + return callConnectionAsync + .playAudioInternal(audioFileUri, loop, audioFileId, callbackUri, operationContext).block(); + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param playAudioOptions Options for play audio. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public PlayAudioResult playAudio(String audioFileUri, PlayAudioOptions playAudioOptions) { + return callConnectionAsync.playAudioInternal(audioFileUri, playAudioOptions).block(); + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param loop The flag indicating whether audio file needs to be played in loop or not. + * @param audioFileId An id for the media in the AudioFileUri, using which we cache the media. + * @param callbackUri call back uri to receive notifications. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @param context A {@link Context} representing the request context. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response playAudioWithResponse( + String audioFileUri, + boolean loop, + String audioFileId, + String callbackUri, + String operationContext, + Context context) { + return callConnectionAsync + .playAudioWithResponseInternal( + audioFileUri, + loop, + audioFileId, + callbackUri, + operationContext, + context) + .block(); + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param playAudioOptions Options for play audio. + * @param context A {@link Context} representing the request context. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response playAudioWithResponse( + String audioFileUri, + PlayAudioOptions playAudioOptions, + Context context) { + return callConnectionAsync + .playAudioWithResponseInternal(audioFileUri, playAudioOptions, context) + .block(); + } + + /** + * Disconnect the current caller in a group-call or end a p2p-call. + * + * @return response for a successful hangup request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Void hangup() { + return callConnectionAsync.hangup().block(); + } + + /** + * Disconnect the current caller in a group-call or end a p2p-call. + * + * @param context A {@link Context} representing the request context. + * @return response for a successful hangup request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response hangupWithResponse(Context context) { + return callConnectionAsync.hangupWithResponse(context).block(); + } + + /** + * Cancel all media operations in the call. + * + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return response for a successful cancel all media operations request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public CancelAllMediaOperationsResult cancelAllMediaOperations(String operationContext) { + return callConnectionAsync.cancelAllMediaOperations(operationContext).block(); + } + + /** + * Cancel all media operations in the call. + * + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @param context A {@link Context} representing the request context. + * @return response for a successful cancel all media operations request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response cancelAllMediaOperationsWithResponse( + String operationContext, + Context context) { + return callConnectionAsync.cancelAllMediaOperationsWithResponse(operationContext, context).block(); + } + + /** + * Add a participant to the call. + * + * @param participant Invited participant. + * @param alternateCallerId The phone number to use when adding a phone number participant. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return response for a successful add participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Void addParticipant( + CommunicationIdentifier participant, + String alternateCallerId, + String operationContext) { + return callConnectionAsync.addParticipant(participant, alternateCallerId, operationContext).block(); + } + + /** + * Add a participant to the call. + * + * @param participant Invited participant. + * @param alternateCallerId The phone number to use when adding a phone number participant. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @param context A {@link Context} representing the request context. + * @return response for a successful add participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response addParticipantWithResponse( + CommunicationIdentifier participant, + String alternateCallerId, + String operationContext, + Context context) { + return callConnectionAsync + .addParticipantWithResponse(participant, alternateCallerId, operationContext, context).block(); + } + + /** + * Remove a participant from the call. + * + * @param participantId Participant id. + * @return response for a successful remove participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Void removeParticipant(String participantId) { + return callConnectionAsync.removeParticipant(participantId).block(); + } + + /** + * Remove a participant from the call. + * + * @param participantId Participant id. + * @param context A {@link Context} representing the request context. + * @return response for a successful remove participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response removeParticipantWithResponse(String participantId, Context context) { + return callConnectionAsync.removeParticipantWithResponse(participantId, context).block(); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallConnectionAsync.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallConnectionAsync.java new file mode 100644 index 0000000000000..d618e563bd28d --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallConnectionAsync.java @@ -0,0 +1,440 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import static com.azure.core.util.FluxUtil.monoError; +import static com.azure.core.util.FluxUtil.withContext; + +import java.util.Objects; + +import com.azure.communication.callingserver.implementation.CallConnectionsImpl; +import com.azure.communication.callingserver.implementation.converters.InviteParticipantRequestConverter; +import com.azure.communication.callingserver.implementation.converters.CancelAllMediaOperationsResultConverter; +import com.azure.communication.callingserver.implementation.converters.CallingServerErrorConverter; +import com.azure.communication.callingserver.implementation.converters.PlayAudioResultConverter; +import com.azure.communication.callingserver.implementation.models.CommunicationErrorException; +import com.azure.communication.callingserver.implementation.models.CancelAllMediaOperationsRequest; +import com.azure.communication.callingserver.implementation.models.InviteParticipantsRequest; +import com.azure.communication.callingserver.implementation.models.PlayAudioRequest; +import com.azure.communication.callingserver.models.CancelAllMediaOperationsResult; +import com.azure.communication.callingserver.models.PlayAudioOptions; +import com.azure.communication.callingserver.models.PlayAudioResult; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.core.annotation.ReturnType; +import com.azure.core.annotation.ServiceMethod; +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.SimpleResponse; +import com.azure.core.util.Context; +import com.azure.core.util.logging.ClientLogger; + +import reactor.core.publisher.Mono; + +/** + * Async client that supports call connection operations. + */ +public final class CallConnectionAsync { + + private final String callConnectionId; + private final CallConnectionsImpl callConnectionInternal; + private final ClientLogger logger = new ClientLogger(CallConnectionAsync.class); + + CallConnectionAsync(String callConnectionId, CallConnectionsImpl callConnectionInternal) { + this.callConnectionId = callConnectionId; + this.callConnectionInternal = callConnectionInternal; + } + + /** + * Get the call connection id property + * + * @return the id value. + */ + public String getCallConnectionId() { + return callConnectionId; + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param loop The flag indicating whether audio file needs to be played in loop or not. + * @param audioFileId An id for the media in the AudioFileUri, using which we cache the media. + * @param callbackUri call back uri to receive notifications. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono playAudio( + String audioFileUri, + boolean loop, + String audioFileId, + String callbackUri, + String operationContext) { + return playAudioInternal(audioFileUri, loop, audioFileId, callbackUri, operationContext); + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param playAudioOptions Options for play audio. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono playAudio(String audioFileUri, PlayAudioOptions playAudioOptions) { + return playAudioInternal(audioFileUri, playAudioOptions); + + } + + Mono playAudioInternal( + String audioFileUri, + boolean loop, + String audioFileId, + String callbackUri, + String operationContext) { + try { + Objects.requireNonNull(audioFileUri, "'audioFileUri' cannot be null."); + PlayAudioRequest playAudioRequest = + new PlayAudioRequest() + .setAudioFileUri(audioFileUri) + .setLoop(loop) + .setAudioFileId(audioFileId) + .setOperationContext(operationContext) + .setCallbackUri(callbackUri); + return playAudioInternal(playAudioRequest); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + Mono playAudioInternal(String audioFileUri, PlayAudioOptions playAudioOptions) { + try { + Objects.requireNonNull(audioFileUri, "'audioFileUri' cannot be null."); + PlayAudioRequest request = new PlayAudioRequest().setAudioFileUri(audioFileUri); + if (playAudioOptions != null) { + request + .setLoop(playAudioOptions.isLoop()) + .setOperationContext(playAudioOptions.getOperationContext()) + .setAudioFileId(playAudioOptions.getAudioFileId()) + .setCallbackUri(playAudioOptions.getCallbackUri()); + } + return playAudioInternal(request); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + Mono playAudioInternal(PlayAudioRequest playAudioRequest) { + try { + return callConnectionInternal.playAudioAsync(callConnectionId, playAudioRequest) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .flatMap(result -> Mono.just(PlayAudioResultConverter.convert(result))); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param loop The flag indicating whether audio file needs to be played in loop or not. + * @param audioFileId An id for the media in the AudioFileUri, using which we cache the media. + * @param callbackUri call back uri to receive notifications. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> playAudioWithResponse( + String audioFileUri, + boolean loop, + String audioFileId, + String callbackUri, + String operationContext) { + return playAudioWithResponseInternal( + audioFileUri, + loop, + audioFileId, + callbackUri, + operationContext, + null); + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param playAudioOptions Options for play audio. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> playAudioWithResponse( + String audioFileUri, + PlayAudioOptions playAudioOptions) { + return playAudioWithResponseInternal(audioFileUri, playAudioOptions, null); + } + + Mono> playAudioWithResponseInternal( + String audioFileUri, + boolean loop, + String audioFileId, + String callbackUri, + String operationContext, + Context context) { + try { + Objects.requireNonNull(audioFileUri, "'audioFileUri' cannot be null."); + PlayAudioRequest playAudioRequest = + new PlayAudioRequest() + .setAudioFileUri(audioFileUri) + .setLoop(loop) + .setAudioFileId(audioFileId) + .setOperationContext(operationContext) + .setCallbackUri(callbackUri); + return playAudioWithResponseInternal(playAudioRequest, context); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + + + } + + Mono> playAudioWithResponseInternal( + String audioFileUri, + PlayAudioOptions playAudioOptions, + Context context) { + try { + Objects.requireNonNull(audioFileUri, "'audioFileUri' cannot be null."); + PlayAudioRequest request = new PlayAudioRequest().setAudioFileUri(audioFileUri); + if (playAudioOptions != null) { + request + .setLoop(playAudioOptions.isLoop()) + .setOperationContext(playAudioOptions.getOperationContext()) + .setAudioFileId(playAudioOptions.getAudioFileId()) + .setCallbackUri(playAudioOptions.getCallbackUri()); + } + return playAudioWithResponseInternal(request, context); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + Mono> playAudioWithResponseInternal( + PlayAudioRequest playAudioRequest, + Context context) { + try { + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return callConnectionInternal + .playAudioWithResponseAsync(callConnectionId, playAudioRequest, contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .map(response -> + new SimpleResponse<>(response, PlayAudioResultConverter.convert(response.getValue()))); + }); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Hangup a call. + * + * @return response for a successful hangup request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono hangup() { + try { + return callConnectionInternal.hangupCallAsync(callConnectionId) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Hangup a call. + * + * @return response for a successful hangup request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> hangupWithResponse() { + return hangupWithResponse(null); + } + + Mono> hangupWithResponse(Context context) { + try { + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return callConnectionInternal.hangupCallWithResponseAsync(callConnectionId, contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + }); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Cancel all media operations in the call. + * + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return the response payload of the cancel all media operations. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono cancelAllMediaOperations(String operationContext) { + try { + CancelAllMediaOperationsRequest request = new CancelAllMediaOperationsRequest(); + request.setOperationContext(operationContext); + return callConnectionInternal.cancelAllMediaOperationsAsync(callConnectionId, request) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .flatMap(result -> Mono.just(CancelAllMediaOperationsResultConverter.convert(result))); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Cancel all media operations in the call. + * + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return the response payload of the cancel all media operations. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> cancelAllMediaOperationsWithResponse(String operationContext) { + return cancelAllMediaOperationsWithResponse(operationContext, null); + } + + Mono> cancelAllMediaOperationsWithResponse( + String operationContext, + Context context) { + try { + CancelAllMediaOperationsRequest request = new CancelAllMediaOperationsRequest(); + request.setOperationContext(operationContext); + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return callConnectionInternal + .cancelAllMediaOperationsWithResponseAsync(callConnectionId, request, contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .map(response -> + new SimpleResponse<>(response, CancelAllMediaOperationsResultConverter.convert(response.getValue()))); + }); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Add a participant to the call. + * + * @param participant Invited participant. + * @param alternateCallerId The phone number to use when adding a phone number participant. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return response for a successful add participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono addParticipant( + CommunicationIdentifier participant, + String alternateCallerId, + String operationContext) { + try { + Objects.requireNonNull(participant, "'participant' cannot be null."); + InviteParticipantsRequest request = InviteParticipantRequestConverter.convert(participant, + alternateCallerId, + operationContext, + null); + return callConnectionInternal.inviteParticipantsAsync(callConnectionId, request) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Add a participant to the call. + * + * @param participant Invited participant. + * @param alternateCallerId The phone number to use when adding a phone number participant. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return response for a successful add participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> addParticipantWithResponse( + CommunicationIdentifier participant, + String alternateCallerId, + String operationContext) { + return addParticipantWithResponse(participant, alternateCallerId, operationContext, null); + } + + Mono> addParticipantWithResponse( + CommunicationIdentifier participant, + String alternateCallerId, + String operationContext, + Context context) { + try { + Objects.requireNonNull(participant, "'participant' cannot be null."); + InviteParticipantsRequest request = + InviteParticipantRequestConverter + .convert(participant, alternateCallerId, operationContext, null); + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return callConnectionInternal + .inviteParticipantsWithResponseAsync(callConnectionId, request, contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + }); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Remove a participant from the call. + * + * @param participantId Participant id. + * @return response for a successful remove participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono removeParticipant(String participantId) { + try { + return callConnectionInternal.removeParticipantAsync(callConnectionId, participantId) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Remove a participant from the call. + * + * @param participantId Participant id. + * @return response for a successful remove participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> removeParticipantWithResponse(String participantId) { + return removeParticipantWithResponse(participantId, null); + } + + Mono> removeParticipantWithResponse(String participantId, Context context) { + try { + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return callConnectionInternal + .removeParticipantWithResponseAsync(callConnectionId, participantId, contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + }); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallingServerAsyncClient.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallingServerAsyncClient.java new file mode 100644 index 0000000000000..eb8a1d240a9d6 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallingServerAsyncClient.java @@ -0,0 +1,444 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.implementation.AzureCommunicationCallingServerServiceImpl; +import com.azure.communication.callingserver.implementation.CallConnectionsImpl; +import com.azure.communication.callingserver.implementation.ServerCallsImpl; +import com.azure.communication.callingserver.implementation.converters.CallConnectionRequestConverter; +import com.azure.communication.callingserver.implementation.converters.JoinCallRequestConverter; +import com.azure.communication.callingserver.implementation.converters.CallingServerErrorConverter; +import com.azure.communication.callingserver.implementation.models.CommunicationErrorException; +import com.azure.communication.callingserver.implementation.models.CreateCallRequest; +import com.azure.communication.callingserver.models.CreateCallOptions; +import com.azure.communication.callingserver.models.JoinCallOptions; +import com.azure.communication.callingserver.models.ParallelDownloadOptions; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.core.annotation.ReturnType; +import com.azure.core.annotation.ServiceClient; +import com.azure.core.annotation.ServiceMethod; +import com.azure.core.http.HttpRange; +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.SimpleResponse; +import com.azure.core.util.Context; +import com.azure.core.util.logging.ClientLogger; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousFileChannel; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import static com.azure.core.util.FluxUtil.fluxError; +import static com.azure.core.util.FluxUtil.monoError; +import static com.azure.core.util.FluxUtil.withContext; + + +/** + * An Async Client that supports calling server operations. + * + *

Instantiating a asynchronous Calling Server Client

+ * + * {@codesnippet com.azure.communication.callingserver.CallingServerAsyncClient.pipeline.instantiation} + * + *

View {@link CallingServerClientBuilder this} for additional ways to construct the client.

+ * + * @see CallingServerClientBuilder + */ +@ServiceClient(builder = CallingServerClientBuilder.class, isAsync = true) +public final class CallingServerAsyncClient { + private final CallConnectionsImpl callConnectionInternal; + private final ServerCallsImpl serverCallInternal; + private final ClientLogger logger = new ClientLogger(CallingServerAsyncClient.class); + private final ContentDownloader contentDownloader; + + CallingServerAsyncClient(AzureCommunicationCallingServerServiceImpl callServiceClient) { + callConnectionInternal = callServiceClient.getCallConnections(); + serverCallInternal = callServiceClient.getServerCalls(); + + contentDownloader = new ContentDownloader( + callServiceClient.getEndpoint(), + callServiceClient.getHttpPipeline()); + } + + /** + * Create a Call Connection Request from source identity to targets identity. + * + * @param source The source of the call. + * @param targets The targets of the call. + * @param createCallOptions The call Options. + * @return response for a successful CreateCallConnection request. + * + * {@codesnippet com.azure.communication.callingserver.CallingServerAsyncClient.create.call.connection.async} + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono createCallConnection( + CommunicationIdentifier source, + CommunicationIdentifier[] targets, + CreateCallOptions createCallOptions) { + try { + Objects.requireNonNull(source, "'source' cannot be null."); + Objects.requireNonNull(targets, "'targets' cannot be null."); + Objects.requireNonNull(createCallOptions, "'createCallOptions' cannot be null."); + CreateCallRequest request = CallConnectionRequestConverter.convert(source, targets, createCallOptions); + return callConnectionInternal.createCallAsync(request) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .flatMap(response -> Mono.just(new CallConnectionAsync(response.getCallConnectionId(), callConnectionInternal))); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Create a Call Connection Request from source identity to targets identity. + * + * @param source The source of the call. + * @param targets The targets of the call. + * @param createCallOptions The call Options. + * @return response for a successful CreateCallConnection request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> createCallConnectionWithResponse( + CommunicationIdentifier source, + CommunicationIdentifier[] targets, + CreateCallOptions createCallOptions) { + try { + Objects.requireNonNull(source, "'source' cannot be null."); + Objects.requireNonNull(targets, "'targets' cannot be null."); + Objects.requireNonNull(createCallOptions, "'CreateCallOptions' cannot be null."); + CreateCallRequest request = CallConnectionRequestConverter.convert(source, targets, createCallOptions); + return callConnectionInternal.createCallWithResponseAsync(request) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .map(response -> new SimpleResponse<>(response, new CallConnectionAsync(response.getValue().getCallConnectionId(), callConnectionInternal))); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + Mono createCallConnectionInternal( + CommunicationIdentifier source, + CommunicationIdentifier[] targets, + CreateCallOptions createCallOptions) { + try { + Objects.requireNonNull(source, "'source' cannot be null."); + Objects.requireNonNull(targets, "'targets' cannot be null."); + Objects.requireNonNull(createCallOptions, "'createCallOptions' cannot be null."); + CreateCallRequest request = CallConnectionRequestConverter.convert(source, targets, createCallOptions); + return callConnectionInternal.createCallAsync(request) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .flatMap(response -> Mono.just(new CallConnection(new CallConnectionAsync(response.getCallConnectionId(), callConnectionInternal)))); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + Mono> createCallConnectionWithResponseInternal( + CommunicationIdentifier source, + CommunicationIdentifier[] targets, + CreateCallOptions createCallOptions, + Context context) { + try { + Objects.requireNonNull(source, "'source' cannot be null."); + Objects.requireNonNull(targets, "'targets' cannot be null."); + Objects.requireNonNull(createCallOptions, "'CreateCallOptions' cannot be null."); + CreateCallRequest request = CallConnectionRequestConverter.convert(source, targets, createCallOptions); + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return callConnectionInternal.createCallWithResponseAsync(request, contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .map(response -> new SimpleResponse<>(response, + new CallConnection(new CallConnectionAsync(response.getValue().getCallConnectionId(), callConnectionInternal)))); + }); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Join a Call + * + * @param serverCallId The server call id. + * @param source to Join Call. + * @param joinCallOptions join call options. + * @return response for a successful join request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono join( + String serverCallId, + CommunicationIdentifier source, + JoinCallOptions joinCallOptions) { + try { + Objects.requireNonNull(source, "'source' cannot be null."); + Objects.requireNonNull(joinCallOptions, "'joinCallOptions' cannot be null."); + return serverCallInternal + .joinCallAsync(serverCallId, JoinCallRequestConverter.convert(source, joinCallOptions)) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .flatMap(response -> Mono.just(new CallConnectionAsync(response.getCallConnectionId(), callConnectionInternal))); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Join a Call + * + * @param serverCallId The server call id. + * @param source to Join Call. + * @param joinCallOptions join call options. + * @return response for a successful join request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono>joinWithResponse( + String serverCallId, + CommunicationIdentifier source, + JoinCallOptions joinCallOptions) { + try { + Objects.requireNonNull(source, "'source' cannot be null."); + Objects.requireNonNull(joinCallOptions, "'joinCallOptions' cannot be null."); + return serverCallInternal. + joinCallWithResponseAsync(serverCallId, JoinCallRequestConverter.convert(source, joinCallOptions)) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .map(response -> new SimpleResponse<>(response, + new CallConnectionAsync(response.getValue().getCallConnectionId(), callConnectionInternal))); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + Mono joinInternal( + String serverCallId, + CommunicationIdentifier source, + JoinCallOptions joinCallOptions) { + try { + Objects.requireNonNull(source, "'source' cannot be null."); + Objects.requireNonNull(joinCallOptions, "'joinCallOptions' cannot be null."); + return serverCallInternal + .joinCallAsync(serverCallId, JoinCallRequestConverter.convert(source, joinCallOptions)) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .flatMap(response -> + Mono.just(new CallConnection(new CallConnectionAsync(response.getCallConnectionId(), + callConnectionInternal)))); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + Mono>joinWithResponseInternal( + String serverCallId, + CommunicationIdentifier source, + JoinCallOptions joinCallOptions, + Context context) { + try { + Objects.requireNonNull(source, "'source' cannot be null."); + Objects.requireNonNull(joinCallOptions, "'joinCallOptions' cannot be null."); + + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return serverCallInternal + .joinCallWithResponseAsync(serverCallId, JoinCallRequestConverter.convert(source, joinCallOptions), contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .map(response -> + new SimpleResponse<>( + response, + new CallConnection(new CallConnectionAsync(response.getValue().getCallConnectionId(), + callConnectionInternal)))); + }); + + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Get CallConnection object + * + * @param callConnectionId The call connection id. + * @return CallConnection + */ + public CallConnectionAsync getCallConnection(String callConnectionId) { + Objects.requireNonNull(callConnectionId, "'callConnectionId' cannot be null."); + return new CallConnectionAsync(callConnectionId, callConnectionInternal); + } + + CallConnection getCallConnectionInternal(String callConnectionId) { + Objects.requireNonNull(callConnectionId, "'callConnectionId' cannot be null."); + return new CallConnection(new CallConnectionAsync(callConnectionId, callConnectionInternal)); + } + + /** + * Get ServerCall object + * + * @param serverCallId The server call id. + * @return ServerCall + */ + public ServerCallAsync initializeServerCall(String serverCallId) { + Objects.requireNonNull(serverCallId, "'serverCallId' cannot be null."); + return new ServerCallAsync(serverCallId, serverCallInternal); + } + + ServerCall initializeServerCallInternal(String serverCallId) { + Objects.requireNonNull(serverCallId, "'serverCallId' cannot be null."); + return new ServerCall(new ServerCallAsync(serverCallId, serverCallInternal)); + } + + /** + * Download the recording content, e.g. Recording's metadata, Recording video, from the ACS endpoint + * passed as parameter. + * @param sourceEndpoint - URL where the content is located. + * @return A {@link Flux} object containing the byte stream of the content requested. + */ + @ServiceMethod(returns = ReturnType.COLLECTION) + public Flux downloadStream(String sourceEndpoint) { + try { + Objects.requireNonNull(sourceEndpoint, "'sourceEndpoint' cannot be null"); + return downloadStream(sourceEndpoint, null); + } catch (RuntimeException ex) { + return fluxError(logger, ex); + } + } + + /** + * Download the recording content, e.g. Recording's metadata, Recording video, from the ACS endpoint + * passed as parameter. + * @param sourceEndpoint - URL where the content is located. + * @param httpRange - An optional {@link HttpRange} value containing the range of bytes to download. If missing, + * the whole content will be downloaded. + * @return A {@link Flux} object containing the byte stream of the content requested. + */ + @ServiceMethod(returns = ReturnType.COLLECTION) + public Flux downloadStream(String sourceEndpoint, HttpRange httpRange) { + try { + Objects.requireNonNull(sourceEndpoint, "'sourceEndpoint' cannot be null"); + return contentDownloader.downloadStreamWithResponse(sourceEndpoint, httpRange, null) + .map(Response::getValue) + .flux() + .flatMap(flux -> flux); + } catch (RuntimeException ex) { + return fluxError(logger, ex); + } + } + + /** + * Download the recording content, (e.g. Recording's metadata, Recording video, etc.) from the {@code endpoint}. + * @param sourceEndpoint - URL where the content is located. + * @param range - An optional {@link HttpRange} value containing the range of bytes to download. If missing, + * the whole content will be downloaded. + * @return A {@link Mono} object containing a {@link Response} with the byte stream of the content requested. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono>> downloadStreamWithResponse(String sourceEndpoint, HttpRange range) { + try { + Objects.requireNonNull(sourceEndpoint, "'sourceEndpoint' cannot be null"); + return contentDownloader.downloadStreamWithResponse(sourceEndpoint, range, null); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Download the content located in {@code endpoint} into a file marked by {@code path}. + * This download will be done using parallel workers. + * @param sourceEndpoint - ACS URL where the content is located. + * @param destinationPath - File location. + * @param parallelDownloadOptions - an optional {@link ParallelDownloadOptions} object to modify how the parallel + * download will work. + * @param overwrite - True to overwrite the file if it exists. + * @return response for a successful downloadTo request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono downloadTo( + String sourceEndpoint, + Path destinationPath, + ParallelDownloadOptions parallelDownloadOptions, + boolean overwrite) { + try { + return downloadToWithResponse(sourceEndpoint, destinationPath, parallelDownloadOptions, overwrite, null) + .then(); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Download the content located in {@code endpoint} into a file marked by {@code path}. + * This download will be done using parallel workers. + * @param sourceEndpoint - ACS URL where the content is located. + * @param destinationPath - File location. + * @param parallelDownloadOptions - an optional {@link ParallelDownloadOptions} object to modify how the parallel + * download will work. + * @param overwrite - True to overwrite the file if it exists. + * @return Response containing the http response information from the download. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> downloadToWithResponse( + String sourceEndpoint, + Path destinationPath, + ParallelDownloadOptions parallelDownloadOptions, + boolean overwrite) { + try { + return downloadToWithResponse(sourceEndpoint, destinationPath, parallelDownloadOptions, overwrite, null); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + Mono> downloadToWithResponse( + String sourceEndpoint, + OutputStream destinationStream, + HttpRange httpRange, + Context context) { + + return contentDownloader.downloadToStreamWithResponse(sourceEndpoint, destinationStream, httpRange, context); + } + + Mono> downloadToWithResponse( + String sourceEndpoint, + Path destinationPath, + ParallelDownloadOptions parallelDownloadOptions, + boolean overwrite, + Context context) { + Objects.requireNonNull(sourceEndpoint, "'sourceEndpoint' cannot be null"); + Objects.requireNonNull(destinationPath, "'destinationPath' cannot be null"); + + Set openOptions = new HashSet<>(); + + if (overwrite) { + openOptions.add(StandardOpenOption.CREATE); + } else { + openOptions.add(StandardOpenOption.CREATE_NEW); + } + openOptions.add(StandardOpenOption.WRITE); + + try { + AsynchronousFileChannel file = AsynchronousFileChannel.open(destinationPath, openOptions, null); + return downloadToWithResponse(sourceEndpoint, destinationPath, file, parallelDownloadOptions, context); + } catch (IOException ex) { + return monoError(logger, new RuntimeException(ex)); + } + } + + Mono> downloadToWithResponse( + String sourceEndpoint, + Path destinationPath, + AsynchronousFileChannel fileChannel, + ParallelDownloadOptions parallelDownloadOptions, + Context context + ) { + ParallelDownloadOptions finalParallelDownloadOptions = + parallelDownloadOptions == null + ? new ParallelDownloadOptions() + : parallelDownloadOptions; + + return Mono.just(fileChannel).flatMap( + c -> contentDownloader.downloadToFileWithResponse(sourceEndpoint, c, finalParallelDownloadOptions, context)) + .doFinally(signalType -> contentDownloader.downloadToFileCleanup(fileChannel, destinationPath, signalType)); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallingServerClient.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallingServerClient.java new file mode 100644 index 0000000000000..d4cc6795e6517 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallingServerClient.java @@ -0,0 +1,208 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.models.CreateCallOptions; +import com.azure.communication.callingserver.models.JoinCallOptions; +import com.azure.communication.callingserver.models.ParallelDownloadOptions; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.core.annotation.ReturnType; +import com.azure.core.annotation.ServiceClient; +import com.azure.core.annotation.ServiceMethod; +import com.azure.core.http.HttpRange; +import com.azure.core.http.rest.Response; +import com.azure.core.util.Context; + +import java.io.OutputStream; +import java.nio.file.Path; +import java.util.Objects; + + +/** + * A Sync Client that supports calling server operations. + * + *

Instantiating a synchronous Calling Server Client

+ * + * {@codesnippet com.azure.communication.callingserver.CallingServerClient.pipeline.instantiation} + * + *

View {@link CallingServerClientBuilder this} for additional ways to construct the client.

+ * + * @see CallingServerClientBuilder + */ +@ServiceClient(builder = CallingServerClientBuilder.class) +public final class CallingServerClient { + private final CallingServerAsyncClient callingServerAsyncClient; + + CallingServerClient(CallingServerAsyncClient callingServerAsyncClient) { + this.callingServerAsyncClient = callingServerAsyncClient; + } + + /** + * Create a call connection request from source identity to targets identity. + * + * @param source The source of the call. + * @param targets The targets of the call. + * @param createCallOptions The call Options. + * @return response for a successful CreateCallConnection request. + * + * {@codesnippet com.azure.communication.callingserver.CallingServerClient.create.call.connection} + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public CallConnection createCallConnection( + CommunicationIdentifier source, + CommunicationIdentifier[] targets, + CreateCallOptions createCallOptions) { + return callingServerAsyncClient.createCallConnectionInternal(source, targets, createCallOptions).block(); + } + + /** + * Create a Call Connection Request from source identity to targets identity. + * + * @param source The source of the call. + * @param targets The targets of the call. + * @param createCallOptions The call Options. + * @param context A {@link Context} representing the request context. + * @return response for a successful CreateCallConnection request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response createCallConnectionWithResponse( + CommunicationIdentifier source, + CommunicationIdentifier[] targets, + CreateCallOptions createCallOptions, + Context context) { + return callingServerAsyncClient + .createCallConnectionWithResponseInternal(source, targets, createCallOptions, context).block(); + } + + /** + * Join a call + * + * @param serverCallId The server call id. + * @param source of Join Call request. + * @param joinCallOptions to Join Call. + * @return CallConnection for a successful Join request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public CallConnection join( + String serverCallId, + CommunicationIdentifier source, + JoinCallOptions joinCallOptions) { + return callingServerAsyncClient.joinInternal(serverCallId, source, joinCallOptions).block(); + } + + /** + * Join a call + * + * @param serverCallId The server call id. + * @param source of Join Call request. + * @param joinCallOptions to Join Call. + * @param context A {@link Context} representing the request context. + * @return response for a successful Join request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response joinWithResponse( + String serverCallId, + CommunicationIdentifier source, + JoinCallOptions joinCallOptions, + Context context) { + return callingServerAsyncClient.joinWithResponseInternal(serverCallId, source, joinCallOptions, context).block(); + } + + /** + * Get CallConnection object + * + * @param callConnectionId The call connection id. + * @return CallConnection + */ + public CallConnection getCallConnection(String callConnectionId) { + return callingServerAsyncClient.getCallConnectionInternal(callConnectionId); + } + + /** + * Get ServerCall object + * + * @param serverCallId The server call id. + * @return ServerCall + */ + public ServerCall initializeServerCall(String serverCallId) { + return callingServerAsyncClient.initializeServerCallInternal(serverCallId); + } + + /** + * Download the recording content, e.g. Recording's metadata, Recording video, etc., from + * {@code endpoint} and write it into the {@link OutputStream} passed as parameter. + * @param sourceEndpoint - ACS URL where the content is located. + * @param destinationStream - A stream where to write the downloaded content. + * @param httpRange - An optional {@link HttpRange} value containing the range of bytes to download. If missing, + * the whole content will be downloaded. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public void downloadTo(String sourceEndpoint, OutputStream destinationStream, HttpRange httpRange) { + downloadToWithResponse(sourceEndpoint, destinationStream, httpRange, null); + } + + /** + * Download the recording content, e.g. Recording's metadata, Recording video, etc., from + * {@code endpoint} and write it in the {@link OutputStream} passed as parameter. + * @param sourceEndpoint - ACS URL where the content is located. + * @param destinationStream - A stream where to write the downloaded content. + * @param httpRange - An optional {@link HttpRange} value containing the range of bytes to download. If missing, + * the whole content will be downloaded. + * @param context A {@link Context} representing the request context. + * @return Response containing the http response information from the download. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response downloadToWithResponse(String sourceEndpoint, + OutputStream destinationStream, + HttpRange httpRange, + Context context) { + Objects.requireNonNull(sourceEndpoint, "'sourceEndpoint' cannot be null"); + Objects.requireNonNull(destinationStream, "'destinationStream' cannot be null"); + return callingServerAsyncClient + .downloadToWithResponse(sourceEndpoint, destinationStream, httpRange, context) + .block(); + } + + /** + * Download the content located in {@code endpoint} into a file marked by {@code path}. + * This download will be done using parallel workers. + * @param sourceEndpoint - ACS URL where the content is located. + * @param destinationPath - File location. + * @param parallelDownloadOptions - an optional {@link ParallelDownloadOptions} object to modify how the parallel + * download will work. + * @param overwrite - True to overwrite the file if it exists. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public void downloadTo(String sourceEndpoint, + Path destinationPath, + ParallelDownloadOptions parallelDownloadOptions, + boolean overwrite) { + downloadToWithResponse(sourceEndpoint, destinationPath, parallelDownloadOptions, overwrite, null); + } + + /** + * Download the content located in {@code endpoint} into a file marked by {@code path}. + * This download will be done using parallel workers. + * @param sourceEndpoint - ACS URL where the content is located. + * @param destinationPath - File location. + * @param parallelDownloadOptions - an optional {@link ParallelDownloadOptions} object to modify how the parallel + * download will work. + * @param overwrite - True to overwrite the file if it exists. + * @param context A {@link Context} representing the request context. + * @return Response containing the http response information from the download. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response downloadToWithResponse(String sourceEndpoint, + Path destinationPath, + ParallelDownloadOptions parallelDownloadOptions, + boolean overwrite, + Context context) { + Objects.requireNonNull(sourceEndpoint, "'sourceEndpoint' cannot be null"); + Objects.requireNonNull(destinationPath, "'destinationPath' cannot be null"); + return callingServerAsyncClient.downloadToWithResponse(sourceEndpoint, destinationPath, + parallelDownloadOptions, overwrite, context).block(); + } +} + + diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallingServerClientBuilder.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallingServerClientBuilder.java new file mode 100644 index 0000000000000..f7e0f9b929f52 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/CallingServerClientBuilder.java @@ -0,0 +1,346 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.implementation.AzureCommunicationCallingServerServiceImpl; +import com.azure.communication.callingserver.implementation.AzureCommunicationCallingServerServiceImplBuilder; +import com.azure.communication.common.implementation.CommunicationConnectionString; +import com.azure.communication.common.implementation.HmacAuthenticationPolicy; +import com.azure.core.annotation.ServiceClientBuilder; +import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.credential.TokenCredential; +import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpPipelineBuilder; +import com.azure.core.http.policy.BearerTokenAuthenticationPolicy; +import com.azure.core.http.policy.CookiePolicy; +import com.azure.core.http.policy.HttpLogOptions; +import com.azure.core.http.policy.HttpLoggingPolicy; +import com.azure.core.http.policy.HttpPipelinePolicy; +import com.azure.core.http.policy.RequestIdPolicy; +import com.azure.core.http.policy.RetryPolicy; +import com.azure.core.http.policy.UserAgentPolicy; +import com.azure.core.util.ClientOptions; +import com.azure.core.util.Configuration; +import com.azure.core.util.CoreUtils; +import com.azure.core.util.logging.ClientLogger; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +/** + * CallingServerClientBuilder that creates CallingServerAsyncClient and CallingServerClient. + * + *

Instantiating synchronous and asynchronous Calling Server Clients

+ * + * {@codesnippet com.azure.communication.callingserver.CallingServerClientBuilder.pipeline.instantiation} + */ +@ServiceClientBuilder(serviceClients = { CallingServerClient.class, CallingServerAsyncClient.class }) +public final class CallingServerClientBuilder { + private static final String SDK_NAME = "name"; + private static final String SDK_VERSION = "version"; + private static final String APP_CONFIG_PROPERTIES = "azure-communication-callingserver.properties"; + + private final ClientLogger logger = new ClientLogger(CallingServerClientBuilder.class); + private String connectionString; + private String endpoint; + private AzureKeyCredential azureKeyCredential; + private TokenCredential tokenCredential; + private HttpClient httpClient; + private HttpLogOptions httpLogOptions = new HttpLogOptions(); + private HttpPipeline pipeline; + private Configuration configuration; + private final Map properties = CoreUtils.getProperties(APP_CONFIG_PROPERTIES); + private final List customPolicies = new ArrayList<>(); + private ClientOptions clientOptions; + private RetryPolicy retryPolicy; + + /** + * Set endpoint of the service + * + * @param endpoint url of the service + * @return CallingServerClientBuilder + */ + public CallingServerClientBuilder endpoint(String endpoint) { + this.endpoint = Objects.requireNonNull(endpoint, "'endpoint' cannot be null."); + return this; + } + + /** + * Set endpoint of the service + * + * @param pipeline HttpPipeline to use, if a pipeline is not supplied, the + * credential and httpClient fields must be set + * @return CallingServerClientBuilder + */ + public CallingServerClientBuilder pipeline(HttpPipeline pipeline) { + this.pipeline = Objects.requireNonNull(pipeline, "'pipeline' cannot be null."); + return this; + } + + /** + * Sets the {@link TokenCredential} used to authenticate HTTP requests. + * + * @param tokenCredential {@link TokenCredential} used to authenticate HTTP + * requests. + * @return The updated {@link CallingServerClientBuilder} object. + * @throws NullPointerException If {@code tokenCredential} is null. + */ + public CallingServerClientBuilder credential(TokenCredential tokenCredential) { + this.tokenCredential = Objects.requireNonNull(tokenCredential, "'tokenCredential' cannot be null."); + return this; + } + + /** + * Sets the {@link AzureKeyCredential} used to authenticate HTTP requests. + * + * @param keyCredential The {@link AzureKeyCredential} used to authenticate HTTP + * requests. + * @return The updated {@link CallingServerClientBuilder} object. + * @throws NullPointerException If {@code keyCredential} is null. + */ + public CallingServerClientBuilder credential(AzureKeyCredential keyCredential) { + this.azureKeyCredential = Objects.requireNonNull(keyCredential, "'keyCredential' cannot be null."); + return this; + } + + /** + * Set connectionString to use + * + * @param connectionString connection string to set + * @return The updated {@link CallingServerClientBuilder} object. + */ + public CallingServerClientBuilder connectionString(String connectionString) { + Objects.requireNonNull(connectionString, "'connectionString' cannot be null."); + this.connectionString = connectionString; + return this; + } + + /** + * Sets the retry policy to use (using the RetryPolicy type). + * + * @param retryPolicy object to be applied + * @return The updated {@link CallingServerClientBuilder} object. + */ + public CallingServerClientBuilder retryPolicy(RetryPolicy retryPolicy) { + this.retryPolicy = Objects.requireNonNull(retryPolicy, "'retryPolicy' cannot be null."); + return this; + } + + /** + * Sets the configuration object used to retrieve environment configuration + * values during building of the client. + * + * @param configuration Configuration store used to retrieve environment + * configurations. + * @return The updated {@link CallingServerClientBuilder} object. + */ + public CallingServerClientBuilder configuration(Configuration configuration) { + this.configuration = Objects.requireNonNull(configuration, "'configuration' cannot be null."); + return this; + } + + /** + * Sets the {@link HttpLogOptions} for service requests. + * + * @param logOptions The logging configuration to use when sending and receiving + * HTTP requests/responses. + * @return The updated {@link CallingServerClientBuilder} object. + */ + public CallingServerClientBuilder httpLogOptions(HttpLogOptions logOptions) { + this.httpLogOptions = Objects.requireNonNull(logOptions, "'logOptions' cannot be null."); + return this; + } + + /** + * Sets the {@link CallingServerClientBuilder} that is used when making API requests. + *

+ * If a service version is not provided, the service version that will be used + * will be the latest known service version based on the version of the client + * library being used. If no service version is specified, updating to a newer + * version of the client library will have the result of potentially moving to a + * newer service version. + *

+ * Targeting a specific service version may also mean that the service will + * return an error for newer APIs. + * + * @param version {@link CallingServerClientBuilder} of the service to be used when + * making requests. + * @return the updated CallingServerClientBuilder object + */ + public CallingServerClientBuilder serviceVersion(CallingServerClientBuilder version) { + return this; + } + + /** + * Set httpClient to use + * + * @param httpClient httpClient to use, overridden by the pipeline field. + * @return The updated {@link CallingServerClientBuilder} object. + */ + public CallingServerClientBuilder httpClient(HttpClient httpClient) { + this.httpClient = Objects.requireNonNull(httpClient, "'httpClient' cannot be null."); + return this; + } + + /** + * Apply additional HttpPipelinePolicy + * + * @param customPolicy HttpPipelinePolicy object to be applied after + * AzureKeyCredentialPolicy, UserAgentPolicy, RetryPolicy, and CookiePolicy + * @return The updated {@link CallingServerClientBuilder} object. + */ + public CallingServerClientBuilder addPolicy(HttpPipelinePolicy customPolicy) { + this.customPolicies.add(Objects.requireNonNull(customPolicy, "'customPolicy' cannot be null.")); + return this; + } + + /** + * Create asynchronous client applying HMACAuthenticationPolicy, + * UserAgentPolicy, RetryPolicy, and CookiePolicy. Additional HttpPolicies + * specified by additionalPolicies will be applied after them + * + * @return The updated {@link CallingServerClientBuilder} object. + */ + public CallingServerAsyncClient buildAsyncClient() { + return new CallingServerAsyncClient(createServiceImpl()); + } + + /** + * Create synchronous client applying HmacAuthenticationPolicy, UserAgentPolicy, + * RetryPolicy, and CookiePolicy. Additional HttpPolicies specified by + * additionalPolicies will be applied after them + * + * @return The updated {@link CallingServerClientBuilder} object. + */ + public CallingServerClient buildClient() { + return new CallingServerClient(buildAsyncClient()); + } + + private AzureCommunicationCallingServerServiceImpl createServiceImpl() { + boolean isConnectionStringSet = connectionString != null && !connectionString.trim().isEmpty(); + boolean isEndpointSet = endpoint != null && !endpoint.trim().isEmpty(); + boolean isAzureKeyCredentialSet = azureKeyCredential != null; + boolean isTokenCredentialSet = tokenCredential != null; + + if (isConnectionStringSet && isEndpointSet) { + throw logger.logExceptionAsError(new IllegalArgumentException( + "Both 'connectionString' and 'endpoint' are set. Just one may be used.")); + } + + if (isConnectionStringSet && isAzureKeyCredentialSet) { + throw logger.logExceptionAsError(new IllegalArgumentException( + "Both 'connectionString' and 'keyCredential' are set. Just one may be used.")); + } + + if (isConnectionStringSet && isTokenCredentialSet) { + throw logger.logExceptionAsError(new IllegalArgumentException( + "Both 'connectionString' and 'tokenCredential' are set. Just one may be used.")); + } + + if (isAzureKeyCredentialSet && isTokenCredentialSet) { + throw logger.logExceptionAsError(new IllegalArgumentException( + "Both 'tokenCredential' and 'keyCredential' are set. Just one may be used.")); + } + + if (isConnectionStringSet) { + CommunicationConnectionString connectionStringObject = new CommunicationConnectionString(connectionString); + String endpoint = connectionStringObject.getEndpoint(); + String accessKey = connectionStringObject.getAccessKey(); + endpoint(endpoint).credential(new AzureKeyCredential(accessKey)); + } + + Objects.requireNonNull(endpoint); + if (pipeline == null) { + Objects.requireNonNull(httpClient); + } + + HttpPipeline builderPipeline = pipeline; + if (pipeline == null) { + builderPipeline = createHttpPipeline(httpClient); + } + + AzureCommunicationCallingServerServiceImplBuilder clientBuilder = new AzureCommunicationCallingServerServiceImplBuilder(); + clientBuilder.endpoint(endpoint).pipeline(builderPipeline); + + return clientBuilder.buildClient(); + } + + /** + * Allows the user to set a variety of client-related options, such as + * user-agent string, headers, etc. + * + * @param clientOptions object to be applied + * @return The updated {@link CallingServerClientBuilder} object. + */ + public CallingServerClientBuilder clientOptions(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + return this; + } + + private HttpPipelinePolicy createHttpPipelineAuthPolicy() { + if (tokenCredential != null && azureKeyCredential != null) { + throw logger.logExceptionAsError(new IllegalArgumentException( + "Both 'credential' and 'keyCredential' are set. Just one may be used.")); + } + if (tokenCredential != null) { + return new BearerTokenAuthenticationPolicy(tokenCredential, + "https://communication.azure.com//.default"); + } else if (azureKeyCredential != null) { + return new HmacAuthenticationPolicy(azureKeyCredential); + } else { + throw logger.logExceptionAsError( + new IllegalArgumentException("Missing credential information while building a client.")); + } + } + + private HttpPipeline createHttpPipeline(HttpClient httpClient) { + if (pipeline != null) { + return pipeline; + } + + List policyList = new ArrayList<>(); + + ClientOptions buildClientOptions = (clientOptions == null) ? new ClientOptions() : clientOptions; + HttpLogOptions buildLogOptions = (httpLogOptions == null) ? new HttpLogOptions() : httpLogOptions; + + String applicationId = null; + if (!CoreUtils.isNullOrEmpty(buildClientOptions.getApplicationId())) { + applicationId = buildClientOptions.getApplicationId(); + } else if (!CoreUtils.isNullOrEmpty(buildLogOptions.getApplicationId())) { + applicationId = buildLogOptions.getApplicationId(); + } + + // Add required policies + String clientName = properties.getOrDefault(SDK_NAME, "UnknownName"); + String clientVersion = properties.getOrDefault(SDK_VERSION, "UnknownVersion"); + policyList.add(new UserAgentPolicy(applicationId, clientName, clientVersion, configuration)); + policyList.add(new RequestIdPolicy()); + policyList.add((retryPolicy == null) ? new RetryPolicy() : retryPolicy); + policyList.add(createHttpPipelineAuthPolicy()); + policyList.add(new CookiePolicy()); + + // Add additional policies + if (!customPolicies.isEmpty()) { + policyList.addAll(customPolicies); + } + + // Add logging policy + policyList.add(new HttpLoggingPolicy(getHttpLogOptions())); + + return new HttpPipelineBuilder().policies(policyList.toArray(new HttpPipelinePolicy[0])).httpClient(httpClient) + .build(); + } + + private HttpLogOptions getHttpLogOptions() { + if (httpLogOptions == null) { + httpLogOptions = new HttpLogOptions(); + } + + return httpLogOptions; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/ContentDownloader.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/ContentDownloader.java new file mode 100644 index 0000000000000..6ee8317fe7f1c --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/ContentDownloader.java @@ -0,0 +1,286 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.implementation.Constants; +import com.azure.communication.callingserver.models.CallingServerErrorException; +import com.azure.communication.callingserver.models.ParallelDownloadOptions; +import com.azure.communication.callingserver.models.ProgressReporter; +import com.azure.core.http.HttpMethod; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpRange; +import com.azure.core.http.HttpRequest; +import com.azure.core.http.HttpResponse; +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.SimpleResponse; +import com.azure.core.util.Context; +import com.azure.core.util.FluxUtil; +import com.azure.core.util.logging.ClientLogger; +import reactor.core.Exceptions; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.SignalType; +import reactor.core.scheduler.Schedulers; +import reactor.util.function.Tuple2; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousFileChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Function; + +import static java.lang.StrictMath.toIntExact; + +class ContentDownloader { + private final String resourceEndpoint; + private final HttpPipeline httpPipeline; + private final ClientLogger logger = new ClientLogger(ContentDownloader.class); + + ContentDownloader(String resourceEndpoint, HttpPipeline httpPipeline) { + this.resourceEndpoint = resourceEndpoint; + this.httpPipeline = httpPipeline; + } + + Mono> downloadToStreamWithResponse( + String sourceEndpoint, + OutputStream destinationStream, + HttpRange httpRange, + Context context) { + return downloadStreamWithResponse(sourceEndpoint, httpRange, context) + .flatMap(response -> response.getValue().reduce(destinationStream, (outputStream, buffer) -> { + try { + outputStream.write(FluxUtil.byteBufferToArray(buffer)); + return outputStream; + } catch (IOException ex) { + throw logger.logExceptionAsError(Exceptions.propagate(new UncheckedIOException(ex))); + } + }).thenReturn(new SimpleResponse<>(response.getRequest(), response.getStatusCode(), + response.getHeaders(), null))); + } + + Mono>> downloadStreamWithResponse( + String sourceEndpoint, + HttpRange httpRange, + Context context) { + Mono httpResponse = makeDownloadRequest(sourceEndpoint, httpRange, context); + return httpResponse.map(response -> { + Flux result = getFluxStream(response, sourceEndpoint, httpRange, context); + return new SimpleResponse<>(response.getRequest(), response.getStatusCode(), + response.getHeaders(), result); + }); + } + + Mono> downloadToFileWithResponse( + String sourceEndpoint, + AsynchronousFileChannel destinationFile, + ParallelDownloadOptions parallelDownloadOptions, + Context context) { + Lock progressLock = new ReentrantLock(); + AtomicLong totalProgress = new AtomicLong(0); + + Function>>> downloadFunc = + range -> downloadStreamWithResponse(sourceEndpoint, range, context); + + return downloadFirstChunk(parallelDownloadOptions, downloadFunc) + .flatMap(setupTuple2 -> { + long newCount = setupTuple2.getT1(); + int numChunks = calculateNumBlocks(newCount, parallelDownloadOptions.getBlockSizeLong()); + + // In case it is an empty blob, this ensures we still actually perform a download operation. + numChunks = numChunks == 0 ? 1 : numChunks; + + Response> initialResponse = setupTuple2.getT2(); + return Flux.range(0, numChunks) + .flatMap(chunkNum -> downloadChunk(chunkNum, initialResponse, + parallelDownloadOptions, newCount, downloadFunc, + response -> + writeBodyToFile(response, destinationFile, chunkNum, + parallelDownloadOptions, progressLock, totalProgress).flux())) + .then(Mono.just(new SimpleResponse<>(initialResponse, null))); + }); + } + + private Flux getFluxStream( + HttpResponse httpResponse, + String sourceEndpoint, + HttpRange httpRange, + Context context) { + return FluxUtil.createRetriableDownloadFlux( + () -> getResponseBody(httpResponse), + (Throwable throwable, Long aLong) -> { + if (throwable instanceof CallingServerErrorException) { + CallingServerErrorException exception = (CallingServerErrorException) throwable; + if (exception.getResponse().getStatusCode() == 416) { + return makeDownloadRequest(sourceEndpoint, null, context) + .map(this::getResponseBody) + .flux() + .flatMap(flux -> flux); + } + } + + HttpRange range; + if (httpRange != null) { + range = new HttpRange(aLong + 1, httpRange.getLength() - aLong - 1); + } else { + range = new HttpRange(aLong + 1); + } + + return makeDownloadRequest(sourceEndpoint, range, context) + .map(this::getResponseBody) + .flux() + .flatMap(flux -> flux); + }, + Constants.ContentDownloader.MAX_RETRIES + ); + } + + private Flux getResponseBody(HttpResponse response) { + switch (response.getStatusCode()) { + case 200: + case 206: + return response.getBody(); + case 416: // Retriable with new HttpRange, potentially bytes=0- + return FluxUtil.fluxError(logger, + new CallingServerErrorException(formatExceptionMessage(response), response)); + default: + throw logger.logExceptionAsError( + new CallingServerErrorException(formatExceptionMessage(response), response) + ); + } + } + + private String formatExceptionMessage(HttpResponse httpResponse) { + return String.format("Service Request failed!%nStatus: %s", httpResponse.getStatusCode()); + } + + private Mono makeDownloadRequest( + String sourceEndpoint, + HttpRange httpRange, + Context context) { + HttpRequest request = getHttpRequest(sourceEndpoint, httpRange); + URL urlToSignWith = getUrlToSignRequestWith(sourceEndpoint); + + Context finalContext; + if (context == null) { + finalContext = new Context("hmacSignatureURL", urlToSignWith); + } else { + finalContext = context.addData("hmacSignatureURL", urlToSignWith); + } + + return httpPipeline.send(request, finalContext); + } + + private URL getUrlToSignRequestWith(String endpoint) { + try { + String path = new URL(endpoint).getPath(); + + if (path.startsWith("/")) { + path = path.substring(1); + } + + return new URL(resourceEndpoint + path); + } catch (MalformedURLException ex) { + throw logger.logExceptionAsError(new IllegalArgumentException(ex)); + } + } + + private HttpRequest getHttpRequest(String sourceEndpoint, HttpRange httpRange) { + HttpRequest request = new HttpRequest(HttpMethod.GET, sourceEndpoint); + + if (null != httpRange) { + request.setHeader(Constants.HeaderNames.RANGE, httpRange.toString()); + } + + return request; + } + + private Mono>>> downloadFirstChunk( + ParallelDownloadOptions parallelDownloadOptions, + Function>>> downloader) { + return downloader.apply(new HttpRange(0, parallelDownloadOptions.getBlockSizeLong())) + .subscribeOn(Schedulers.boundedElastic()) + .flatMap(response -> { + // Extract the total length of the blob from the contentRange header. e.g. "bytes 1-6/7" + long totalLength = extractTotalBlobLength( + response.getHeaders().getValue(Constants.HeaderNames.CONTENT_RANGE) + ); + + return Mono.zip(Mono.just(totalLength), Mono.just(response)); + }); + } + + private long extractTotalBlobLength(String contentRange) { + return contentRange == null ? 0 : Long.parseLong(contentRange.split("/")[1]); + } + + private int calculateNumBlocks(long dataSize, long blockLength) { + // Can successfully cast to an int because MaxBlockSize is an int, which this expression must be less than. + int numBlocks = toIntExact(dataSize / blockLength); + // Include an extra block for trailing data. + if (dataSize % blockLength != 0) { + numBlocks++; + } + return numBlocks; + } + + private Flux downloadChunk( + Integer chunkNum, + Response> initialResponse, + ParallelDownloadOptions parallelDownloadOptions, + long newCount, + Function>>> downloader, + Function>, Flux> returnTransformer) { + if (chunkNum == 0) { + return returnTransformer.apply(initialResponse); + } + + // Calculate whether we need a full chunk or something smaller because we are at the end. + long modifier = chunkNum.longValue() * parallelDownloadOptions.getBlockSizeLong(); + long chunkSizeActual = Math.min(parallelDownloadOptions.getBlockSizeLong(), + newCount - modifier); + HttpRange chunkRange = new HttpRange(modifier, chunkSizeActual); + + // Make the download call. + return downloader.apply(chunkRange) + .subscribeOn(Schedulers.boundedElastic()) + .flatMapMany(returnTransformer); + } + + private static Mono writeBodyToFile( + Response> response, + AsynchronousFileChannel file, + long chunkNum, + ParallelDownloadOptions parallelDownloadOptions, + Lock progressLock, + AtomicLong totalProgress) { + // Extract the body. + Flux data = response.getValue(); + + // Report progress as necessary. + data = ProgressReporter.addParallelProgressReporting(data, + parallelDownloadOptions.getProgressReceiver(), progressLock, totalProgress); + + // Write to the file. + return FluxUtil.writeFile(data, file, chunkNum * parallelDownloadOptions.getBlockSizeLong()); + } + + void downloadToFileCleanup(AsynchronousFileChannel channel, Path filePath, SignalType signalType) { + try { + channel.close(); + if (!signalType.equals(SignalType.ON_COMPLETE)) { + Files.deleteIfExists(filePath); + logger.verbose("Downloading to file failed. Cleaning up resources."); + } + } catch (IOException e) { + throw logger.logExceptionAsError(new UncheckedIOException(e)); + } + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/ServerCall.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/ServerCall.java new file mode 100644 index 0000000000000..653db215187f7 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/ServerCall.java @@ -0,0 +1,301 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.models.CallRecordingStateResult; +import com.azure.communication.callingserver.models.PlayAudioOptions; +import com.azure.communication.callingserver.models.PlayAudioResult; +import com.azure.communication.callingserver.models.StartCallRecordingResult; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.core.annotation.ReturnType; +import com.azure.core.annotation.ServiceMethod; +import com.azure.core.http.rest.Response; +import com.azure.core.util.Context; + +/** + * Sync Client that supports server call operations. + */ +public final class ServerCall { + private final ServerCallAsync serverCallAsync; + + ServerCall(ServerCallAsync serverCallAsync) { + this.serverCallAsync = serverCallAsync; + } + + /** + * Get the server call id property + * + * @return the id value. + */ + public String getServerCallId() { + return serverCallAsync.getServerCallId(); + } + + /** + * Add a participant to the call. + * + * @param participant Invited participant. + * @param callBackUri callBackUri to get notifications. + * @param alternateCallerId The phone number to use when adding a phone number participant. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return response for a successful add participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Void addParticipant( + CommunicationIdentifier participant, + String callBackUri, + String alternateCallerId, + String operationContext) { + return serverCallAsync.addParticipant(participant, alternateCallerId, operationContext, callBackUri).block(); + } + + /** + * Add a participant to the call. + * + * @param participant Invited participant. + * @param callBackUri callBackUri to get notifications. + * @param alternateCallerId The phone number to use when adding a phone number participant. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @param context A {@link Context} representing the request context. + * @return response for a successful add participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response addParticipantWithResponse( + CommunicationIdentifier participant, + String callBackUri, + String alternateCallerId, + String operationContext, + Context context) { + return serverCallAsync.addParticipantWithResponse( + participant, + callBackUri, + alternateCallerId, + operationContext, + context).block(); + } + + /** + * Remove a participant from the call. + * + * @param participantId Participant id. + * @return response for a successful remove participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Void removeParticipant(String participantId) { + return serverCallAsync.removeParticipant(participantId).block(); + } + + /** + * Remove a participant from the call. + * + * @param participantId Participant id. + * @param context A {@link Context} representing the request context. + * @return response for a successful remove participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response removeParticipantWithResponse(String participantId, Context context) { + return serverCallAsync.removeParticipantWithResponse(participantId, context).block(); + } + + /** + * Start recording + * + * @param recordingStateCallbackUri The uri to send state change callbacks. + * @return result for a successful start recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public StartCallRecordingResult startRecording(String recordingStateCallbackUri) { + return serverCallAsync.startRecording(recordingStateCallbackUri).block(); + } + + /** + * Start recording + * + * @param recordingStateCallbackUri The uri to send state change callbacks. + * @param context A {@link Context} representing the request context. + * @return result for a successful start recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response startRecordingWithResponse( + String recordingStateCallbackUri, + Context context) { + return serverCallAsync.startRecordingWithResponse(recordingStateCallbackUri, context).block(); + } + + /** + * Stop recording + * + * @param recordingId The recording id to stop. + * @return response for a successful stop recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Void stopRecording(String recordingId) { + return serverCallAsync.stopRecording(recordingId).block(); + } + + /** + * Stop recording + * + * @param recordingId The recording id to stop. + * @param context A {@link Context} representing the request context. + * @return response for a successful stop recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response stopRecordingWithResponse(String recordingId, Context context) { + return serverCallAsync.stopRecordingWithResponse(recordingId, context).block(); + } + + /** + * Pause recording + * + * @param recordingId The recording id to stop. + * @return response for a successful pause recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Void pauseRecording(String recordingId) { + return serverCallAsync.pauseRecording(recordingId).block(); + } + + /** + * Pause recording + * + * @param recordingId The recording id to stop. + * @param context A {@link Context} representing the request context. + * @return response for a successful pause recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response pauseRecordingWithResponse(String recordingId, Context context) { + return serverCallAsync.pauseRecordingWithResponse(recordingId, context).block(); + } + + /** + * Resume recording + * + * @param recordingId The recording id to stop. + * @return response for a successful resume recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Void resumeRecording(String recordingId) { + return serverCallAsync.resumeRecording(recordingId).block(); + } + + /** + * Resume recording + * + * @param recordingId The recording id to stop. + * @param context A {@link Context} representing the request context. + * @return response for a successful resume recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response resumeRecordingWithResponse(String recordingId, Context context) { + return serverCallAsync.resumeRecordingWithResponse(recordingId, context).block(); + } + + /** + * Get recording state + * + * @param recordingId The recording id to stop. + * @return response for a successful get recording state request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public CallRecordingStateResult getRecordingState(String recordingId) { + return serverCallAsync.getRecordingState(recordingId).block(); + } + + /** + * Get recording state + * + * @param recordingId The recording id to stop. + * @param context A {@link Context} representing the request context. + * @return response for a successful get recording state request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response getRecordingStateWithResponse(String recordingId, Context context) { + return serverCallAsync.getRecordingStateWithResponse(recordingId, context).block(); + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param audioFileId An id for the media in the AudioFileUri, using which we cache the media. + * @param callbackUri The callback Uri to receive PlayAudio status notifications. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public PlayAudioResult playAudio( + String audioFileUri, + String audioFileId, + String callbackUri, + String operationContext) { + return serverCallAsync.playAudioInternal(audioFileUri, audioFileId, callbackUri, operationContext).block(); + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param playAudioOptions Options for play audio. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public PlayAudioResult playAudio(String audioFileUri, PlayAudioOptions playAudioOptions) { + return serverCallAsync.playAudioInternal(audioFileUri, playAudioOptions).block(); + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param audioFileId An id for the media in the AudioFileUri, using which we cache the media. + * @param callbackUri The callback Uri to receive PlayAudio status notifications. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @param context A {@link Context} representing the request context. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response playAudioWithResponse( + String audioFileUri, + String audioFileId, + String callbackUri, + String operationContext, + Context context) { + return serverCallAsync + .playAudioWithResponseInternal( + audioFileUri, + audioFileId, + callbackUri, + operationContext, + context).block(); + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param playAudioOptions Options for play audio. + * @param context A {@link Context} representing the request context. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response playAudioWithResponse( + String audioFileUri, + PlayAudioOptions playAudioOptions, + Context context) { + return serverCallAsync.playAudioWithResponseInternal(audioFileUri, playAudioOptions, context).block(); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/ServerCallAsync.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/ServerCallAsync.java new file mode 100644 index 0000000000000..067bec9db3baa --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/ServerCallAsync.java @@ -0,0 +1,586 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.implementation.ServerCallsImpl; +import com.azure.communication.callingserver.implementation.converters.CallingServerErrorConverter; +import com.azure.communication.callingserver.implementation.converters.InviteParticipantRequestConverter; +import com.azure.communication.callingserver.implementation.converters.PlayAudioResultConverter; +import com.azure.communication.callingserver.implementation.models.CommunicationErrorException; +import com.azure.communication.callingserver.implementation.models.InviteParticipantsRequest; +import com.azure.communication.callingserver.implementation.models.PlayAudioRequest; +import com.azure.communication.callingserver.implementation.models.StartCallRecordingRequest; +import com.azure.communication.callingserver.models.CallRecordingStateResult; +import com.azure.communication.callingserver.models.PlayAudioOptions; +import com.azure.communication.callingserver.models.PlayAudioResult; +import com.azure.communication.callingserver.models.StartCallRecordingResult; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.core.annotation.ReturnType; +import com.azure.core.annotation.ServiceMethod; +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.SimpleResponse; +import com.azure.core.util.Context; +import com.azure.core.util.logging.ClientLogger; +import reactor.core.publisher.Mono; + +import java.net.URI; +import java.net.URISyntaxException; +import java.security.InvalidParameterException; +import java.util.Objects; + +import static com.azure.core.util.FluxUtil.monoError; +import static com.azure.core.util.FluxUtil.withContext; + +/** + * Async client that supports server call operations. + */ +public final class ServerCallAsync { + private final String serverCallId; + private final ServerCallsImpl serverCallInternal; + private final ClientLogger logger = new ClientLogger(ServerCallAsync.class); + + ServerCallAsync(String serverCallId, ServerCallsImpl serverCallInternal) { + this.serverCallId = serverCallId; + this.serverCallInternal = serverCallInternal; + } + + /** + * Get the server call id property + * + * @return the id value. + */ + public String getServerCallId() { + return serverCallId; + } + + /** + * Add a participant to the call. + * + * @param participant Invited participant. + * @param callBackUri callBackUri to get notifications. + * @param alternateCallerId The phone number to use when adding a phone number participant. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return response for a successful add participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono addParticipant( + CommunicationIdentifier participant, + String callBackUri, + String alternateCallerId, + String operationContext) { + try { + Objects.requireNonNull(participant, "'participant' cannot be null."); + InviteParticipantsRequest request = + InviteParticipantRequestConverter.convert(participant, + alternateCallerId, + operationContext, + callBackUri); + return serverCallInternal.inviteParticipantsAsync(serverCallId, request) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Add a participant to the call. + * + * @param participant Invited participant. + * @param callBackUri callBackUri to get notifications. + * @param alternateCallerId The phone number to use when adding a phone number participant. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return response for a successful add participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> addParticipantWithResponse( + CommunicationIdentifier participant, + String callBackUri, + String alternateCallerId, + String operationContext) { + return addParticipantWithResponse(participant, + callBackUri, + alternateCallerId, + operationContext, + null); + } + + Mono> addParticipantWithResponse( + CommunicationIdentifier participant, + String callBackUri, + String alternateCallerId, + String operationContext, + Context context) { + try { + Objects.requireNonNull(participant, "'participant' cannot be null."); + InviteParticipantsRequest request = + InviteParticipantRequestConverter.convert(participant, + alternateCallerId, + operationContext, + callBackUri); + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return serverCallInternal + .inviteParticipantsWithResponseAsync(serverCallId, request, contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + }); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Remove a participant from the call. + * + * @param participantId Participant id. + * @return response for a successful remove participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono removeParticipant(String participantId) { + try { + return serverCallInternal.removeParticipantAsync(serverCallId, participantId) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Remove a participant from the call. + * + * @param participantId Participant id. + * @return response for a successful remove participant request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> removeParticipantWithResponse(String participantId) { + return removeParticipantWithResponse(participantId, null); + } + + Mono> removeParticipantWithResponse(String participantId, Context context) { + try { + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return serverCallInternal + .removeParticipantWithResponseAsync(serverCallId, participantId, contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + }); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Start recording + * + * @param recordingStateCallbackUri The uri to send state change callbacks. + * @throws InvalidParameterException is recordingStateCallbackUri is absolute uri. + * @return response for a successful start recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono startRecording(String recordingStateCallbackUri) { + try { + Objects.requireNonNull(recordingStateCallbackUri, "'recordingStateCallbackUri' cannot be null."); + if (!Boolean.TRUE.equals(new URI(recordingStateCallbackUri).isAbsolute())) { + throw logger.logExceptionAsError(new InvalidParameterException("'recordingStateCallbackUri' has to be an absolute Uri")); + } + StartCallRecordingRequest request = new StartCallRecordingRequest(); + request.setRecordingStateCallbackUri(recordingStateCallbackUri); + return serverCallInternal.startRecordingAsync(serverCallId, request) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .flatMap(result -> Mono.just(new StartCallRecordingResult(result.getRecordingId()))); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } catch (URISyntaxException ex) { + return monoError(logger, new RuntimeException(ex.getMessage())); + } + } + + /** + * Start recording + * + * @param recordingStateCallbackUri The uri to send state change callbacks. + * @throws InvalidParameterException is recordingStateCallbackUri is absolute uri. + * @return response for a successful start recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> startRecordingWithResponse(String recordingStateCallbackUri) { + return startRecordingWithResponse(recordingStateCallbackUri, null); + } + + Mono> startRecordingWithResponse( + String recordingStateCallbackUri, + Context context) { + try { + Objects.requireNonNull(recordingStateCallbackUri, "'recordingStateCallbackUri' cannot be null."); + if (!Boolean.TRUE.equals(new URI(recordingStateCallbackUri).isAbsolute())) { + throw logger.logExceptionAsError(new InvalidParameterException("'recordingStateCallbackUri' has to be an absolute Uri")); + } + StartCallRecordingRequest request = new StartCallRecordingRequest(); + request.setRecordingStateCallbackUri(recordingStateCallbackUri); + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return serverCallInternal + .startRecordingWithResponseAsync(serverCallId, request, contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .map(response -> + new SimpleResponse<>(response, new StartCallRecordingResult(response.getValue().getRecordingId()))); + }); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } catch (URISyntaxException ex) { + return monoError(logger, new RuntimeException(ex.getMessage())); + } + } + + /** + * Stop recording + * + * @param recordingId The recording id to stop. + * @return response for a successful stop recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono stopRecording(String recordingId) { + try { + return serverCallInternal.stopRecordingAsync(serverCallId, recordingId) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Stop recording + * + * @param recordingId The recording id to stop. + * @return response for a successful stop recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> stopRecordingWithResponse(String recordingId) { + return stopRecordingWithResponse(recordingId, null); + } + + Mono> stopRecordingWithResponse(String recordingId, Context context) { + try { + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return serverCallInternal + .stopRecordingWithResponseAsync(serverCallId, recordingId, contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + }); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Pause recording + * + * @param recordingId The recording id to stop. + * @return response for a successful pause recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono pauseRecording(String recordingId) { + try { + return serverCallInternal.pauseRecordingAsync(serverCallId, recordingId) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Pause recording + * + * @param recordingId The recording id to stop. + * @return response for a successful pause recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> pauseRecordingWithResponse(String recordingId) { + return pauseRecordingWithResponse(recordingId, null); + } + + Mono> pauseRecordingWithResponse(String recordingId, Context context) { + try { + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return serverCallInternal + .pauseRecordingWithResponseAsync(serverCallId, recordingId, contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + }); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Resume recording + * + * @param recordingId The recording id to stop. + * @return response for a successful resume recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono resumeRecording(String recordingId) { + try { + return serverCallInternal.resumeRecordingAsync(serverCallId, recordingId) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Resume recording + * + * @param recordingId The recording id to stop. + * @return response for a successful resume recording request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> resumeRecordingWithResponse(String recordingId) { + return resumeRecordingWithResponse(recordingId, null); + } + + Mono> resumeRecordingWithResponse(String recordingId, Context context) { + try { + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return serverCallInternal + .resumeRecordingWithResponseAsync(serverCallId, recordingId, contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException); + }); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Get recording state + * + * @param recordingId The recording id to stop. + * @return response for a successful get recording state request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono getRecordingState(String recordingId) { + try { + return serverCallInternal.recordingStateAsync(serverCallId, recordingId) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .flatMap(result -> Mono.just(new CallRecordingStateResult(result.getRecordingState()))); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Get recording state + * + * @param recordingId The recording id to stop. + * @return response for a successful get recording state request. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getRecordingStateWithResponse(String recordingId) { + return getRecordingStateWithResponse(recordingId, null); + } + + Mono> getRecordingStateWithResponse(String recordingId, Context context) { + try { + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return serverCallInternal + .recordingStateWithResponseAsync(serverCallId, recordingId, contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .map(response -> + new SimpleResponse<>(response, new CallRecordingStateResult(response.getValue().getRecordingState()))); + }); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param audioFileId Tne id for the media in the AudioFileUri, using which we cache the media resource. + * @param callbackUri The callback Uri to receive PlayAudio status notifications. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono playAudio( + String audioFileUri, + String audioFileId, + String callbackUri, + String operationContext) { + return playAudioInternal(audioFileUri, audioFileId, callbackUri, operationContext); + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param playAudioOptions Options for play audio. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono playAudio(String audioFileUri, PlayAudioOptions playAudioOptions) { + return playAudioInternal(audioFileUri, playAudioOptions); + } + + Mono playAudioInternal( + String audioFileUri, + String audioFileId, + String callbackUri, + String operationContext) { + try { + Objects.requireNonNull(audioFileUri, "'audioFileUri' cannot be null."); + + //Currently we do not support loop on the audio media for out-call, thus setting the loop to false + PlayAudioRequest playAudioRequest = + new PlayAudioRequest() + .setAudioFileUri(audioFileUri) + .setLoop(false) + .setAudioFileId(audioFileId) + .setOperationContext(operationContext) + .setCallbackUri(callbackUri); + return playAudioInternal(playAudioRequest); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + Mono playAudioInternal(String audioFileUri, PlayAudioOptions playAudioOptions) { + try { + Objects.requireNonNull(audioFileUri, "'audioFileUri' cannot be null."); + + //Currently we do not support loop on the audio media for out-call, thus setting the loop to false + PlayAudioRequest request = new PlayAudioRequest().setAudioFileUri(audioFileUri); + if (playAudioOptions != null) { + request + .setLoop(false) + .setOperationContext(playAudioOptions.getOperationContext()) + .setAudioFileId(playAudioOptions.getAudioFileId()) + .setCallbackUri(playAudioOptions.getCallbackUri()); + } + return playAudioInternal(request); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + + } + + Mono playAudioInternal(PlayAudioRequest playAudioRequest) { + try { + return serverCallInternal.playAudioAsync(serverCallId, playAudioRequest) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .flatMap(result -> Mono.just(PlayAudioResultConverter.convert(result))); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param audioFileId Tne id for the media in the AudioFileUri, using which we cache the media resource. + * @param callbackUri The callback Uri to receive PlayAudio status notifications. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> playAudioWithResponse( + String audioFileUri, + String audioFileId, + String callbackUri, + String operationContext) { + return playAudioWithResponseInternal(audioFileUri, audioFileId, callbackUri, operationContext, null); + } + + /** + * Play audio in a call. + * + * @param audioFileUri The media resource uri of the play audio request. Currently only Wave file (.wav) format + * audio prompts are supported. More specifically, the audio content in the wave file must + * be mono (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * @param playAudioOptions Options for play audio. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> playAudioWithResponse( + String audioFileUri, + PlayAudioOptions playAudioOptions) { + return playAudioWithResponseInternal(audioFileUri, playAudioOptions, null); + } + + Mono> playAudioWithResponseInternal( + String audioFileUri, + String audioFileId, + String callbackUri, + String operationContext, + Context context) { + try { + Objects.requireNonNull(audioFileUri, "'audioFileUri' cannot be null."); + + //Currently we do not support loop on the audio media for out-call, thus setting the loop to false + PlayAudioRequest playAudioRequest = + new PlayAudioRequest() + .setAudioFileUri(audioFileUri) + .setLoop(false) + .setAudioFileId(audioFileId) + .setOperationContext(operationContext) + .setCallbackUri(callbackUri); + return playAudioWithResponse(playAudioRequest, context); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + + + } + + Mono> playAudioWithResponseInternal( + String audioFileUri, + PlayAudioOptions playAudioOptions, + Context context) { + try { + Objects.requireNonNull(audioFileUri, "'audioFileUri' cannot be null."); + + //Currently we do not support loop on the audio media for out-call, thus setting the loop to false + PlayAudioRequest request = new PlayAudioRequest().setAudioFileUri(audioFileUri); + if (playAudioOptions != null) { + request + .setLoop(false) + .setOperationContext(playAudioOptions.getOperationContext()) + .setAudioFileId(playAudioOptions.getAudioFileId()) + .setCallbackUri(playAudioOptions.getCallbackUri()); + } + return playAudioWithResponse(request, context); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } + + Mono> playAudioWithResponse(PlayAudioRequest playAudioRequest, Context context) { + try { + return withContext(contextValue -> { + contextValue = context == null ? contextValue : context; + return serverCallInternal + .playAudioWithResponseAsync(serverCallId, playAudioRequest, contextValue) + .onErrorMap(CommunicationErrorException.class, CallingServerErrorConverter::translateException) + .map(response -> + new SimpleResponse<>(response, PlayAudioResultConverter.convert(response.getValue()))); + }); + } catch (RuntimeException ex) { + return monoError(logger, ex); + } + } +} + diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/AzureCommunicationCallingServerServiceImpl.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/AzureCommunicationCallingServerServiceImpl.java new file mode 100644 index 0000000000000..8e504c2dabb94 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/AzureCommunicationCallingServerServiceImpl.java @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation; + +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpPipelineBuilder; +import com.azure.core.http.policy.CookiePolicy; +import com.azure.core.http.policy.RetryPolicy; +import com.azure.core.http.policy.UserAgentPolicy; +import com.azure.core.util.serializer.JacksonAdapter; +import com.azure.core.util.serializer.SerializerAdapter; + +/** Initializes a new instance of the AzureCommunicationCallingServerService type. */ +public final class AzureCommunicationCallingServerServiceImpl { + /** The endpoint of the Azure Communication resource. */ + private final String endpoint; + + /** + * Gets The endpoint of the Azure Communication resource. + * + * @return the endpoint value. + */ + public String getEndpoint() { + return this.endpoint; + } + + /** Api Version. */ + private final String apiVersion; + + /** + * Gets Api Version. + * + * @return the apiVersion value. + */ + public String getApiVersion() { + return this.apiVersion; + } + + /** The HTTP pipeline to send requests through. */ + private final HttpPipeline httpPipeline; + + /** + * Gets The HTTP pipeline to send requests through. + * + * @return the httpPipeline value. + */ + public HttpPipeline getHttpPipeline() { + return this.httpPipeline; + } + + /** The serializer to serialize an object into a string. */ + private final SerializerAdapter serializerAdapter; + + /** + * Gets The serializer to serialize an object into a string. + * + * @return the serializerAdapter value. + */ + public SerializerAdapter getSerializerAdapter() { + return this.serializerAdapter; + } + + /** The CallConnectionsImpl object to access its operations. */ + private final CallConnectionsImpl callConnections; + + /** + * Gets the CallConnectionsImpl object to access its operations. + * + * @return the CallConnectionsImpl object. + */ + public CallConnectionsImpl getCallConnections() { + return this.callConnections; + } + + /** The ServerCallsImpl object to access its operations. */ + private final ServerCallsImpl serverCalls; + + /** + * Gets the ServerCallsImpl object to access its operations. + * + * @return the ServerCallsImpl object. + */ + public ServerCallsImpl getServerCalls() { + return this.serverCalls; + } + + /** + * Initializes an instance of AzureCommunicationCallingServerService client. + * + * @param endpoint The endpoint of the Azure Communication resource. + * @param apiVersion Api Version. + */ + AzureCommunicationCallingServerServiceImpl(String endpoint, String apiVersion) { + this( + new HttpPipelineBuilder() + .policies(new UserAgentPolicy(), new RetryPolicy(), new CookiePolicy()) + .build(), + JacksonAdapter.createDefaultSerializerAdapter(), + endpoint, + apiVersion); + } + + /** + * Initializes an instance of AzureCommunicationCallingServerService client. + * + * @param httpPipeline The HTTP pipeline to send requests through. + * @param endpoint The endpoint of the Azure Communication resource. + * @param apiVersion Api Version. + */ + AzureCommunicationCallingServerServiceImpl(HttpPipeline httpPipeline, String endpoint, String apiVersion) { + this(httpPipeline, JacksonAdapter.createDefaultSerializerAdapter(), endpoint, apiVersion); + } + + /** + * Initializes an instance of AzureCommunicationCallingServerService client. + * + * @param httpPipeline The HTTP pipeline to send requests through. + * @param serializerAdapter The serializer to serialize an object into a string. + * @param endpoint The endpoint of the Azure Communication resource. + * @param apiVersion Api Version. + */ + AzureCommunicationCallingServerServiceImpl( + HttpPipeline httpPipeline, SerializerAdapter serializerAdapter, String endpoint, String apiVersion) { + this.httpPipeline = httpPipeline; + this.serializerAdapter = serializerAdapter; + this.endpoint = endpoint; + this.apiVersion = apiVersion; + this.callConnections = new CallConnectionsImpl(this); + this.serverCalls = new ServerCallsImpl(this); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/AzureCommunicationCallingServerServiceImplBuilder.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/AzureCommunicationCallingServerServiceImplBuilder.java new file mode 100644 index 0000000000000..0af415c24b57a --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/AzureCommunicationCallingServerServiceImplBuilder.java @@ -0,0 +1,230 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation; + +import com.azure.core.annotation.ServiceClientBuilder; +import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpPipelineBuilder; +import com.azure.core.http.policy.CookiePolicy; +import com.azure.core.http.policy.HttpLogOptions; +import com.azure.core.http.policy.HttpLoggingPolicy; +import com.azure.core.http.policy.HttpPipelinePolicy; +import com.azure.core.http.policy.HttpPolicyProviders; +import com.azure.core.http.policy.RetryPolicy; +import com.azure.core.http.policy.UserAgentPolicy; +import com.azure.core.util.Configuration; +import com.azure.core.util.serializer.JacksonAdapter; +import com.azure.core.util.serializer.SerializerAdapter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** A builder for creating a new instance of the AzureCommunicationCallingServerService type. */ +@ServiceClientBuilder(serviceClients = {AzureCommunicationCallingServerServiceImpl.class}) +public final class AzureCommunicationCallingServerServiceImplBuilder { + private static final String SDK_NAME = "name"; + + private static final String SDK_VERSION = "version"; + + private final Map properties = new HashMap<>(); + + /** Create an instance of the AzureCommunicationCallingServerServiceImplBuilder. */ + public AzureCommunicationCallingServerServiceImplBuilder() { + this.pipelinePolicies = new ArrayList<>(); + } + + /* + * The endpoint of the Azure Communication resource. + */ + private String endpoint; + + /** + * Sets The endpoint of the Azure Communication resource. + * + * @param endpoint the endpoint value. + * @return the AzureCommunicationCallingServerServiceImplBuilder. + */ + public AzureCommunicationCallingServerServiceImplBuilder endpoint(String endpoint) { + this.endpoint = endpoint; + return this; + } + + /* + * Api Version + */ + private String apiVersion; + + /** + * Sets Api Version. + * + * @param apiVersion the apiVersion value. + * @return the AzureCommunicationCallingServerServiceImplBuilder. + */ + public AzureCommunicationCallingServerServiceImplBuilder apiVersion(String apiVersion) { + this.apiVersion = apiVersion; + return this; + } + + /* + * The HTTP pipeline to send requests through + */ + private HttpPipeline pipeline; + + /** + * Sets The HTTP pipeline to send requests through. + * + * @param pipeline the pipeline value. + * @return the AzureCommunicationCallingServerServiceImplBuilder. + */ + public AzureCommunicationCallingServerServiceImplBuilder pipeline(HttpPipeline pipeline) { + this.pipeline = pipeline; + return this; + } + + /* + * The serializer to serialize an object into a string + */ + private SerializerAdapter serializerAdapter; + + /** + * Sets The serializer to serialize an object into a string. + * + * @param serializerAdapter the serializerAdapter value. + * @return the AzureCommunicationCallingServerServiceImplBuilder. + */ + public AzureCommunicationCallingServerServiceImplBuilder serializerAdapter(SerializerAdapter serializerAdapter) { + this.serializerAdapter = serializerAdapter; + return this; + } + + /* + * The HTTP client used to send the request. + */ + private HttpClient httpClient; + + /** + * Sets The HTTP client used to send the request. + * + * @param httpClient the httpClient value. + * @return the AzureCommunicationCallingServerServiceImplBuilder. + */ + public AzureCommunicationCallingServerServiceImplBuilder httpClient(HttpClient httpClient) { + this.httpClient = httpClient; + return this; + } + + /* + * The configuration store that is used during construction of the service + * client. + */ + private Configuration configuration; + + /** + * Sets The configuration store that is used during construction of the service client. + * + * @param configuration the configuration value. + * @return the AzureCommunicationCallingServerServiceImplBuilder. + */ + public AzureCommunicationCallingServerServiceImplBuilder configuration(Configuration configuration) { + this.configuration = configuration; + return this; + } + + /* + * The logging configuration for HTTP requests and responses. + */ + private HttpLogOptions httpLogOptions; + + /** + * Sets The logging configuration for HTTP requests and responses. + * + * @param httpLogOptions the httpLogOptions value. + * @return the AzureCommunicationCallingServerServiceImplBuilder. + */ + public AzureCommunicationCallingServerServiceImplBuilder httpLogOptions(HttpLogOptions httpLogOptions) { + this.httpLogOptions = httpLogOptions; + return this; + } + + /* + * The retry policy that will attempt to retry failed requests, if + * applicable. + */ + private RetryPolicy retryPolicy; + + /** + * Sets The retry policy that will attempt to retry failed requests, if applicable. + * + * @param retryPolicy the retryPolicy value. + * @return the AzureCommunicationCallingServerServiceImplBuilder. + */ + public AzureCommunicationCallingServerServiceImplBuilder retryPolicy(RetryPolicy retryPolicy) { + this.retryPolicy = retryPolicy; + return this; + } + + /* + * The list of Http pipeline policies to add. + */ + private final List pipelinePolicies; + + /** + * Adds a custom Http pipeline policy. + * + * @param customPolicy The custom Http pipeline policy to add. + * @return the AzureCommunicationCallingServerServiceImplBuilder. + */ + public AzureCommunicationCallingServerServiceImplBuilder addPolicy(HttpPipelinePolicy customPolicy) { + pipelinePolicies.add(customPolicy); + return this; + } + + /** + * Builds an instance of AzureCommunicationCallingServerServiceImpl with the provided parameters. + * + * @return an instance of AzureCommunicationCallingServerServiceImpl. + */ + public AzureCommunicationCallingServerServiceImpl buildClient() { + if (apiVersion == null) { + this.apiVersion = "2021-06-15-preview"; + } + if (pipeline == null) { + this.pipeline = createHttpPipeline(); + } + if (serializerAdapter == null) { + this.serializerAdapter = JacksonAdapter.createDefaultSerializerAdapter(); + } + AzureCommunicationCallingServerServiceImpl client = + new AzureCommunicationCallingServerServiceImpl(pipeline, serializerAdapter, endpoint, apiVersion); + return client; + } + + private HttpPipeline createHttpPipeline() { + Configuration buildConfiguration = + (configuration == null) ? Configuration.getGlobalConfiguration() : configuration; + if (httpLogOptions == null) { + httpLogOptions = new HttpLogOptions(); + } + List policies = new ArrayList<>(); + String clientName = properties.getOrDefault(SDK_NAME, "UnknownName"); + String clientVersion = properties.getOrDefault(SDK_VERSION, "UnknownVersion"); + policies.add( + new UserAgentPolicy(httpLogOptions.getApplicationId(), clientName, clientVersion, buildConfiguration)); + HttpPolicyProviders.addBeforeRetryPolicies(policies); + policies.add(retryPolicy == null ? new RetryPolicy() : retryPolicy); + policies.add(new CookiePolicy()); + policies.addAll(this.pipelinePolicies); + HttpPolicyProviders.addAfterRetryPolicies(policies); + policies.add(new HttpLoggingPolicy(httpLogOptions)); + HttpPipeline httpPipeline = + new HttpPipelineBuilder() + .policies(policies.toArray(new HttpPipelinePolicy[0])) + .httpClient(httpClient) + .build(); + return httpPipeline; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/CallConnectionsImpl.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/CallConnectionsImpl.java new file mode 100644 index 0000000000000..7593a6c1d74aa --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/CallConnectionsImpl.java @@ -0,0 +1,812 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation; + +import com.azure.communication.callingserver.implementation.models.CancelAllMediaOperationsRequest; +import com.azure.communication.callingserver.implementation.models.CancelAllMediaOperationsResultInternal; +import com.azure.communication.callingserver.implementation.models.CommunicationErrorException; +import com.azure.communication.callingserver.implementation.models.CreateCallRequest; +import com.azure.communication.callingserver.implementation.models.CreateCallResultInternal; +import com.azure.communication.callingserver.implementation.models.InviteParticipantsRequest; +import com.azure.communication.callingserver.implementation.models.PlayAudioRequest; +import com.azure.communication.callingserver.implementation.models.PlayAudioResultInternal; +import com.azure.core.annotation.BodyParam; +import com.azure.core.annotation.Delete; +import com.azure.core.annotation.ExpectedResponses; +import com.azure.core.annotation.HeaderParam; +import com.azure.core.annotation.Host; +import com.azure.core.annotation.HostParam; +import com.azure.core.annotation.PathParam; +import com.azure.core.annotation.Post; +import com.azure.core.annotation.QueryParam; +import com.azure.core.annotation.ReturnType; +import com.azure.core.annotation.ServiceInterface; +import com.azure.core.annotation.ServiceMethod; +import com.azure.core.annotation.UnexpectedResponseExceptionType; +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.RestProxy; +import com.azure.core.util.Context; +import com.azure.core.util.FluxUtil; +import reactor.core.publisher.Mono; + +/** An instance of this class provides access to all the operations defined in CallConnections. */ +public final class CallConnectionsImpl { + /** The proxy service used to perform REST calls. */ + private final CallConnectionsService service; + + /** The service client containing this operation class. */ + private final AzureCommunicationCallingServerServiceImpl client; + + /** + * Initializes an instance of CallConnectionsImpl. + * + * @param client the instance of the service client containing this operation class. + */ + CallConnectionsImpl(AzureCommunicationCallingServerServiceImpl client) { + this.service = + RestProxy.create(CallConnectionsService.class, client.getHttpPipeline(), client.getSerializerAdapter()); + this.client = client; + } + + /** + * The interface defining all the services for AzureCommunicationCallingServerServiceCallConnections to be used by + * the proxy service to perform REST calls. + */ + @Host("{endpoint}") + @ServiceInterface(name = "AzureCommunicationCa") + private interface CallConnectionsService { + @Post("/calling/callConnections") + @ExpectedResponses({201}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> createCall( + @HostParam("endpoint") String endpoint, + @QueryParam("api-version") String apiVersion, + @BodyParam("application/json") CreateCallRequest callRequest, + @HeaderParam("Accept") String accept, + Context context); + + @Post("/calling/callConnections/{callConnectionId}/:hangup") + @ExpectedResponses({202}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> hangupCall( + @HostParam("endpoint") String endpoint, + @PathParam("callConnectionId") String callConnectionId, + @QueryParam("api-version") String apiVersion, + @HeaderParam("Accept") String accept, + Context context); + + @Post("/calling/callConnections/{callConnectionId}/:playAudio") + @ExpectedResponses({202}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> playAudio( + @HostParam("endpoint") String endpoint, + @PathParam("callConnectionId") String callConnectionId, + @QueryParam("api-version") String apiVersion, + @BodyParam("application/json") PlayAudioRequest request, + @HeaderParam("Accept") String accept, + Context context); + + @Post("/calling/callConnections/{callConnectionId}/:cancelAllMediaOperations") + @ExpectedResponses({200}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> cancelAllMediaOperations( + @HostParam("endpoint") String endpoint, + @PathParam("callConnectionId") String callConnectionId, + @QueryParam("api-version") String apiVersion, + @BodyParam("application/json") CancelAllMediaOperationsRequest cancelAllMediaOperationRequest, + @HeaderParam("Accept") String accept, + Context context); + + @Post("/calling/callConnections/{callConnectionId}/participants") + @ExpectedResponses({202}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> inviteParticipants( + @HostParam("endpoint") String endpoint, + @PathParam("callConnectionId") String callConnectionId, + @QueryParam("api-version") String apiVersion, + @BodyParam("application/json") InviteParticipantsRequest inviteParticipantsRequest, + @HeaderParam("Accept") String accept, + Context context); + + @Delete("/calling/callConnections/{callConnectionId}/participants/{participantId}") + @ExpectedResponses({202}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> removeParticipant( + @HostParam("endpoint") String endpoint, + @PathParam("callConnectionId") String callConnectionId, + @PathParam("participantId") String participantId, + @QueryParam("api-version") String apiVersion, + @HeaderParam("Accept") String accept, + Context context); + } + + /** + * Create a new call. + * + * @param callRequest Create call request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the create call operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> createCallWithResponseAsync(CreateCallRequest callRequest) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.createCall( + this.client.getEndpoint(), this.client.getApiVersion(), callRequest, accept, context)); + } + + /** + * Create a new call. + * + * @param callRequest Create call request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the create call operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> createCallWithResponseAsync( + CreateCallRequest callRequest, Context context) { + final String accept = "application/json"; + return service.createCall(this.client.getEndpoint(), this.client.getApiVersion(), callRequest, accept, context); + } + + /** + * Create a new call. + * + * @param callRequest Create call request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the create call operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono createCallAsync(CreateCallRequest callRequest) { + return createCallWithResponseAsync(callRequest) + .flatMap( + (Response res) -> { + if (res.getValue() != null) { + return Mono.just(res.getValue()); + } else { + return Mono.empty(); + } + }); + } + + /** + * Create a new call. + * + * @param callRequest Create call request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the create call operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono createCallAsync(CreateCallRequest callRequest, Context context) { + return createCallWithResponseAsync(callRequest, context) + .flatMap( + (Response res) -> { + if (res.getValue() != null) { + return Mono.just(res.getValue()); + } else { + return Mono.empty(); + } + }); + } + + /** + * Create a new call. + * + * @param callRequest Create call request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the create call operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public CreateCallResultInternal createCall(CreateCallRequest callRequest) { + return createCallAsync(callRequest).block(); + } + + /** + * Create a new call. + * + * @param callRequest Create call request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the create call operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response createCallWithResponse(CreateCallRequest callRequest, Context context) { + return createCallWithResponseAsync(callRequest, context).block(); + } + + /** + * Hangup a call. + * + * @param callConnectionId The call connection id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> hangupCallWithResponseAsync(String callConnectionId) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.hangupCall( + this.client.getEndpoint(), + callConnectionId, + this.client.getApiVersion(), + accept, + context)); + } + + /** + * Hangup a call. + * + * @param callConnectionId The call connection id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> hangupCallWithResponseAsync(String callConnectionId, Context context) { + final String accept = "application/json"; + return service.hangupCall( + this.client.getEndpoint(), callConnectionId, this.client.getApiVersion(), accept, context); + } + + /** + * Hangup a call. + * + * @param callConnectionId The call connection id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono hangupCallAsync(String callConnectionId) { + return hangupCallWithResponseAsync(callConnectionId).flatMap((Response res) -> Mono.empty()); + } + + /** + * Hangup a call. + * + * @param callConnectionId The call connection id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono hangupCallAsync(String callConnectionId, Context context) { + return hangupCallWithResponseAsync(callConnectionId, context).flatMap((Response res) -> Mono.empty()); + } + + /** + * Hangup a call. + * + * @param callConnectionId The call connection id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public void hangupCall(String callConnectionId) { + hangupCallAsync(callConnectionId).block(); + } + + /** + * Hangup a call. + * + * @param callConnectionId The call connection id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response hangupCallWithResponse(String callConnectionId, Context context) { + return hangupCallWithResponseAsync(callConnectionId, context).block(); + } + + /** + * Play audio in a call. + * + * @param callConnectionId The call connection id. + * @param request Play audio request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> playAudioWithResponseAsync( + String callConnectionId, PlayAudioRequest request) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.playAudio( + this.client.getEndpoint(), + callConnectionId, + this.client.getApiVersion(), + request, + accept, + context)); + } + + /** + * Play audio in a call. + * + * @param callConnectionId The call connection id. + * @param request Play audio request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> playAudioWithResponseAsync( + String callConnectionId, PlayAudioRequest request, Context context) { + final String accept = "application/json"; + return service.playAudio( + this.client.getEndpoint(), callConnectionId, this.client.getApiVersion(), request, accept, context); + } + + /** + * Play audio in a call. + * + * @param callConnectionId The call connection id. + * @param request Play audio request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono playAudioAsync(String callConnectionId, PlayAudioRequest request) { + return playAudioWithResponseAsync(callConnectionId, request) + .flatMap( + (Response res) -> { + if (res.getValue() != null) { + return Mono.just(res.getValue()); + } else { + return Mono.empty(); + } + }); + } + + /** + * Play audio in a call. + * + * @param callConnectionId The call connection id. + * @param request Play audio request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono playAudioAsync( + String callConnectionId, PlayAudioRequest request, Context context) { + return playAudioWithResponseAsync(callConnectionId, request, context) + .flatMap( + (Response res) -> { + if (res.getValue() != null) { + return Mono.just(res.getValue()); + } else { + return Mono.empty(); + } + }); + } + + /** + * Play audio in a call. + * + * @param callConnectionId The call connection id. + * @param request Play audio request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public PlayAudioResultInternal playAudio(String callConnectionId, PlayAudioRequest request) { + return playAudioAsync(callConnectionId, request).block(); + } + + /** + * Play audio in a call. + * + * @param callConnectionId The call connection id. + * @param request Play audio request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response playAudioWithResponse( + String callConnectionId, PlayAudioRequest request, Context context) { + return playAudioWithResponseAsync(callConnectionId, request, context).block(); + } + + /** + * Cancel all media operations. + * + * @param callConnectionId The call connection id. + * @param cancelAllMediaOperationRequest The cancel all media operations context. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the cancel all media operations. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> cancelAllMediaOperationsWithResponseAsync( + String callConnectionId, CancelAllMediaOperationsRequest cancelAllMediaOperationRequest) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.cancelAllMediaOperations( + this.client.getEndpoint(), + callConnectionId, + this.client.getApiVersion(), + cancelAllMediaOperationRequest, + accept, + context)); + } + + /** + * Cancel all media operations. + * + * @param callConnectionId The call connection id. + * @param cancelAllMediaOperationRequest The cancel all media operations context. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the cancel all media operations. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> cancelAllMediaOperationsWithResponseAsync( + String callConnectionId, CancelAllMediaOperationsRequest cancelAllMediaOperationRequest, Context context) { + final String accept = "application/json"; + return service.cancelAllMediaOperations( + this.client.getEndpoint(), + callConnectionId, + this.client.getApiVersion(), + cancelAllMediaOperationRequest, + accept, + context); + } + + /** + * Cancel all media operations. + * + * @param callConnectionId The call connection id. + * @param cancelAllMediaOperationRequest The cancel all media operations context. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the cancel all media operations. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono cancelAllMediaOperationsAsync( + String callConnectionId, CancelAllMediaOperationsRequest cancelAllMediaOperationRequest) { + return cancelAllMediaOperationsWithResponseAsync(callConnectionId, cancelAllMediaOperationRequest) + .flatMap( + (Response res) -> { + if (res.getValue() != null) { + return Mono.just(res.getValue()); + } else { + return Mono.empty(); + } + }); + } + + /** + * Cancel all media operations. + * + * @param callConnectionId The call connection id. + * @param cancelAllMediaOperationRequest The cancel all media operations context. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the cancel all media operations. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono cancelAllMediaOperationsAsync( + String callConnectionId, CancelAllMediaOperationsRequest cancelAllMediaOperationRequest, Context context) { + return cancelAllMediaOperationsWithResponseAsync(callConnectionId, cancelAllMediaOperationRequest, context) + .flatMap( + (Response res) -> { + if (res.getValue() != null) { + return Mono.just(res.getValue()); + } else { + return Mono.empty(); + } + }); + } + + /** + * Cancel all media operations. + * + * @param callConnectionId The call connection id. + * @param cancelAllMediaOperationRequest The cancel all media operations context. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the cancel all media operations. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public CancelAllMediaOperationsResultInternal cancelAllMediaOperations( + String callConnectionId, CancelAllMediaOperationsRequest cancelAllMediaOperationRequest) { + return cancelAllMediaOperationsAsync(callConnectionId, cancelAllMediaOperationRequest).block(); + } + + /** + * Cancel all media operations. + * + * @param callConnectionId The call connection id. + * @param cancelAllMediaOperationRequest The cancel all media operations context. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the cancel all media operations. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response cancelAllMediaOperationsWithResponse( + String callConnectionId, CancelAllMediaOperationsRequest cancelAllMediaOperationRequest, Context context) { + return cancelAllMediaOperationsWithResponseAsync(callConnectionId, cancelAllMediaOperationRequest, context) + .block(); + } + + /** + * Invite participants to the call. + * + * @param callConnectionId The call connection id. + * @param inviteParticipantsRequest Invite participant request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> inviteParticipantsWithResponseAsync( + String callConnectionId, InviteParticipantsRequest inviteParticipantsRequest) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.inviteParticipants( + this.client.getEndpoint(), + callConnectionId, + this.client.getApiVersion(), + inviteParticipantsRequest, + accept, + context)); + } + + /** + * Invite participants to the call. + * + * @param callConnectionId The call connection id. + * @param inviteParticipantsRequest Invite participant request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> inviteParticipantsWithResponseAsync( + String callConnectionId, InviteParticipantsRequest inviteParticipantsRequest, Context context) { + final String accept = "application/json"; + return service.inviteParticipants( + this.client.getEndpoint(), + callConnectionId, + this.client.getApiVersion(), + inviteParticipantsRequest, + accept, + context); + } + + /** + * Invite participants to the call. + * + * @param callConnectionId The call connection id. + * @param inviteParticipantsRequest Invite participant request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono inviteParticipantsAsync( + String callConnectionId, InviteParticipantsRequest inviteParticipantsRequest) { + return inviteParticipantsWithResponseAsync(callConnectionId, inviteParticipantsRequest) + .flatMap((Response res) -> Mono.empty()); + } + + /** + * Invite participants to the call. + * + * @param callConnectionId The call connection id. + * @param inviteParticipantsRequest Invite participant request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono inviteParticipantsAsync( + String callConnectionId, InviteParticipantsRequest inviteParticipantsRequest, Context context) { + return inviteParticipantsWithResponseAsync(callConnectionId, inviteParticipantsRequest, context) + .flatMap((Response res) -> Mono.empty()); + } + + /** + * Invite participants to the call. + * + * @param callConnectionId The call connection id. + * @param inviteParticipantsRequest Invite participant request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public void inviteParticipants(String callConnectionId, InviteParticipantsRequest inviteParticipantsRequest) { + inviteParticipantsAsync(callConnectionId, inviteParticipantsRequest).block(); + } + + /** + * Invite participants to the call. + * + * @param callConnectionId The call connection id. + * @param inviteParticipantsRequest Invite participant request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response inviteParticipantsWithResponse( + String callConnectionId, InviteParticipantsRequest inviteParticipantsRequest, Context context) { + return inviteParticipantsWithResponseAsync(callConnectionId, inviteParticipantsRequest, context).block(); + } + + /** + * Remove participant from the call. + * + * @param callConnectionId The call connection id. + * @param participantId The participant id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> removeParticipantWithResponseAsync(String callConnectionId, String participantId) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.removeParticipant( + this.client.getEndpoint(), + callConnectionId, + participantId, + this.client.getApiVersion(), + accept, + context)); + } + + /** + * Remove participant from the call. + * + * @param callConnectionId The call connection id. + * @param participantId The participant id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> removeParticipantWithResponseAsync( + String callConnectionId, String participantId, Context context) { + final String accept = "application/json"; + return service.removeParticipant( + this.client.getEndpoint(), + callConnectionId, + participantId, + this.client.getApiVersion(), + accept, + context); + } + + /** + * Remove participant from the call. + * + * @param callConnectionId The call connection id. + * @param participantId The participant id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono removeParticipantAsync(String callConnectionId, String participantId) { + return removeParticipantWithResponseAsync(callConnectionId, participantId) + .flatMap((Response res) -> Mono.empty()); + } + + /** + * Remove participant from the call. + * + * @param callConnectionId The call connection id. + * @param participantId The participant id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono removeParticipantAsync(String callConnectionId, String participantId, Context context) { + return removeParticipantWithResponseAsync(callConnectionId, participantId, context) + .flatMap((Response res) -> Mono.empty()); + } + + /** + * Remove participant from the call. + * + * @param callConnectionId The call connection id. + * @param participantId The participant id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public void removeParticipant(String callConnectionId, String participantId) { + removeParticipantAsync(callConnectionId, participantId).block(); + } + + /** + * Remove participant from the call. + * + * @param callConnectionId The call connection id. + * @param participantId The participant id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response removeParticipantWithResponse( + String callConnectionId, String participantId, Context context) { + return removeParticipantWithResponseAsync(callConnectionId, participantId, context).block(); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/Constants.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/Constants.java new file mode 100644 index 0000000000000..e31a00bb73013 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/Constants.java @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.implementation; + +/*** + * Constants to be used by CallingServer. + */ +public final class Constants { + public static final int KB = 1024; + public static final int MB = KB * 1024; + + /*** + * Content downloader constants + */ + public static class ContentDownloader { + public static final int DEFAULT_CONCURRENT_TRANSFERS_COUNT = 5; + public static final int DEFAULT_BUFFER_SIZE = 4 * MB; + public static final int DEFAULT_INITIAL_DOWNLOAD_RANGE_SIZE = 256 * MB; + public static final int MAX_RETRIES = 4; + } + + /*** + * HTTP Header Names + */ + public static class HeaderNames { + public static final String RANGE = "Range"; + public static final String CONTENT_RANGE = "Content-Range"; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/ServerCallsImpl.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/ServerCallsImpl.java new file mode 100644 index 0000000000000..ed3b628daa51d --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/ServerCallsImpl.java @@ -0,0 +1,1195 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation; + +import com.azure.communication.callingserver.implementation.models.CallRecordingStateResultInternal; +import com.azure.communication.callingserver.implementation.models.CommunicationErrorException; +import com.azure.communication.callingserver.implementation.models.InviteParticipantsRequest; +import com.azure.communication.callingserver.implementation.models.JoinCallRequest; +import com.azure.communication.callingserver.implementation.models.JoinCallResultInternal; +import com.azure.communication.callingserver.implementation.models.PlayAudioRequest; +import com.azure.communication.callingserver.implementation.models.PlayAudioResultInternal; +import com.azure.communication.callingserver.implementation.models.StartCallRecordingRequest; +import com.azure.communication.callingserver.implementation.models.StartCallRecordingResultInternal; +import com.azure.core.annotation.BodyParam; +import com.azure.core.annotation.Delete; +import com.azure.core.annotation.ExpectedResponses; +import com.azure.core.annotation.Get; +import com.azure.core.annotation.HeaderParam; +import com.azure.core.annotation.Host; +import com.azure.core.annotation.HostParam; +import com.azure.core.annotation.PathParam; +import com.azure.core.annotation.Post; +import com.azure.core.annotation.QueryParam; +import com.azure.core.annotation.ReturnType; +import com.azure.core.annotation.ServiceInterface; +import com.azure.core.annotation.ServiceMethod; +import com.azure.core.annotation.UnexpectedResponseExceptionType; +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.RestProxy; +import com.azure.core.util.Context; +import com.azure.core.util.FluxUtil; +import reactor.core.publisher.Mono; + +/** An instance of this class provides access to all the operations defined in ServerCalls. */ +public final class ServerCallsImpl { + /** The proxy service used to perform REST calls. */ + private final ServerCallsService service; + + /** The service client containing this operation class. */ + private final AzureCommunicationCallingServerServiceImpl client; + + /** + * Initializes an instance of ServerCallsImpl. + * + * @param client the instance of the service client containing this operation class. + */ + ServerCallsImpl(AzureCommunicationCallingServerServiceImpl client) { + this.service = + RestProxy.create(ServerCallsService.class, client.getHttpPipeline(), client.getSerializerAdapter()); + this.client = client; + } + + /** + * The interface defining all the services for AzureCommunicationCallingServerServiceServerCalls to be used by the + * proxy service to perform REST calls. + */ + @Host("{endpoint}") + @ServiceInterface(name = "AzureCommunicationCa") + private interface ServerCallsService { + @Post("/calling/serverCalls/{serverCallId}/participants") + @ExpectedResponses({202}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> inviteParticipants( + @HostParam("endpoint") String endpoint, + @PathParam("serverCallId") String serverCallId, + @QueryParam("api-version") String apiVersion, + @BodyParam("application/json") InviteParticipantsRequest inviteParticipantsRequest, + @HeaderParam("Accept") String accept, + Context context); + + @Delete("/calling/serverCalls/{serverCallId}/participants/{participantId}") + @ExpectedResponses({202}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> removeParticipant( + @HostParam("endpoint") String endpoint, + @PathParam("serverCallId") String serverCallId, + @PathParam("participantId") String participantId, + @QueryParam("api-version") String apiVersion, + @HeaderParam("Accept") String accept, + Context context); + + @Post("/calling/serverCalls/{serverCallId}/recordings") + @ExpectedResponses({200}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> startRecording( + @HostParam("endpoint") String endpoint, + @PathParam("serverCallId") String serverCallId, + @QueryParam("api-version") String apiVersion, + @BodyParam("application/json") StartCallRecordingRequest request, + @HeaderParam("Accept") String accept, + Context context); + + @Get("/calling/serverCalls/{serverCallId}/recordings/{recordingId}") + @ExpectedResponses({200}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> recordingState( + @HostParam("endpoint") String endpoint, + @PathParam("serverCallId") String serverCallId, + @PathParam("recordingId") String recordingId, + @QueryParam("api-version") String apiVersion, + @HeaderParam("Accept") String accept, + Context context); + + @Delete("/calling/serverCalls/{serverCallId}/recordings/{recordingId}") + @ExpectedResponses({200}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> stopRecording( + @HostParam("endpoint") String endpoint, + @PathParam("serverCallId") String serverCallId, + @PathParam("recordingId") String recordingId, + @QueryParam("api-version") String apiVersion, + @HeaderParam("Accept") String accept, + Context context); + + @Post("/calling/serverCalls/{serverCallId}/recordings/{recordingId}/:pause") + @ExpectedResponses({200}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> pauseRecording( + @HostParam("endpoint") String endpoint, + @PathParam("serverCallId") String serverCallId, + @PathParam("recordingId") String recordingId, + @QueryParam("api-version") String apiVersion, + @HeaderParam("Accept") String accept, + Context context); + + @Post("/calling/serverCalls/{serverCallId}/recordings/{recordingId}/:resume") + @ExpectedResponses({200}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> resumeRecording( + @HostParam("endpoint") String endpoint, + @PathParam("serverCallId") String serverCallId, + @PathParam("recordingId") String recordingId, + @QueryParam("api-version") String apiVersion, + @HeaderParam("Accept") String accept, + Context context); + + @Post("/calling/serverCalls/{serverCallId}/:join") + @ExpectedResponses({202}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> joinCall( + @HostParam("endpoint") String endpoint, + @PathParam("serverCallId") String serverCallId, + @QueryParam("api-version") String apiVersion, + @BodyParam("application/json") JoinCallRequest callRequest, + @HeaderParam("Accept") String accept, + Context context); + + @Post("/calling/serverCalls/{serverCallId}/:playAudio") + @ExpectedResponses({202}) + @UnexpectedResponseExceptionType(CommunicationErrorException.class) + Mono> playAudio( + @HostParam("endpoint") String endpoint, + @PathParam("serverCallId") String serverCallId, + @QueryParam("api-version") String apiVersion, + @BodyParam("application/json") PlayAudioRequest request, + @HeaderParam("Accept") String accept, + Context context); + } + + /** + * Invite participants to the call. + * + * @param serverCallId The server call id. + * @param inviteParticipantsRequest The invite participant request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> inviteParticipantsWithResponseAsync( + String serverCallId, InviteParticipantsRequest inviteParticipantsRequest) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.inviteParticipants( + this.client.getEndpoint(), + serverCallId, + this.client.getApiVersion(), + inviteParticipantsRequest, + accept, + context)); + } + + /** + * Invite participants to the call. + * + * @param serverCallId The server call id. + * @param inviteParticipantsRequest The invite participant request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> inviteParticipantsWithResponseAsync( + String serverCallId, InviteParticipantsRequest inviteParticipantsRequest, Context context) { + final String accept = "application/json"; + return service.inviteParticipants( + this.client.getEndpoint(), + serverCallId, + this.client.getApiVersion(), + inviteParticipantsRequest, + accept, + context); + } + + /** + * Invite participants to the call. + * + * @param serverCallId The server call id. + * @param inviteParticipantsRequest The invite participant request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono inviteParticipantsAsync( + String serverCallId, InviteParticipantsRequest inviteParticipantsRequest) { + return inviteParticipantsWithResponseAsync(serverCallId, inviteParticipantsRequest) + .flatMap((Response res) -> Mono.empty()); + } + + /** + * Invite participants to the call. + * + * @param serverCallId The server call id. + * @param inviteParticipantsRequest The invite participant request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono inviteParticipantsAsync( + String serverCallId, InviteParticipantsRequest inviteParticipantsRequest, Context context) { + return inviteParticipantsWithResponseAsync(serverCallId, inviteParticipantsRequest, context) + .flatMap((Response res) -> Mono.empty()); + } + + /** + * Invite participants to the call. + * + * @param serverCallId The server call id. + * @param inviteParticipantsRequest The invite participant request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public void inviteParticipants(String serverCallId, InviteParticipantsRequest inviteParticipantsRequest) { + inviteParticipantsAsync(serverCallId, inviteParticipantsRequest).block(); + } + + /** + * Invite participants to the call. + * + * @param serverCallId The server call id. + * @param inviteParticipantsRequest The invite participant request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response inviteParticipantsWithResponse( + String serverCallId, InviteParticipantsRequest inviteParticipantsRequest, Context context) { + return inviteParticipantsWithResponseAsync(serverCallId, inviteParticipantsRequest, context).block(); + } + + /** + * Remove participant from the call. + * + * @param serverCallId Server call id. + * @param participantId Participant id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> removeParticipantWithResponseAsync(String serverCallId, String participantId) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.removeParticipant( + this.client.getEndpoint(), + serverCallId, + participantId, + this.client.getApiVersion(), + accept, + context)); + } + + /** + * Remove participant from the call. + * + * @param serverCallId Server call id. + * @param participantId Participant id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> removeParticipantWithResponseAsync( + String serverCallId, String participantId, Context context) { + final String accept = "application/json"; + return service.removeParticipant( + this.client.getEndpoint(), serverCallId, participantId, this.client.getApiVersion(), accept, context); + } + + /** + * Remove participant from the call. + * + * @param serverCallId Server call id. + * @param participantId Participant id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono removeParticipantAsync(String serverCallId, String participantId) { + return removeParticipantWithResponseAsync(serverCallId, participantId) + .flatMap((Response res) -> Mono.empty()); + } + + /** + * Remove participant from the call. + * + * @param serverCallId Server call id. + * @param participantId Participant id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono removeParticipantAsync(String serverCallId, String participantId, Context context) { + return removeParticipantWithResponseAsync(serverCallId, participantId, context) + .flatMap((Response res) -> Mono.empty()); + } + + /** + * Remove participant from the call. + * + * @param serverCallId Server call id. + * @param participantId Participant id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public void removeParticipant(String serverCallId, String participantId) { + removeParticipantAsync(serverCallId, participantId).block(); + } + + /** + * Remove participant from the call. + * + * @param serverCallId Server call id. + * @param participantId Participant id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response removeParticipantWithResponse(String serverCallId, String participantId, Context context) { + return removeParticipantWithResponseAsync(serverCallId, participantId, context).block(); + } + + /** + * Start call recording request. + * + * @param serverCallId The server call id. + * @param request The request body of start call recording request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of start call recording operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> startRecordingWithResponseAsync( + String serverCallId, StartCallRecordingRequest request) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.startRecording( + this.client.getEndpoint(), + serverCallId, + this.client.getApiVersion(), + request, + accept, + context)); + } + + /** + * Start call recording request. + * + * @param serverCallId The server call id. + * @param request The request body of start call recording request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of start call recording operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> startRecordingWithResponseAsync( + String serverCallId, StartCallRecordingRequest request, Context context) { + final String accept = "application/json"; + return service.startRecording( + this.client.getEndpoint(), serverCallId, this.client.getApiVersion(), request, accept, context); + } + + /** + * Start call recording request. + * + * @param serverCallId The server call id. + * @param request The request body of start call recording request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of start call recording operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono startRecordingAsync( + String serverCallId, StartCallRecordingRequest request) { + return startRecordingWithResponseAsync(serverCallId, request) + .flatMap( + (Response res) -> { + if (res.getValue() != null) { + return Mono.just(res.getValue()); + } else { + return Mono.empty(); + } + }); + } + + /** + * Start call recording request. + * + * @param serverCallId The server call id. + * @param request The request body of start call recording request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of start call recording operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono startRecordingAsync( + String serverCallId, StartCallRecordingRequest request, Context context) { + return startRecordingWithResponseAsync(serverCallId, request, context) + .flatMap( + (Response res) -> { + if (res.getValue() != null) { + return Mono.just(res.getValue()); + } else { + return Mono.empty(); + } + }); + } + + /** + * Start call recording request. + * + * @param serverCallId The server call id. + * @param request The request body of start call recording request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of start call recording operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public StartCallRecordingResultInternal startRecording(String serverCallId, StartCallRecordingRequest request) { + return startRecordingAsync(serverCallId, request).block(); + } + + /** + * Start call recording request. + * + * @param serverCallId The server call id. + * @param request The request body of start call recording request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of start call recording operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response startRecordingWithResponse( + String serverCallId, StartCallRecordingRequest request, Context context) { + return startRecordingWithResponseAsync(serverCallId, request, context).block(); + } + + /** + * Get call recording state. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return call recording state. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> recordingStateWithResponseAsync( + String serverCallId, String recordingId) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.recordingState( + this.client.getEndpoint(), + serverCallId, + recordingId, + this.client.getApiVersion(), + accept, + context)); + } + + /** + * Get call recording state. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return call recording state. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> recordingStateWithResponseAsync( + String serverCallId, String recordingId, Context context) { + final String accept = "application/json"; + return service.recordingState( + this.client.getEndpoint(), serverCallId, recordingId, this.client.getApiVersion(), accept, context); + } + + /** + * Get call recording state. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return call recording state. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono recordingStateAsync(String serverCallId, String recordingId) { + return recordingStateWithResponseAsync(serverCallId, recordingId) + .flatMap( + (Response res) -> { + if (res.getValue() != null) { + return Mono.just(res.getValue()); + } else { + return Mono.empty(); + } + }); + } + + /** + * Get call recording state. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return call recording state. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono recordingStateAsync( + String serverCallId, String recordingId, Context context) { + return recordingStateWithResponseAsync(serverCallId, recordingId, context) + .flatMap( + (Response res) -> { + if (res.getValue() != null) { + return Mono.just(res.getValue()); + } else { + return Mono.empty(); + } + }); + } + + /** + * Get call recording state. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return call recording state. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public CallRecordingStateResultInternal recordingState(String serverCallId, String recordingId) { + return recordingStateAsync(serverCallId, recordingId).block(); + } + + /** + * Get call recording state. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return call recording state. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response recordingStateWithResponse( + String serverCallId, String recordingId, Context context) { + return recordingStateWithResponseAsync(serverCallId, recordingId, context).block(); + } + + /** + * Stop recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> stopRecordingWithResponseAsync(String serverCallId, String recordingId) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.stopRecording( + this.client.getEndpoint(), + serverCallId, + recordingId, + this.client.getApiVersion(), + accept, + context)); + } + + /** + * Stop recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> stopRecordingWithResponseAsync( + String serverCallId, String recordingId, Context context) { + final String accept = "application/json"; + return service.stopRecording( + this.client.getEndpoint(), serverCallId, recordingId, this.client.getApiVersion(), accept, context); + } + + /** + * Stop recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono stopRecordingAsync(String serverCallId, String recordingId) { + return stopRecordingWithResponseAsync(serverCallId, recordingId).flatMap((Response res) -> Mono.empty()); + } + + /** + * Stop recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono stopRecordingAsync(String serverCallId, String recordingId, Context context) { + return stopRecordingWithResponseAsync(serverCallId, recordingId, context) + .flatMap((Response res) -> Mono.empty()); + } + + /** + * Stop recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public void stopRecording(String serverCallId, String recordingId) { + stopRecordingAsync(serverCallId, recordingId).block(); + } + + /** + * Stop recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response stopRecordingWithResponse(String serverCallId, String recordingId, Context context) { + return stopRecordingWithResponseAsync(serverCallId, recordingId, context).block(); + } + + /** + * Pause recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> pauseRecordingWithResponseAsync(String serverCallId, String recordingId) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.pauseRecording( + this.client.getEndpoint(), + serverCallId, + recordingId, + this.client.getApiVersion(), + accept, + context)); + } + + /** + * Pause recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> pauseRecordingWithResponseAsync( + String serverCallId, String recordingId, Context context) { + final String accept = "application/json"; + return service.pauseRecording( + this.client.getEndpoint(), serverCallId, recordingId, this.client.getApiVersion(), accept, context); + } + + /** + * Pause recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono pauseRecordingAsync(String serverCallId, String recordingId) { + return pauseRecordingWithResponseAsync(serverCallId, recordingId).flatMap((Response res) -> Mono.empty()); + } + + /** + * Pause recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono pauseRecordingAsync(String serverCallId, String recordingId, Context context) { + return pauseRecordingWithResponseAsync(serverCallId, recordingId, context) + .flatMap((Response res) -> Mono.empty()); + } + + /** + * Pause recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public void pauseRecording(String serverCallId, String recordingId) { + pauseRecordingAsync(serverCallId, recordingId).block(); + } + + /** + * Pause recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response pauseRecordingWithResponse(String serverCallId, String recordingId, Context context) { + return pauseRecordingWithResponseAsync(serverCallId, recordingId, context).block(); + } + + /** + * Resume recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> resumeRecordingWithResponseAsync(String serverCallId, String recordingId) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.resumeRecording( + this.client.getEndpoint(), + serverCallId, + recordingId, + this.client.getApiVersion(), + accept, + context)); + } + + /** + * Resume recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> resumeRecordingWithResponseAsync( + String serverCallId, String recordingId, Context context) { + final String accept = "application/json"; + return service.resumeRecording( + this.client.getEndpoint(), serverCallId, recordingId, this.client.getApiVersion(), accept, context); + } + + /** + * Resume recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono resumeRecordingAsync(String serverCallId, String recordingId) { + return resumeRecordingWithResponseAsync(serverCallId, recordingId) + .flatMap((Response res) -> Mono.empty()); + } + + /** + * Resume recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the completion. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono resumeRecordingAsync(String serverCallId, String recordingId, Context context) { + return resumeRecordingWithResponseAsync(serverCallId, recordingId, context) + .flatMap((Response res) -> Mono.empty()); + } + + /** + * Resume recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public void resumeRecording(String serverCallId, String recordingId) { + resumeRecordingAsync(serverCallId, recordingId).block(); + } + + /** + * Resume recording a call. + * + * @param serverCallId The server call id. + * @param recordingId The recording id. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response resumeRecordingWithResponse(String serverCallId, String recordingId, Context context) { + return resumeRecordingWithResponseAsync(serverCallId, recordingId, context).block(); + } + + /** + * Join a call. + * + * @param serverCallId The server call id. + * @param callRequest The join call request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the join call operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> joinCallWithResponseAsync( + String serverCallId, JoinCallRequest callRequest) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.joinCall( + this.client.getEndpoint(), + serverCallId, + this.client.getApiVersion(), + callRequest, + accept, + context)); + } + + /** + * Join a call. + * + * @param serverCallId The server call id. + * @param callRequest The join call request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the join call operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> joinCallWithResponseAsync( + String serverCallId, JoinCallRequest callRequest, Context context) { + final String accept = "application/json"; + return service.joinCall( + this.client.getEndpoint(), serverCallId, this.client.getApiVersion(), callRequest, accept, context); + } + + /** + * Join a call. + * + * @param serverCallId The server call id. + * @param callRequest The join call request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the join call operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono joinCallAsync(String serverCallId, JoinCallRequest callRequest) { + return joinCallWithResponseAsync(serverCallId, callRequest) + .flatMap( + (Response res) -> { + if (res.getValue() != null) { + return Mono.just(res.getValue()); + } else { + return Mono.empty(); + } + }); + } + + /** + * Join a call. + * + * @param serverCallId The server call id. + * @param callRequest The join call request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the join call operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono joinCallAsync( + String serverCallId, JoinCallRequest callRequest, Context context) { + return joinCallWithResponseAsync(serverCallId, callRequest, context) + .flatMap( + (Response res) -> { + if (res.getValue() != null) { + return Mono.just(res.getValue()); + } else { + return Mono.empty(); + } + }); + } + + /** + * Join a call. + * + * @param serverCallId The server call id. + * @param callRequest The join call request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the join call operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public JoinCallResultInternal joinCall(String serverCallId, JoinCallRequest callRequest) { + return joinCallAsync(serverCallId, callRequest).block(); + } + + /** + * Join a call. + * + * @param serverCallId The server call id. + * @param callRequest The join call request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload of the join call operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response joinCallWithResponse( + String serverCallId, JoinCallRequest callRequest, Context context) { + return joinCallWithResponseAsync(serverCallId, callRequest, context).block(); + } + + /** + * Play audio in a call. + * + * @param serverCallId The server call id. + * @param request Play audio request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> playAudioWithResponseAsync( + String serverCallId, PlayAudioRequest request) { + final String accept = "application/json"; + return FluxUtil.withContext( + context -> + service.playAudio( + this.client.getEndpoint(), + serverCallId, + this.client.getApiVersion(), + request, + accept, + context)); + } + + /** + * Play audio in a call. + * + * @param serverCallId The server call id. + * @param request Play audio request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> playAudioWithResponseAsync( + String serverCallId, PlayAudioRequest request, Context context) { + final String accept = "application/json"; + return service.playAudio( + this.client.getEndpoint(), serverCallId, this.client.getApiVersion(), request, accept, context); + } + + /** + * Play audio in a call. + * + * @param serverCallId The server call id. + * @param request Play audio request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono playAudioAsync(String serverCallId, PlayAudioRequest request) { + return playAudioWithResponseAsync(serverCallId, request) + .flatMap( + (Response res) -> { + if (res.getValue() != null) { + return Mono.just(res.getValue()); + } else { + return Mono.empty(); + } + }); + } + + /** + * Play audio in a call. + * + * @param serverCallId The server call id. + * @param request Play audio request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono playAudioAsync( + String serverCallId, PlayAudioRequest request, Context context) { + return playAudioWithResponseAsync(serverCallId, request, context) + .flatMap( + (Response res) -> { + if (res.getValue() != null) { + return Mono.just(res.getValue()); + } else { + return Mono.empty(); + } + }); + } + + /** + * Play audio in a call. + * + * @param serverCallId The server call id. + * @param request Play audio request. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public PlayAudioResultInternal playAudio(String serverCallId, PlayAudioRequest request) { + return playAudioAsync(serverCallId, request).block(); + } + + /** + * Play audio in a call. + * + * @param serverCallId The server call id. + * @param request Play audio request. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws CommunicationErrorException thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the response payload for play audio operation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response playAudioWithResponse( + String serverCallId, PlayAudioRequest request, Context context) { + return playAudioWithResponseAsync(serverCallId, request, context).block(); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/CallConnectionRequestConverter.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/CallConnectionRequestConverter.java new file mode 100644 index 0000000000000..49d5357d607b3 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/CallConnectionRequestConverter.java @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.implementation.converters; + +import com.azure.communication.callingserver.implementation.models.CreateCallRequest; +import com.azure.communication.callingserver.implementation.models.PhoneNumberIdentifierModel; +import com.azure.communication.callingserver.models.CallModality; +import com.azure.communication.callingserver.models.CreateCallOptions; +import com.azure.communication.callingserver.models.EventSubscriptionType; +import com.azure.communication.common.CommunicationIdentifier; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * A converter for {@link CreateCallRequest} + */ +public final class CallConnectionRequestConverter { + + /** + * Converts to {@link CreateCallRequest}. + */ + public static CreateCallRequest convert( + CommunicationIdentifier source, + CommunicationIdentifier[] targets, + CreateCallOptions createCallOptions) { + if (source == null || targets == null || targets.length == 0) { + return null; + } + + CreateCallRequest createCallRequest = + new CreateCallRequest() + .setSource(CommunicationIdentifierConverter.convert(source)) + .setTargets(new ArrayList<>(Arrays.asList(targets)) + .stream() + .map(CommunicationIdentifierConverter::convert) + .collect(Collectors.toList())); + + if (createCallOptions == null) { + return createCallRequest; + } + + List requestedMediaTypes = new LinkedList<>(); + for (CallModality modality : createCallOptions.getRequestedMediaTypes()) { + requestedMediaTypes.add(CallModality.fromString(modality.toString())); + } + + List requestedCallEvents = new LinkedList<>(); + for (EventSubscriptionType requestedCallEvent : createCallOptions.getRequestedCallEvents()) { + requestedCallEvents.add(EventSubscriptionType.fromString(requestedCallEvent.toString())); + } + + PhoneNumberIdentifierModel sourceAlternateIdentity = null; + if (createCallOptions.getAlternateCallerId() != null) { + sourceAlternateIdentity = new PhoneNumberIdentifierModel() + .setValue(createCallOptions.getAlternateCallerId().getPhoneNumber()); + } + + return createCallRequest + .setRequestedMediaTypes(requestedMediaTypes) + .setRequestedCallEvents(requestedCallEvents) + .setAlternateCallerId(sourceAlternateIdentity) + .setCallbackUri(createCallOptions.getCallbackUri()); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/CallingServerErrorConverter.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/CallingServerErrorConverter.java new file mode 100644 index 0000000000000..4099ec4e20963 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/CallingServerErrorConverter.java @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.implementation.converters; + +import com.azure.communication.callingserver.implementation.models.CommunicationError; +import com.azure.communication.callingserver.implementation.models.CommunicationErrorException; +import com.azure.communication.callingserver.models.CallingServerError; +import com.azure.communication.callingserver.models.CallingServerErrorException; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * A converter between {@link CommunicationError} and {@link CallingServerError}. + */ +public final class CallingServerErrorConverter { + /** + * Maps from {@link CommunicationError} to {@link CallingServerError}. + */ + public static CallingServerError convert(CommunicationError communicationError) { + if (communicationError == null) { + return null; + } + + List details = new ArrayList<>(); + + if (communicationError.getDetails() != null) { + details = communicationError + .getDetails() + .stream() + .map(CallingServerErrorConverter::convert) + .collect(Collectors.toList()); + } + + return new CallingServerError( + communicationError.getMessage(), + communicationError.getCode(), + communicationError.getTarget(), + details, + convert(communicationError.getInnerError()) + ); + } + + /** + * Maps from {@link CommunicationErrorException} to {@link CallingServerErrorException}. + */ + public static CallingServerErrorException translateException(CommunicationErrorException exception) { + CallingServerError error = null; + if (exception.getValue() != null) { + error = CallingServerErrorConverter.convert(exception.getValue()); + } + return new CallingServerErrorException(exception.getMessage(), exception.getResponse(), error); + } + + private CallingServerErrorConverter() { + } +} + diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/CancelAllMediaOperationsResultConverter.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/CancelAllMediaOperationsResultConverter.java new file mode 100644 index 0000000000000..76a4dff8b8417 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/CancelAllMediaOperationsResultConverter.java @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.implementation.converters; + +import com.azure.communication.callingserver.implementation.models.CancelAllMediaOperationsResultInternal; +import com.azure.communication.callingserver.models.CancelAllMediaOperationsResult; + +/** + * A converter between {@link CancelAllMediaOperationsResultInternal} and {@link CancelAllMediaOperationsResult}. + */ +public class CancelAllMediaOperationsResultConverter { + + /** + * Maps from {@link CancelAllMediaOperationsResultInternal} to {@link CancelAllMediaOperationsResult}. + */ + public static CancelAllMediaOperationsResult convert(CancelAllMediaOperationsResultInternal resultInternal) { + if (resultInternal == null) { + return null; + } + + return new CancelAllMediaOperationsResult( + resultInternal.getId(), + resultInternal.getStatus(), + resultInternal.getOperationContext(), + ResultInfoConverter.convert(resultInternal.getResultInfo())); + } +} + diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/CommunicationIdentifierConverter.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/CommunicationIdentifierConverter.java new file mode 100644 index 0000000000000..06ad0d062fcfb --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/CommunicationIdentifierConverter.java @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.implementation.converters; + +import com.azure.communication.callingserver.implementation.models.CommunicationCloudEnvironmentModel; +import com.azure.communication.callingserver.implementation.models.CommunicationIdentifierModel; +import com.azure.communication.callingserver.implementation.models.CommunicationUserIdentifierModel; +import com.azure.communication.callingserver.implementation.models.MicrosoftTeamsUserIdentifierModel; +import com.azure.communication.callingserver.implementation.models.PhoneNumberIdentifierModel; +import com.azure.communication.common.CommunicationCloudEnvironment; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.communication.common.CommunicationUserIdentifier; +import com.azure.communication.common.MicrosoftTeamsUserIdentifier; +import com.azure.communication.common.PhoneNumberIdentifier; +import com.azure.communication.common.UnknownIdentifier; + +import java.util.ArrayList; +import java.util.Objects; + +/** + * A converter between {@link CommunicationIdentifierModel} and {@link CommunicationIdentifier}. + */ +public class CommunicationIdentifierConverter { + + /** + * Maps from {@link CommunicationIdentifierModel} to {@link CommunicationIdentifier}. + */ + public static CommunicationIdentifier convert(CommunicationIdentifierModel identifier) { + if (identifier == null) { + return 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()); + 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 + .fromString(teamsUserIdentifierModel.getCloud().toString())); + } + + Objects.requireNonNull(rawId); + return new UnknownIdentifier(rawId); + } + + /** + * Maps from {@link CommunicationIdentifier} to {@link CommunicationIdentifierModel}. + */ + public static CommunicationIdentifierModel convert(CommunicationIdentifier identifier) + throws IllegalArgumentException { + + if (identifier == null) { + return null; + } + + if (identifier instanceof CommunicationUserIdentifier) { + CommunicationUserIdentifier communicationUserIdentifier = (CommunicationUserIdentifier) identifier; + return new CommunicationIdentifierModel() + .setCommunicationUser( + new CommunicationUserIdentifierModel().setId(communicationUserIdentifier.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) { + UnknownIdentifier unknownIdentifier = (UnknownIdentifier) identifier; + return new CommunicationIdentifierModel().setRawId(unknownIdentifier.getId()); + } + + throw new IllegalArgumentException(String.format("Unknown identifier class '%s'", identifier.getClass().getName())); + } + + private static void assertSingleType(CommunicationIdentifierModel identifier) { + CommunicationUserIdentifierModel communicationUser = identifier.getCommunicationUser(); + PhoneNumberIdentifierModel phoneNumber = identifier.getPhoneNumber(); + MicrosoftTeamsUserIdentifierModel microsoftTeamsUser = identifier.getMicrosoftTeamsUser(); + + ArrayList presentProperties = new ArrayList<>(); + 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.", + String.join(", ", presentProperties))); + } + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/InviteParticipantRequestConverter.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/InviteParticipantRequestConverter.java new file mode 100644 index 0000000000000..90eb327a56b01 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/InviteParticipantRequestConverter.java @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.implementation.converters; + +import com.azure.communication.callingserver.implementation.models.InviteParticipantsRequest; +import com.azure.communication.callingserver.implementation.models.PhoneNumberIdentifierModel; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.communication.common.PhoneNumberIdentifier; + +import java.util.Collections; +import java.util.LinkedList; + +/** + * A converter for {@link InviteParticipantsRequest} + */ +public final class InviteParticipantRequestConverter { + + /** + * Converts to {@link InviteParticipantsRequest}. + */ + public static InviteParticipantsRequest convert( + CommunicationIdentifier participant, + String alternateCallerId, + String operationContext, + String callBackUri) { + if (participant == null) { + return null; + } + + PhoneNumberIdentifierModel phoneNumberIdentifierModel = + (alternateCallerId == null || alternateCallerId.isEmpty()) ? null + : CommunicationIdentifierConverter + .convert(new PhoneNumberIdentifier(alternateCallerId)).getPhoneNumber(); + + return new InviteParticipantsRequest() + .setParticipants(new LinkedList<>(Collections + .singletonList(CommunicationIdentifierConverter.convert(participant)))) + .setAlternateCallerId(phoneNumberIdentifierModel) + .setOperationContext(operationContext) + .setCallbackUri(callBackUri); + } + + private InviteParticipantRequestConverter() { + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/JoinCallRequestConverter.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/JoinCallRequestConverter.java new file mode 100644 index 0000000000000..ffacfb51c67b5 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/JoinCallRequestConverter.java @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.implementation.converters; + +import com.azure.communication.callingserver.implementation.models.JoinCallRequest; +import com.azure.communication.callingserver.models.CallModality; +import com.azure.communication.callingserver.models.EventSubscriptionType; +import com.azure.communication.callingserver.models.JoinCallOptions; +import com.azure.communication.common.CommunicationIdentifier; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A converter for {@link JoinCallRequest} + */ +public final class JoinCallRequestConverter { + + /** + * Converts to {@link JoinCallRequest}. + */ + public static JoinCallRequest convert(CommunicationIdentifier source, JoinCallOptions joinCallOptions) { + if (source == null) { + return null; + } + + JoinCallRequest joinCallRequest = new JoinCallRequest() + .setSource(CommunicationIdentifierConverter.convert(source)); + + if (joinCallOptions == null) { + return joinCallRequest; + } + + joinCallRequest.setSubject(joinCallOptions.getSubject()); + joinCallRequest.setCallbackUri(joinCallOptions.getCallbackUri()); + + List requestedModalities = new ArrayList<>(); + Collections.addAll(requestedModalities, joinCallOptions.getRequestedMediaTypes()); + joinCallRequest.setRequestedMediaTypes(requestedModalities); + + List requestedCallEvents = new ArrayList<>(); + Collections.addAll(requestedCallEvents, joinCallOptions.getRequestedCallEvents()); + joinCallRequest.setRequestedCallEvents(requestedCallEvents); + + return joinCallRequest; + } + + private JoinCallRequestConverter() { + } +} + diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/PlayAudioResultConverter.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/PlayAudioResultConverter.java new file mode 100644 index 0000000000000..80d27380b8900 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/PlayAudioResultConverter.java @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.implementation.converters; + +import com.azure.communication.callingserver.implementation.models.PlayAudioResultInternal; +import com.azure.communication.callingserver.models.PlayAudioResult; + +/** + * A converter between {@link PlayAudioResultInternal} and {@link PlayAudioResult}. + */ +public final class PlayAudioResultConverter { + + /** + * Maps from {@link PlayAudioResultInternal} to {@link PlayAudioResult}. + */ + public static PlayAudioResult convert(PlayAudioResultInternal playAudioResultInternal) { + if (playAudioResultInternal == null) { + return null; + } + + return new PlayAudioResult( + playAudioResultInternal.getId(), + playAudioResultInternal.getStatus(), + playAudioResultInternal.getOperationContext(), + ResultInfoConverter.convert(playAudioResultInternal.getResultInfo())); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/ResultInfoConverter.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/ResultInfoConverter.java new file mode 100644 index 0000000000000..7fbe7ac1525f2 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/ResultInfoConverter.java @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.implementation.converters; + +import com.azure.communication.callingserver.implementation.models.ResultInfoInternal; +import com.azure.communication.callingserver.models.ResultInfo; + +/** + * A converter between {@link ResultInfoInternal} and {@link ResultInfo}. + */ +public final class ResultInfoConverter { + + /** + * Maps from {@link ResultInfoInternal} to {@link ResultInfo}. + */ + public static ResultInfo convert(ResultInfoInternal resultInfoInternal) { + if (resultInfoInternal == null) { + return null; + } + + return new ResultInfo( + resultInfoInternal.getCode(), + resultInfoInternal.getSubcode(), + resultInfoInternal.getMessage()); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/package-info.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/package-info.java new file mode 100644 index 0000000000000..1bb1c1261e57d --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/converters/package-info.java @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.implementation.converters; diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CallConnectionStateChangedEventInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CallConnectionStateChangedEventInternal.java new file mode 100644 index 0000000000000..3e29101ece8ff --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CallConnectionStateChangedEventInternal.java @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.communication.callingserver.models.CallConnectionState; +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The call connection state changed event. */ +@Fluent +public final class CallConnectionStateChangedEventInternal { + /* + * The server call.id. + */ + @JsonProperty(value = "serverCallId") + private String serverCallId; + + /* + * The call connection id. + */ + @JsonProperty(value = "callConnectionId") + private String callConnectionId; + + /* + * The call connection state. + */ + @JsonProperty(value = "callConnectionState") + private CallConnectionState callConnectionState; + + /** + * Get the serverCallId property: The server call.id. + * + * @return the serverCallId value. + */ + public String getServerCallId() { + return this.serverCallId; + } + + /** + * Set the serverCallId property: The server call.id. + * + * @param serverCallId the serverCallId value to set. + * @return the CallConnectionStateChangedEventInternal object itself. + */ + public CallConnectionStateChangedEventInternal setServerCallId(String serverCallId) { + this.serverCallId = serverCallId; + return this; + } + + /** + * Get the callConnectionId property: The call connection id. + * + * @return the callConnectionId value. + */ + public String getCallConnectionId() { + return this.callConnectionId; + } + + /** + * Set the callConnectionId property: The call connection id. + * + * @param callConnectionId the callConnectionId value to set. + * @return the CallConnectionStateChangedEventInternal object itself. + */ + public CallConnectionStateChangedEventInternal setCallConnectionId(String callConnectionId) { + this.callConnectionId = callConnectionId; + return this; + } + + /** + * Get the callConnectionState property: The call connection state. + * + * @return the callConnectionState value. + */ + public CallConnectionState getCallConnectionState() { + return this.callConnectionState; + } + + /** + * Set the callConnectionState property: The call connection state. + * + * @param callConnectionState the callConnectionState value to set. + * @return the CallConnectionStateChangedEventInternal object itself. + */ + public CallConnectionStateChangedEventInternal setCallConnectionState(CallConnectionState callConnectionState) { + this.callConnectionState = callConnectionState; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CallRecordingStateChangeEventInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CallRecordingStateChangeEventInternal.java new file mode 100644 index 0000000000000..6c2f8d352f5e8 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CallRecordingStateChangeEventInternal.java @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.communication.callingserver.models.CallRecordingState; +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.OffsetDateTime; + +/** The call recording state change event. */ +@Fluent +public final class CallRecordingStateChangeEventInternal { + /* + * The call recording id + */ + @JsonProperty(value = "recordingId") + private String recordingId; + + /* + * The recording state of the recording + */ + @JsonProperty(value = "state") + private CallRecordingState state; + + /* + * The time of the recording started + */ + @JsonProperty(value = "startDateTime") + private OffsetDateTime startDateTime; + + /* + * The server call.id. + */ + @JsonProperty(value = "serverCallId") + private String serverCallId; + + /** + * Get the recordingId property: The call recording id. + * + * @return the recordingId value. + */ + public String getRecordingId() { + return this.recordingId; + } + + /** + * Set the recordingId property: The call recording id. + * + * @param recordingId the recordingId value to set. + * @return the CallRecordingStateChangeEventInternal object itself. + */ + public CallRecordingStateChangeEventInternal setRecordingId(String recordingId) { + this.recordingId = recordingId; + return this; + } + + /** + * Get the state property: The recording state of the recording. + * + * @return the state value. + */ + public CallRecordingState getState() { + return this.state; + } + + /** + * Set the state property: The recording state of the recording. + * + * @param state the state value to set. + * @return the CallRecordingStateChangeEventInternal object itself. + */ + public CallRecordingStateChangeEventInternal setState(CallRecordingState state) { + this.state = state; + return this; + } + + /** + * Get the startDateTime property: The time of the recording started. + * + * @return the startDateTime value. + */ + public OffsetDateTime getStartDateTime() { + return this.startDateTime; + } + + /** + * Set the startDateTime property: The time of the recording started. + * + * @param startDateTime the startDateTime value to set. + * @return the CallRecordingStateChangeEventInternal object itself. + */ + public CallRecordingStateChangeEventInternal setStartDateTime(OffsetDateTime startDateTime) { + this.startDateTime = startDateTime; + return this; + } + + /** + * Get the serverCallId property: The server call.id. + * + * @return the serverCallId value. + */ + public String getServerCallId() { + return this.serverCallId; + } + + /** + * Set the serverCallId property: The server call.id. + * + * @param serverCallId the serverCallId value to set. + * @return the CallRecordingStateChangeEventInternal object itself. + */ + public CallRecordingStateChangeEventInternal setServerCallId(String serverCallId) { + this.serverCallId = serverCallId; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CallRecordingStateResultInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CallRecordingStateResultInternal.java new file mode 100644 index 0000000000000..41d34d7bfad96 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CallRecordingStateResultInternal.java @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.communication.callingserver.models.CallRecordingState; +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The response payload of get call recording state operation. */ +@Fluent +public final class CallRecordingStateResultInternal { + /* + * The recording state of the recording + */ + @JsonProperty(value = "recordingState") + private CallRecordingState recordingState; + + /** + * Get the recordingState property: The recording state of the recording. + * + * @return the recordingState value. + */ + public CallRecordingState getRecordingState() { + return this.recordingState; + } + + /** + * Set the recordingState property: The recording state of the recording. + * + * @param recordingState the recordingState value to set. + * @return the CallRecordingStateResultInternal object itself. + */ + public CallRecordingStateResultInternal setRecordingState(CallRecordingState recordingState) { + this.recordingState = recordingState; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CancelAllMediaOperationsRequest.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CancelAllMediaOperationsRequest.java new file mode 100644 index 0000000000000..301c6a3a1eac8 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CancelAllMediaOperationsRequest.java @@ -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.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The request payload for cancel all media operations. */ +@Fluent +public final class CancelAllMediaOperationsRequest { + /* + * The context for this operation. + */ + @JsonProperty(value = "operationContext") + private String operationContext; + + /** + * Get the operationContext property: The context for this operation. + * + * @return the operationContext value. + */ + public String getOperationContext() { + return this.operationContext; + } + + /** + * Set the operationContext property: The context for this operation. + * + * @param operationContext the operationContext value to set. + * @return the CancelAllMediaOperationsRequest object itself. + */ + public CancelAllMediaOperationsRequest setOperationContext(String operationContext) { + this.operationContext = operationContext; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CancelAllMediaOperationsResultInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CancelAllMediaOperationsResultInternal.java new file mode 100644 index 0000000000000..50743cb118067 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CancelAllMediaOperationsResultInternal.java @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.communication.callingserver.models.OperationStatus; +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The response payload of the cancel all media operations. */ +@Fluent +public final class CancelAllMediaOperationsResultInternal { + /* + * Gets or sets the identifier. + */ + @JsonProperty(value = "id") + private String id; + + /* + * Gets or sets the status of the operation + */ + @JsonProperty(value = "status") + private OperationStatus status; + + /* + * Gets or sets the operation context + */ + @JsonProperty(value = "operationContext") + private String operationContext; + + /* + * Gets or sets the result info + */ + @JsonProperty(value = "resultInfo") + private ResultInfoInternal resultInfo; + + /** + * Get the id property: Gets or sets the identifier. + * + * @return the id value. + */ + public String getId() { + return this.id; + } + + /** + * Set the id property: Gets or sets the identifier. + * + * @param id the id value to set. + * @return the CancelAllMediaOperationsResultInternal object itself. + */ + public CancelAllMediaOperationsResultInternal setId(String id) { + this.id = id; + return this; + } + + /** + * Get the status property: Gets or sets the status of the operation. + * + * @return the status value. + */ + public OperationStatus getStatus() { + return this.status; + } + + /** + * Set the status property: Gets or sets the status of the operation. + * + * @param status the status value to set. + * @return the CancelAllMediaOperationsResultInternal object itself. + */ + public CancelAllMediaOperationsResultInternal setStatus(OperationStatus status) { + this.status = status; + return this; + } + + /** + * Get the operationContext property: Gets or sets the operation context. + * + * @return the operationContext value. + */ + public String getOperationContext() { + return this.operationContext; + } + + /** + * Set the operationContext property: Gets or sets the operation context. + * + * @param operationContext the operationContext value to set. + * @return the CancelAllMediaOperationsResultInternal object itself. + */ + public CancelAllMediaOperationsResultInternal setOperationContext(String operationContext) { + this.operationContext = operationContext; + return this; + } + + /** + * Get the resultInfo property: Gets or sets the result info. + * + * @return the resultInfo value. + */ + public ResultInfoInternal getResultInfo() { + return this.resultInfo; + } + + /** + * Set the resultInfo property: Gets or sets the result info. + * + * @param resultInfo the resultInfo value to set. + * @return the CancelAllMediaOperationsResultInternal object itself. + */ + public CancelAllMediaOperationsResultInternal setResultInfo(ResultInfoInternal resultInfo) { + this.resultInfo = resultInfo; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationCloudEnvironmentModel.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationCloudEnvironmentModel.java new file mode 100644 index 0000000000000..7c9d3518458d2 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationCloudEnvironmentModel.java @@ -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.communication.callingserver.implementation.models; + +import com.azure.core.util.ExpandableStringEnum; +import com.fasterxml.jackson.annotation.JsonCreator; +import java.util.Collection; + +/** Defines values for CommunicationCloudEnvironmentModel. */ +public final class CommunicationCloudEnvironmentModel extends ExpandableStringEnum { + /** 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 values() { + return values(CommunicationCloudEnvironmentModel.class); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationError.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationError.java new file mode 100644 index 0000000000000..c79d1741def15 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationError.java @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; + +/** The Communication Services error. */ +@Fluent +public final class CommunicationError { + /* + * The error code. + */ + @JsonProperty(value = "code", required = true) + private String code; + + /* + * The error message. + */ + @JsonProperty(value = "message", required = true) + private String message; + + /* + * The error target. + */ + @JsonProperty(value = "target", access = JsonProperty.Access.WRITE_ONLY) + private String target; + + /* + * Further details about specific errors that led to this error. + */ + @JsonProperty(value = "details", access = JsonProperty.Access.WRITE_ONLY) + private List details; + + /* + * The inner error if any. + */ + @JsonProperty(value = "innererror", access = JsonProperty.Access.WRITE_ONLY) + private CommunicationError innerError; + + /** + * Get the code property: The error code. + * + * @return the code value. + */ + public String getCode() { + return this.code; + } + + /** + * Set the code property: The error code. + * + * @param code the code value to set. + * @return the CommunicationError object itself. + */ + public CommunicationError setCode(String code) { + this.code = code; + return this; + } + + /** + * Get the message property: The error message. + * + * @return the message value. + */ + public String getMessage() { + return this.message; + } + + /** + * Set the message property: The error message. + * + * @param message the message value to set. + * @return the CommunicationError object itself. + */ + public CommunicationError setMessage(String message) { + this.message = message; + return this; + } + + /** + * Get the target property: The error target. + * + * @return the target value. + */ + public String getTarget() { + return this.target; + } + + /** + * Get the details property: Further details about specific errors that led to this error. + * + * @return the details value. + */ + public List getDetails() { + return this.details; + } + + /** + * Get the innerError property: The inner error if any. + * + * @return the innerError value. + */ + public CommunicationError getInnerError() { + return this.innerError; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationErrorException.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationErrorException.java new file mode 100644 index 0000000000000..a30d1265f0d84 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationErrorException.java @@ -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.communication.callingserver.implementation.models; + +import com.azure.core.exception.HttpResponseException; +import com.azure.core.http.HttpResponse; + +/** Exception thrown for an invalid response with CommunicationError information. */ +public final class CommunicationErrorException extends HttpResponseException { + /** + * Initializes a new instance of the CommunicationErrorException class. + * + * @param message the exception message or the response content if a message is not available. + * @param response the HTTP response. + */ + public CommunicationErrorException(String message, HttpResponse response) { + super(message, response); + } + + /** + * Initializes a new instance of the CommunicationErrorException class. + * + * @param message the exception message or the response content if a message is not available. + * @param response the HTTP response. + * @param value the deserialized response value. + */ + public CommunicationErrorException(String message, HttpResponse response, CommunicationError value) { + super(message, response, value); + } + + @Override + public CommunicationError getValue() { + return (CommunicationError) super.getValue(); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationIdentifierModel.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationIdentifierModel.java new file mode 100644 index 0000000000000..378d1b0daa085 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationIdentifierModel.java @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Identifies a participant in Azure Communication services. A participant is, for example, a phone number or an Azure + * communication user. This model must be interpreted as a union: Apart from rawId, at most one further property may be + * set. + */ +@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; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationParticipantInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationParticipantInternal.java new file mode 100644 index 0000000000000..a0b894f9a8ff8 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationParticipantInternal.java @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** A participant in a call. */ +@Fluent +public final class CommunicationParticipantInternal { + /* + * Communication identifier of the participant + */ + @JsonProperty(value = "identifier") + private CommunicationIdentifierModel identifier; + + /* + * Participant id + */ + @JsonProperty(value = "participantId") + private String participantId; + + /* + * Is participant muted + */ + @JsonProperty(value = "isMuted") + private Boolean isMuted; + + /** + * Get the identifier property: Communication identifier of the participant. + * + * @return the identifier value. + */ + public CommunicationIdentifierModel getIdentifier() { + return this.identifier; + } + + /** + * Set the identifier property: Communication identifier of the participant. + * + * @param identifier the identifier value to set. + * @return the CommunicationParticipantInternal object itself. + */ + public CommunicationParticipantInternal setIdentifier(CommunicationIdentifierModel identifier) { + this.identifier = identifier; + return this; + } + + /** + * Get the participantId property: Participant id. + * + * @return the participantId value. + */ + public String getParticipantId() { + return this.participantId; + } + + /** + * Set the participantId property: Participant id. + * + * @param participantId the participantId value to set. + * @return the CommunicationParticipantInternal object itself. + */ + public CommunicationParticipantInternal setParticipantId(String participantId) { + this.participantId = participantId; + return this; + } + + /** + * Get the isMuted property: Is participant muted. + * + * @return the isMuted value. + */ + public Boolean isMuted() { + return this.isMuted; + } + + /** + * Set the isMuted property: Is participant muted. + * + * @param isMuted the isMuted value to set. + * @return the CommunicationParticipantInternal object itself. + */ + public CommunicationParticipantInternal setIsMuted(Boolean isMuted) { + this.isMuted = isMuted; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationUserIdentifierModel.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationUserIdentifierModel.java new file mode 100644 index 0000000000000..3f3e1e563dd37 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CommunicationUserIdentifierModel.java @@ -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.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** A user that got created with an Azure Communication Services resource. */ +@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; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CreateCallRequest.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CreateCallRequest.java new file mode 100644 index 0000000000000..0a05098b03543 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CreateCallRequest.java @@ -0,0 +1,200 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.communication.callingserver.models.CallModality; +import com.azure.communication.callingserver.models.EventSubscriptionType; +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; + +/** The request payload for create call. */ +@Fluent +public final class CreateCallRequest { + /* + * The alternate identity of the source of the call if dialing out to a + * pstn number + */ + @JsonProperty(value = "alternateCallerId") + private PhoneNumberIdentifierModel alternateCallerId; + + /* + * The targets of the call. + */ + @JsonProperty(value = "targets", required = true) + private List targets; + + /* + * The source of the call. + */ + @JsonProperty(value = "source", required = true) + private CommunicationIdentifierModel source; + + /* + * The subject. + */ + @JsonProperty(value = "subject") + private String subject; + + /* + * The callback URI. + */ + @JsonProperty(value = "callbackUri", required = true) + private String callbackUri; + + /* + * The requested modalities. + */ + @JsonProperty(value = "requestedMediaTypes") + private List requestedMediaTypes; + + /* + * The requested call events to subscribe to. + */ + @JsonProperty(value = "requestedCallEvents") + private List requestedCallEvents; + + /** + * Get the alternateCallerId property: The alternate identity of the source of the call if dialing out to a pstn + * number. + * + * @return the alternateCallerId value. + */ + public PhoneNumberIdentifierModel getAlternateCallerId() { + return this.alternateCallerId; + } + + /** + * Set the alternateCallerId property: The alternate identity of the source of the call if dialing out to a pstn + * number. + * + * @param alternateCallerId the alternateCallerId value to set. + * @return the CreateCallRequest object itself. + */ + public CreateCallRequest setAlternateCallerId(PhoneNumberIdentifierModel alternateCallerId) { + this.alternateCallerId = alternateCallerId; + return this; + } + + /** + * Get the targets property: The targets of the call. + * + * @return the targets value. + */ + public List getTargets() { + return this.targets; + } + + /** + * Set the targets property: The targets of the call. + * + * @param targets the targets value to set. + * @return the CreateCallRequest object itself. + */ + public CreateCallRequest setTargets(List targets) { + this.targets = targets; + return this; + } + + /** + * Get the source property: The source of the call. + * + * @return the source value. + */ + public CommunicationIdentifierModel getSource() { + return this.source; + } + + /** + * Set the source property: The source of the call. + * + * @param source the source value to set. + * @return the CreateCallRequest object itself. + */ + public CreateCallRequest setSource(CommunicationIdentifierModel source) { + this.source = source; + return this; + } + + /** + * Get the subject property: The subject. + * + * @return the subject value. + */ + public String getSubject() { + return this.subject; + } + + /** + * Set the subject property: The subject. + * + * @param subject the subject value to set. + * @return the CreateCallRequest object itself. + */ + public CreateCallRequest setSubject(String subject) { + this.subject = subject; + return this; + } + + /** + * Get the callbackUri property: The callback URI. + * + * @return the callbackUri value. + */ + public String getCallbackUri() { + return this.callbackUri; + } + + /** + * Set the callbackUri property: The callback URI. + * + * @param callbackUri the callbackUri value to set. + * @return the CreateCallRequest object itself. + */ + public CreateCallRequest setCallbackUri(String callbackUri) { + this.callbackUri = callbackUri; + return this; + } + + /** + * Get the requestedMediaTypes property: The requested modalities. + * + * @return the requestedMediaTypes value. + */ + public List getRequestedMediaTypes() { + return this.requestedMediaTypes; + } + + /** + * Set the requestedMediaTypes property: The requested modalities. + * + * @param requestedMediaTypes the requestedMediaTypes value to set. + * @return the CreateCallRequest object itself. + */ + public CreateCallRequest setRequestedMediaTypes(List requestedMediaTypes) { + this.requestedMediaTypes = requestedMediaTypes; + return this; + } + + /** + * Get the requestedCallEvents property: The requested call events to subscribe to. + * + * @return the requestedCallEvents value. + */ + public List getRequestedCallEvents() { + return this.requestedCallEvents; + } + + /** + * Set the requestedCallEvents property: The requested call events to subscribe to. + * + * @param requestedCallEvents the requestedCallEvents value to set. + * @return the CreateCallRequest object itself. + */ + public CreateCallRequest setRequestedCallEvents(List requestedCallEvents) { + this.requestedCallEvents = requestedCallEvents; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CreateCallResultInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CreateCallResultInternal.java new file mode 100644 index 0000000000000..625f74c2a9684 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/CreateCallResultInternal.java @@ -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.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The response payload of the create call operation. */ +@Fluent +public final class CreateCallResultInternal { + /* + * The call connection id. + */ + @JsonProperty(value = "callConnectionId") + private String callConnectionId; + + /** + * Get the callConnectionId property: The call connection id. + * + * @return the callConnectionId value. + */ + public String getCallConnectionId() { + return this.callConnectionId; + } + + /** + * Set the callConnectionId property: The call connection id. + * + * @param callConnectionId the callConnectionId value to set. + * @return the CreateCallResultInternal object itself. + */ + public CreateCallResultInternal setCallConnectionId(String callConnectionId) { + this.callConnectionId = callConnectionId; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/InviteParticipantsRequest.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/InviteParticipantsRequest.java new file mode 100644 index 0000000000000..0a8e51d717691 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/InviteParticipantsRequest.java @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; + +/** The invite participants request. */ +@Fluent +public final class InviteParticipantsRequest { + /* + * The alternate identity of source participant. + */ + @JsonProperty(value = "alternateCallerId") + private PhoneNumberIdentifierModel alternateCallerId; + + /* + * The list of participants to be added to the call. + */ + @JsonProperty(value = "participants", required = true) + private List participants; + + /* + * The operation context. + */ + @JsonProperty(value = "operationContext") + private String operationContext; + + /* + * The callback URI. + */ + @JsonProperty(value = "callbackUri") + private String callbackUri; + + /** + * Get the alternateCallerId property: The alternate identity of source participant. + * + * @return the alternateCallerId value. + */ + public PhoneNumberIdentifierModel getAlternateCallerId() { + return this.alternateCallerId; + } + + /** + * Set the alternateCallerId property: The alternate identity of source participant. + * + * @param alternateCallerId the alternateCallerId value to set. + * @return the InviteParticipantsRequest object itself. + */ + public InviteParticipantsRequest setAlternateCallerId(PhoneNumberIdentifierModel alternateCallerId) { + this.alternateCallerId = alternateCallerId; + return this; + } + + /** + * Get the participants property: The list of participants to be added to the call. + * + * @return the participants value. + */ + public List getParticipants() { + return this.participants; + } + + /** + * Set the participants property: The list of participants to be added to the call. + * + * @param participants the participants value to set. + * @return the InviteParticipantsRequest object itself. + */ + public InviteParticipantsRequest setParticipants(List participants) { + this.participants = participants; + return this; + } + + /** + * Get the operationContext property: The operation context. + * + * @return the operationContext value. + */ + public String getOperationContext() { + return this.operationContext; + } + + /** + * Set the operationContext property: The operation context. + * + * @param operationContext the operationContext value to set. + * @return the InviteParticipantsRequest object itself. + */ + public InviteParticipantsRequest setOperationContext(String operationContext) { + this.operationContext = operationContext; + return this; + } + + /** + * Get the callbackUri property: The callback URI. + * + * @return the callbackUri value. + */ + public String getCallbackUri() { + return this.callbackUri; + } + + /** + * Set the callbackUri property: The callback URI. + * + * @param callbackUri the callbackUri value to set. + * @return the InviteParticipantsRequest object itself. + */ + public InviteParticipantsRequest setCallbackUri(String callbackUri) { + this.callbackUri = callbackUri; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/InviteParticipantsResultEventInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/InviteParticipantsResultEventInternal.java new file mode 100644 index 0000000000000..6e3230b873a51 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/InviteParticipantsResultEventInternal.java @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.communication.callingserver.models.OperationStatus; +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The InviteParticipantsResultEventInternal model. */ +@Fluent +public final class InviteParticipantsResultEventInternal { + /* + * The result details. + */ + @JsonProperty(value = "resultInfo") + private ResultInfoInternal resultInfo; + + /* + * The operation context. + */ + @JsonProperty(value = "operationContext") + private String operationContext; + + /* + * Gets or sets the status of the operation + */ + @JsonProperty(value = "status") + private OperationStatus status; + + /** + * Get the resultInfo property: The result details. + * + * @return the resultInfo value. + */ + public ResultInfoInternal getResultInfo() { + return this.resultInfo; + } + + /** + * Set the resultInfo property: The result details. + * + * @param resultInfo the resultInfo value to set. + * @return the InviteParticipantsResultEventInternal object itself. + */ + public InviteParticipantsResultEventInternal setResultInfo(ResultInfoInternal resultInfo) { + this.resultInfo = resultInfo; + return this; + } + + /** + * Get the operationContext property: The operation context. + * + * @return the operationContext value. + */ + public String getOperationContext() { + return this.operationContext; + } + + /** + * Set the operationContext property: The operation context. + * + * @param operationContext the operationContext value to set. + * @return the InviteParticipantsResultEventInternal object itself. + */ + public InviteParticipantsResultEventInternal setOperationContext(String operationContext) { + this.operationContext = operationContext; + return this; + } + + /** + * Get the status property: Gets or sets the status of the operation. + * + * @return the status value. + */ + public OperationStatus getStatus() { + return this.status; + } + + /** + * Set the status property: Gets or sets the status of the operation. + * + * @param status the status value to set. + * @return the InviteParticipantsResultEventInternal object itself. + */ + public InviteParticipantsResultEventInternal setStatus(OperationStatus status) { + this.status = status; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/JoinCallRequest.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/JoinCallRequest.java new file mode 100644 index 0000000000000..6a251000d8531 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/JoinCallRequest.java @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.communication.callingserver.models.CallModality; +import com.azure.communication.callingserver.models.EventSubscriptionType; +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; + +/** The request payload for join call. */ +@Fluent +public final class JoinCallRequest { + /* + * The source of the call. + */ + @JsonProperty(value = "source", required = true) + private CommunicationIdentifierModel source; + + /* + * The subject. + */ + @JsonProperty(value = "subject") + private String subject; + + /* + * The callback URI. + */ + @JsonProperty(value = "callbackUri", required = true) + private String callbackUri; + + /* + * The requested modalities. + */ + @JsonProperty(value = "requestedMediaTypes") + private List requestedMediaTypes; + + /* + * The requested call events to subscribe to. + */ + @JsonProperty(value = "requestedCallEvents") + private List requestedCallEvents; + + /** + * Get the source property: The source of the call. + * + * @return the source value. + */ + public CommunicationIdentifierModel getSource() { + return this.source; + } + + /** + * Set the source property: The source of the call. + * + * @param source the source value to set. + * @return the JoinCallRequest object itself. + */ + public JoinCallRequest setSource(CommunicationIdentifierModel source) { + this.source = source; + return this; + } + + /** + * Get the subject property: The subject. + * + * @return the subject value. + */ + public String getSubject() { + return this.subject; + } + + /** + * Set the subject property: The subject. + * + * @param subject the subject value to set. + * @return the JoinCallRequest object itself. + */ + public JoinCallRequest setSubject(String subject) { + this.subject = subject; + return this; + } + + /** + * Get the callbackUri property: The callback URI. + * + * @return the callbackUri value. + */ + public String getCallbackUri() { + return this.callbackUri; + } + + /** + * Set the callbackUri property: The callback URI. + * + * @param callbackUri the callbackUri value to set. + * @return the JoinCallRequest object itself. + */ + public JoinCallRequest setCallbackUri(String callbackUri) { + this.callbackUri = callbackUri; + return this; + } + + /** + * Get the requestedMediaTypes property: The requested modalities. + * + * @return the requestedMediaTypes value. + */ + public List getRequestedMediaTypes() { + return this.requestedMediaTypes; + } + + /** + * Set the requestedMediaTypes property: The requested modalities. + * + * @param requestedMediaTypes the requestedMediaTypes value to set. + * @return the JoinCallRequest object itself. + */ + public JoinCallRequest setRequestedMediaTypes(List requestedMediaTypes) { + this.requestedMediaTypes = requestedMediaTypes; + return this; + } + + /** + * Get the requestedCallEvents property: The requested call events to subscribe to. + * + * @return the requestedCallEvents value. + */ + public List getRequestedCallEvents() { + return this.requestedCallEvents; + } + + /** + * Set the requestedCallEvents property: The requested call events to subscribe to. + * + * @param requestedCallEvents the requestedCallEvents value to set. + * @return the JoinCallRequest object itself. + */ + public JoinCallRequest setRequestedCallEvents(List requestedCallEvents) { + this.requestedCallEvents = requestedCallEvents; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/JoinCallResultInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/JoinCallResultInternal.java new file mode 100644 index 0000000000000..c511d323ac2e0 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/JoinCallResultInternal.java @@ -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.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The response payload of the join call operation. */ +@Fluent +public final class JoinCallResultInternal { + /* + * The call connection id. + */ + @JsonProperty(value = "callConnectionId") + private String callConnectionId; + + /** + * Get the callConnectionId property: The call connection id. + * + * @return the callConnectionId value. + */ + public String getCallConnectionId() { + return this.callConnectionId; + } + + /** + * Set the callConnectionId property: The call connection id. + * + * @param callConnectionId the callConnectionId value to set. + * @return the JoinCallResultInternal object itself. + */ + public JoinCallResultInternal setCallConnectionId(String callConnectionId) { + this.callConnectionId = callConnectionId; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/MicrosoftTeamsUserIdentifierModel.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/MicrosoftTeamsUserIdentifierModel.java new file mode 100644 index 0000000000000..31981b2916b70 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/MicrosoftTeamsUserIdentifierModel.java @@ -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.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** A Microsoft Teams user. */ +@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; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/ParticipantsUpdatedEventInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/ParticipantsUpdatedEventInternal.java new file mode 100644 index 0000000000000..e1642fbc4b718 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/ParticipantsUpdatedEventInternal.java @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; + +/** The participant update event. */ +@Fluent +public final class ParticipantsUpdatedEventInternal { + /* + * The call connection id. + */ + @JsonProperty(value = "callConnectionId") + private String callConnectionId; + + /* + * The list of participants. + */ + @JsonProperty(value = "participants") + private List participants; + + /** + * Get the callConnectionId property: The call connection id. + * + * @return the callConnectionId value. + */ + public String getCallConnectionId() { + return this.callConnectionId; + } + + /** + * Set the callConnectionId property: The call connection id. + * + * @param callConnectionId the callConnectionId value to set. + * @return the ParticipantsUpdatedEventInternal object itself. + */ + public ParticipantsUpdatedEventInternal setCallConnectionId(String callConnectionId) { + this.callConnectionId = callConnectionId; + return this; + } + + /** + * Get the participants property: The list of participants. + * + * @return the participants value. + */ + public List getParticipants() { + return this.participants; + } + + /** + * Set the participants property: The list of participants. + * + * @param participants the participants value to set. + * @return the ParticipantsUpdatedEventInternal object itself. + */ + public ParticipantsUpdatedEventInternal setParticipants(List participants) { + this.participants = participants; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/PhoneNumberIdentifierModel.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/PhoneNumberIdentifierModel.java new file mode 100644 index 0000000000000..f1bd23f38e9d1 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/PhoneNumberIdentifierModel.java @@ -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.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** A phone number. */ +@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; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/PlayAudioRequest.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/PlayAudioRequest.java new file mode 100644 index 0000000000000..465a4102d110d --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/PlayAudioRequest.java @@ -0,0 +1,152 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The request payload for playing audio. */ +@Fluent +public final class PlayAudioRequest { + /* + * The media resource uri of the play audio request. + * Currently only Wave file (.wav) format audio prompts are supported. + * More specifically, the audio content in the wave file must be mono + * (single-channel), + * 16-bit samples with a 16,000 (16KHz) sampling rate. + */ + @JsonProperty(value = "audioFileUri") + private String audioFileUri; + + /* + * The flag indicating whether audio file needs to be played in loop or + * not. + */ + @JsonProperty(value = "loop") + private Boolean loop; + + /* + * The value to identify context of the operation. + */ + @JsonProperty(value = "operationContext") + private String operationContext; + + /* + * An id for the media in the AudioFileUri, using which we cache the media + * resource. + */ + @JsonProperty(value = "audioFileId") + private String audioFileId; + + /* + * The callback Uri to receive PlayAudio status notifications. + */ + @JsonProperty(value = "callbackUri") + private String callbackUri; + + /** + * Get the audioFileUri property: The media resource uri of the play audio request. Currently only Wave file (.wav) + * format audio prompts are supported. More specifically, the audio content in the wave file must be mono + * (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * + * @return the audioFileUri value. + */ + public String getAudioFileUri() { + return this.audioFileUri; + } + + /** + * Set the audioFileUri property: The media resource uri of the play audio request. Currently only Wave file (.wav) + * format audio prompts are supported. More specifically, the audio content in the wave file must be mono + * (single-channel), 16-bit samples with a 16,000 (16KHz) sampling rate. + * + * @param audioFileUri the audioFileUri value to set. + * @return the PlayAudioRequest object itself. + */ + public PlayAudioRequest setAudioFileUri(String audioFileUri) { + this.audioFileUri = audioFileUri; + return this; + } + + /** + * Get the loop property: The flag indicating whether audio file needs to be played in loop or not. + * + * @return the loop value. + */ + public Boolean isLoop() { + return this.loop; + } + + /** + * Set the loop property: The flag indicating whether audio file needs to be played in loop or not. + * + * @param loop the loop value to set. + * @return the PlayAudioRequest object itself. + */ + public PlayAudioRequest setLoop(Boolean loop) { + this.loop = loop; + return this; + } + + /** + * Get the operationContext property: The value to identify context of the operation. + * + * @return the operationContext value. + */ + public String getOperationContext() { + return this.operationContext; + } + + /** + * Set the operationContext property: The value to identify context of the operation. + * + * @param operationContext the operationContext value to set. + * @return the PlayAudioRequest object itself. + */ + public PlayAudioRequest setOperationContext(String operationContext) { + this.operationContext = operationContext; + return this; + } + + /** + * Get the audioFileId property: An id for the media in the AudioFileUri, using which we cache the media resource. + * + * @return the audioFileId value. + */ + public String getAudioFileId() { + return this.audioFileId; + } + + /** + * Set the audioFileId property: An id for the media in the AudioFileUri, using which we cache the media resource. + * + * @param audioFileId the audioFileId value to set. + * @return the PlayAudioRequest object itself. + */ + public PlayAudioRequest setAudioFileId(String audioFileId) { + this.audioFileId = audioFileId; + return this; + } + + /** + * Get the callbackUri property: The callback Uri to receive PlayAudio status notifications. + * + * @return the callbackUri value. + */ + public String getCallbackUri() { + return this.callbackUri; + } + + /** + * Set the callbackUri property: The callback Uri to receive PlayAudio status notifications. + * + * @param callbackUri the callbackUri value to set. + * @return the PlayAudioRequest object itself. + */ + public PlayAudioRequest setCallbackUri(String callbackUri) { + this.callbackUri = callbackUri; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/PlayAudioResultEventInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/PlayAudioResultEventInternal.java new file mode 100644 index 0000000000000..2a53f5e788d0b --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/PlayAudioResultEventInternal.java @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.communication.callingserver.models.OperationStatus; +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The play audio result event. */ +@Fluent +public final class PlayAudioResultEventInternal { + /* + * The result details. + */ + @JsonProperty(value = "resultInfo") + private ResultInfoInternal resultInfo; + + /* + * The operation context. + */ + @JsonProperty(value = "operationContext") + private String operationContext; + + /* + * Gets or sets the status of the operation + */ + @JsonProperty(value = "status") + private OperationStatus status; + + /** + * Get the resultInfo property: The result details. + * + * @return the resultInfo value. + */ + public ResultInfoInternal getResultInfo() { + return this.resultInfo; + } + + /** + * Set the resultInfo property: The result details. + * + * @param resultInfo the resultInfo value to set. + * @return the PlayAudioResultEventInternal object itself. + */ + public PlayAudioResultEventInternal setResultInfo(ResultInfoInternal resultInfo) { + this.resultInfo = resultInfo; + return this; + } + + /** + * Get the operationContext property: The operation context. + * + * @return the operationContext value. + */ + public String getOperationContext() { + return this.operationContext; + } + + /** + * Set the operationContext property: The operation context. + * + * @param operationContext the operationContext value to set. + * @return the PlayAudioResultEventInternal object itself. + */ + public PlayAudioResultEventInternal setOperationContext(String operationContext) { + this.operationContext = operationContext; + return this; + } + + /** + * Get the status property: Gets or sets the status of the operation. + * + * @return the status value. + */ + public OperationStatus getStatus() { + return this.status; + } + + /** + * Set the status property: Gets or sets the status of the operation. + * + * @param status the status value to set. + * @return the PlayAudioResultEventInternal object itself. + */ + public PlayAudioResultEventInternal setStatus(OperationStatus status) { + this.status = status; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/PlayAudioResultInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/PlayAudioResultInternal.java new file mode 100644 index 0000000000000..d81709e963536 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/PlayAudioResultInternal.java @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.communication.callingserver.models.OperationStatus; +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The response payload for play audio operation. */ +@Fluent +public final class PlayAudioResultInternal { + /* + * Gets or sets the identifier. + */ + @JsonProperty(value = "id") + private String id; + + /* + * Gets or sets the status of the operation + */ + @JsonProperty(value = "status") + private OperationStatus status; + + /* + * Gets or sets the operation context + */ + @JsonProperty(value = "operationContext") + private String operationContext; + + /* + * Gets or sets the result info + */ + @JsonProperty(value = "resultInfo") + private ResultInfoInternal resultInfo; + + /** + * Get the id property: Gets or sets the identifier. + * + * @return the id value. + */ + public String getId() { + return this.id; + } + + /** + * Set the id property: Gets or sets the identifier. + * + * @param id the id value to set. + * @return the PlayAudioResultInternal object itself. + */ + public PlayAudioResultInternal setId(String id) { + this.id = id; + return this; + } + + /** + * Get the status property: Gets or sets the status of the operation. + * + * @return the status value. + */ + public OperationStatus getStatus() { + return this.status; + } + + /** + * Set the status property: Gets or sets the status of the operation. + * + * @param status the status value to set. + * @return the PlayAudioResultInternal object itself. + */ + public PlayAudioResultInternal setStatus(OperationStatus status) { + this.status = status; + return this; + } + + /** + * Get the operationContext property: Gets or sets the operation context. + * + * @return the operationContext value. + */ + public String getOperationContext() { + return this.operationContext; + } + + /** + * Set the operationContext property: Gets or sets the operation context. + * + * @param operationContext the operationContext value to set. + * @return the PlayAudioResultInternal object itself. + */ + public PlayAudioResultInternal setOperationContext(String operationContext) { + this.operationContext = operationContext; + return this; + } + + /** + * Get the resultInfo property: Gets or sets the result info. + * + * @return the resultInfo value. + */ + public ResultInfoInternal getResultInfo() { + return this.resultInfo; + } + + /** + * Set the resultInfo property: Gets or sets the result info. + * + * @param resultInfo the resultInfo value to set. + * @return the PlayAudioResultInternal object itself. + */ + public PlayAudioResultInternal setResultInfo(ResultInfoInternal resultInfo) { + this.resultInfo = resultInfo; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/ResultInfoInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/ResultInfoInternal.java new file mode 100644 index 0000000000000..d44a254798679 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/ResultInfoInternal.java @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** Result info class to be used to report result status for actions/operations. */ +@Fluent +public final class ResultInfoInternal { + /* + * Gets or sets the result code + * For synchronous failures, this maps one-to-one with HTTP responses. For + * asynchronous failures or messages, it is contextual. + */ + @JsonProperty(value = "code") + private Integer code; + + /* + * Gets or sets the result subcode. + * The subcode further classifies a failure. For example. + */ + @JsonProperty(value = "subcode") + private Integer subcode; + + /* + * Gets or sets the message + * The message is a detail explanation of subcode. + */ + @JsonProperty(value = "message") + private String message; + + /** + * Get the code property: Gets or sets the result code For synchronous failures, this maps one-to-one with HTTP + * responses. For asynchronous failures or messages, it is contextual. + * + * @return the code value. + */ + public Integer getCode() { + return this.code; + } + + /** + * Set the code property: Gets or sets the result code For synchronous failures, this maps one-to-one with HTTP + * responses. For asynchronous failures or messages, it is contextual. + * + * @param code the code value to set. + * @return the ResultInfoInternal object itself. + */ + public ResultInfoInternal setCode(Integer code) { + this.code = code; + return this; + } + + /** + * Get the subcode property: Gets or sets the result subcode. The subcode further classifies a failure. For example. + * + * @return the subcode value. + */ + public Integer getSubcode() { + return this.subcode; + } + + /** + * Set the subcode property: Gets or sets the result subcode. The subcode further classifies a failure. For example. + * + * @param subcode the subcode value to set. + * @return the ResultInfoInternal object itself. + */ + public ResultInfoInternal setSubcode(Integer subcode) { + this.subcode = subcode; + return this; + } + + /** + * Get the message property: Gets or sets the message The message is a detail explanation of subcode. + * + * @return the message value. + */ + public String getMessage() { + return this.message; + } + + /** + * Set the message property: Gets or sets the message The message is a detail explanation of subcode. + * + * @param message the message value to set. + * @return the ResultInfoInternal object itself. + */ + public ResultInfoInternal setMessage(String message) { + this.message = message; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/StartCallRecordingRequest.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/StartCallRecordingRequest.java new file mode 100644 index 0000000000000..06daaa80de4ae --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/StartCallRecordingRequest.java @@ -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.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The request payload start call recording operation. */ +@Fluent +public final class StartCallRecordingRequest { + /* + * The uri to send notifications to. + */ + @JsonProperty(value = "recordingStateCallbackUri") + private String recordingStateCallbackUri; + + /** + * Get the recordingStateCallbackUri property: The uri to send notifications to. + * + * @return the recordingStateCallbackUri value. + */ + public String getRecordingStateCallbackUri() { + return this.recordingStateCallbackUri; + } + + /** + * Set the recordingStateCallbackUri property: The uri to send notifications to. + * + * @param recordingStateCallbackUri the recordingStateCallbackUri value to set. + * @return the StartCallRecordingRequest object itself. + */ + public StartCallRecordingRequest setRecordingStateCallbackUri(String recordingStateCallbackUri) { + this.recordingStateCallbackUri = recordingStateCallbackUri; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/StartCallRecordingResultInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/StartCallRecordingResultInternal.java new file mode 100644 index 0000000000000..96ee82f6e6d53 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/StartCallRecordingResultInternal.java @@ -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.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The response payload of start call recording operation. */ +@Fluent +public final class StartCallRecordingResultInternal { + /* + * The recording id of the started recording + */ + @JsonProperty(value = "recordingId") + private String recordingId; + + /** + * Get the recordingId property: The recording id of the started recording. + * + * @return the recordingId value. + */ + public String getRecordingId() { + return this.recordingId; + } + + /** + * Set the recordingId property: The recording id of the started recording. + * + * @param recordingId the recordingId value to set. + * @return the StartCallRecordingResultInternal object itself. + */ + public StartCallRecordingResultInternal setRecordingId(String recordingId) { + this.recordingId = recordingId; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/ToneInfoInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/ToneInfoInternal.java new file mode 100644 index 0000000000000..a3d17cd58efcd --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/ToneInfoInternal.java @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.communication.callingserver.models.ToneValue; +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** Gets or sets the tone info. */ +@Fluent +public final class ToneInfoInternal { + /* + * Gets or sets the sequence id. This id can be used to determine if the + * same tone + * was played multiple times or if any tones were missed. + */ + @JsonProperty(value = "sequenceId") + private Integer sequenceId; + + /* + * Gets or sets the tone detected. + */ + @JsonProperty(value = "tone") + private ToneValue tone; + + /** + * Get the sequenceId property: Gets or sets the sequence id. This id can be used to determine if the same tone was + * played multiple times or if any tones were missed. + * + * @return the sequenceId value. + */ + public Integer getSequenceId() { + return this.sequenceId; + } + + /** + * Set the sequenceId property: Gets or sets the sequence id. This id can be used to determine if the same tone was + * played multiple times or if any tones were missed. + * + * @param sequenceId the sequenceId value to set. + * @return the ToneInfoInternal object itself. + */ + public ToneInfoInternal setSequenceId(Integer sequenceId) { + this.sequenceId = sequenceId; + return this; + } + + /** + * Get the tone property: Gets or sets the tone detected. + * + * @return the tone value. + */ + public ToneValue getTone() { + return this.tone; + } + + /** + * Set the tone property: Gets or sets the tone detected. + * + * @param tone the tone value to set. + * @return the ToneInfoInternal object itself. + */ + public ToneInfoInternal setTone(ToneValue tone) { + this.tone = tone; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/ToneReceivedEventInternal.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/ToneReceivedEventInternal.java new file mode 100644 index 0000000000000..cc0f5897123a6 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/ToneReceivedEventInternal.java @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The subscribe to tone event. */ +@Fluent +public final class ToneReceivedEventInternal { + /* + * The tone info. + */ + @JsonProperty(value = "toneInfo") + private ToneInfoInternal toneInfo; + + /* + * The call connection id. + */ + @JsonProperty(value = "callConnectionId") + private String callConnectionId; + + /** + * Get the toneInfo property: The tone info. + * + * @return the toneInfo value. + */ + public ToneInfoInternal getToneInfo() { + return this.toneInfo; + } + + /** + * Set the toneInfo property: The tone info. + * + * @param toneInfo the toneInfo value to set. + * @return the ToneReceivedEventInternal object itself. + */ + public ToneReceivedEventInternal setToneInfo(ToneInfoInternal toneInfo) { + this.toneInfo = toneInfo; + return this; + } + + /** + * Get the callConnectionId property: The call connection id. + * + * @return the callConnectionId value. + */ + public String getCallConnectionId() { + return this.callConnectionId; + } + + /** + * Set the callConnectionId property: The call connection id. + * + * @param callConnectionId the callConnectionId value to set. + * @return the ToneReceivedEventInternal object itself. + */ + public ToneReceivedEventInternal setCallConnectionId(String callConnectionId) { + this.callConnectionId = callConnectionId; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/package-info.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/package-info.java new file mode 100644 index 0000000000000..8d928e5256a32 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/models/package-info.java @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +/** + * Package containing the data models for AzureCommunicationCallingServerService. Azure Communication CallingServer + * Service. + */ +package com.azure.communication.callingserver.implementation.models; diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/package-info.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/package-info.java new file mode 100644 index 0000000000000..9bfc9adf8b53a --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/implementation/package-info.java @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +/** + * Package containing the implementations for AzureCommunicationCallingServerService. Azure Communication CallingServer + * Service. + */ +package com.azure.communication.callingserver.implementation; diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallConnectionState.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallConnectionState.java new file mode 100644 index 0000000000000..d26b539c93cab --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallConnectionState.java @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.models; + +import com.azure.core.util.ExpandableStringEnum; +import com.fasterxml.jackson.annotation.JsonCreator; +import java.util.Collection; + +/** Defines values for CallConnectionState. */ +public final class CallConnectionState extends ExpandableStringEnum { + /** Static value unknown for CallConnectionState. */ + public static final CallConnectionState UNKNOWN = fromString("unknown"); + + /** Static value idle for CallConnectionState. */ + public static final CallConnectionState IDLE = fromString("idle"); + + /** Static value incoming for CallConnectionState. */ + public static final CallConnectionState INCOMING = fromString("incoming"); + + /** Static value establishing for CallConnectionState. */ + public static final CallConnectionState ESTABLISHING = fromString("establishing"); + + /** Static value established for CallConnectionState. */ + public static final CallConnectionState ESTABLISHED = fromString("established"); + + /** Static value hold for CallConnectionState. */ + public static final CallConnectionState HOLD = fromString("hold"); + + /** Static value unhold for CallConnectionState. */ + public static final CallConnectionState UNHOLD = fromString("unhold"); + + /** Static value transferring for CallConnectionState. */ + public static final CallConnectionState TRANSFERRING = fromString("transferring"); + + /** Static value redirecting for CallConnectionState. */ + public static final CallConnectionState REDIRECTING = fromString("redirecting"); + + /** Static value terminating for CallConnectionState. */ + public static final CallConnectionState TERMINATING = fromString("terminating"); + + /** Static value terminated for CallConnectionState. */ + public static final CallConnectionState TERMINATED = fromString("terminated"); + + /** + * Creates or finds a CallConnectionState from its string representation. + * + * @param name a name to look for. + * @return the corresponding CallConnectionState. + */ + @JsonCreator + public static CallConnectionState fromString(String name) { + return fromString(name, CallConnectionState.class); + } + + /** @return known CallConnectionState values. */ + public static Collection values() { + return values(CallConnectionState.class); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallModality.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallModality.java new file mode 100644 index 0000000000000..3c770d5a2e072 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallModality.java @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.models; + +import com.azure.core.util.ExpandableStringEnum; +import com.fasterxml.jackson.annotation.JsonCreator; +import java.util.Collection; + +/** Defines values for CallModality. */ +public final class CallModality extends ExpandableStringEnum { + /** Static value audio for CallModality. */ + public static final CallModality AUDIO = fromString("audio"); + + /** Static value video for CallModality. */ + public static final CallModality VIDEO = fromString("video"); + + /** + * Creates or finds a CallModality from its string representation. + * + * @param name a name to look for. + * @return the corresponding CallModality. + */ + @JsonCreator + public static CallModality fromString(String name) { + return fromString(name, CallModality.class); + } + + /** @return known CallModality values. */ + public static Collection values() { + return values(CallModality.class); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallParticipant.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallParticipant.java new file mode 100644 index 0000000000000..8d67860d7681c --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallParticipant.java @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.core.annotation.Immutable; + +/** + * The participant in a call. + */ +@Immutable +public final class CallParticipant { + + /** + * The communication identity of the participant. + */ + private final CommunicationIdentifier identifier; + + /** + * The participant id. + */ + private final String participantId; + + /** + * Is participant muted. + */ + private final boolean isMuted; + + /** + * Get the communication identity of the participant. + * + * @return the communication identity of the participant object itself + */ + public CommunicationIdentifier getIdentifier() { + return identifier; + } + + /** + * Get the participant id. + * + * @return the participant id value. + */ + public String getParticipantId() { + return participantId; + } + + /** + * Get is participant muted. + * + * @return the communication identity of the participant object itself + */ + public boolean getIsMuted() { + return isMuted; + } + + /** + * Initializes a new instance of CallParticipant. + * + * @param communicationIdentifier Communication Identifier. + * @param participantId Participant Id. + * @param isMuted Is participant muted. + * @throws IllegalArgumentException if either parameter is null. + */ + public CallParticipant( + CommunicationIdentifier communicationIdentifier, + String participantId, + boolean isMuted) { + if (communicationIdentifier == null) { + throw new IllegalArgumentException("object communicationIdentifier cannot be null"); + } + if (participantId == null) { + throw new IllegalArgumentException("object participantId cannot be null"); + } + + this.identifier = communicationIdentifier; + this.participantId = participantId; + this.isMuted = isMuted; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallRecordingState.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallRecordingState.java new file mode 100644 index 0000000000000..9a832152d58a5 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallRecordingState.java @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.models; + +import com.azure.core.util.ExpandableStringEnum; +import com.fasterxml.jackson.annotation.JsonCreator; +import java.util.Collection; + +/** Defines values for CallRecordingState. */ +public final class CallRecordingState extends ExpandableStringEnum { + /** Static value active for CallRecordingState. */ + public static final CallRecordingState ACTIVE = fromString("active"); + + /** Static value inactive for CallRecordingState. */ + public static final CallRecordingState INACTIVE = fromString("inactive"); + + /** + * Creates or finds a CallRecordingState from its string representation. + * + * @param name a name to look for. + * @return the corresponding CallRecordingState. + */ + @JsonCreator + public static CallRecordingState fromString(String name) { + return fromString(name, CallRecordingState.class); + } + + /** @return known CallRecordingState values. */ + public static Collection values() { + return values(CallRecordingState.class); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallRecordingStateResult.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallRecordingStateResult.java new file mode 100644 index 0000000000000..1b02780805f10 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallRecordingStateResult.java @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import com.azure.core.annotation.Immutable; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The result payload of get call recording state operation. */ +@Immutable +public final class CallRecordingStateResult { + /* + * The state of the recording + */ + @JsonProperty(value = "recordingState") + private CallRecordingState recordingState; + + /** + * Get the recordingState property: The state of the recording. + * + * @return the recordingState value. + */ + public CallRecordingState getRecordingState() { + return recordingState; + } + + /** + * Initializes a new instance of CallRecordingStateResult. + * + * @param recordingState the recordingState value. + */ + public CallRecordingStateResult(CallRecordingState recordingState) { + this.recordingState = recordingState; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallingServerError.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallingServerError.java new file mode 100644 index 0000000000000..8d2259cde82d8 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallingServerError.java @@ -0,0 +1,103 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import com.azure.core.annotation.Immutable; + +import java.util.List; + +/** The Calling Server error. */ +@Immutable +public final class CallingServerError { + /* + * The error code. + */ + private final String code; + + /* + * The error message. + */ + private final String message; + + /* + * The error target. + */ + private final String target; + + /* + * Further details about specific errors that led to this error. + */ + private final List details; + + /* + * The inner error if any. + */ + private final CallingServerError innerError; + + /** + * Get the code property: The error code. + * + * @return the code value. + */ + public String getCode() { + return code; + } + + /** + * Get the message property: The error message. + * + * @return the message value. + */ + public String getMessage() { + return message; + } + + /** + * Get the target property: The error target. + * + * @return the target value. + */ + public String getTarget() { + return target; + } + + /** + * Get the details property: Further details about specific errors that led to + * this error. + * + * @return the details value. + */ + public List getDetails() { + return details; + } + + /** + * Get the innerError property: The inner error if any. + * + * @return the innerError value. + */ + public CallingServerError getInnerError() { + return innerError; + } + + /** + * Constructs a new CallingServerError + * @param message The message of the original error + * @param code The error code + * @param target The target of the error + * @param details Additional details + * @param innerError The inner error + */ + public CallingServerError( + String message, + String code, String target, + List details, + CallingServerError innerError) { + this.message = message; + this.code = code; + this.target = target; + this.details = details; + this.innerError = innerError; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallingServerErrorException.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallingServerErrorException.java new file mode 100644 index 0000000000000..5b4732d657161 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CallingServerErrorException.java @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import com.azure.core.annotation.Immutable; +import com.azure.core.exception.HttpResponseException; +import com.azure.core.http.HttpResponse; + +/** + * Exception thrown for an invalid response with {@link CallingServerError} information. + **/ +@Immutable +public final class CallingServerErrorException extends HttpResponseException { + /** + * Initializes a new instance of the CallingServerResponseException class. + * + * @param message the exception message or the response content if a message is not available. + * @param response the HTTP response. + */ + public CallingServerErrorException(String message, HttpResponse response) { + super(message, response); + } + + /** + * Initializes a new instance of the CallingServerErrorException class. + * + * @param message the exception message or the response content if a message is not available. + * @param response the HTTP response. + * @param value the deserialized response value. + */ + public CallingServerErrorException(String message, HttpResponse response, CallingServerError value) { + super(message, response, value); + } + + @Override + public CallingServerError getValue() { + return (CallingServerError) super.getValue(); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CancelAllMediaOperationsResult.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CancelAllMediaOperationsResult.java new file mode 100644 index 0000000000000..0d19b25f77c24 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CancelAllMediaOperationsResult.java @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import com.azure.core.annotation.Immutable; + +/** The response payload of the cancel all media operations. */ +@Immutable +public final class CancelAllMediaOperationsResult { + /* + * The identifier. + */ + private final String id; + + /* + * The status of the operation + */ + private final OperationStatus status; + + /* + * The operation context + */ + private final String operationContext; + + /* + * The result info + */ + private final ResultInfo resultInfo; + + /** + * Get the id property: Gets the identifier. + * + * @return the id value. + */ + public String getId() { + return id; + } + + /** + * Get the status property: Gets the status of the operation. + * + * @return the status value. + */ + public OperationStatus getStatus() { + return status; + } + + /** + * Get the operationContext property: Gets the operation context. + * + * @return the operationContext value. + */ + public String getOperationContext() { + return operationContext; + } + + /** + * Get the resultInfo property: Gets the result info. + * + * @return the resultInfo value. + */ + public ResultInfo getResultInfo() { + return resultInfo; + } + + /** + * Initializes a new instance of CancelAllMediaOperationsResult. + * + * @param id the id value. + * @param status the status value. + * @param operationContext the operationContext value. + * @param resultInfo the resultInfo value. + */ + public CancelAllMediaOperationsResult( + String id, + OperationStatus status, + String operationContext, + ResultInfo resultInfo) { + this.id = id; + this.status = status; + this.operationContext = operationContext; + this.resultInfo = resultInfo; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CreateCallOptions.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CreateCallOptions.java new file mode 100644 index 0000000000000..01adbda920146 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/CreateCallOptions.java @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import com.azure.communication.common.PhoneNumberIdentifier; +import com.azure.core.annotation.Fluent; + +/** + * The options for creating a call. + */ +@Fluent +public final class CreateCallOptions { + + /** + * The alternate caller id of the source. + */ + private PhoneNumberIdentifier alternateCallerId; + + /** + * The subject. + */ + private String subject; + + /** + * The callback URI. + */ + private final String callbackUri; + + /** + * The requested media types. + */ + private final CallModality[] requestedMediaTypes; + + /** + * The requested call events to subscribe to. + */ + private final EventSubscriptionType[] requestedCallEvents; + + /** + * Get the alternate caller id of the source. + * + * @return the alternate caller id object itself. + */ + public PhoneNumberIdentifier getAlternateCallerId() { + return alternateCallerId; + } + + /** + * Set the alternate caller id of the source to be used when target is phone number. + * + * @param alternateCallerId the alternate caller id value to set. + * @return the CreateCallOptions object itself. + */ + public CreateCallOptions setAlternateCallerId(PhoneNumberIdentifier alternateCallerId) { + this.alternateCallerId = alternateCallerId; + return this; + } + + /** + * Get the subject. + * + * @return the subject value. + */ + public String getSubject() { + return subject; + } + + /** + * Set the subject. + * + * @param subject the subject. + * @return the CreateCallOptions object itself. + */ + public CreateCallOptions setSubject(String subject) { + this.subject = subject; + return this; + } + + /** + * Get the subject. + * + * @return the subject value. + */ + public String getCallbackUri() { + return callbackUri; + } + + /** + * Get the requested modalities. + * + * @return the requested modalities object itself. + */ + public CallModality[] getRequestedMediaTypes() { + return this.requestedMediaTypes == null ? new CallModality[0] : this.requestedMediaTypes.clone(); + } + + /** + * Get the requested call events to subscribe to. + * + * @return the requested call events to subscribe to object itself. + */ + public EventSubscriptionType[] getRequestedCallEvents() { + return requestedCallEvents.clone(); + } + + /** + * Initializes a new instance of CreateCallOptions. + * + * @param callbackUri the callback URI. + * @param requestedMediaTypes the requested media types. + * @param requestedCallEvents the requested call events to subscribe to. + * @throws IllegalArgumentException if any parameters are null. + */ + public CreateCallOptions( + String callbackUri, + CallModality[] requestedMediaTypes, + EventSubscriptionType[] requestedCallEvents) { + if (callbackUri == null) { + throw new IllegalArgumentException("object callbackUri cannot be null"); + } + + if (requestedMediaTypes == null) { + throw new IllegalArgumentException("object requestedMediaTypes cannot be null"); + } + if (requestedCallEvents == null) { + throw new IllegalArgumentException("object requestedCallEvents cannot be null"); + } + + this.callbackUri = callbackUri; + + this.requestedMediaTypes = requestedMediaTypes.clone(); + this.requestedCallEvents = requestedCallEvents.clone(); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/EventSubscriptionType.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/EventSubscriptionType.java new file mode 100644 index 0000000000000..0a6bcc82f16f0 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/EventSubscriptionType.java @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.models; + +import com.azure.core.util.ExpandableStringEnum; +import com.fasterxml.jackson.annotation.JsonCreator; +import java.util.Collection; + +/** Defines values for EventSubscriptionType. */ +public final class EventSubscriptionType extends ExpandableStringEnum { + /** Static value participantsUpdated for EventSubscriptionType. */ + public static final EventSubscriptionType PARTICIPANTS_UPDATED = fromString("participantsUpdated"); + + /** Static value dtmfReceived for EventSubscriptionType. */ + public static final EventSubscriptionType DTMF_RECEIVED = fromString("dtmfReceived"); + + /** + * Creates or finds a EventSubscriptionType from its string representation. + * + * @param name a name to look for. + * @return the corresponding EventSubscriptionType. + */ + @JsonCreator + public static EventSubscriptionType fromString(String name) { + return fromString(name, EventSubscriptionType.class); + } + + /** @return known EventSubscriptionType values. */ + public static Collection values() { + return values(EventSubscriptionType.class); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/JoinCallOptions.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/JoinCallOptions.java new file mode 100644 index 0000000000000..8108d3dc522dd --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/JoinCallOptions.java @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The options for join call. */ +@Fluent +public final class JoinCallOptions { + /* + * The subject. + */ + @JsonProperty(value = "subject") + private String subject; + + /* + * The callback URI. + */ + @JsonProperty(value = "callbackUri", required = true) + private String callbackUri; + + /* + * The requested MediaTypes. + */ + @JsonProperty(value = "requestedMediaTypes", required = true) + private CallModality[] requestedMediaTypes; + + /* + * The requested call events to subscribe to. + */ + @JsonProperty(value = "requestedCallEvents", required = true) + private EventSubscriptionType[] requestedCallEvents; + + /** + * Get the subject property: The subject. + * + * @return the subject value. + */ + public String getSubject() { + return subject; + } + + /** + * Set the subject property: The subject. + * + * @param subject the subject value to set. + * @return the JoinCallOptions object itself. + */ + public JoinCallOptions setSubject(String subject) { + this.subject = subject; + return this; + } + + /** + * Get the callbackUri property: The callback URI. + * + * @return the callbackUri value. + */ + public String getCallbackUri() { + return callbackUri; + } + + /** + * Set the callbackUri property: The callback URI. + * + * @param callbackUri the callbackUri value to set. + * @return the JoinCallOptions object itself. + */ + public JoinCallOptions setCallbackUri(String callbackUri) { + this.callbackUri = callbackUri; + return this; + } + + /** + * Get the requestedMediaTypes property: The requested MediaTypes. + * + * @return the requestedMediaTypes value. + */ + public CallModality[] getRequestedMediaTypes() { + return requestedMediaTypes == null ? new CallModality[0] : requestedMediaTypes.clone(); + } + + /** + * Set the requestedMediaTypes property: The requested MediaTypes. + * + * @param requestedMediaTypes the requestedModalities value to set. + * @return the JoinCallOptions object itself. + */ + public JoinCallOptions setRequestedMediaTypes(CallModality[] requestedMediaTypes) { + this.requestedMediaTypes = requestedMediaTypes == null ? new CallModality[0] : requestedMediaTypes.clone(); + return this; + } + + /** + * Get the requestedCallEvents property: The requested call events to subscribe + * to. + * + * @return the requestedCallEvents value. + */ + public EventSubscriptionType[] getRequestedCallEvents() { + return requestedCallEvents == null ? new EventSubscriptionType[0] : requestedCallEvents.clone(); + } + + /** + * Set the requestedCallEvents property: The requested call events to subscribe + * to. + * + * @param requestedCallEvents the requestedCallEvents value to set. + * @return the JoinCallOptions object itself. + */ + public JoinCallOptions setRequestedCallEvents(EventSubscriptionType[] requestedCallEvents) { + this.requestedCallEvents = requestedCallEvents == null ? new EventSubscriptionType[0] : requestedCallEvents.clone(); + return this; + } + + /** + * Initializes a new instance of JoinCallOptions. + * + * @param callbackUri the callback URI. + * @param requestedMediaTypes the requested media types. + * @param requestedCallEvents the requested call events to subscribe to. + * @throws IllegalArgumentException if any parameters are null. + */ + public JoinCallOptions(String callbackUri, + CallModality[] requestedMediaTypes, + EventSubscriptionType[] requestedCallEvents) { + if (callbackUri == null) { + throw new IllegalArgumentException("object callbackUri cannot be null"); + } + + if (requestedMediaTypes == null) { + throw new IllegalArgumentException("object requestedMediaTypes cannot be null"); + } + if (requestedCallEvents == null) { + throw new IllegalArgumentException("object requestedCallEvents cannot be null"); + } + + this.callbackUri = callbackUri; + + this.requestedMediaTypes = requestedMediaTypes.clone(); + this.requestedCallEvents = requestedCallEvents.clone(); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/JoinCallResult.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/JoinCallResult.java new file mode 100644 index 0000000000000..2e57e3b3c898f --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/JoinCallResult.java @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import com.azure.core.annotation.Immutable; + +/** The response payload of the join call operation. */ +@Immutable +public final class JoinCallResult { + /* + * The call connection id. + */ + private final String callConnectionId; + + /** + * Get the callConnectionId property: The call connection id. + * + * @return the callConnectionId value. + */ + public String getCallConnectionId() { + return callConnectionId; + } + + /** + * Initializes a new instance of JoinCallResult. + * + * @param callConnectionId the callConnectionId value to set. + */ + public JoinCallResult(String callConnectionId) { + this.callConnectionId = callConnectionId; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/OperationStatus.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/OperationStatus.java new file mode 100644 index 0000000000000..1c06cc1709063 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/OperationStatus.java @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.models; + +import com.azure.core.util.ExpandableStringEnum; +import com.fasterxml.jackson.annotation.JsonCreator; +import java.util.Collection; + +/** Defines values for OperationStatus. */ +public final class OperationStatus extends ExpandableStringEnum { + /** Static value notStarted for OperationStatus. */ + public static final OperationStatus NOT_STARTED = fromString("notStarted"); + + /** Static value running for OperationStatus. */ + public static final OperationStatus RUNNING = fromString("running"); + + /** Static value completed for OperationStatus. */ + public static final OperationStatus COMPLETED = fromString("completed"); + + /** Static value failed for OperationStatus. */ + public static final OperationStatus FAILED = fromString("failed"); + + /** + * Creates or finds a OperationStatus from its string representation. + * + * @param name a name to look for. + * @return the corresponding OperationStatus. + */ + @JsonCreator + public static OperationStatus fromString(String name) { + return fromString(name, OperationStatus.class); + } + + /** @return known OperationStatus values. */ + public static Collection values() { + return values(OperationStatus.class); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ParallelDownloadOptions.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ParallelDownloadOptions.java new file mode 100644 index 0000000000000..66519aad53ac2 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ParallelDownloadOptions.java @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import com.azure.communication.callingserver.implementation.Constants.ContentDownloader; +import com.azure.core.annotation.Fluent; + +import java.util.Locale; + +/** + * This class contains configuration used to parallelize data transfer operations. Note that not all values are used + * by every method which accepts this type. Please refer to the javadoc on specific methods for these cases. + */ +@Fluent +public final class ParallelDownloadOptions { + private static final String PARAMETER_NOT_IN_RANGE = "The value of the parameter '%s' should be between %s and %s."; + + private Long blockSize; + private Integer maxConcurrency; + private ProgressReceiver progressReceiver; + + /** + * Creates a new {@link ParallelDownloadOptions} with default parameters applied. + */ + public ParallelDownloadOptions() { + } + + /** + * Gets the block size (chunk size) to transfer at a time. + * @return The block size. + */ + public long getBlockSizeLong() { + return blockSize == null ? ContentDownloader.DEFAULT_BUFFER_SIZE : blockSize; + } + + + /** + * Sets the block size. + * For upload, The block size is the size of each block that will be staged. This value also determines the number + * of requests that need to be made. This parameter also determines the size that each buffer uses when buffering + * is required and consequently amount of memory consumed by such methods may be up to blockSize * numBuffers. + * For download to file, the block size is the size of each data chunk returned from the service. + * For both applications, If block size is large, upload will make fewer network calls, but each + * individual call will send more data and will therefore take longer. + * + * @param blockSize The block size. + * @return The ParallelDownloadOptions object itself. + */ + public ParallelDownloadOptions setBlockSizeLong(Long blockSize) { + if (blockSize != null) { + assertInBounds("blockSize", blockSize, 1, Long.MAX_VALUE); + } + this.blockSize = blockSize; + return this; + } + + /** + * Gets the Progress receiver for parallel reporting + * @return The progress reporter + */ + public ProgressReceiver getProgressReceiver() { + return progressReceiver; + } + + /** + * Sets the {@link ProgressReceiver}. + * + * @param progressReceiver The {@link ProgressReceiver}. + * @return The ParallelDownloadOptions object itself. + */ + public ParallelDownloadOptions setProgressReceiver(ProgressReceiver progressReceiver) { + this.progressReceiver = progressReceiver; + return this; + } + + /** + * Gets the maximum number of parallel requests that will be issued at any given time. + * @return The max concurrency value. + */ + public int getMaxConcurrency() { + return maxConcurrency == null ? ContentDownloader.DEFAULT_CONCURRENT_TRANSFERS_COUNT : maxConcurrency; + } + + /** + * @param maxConcurrency The maximum number of parallel requests that will be issued at any given time as a part of + * a single parallel transfer. This value applies per api. For example, if two calls to uploadFromFile are made at + * the same time, and each specifies a maxConcurrency of 5, there may be up to 10 outstanding, concurrent requests, + * up to 5 for each of the upload operations. For buffered uploads only, the maximum number of buffers to be + * allocated as part of the transfer will be {@code maxConcurrency + 1}. In those cases, memory will be allocated + * lazily as needed. The amount of memory consumed by methods which buffer may be up to blockSize * maxConcurrency. + * In general, upload methods which do not accept a length parameter must perform some buffering. + * @return The ParallelDownloadOptions object itself. + */ + public ParallelDownloadOptions setMaxConcurrency(Integer maxConcurrency) { + if (maxConcurrency != null) { + assertInBounds("numBuffers", maxConcurrency, 1, Integer.MAX_VALUE); + } + this.maxConcurrency = maxConcurrency; + return this; + } + + /** + * Asserts that the specified number is in the valid range. The range is inclusive. + * + * @param param Name of the parameter + * @param value Value of the parameter + * @param min The minimum allowed value + * @param max The maximum allowed value + * @throws IllegalArgumentException If {@code value} is less than {@code min} or {@code value} is greater than + * {@code max}. + */ + public static void assertInBounds(final String param, final long value, final long min, final long max) { + if (value < min || value > max) { + throw new IllegalArgumentException(String.format(Locale.ROOT, + PARAMETER_NOT_IN_RANGE, param, min, max)); + } + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/PlayAudioOptions.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/PlayAudioOptions.java new file mode 100644 index 0000000000000..a1844f9348f48 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/PlayAudioOptions.java @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import com.azure.core.annotation.Fluent; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The request payload for playing audio. */ +@Fluent +public final class PlayAudioOptions { + + /* + * The flag indicating whether audio file needs to be played in loop or not. + */ + @JsonProperty(value = "loop") + private Boolean loop; + + /* + * The value to identify context of the operation. + */ + @JsonProperty(value = "operationContext") + private String operationContext; + + /* + * An id for the media in the AudioFileUri, using which we cache the media resource. + */ + @JsonProperty(value = "audioFileId") + private String audioFileId; + + /* + * The callback Uri to receive playAudio status notifications. + */ + @JsonProperty(value = "callbackUri") + private String callbackUri; + + /** + * Get the loop property: The flag indicating whether audio file needs to be played in loop or not. + * + * @return the loop value. + */ + public Boolean isLoop() { + return loop; + } + + /** + * Set the loop property: The flag indicating whether audio file needs to be played in loop or not. + * + * @param loop the loop value to set. + * @return the PlayAudioOptions object itself. + */ + public PlayAudioOptions setLoop(Boolean loop) { + this.loop = loop; + return this; + } + + /** + * Get the operationContext property: The value to identify context of the operation. + * + * @return the operationContext value. + */ + public String getOperationContext() { + return operationContext; + } + + /** + * Set the operationContext property: The value to identify context of the operation. + * + * @param operationContext the operationContext value to set. + * @return the PlayAudioOptions object itself. + */ + public PlayAudioOptions setOperationContext(String operationContext) { + this.operationContext = operationContext; + return this; + } + + /** + * Get the audioFileId property: An id for the media in the AudioFileUri, using which we cache the media resource. + * + * @return the audioFileId value. + */ + public String getAudioFileId() { + return audioFileId; + } + + /** + * Set the audioFileId property: An id for the media in the AudioFileUri, using which we cache the media resource. + * + * @param audioFileId the audioFileId value to set. + * @return the PlayAudioOptions object itself. + */ + public PlayAudioOptions setAudioFileId(String audioFileId) { + this.audioFileId = audioFileId; + return this; + } + + /** + * Get the callbackUri property: The callback Uri to receive PlayAudio status notifications. + * + * @return the callbackUri value. + */ + public String getCallbackUri() { + return callbackUri; + } + + /** + * Set the callbackUri property: The callback Uri to receive PlayAudio status notifications. + * + * @param callbackUri the callbackUri value to set. + * @return the PlayAudioOptions object itself. + */ + public PlayAudioOptions setCallbackUri(String callbackUri) { + this.callbackUri = callbackUri; + return this; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/PlayAudioResult.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/PlayAudioResult.java new file mode 100644 index 0000000000000..a898dd168d30a --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/PlayAudioResult.java @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import com.azure.core.annotation.Immutable; + +/** The response payload for play audio operation. */ +@Immutable +public final class PlayAudioResult { + /* + * The identifier. + */ + private final String id; + + /* + * The status of the operation + */ + private final OperationStatus status; + + /* + * The operation context + */ + private final String operationContext; + + /* + * The result info + */ + private final ResultInfo resultInfo; + + /** + * Get the id property: Gets or sets the identifier. + * + * @return the id value. + */ + public String getId() { + return this.id; + } + + /** + * Get the status property: Gets or sets the status of the operation. + * + * @return the status value. + */ + public OperationStatus getStatus() { + return status; + } + + /** + * Get the operationContext property: Gets or sets the operation context. + * + * @return the operationContext value. + */ + public String getOperationContext() { + return operationContext; + } + + /** + * Get the resultInfo property: Gets or sets the result info. + * + * @return the resultInfo value. + */ + public ResultInfo getResultInfo() { + return resultInfo; + } + + /** + * Initializes a new instance of PlayAudioResult. + * + * @param id the id value. + * @param status the status value. + * @param operationContext the operationContext value. + * @param resultInfo the resultInfo value. + */ + public PlayAudioResult(String id, OperationStatus status, String operationContext, ResultInfo resultInfo) { + this.id = id; + this.status = status; + this.operationContext = operationContext; + this.resultInfo = resultInfo; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ProgressReceiver.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ProgressReceiver.java new file mode 100644 index 0000000000000..6e300e663bcce --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ProgressReceiver.java @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import reactor.core.publisher.Flux; + +/** + * A {@code ProgressReceiver} is an object that can be used to report progress on network transfers. When specified on + * transfer operations, the {@code reportProgress} method will be called periodically with the total number of bytes + * transferred. The user may configure this method to report progress in whatever format desired. It is recommended + * that this type be used in conjunction with {@link ProgressReporter#addProgressReporting(Flux, ProgressReceiver)} to + * enable reporting on sequential transfers. Note that any method accepting a {@link ParallelDownloadOptions} will use + * the {@code ProgressReceiver} specified there and will handle the logic to coordinate the reporting between parallel + * operations. + */ +public interface ProgressReceiver { + + /** + * The callback function invoked as progress is reported. + * + * @param bytesTransferred The total number of bytes transferred during this transaction. + */ + void reportProgress(long bytesTransferred); +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ProgressReporter.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ProgressReporter.java new file mode 100644 index 0000000000000..79a5e62f2e13c --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ProgressReporter.java @@ -0,0 +1,175 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; + +/** + * {@code ProgressReporter} offers a convenient way to add progress tracking to a given Flux. + */ +public final class ProgressReporter { + + private abstract static class ProgressReporterImpl implements ProgressReceiver { + long blockProgress; + + final ProgressReceiver progressReceiver; + + ProgressReporterImpl(ProgressReceiver progressReceiver) { + this.blockProgress = 0; + this.progressReceiver = progressReceiver; + } + + @Override + public void reportProgress(long bytesTransferred) { + blockProgress += bytesTransferred; + } + + void rewindProgress() { + blockProgress = 0; + } + + Flux addProgressReporting(Flux data) { + return Mono.just(this).flatMapMany(progressReporter -> { + /* + Each time there is a new subscription, we will rewind the progress. This is desirable specifically + for retries, which resubscribe on each try. The first time this Flux is subscribed to, the + rewind will be a noop as there will have been no progress made. Subsequent rewinds will work as + expected. + */ + progressReporter.rewindProgress(); + + /* + Every time we emit some data, report it to the Tracker, which will pass it on to the end user. + */ + return data.doOnNext(buffer -> progressReporter.reportProgress(buffer.remaining())); + }); + } + } + + /** + * This type is used to keep track of the total amount of data transferred for a single request. This is the type we + * will use when the customer uses the factory to add progress reporting to their Flowable. We need this additional + * type because we can't keep local state directly as lambdas require captured local variables to be effectively + * final. + */ + private static class SequentialProgressReporter extends ProgressReporterImpl { + SequentialProgressReporter(ProgressReceiver progressReceiver) { + super(progressReceiver); + } + + @Override + public void reportProgress(long bytesTransferred) { + super.reportProgress(bytesTransferred); + progressReceiver.reportProgress(blockProgress); + } + } + + /** + * This type is used to keep track of the total amount of data transferred as a part of a parallel upload in order + * to coordinate progress reporting to the end user. We need this additional type because we can't keep local state + * directly as lambdas require captured local variables to be effectively final. + */ + private static class ParallelProgressReporter extends ProgressReporterImpl { + /* + This lock will be instantiated by the operation initiating the whole transfer to coordinate each + ProgressReporterImpl. + */ + private final Lock transferLock; + + /* + We need an AtomicLong to be able to update the value referenced. Because we are already synchronizing with the + lock, we don't incur any additional performance hit here by the synchronization. + */ + private final AtomicLong totalProgress; + + ParallelProgressReporter(ProgressReceiver progressReceiver, Lock lock, AtomicLong totalProgress) { + super(progressReceiver); + this.transferLock = lock; + this.totalProgress = totalProgress; + } + + @Override + public void reportProgress(long bytesTransferred) { + super.reportProgress(bytesTransferred); + + /* + It is typically a bad idea to lock around customer code (which the progressReceiver is) because they could + never release the lock. However, we have decided that it is sufficiently difficult for them to make their + progressReporting code threadsafe that we will take that burden and the ensuing risks. Although it is the + case that only one thread is allowed to be in onNext at once, there are multiple independent + requests happening at once to stage/download separate chunks, so we still need to lock either way. + */ + transferLock.lock(); + try { + progressReceiver.reportProgress(totalProgress.addAndGet(bytesTransferred)); + } finally { + transferLock.unlock(); + } + } + + /* + This is used in the case of retries to rewind the amount of progress reported so as not to over-report at the + end. + */ + @Override + public void rewindProgress() { + /* + Blocks do not interfere with each other's block progress and there is no way that, for a single block, one + thread will be trying to add to the progress while the other is trying to zero it. The updates are strictly + sequential. Avoiding using the lock is ideal. + */ + this.totalProgress.addAndGet(-1 * blockProgress); + super.rewindProgress(); + } + + } + + /** + * Adds progress reporting functionality to the given {@code Flux}. Each subscription (and therefore each retry) + * will rewind the progress reported so as not to over-report. The data reported will be the total amount of data + * emitted so far, or the "current position" of the Flux. + * + * @param data The data whose transfer progress is to be tracked. + * @param progressReceiver {@link ProgressReceiver} + * @return A {@code Flux} that emits the same data as the source but calls a callback to report the total amount of + * data emitted so far. + */ + public static Flux addProgressReporting(Flux data, ProgressReceiver progressReceiver) { + if (progressReceiver == null) { + return data; + } else { + ProgressReporterImpl tracker = new SequentialProgressReporter(progressReceiver); + return tracker.addProgressReporting(data); + } + } + + /** + * Adds parallel progress reporting functionality to the given {@code Flux}. Each subscription (and therefore each + * retry) will rewind the progress reported so as not to over-report. The data reported will be the total amount + * of data emitted so far, or the "current position" of the Flux in parallel. + * + * @param data The data whose transfer progress is to be tracked. + * @param progressReceiver {@link ProgressReceiver} + * @param lock This lock will be instantiated by the operation initiating the whole transfer to coordinate each + * ProgressReporterImpl. + * @param totalProgress We need an AtomicLong to be able to update the value referenced. Because we are already + * synchronizing with the lock, we don't incur any additional performance hit here by the synchronization. + * @return A {@code Flux} that emits the same data as the source but calls a callback to report the total amount of + * data emitted so far. + */ + public static Flux addParallelProgressReporting(Flux data, + ProgressReceiver progressReceiver, Lock lock, AtomicLong totalProgress) { + if (progressReceiver == null) { + return data; + } else { + ParallelProgressReporter tracker = new ParallelProgressReporter(progressReceiver, lock, totalProgress); + return tracker.addProgressReporting(data); + } + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ResultInfo.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ResultInfo.java new file mode 100644 index 0000000000000..6e65263260e44 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ResultInfo.java @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import com.azure.core.annotation.Immutable; + +/** Result info class to be used to report result status for actions/operations. */ +@Immutable +public final class ResultInfo { + /* + * The result code + * For synchronous failures, this maps one-to-one with HTTP responses. For + * asynchronous failures or messages, it is contextual. + */ + private final Integer code; + + /* + * The result subcode. + * The subcode further classifies a failure. For example. + */ + private final Integer subcode; + + /* + * The message + * The message is a detail explanation of subcode. + */ + private final String message; + + /** + * Get the code property: Gets the result code For synchronous failures, this maps one-to-one with HTTP + * responses. For asynchronous failures or messages, it is contextual. + * + * @return the code value. + */ + public Integer getCode() { + return code; + } + + /** + * Get the subcode property: Gets the result subcode. The subcode further classifies a failure. + * + * @return the subcode value. + */ + public Integer getSubcode() { + return subcode; + } + + /** + * Get the message property: Gets the message The message is a detail explanation of subcode. + * + * @return the message value. + */ + public String getMessage() { + return message; + } + + /** + * Initializes a new instance of ResultInfo. + * + * @param code the code value. + * @param subcode the subcode value. + * @param message the message value. + */ + public ResultInfo(Integer code, Integer subcode, String message) { + this.code = code; + this.subcode = subcode; + this.message = message; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/StartCallRecordingResult.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/StartCallRecordingResult.java new file mode 100644 index 0000000000000..73acc2b978e11 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/StartCallRecordingResult.java @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import com.azure.core.annotation.Immutable; + +/** The response payload of start call recording operation. */ +@Immutable +public final class StartCallRecordingResult { + /* + * The recording id + */ + private final String recordingId; + + /** + * Get the recordingId property: The recording id of the started recording. + * + * @return the recordingId value. + */ + public String getRecordingId() { + return recordingId; + } + + /** + * Initializes a new instance of StartCallRecordingResult. + * + * @param recordingId the recordingId value. + */ + public StartCallRecordingResult(String recordingId) { + this.recordingId = recordingId; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ToneInfo.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ToneInfo.java new file mode 100644 index 0000000000000..e15bd5949bed7 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ToneInfo.java @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models; + +import com.azure.core.annotation.Immutable; + +/** The class to represent Tone info detail. */ +@Immutable +public final class ToneInfo { + /* + * The sequence id. This id can be used to determine if the same tone + * was played multiple times or if any tones were missed. + */ + private final Integer sequenceId; + + /* + * The tone detected. + */ + private final ToneValue tone; + + /** + * Get the sequenceId property: Gets the sequence id. This id can be used to determine if the same tone was + * played multiple times or if any tones were missed. + * + * @return the sequenceId value. + */ + public Integer getSequenceId() { + return sequenceId; + } + + /** + * Get the tone property: Gets the tone detected. + * + * @return the tone value. + */ + public ToneValue getTone() { + return tone; + } + + /** + * Initializes a new instance of ToneInfo. + * + * @param sequenceId the sequenceId value. + * @param tone the tone value. + */ + public ToneInfo(Integer sequenceId, ToneValue tone) { + this.sequenceId = sequenceId; + this.tone = tone; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ToneValue.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ToneValue.java new file mode 100644 index 0000000000000..36e6200a37a52 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/ToneValue.java @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +package com.azure.communication.callingserver.models; + +import com.azure.core.util.ExpandableStringEnum; +import com.fasterxml.jackson.annotation.JsonCreator; +import java.util.Collection; + +/** Defines values for ToneValue. */ +public final class ToneValue extends ExpandableStringEnum { + /** Static value tone0 for ToneValue. */ + public static final ToneValue TONE0 = fromString("tone0"); + + /** Static value tone1 for ToneValue. */ + public static final ToneValue TONE1 = fromString("tone1"); + + /** Static value tone2 for ToneValue. */ + public static final ToneValue TONE2 = fromString("tone2"); + + /** Static value tone3 for ToneValue. */ + public static final ToneValue TONE3 = fromString("tone3"); + + /** Static value tone4 for ToneValue. */ + public static final ToneValue TONE4 = fromString("tone4"); + + /** Static value tone5 for ToneValue. */ + public static final ToneValue TONE5 = fromString("tone5"); + + /** Static value tone6 for ToneValue. */ + public static final ToneValue TONE6 = fromString("tone6"); + + /** Static value tone7 for ToneValue. */ + public static final ToneValue TONE7 = fromString("tone7"); + + /** Static value tone8 for ToneValue. */ + public static final ToneValue TONE8 = fromString("tone8"); + + /** Static value tone9 for ToneValue. */ + public static final ToneValue TONE9 = fromString("tone9"); + + /** Static value star for ToneValue. */ + public static final ToneValue STAR = fromString("star"); + + /** Static value pound for ToneValue. */ + public static final ToneValue POUND = fromString("pound"); + + /** Static value a for ToneValue. */ + public static final ToneValue A = fromString("a"); + + /** Static value b for ToneValue. */ + public static final ToneValue B = fromString("b"); + + /** Static value c for ToneValue. */ + public static final ToneValue C = fromString("c"); + + /** Static value d for ToneValue. */ + public static final ToneValue D = fromString("d"); + + /** Static value flash for ToneValue. */ + public static final ToneValue FLASH = fromString("flash"); + + /** + * Creates or finds a ToneValue from its string representation. + * + * @param name a name to look for. + * @return the corresponding ToneValue. + */ + @JsonCreator + public static ToneValue fromString(String name) { + return fromString(name, ToneValue.class); + } + + /** @return known ToneValue values. */ + public static Collection values() { + return values(ToneValue.class); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/CallConnectionStateChangedEvent.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/CallConnectionStateChangedEvent.java new file mode 100644 index 0000000000000..16c0658e2900f --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/CallConnectionStateChangedEvent.java @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models.events; + +import com.azure.communication.callingserver.implementation.models.CallConnectionStateChangedEventInternal; +import com.azure.communication.callingserver.models.CallConnectionState; +import com.azure.core.annotation.Immutable; +import com.azure.core.util.BinaryData; + +/** The call connection state changed event. */ +@Immutable +public final class CallConnectionStateChangedEvent extends CallingServerEventBase { + /* + * The server call id. + */ + private final String serverCallId; + + /* + * The call connection id. + */ + private final String callConnectionId; + + /* + * The call connection state. + */ + private final CallConnectionState callConnectionState; + + /** + * Get the serverCallId property: The server call id. + * + * @return the serverCallId value. + */ + public String getServerCallId() { + return serverCallId; + } + + /** + * Get the callConnectionId property: The call connection id. + * + * @return the callConnectionId value. + */ + public String getCallConnectionId() { + return callConnectionId; + } + + /** + * Get the callConnectionState property: The call connection state. + * + * @return the callConnectionState value. + */ + public CallConnectionState getCallConnectionState() { + return callConnectionState; + } + + /** + * Initializes a new instance of CallConnectionStateChangedEvent. + * + * @param serverCallId the serverCallId value. + * @param callConnectionId the callConnectionId value. + * @param callConnectionState the callConnectionState value. + */ + public CallConnectionStateChangedEvent( + String serverCallId, + String callConnectionId, + CallConnectionState callConnectionState) { + this.serverCallId = serverCallId; + this.callConnectionId = callConnectionId; + this.callConnectionState = callConnectionState; + } + + /** + * Deserialize {@link CallConnectionStateChangedEvent} event. + * + * @param eventData binary data for event + * @return {@link CallConnectionStateChangedEvent} event. + */ + public static CallConnectionStateChangedEvent deserialize(BinaryData eventData) { + if (eventData == null) { + return null; + } + CallConnectionStateChangedEventInternal callConnectionStateChangedEventInternal = + eventData.toObject(CallConnectionStateChangedEventInternal.class); + + return new CallConnectionStateChangedEvent( + callConnectionStateChangedEventInternal.getServerCallId(), + callConnectionStateChangedEventInternal.getCallConnectionId(), + callConnectionStateChangedEventInternal.getCallConnectionState()); + } +} + + + diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/CallRecordingStateChangeEvent.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/CallRecordingStateChangeEvent.java new file mode 100644 index 0000000000000..317b7db464b39 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/CallRecordingStateChangeEvent.java @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models.events; + +import com.azure.communication.callingserver.implementation.models.CallRecordingStateChangeEventInternal; +import com.azure.communication.callingserver.models.CallRecordingState; +import com.azure.core.annotation.Immutable; +import com.azure.core.util.BinaryData; + +import java.time.OffsetDateTime; + +/** The call recording state change event. */ +@Immutable +public final class CallRecordingStateChangeEvent extends CallingServerEventBase { + /* + * The call recording id + */ + private final String recordingId; + + /* + * The state of the recording + */ + private final CallRecordingState state; + + /* + * The time the recording started + */ + private final OffsetDateTime startDateTime; + + /* + * The server call id. + */ + private final String serverCallId; + + /** + * Get the recordingId property: The call recording id. + * + * @return the recordingId value. + */ + public String getRecordingId() { + return recordingId; + } + + /** + * Get the state property: The state of the recording. + * + * @return the state value. + */ + public CallRecordingState getState() { + return state; + } + + /** + * Get the startDateTime property: The time the recording started. + * + * @return the startDateTime value. + */ + public OffsetDateTime getStartDateTime() { + return startDateTime; + } + + /** + * Get the serverCallId property: The server call id. + * + * @return the serverCallId value. + */ + public String getServerCallId() { + return serverCallId; + } + + /** + * Initializes a new instance of CallRecordingStateChangeEvent. + * + * @param recordingId the recordingId value. + * @param state the state value. + * @param startDateTime the startDateTime value. + * @param serverCallId the serverCallId value. + */ + public CallRecordingStateChangeEvent( + String recordingId, + CallRecordingState state, + OffsetDateTime startDateTime, + String serverCallId) { + this.recordingId = recordingId; + this.state = state; + this.startDateTime = startDateTime; + this.serverCallId = serverCallId; + } + + /** + * Deserialize {@link CallRecordingStateChangeEvent} event. + * + * @param eventData binary data for event + * @return {@link CallRecordingStateChangeEvent} event. + */ + public static CallRecordingStateChangeEvent deserialize(BinaryData eventData) { + if (eventData == null) { + return null; + } + CallRecordingStateChangeEventInternal callRecordingStateChangeEventInternal = + eventData.toObject(CallRecordingStateChangeEventInternal.class); + + return new CallRecordingStateChangeEvent( + callRecordingStateChangeEventInternal.getRecordingId(), + callRecordingStateChangeEventInternal.getState(), + callRecordingStateChangeEventInternal.getStartDateTime(), + callRecordingStateChangeEventInternal.getServerCallId()); + } +} + diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/CallingServerEventBase.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/CallingServerEventBase.java new file mode 100644 index 0000000000000..0915bd89a48b6 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/CallingServerEventBase.java @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models.events; + +/** + * Base type for all calling events. + */ +public abstract class CallingServerEventBase { + +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/CallingServerEventType.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/CallingServerEventType.java new file mode 100644 index 0000000000000..d054b8f7a7958 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/CallingServerEventType.java @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models.events; + +import com.azure.core.annotation.Immutable; +import com.azure.core.util.ExpandableStringEnum; +import com.fasterxml.jackson.annotation.JsonCreator; + +import java.util.Collection; + +/** Defines values for CallingServerEventType. */ +@Immutable +public final class CallingServerEventType extends ExpandableStringEnum { + + /** The call connection state change event type. */ + public static final CallingServerEventType CALL_CONNECTION_STATE_CHANGED_EVENT = fromString("Microsoft.Communication.CallConnectionStateChanged"); + + /** The invited participants result event type. */ + public static final CallingServerEventType INVITE_PARTICIPANT_RESULT_EVENT = fromString("Microsoft.Communication.InviteParticipantResult"); + + /** The call recording state change event type. */ + public static final CallingServerEventType CALL_RECORDING_STATE_CHANGED_EVENT = fromString("Microsoft.Communication.CallRecordingStateChanged"); + + /** The play audio result event type. */ + public static final CallingServerEventType PLAY_AUDIO_RESULT_EVENT = fromString("Microsoft.Communication.PlayAudioResult"); + + /** The participants updated event type. */ + public static final CallingServerEventType PARTICIPANTS_UPDATED_EVENT = fromString("Microsoft.Communication.ParticipantsUpdated"); + + /** The subscribe to tone event type. */ + public static final CallingServerEventType TONE_RECEIVED_EVENT = fromString("Microsoft.Communication.DtmfReceived"); + + /** + * Creates or finds a CallingServerEventType from its string representation. + * + * @param name a name to look for. + * @return the corresponding CallingServerEventType. + */ + @JsonCreator + public static CallingServerEventType fromString(String name) { + return fromString(name, CallingServerEventType.class); + } + + /** @return known CallModality values. */ + public static Collection values() { + return values(CallingServerEventType.class); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/InviteParticipantResultEvent.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/InviteParticipantResultEvent.java new file mode 100644 index 0000000000000..1b7b05a5c54bf --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/InviteParticipantResultEvent.java @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models.events; + +import com.azure.communication.callingserver.implementation.converters.ResultInfoConverter; +import com.azure.communication.callingserver.implementation.models.InviteParticipantsResultEventInternal; +import com.azure.communication.callingserver.models.OperationStatus; +import com.azure.communication.callingserver.models.ResultInfo; +import com.azure.core.annotation.Immutable; +import com.azure.core.util.BinaryData; + +/** The invite participant result event. */ +@Immutable +public final class InviteParticipantResultEvent extends CallingServerEventBase { + /* + * The result details. + */ + private final ResultInfo resultInfo; + + /* + * The operation context. + */ + private final String operationContext; + + /* + * The status of the operation + */ + private final OperationStatus status; + + /** + * Get the resultInfo property: The result details. + * + * @return the resultInfo value. + */ + public ResultInfo getResultInfo() { + return resultInfo; + } + + /** + * Get the operationContext property: The operation context. + * + * @return the operationContext value. + */ + public String getOperationContext() { + return operationContext; + } + + /** + * Get the status property: Gets the status of the operation. + * + * @return the operation status value. + */ + public OperationStatus getStatus() { + return status; + } + + /** + * Initializes a new instance of InviteParticipantResultEvent. + * + * @param resultInfo the resultInfo value. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @param status the status value. + */ + public InviteParticipantResultEvent(ResultInfo resultInfo, String operationContext, OperationStatus status) { + this.resultInfo = resultInfo; + this.operationContext = operationContext; + this.status = status; + } + + /** + * Deserialize {@link InviteParticipantResultEvent} event. + * + * @param eventData binary data for event + * @return {@link InviteParticipantResultEvent} event. + */ + public static InviteParticipantResultEvent deserialize(BinaryData eventData) { + if (eventData == null) { + return null; + } + InviteParticipantsResultEventInternal inviteParticipantsResultEventInternal = + eventData.toObject(InviteParticipantsResultEventInternal.class); + + return new InviteParticipantResultEvent( + ResultInfoConverter.convert(inviteParticipantsResultEventInternal.getResultInfo()), + inviteParticipantsResultEventInternal.getOperationContext(), + inviteParticipantsResultEventInternal.getStatus()); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/ParticipantsUpdatedEvent.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/ParticipantsUpdatedEvent.java new file mode 100644 index 0000000000000..f7fffde2d5d5d --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/ParticipantsUpdatedEvent.java @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models.events; + +import com.azure.communication.callingserver.implementation.converters.CommunicationIdentifierConverter; +import com.azure.communication.callingserver.implementation.models.CommunicationParticipantInternal; +import com.azure.communication.callingserver.implementation.models.ParticipantsUpdatedEventInternal; +import com.azure.communication.callingserver.models.CallParticipant; +import com.azure.core.annotation.Immutable; +import com.azure.core.util.BinaryData; + +import java.util.LinkedList; +import java.util.List; + +/** + * The participants updated event. + */ +@Immutable +public final class ParticipantsUpdatedEvent extends CallingServerEventBase { + /** + * The call connection id. + */ + private final String callConnectionId; + + /** + * The participants. + */ + private final CallParticipant[] participants; + + /** + * Get the callConnectionId property: The call connection id. + * + * @return the callConnectionId value. + */ + public String getCallConnectionId() { + return callConnectionId; + } + + + /** + * Get the participants. + * + * @return the list of participants value. + */ + public CallParticipant[] getParticipants() { + return participants == null ? new CallParticipant[0] : participants.clone(); + } + + /** + * Initializes a new instance of ParticipantsUpdatedEvent. + * + * @param callConnectionId The call connection id. + * @param participants The participants + * @throws IllegalArgumentException if any parameter is null or empty. + */ + public ParticipantsUpdatedEvent(String callConnectionId, CallParticipant[] participants) { + if (callConnectionId == null || callConnectionId.isEmpty()) { + throw new IllegalArgumentException("object callConnectionId cannot be null or empty"); + } + if (participants == null) { + throw new IllegalArgumentException("object participants cannot be null"); + } + this.callConnectionId = callConnectionId; + this.participants = participants.clone(); + } + + /** + * Deserialize {@link ParticipantsUpdatedEvent} event. + * + * @param eventData binary data for event + * @return {@link ParticipantsUpdatedEvent} event. + */ + public static ParticipantsUpdatedEvent deserialize(BinaryData eventData) { + if (eventData == null) { + return null; + } + ParticipantsUpdatedEventInternal internalEvent = eventData.toObject(ParticipantsUpdatedEventInternal.class); + List participants = new LinkedList<>(); + for (CommunicationParticipantInternal communicationParticipantInternal : internalEvent.getParticipants()) { + participants.add( + new CallParticipant( + CommunicationIdentifierConverter.convert(communicationParticipantInternal.getIdentifier()), + communicationParticipantInternal.getParticipantId(), + communicationParticipantInternal.isMuted())); + } + + return new ParticipantsUpdatedEvent(internalEvent.getCallConnectionId(), + participants.toArray(new CallParticipant[0])); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/PlayAudioResultEvent.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/PlayAudioResultEvent.java new file mode 100644 index 0000000000000..7f4149dc54093 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/PlayAudioResultEvent.java @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models.events; + +import com.azure.communication.callingserver.implementation.converters.ResultInfoConverter; +import com.azure.communication.callingserver.implementation.models.PlayAudioResultEventInternal; +import com.azure.communication.callingserver.models.OperationStatus; +import com.azure.communication.callingserver.models.ResultInfo; +import com.azure.core.annotation.Immutable; +import com.azure.core.util.BinaryData; + +/** The play audio result event. */ +@Immutable +public final class PlayAudioResultEvent extends CallingServerEventBase { + /* + * The result details. + */ + private final ResultInfo resultInfo; + + /* + * The operation context. + */ + private final String operationContext; + + /* + * The status of the operation + */ + private final OperationStatus status; + + /** + * Get the resultInfo property: The result details. + * + * @return the resultInfo value. + */ + public ResultInfo getResultInfo() { + return resultInfo; + } + + /** + * Get the operationContext property: The operation context. + * + * @return the operationContext value. + */ + public String getOperationContext() { + return operationContext; + } + + /** + * Get the status property: Gets the status of the operation. + * + * @return the status value. + */ + public OperationStatus getStatus() { + return status; + } + + /** + * Initializes a new instance of PlayAudioResultEvent. + * + * @param resultInfo the resultInfo value. + * @param operationContext The value to identify context of the operation. This is used to co-relate other + * communications related to this operation + * @param status the status value. + */ + public PlayAudioResultEvent(ResultInfo resultInfo, String operationContext, OperationStatus status) { + this.resultInfo = resultInfo; + this.operationContext = operationContext; + this.status = status; + } + + /** + * Deserialize {@link PlayAudioResultEvent} event. + * + * @param eventData binary data for event + * @return {@link PlayAudioResultEvent} event. + */ + public static PlayAudioResultEvent deserialize(BinaryData eventData) { + if (eventData == null) { + return null; + } + PlayAudioResultEventInternal playAudioResultEventInternal = + eventData.toObject(PlayAudioResultEventInternal.class); + + return new PlayAudioResultEvent( + ResultInfoConverter.convert(playAudioResultEventInternal.getResultInfo()), + playAudioResultEventInternal.getOperationContext(), + playAudioResultEventInternal.getStatus()); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/ToneReceivedEvent.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/ToneReceivedEvent.java new file mode 100644 index 0000000000000..866fbcc4e0aec --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/ToneReceivedEvent.java @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver.models.events; + +import com.azure.communication.callingserver.implementation.models.ToneReceivedEventInternal; +import com.azure.communication.callingserver.models.ToneInfo; +import com.azure.core.annotation.Immutable; +import com.azure.core.util.BinaryData; + +/** The subscribe to tone event. */ +@Immutable +public final class ToneReceivedEvent extends CallingServerEventBase { + /* + * The tone info. + */ + private final ToneInfo toneInfo; + + /* + * The call connection id. + */ + private final String callConnectionId; + + /** + * Get the toneInfo property: The tone info. + * + * @return the toneInfo value. + */ + public ToneInfo getToneInfo() { + return toneInfo; + } + + /** + * Get the callConnectionId property: The call connection id. + * + * @return the callConnectionId value. + */ + public String getCallConnectionId() { + return callConnectionId; + } + + /** + * Initializes a new instance of ToneReceivedEvent. + * + * @param toneInfo the toneInfo value. + * @param callConnectionId the callConnectionId value. + */ + public ToneReceivedEvent(ToneInfo toneInfo, String callConnectionId) { + this.toneInfo = toneInfo; + this.callConnectionId = callConnectionId; + } + + /** + * Deserialize {@link ToneReceivedEvent} event. + * + * @param eventData binary data for event + * @return {@link ToneReceivedEvent} event. + */ + public static ToneReceivedEvent deserialize(BinaryData eventData) { + if (eventData == null) { + return null; + } + ToneReceivedEventInternal toneReceivedEventInternal = eventData.toObject(ToneReceivedEventInternal.class); + + return new ToneReceivedEvent( + new ToneInfo( + toneReceivedEventInternal.getToneInfo().getSequenceId(), + toneReceivedEventInternal.getToneInfo().getTone()), + toneReceivedEventInternal.getCallConnectionId()); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/package-info.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/package-info.java new file mode 100644 index 0000000000000..072ceff8612ca --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/events/package-info.java @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/** + * Package containing the data models for AzureCommunicationCallingServerService. Azure Communication CallingServer + * Service. + */ +package com.azure.communication.callingserver.models.events; diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/package-info.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/package-info.java new file mode 100644 index 0000000000000..e600ceee2b135 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/models/package-info.java @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. + +/** Package containing classes for AzureCommunicationCallingServerService. Azure Communication CallingServer Service. */ +package com.azure.communication.callingserver.models; diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/package-info.java b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/package-info.java new file mode 100644 index 0000000000000..5ac3f12cf0495 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/com/azure/communication/callingserver/package-info.java @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/** Package containing the classes for AzureCommunicationIdentity. Azure Communication Identity Service. */ +package com.azure.communication.callingserver; diff --git a/sdk/communication/azure-communication-callingserver/src/main/java/module-info.java b/sdk/communication/azure-communication-callingserver/src/main/java/module-info.java new file mode 100644 index 0000000000000..b3ead38e94a3b --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/java/module-info.java @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module com.azure.communication.callingserver { + + requires transitive com.azure.communication.common; + + // public API surface area + exports com.azure.communication.callingserver; + exports com.azure.communication.callingserver.models; + exports com.azure.communication.callingserver.models.events; + + // exporting some packages specifically for Jackson + opens com.azure.communication.callingserver.models to com.fasterxml.jackson.databind; + opens com.azure.communication.callingserver.implementation.models to com.fasterxml.jackson.databind, com.azure.core; +} diff --git a/sdk/communication/azure-communication-callingserver/src/main/resources/azure-communication-callingserver.properties b/sdk/communication/azure-communication-callingserver/src/main/resources/azure-communication-callingserver.properties new file mode 100644 index 0000000000000..ca812989b4f27 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/main/resources/azure-communication-callingserver.properties @@ -0,0 +1,2 @@ +name=${project.artifactId} +version=${project.version} diff --git a/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/CallingServerAsyncClientJavaDocCodeSnippets.java b/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/CallingServerAsyncClientJavaDocCodeSnippets.java new file mode 100644 index 0000000000000..764bbf8f088ee --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/CallingServerAsyncClientJavaDocCodeSnippets.java @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.models.CallModality; +import com.azure.communication.callingserver.models.CreateCallOptions; +import com.azure.communication.callingserver.models.EventSubscriptionType; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.communication.common.CommunicationUserIdentifier; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpPipelineBuilder; + +public class CallingServerAsyncClientJavaDocCodeSnippets { + public CallingServerAsyncClient createCallingServerAsyncClientWithPipeline() { + + String connectionString = getConnectionString(); + // BEGIN: com.azure.communication.callingserver.CallingServerAsyncClient.pipeline.instantiation + HttpPipeline pipeline = new HttpPipelineBuilder() + .policies(/* add policies */) + .build(); + + CallingServerAsyncClient callingServerAsyncClient = new CallingServerClientBuilder() + .pipeline(pipeline) + .connectionString(connectionString) + .buildAsyncClient(); + // END: com.azure.communication.callingserver.CallingServerAsyncClient.pipeline.instantiation + return callingServerAsyncClient; + } + + private String getConnectionString() { + return "endpoint=https://.communication.azure.com/;accesskey="; + } + + /** + * Sample code for creating a call connection using the async call client. + */ + public void createCallConnectionAsync() { + CallingServerAsyncClient callingServerAsyncClient = createCallingServerAsyncClientWithPipeline(); + + CommunicationIdentifier source = new CommunicationUserIdentifier(""); + CommunicationIdentifier firstCallee = new CommunicationUserIdentifier(""); + CommunicationIdentifier secondCallee = new CommunicationUserIdentifier(""); + String callbackUri = ""; + + // BEGIN: com.azure.communication.callingserver.CallingServerAsyncClient.create.call.connection.async + CommunicationIdentifier[] targets = new CommunicationIdentifier[] { firstCallee, secondCallee }; + CallModality[] requestedMediaTypes = new CallModality[] { CallModality.AUDIO, CallModality.VIDEO }; + EventSubscriptionType[] requestedCallEvents = new EventSubscriptionType[] { + EventSubscriptionType.DTMF_RECEIVED, + EventSubscriptionType.PARTICIPANTS_UPDATED + }; + CreateCallOptions createCallOptions = new CreateCallOptions( + callbackUri, + requestedMediaTypes, + requestedCallEvents); + CallConnectionAsync callAsyncConnection = callingServerAsyncClient + .createCallConnection(source, targets, createCallOptions).block(); + // END: com.azure.communication.callingserver.CallingServerAsyncClient.create.call.connection.async + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/CallingServerClientBuilderJavaDocCodeSnippets.java b/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/CallingServerClientBuilderJavaDocCodeSnippets.java new file mode 100644 index 0000000000000..ab9dfa3bab0ba --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/CallingServerClientBuilderJavaDocCodeSnippets.java @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpPipelineBuilder; + +public class CallingServerClientBuilderJavaDocCodeSnippets { + public void createCallingServerClientWithPipeline() { + + String connectionString = getConnectionString(); + // BEGIN: com.azure.communication.callingserver.CallingServerClientBuilder.pipeline.instantiation + HttpPipeline pipeline = new HttpPipelineBuilder() + .policies(/* add policies */) + .build(); + + CallingServerClient callingServerClient = new CallingServerClientBuilder() + .pipeline(pipeline) + .connectionString(connectionString) + .buildClient(); + + CallingServerAsyncClient callingServerAsyncClient = new CallingServerClientBuilder() + .pipeline(pipeline) + .connectionString(connectionString) + .buildAsyncClient(); + // END: com.azure.communication.callingserver.CallingServerClientBuilder.pipeline.instantiation + + } + + private String getConnectionString() { + return "endpoint=https://.communication.azure.com/;accesskey="; + } + +} diff --git a/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/CallingServerClientJavaDocCodeSnippets.java b/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/CallingServerClientJavaDocCodeSnippets.java new file mode 100644 index 0000000000000..fc53f927cdf60 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/CallingServerClientJavaDocCodeSnippets.java @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.models.CallModality; +import com.azure.communication.callingserver.models.CreateCallOptions; +import com.azure.communication.callingserver.models.EventSubscriptionType; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.communication.common.CommunicationUserIdentifier; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpPipelineBuilder; + +public class CallingServerClientJavaDocCodeSnippets { + public CallingServerClient createCallingServerClientWithPipeline() { + + String connectionString = getConnectionString(); + // BEGIN: com.azure.communication.callingserver.CallingServerClient.pipeline.instantiation + HttpPipeline pipeline = new HttpPipelineBuilder() + .policies(/* add policies */) + .build(); + + CallingServerClient callingServerClient = new CallingServerClientBuilder() + .pipeline(pipeline) + .connectionString(connectionString) + .buildClient(); + // END: com.azure.communication.callingserver.CallingServerClient.pipeline.instantiation + return callingServerClient; + } + + private String getConnectionString() { + return "endpoint=https://.communication.azure.com/;accesskey="; + } + + /** + * Sample code for creating a call connection using the sync call client. + */ + public void createCallConnection() { + CallingServerClient callingServerClient = createCallingServerClientWithPipeline(); + + CommunicationIdentifier source = new CommunicationUserIdentifier(""); + CommunicationIdentifier firstCallee = new CommunicationUserIdentifier(""); + CommunicationIdentifier secondCallee = new CommunicationUserIdentifier(""); + String callbackUri = ""; + + // BEGIN: com.azure.communication.callingserver.CallingServerClient.create.call.connection + CommunicationIdentifier[] targets = new CommunicationIdentifier[] { firstCallee, secondCallee }; + CallModality[] requestedMediaTypes = new CallModality[] { CallModality.AUDIO, CallModality.VIDEO }; + EventSubscriptionType[] requestedCallEvents = new EventSubscriptionType[] { + EventSubscriptionType.DTMF_RECEIVED, + EventSubscriptionType.PARTICIPANTS_UPDATED + }; + CreateCallOptions createCallOptions = new CreateCallOptions( + callbackUri, + requestedMediaTypes, + requestedCallEvents); + CallConnection callConnection = callingServerClient.createCallConnection(source, targets, createCallOptions); + // END: com.azure.communication.callingserver.CallingServerClient.create.call.connection + } + +} diff --git a/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/ConversationClientReadmeSamples.java b/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/ConversationClientReadmeSamples.java new file mode 100644 index 0000000000000..aeb77e4a1f0f0 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/ConversationClientReadmeSamples.java @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.models.CallRecordingState; +import com.azure.communication.callingserver.models.CallRecordingStateResult; +import com.azure.communication.callingserver.models.PlayAudioResult; +import com.azure.communication.callingserver.models.StartCallRecordingResult; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + * + * Class containing code snippets that will be injected to README.md. + */ + +public class ConversationClientReadmeSamples { + + /** + * Sample code for creating a sync calling server client. + * + * @return the calling server client. + */ + public CallingServerClient createCallingServerClient() { + String endpoint = "https://.communcationservices.azure.com"; + + // Your connectionString retrieved from your Azure Communication Service + String connectionString = "endpoint=https://.communication.azure.com/;accesskey="; + + // Initialize the calling server client + final CallingServerClientBuilder builder = new CallingServerClientBuilder(); + builder.connectionString(connectionString); + CallingServerClient callingServerClient = builder.buildClient(); + + return callingServerClient; + } + + /** + * Sample code for starting a recording. + * + * @param callingServerClient {@link CallingServerClient} to use for recording. + * @return recordingId to use with other recording operations. + */ + public String startRecording(CallingServerClient callingServerClient) { + String serverCallId = ""; + String recordingStateCallbackUri = ""; + ServerCall serverCall = callingServerClient.initializeServerCall(serverCallId); + StartCallRecordingResult result = serverCall.startRecording(recordingStateCallbackUri); + String recordingId = result.getRecordingId(); + return recordingId; + } + + /** + * Sample code for pausing a recording. + * + * @param callingServerClient {@link CallingServerClient} to use for recording. + * @param serverCallId Identifier of the current server call. + * @param recordingId Identifier of the recording to pause. + */ + public void pauseRecording(CallingServerClient callingServerClient, + String serverCallId, String recordingId) { + ServerCall serverCall = callingServerClient.initializeServerCall(serverCallId); + serverCall.pauseRecording(recordingId); + } + + /** + * Sample code for resuming a recording. + * + * @param callingServerClient {@link CallingServerClient} to use for recording. + * @param serverCallId Identifier of the current server call. + * @param recordingId Identifier of the recording to resume. + */ + public void resumeRecording(CallingServerClient callingServerClient, + String serverCallId, String recordingId) { + ServerCall serverCall = callingServerClient.initializeServerCall(serverCallId); + serverCall.resumeRecording(recordingId); + } + + /** + * Sample code for stopping a recording. + * + * @param callingServerClient {@link CallingServerClient} to use for recording. + * @param serverCallId Identifier of the current server call. + * @param recordingId Identifier of the recording to stop. + */ + public void stopRecording(CallingServerClient callingServerClient, + String serverCallId, String recordingId) { + ServerCall serverCall = callingServerClient.initializeServerCall(serverCallId); + serverCall.stopRecording(recordingId); + } + + /** + * Sample code for requesting the state of a recording. + * + * @param callingServerClient {@link CallingServerClient} to use for recording. + * @param serverCallId Identifier of the current server call. + * @param recordingId Identifier of the recording from which to request state. + * @return state of the recording, {@link CallRecordingState}. + */ + public CallRecordingState getRecordingState(CallingServerClient callingServerClient, + String serverCallId, String recordingId) { + ServerCall serverCall = callingServerClient.initializeServerCall(serverCallId); + CallRecordingStateResult callRecordingStateResult = serverCall.getRecordingState(recordingId); + + // CallRecordingState: Active, Inactive + // If the call has ended, CommunicationErrorException will be thrown. Inactive is + // only returned when the recording is paused. + CallRecordingState callRecordingState = callRecordingStateResult.getRecordingState(); + return callRecordingState; + } + + /** + * Sample code for playing an audio notification in a call. + * + * @param callingServerClient {@link CallingServerClient} to use for recording. + * @param serverCallId Identifier of the current server call. + * @return information about the play audio request, {@link PlayAudioResult}. + */ + public PlayAudioResult playAudio(CallingServerClient callingServerClient, String serverCallId) { + String audioFileUri = ""; + String audioFileId = ""; + String callbackUri = ""; + String context = ""; + ServerCall serverCall = callingServerClient.initializeServerCall(serverCallId); + PlayAudioResult playAudioResult = serverCall.playAudio(audioFileUri, audioFileId, callbackUri, context); + return playAudioResult; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/ReadmeSamples.java b/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/ReadmeSamples.java new file mode 100644 index 0000000000000..e177dfebeb89f --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/samples/java/com/azure/communication/callingserver/ReadmeSamples.java @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.models.CallModality; +import com.azure.communication.callingserver.models.EventSubscriptionType; +import com.azure.communication.callingserver.models.CreateCallOptions; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.communication.common.CommunicationUserIdentifier; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + * + * Class containing code snippets that will be injected to README.md. + */ + +public class ReadmeSamples { + + /** + * Sample code for creating a sync calling server client. + * + * @return the calling server client. + */ + public CallingServerClient createCallingServerClient() { + // Your connectionString retrieved from your Azure Communication Service + String connectionString = "endpoint=https://.communication.azure.com/;accesskey="; + + // Initialize the calling server client + final CallingServerClientBuilder builder = new CallingServerClientBuilder(); + builder.connectionString(connectionString); + CallingServerClient callingServerClient = builder.buildClient(); + + return callingServerClient; + } + + /** + * Sample code for creating a call connection using the sync call client. + */ + public void createCallConnection() { + + CallingServerClient callingServerClient = createCallingServerClient(); + + CommunicationIdentifier source = new CommunicationUserIdentifier(""); + CommunicationIdentifier firstCallee = new CommunicationUserIdentifier(""); + CommunicationIdentifier secondCallee = new CommunicationUserIdentifier(""); + + CommunicationIdentifier[] targets = new CommunicationIdentifier[] { firstCallee, secondCallee }; + + String callbackUri = ""; + + CallModality[] requestedMediaTypes = new CallModality[] { CallModality.AUDIO, CallModality.VIDEO }; + + EventSubscriptionType[] requestedCallEvents = new EventSubscriptionType[] { + EventSubscriptionType.DTMF_RECEIVED, + EventSubscriptionType.PARTICIPANTS_UPDATED + }; + + CreateCallOptions createCallOptions = new CreateCallOptions( + callbackUri, + requestedMediaTypes, + requestedCallEvents); + + CallConnection callConnection = callingServerClient.createCallConnection(source, targets, createCallOptions); + } + + /** + * Sample code for hanging up a call connection using the sync call client. + */ + public void hangupCallConnection() { + String callConnectionId = "callId"; + CallingServerClient callingServerClient = createCallingServerClient(); + CallConnection callConnection = callingServerClient.getCallConnection(callConnectionId); + callConnection.hangup(); + } + + /** + * Sample code for deleting a call using the sync call client. + */ + public void addParticipant() { + String callConnectionId = "callId"; + CallingServerClient callingServerClient = createCallingServerClient(); + CallConnection callConnection = callingServerClient.getCallConnection(callConnectionId); + CommunicationIdentifier thirdCallee = new CommunicationUserIdentifier(""); + callConnection.addParticipant(thirdCallee, "ACS User 3", ""); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallConnectionAsyncLiveTests.java b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallConnectionAsyncLiveTests.java new file mode 100644 index 0000000000000..92f167f6cf12e --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallConnectionAsyncLiveTests.java @@ -0,0 +1,347 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.communication.callingserver; + +import java.util.UUID; + +import com.azure.communication.callingserver.models.CallModality; +import com.azure.communication.callingserver.models.CancelAllMediaOperationsResult; +import com.azure.communication.callingserver.models.EventSubscriptionType; +import com.azure.communication.callingserver.models.CreateCallOptions; +import com.azure.communication.callingserver.models.JoinCallOptions; +import com.azure.communication.callingserver.models.PlayAudioResult; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.communication.common.CommunicationUserIdentifier; +import com.azure.communication.common.PhoneNumberIdentifier; +import com.azure.core.http.HttpClient; +import com.azure.core.http.rest.Response; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class CallConnectionAsyncLiveTests extends CallingServerTestBase { + + private final String fromUser = getNewUserId(); + private final String toUser = getNewUserId(); + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runCreatePlayCancelHangupScenarioAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getCallClientUsingConnectionString(httpClient); + CallingServerAsyncClient callingServerAsyncClient = + setupAsyncClient(builder, "runCreatePlayCancelHangupScenarioAsync"); + + try { + // Establish a call + CreateCallOptions options = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + options.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + CallConnectionAsync callConnectionAsync = callingServerAsyncClient.createCallConnection( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + options).block(); + + CallingServerTestUtils.validateCallConnectionAsync(callConnectionAsync); + + // Play Audio + String operationContext = UUID.randomUUID().toString(); + assert callConnectionAsync != null; + PlayAudioResult playAudioResult = callConnectionAsync.playAudio( + AUDIO_FILE_URI, + false, + UUID.randomUUID().toString(), + null, + operationContext).block(); + CallingServerTestUtils.validatePlayAudioResult(playAudioResult); + + // Cancel All Media Operations + String cancelMediaOperationContext = UUID.randomUUID().toString(); + CancelAllMediaOperationsResult cancelAllMediaOperationsResult = + callConnectionAsync.cancelAllMediaOperations(cancelMediaOperationContext).block(); + CallingServerTestUtils.validateCancelAllMediaOperations(cancelAllMediaOperationsResult); + + // Hang up + callConnectionAsync.hangup().block(); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runCreatePlayCancelHangupScenarioWithResponseAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getCallClientUsingConnectionString(httpClient); + CallingServerAsyncClient callingServerAsyncClient = + setupAsyncClient(builder, "runCreatePlayCancelHangupScenarioWithResponseAsync"); + + try { + // Establish a call + CreateCallOptions options = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + options.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + Response callConnectionAsyncResponse = + callingServerAsyncClient.createCallConnectionWithResponse( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + options).block(); + + CallingServerTestUtils.validateCallConnectionAsyncResponse(callConnectionAsyncResponse); + assert callConnectionAsyncResponse != null; + CallConnectionAsync callConnectionAsync = callConnectionAsyncResponse.getValue(); + + // Play Audio + String operationContext = UUID.randomUUID().toString(); + Response playAudioResponse = + callConnectionAsync.playAudioWithResponse( + AUDIO_FILE_URI, + false, + UUID.randomUUID().toString(), + null, + operationContext).block(); + CallingServerTestUtils.validatePlayAudioResponse(playAudioResponse); + + // Cancel All Media Operations + String cancelMediaOperationContext = UUID.randomUUID().toString(); + Response cancelAllMediaOperationsResult = + callConnectionAsync.cancelAllMediaOperationsWithResponse(cancelMediaOperationContext).block(); + CallingServerTestUtils.validateCancelAllMediaOperationsResult(cancelAllMediaOperationsResult); + + // Hang up + Response hangupResponse = callConnectionAsync.hangupWithResponse().block(); + CallingServerTestUtils.validateResponse(hangupResponse); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runCreateAddRemoveHangupScenarioAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getCallClientUsingConnectionString(httpClient); + CallingServerAsyncClient callingServerAsyncClient = + setupAsyncClient(builder, "runCreateAddRemoveHangupScenarioAsync"); + + try { + // Establish a call + CreateCallOptions options = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + options.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + CallConnectionAsync callConnectionAsync = callingServerAsyncClient.createCallConnection( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + options).block(); + + CallingServerTestUtils.validateCallConnectionAsync(callConnectionAsync); + + // Invite User + String operationContext = UUID.randomUUID().toString(); + assert callConnectionAsync != null; + callConnectionAsync.addParticipant( + new CommunicationUserIdentifier(toUser), + null, + operationContext).block(); + + // Remove Participant + /* + There is an update that we require to be able to get + the participantId from the service when a user is + added to a call. Until that is fixed this recorded + value needs to be used. + */ + String participantId = "e3560385-776f-41d1-bf04-07ef738f2fc1"; + callConnectionAsync.removeParticipant(participantId).block(); + + // Hang up + callConnectionAsync.hangup().block(); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runCreateAddRemoveHangupScenarioWithResponseAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getCallClientUsingConnectionString(httpClient); + CallingServerAsyncClient callingServerAsyncClient = + setupAsyncClient(builder, "runCreateAddRemoveHangupScenarioWithResponseAsync"); + + try { + // Establish a call + CreateCallOptions options = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + options.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + Response callConnectionAsyncResponse = + callingServerAsyncClient.createCallConnectionWithResponse( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + options).block(); + + CallingServerTestUtils.validateCallConnectionAsyncResponse(callConnectionAsyncResponse); + assert callConnectionAsyncResponse != null; + CallConnectionAsync callConnectionAsync = callConnectionAsyncResponse.getValue(); + + // Invite User + String operationContext = UUID.randomUUID().toString(); + Response inviteParticipantResponse = + callConnectionAsync.addParticipantWithResponse( + new CommunicationUserIdentifier(toUser), + null, + operationContext).block(); + CallingServerTestUtils.validateResponse(inviteParticipantResponse); + + // Remove Participant + /* + There is an update that we require to be able to get + the participantId from the service when a user is + added to a call. Until that is fixed this recorded + value needs to be used. + */ + String participantId = "80238d5f-9eda-481a-b911-e2e12eba9eda"; + Response removeParticipantResponse = + callConnectionAsync.removeParticipantWithResponse(participantId).block(); + CallingServerTestUtils.validateResponse(removeParticipantResponse); + + // Hang up + Response hangupResponse = callConnectionAsync.hangupWithResponse().block(); + CallingServerTestUtils.validateResponse(hangupResponse); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runCreateJoinHangupScenarioAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getCallClientUsingConnectionString(httpClient); + CallingServerAsyncClient callingServerAsyncClient = + setupAsyncClient(builder, "runCreateJoinHangupScenarioAsync"); + + try { + // Establish a call + CreateCallOptions options = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + options.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + CallConnectionAsync callConnectionAsync = callingServerAsyncClient.createCallConnection( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + options).block(); + + CallingServerTestUtils.validateCallConnectionAsync(callConnectionAsync); + + // Join + /* + Waiting for an update to be able to get this serverCallId when using + createCallConnection() + */ + String serverCallId = "aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L3VodHNzZEZ3NFVHX1J4d1lHYWlLRmc_aT0yJmU9NjM3NTg0Mzk2NDM5NzQ5NzY4"; + JoinCallOptions joinCallOptions = new JoinCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + CallConnectionAsync joinedCallConnectionAsync = + callingServerAsyncClient.join( + serverCallId, + new CommunicationUserIdentifier(toUser), + joinCallOptions).block(); + CallingServerTestUtils.validateCallConnectionAsync(joinedCallConnectionAsync); + + //Hangup + assert callConnectionAsync != null; + callConnectionAsync.hangup().block(); + assert joinedCallConnectionAsync != null; + joinedCallConnectionAsync.hangup().block(); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runCreateJoinHangupScenarioWithResponseAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getCallClientUsingConnectionString(httpClient); + CallingServerAsyncClient callingServerAsyncClient = + setupAsyncClient(builder, "runCreateJoinHangupScenarioWithResponseAsync"); + + try { + // Establish a call + CreateCallOptions options = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + options.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + Response callConnectionAsyncResponse = + callingServerAsyncClient.createCallConnectionWithResponse( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + options).block(); + + CallingServerTestUtils.validateCallConnectionAsyncResponse(callConnectionAsyncResponse); + assert callConnectionAsyncResponse != null; + CallConnectionAsync callConnectionAsync = callConnectionAsyncResponse.getValue(); + + // Join + /* + Waiting for an update to be able to get this serverCallId when using + createCallConnection() + */ + String serverCallId = "aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L3lKQXY0TnVlOEV5bUpYVm1IYklIeUE_aT0wJmU9NjM3NTg0MzkwMjcxMzg0MTc3"; + JoinCallOptions joinCallOptions = new JoinCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + Response joinedCallConnectionAsyncResponse = + callingServerAsyncClient.joinWithResponse( + serverCallId, + new CommunicationUserIdentifier(toUser), + joinCallOptions).block(); + CallingServerTestUtils.validateJoinCallConnectionAsyncResponse(joinedCallConnectionAsyncResponse); + assert joinedCallConnectionAsyncResponse != null; + CallConnectionAsync joinedCallConnectionAsync = joinedCallConnectionAsyncResponse.getValue(); + + //Hangup + Response hangupResponse = callConnectionAsync.hangupWithResponse().block(); + CallingServerTestUtils.validateResponse(hangupResponse); + hangupResponse = joinedCallConnectionAsync.hangupWithResponse().block(); + CallingServerTestUtils.validateResponse(hangupResponse); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + private CallingServerAsyncClient setupAsyncClient(CallingServerClientBuilder builder, String testName) { + return addLoggingPolicy(builder, testName).buildAsyncClient(); + } + + protected CallingServerClientBuilder addLoggingPolicy(CallingServerClientBuilder builder, String testName) { + return builder.addPolicy((context, next) -> logHeaders(testName, next)); + } +} + diff --git a/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallConnectionLiveTests.java b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallConnectionLiveTests.java new file mode 100644 index 0000000000000..d71de76df9ad3 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallConnectionLiveTests.java @@ -0,0 +1,329 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.models.CallModality; +import com.azure.communication.callingserver.models.CancelAllMediaOperationsResult; +import com.azure.communication.callingserver.models.CreateCallOptions; +import com.azure.communication.callingserver.models.EventSubscriptionType; +import com.azure.communication.callingserver.models.JoinCallOptions; +import com.azure.communication.callingserver.models.PlayAudioResult; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.communication.common.CommunicationUserIdentifier; +import com.azure.communication.common.PhoneNumberIdentifier; +import com.azure.core.http.HttpClient; +import com.azure.core.http.rest.Response; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.UUID; + +public class CallConnectionLiveTests extends CallingServerTestBase { + + private final String fromUser = getNewUserId(); + private final String toUser = getNewUserId(); + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runCreatePlayCancelHangupScenario(HttpClient httpClient) { + CallingServerClientBuilder builder = getCallClientUsingConnectionString(httpClient); + CallingServerClient callingServerClient = setupClient(builder, "runCreatePlayCancelHangupScenario"); + + try { + // Establish a call + CreateCallOptions options = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + options.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + CallConnection callConnection = callingServerClient.createCallConnection( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + options); + + CallingServerTestUtils.validateCallConnection(callConnection); + + // Play Audio + String playAudioOperationContext = UUID.randomUUID().toString(); + PlayAudioResult playAudioResult = callConnection.playAudio( + AUDIO_FILE_URI, + false, + UUID.randomUUID().toString(), + null, + playAudioOperationContext); + CallingServerTestUtils.validatePlayAudioResult(playAudioResult); + + // Cancel All Media Operations + String cancelMediaOperationContext = UUID.randomUUID().toString(); + CancelAllMediaOperationsResult cancelAllMediaOperationsResult = + callConnection.cancelAllMediaOperations(cancelMediaOperationContext); + CallingServerTestUtils.validateCancelAllMediaOperations(cancelAllMediaOperationsResult); + + // Hang up + callConnection.hangup(); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runCreatePlayCancelHangupScenarioWithResponse(HttpClient httpClient) { + CallingServerClientBuilder builder = getCallClientUsingConnectionString(httpClient); + CallingServerClient callingServerClient = + setupClient(builder, "runCreatePlayCancelHangupScenarioWithResponse"); + + try { + // Establish a call + CreateCallOptions options = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + options.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + Response callConnectionResponse = + callingServerClient.createCallConnectionWithResponse( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + options, + null); + + CallingServerTestUtils.validateCallConnectionResponse(callConnectionResponse); + CallConnection callConnection = callConnectionResponse.getValue(); + + // Play Audio + String operationContext = UUID.randomUUID().toString(); + Response playAudioResult = + callConnection.playAudioWithResponse( + AUDIO_FILE_URI, + false, + UUID.randomUUID().toString(), + null, + operationContext, + null); + CallingServerTestUtils.validatePlayAudioResponse(playAudioResult); + + // Cancel All Media Operations + String cancelMediaOperationContext = UUID.randomUUID().toString(); + Response cancelAllMediaOperationsResult = + callConnection.cancelAllMediaOperationsWithResponse(cancelMediaOperationContext, null); + CallingServerTestUtils.validateCancelAllMediaOperationsResult(cancelAllMediaOperationsResult); + + // Hang up + Response hangupResponse = callConnection.hangupWithResponse(null); + CallingServerTestUtils.validateResponse(hangupResponse); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runCreateAddRemoveHangupScenario(HttpClient httpClient) { + CallingServerClientBuilder builder = getCallClientUsingConnectionString(httpClient); + CallingServerClient callingServerClient = setupClient(builder, "runCreateAddRemoveHangupScenario"); + + try { + // Establish a call + CreateCallOptions options = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + options.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + CallConnection callConnection = callingServerClient.createCallConnection( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + options); + + CallingServerTestUtils.validateCallConnection(callConnection); + + // Add User + String operationContext = UUID.randomUUID().toString(); + callConnection.addParticipant(new CommunicationUserIdentifier(toUser), null, operationContext); + + // Remove Participant + /* + There is an update that we require to be able to get + the participantId from the service when a user is + added to a call. Until that is fixed this recorded + value needs to be used. + */ + String participantId = "f29f70e3-1eaf-44c0-839c-b4e8a74ffec3"; + callConnection.removeParticipant(participantId); + + // Hang up + callConnection.hangup(); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runCreateAddRemoveHangupScenarioWithResponse(HttpClient httpClient) { + CallingServerClientBuilder builder = getCallClientUsingConnectionString(httpClient); + CallingServerClient callingServerClient = + setupClient(builder, "runCreateAddRemoveHangupScenarioWithResponse"); + + try { + // Establish a call + CreateCallOptions options = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + options.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + Response callConnectionResponse = + callingServerClient.createCallConnectionWithResponse( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + options, + null); + + CallingServerTestUtils.validateCallConnectionResponse(callConnectionResponse); + CallConnection callConnection = callConnectionResponse.getValue(); + + // Add User + String operationContext = UUID.randomUUID().toString(); + Response inviteParticipantResponse = callConnection + .addParticipantWithResponse( + new CommunicationUserIdentifier(toUser), + null, + operationContext, + null); + CallingServerTestUtils.validateResponse(inviteParticipantResponse); + + // Remove Participant + /* + There is an update that we require to be able to get + the participantId from the service when a user is + added to a call. Until that is fixed this recorded + value needs to be used. + */ + String participantId = "71ed956b-366e-450c-9a61-3bbccf42baa5"; + Response removeParticipantResponse = + callConnection.removeParticipantWithResponse(participantId, null); + CallingServerTestUtils.validateResponse(removeParticipantResponse); + + // Hang up + Response hangupResponse = callConnection.hangupWithResponse(null); + CallingServerTestUtils.validateResponse(hangupResponse); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runCreateJoinHangupScenario(HttpClient httpClient) { + CallingServerClientBuilder builder = getCallClientUsingConnectionString(httpClient); + CallingServerClient callingServerClient = setupClient(builder, "runCreateJoinHangupScenario"); + + try { + // Establish a call + CreateCallOptions createCallOptions = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + createCallOptions.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + CallConnection callConnection = callingServerClient.createCallConnection( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + createCallOptions); + + CallingServerTestUtils.validateCallConnection(callConnection); + + // Join + /* + Waiting for an update to be able to get this serverCallId when using + createCallConnection() + */ + String serverCallId = "aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L2RUUjRPVGFxVzAyZ3cxVGpNSUNBdEE_aT0wJmU9NjM3NTg0MzkwMjcxMzg0MTc3"; + JoinCallOptions joinCallOptions = new JoinCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + CallConnection joinedCallConnection = + callingServerClient.join(serverCallId, new CommunicationUserIdentifier(toUser), joinCallOptions); + CallingServerTestUtils.validateCallConnection(joinedCallConnection); + + //Hangup + callConnection.hangup(); + joinedCallConnection.hangup(); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runCreateJoinHangupScenarioWithResponse(HttpClient httpClient) { + CallingServerClientBuilder builder = getCallClientUsingConnectionString(httpClient); + CallingServerClient callingServerClient = + setupClient(builder, "runCreateJoinHangupScenarioWithResponse"); + + try { + // Establish a call + CreateCallOptions createCallOptions = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + createCallOptions.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + Response callConnectionResponse = callingServerClient.createCallConnectionWithResponse( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + createCallOptions, + null); + + CallingServerTestUtils.validateCallConnectionResponse(callConnectionResponse); + CallConnection callConnection = callConnectionResponse.getValue(); + + // Join + String serverCallId = "aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L3dXZW9hNjAweGtPZ0d6eHE2eG1tQVE_aT0yJmU9NjM3NTg0Mzk2NDM5NzQ5NzY4"; + JoinCallOptions joinCallOptions = new JoinCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + Response joinedCallConnectionResponse = + callingServerClient.joinWithResponse( + serverCallId, + new CommunicationUserIdentifier(toUser), + joinCallOptions, + null); + CallingServerTestUtils.validateJoinCallConnectionResponse(joinedCallConnectionResponse); + CallConnection joinedCallConnection = joinedCallConnectionResponse.getValue(); + + //Hangup + Response hangupResponse = callConnection.hangupWithResponse(null); + CallingServerTestUtils.validateResponse(hangupResponse); + Response joinCallHangupResponse = joinedCallConnection.hangupWithResponse(null); + CallingServerTestUtils.validateResponse(joinCallHangupResponse); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + private CallingServerClient setupClient(CallingServerClientBuilder builder, String testName) { + return addLoggingPolicy(builder, testName).buildClient(); + } + + protected CallingServerClientBuilder addLoggingPolicy(CallingServerClientBuilder builder, String testName) { + return builder.addPolicy((context, next) -> logHeaders(testName, next)); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallingServerClientBuilderUnitTests.java b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallingServerClientBuilderUnitTests.java new file mode 100644 index 0000000000000..7608722349ab2 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallingServerClientBuilderUnitTests.java @@ -0,0 +1,221 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.communication.callingserver; + +import com.azure.communication.common.implementation.HmacAuthenticationPolicy; +import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.credential.TokenCredential; +import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpPipelineBuilder; +import com.azure.core.http.HttpRequest; +import com.azure.core.http.HttpResponse; +import com.azure.core.http.policy.HttpLogOptions; +import com.azure.core.util.ClientOptions; +import com.azure.identity.DefaultAzureCredentialBuilder; +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class CallingServerClientBuilderUnitTests { + static final String MOCK_URL = "https://REDACTED.communication.azure.com"; + static final String MOCK_ACCESS_KEY = "eyKfcHciOiJIUzI1NiIsInR5cCI6IkqXVCJ9eyJzdWIiOiIxMjM0NTY5ODkwIiwibmFtZSI7IkpvaGfQSflKxwRJSMeKKF2QT4fwpMeJf36POk6yJVadUs4s5d"; + static final String MOCK_CONNECTION_STRING = "endpoint=https://REDACTED.communication.azure.com/;accesskey=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaGfQSflKxwRJSMeKKF2QT4fwpMeJf36POk6yJVadQssw5c"; + private static final String APPLICATION_ID = "833bad32-4432-4d41-8bb4"; + + static class NoOpHttpClient implements HttpClient { + @Override + public Mono send(HttpRequest request) { + return Mono.empty(); // NOP + } + } + + private final CallingServerClientBuilder builder = new CallingServerClientBuilder(); + + @Test + public void missingTokenCredentialTest() throws NullPointerException { + builder + .endpoint(MOCK_URL) + .httpClient(new NoOpHttpClient()); + assertThrows(Exception.class, builder::buildAsyncClient); + } + + @Test + public void missingUrlTest() + throws NullPointerException { + builder + .credential(new AzureKeyCredential(MOCK_ACCESS_KEY)) + .httpClient(new NoOpHttpClient()); + assertThrows(Exception.class, builder::buildAsyncClient); + } + + @Test + public void nullPipelineTest() { + assertThrows(NullPointerException.class, () -> builder + .connectionString(MOCK_CONNECTION_STRING) + .httpClient(new NoOpHttpClient()) + .pipeline(null)); + } + + @Test + public void nullCustomPolicyTest() { + assertThrows(NullPointerException.class, () -> builder + .connectionString(MOCK_CONNECTION_STRING) + .httpClient(new NoOpHttpClient()) + .addPolicy(null)); + } + + @Test + public void nullConfigurationTest() { + assertThrows(NullPointerException.class, () -> builder + .connectionString(MOCK_CONNECTION_STRING) + .httpClient(new NoOpHttpClient()) + .configuration(null)); + } + + @Test + public void nullHttpLogOptionsTest() { + assertThrows(NullPointerException.class, () -> builder + .connectionString(MOCK_CONNECTION_STRING) + .httpClient(new NoOpHttpClient()) + .httpLogOptions(null)); + } + + @Test + public void nullRetryPolicyTest() { + assertThrows( + NullPointerException.class, () -> builder + .connectionString(MOCK_CONNECTION_STRING) + .httpClient(new NoOpHttpClient()) + .retryPolicy(null)); + } + + @Test + public void buildPiplineForClient() { + CallingServerAsyncClient callingServerAsyncClient = builder + .connectionString(MOCK_CONNECTION_STRING) + .httpClient(new NoOpHttpClient()) + .pipeline(new HttpPipelineBuilder().build()) + .buildAsyncClient(); + assertNotNull(callingServerAsyncClient); + } + + @Test + public void setHttpLogOptions() { + HttpLogOptions options = new HttpLogOptions().setApplicationId(APPLICATION_ID); + CallingServerAsyncClient callAsyncClient = builder + .connectionString(MOCK_CONNECTION_STRING) + .httpLogOptions(options) + .httpClient(new NoOpHttpClient()) + .buildAsyncClient(); + assertNotNull(callAsyncClient); + } + + @Test + public void setClientOptions() { + ClientOptions options = new ClientOptions().setApplicationId(APPLICATION_ID); + CallingServerAsyncClient callAsyncClient = builder + .connectionString(MOCK_CONNECTION_STRING) + .clientOptions(options) + .httpClient(new NoOpHttpClient()) + .buildAsyncClient(); + assertNotNull(callAsyncClient); + } + + @Test + public void noClientOptionsNoPipeline() { + CallingServerAsyncClient callAsyncClient = builder + .connectionString(MOCK_CONNECTION_STRING) + .httpClient(new NoOpHttpClient()) + .buildAsyncClient(); + assertNotNull(callAsyncClient); + } + + @Test + public void addPolicy() { + AzureKeyCredential credential = new AzureKeyCredential("key"); + CallingServerAsyncClient callAsyncClient = + builder + .connectionString(MOCK_CONNECTION_STRING) + .addPolicy(new HmacAuthenticationPolicy(credential)) + .httpClient(new NoOpHttpClient()) + .pipeline(new HttpPipelineBuilder().build()) + .buildAsyncClient(); + assertNotNull(callAsyncClient); + } + + @Test + public void argumentExceptionOnConnectionStringAndEndpoint() { + assertThrows(IllegalArgumentException.class, + () -> builder + .connectionString(MOCK_CONNECTION_STRING) + .endpoint(MOCK_URL) + .httpClient(new NoOpHttpClient()) + .buildAsyncClient()); + } + + @Test + public void argumentExceptionOnConnectionStringAndAzureKeyCredential() { + AzureKeyCredential credential = new AzureKeyCredential("key"); + assertThrows( + IllegalArgumentException.class, () -> builder + .connectionString(MOCK_CONNECTION_STRING) + .credential(credential) + .httpClient(new NoOpHttpClient()) + .buildAsyncClient()); + } + + @Test + public void argumentExceptionOnConnectionStringAndTokenCredential() { + TokenCredential tokenCredential = new DefaultAzureCredentialBuilder().build(); + assertThrows( + IllegalArgumentException.class, () -> builder + .connectionString(MOCK_CONNECTION_STRING) + .credential(tokenCredential) + .httpClient(new NoOpHttpClient()) + .buildAsyncClient()); + } + + @Test + public void argumentExceptionOnAzureKeyCredentialAndTokenCredential() { + AzureKeyCredential credential = new AzureKeyCredential("key"); + TokenCredential tokenCredential = new DefaultAzureCredentialBuilder().build(); + assertThrows( + IllegalArgumentException.class, () -> builder + .credential(credential) + .credential(tokenCredential) + .httpClient(new NoOpHttpClient()) + .buildAsyncClient()); + } + + @Test + public void noPipelineWithToken() { + TokenCredential tokenCredential = new DefaultAzureCredentialBuilder().build(); + CallingServerAsyncClient callAsyncClient = builder + .endpoint(MOCK_URL) + .credential(tokenCredential) + .httpClient(new NoOpHttpClient()) + .buildAsyncClient(); + + assertNotNull(callAsyncClient); + } + + @Test + public void noCredential() { + assertThrows( + IllegalArgumentException.class, () -> builder + .endpoint(MOCK_URL) + .httpClient(new NoOpHttpClient()) + .buildAsyncClient()); + } + + @Test + public void noEndpoint() { + assertThrows( + NullPointerException.class, () -> builder + .httpClient(new NoOpHttpClient()) + .buildAsyncClient()); + } +} + diff --git a/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallingServerTestBase.java b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallingServerTestBase.java new file mode 100644 index 0000000000000..72213f806f303 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallingServerTestBase.java @@ -0,0 +1,275 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.models.CallModality; +import com.azure.communication.callingserver.models.EventSubscriptionType; +import com.azure.communication.callingserver.models.JoinCallOptions; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.communication.common.CommunicationUserIdentifier; +import com.azure.communication.identity.CommunicationIdentityClient; +import com.azure.communication.identity.CommunicationIdentityClientBuilder; +import com.azure.core.credential.AccessToken; +import com.azure.core.credential.TokenCredential; +import com.azure.core.credential.TokenRequestContext; +import com.azure.core.http.HttpClient; +import com.azure.core.test.TestBase; +import com.azure.core.test.TestMode; +import com.azure.core.util.Configuration; +import com.azure.core.util.CoreUtils; +import com.azure.core.util.logging.ClientLogger; + +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.StringJoiner; +import java.util.UUID; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import reactor.core.publisher.Mono; +import com.azure.core.http.HttpPipelineNextPolicy; +import com.azure.core.http.HttpResponse; + +public class CallingServerTestBase extends TestBase { + protected static final TestMode TEST_MODE = initializeTestMode(); + + protected static final String CONNECTION_STRING = Configuration.getGlobalConfiguration() + .get("COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING", + "endpoint=https://REDACTED.communication.azure.com/;accesskey=QWNjZXNzS2V5"); + + protected static final String RESOURCE_IDENTIFIER = Configuration.getGlobalConfiguration() + .get("COMMUNICATION_LIVETEST_STATIC_RESOURCE_IDENTIFIER", + "016a7064-0581-40b9-be73-6dde64d69d72"); + + protected static final String GROUP_IDENTIFIER = Configuration.getGlobalConfiguration() + .get("COMMUNICATION_LIVETEST_STATIC_GROUP_IDENTIFIER", + "c400789f-e11b-4ceb-88cb-bc8df2a01568"); + + protected static final String FROM_PHONE_NUMBER = Configuration.getGlobalConfiguration() + .get("AZURE_PHONE_NUMBER", "+15551234567"); + + protected static final String TO_PHONE_NUMBER = Configuration.getGlobalConfiguration() + .get("AZURE_PHONE_NUMBER", "+15551234567"); + + protected static final String CALLBACK_URI = Configuration.getGlobalConfiguration() + .get("CALLBACK_URI", "https://host.app/api/callback/calling"); + + protected static final String AUDIO_FILE_URI = Configuration.getGlobalConfiguration() + .get("AUDIO_FILE_URI", "https://host.app/audio/bot-callcenter-intro.wav"); + + protected static final String METADATA_URL = Configuration.getGlobalConfiguration() + .get("METADATA_URL", "https://storage.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/acsmetadata"); + + protected static final String VIDEO_URL = Configuration.getGlobalConfiguration() + .get("VIDEO_URL", "https://storage.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/video"); + + protected static final String CONTENT_URL_404 = Configuration.getGlobalConfiguration() + .get("CONTENT_URL_404", "https://storage.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141d/content/acsmetadata"); + + private static final StringJoiner JSON_PROPERTIES_TO_REDACT + = new StringJoiner("\":\"|\"", "\"", "\":\"") + .add("to"); + + private static final Pattern JSON_PROPERTY_VALUE_REDACTION_PATTERN + = Pattern.compile(String.format("(?:%s)(.*?)(?:\",|\"})", JSON_PROPERTIES_TO_REDACT), + Pattern.CASE_INSENSITIVE); + + protected CallingServerClientBuilder getCallClientUsingConnectionString(HttpClient httpClient) { + CallingServerClientBuilder builder = new CallingServerClientBuilder() + .connectionString(CONNECTION_STRING) + .httpClient(httpClient == null ? interceptorManager.getPlaybackClient() : httpClient); + + if (getTestMode() == TestMode.RECORD) { + List> redactors = new ArrayList<>(); + redactors.add(data -> redact(data, JSON_PROPERTY_VALUE_REDACTION_PATTERN.matcher(data), "REDACTED")); + builder.addPolicy(interceptorManager.getRecordPolicy(redactors)); + } + return builder; + } + + protected String getNewUserId() { + if (getTestMode() == TestMode.LIVE) { + CommunicationIdentityClient communicationIdentityClient = new CommunicationIdentityClientBuilder() + .connectionString(CONNECTION_STRING) + .buildClient(); + CommunicationUserIdentifier user = communicationIdentityClient.createUser(); + return user.getId(); + } + return getRandomUserId(); + } + + private String getRandomUserId() { + return "8:acs:" + RESOURCE_IDENTIFIER + "_" + UUID.randomUUID(); + } + + protected String getGroupId() { + /* + If tests are running in live mode, we want them to all + have unique groupId's so they do not conflict with other + recording tests running in live mode. + */ + if (getTestMode() == TestMode.LIVE) { + return UUID.randomUUID().toString(); + } + + /* + For recording tests we need to make sure the groupId + matches the recorded groupId, or the call will fail. + */ + return GROUP_IDENTIFIER; + } + + protected CallingServerClientBuilder getConversationClientUsingConnectionString(HttpClient httpClient) { + CallingServerClientBuilder builder = new CallingServerClientBuilder() + .connectionString(CONNECTION_STRING) + .httpClient(httpClient == null ? interceptorManager.getPlaybackClient() : httpClient); + + if (getTestMode() == TestMode.RECORD) { + List> redactors = new ArrayList<>(); + redactors.add(data -> redact(data, JSON_PROPERTY_VALUE_REDACTION_PATTERN.matcher(data), "REDACTED")); + builder.addPolicy(interceptorManager.getRecordPolicy(redactors)); + } + return builder; + } + + private static TestMode initializeTestMode() { + ClientLogger logger = new ClientLogger(CallingServerTestBase.class); + String azureTestMode = Configuration.getGlobalConfiguration().get("AZURE_TEST_MODE"); + if (azureTestMode != null) { + System.out.println("azureTestMode: " + azureTestMode); + try { + return TestMode.valueOf(azureTestMode.toUpperCase(Locale.US)); + } catch (IllegalArgumentException var3) { + logger.error("Could not parse '{}' into TestEnum. Using 'Playback' mode.", azureTestMode); + return TestMode.PLAYBACK; + } + } else { + logger.info("Environment variable '{}' has not been set yet. Using 'Playback' mode.", "AZURE_TEST_MODE"); + return TestMode.PLAYBACK; + } + } + + protected Mono logHeaders(String testName, HttpPipelineNextPolicy next) { + return next.process() + .flatMap(httpResponse -> { + final HttpResponse bufferedResponse = httpResponse.buffer(); + + /* Should sanitize printed response url */ + System.out.println("Chain-ID header for " + testName + " request " + + bufferedResponse.getRequest().getUrl() + + ": " + bufferedResponse.getHeaderValue("X-Microsoft-Skype-Chain-ID")); + return Mono.just(bufferedResponse); + }); + } + + protected List createCall(CallingServerClient callingServerClient, + String groupId, + String from, + String to, + String callBackUri) { + CallConnection fromCallConnection = null; + CallConnection toCallConnection = null; + + try { + CommunicationIdentifier fromParticipant = new CommunicationUserIdentifier(from); + CommunicationIdentifier toParticipant = new CommunicationUserIdentifier(to); + + JoinCallOptions fromCallOptions = new JoinCallOptions( + callBackUri, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + fromCallConnection = callingServerClient.join(groupId, fromParticipant, fromCallOptions); + sleepIfRunningAgainstService(1000); + CallingServerTestUtils.validateCallConnection(fromCallConnection); + + JoinCallOptions joinCallOptions = new JoinCallOptions( + callBackUri, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + toCallConnection = callingServerClient.join(groupId, toParticipant, joinCallOptions); + sleepIfRunningAgainstService(1000); + CallingServerTestUtils.validateCallConnection(toCallConnection); + + return Arrays.asList(fromCallConnection, toCallConnection); + } catch (Exception e) { + System.out.println("Error creating call: " + e.getMessage()); + + if (fromCallConnection != null) { + fromCallConnection.hangup(); + } + + if (toCallConnection != null) { + toCallConnection.hangup(); + } + + throw e; + } + } + + protected List createAsyncCall(CallingServerAsyncClient callingServerClient, + String groupId, + String from, + String to, + String callBackUri) { + CallConnectionAsync fromCallConnection = null; + CallConnectionAsync toCallConnection = null; + + try { + CommunicationIdentifier fromParticipant = new CommunicationUserIdentifier(from); + CommunicationIdentifier toParticipant = new CommunicationUserIdentifier(to); + + JoinCallOptions fromCallOptions = new JoinCallOptions( + callBackUri, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + fromCallConnection = callingServerClient.join(groupId, fromParticipant, fromCallOptions).block(); + sleepIfRunningAgainstService(1000); + CallingServerTestUtils.validateCallConnectionAsync(fromCallConnection); + + JoinCallOptions joinCallOptions = new JoinCallOptions( + callBackUri, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + toCallConnection = callingServerClient.join(groupId, toParticipant, joinCallOptions).block(); + sleepIfRunningAgainstService(1000); + CallingServerTestUtils.validateCallConnectionAsync(toCallConnection); + + return Arrays.asList(fromCallConnection, toCallConnection); + } catch (Exception e) { + System.out.println("Error creating call: " + e.getMessage()); + + if (fromCallConnection != null) { + fromCallConnection.hangup(); + } + + if (toCallConnection != null) { + toCallConnection.hangup(); + } + + throw e; + } + } + + static class FakeCredentials implements TokenCredential { + @Override + public Mono getToken(TokenRequestContext tokenRequestContext) { + return Mono.just(new AccessToken("someFakeToken", OffsetDateTime.MAX)); + } + } + + private String redact(String content, Matcher matcher, String replacement) { + while (matcher.find()) { + String captureGroup = matcher.group(1); + if (!CoreUtils.isNullOrEmpty(captureGroup)) { + content = content.replace(matcher.group(1), replacement); + } + } + return content; + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallingServerTestUtils.java b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallingServerTestUtils.java new file mode 100644 index 0000000000000..218df3dd210b1 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/CallingServerTestUtils.java @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import com.azure.communication.callingserver.models.CancelAllMediaOperationsResult; +import com.azure.communication.callingserver.models.OperationStatus; +import com.azure.communication.callingserver.models.PlayAudioResult; +import com.azure.core.http.rest.Response; +import org.junit.jupiter.api.Assertions; + +public class CallingServerTestUtils { + protected static void validateCallConnectionResponse(Response callConnectionResponse) { + assertNotNull(callConnectionResponse); + assertEquals(201, callConnectionResponse.getStatusCode()); + assertNotNull(callConnectionResponse.getValue()); + validateCallConnection(callConnectionResponse.getValue()); + } + + protected static void validateJoinCallConnectionResponse(Response callConnectionResponse) { + assertNotNull(callConnectionResponse); + assertEquals(202, callConnectionResponse.getStatusCode()); + assertNotNull(callConnectionResponse.getValue()); + validateCallConnection(callConnectionResponse.getValue()); + } + + protected static void validateCallConnection(CallConnection callConnection) { + assertNotNull(callConnection); + assertNotNull(callConnection.getCallConnectionId()); + assertFalse(callConnection.getCallConnectionId().isEmpty()); + } + + protected static void validateCallConnectionAsyncResponse(Response response) { + assertNotNull(response); + assertEquals(201, response.getStatusCode()); + assertNotNull(response.getValue()); + validateCallConnectionAsync(response.getValue()); + } + + protected static void validateJoinCallConnectionAsyncResponse(Response response) { + assertNotNull(response); + assertEquals(202, response.getStatusCode()); + assertNotNull(response.getValue()); + validateCallConnectionAsync(response.getValue()); + } + + protected static void validateCallConnectionAsync(CallConnectionAsync callConnectionAsync) { + assertNotNull(callConnectionAsync); + assertNotNull(callConnectionAsync.getCallConnectionId()); + assertFalse(callConnectionAsync.getCallConnectionId().isEmpty()); + } + + protected static void validatePlayAudioResponse(Response playAudioResponse) { + assertNotNull(playAudioResponse); + Assertions.assertEquals(202, playAudioResponse.getStatusCode()); + assertNotNull(playAudioResponse.getValue()); + validatePlayAudioResult(playAudioResponse.getValue()); + } + + protected static void validatePlayAudioResult(PlayAudioResult playAudioResponse) { + assertNotNull(playAudioResponse); + assertNotNull(playAudioResponse.getId()); + assertFalse(playAudioResponse.getId().isEmpty()); + assertNotNull(playAudioResponse.getStatus()); + assertSame(playAudioResponse.getStatus(), OperationStatus.RUNNING); + } + + protected static void validateCancelAllMediaOperationsResult(Response result) { + assertNotNull(result); + Assertions.assertEquals(200, result.getStatusCode()); + assertNotNull(result.getValue()); + validateCancelAllMediaOperations(result.getValue()); + } + + protected static void validateCancelAllMediaOperations(CancelAllMediaOperationsResult result) { + assertNotNull(result); + assertNotNull(result.getId()); + assertFalse(result.getId().isEmpty()); + assertNotNull(result.getStatus()); + assertSame(result.getStatus(), OperationStatus.COMPLETED); + } + + protected static void validateResponse(Response response) { + assertNotNull(response); + Assertions.assertEquals(202, response.getStatusCode()); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/DownloadContentAsyncLiveTests.java b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/DownloadContentAsyncLiveTests.java new file mode 100644 index 0000000000000..a89f93d7384e0 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/DownloadContentAsyncLiveTests.java @@ -0,0 +1,172 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.models.CallingServerErrorException; +import com.azure.communication.callingserver.models.ParallelDownloadOptions; +import com.azure.core.http.HttpClient; +import com.azure.core.http.rest.Response; +import com.azure.core.util.FluxUtil; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; +import reactor.core.publisher.Flux; + +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousFileChannel; +import java.nio.channels.CompletionHandler; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.times; + +public class DownloadContentAsyncLiveTests extends CallingServerTestBase { + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void downloadMetadataAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerAsyncClient conversationAsyncClient = setupAsyncClient(builder, "downloadMetadataAsync"); + + try { + Flux content = conversationAsyncClient.downloadStream(METADATA_URL); + byte[] contentBytes = FluxUtil.collectBytesInByteBufferStream(content).block(); + assertThat(contentBytes, is(notNullValue())); + String metadata = new String(contentBytes, StandardCharsets.UTF_8); + assertThat(metadata.contains("0-eus-d2-3cca2175891f21c6c9a5975a12c0141c"), is(true)); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void downloadMetadataRetryingAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerAsyncClient conversationAsyncClient = setupAsyncClient(builder, "downloadMetadataAsync"); + + try { + Flux content = conversationAsyncClient.downloadStream(METADATA_URL); + byte[] contentBytes = FluxUtil.collectBytesInByteBufferStream(content).block(); + assertThat(contentBytes, is(notNullValue())); + String metadata = new String(contentBytes, StandardCharsets.UTF_8); + assertThat(metadata.contains("0-eus-d2-3cca2175891f21c6c9a5975a12c0141c"), is(true)); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void downloadVideoAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerAsyncClient conversationAsyncClient = setupAsyncClient(builder, "downloadVideoAsync"); + + try { + Response> response = conversationAsyncClient.downloadStreamWithResponse(VIDEO_URL, null).block(); + assertThat(response, is(notNullValue())); + byte[] contentBytes = FluxUtil.collectBytesInByteBufferStream(response.getValue()).block(); + assertThat(contentBytes, is(notNullValue())); + assertThat(Integer.parseInt(response.getHeaders().getValue("Content-Length")), is(equalTo(contentBytes.length))); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void downloadToFileAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerAsyncClient conversationAsyncClient = setupAsyncClient(builder, "downloadToFileAsync"); + AsynchronousFileChannel channel = Mockito.mock(AsynchronousFileChannel.class); + + doAnswer(invocation -> { + CompletionHandler completionHandler = invocation.getArgument(3); + completionHandler.completed(439, null); + return null; + }).doAnswer(invocation -> { + CompletionHandler completionHandler = invocation.getArgument(3); + completionHandler.completed(438, null); + return null; + }).when(channel).write(any(ByteBuffer.class), + anyLong(), + any(), + any()); + + conversationAsyncClient + .downloadToWithResponse(METADATA_URL, + Paths.get("dummyPath"), + channel, + new ParallelDownloadOptions().setBlockSizeLong(479L), + null).block(); + + Mockito.verify(channel, times(2)).write(any(ByteBuffer.class), anyLong(), + any(), any()); + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void downloadToFileRetryingAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerAsyncClient conversationAsyncClient = setupAsyncClient(builder, "downloadToFileAsync"); + AsynchronousFileChannel channel = Mockito.mock(AsynchronousFileChannel.class); + + doAnswer(invocation -> { + ByteBuffer stream = invocation.getArgument(0); + String metadata = new String(stream.array(), StandardCharsets.UTF_8); + assertTrue(metadata.contains("0-eus-d2-3cca2175891f21c6c9a5975a12c0141c")); + CompletionHandler completionHandler = invocation.getArgument(3); + completionHandler.completed(957, null); + return null; + }).when(channel).write(any(ByteBuffer.class), + anyLong(), + any(), + any()); + + + conversationAsyncClient + .downloadToWithResponse(METADATA_URL, + Paths.get("dummyPath"), + channel, + null, + null).block(); + + Mockito.verify(channel).write(any(ByteBuffer.class), anyLong(), + any(), any()); + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void downloadContent404Async(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerAsyncClient conversationAsyncClient = setupAsyncClient(builder, "downloadContent404Async"); + Response> response = conversationAsyncClient + .downloadStreamWithResponse(CONTENT_URL_404, null).block(); + assertThat(response, is(notNullValue())); + assertThat(response.getStatusCode(), is(equalTo(404))); + assertThrows(CallingServerErrorException.class, + () -> FluxUtil.collectBytesInByteBufferStream(response.getValue()).block()); + } + + + private CallingServerAsyncClient setupAsyncClient(CallingServerClientBuilder builder, String testName) { + return addLoggingPolicy(builder, testName).buildAsyncClient(); + } + + protected CallingServerClientBuilder addLoggingPolicy(CallingServerClientBuilder builder, String testName) { + return builder.addPolicy((context, next) -> logHeaders(testName, next)); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/DownloadContentLiveTests.java b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/DownloadContentLiveTests.java new file mode 100644 index 0000000000000..47222c4f0e011 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/DownloadContentLiveTests.java @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.models.CallingServerErrorException; +import com.azure.core.http.HttpClient; +import com.azure.core.http.rest.Response; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doThrow; + +public class DownloadContentLiveTests extends CallingServerTestBase { + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void downloadMetadata(HttpClient httpClient) throws UnsupportedEncodingException { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerClient conversationClient = setupClient(builder, "downloadMetadata"); + + try { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + conversationClient.downloadTo(METADATA_URL, byteArrayOutputStream, null); + String metadata = byteArrayOutputStream.toString(StandardCharsets.UTF_8.name()); + assertThat(metadata.contains("0-eus-d2-3cca2175891f21c6c9a5975a12c0141c"), is(true)); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void downloadVideo(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerClient conversationClient = setupClient(builder, "downloadVideo"); + + try { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + Response response = conversationClient + .downloadToWithResponse(VIDEO_URL, byteArrayOutputStream, null, null); + assertThat(response, is(notNullValue())); + assertThat( + response.getHeaders().getValue("Content-Type"), + is(equalTo("application/octet-stream"))); + assertThat( + Integer.parseInt(response.getHeaders().getValue("Content-Length")), + is(equalTo(byteArrayOutputStream.size()))); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void downloadContent404(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerClient conversationClient = setupClient(builder, "downloadContent404"); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + CallingServerErrorException ex = assertThrows(CallingServerErrorException.class, + () -> conversationClient + .downloadTo(CONTENT_URL_404, byteArrayOutputStream, null)); + assertThat(ex.getResponse().getStatusCode(), is(equalTo(404))); + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void downloadContentWrongUrl(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerClient conversationClient = setupClient(builder, "downloadContent404"); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + IllegalArgumentException ex = + assertThrows( + IllegalArgumentException.class, + () -> conversationClient + .downloadTo("wrongurl", byteArrayOutputStream, null)); + assertThat(ex, is(notNullValue())); + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void downloadContentStreamFailure(HttpClient httpClient) throws IOException { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerClient conversationClient = setupClient(builder, "downloadContent404"); + + ByteArrayOutputStream byteArrayOutputStream = Mockito.mock(ByteArrayOutputStream.class); + doThrow(IOException.class).when(byteArrayOutputStream).write(Mockito.any()); + assertThrows( + UncheckedIOException.class, + () -> conversationClient + .downloadTo(METADATA_URL, byteArrayOutputStream, null)); + } + + private CallingServerClient setupClient(CallingServerClientBuilder builder, String testName) { + return addLoggingPolicy(builder, testName).buildClient(); + } + + protected CallingServerClientBuilder addLoggingPolicy(CallingServerClientBuilder builder, String testName) { + return builder.addPolicy((context, next) -> logHeaders(testName, next)); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/ServerCallAsyncLiveTests.java b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/ServerCallAsyncLiveTests.java new file mode 100644 index 0000000000000..8cbedef58a9d2 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/ServerCallAsyncLiveTests.java @@ -0,0 +1,386 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import com.azure.communication.callingserver.models.CallModality; +import com.azure.communication.callingserver.models.CallRecordingState; +import com.azure.communication.callingserver.models.CallRecordingStateResult; +import com.azure.communication.callingserver.models.CallingServerErrorException; +import com.azure.communication.callingserver.models.CreateCallOptions; +import com.azure.communication.callingserver.models.EventSubscriptionType; +import com.azure.communication.callingserver.models.PlayAudioResult; +import com.azure.communication.callingserver.models.StartCallRecordingResult; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.communication.common.CommunicationUserIdentifier; +import com.azure.communication.common.PhoneNumberIdentifier; +import com.azure.core.http.HttpClient; +import com.azure.core.http.rest.Response; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class ServerCallAsyncLiveTests extends CallingServerTestBase { + + private final String groupId = getGroupId(); + private final String fromUser = getNewUserId(); + private final String toUser = getNewUserId(); + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runAllClientFunctionsAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerAsyncClient callingServerAsyncClient = + setupAsyncClient(builder, "runAllClientFunctionsAsync"); + String recordingId = ""; + List callConnections = new ArrayList<>(); + ServerCallAsync serverCall = null; + + try { + callConnections = createAsyncCall(callingServerAsyncClient, groupId, fromUser, toUser, CALLBACK_URI); + serverCall = callingServerAsyncClient.initializeServerCall(groupId); + + StartCallRecordingResult startCallRecordingResult = serverCall.startRecording(CALLBACK_URI).block(); + assert startCallRecordingResult != null; + recordingId = startCallRecordingResult.getRecordingId(); + validateCallRecordingState(serverCall, recordingId, CallRecordingState.ACTIVE); + + serverCall.pauseRecording(recordingId).block(); + validateCallRecordingState(serverCall, recordingId, CallRecordingState.INACTIVE); + + serverCall.resumeRecording(recordingId).block(); + validateCallRecordingState(serverCall, recordingId, CallRecordingState.ACTIVE); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } finally { + if (serverCall != null) { + try { + serverCall.stopRecording(recordingId).block(); + } catch (Exception e) { + System.out.println("Error stopping recording: " + e.getMessage()); + } + } + + cleanUpConnectionsAsync(callConnections); + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runAllClientFunctionsWithResponseAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerAsyncClient callingServerAsyncClient = + setupAsyncClient(builder, "runAllClientFunctionsWithResponseAsync"); + String recordingId = ""; + List callConnections = new ArrayList<>(); + ServerCallAsync serverCallAsync = null; + + try { + callConnections = createAsyncCall(callingServerAsyncClient, groupId, fromUser, toUser, CALLBACK_URI); + serverCallAsync = callingServerAsyncClient.initializeServerCall(groupId); + + Response startRecordingResponse = + serverCallAsync.startRecordingWithResponse(CALLBACK_URI).block(); + assert startRecordingResponse != null; + assertEquals(startRecordingResponse.getStatusCode(), 200); + StartCallRecordingResult startCallRecordingResult = startRecordingResponse.getValue(); + recordingId = startCallRecordingResult.getRecordingId(); + validateCallRecordingStateWithResponse(serverCallAsync, recordingId, CallRecordingState.ACTIVE); + + Response pauseResponse = serverCallAsync.pauseRecordingWithResponse(recordingId).block(); + assert pauseResponse != null; + assertEquals(pauseResponse.getStatusCode(), 200); + validateCallRecordingStateWithResponse(serverCallAsync, recordingId, CallRecordingState.INACTIVE); + + Response resumeResponse = serverCallAsync.resumeRecordingWithResponse(recordingId).block(); + assert resumeResponse != null; + assertEquals(resumeResponse.getStatusCode(), 200); + validateCallRecordingStateWithResponse(serverCallAsync, recordingId, CallRecordingState.ACTIVE); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } finally { + if (serverCallAsync != null) { + try { + Response stopResponse = serverCallAsync.stopRecordingWithResponse(recordingId).block(); + assert stopResponse != null; + assertEquals(stopResponse.getStatusCode(), 200); + } catch (Exception e) { + System.out.println("Error stopping recording: " + e.getMessage()); + } + } + + cleanUpConnectionsAsync(callConnections); + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runPlayAudioFunctionAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerAsyncClient callingServerAsyncClient = + setupAsyncClient(builder, "runPlayAudioFunctionAsync"); + ServerCallAsync serverCallAsync; + + List callConnections = new ArrayList<>(); + String operationContext = UUID.randomUUID().toString(); + + try { + callConnections = createAsyncCall(callingServerAsyncClient, groupId, fromUser, toUser, CALLBACK_URI); + serverCallAsync = callingServerAsyncClient.initializeServerCall(groupId); + + PlayAudioResult playAudioResult = + serverCallAsync.playAudio(AUDIO_FILE_URI, operationContext, CALLBACK_URI, operationContext).block(); + CallingServerTestUtils.validatePlayAudioResult(playAudioResult); + + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } finally { + cleanUpConnectionsAsync(callConnections); + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runPlayAudioFunctionWithResponseAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerAsyncClient callingServerAsyncClient = + setupAsyncClient(builder, "runPlayAudioFunctionWithResponseAsync"); + ServerCallAsync serverCallAsync; + + List callConnections = new ArrayList<>(); + String operationContext = UUID.randomUUID().toString(); + + try { + callConnections = createAsyncCall(callingServerAsyncClient, groupId, fromUser, toUser, CALLBACK_URI); + serverCallAsync = callingServerAsyncClient.initializeServerCall(groupId); + + Response playAudioResult = + serverCallAsync.playAudioWithResponse( + AUDIO_FILE_URI, + operationContext, + CALLBACK_URI, + operationContext).block(); + CallingServerTestUtils.validatePlayAudioResponse(playAudioResult); + + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } finally { + cleanUpConnectionsAsync(callConnections); + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void startRecordingFailsAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerAsyncClient callingServerAsyncClient = setupAsyncClient(builder, "startRecordingFailsAsync"); + String invalidServerCallId = "aHR0cHM6Ly9jb252LXVzd2UtMDkuY29udi5za3lwZS5jb20vY29udi9EZVF2WEJGVVlFV1NNZkFXYno2azN3P2k9MTEmZT02Mzc1NzIyMjk0Mjc0NTI4Nzk="; + ServerCallAsync serverCallAsync = callingServerAsyncClient.initializeServerCall(invalidServerCallId); + + try { + Response response = + serverCallAsync.startRecordingWithResponse(CALLBACK_URI).block(); + assert response != null; + assertEquals(response.getStatusCode(), 400); + } catch (CallingServerErrorException e) { + assertEquals(e.getResponse().getStatusCode(), 400); + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runAddRemoveScenarioAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerAsyncClient callingServerAsyncClient = + setupAsyncClient(builder, "runAddRemoveScenarioAsync"); + + try { + // Establish a call + CreateCallOptions options = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + options.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + CallConnectionAsync callConnectionAsync = callingServerAsyncClient.createCallConnection( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + options).block(); + + CallingServerTestUtils.validateCallConnectionAsync(callConnectionAsync); + + // Get Server Call + /* + Waiting for an update to be able to get this serverCallId when using + createCallConnection() + */ + String serverCallId = "aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L3NXdWxkazBmMEVpdnAxWjhiU2NuUHc_aT0yJmU9NjM3NTg0Mzk2NDM5NzQ5NzY4"; + ServerCallAsync serverCallAsync = callingServerAsyncClient.initializeServerCall(serverCallId); + + // Add User + String operationContext = UUID.randomUUID().toString(); + serverCallAsync + .addParticipant( + new CommunicationUserIdentifier(toUser), + null, + operationContext, + CALLBACK_URI) + .block(); + + // Remove User + /* + There is an update that we require to be able to get + the participantId from the service when a user is + added to a call. Until that is fixed this recorded + value needs to be used. + */ + String participantId = "206ac04a-1aae-4d82-9015-9c30cb174888"; + serverCallAsync.removeParticipant(participantId).block(); + + // Hang up + assert callConnectionAsync != null; + callConnectionAsync.hangup().block(); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runAddRemoveScenarioWithResponseAsync(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerAsyncClient callingServerAsyncClient = setupAsyncClient(builder, "runAddRemoveScenarioWithResponseAsync"); + + try { + // Establish a call + CreateCallOptions options = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + options.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + CallConnectionAsync callConnectionAsync = callingServerAsyncClient.createCallConnection( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + options).block(); + + CallingServerTestUtils.validateCallConnectionAsync(callConnectionAsync); + + // Get Server Call + /* + Waiting for an update to be able to get this serverCallId when using + createCallConnection() + */ + String serverCallId = "aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L0NUT014YmNIRmttZ1BqbE5kYjExNlE_aT0yJmU9NjM3NTg0Mzk2NDM5NzQ5NzY4"; + ServerCallAsync serverCallAsync = callingServerAsyncClient.initializeServerCall(serverCallId); + + // Add User + String operationContext = UUID.randomUUID().toString(); + Response addResponse = + serverCallAsync + .addParticipantWithResponse( + new CommunicationUserIdentifier(toUser), + null, + operationContext, + CALLBACK_URI) + .block(); + CallingServerTestUtils.validateResponse(addResponse); + + // Remove User + /* + There is an update that we require to be able to get + the participantId from the service when a user is + added to a call. Until that is fixed this recorded + value needs to be used. + */ + String participantId = "b133b1f3-4a11-49e4-abe0-ac9fdd660634"; + Response removeResponse = serverCallAsync.removeParticipantWithResponse(participantId).block(); + CallingServerTestUtils.validateResponse(removeResponse); + + // Hang up + assert callConnectionAsync != null; + callConnectionAsync.hangup().block(); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + private CallingServerAsyncClient setupAsyncClient(CallingServerClientBuilder builder, String testName) { + return addLoggingPolicy(builder, testName).buildAsyncClient(); + } + + protected CallingServerClientBuilder addLoggingPolicy(CallingServerClientBuilder builder, String testName) { + return builder.addPolicy((context, next) -> logHeaders(testName, next)); + } + + private void validateCallRecordingState(ServerCallAsync serverCallAsync, + String recordingId, + CallRecordingState expectedCallRecordingState) { + assertNotNull(serverCallAsync); + assertNotNull(serverCallAsync.getServerCallId()); + assertNotNull(recordingId); + + + // There is a delay between the action and when the state is available. + // Waiting to make sure we get the updated state, when we are running + // against a live service. + sleepIfRunningAgainstService(6000); + + CallRecordingStateResult callRecordingStateResult = serverCallAsync.getRecordingState(recordingId).block(); + assert callRecordingStateResult != null; + assertEquals(callRecordingStateResult.getRecordingState(), expectedCallRecordingState); + } + + protected void validateCallRecordingStateWithResponse( + ServerCallAsync serverCallAsync, + String recordingId, + CallRecordingState expectedCallRecordingState) { + assertNotNull(serverCallAsync); + assertNotNull(serverCallAsync.getServerCallId()); + assertNotNull(recordingId); + + + // There is a delay between the action and when the state is available. + // Waiting to make sure we get the updated state, when we are running + // against a live service. + sleepIfRunningAgainstService(6000); + + Response response = + serverCallAsync.getRecordingStateWithResponse(recordingId).block(); + assertNotNull(response); + assertEquals(response.getStatusCode(), 200); + assertNotNull(response.getValue()); + assertEquals(response.getValue().getRecordingState(), expectedCallRecordingState); + } + + protected void cleanUpConnectionsAsync(List connections) { + if (connections == null) { + return; + } + + connections.forEach(c -> { + if (c != null) { + try { + c.hangup().block(); + } catch (Exception e) { + System.out.println("Error hanging up: " + e.getMessage()); + } + } + }); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/ServerCallLiveTests.java b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/ServerCallLiveTests.java new file mode 100644 index 0000000000000..bcd07a3d1008c --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/ServerCallLiveTests.java @@ -0,0 +1,370 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import com.azure.communication.callingserver.models.CallModality; +import com.azure.communication.callingserver.models.CallRecordingState; +import com.azure.communication.callingserver.models.CallRecordingStateResult; +import com.azure.communication.callingserver.models.CallingServerErrorException; +import com.azure.communication.callingserver.models.CreateCallOptions; +import com.azure.communication.callingserver.models.EventSubscriptionType; +import com.azure.communication.callingserver.models.PlayAudioResult; +import com.azure.communication.callingserver.models.StartCallRecordingResult; +import com.azure.communication.common.CommunicationIdentifier; +import com.azure.communication.common.CommunicationUserIdentifier; +import com.azure.communication.common.PhoneNumberIdentifier; +import com.azure.core.http.HttpClient; +import com.azure.core.http.rest.Response; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static com.azure.communication.callingserver.CallingServerTestUtils.validateCallConnection; +import static com.azure.communication.callingserver.CallingServerTestUtils.validatePlayAudioResponse; +import static com.azure.communication.callingserver.CallingServerTestUtils.validatePlayAudioResult; +import static com.azure.communication.callingserver.CallingServerTestUtils.validateResponse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class ServerCallLiveTests extends CallingServerTestBase { + + private final String groupId = getGroupId(); + private final String fromUser = getNewUserId(); + private final String toUser = getNewUserId(); + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runAllClientFunctions(HttpClient httpClient) { + CallingServerClientBuilder builder = getCallClientUsingConnectionString(httpClient); + CallingServerClient callingServerClient = setupClient(builder, "runAllClientFunctions"); + String recordingId = ""; + List callConnections = new ArrayList<>(); + ServerCall serverCall = null; + + try { + callConnections = createCall(callingServerClient, groupId, fromUser, toUser, CALLBACK_URI); + serverCall = callingServerClient.initializeServerCall(groupId); + + StartCallRecordingResult startCallRecordingResult = serverCall.startRecording(CALLBACK_URI); + recordingId = startCallRecordingResult.getRecordingId(); + validateCallRecordingState(serverCall, recordingId, CallRecordingState.ACTIVE); + + serverCall.pauseRecording(recordingId); + validateCallRecordingState(serverCall, recordingId, CallRecordingState.INACTIVE); + + serverCall.resumeRecording(recordingId); + validateCallRecordingState(serverCall, recordingId, CallRecordingState.ACTIVE); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } finally { + if (serverCall != null) { + try { + serverCall.stopRecording(recordingId); + } catch (Exception e) { + System.out.println("Error stopping recording: " + e.getMessage()); + } + } + + cleanUpConnections(callConnections); + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runAllClientFunctionsWithResponse(HttpClient httpClient) { + CallingServerClientBuilder builder = getCallClientUsingConnectionString(httpClient); + CallingServerClient callingServerClient = setupClient(builder, "runAllClientFunctionsWithResponse"); + String recordingId = ""; + List callConnections = new ArrayList<>(); + ServerCall serverCall = null; + + try { + callConnections = createCall(callingServerClient, groupId, fromUser, toUser, CALLBACK_URI); + serverCall = callingServerClient.initializeServerCall(groupId); + + Response startRecordingResponse = + serverCall.startRecordingWithResponse(CALLBACK_URI, null); + assertEquals(startRecordingResponse.getStatusCode(), 200); + StartCallRecordingResult startCallRecordingResult = startRecordingResponse.getValue(); + recordingId = startCallRecordingResult.getRecordingId(); + validateCallRecordingStateWithResponse(serverCall, recordingId, CallRecordingState.ACTIVE); + + Response pauseResponse = serverCall.pauseRecordingWithResponse(recordingId, null); + assertEquals(pauseResponse.getStatusCode(), 200); + validateCallRecordingStateWithResponse(serverCall, recordingId, CallRecordingState.INACTIVE); + + Response resumeResponse = serverCall.resumeRecordingWithResponse(recordingId, null); + assertEquals(resumeResponse.getStatusCode(), 200); + validateCallRecordingStateWithResponse(serverCall, recordingId, CallRecordingState.ACTIVE); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } finally { + if (serverCall != null) { + try { + Response stopResponse = serverCall.stopRecordingWithResponse(recordingId, null); + assertEquals(stopResponse.getStatusCode(), 200); + } catch (Exception e) { + System.out.println("Error stopping recording: " + e.getMessage()); + } + } + + cleanUpConnections(callConnections); + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runPlayAudioFunction(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerClient callingServerClient = setupClient(builder, "runPlayAudioFunction"); + ServerCall serverCall; + + List callConnections = new ArrayList<>(); + String operationContext = UUID.randomUUID().toString(); + + try { + callConnections = createCall(callingServerClient, groupId, fromUser, toUser, CALLBACK_URI); + serverCall = callingServerClient.initializeServerCall(groupId); + + PlayAudioResult playAudioResult = + serverCall.playAudio(AUDIO_FILE_URI, UUID.randomUUID().toString(), CALLBACK_URI, operationContext); + validatePlayAudioResult(playAudioResult); + + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } finally { + cleanUpConnections(callConnections); + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runPlayAudioFunctionWithResponse(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerClient callingServerClient = setupClient(builder, "runPlayAudioFunctionWithResponse"); + ServerCall serverCall; + + List callConnections = new ArrayList<>(); + String operationContext = UUID.randomUUID().toString(); + + try { + callConnections = createCall(callingServerClient, groupId, fromUser, toUser, CALLBACK_URI); + serverCall = callingServerClient.initializeServerCall(groupId); + + Response playAudioResult = + serverCall.playAudioWithResponse( + AUDIO_FILE_URI, operationContext, + CALLBACK_URI, operationContext, + null); + validatePlayAudioResponse(playAudioResult); + + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } finally { + cleanUpConnections(callConnections); + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void startRecordingFails(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerClient callingServerClient = setupClient(builder, "startRecordingFails"); + String invalidServerCallId = "aHR0cHM6Ly9jb252LXVzd2UtMDkuY29udi5za3lwZS5jb20vY29udi9EZVF2WEJGVVlFV1NNZkFXYno2azN3P2k9MTEmZT02Mzc1NzIyMjk0Mjc0NTI4Nzk="; + ServerCall serverCall = callingServerClient.initializeServerCall(invalidServerCallId); + + try { + Response response = + serverCall.startRecordingWithResponse(CALLBACK_URI, null); + assertEquals(response.getStatusCode(), 400); + } catch (CallingServerErrorException e) { + assertEquals(e.getResponse().getStatusCode(), 400); + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runAddRemoveScenario(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerClient callingServerClient = setupClient(builder, "runAddRemoveScenario"); + try { + // Establish a call + CreateCallOptions options = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + options.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + CallConnection callConnection = callingServerClient.createCallConnection( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + options); + + validateCallConnection(callConnection); + + // Get Server Call + /* + Waiting for an update to be able to get this serverCallId when using + createCallConnection() + */ + String serverCallId = "aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L1ktWjZ5dzFzWVVTUUdWX2xPQWk1X2c_aT0xJmU9NjM3NTg0MzkzMzg3ODg3MDI3"; + ServerCall serverCall = callingServerClient.initializeServerCall(serverCallId); + + // Add User + String operationContext = UUID.randomUUID().toString(); + serverCall + .addParticipant( + new CommunicationUserIdentifier(toUser), + null, + operationContext, + CALLBACK_URI); + + // Remove User + /* + There is an update that we require to be able to get + the participantId from the service when a user is + added to a call. Until that is fixed this recorded + value needs to be used. + */ + String participantId = "72647661-033a-4d1a-b858-465375977be0"; + serverCall.removeParticipant(participantId); + + // Hangup + callConnection.hangup(); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + public void runAddRemoveScenarioWithResponse(HttpClient httpClient) { + CallingServerClientBuilder builder = getConversationClientUsingConnectionString(httpClient); + CallingServerClient callingServerClient = setupClient(builder, "runAddRemoveScenarioWithResponse"); + + try { + // Establish a call + CreateCallOptions options = new CreateCallOptions( + CALLBACK_URI, + new CallModality[] { CallModality.AUDIO }, + new EventSubscriptionType[] { EventSubscriptionType.PARTICIPANTS_UPDATED }); + + options.setAlternateCallerId(new PhoneNumberIdentifier(FROM_PHONE_NUMBER)); + + CallConnection callConnection = callingServerClient.createCallConnection( + new CommunicationUserIdentifier(fromUser), + new CommunicationIdentifier[] { new PhoneNumberIdentifier(TO_PHONE_NUMBER) }, + options); + + validateCallConnection(callConnection); + + // Get Server Call + /* + Waiting for an update to be able to get this serverCallId when using + createCallConnection() + */ + String serverCallId = "aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L1lXS2R2TTNRc0Vpc0VNYVUtNlhvSlE_aT0yJmU9NjM3NTg0Mzk2NDM5NzQ5NzY4"; + ServerCall serverCall = callingServerClient.initializeServerCall(serverCallId); + + // Add User + String operationContext = UUID.randomUUID().toString(); + Response addResponse = + serverCall.addParticipantWithResponse( + new CommunicationUserIdentifier(toUser), + null, + operationContext, CALLBACK_URI, + null); + validateResponse(addResponse); + + // Remove User + /* + There is an update that we require to be able to get + the participantId from the service when a user is + added to a call. Until that is fixed this recorded + values needs to be used. + */ + String participantId = "76b33acb-5097-4af0-a646-e07ccee48957"; + Response removeResponse = serverCall.removeParticipantWithResponse(participantId, null); + validateResponse(removeResponse); + + // Hangup + callConnection.hangup(); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + throw e; + } + } + + private CallingServerClient setupClient(CallingServerClientBuilder builder, String testName) { + return addLoggingPolicy(builder, testName).buildClient(); + } + + protected CallingServerClientBuilder addLoggingPolicy(CallingServerClientBuilder builder, String testName) { + return builder.addPolicy((context, next) -> logHeaders(testName, next)); + } + + private void validateCallRecordingState( + ServerCall serverCall, + String recordingId, + CallRecordingState expectedCallRecordingState) { + assertNotNull(serverCall); + assertNotNull(serverCall.getServerCallId()); + assertNotNull(recordingId); + + // There is a delay between the action and when the state is available. + // Waiting to make sure we get the updated state, when we are running + // against a live service. + sleepIfRunningAgainstService(6000); + + CallRecordingStateResult callRecordingStateResult = serverCall.getRecordingState(recordingId); + assertEquals(callRecordingStateResult.getRecordingState(), expectedCallRecordingState); + } + + protected void validateCallRecordingStateWithResponse( + ServerCall serverCall, + String recordingId, + CallRecordingState expectedCallRecordingState) { + assertNotNull(serverCall); + assertNotNull(serverCall.getServerCallId()); + assertNotNull(recordingId); + + + // There is a delay between the action and when the state is available. + // Waiting to make sure we get the updated state, when we are running + // against a live service. + sleepIfRunningAgainstService(6000); + + Response response = + serverCall.getRecordingStateWithResponse(recordingId, null); + assertNotNull(response); + assertEquals(response.getStatusCode(), 200); + assertNotNull(response.getValue()); + assertEquals(response.getValue().getRecordingState(), expectedCallRecordingState); + } + + protected void cleanUpConnections(List connections) { + if (connections == null) { + return; + } + + connections.forEach(c -> { + if (c != null) { + try { + c.hangup(); + } catch (Exception e) { + System.out.println("Error hanging up: " + e.getMessage()); + } + } + }); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/ServerCallUnitTests.java b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/ServerCallUnitTests.java new file mode 100644 index 0000000000000..e79c9e38217b1 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/java/com/azure/communication/callingserver/ServerCallUnitTests.java @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.communication.callingserver; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import java.security.InvalidParameterException; +import com.azure.core.test.http.NoOpHttpClient; +import org.junit.jupiter.api.Test; + +public class ServerCallUnitTests { + + private final String serverCallId = "aHR0cHM6Ly9jb252LXVzd2UtMDguY29udi5za3lwZS5jb20vY29udi8tby1FWjVpMHJrS3RFTDBNd0FST1J3P2k9ODgmZT02Mzc1Nzc0MTY4MDc4MjQyOTM"; + static final String MOCK_CONNECTION_STRING = "endpoint=https://REDACTED.communication.azure.com/;accesskey=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaGfQSflKxwRJSMeKKF2QT4fwpMeJf36POk6yJVadQssw5c"; + + @Test + public void startRecordingRelativeUriFails() { + ServerCall serverCall = new CallingServerClientBuilder() + .httpClient(new NoOpHttpClient()) + .connectionString(MOCK_CONNECTION_STRING) + .buildClient() + .initializeServerCall(serverCallId); + + assertThrows( + InvalidParameterException.class, + () -> serverCall.startRecording("/not/absolute/uri")); + } + + @Test + public void startRecordingWithResponseRelativeUriFails() { + ServerCall serverCall = new CallingServerClientBuilder() + .httpClient(new NoOpHttpClient()) + .connectionString(MOCK_CONNECTION_STRING) + .buildClient() + .initializeServerCall(serverCallId); + + assertThrows( + InvalidParameterException.class, + () -> serverCall.startRecordingWithResponse("/not/absolute/uri", null)); + } + + @Test + public void addParticipantNullParticipantFails() { + ServerCall serverCall = new CallingServerClientBuilder() + .httpClient(new NoOpHttpClient()) + .connectionString(MOCK_CONNECTION_STRING) + .buildClient() + .initializeServerCall(serverCallId); + + assertThrows( + NullPointerException.class, + () -> serverCall.addParticipant(null, null, null, null)); + } + + @Test + public void startRecordingAsyncFails() { + ServerCallAsync serverCall = new CallingServerClientBuilder() + .httpClient(new NoOpHttpClient()) + .connectionString(MOCK_CONNECTION_STRING) + .buildAsyncClient() + .initializeServerCall(serverCallId); + + assertThrows( + InvalidParameterException.class, + () -> serverCall.startRecording("/not/absolute/uri") + .block()); + } +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreateAddRemoveHangupScenarioAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreateAddRemoveHangupScenarioAsync[1].json new file mode 100644 index 0000000000000..6b9800527f558 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreateAddRemoveHangupScenarioAsync[1].json @@ -0,0 +1,76 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0g6/BYAAAAAC5tIq/4AviQoljmgzCwywqQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "99b7c872-47fb-442d-86ef-fbd9e8cf6276", + "Body" : "{\"callLegId\":\"23201300-e9cb-4e9f-aafc-723c9927ed3b\",\"callConnectionId\":\"23201300-e9cb-4e9f-aafc-723c9927ed3b\"}", + "Date" : "Thu, 10 Jun 2021 06:21:54 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "98bab7f8-0a45-46ce-b96b-444dea287328" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/23201300-e9cb-4e9f-aafc-723c9927ed3b/participants?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0j6/BYAAAAACd5NLDqXwxQYQuljIjMJRSQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "59d5c622-ee66-46fd-9eb1-321f2a8c7b34", + "Date" : "Thu, 10 Jun 2021 06:22:07 GMT", + "Client-Request-Id" : "934f0e4d-3730-4b13-be1a-9d433a8591ef" + }, + "Exception" : null + }, { + "Method" : "DELETE", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/23201300-e9cb-4e9f-aafc-723c9927ed3b/participants/e3560385-776f-41d1-bf04-07ef738f2fc1?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0sK/BYAAAAACDUE2SAY0iS79tU/9jgC0VQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "63c5b9de-8f21-40df-8c82-6ee10cb500d4", + "Date" : "Thu, 10 Jun 2021 06:22:40 GMT", + "Client-Request-Id" : "4af9e778-7f7f-4b42-af7d-e92a7861ac19" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/23201300-e9cb-4e9f-aafc-723c9927ed3b/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0va/BYAAAAAC7CRS+ZcsFQLfj1P+lwHVUQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "f571efc2-8305-4401-8050-80a434d3150d", + "Date" : "Thu, 10 Jun 2021 06:22:52 GMT", + "Client-Request-Id" : "7e7b29cd-e859-408a-9eb4-4c9067daa0cc" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreateAddRemoveHangupScenarioWithResponseAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreateAddRemoveHangupScenarioWithResponseAsync[1].json new file mode 100644 index 0000000000000..635d7a6a902a0 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreateAddRemoveHangupScenarioWithResponseAsync[1].json @@ -0,0 +1,76 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "06K/BYAAAAAAN9wbfZannTpOQceLWBzNAREZXMzBFREdFMDUxNAA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "108155da-5e7f-4139-ad3a-b26caf20e4e8", + "Body" : "{\"callLegId\":\"4a201300-dd6b-4743-9f72-3bd93a226187\",\"callConnectionId\":\"4a201300-dd6b-4743-9f72-3bd93a226187\"}", + "Date" : "Thu, 10 Jun 2021 06:23:37 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "69b44cb6-9d94-429a-abbb-dd577866e343" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/4a201300-dd6b-4743-9f72-3bd93a226187/participants?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0+q/BYAAAAADt8lJ4jRXzRbnu/jS3NDmzREZXMzBFREdFMDUxNAA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "54894a95-0a4a-43ce-8114-9ca615173fd3", + "Date" : "Thu, 10 Jun 2021 06:23:53 GMT", + "Client-Request-Id" : "a9e41c63-d2fc-4919-b2fd-1e84cbac1bc8" + }, + "Exception" : null + }, { + "Method" : "DELETE", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/4a201300-dd6b-4743-9f72-3bd93a226187/participants/80238d5f-9eda-481a-b911-e2e12eba9eda?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0LrDBYAAAAADjjidTX1wcQpJMnWmXm8ADREZXMzBFREdFMDUxNAA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "4f9e6379-8238-40f1-b017-dfc76b58e687", + "Date" : "Thu, 10 Jun 2021 06:24:45 GMT", + "Client-Request-Id" : "87328aca-8f5e-4806-b529-22ef87517ef3" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/4a201300-dd6b-4743-9f72-3bd93a226187/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0MrDBYAAAAAAPJfufWdZNT7RmykxwPrGoREZXMzBFREdFMDUxNAA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "9288a08a-a906-4926-ba1c-7a6d1f942345", + "Date" : "Thu, 10 Jun 2021 06:24:50 GMT", + "Client-Request-Id" : "849b2df1-7a22-471c-b262-30827a720ac5" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreateJoinHangupScenarioAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreateJoinHangupScenarioAsync[1].json new file mode 100644 index 0000000000000..2c6945f051f7b --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreateJoinHangupScenarioAsync[1].json @@ -0,0 +1,78 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0arDBYAAAAAD0zhSRdGbUSYGAbulLm8HEREZXMzBFREdFMDUxMgA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "3f6bc040-20a9-4a7e-8ce0-abbaa77441cd", + "Body" : "{\"callLegId\":\"e51f1300-7be7-4d43-8a06-39e0b378fd83\",\"callConnectionId\":\"e51f1300-7be7-4d43-8a06-39e0b378fd83\"}", + "Date" : "Thu, 10 Jun 2021 06:25:47 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "9acb1ae6-9de3-4f10-85a4-de9358a5f186" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L3VodHNzZEZ3NFVHX1J4d1lHYWlLRmc_aT0yJmU9NjM3NTg0Mzk2NDM5NzQ5NzY4/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0jrDBYAAAAAA//s7DmwdCSJ0gVo/D3W5hREZXMzBFREdFMDUxMgA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "4c960d1d-dd8b-48d7-9d4a-3bc7324ce63b", + "Body" : "{\"callLegId\":\"e51f1300-846a-4ef7-b03a-58307b8a2839\",\"callConnectionId\":\"e51f1300-846a-4ef7-b03a-58307b8a2839\"}", + "Date" : "Thu, 10 Jun 2021 06:26:22 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "fc600b71-e142-4002-a853-cb5a4b10e8a1" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/e51f1300-7be7-4d43-8a06-39e0b378fd83/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0kbDBYAAAAADxRFjijyobRpr4LV9BaixXREZXMzBFREdFMDUxMgA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "920ad44f-317d-4e70-b1bc-a972bcb86b8e", + "Date" : "Thu, 10 Jun 2021 06:26:25 GMT", + "Client-Request-Id" : "75bf583d-b89b-4391-b23c-fd5a0876af93" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/e51f1300-846a-4ef7-b03a-58307b8a2839/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0lbDBYAAAAAAih8aWJC6rRJbO77PWFeVKREZXMzBFREdFMDUxMgA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "d11ea42b-a748-478c-b9ba-8013b67a7293", + "Date" : "Thu, 10 Jun 2021 06:26:29 GMT", + "Client-Request-Id" : "d090a8ec-3a29-471a-a97e-6d98a67a7925" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreateJoinHangupScenarioWithResponseAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreateJoinHangupScenarioWithResponseAsync[1].json new file mode 100644 index 0000000000000..521ebc30bb769 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreateJoinHangupScenarioWithResponseAsync[1].json @@ -0,0 +1,78 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "04bDBYAAAAAAerxQ5l6BxTZmUrXY9x4ihQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "8eb1d8db-cde8-4bf5-8fce-f06cf0d926ad", + "Body" : "{\"callLegId\":\"bd1f1300-1312-4713-99b2-f66bca8ce411\",\"callConnectionId\":\"bd1f1300-1312-4713-99b2-f66bca8ce411\"}", + "Date" : "Thu, 10 Jun 2021 06:27:46 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "d32f7294-8d6a-46f6-acc9-2ae9020580bc" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L3lKQXY0TnVlOEV5bUpYVm1IYklIeUE_aT0wJmU9NjM3NTg0MzkwMjcxMzg0MTc3/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0B7HBYAAAAAAA4UyZPP+YSJVnvNbI8m6DQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "b9ff80b4-a9a6-4da9-a984-d6aa9796e2b9", + "Body" : "{\"callLegId\":\"bd1f1300-aa94-4d24-a6bb-b608aedda09f\",\"callConnectionId\":\"bd1f1300-aa94-4d24-a6bb-b608aedda09f\"}", + "Date" : "Thu, 10 Jun 2021 06:28:23 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "b1e8b188-b69e-4c31-845a-f502405fc779" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/bd1f1300-1312-4713-99b2-f66bca8ce411/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0C7HBYAAAAAARlQh4d5DAT72QebrfEeXPQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "ea69c668-e0da-4fd8-8b42-6ab1327e76dd", + "Date" : "Thu, 10 Jun 2021 06:28:27 GMT", + "Client-Request-Id" : "356ea646-2b64-4776-afdd-9807c1a5cfb1" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/bd1f1300-aa94-4d24-a6bb-b608aedda09f/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0DLHBYAAAAAAFCA1PArCvR4IvIanrtC9HQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "f96fe948-8775-4628-8b87-56e79fba4ff3", + "Date" : "Thu, 10 Jun 2021 06:28:28 GMT", + "Client-Request-Id" : "7985dd5b-28c9-4378-99b9-d566c4ee1ef4" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreatePlayCancelHangupScenarioAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreatePlayCancelHangupScenarioAsync[1].json new file mode 100644 index 0000000000000..9486d61735166 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreatePlayCancelHangupScenarioAsync[1].json @@ -0,0 +1,81 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0KK/BYAAAAAAOPBE4tvoUS6uIGQX89MQ3Q0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "8e348ea6-2214-4c71-abd2-2f5934b17bc5", + "Body" : "{\"callLegId\":\"23201300-f409-41c5-a4df-7ae1daf22d01\",\"callConnectionId\":\"23201300-f409-41c5-a4df-7ae1daf22d01\"}", + "Date" : "Thu, 10 Jun 2021 06:20:25 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "0e6ebbf8-941d-45ee-9aa2-d5ec685b1ccd" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/23201300-f409-41c5-a4df-7ae1daf22d01/:playAudio?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "122", + "retry-after" : "0", + "X-Azure-Ref" : "0Na/BYAAAAABDYqtF2UPERq1r8wuEL6R/Q0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "1c683a1a-1a61-4d7f-bfb7-db2fa22b3dcc", + "Body" : "{\"id\":\"9659a4e6-f504-4cfb-b0df-4ea7596d14d2\",\"status\":\"running\",\"operationContext\":\"ac794123-3820-4979-8e2d-50c7d3e07b12\"}", + "Date" : "Thu, 10 Jun 2021 06:20:37 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "fc824212-2f89-4acd-b5a4-33bfae98804f" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/23201300-f409-41c5-a4df-7ae1daf22d01/:cancelAllMediaOperations?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "124", + "retry-after" : "0", + "X-Azure-Ref" : "0OK/BYAAAAABomV7CxJVoSauizye79yd6Q0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "f99391df-5893-4c1e-b4af-33a658b718ad", + "Body" : "{\"id\":\"3b7fcdbc-b6fa-41ac-9407-a42e5ac0cd75\",\"status\":\"completed\",\"operationContext\":\"ac794123-3820-4979-8e2d-50c7d3e07b13\"}", + "Date" : "Thu, 10 Jun 2021 06:20:40 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "e2f09192-a8f8-43f5-a2e8-c19f0f5f45af" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/23201300-f409-41c5-a4df-7ae1daf22d01/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0PK/BYAAAAAA4zaCpX5SHRLDDRm48hh9DQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "b4f1ee34-104b-4dfb-809c-29bb7ea0b781", + "Date" : "Thu, 10 Jun 2021 06:20:43 GMT", + "Client-Request-Id" : "3b843396-dd22-467f-b8eb-e2695f987c90" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreatePlayCancelHangupScenarioWithResponseAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreatePlayCancelHangupScenarioWithResponseAsync[1].json new file mode 100644 index 0000000000000..1c3796e668578 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionAsyncLiveTests.runCreatePlayCancelHangupScenarioWithResponseAsync[1].json @@ -0,0 +1,81 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0T6/BYAAAAADf/TZqAkTRS4u74Irk6dMbQ0hHRURHRTE2MTQAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "ac6c39a4-1390-4561-92de-6253164dbab1", + "Body" : "{\"callLegId\":\"d21f1300-2cee-4b87-ade5-d381d32041d7\",\"callConnectionId\":\"d21f1300-2cee-4b87-ade5-d381d32041d7\"}", + "Date" : "Thu, 10 Jun 2021 06:21:03 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "6525df6b-a1a2-48e3-b8af-74bf3b292ece" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/d21f1300-2cee-4b87-ade5-d381d32041d7/:playAudio?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "122", + "retry-after" : "0", + "X-Azure-Ref" : "0YK/BYAAAAABHopBv9/jaT7fmCiBRlCo1Q0hHRURHRTE2MTQAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "c43d6028-a146-446d-b158-ef9bbbbf3d3d", + "Body" : "{\"id\":\"6c40aa2b-7cff-42f7-ae5e-4ff3407a1571\",\"status\":\"running\",\"operationContext\":\"ac794123-3820-4979-8e2d-50c7d3e07b12\"}", + "Date" : "Thu, 10 Jun 2021 06:21:20 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "d9b75393-4b5e-4566-844a-2d55de9bf669" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/d21f1300-2cee-4b87-ade5-d381d32041d7/:cancelAllMediaOperations?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "124", + "retry-after" : "0", + "X-Azure-Ref" : "0Y6/BYAAAAADMQgdTl7v7Q7gut4WETRXWQ0hHRURHRTE2MTQAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "65c91265-3298-4919-a290-2c4d622a6a5a", + "Body" : "{\"id\":\"35568c58-8600-488c-acf2-ed244d74c5da\",\"status\":\"completed\",\"operationContext\":\"ac794123-3820-4979-8e2d-50c7d3e07b13\"}", + "Date" : "Thu, 10 Jun 2021 06:21:23 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "cc5f2836-97b1-40d8-a88e-35407f8a6864" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/d21f1300-2cee-4b87-ade5-d381d32041d7/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0Zq/BYAAAAADFDfPxMOAPT4ByUUFLGqrdQ0hHRURHRTE2MTQAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "e668c3e5-0a52-4c90-85c1-2a9b3923bfa6", + "Date" : "Thu, 10 Jun 2021 06:21:26 GMT", + "Client-Request-Id" : "783c506a-0a63-4a72-8777-dfbb147f6af0" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreateAddRemoveHangupScenarioWithResponse[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreateAddRemoveHangupScenarioWithResponse[1].json new file mode 100644 index 0000000000000..d312080fabc17 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreateAddRemoveHangupScenarioWithResponse[1].json @@ -0,0 +1,76 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0PqfBYAAAAABDyBPAypUpTbDDAtp9C/5QREZXMzBFREdFMDUxMAA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "6edec820-c43f-4dda-b96d-758be5519392", + "Body" : "{\"callLegId\":\"c2201300-372b-4248-ae00-eb35cdea6a77\",\"callConnectionId\":\"c2201300-372b-4248-ae00-eb35cdea6a77\"}", + "Date" : "Thu, 10 Jun 2021 05:46:40 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "fc0d69a2-038b-4bca-9939-97b0e47af87d" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/c2201300-372b-4248-ae00-eb35cdea6a77/participants?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0TqfBYAAAAABzlxhDFy8ZSoB8m9jWy1fQREZXMzBFREdFMDUxMAA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "05038067-e3fc-42ee-9671-6fead242b7ad", + "Date" : "Thu, 10 Jun 2021 05:46:53 GMT", + "Client-Request-Id" : "acaba54e-0ba5-4628-aa8d-246abe8ab456" + }, + "Exception" : null + }, { + "Method" : "DELETE", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/c2201300-372b-4248-ae00-eb35cdea6a77/participants/71ed956b-366e-450c-9a61-3bbccf42baa5?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0dqfBYAAAAAAF8z+KH8V/QZ7SX/mINltfREZXMzBFREdFMDUxMAA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "45c6895c-71fa-44b5-b2bb-8692268feb35", + "Date" : "Thu, 10 Jun 2021 05:47:33 GMT", + "Client-Request-Id" : "f7946816-80cc-4794-a640-bd6693451fc1" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/c2201300-372b-4248-ae00-eb35cdea6a77/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0eafBYAAAAAAhH5AQSjKXR5cbQKfuCz+CREZXMzBFREdFMDUxMAA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "7851c5ce-e6d4-42f9-b94e-b003145be827", + "Date" : "Thu, 10 Jun 2021 05:47:36 GMT", + "Client-Request-Id" : "b85e142e-6d69-41d0-96f6-820591d8a0cc" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreateAddRemoveHangupScenario[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreateAddRemoveHangupScenario[1].json new file mode 100644 index 0000000000000..52efba7f8e1b4 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreateAddRemoveHangupScenario[1].json @@ -0,0 +1,76 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0NabBYAAAAABqOtzlzgj3Qp5ink0HfTPCQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "b8cff5a1-fa0e-4a78-b638-65d4042356b6", + "Body" : "{\"callLegId\":\"2e201300-7de3-419a-a20d-c31ff293b3e9\",\"callConnectionId\":\"2e201300-7de3-419a-a20d-c31ff293b3e9\"}", + "Date" : "Thu, 10 Jun 2021 05:42:14 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "ba6b7eca-6205-4e44-b171-f390a35d5744" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/2e201300-7de3-419a-a20d-c31ff293b3e9/participants?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0RabBYAAAAAA7deQkC15VSKN0zxVmFplxQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "48d9e35d-50d2-4d08-a10c-cd46b130c317", + "Date" : "Thu, 10 Jun 2021 05:42:29 GMT", + "Client-Request-Id" : "2c468a21-f8f4-4575-8ef9-678158a7aacd" + }, + "Exception" : null + }, { + "Method" : "DELETE", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/2e201300-7de3-419a-a20d-c31ff293b3e9/participants/f29f70e3-1eaf-44c0-839c-b4e8a74ffec3?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0c6bBYAAAAAD33QWwVsIEQ56OQHv9L6EsQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "17146e98-9532-48f1-b44a-37b4703f698b", + "Date" : "Thu, 10 Jun 2021 05:43:15 GMT", + "Client-Request-Id" : "29e9935a-cd73-43ed-88f0-c89f3f5540df" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/2e201300-7de3-419a-a20d-c31ff293b3e9/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0e6bBYAAAAAD9gfarByZWQ4HmiU85MbSUQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "af9d0938-0f39-4a15-8166-a9587dd36f20", + "Date" : "Thu, 10 Jun 2021 05:43:22 GMT", + "Client-Request-Id" : "3b2230b7-361b-4bdc-a9eb-84fd271e6da2" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreateJoinHangupScenarioWithResponse[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreateJoinHangupScenarioWithResponse[1].json new file mode 100644 index 0000000000000..d9c322722a5fe --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreateJoinHangupScenarioWithResponse[1].json @@ -0,0 +1,78 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0U67BYAAAAABEyopJEoKnTpzL+Xsv7bicQ0hHRURHRTE2MTQAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "689ebc85-f59f-4b04-89e0-09c43021b514", + "Body" : "{\"callLegId\":\"56201300-992b-4803-ad89-abb0d303ce0b\",\"callConnectionId\":\"56201300-992b-4803-ad89-abb0d303ce0b\"}", + "Date" : "Thu, 10 Jun 2021 06:16:53 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "b48a0c97-aa5d-413b-9faa-5e7da86e380b" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L3dXZW9hNjAweGtPZ0d6eHE2eG1tQVE_aT0yJmU9NjM3NTg0Mzk2NDM5NzQ5NzY4/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0hq7BYAAAAABeN+lvV1JaQKPUUU0viHz+Q0hHRURHRTE2MTQAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "c6acdbe5-5c27-4dd3-8765-421113a91240", + "Body" : "{\"callLegId\":\"56201300-e964-49b8-8525-fe64190d5c37\",\"callConnectionId\":\"56201300-e964-49b8-8525-fe64190d5c37\"}", + "Date" : "Thu, 10 Jun 2021 06:17:42 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "748c5a9e-c9ba-41a2-89dd-398005e0dd29" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/56201300-992b-4803-ad89-abb0d303ce0b/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0lq7BYAAAAAB/9mULAXyRTYKv25SBZFA7Q0hHRURHRTE2MTQAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "1d9573a8-b8c4-4990-bf1f-d16f19388463", + "Date" : "Thu, 10 Jun 2021 06:17:58 GMT", + "Client-Request-Id" : "c402dbb4-b16c-434a-9196-af05b8fb28f5" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/56201300-e964-49b8-8525-fe64190d5c37/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0mK7BYAAAAACI2rN0Wm06QoNl8YLZRN/iQ0hHRURHRTE2MTQAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "6f8c0d06-36cc-4e8c-9a0f-b377a14366c1", + "Date" : "Thu, 10 Jun 2021 06:17:59 GMT", + "Client-Request-Id" : "18acb10b-d718-496c-8c84-5f79f1f4a55e" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreateJoinHangupScenario[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreateJoinHangupScenario[1].json new file mode 100644 index 0000000000000..41667bb33cc34 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreateJoinHangupScenario[1].json @@ -0,0 +1,78 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0nafBYAAAAACHW+zqavOkR6S2/61+KsMMQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "0b7525f4-ccdd-4de6-909a-9829091981d9", + "Body" : "{\"callLegId\":\"6c201300-b17c-4a2a-8f71-f9dba15efc55\",\"callConnectionId\":\"6c201300-b17c-4a2a-8f71-f9dba15efc55\"}", + "Date" : "Thu, 10 Jun 2021 05:48:14 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "d6e2a569-d69a-4f37-8153-65c2f80a1d97" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L2RUUjRPVGFxVzAyZ3cxVGpNSUNBdEE_aT0wJmU9NjM3NTg0MzkwMjcxMzg0MTc3/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "026fBYAAAAAB8CXxw4DV0QpLbN2ir3wfQQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "ab14e041-1480-4185-baf2-b22e1fde362a", + "Body" : "{\"callLegId\":\"6c201300-7002-4c7e-9b99-90ccf73087c7\",\"callConnectionId\":\"6c201300-7002-4c7e-9b99-90ccf73087c7\"}", + "Date" : "Thu, 10 Jun 2021 05:49:15 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "0c952bbc-7091-4248-a530-7e2193d0e80a" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/6c201300-b17c-4a2a-8f71-f9dba15efc55/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "03qfBYAAAAADWWCQB+sCwRbD0/YsUov8tQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "acfc4aaa-31c1-4892-8de9-6fe3c5f10e67", + "Date" : "Thu, 10 Jun 2021 05:49:18 GMT", + "Client-Request-Id" : "87f8dd63-5d45-4fe8-907e-c59a498be0f0" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/6c201300-7002-4c7e-9b99-90ccf73087c7/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "04qfBYAAAAABcBNaac1hnS5+t8Ujs0OSfQ0hHRURHRTE2MTkAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "1452e1b6-5cba-4581-941f-02429beb69b2", + "Date" : "Thu, 10 Jun 2021 05:49:22 GMT", + "Client-Request-Id" : "f9cae3eb-79b7-4517-a09d-e5a744e29002" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreatePlayCancelHangupScenarioWithResponse[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreatePlayCancelHangupScenarioWithResponse[1].json new file mode 100644 index 0000000000000..3fb2624fd5da5 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreatePlayCancelHangupScenarioWithResponse[1].json @@ -0,0 +1,81 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0/qXBYAAAAABQ08P5ira4TbEsDSIVR9oRQ0hHRURHRTE2MDcAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "d3671253-f9b4-4fdc-ba85-db7d4ac3fd85", + "Body" : "{\"callLegId\":\"9e201300-9491-4c19-b223-bada41dd75d7\",\"callConnectionId\":\"9e201300-9491-4c19-b223-bada41dd75d7\"}", + "Date" : "Thu, 10 Jun 2021 05:41:19 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "d5357e0c-8cfe-416e-b244-c26cd28cce08" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/9e201300-9491-4c19-b223-bada41dd75d7/:playAudio?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "122", + "retry-after" : "0", + "X-Azure-Ref" : "0C6bBYAAAAABlt5C6WQRqSrAy0fcUSR7SQ0hHRURHRTE2MDcAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "a08d834c-b8bd-44b8-9d1c-38ff56785011", + "Body" : "{\"id\":\"5fb5d0a7-54df-4de5-b7dc-6132eb75a8f8\",\"status\":\"running\",\"operationContext\":\"ac794123-3820-4979-8e2d-50c7d3e07b12\"}", + "Date" : "Thu, 10 Jun 2021 05:41:31 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "38998e0d-ccb5-483a-995c-ff09382f00a9" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/9e201300-9491-4c19-b223-bada41dd75d7/:cancelAllMediaOperations?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "124", + "retry-after" : "0", + "X-Azure-Ref" : "0EKbBYAAAAAD3zcpmAJ13RYORG0zcbRJ0Q0hHRURHRTE2MDcAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "31aac915-1e9a-4420-9e33-a0c512f5f2a1", + "Body" : "{\"id\":\"98e4d36f-3fcd-4d1d-b3a6-e190e27e62d4\",\"status\":\"completed\",\"operationContext\":\"ac794123-3820-4979-8e2d-50c7d3e07b13\"}", + "Date" : "Thu, 10 Jun 2021 05:41:36 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "b813317c-ea02-4660-8a61-1b1ea9827aab" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/9e201300-9491-4c19-b223-bada41dd75d7/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0EqbBYAAAAABVwh6cYX7IRrdD+3WLNMvbQ0hHRURHRTE2MDcAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "d715b901-906a-4c8e-87fd-7bacf07a996e", + "Date" : "Thu, 10 Jun 2021 05:41:38 GMT", + "Client-Request-Id" : "41295ad5-eb73-4948-a375-49dac0f9b576" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreatePlayCancelHangupScenario[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreatePlayCancelHangupScenario[1].json new file mode 100644 index 0000000000000..6bdcbdf774d59 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/CallConnectionLiveTests.runCreatePlayCancelHangupScenario[1].json @@ -0,0 +1,81 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0kKXBYAAAAABoBk5OeHo3Qbdg4jBHzvvDQ0hHRURHRTE2MTEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "802713e8-3b20-43ab-baa9-9556e9fa5b97", + "Body" : "{\"callLegId\":\"c5201300-911b-4531-9e31-e40399b6cc5f\",\"callConnectionId\":\"c5201300-911b-4531-9e31-e40399b6cc5f\"}", + "Date" : "Thu, 10 Jun 2021 05:39:29 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "72e54070-c9cd-4795-b79f-3141550e4989" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/c5201300-911b-4531-9e31-e40399b6cc5f/:playAudio?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "122", + "retry-after" : "0", + "X-Azure-Ref" : "0oKXBYAAAAABNU3kdFjmNT5xy3dN4K86zQ0hHRURHRTE2MTEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "e636a27a-7f61-4fc1-9ec4-9590a86b2bf5", + "Body" : "{\"id\":\"d7e0adb0-4270-40ec-9765-3ea36333d117\",\"status\":\"running\",\"operationContext\":\"ac794123-3820-4979-8e2d-50c7d3e07b12\"}", + "Date" : "Thu, 10 Jun 2021 05:39:44 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "2f98ae53-872d-42a7-aea1-25c8d2229fc3" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/c5201300-911b-4531-9e31-e40399b6cc5f/:cancelAllMediaOperations?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "124", + "retry-after" : "0", + "X-Azure-Ref" : "0paXBYAAAAAAMod1Bg9TxT4yzrPIy9p8dQ0hHRURHRTE2MTEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "0fc01f99-334d-4897-82a5-fb92e4260e1d", + "Body" : "{\"id\":\"1e448fc0-d041-4cb6-9631-55fc8baa88d9\",\"status\":\"completed\",\"operationContext\":\"ac794123-3820-4979-8e2d-50c7d3e07b13\"}", + "Date" : "Thu, 10 Jun 2021 05:39:48 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "87982420-e99e-49d9-8e1f-6f7989e4248b" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/c5201300-911b-4531-9e31-e40399b6cc5f/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0p6XBYAAAAAAuJRrLwELbRovglo2uhLyyQ0hHRURHRTE2MTEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "2743db79-3e90-476d-9618-d75f4f518a5f", + "Date" : "Thu, 10 Jun 2021 05:39:51 GMT", + "Client-Request-Id" : "7faf65f3-ad9b-4ea8-8693-06d1228136a7" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadContent404Async[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadContent404Async[1].json new file mode 100644 index 0000000000000..5f2b54f7823b0 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadContent404Async[1].json @@ -0,0 +1,19 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141d/content/acsmetadata", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "0", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Server" : "Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "404", + "Date" : "Thu, 03 Jun 2021 00:22:03 GMT" + }, + "Exception" : null + }], + "variables" : [ ] +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadMetadataAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadMetadataAsync[1].json new file mode 100644 index 0000000000000..1bd59c9f9c483 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadMetadataAsync[1].json @@ -0,0 +1,23 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/acsmetadata", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "957", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Cache-Control" : "no-cache, max-age=0, s-maxage=0, private", + "Server" : "Microsoft-HTTPAPI/2.0", + "Content-Range" : "bytes 0-956/957", + "retry-after" : "0", + "StatusCode" : "206", + "Body" : "ew0KICAicmVzb3VyY2VJZCI6ICI2MzFmYThkOC1hYWI1LTRhYzUtOGUxNS0yNjFhYTI1OTA3NTAiLA0KICAiY2FsbElkIjogImEzMjdhOGU0LTRjMjQtNGM4NC05ZmUyLTA5ZmZlNjIzYzg1OCIsDQogICJjaHVua0RvY3VtZW50SWQiOiAiMC1ldXMtZDItM2NjYTIxNzU4OTFmMjFjNmM5YTU5NzVhMTJjMDE0MWMiLA0KICAiY2h1bmtJbmRleCI6IDAsDQogICJjaHVua1N0YXJ0VGltZSI6ICIyMDIxLTA2LTAyVDIxOjQ1OjQxLjY0OTQyMjRaIiwNCiAgImNodW5rRHVyYXRpb24iOiA1NTgwLjAsDQogICJwYXVzZVJlc3VtZUludGVydmFscyI6IFtdLA0KICAicmVjb3JkaW5nSW5mbyI6IHsNCiAgICAiY29udGVudFR5cGUiOiAibWl4ZWQiLA0KICAgICJjaGFubmVsVHlwZSI6ICJhdWRpb1ZpZGVvIiwNCiAgICAiZm9ybWF0IjogIm1wNCIsDQogICAgImF1ZGlvQ29uZmlndXJhdGlvbiI6IHsNCiAgICAgICJzYW1wbGVSYXRlIjogMTYwMDAsDQogICAgICAiYml0UmF0ZSI6IDEyODAwMCwNCiAgICAgICJjaGFubmVscyI6IDENCiAgICB9LA0KICAgICJ2aWRlb0NvbmZpZ3VyYXRpb24iOiB7DQogICAgICAibG9uZ2VyU2lkZUxlbmd0aCI6IDE5MjAsDQogICAgICAic2hvcnRlclNpZGVMZW5ndGgiOiAxMDgwLA0KICAgICAgImZyYW1lcmF0ZSI6IDgsDQogICAgICAiYml0UmF0ZSI6IDEwMDAwMDANCiAgICB9DQogIH0sDQogICJwYXJ0aWNpcGFudHMiOiBbDQogICAgew0KICAgICAgInBhcnRpY2lwYW50SWQiOiAiODphY3M6NjMxZmE4ZDgtYWFiNS00YWM1LThlMTUtMjYxYWEyNTkwNzUwXzAwMDAwMDBhLTZlOGItYjMzYy1kZWZkLThiM2EwZDAwNTFjYiINCiAgICB9LA0KICAgIHsNCiAgICAgICJwYXJ0aWNpcGFudElkIjogIjg6YWNzOjYzMWZhOGQ4LWFhYjUtNGFjNS04ZTE1LTI2MWFhMjU5MDc1MF8wMDAwMDAwYS02ZThiLWNhMTctZGVmZC04YjNhMGQwMDUxY2QiDQogICAgfQ0KICBdDQp9", + "Date" : "Thu, 03 Jun 2021 00:07:30 GMT", + "Content-Type" : "application/octet-stream" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadMetadataRetryingAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadMetadataRetryingAsync[1].json new file mode 100644 index 0000000000000..2b2a1c3aae675 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadMetadataRetryingAsync[1].json @@ -0,0 +1,39 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/acsmetadata", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "0", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Server" : "Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "416", + "Date" : "Thu, 03 Jun 2021 00:22:03 GMT" + }, + "Exception" : null + }, + { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/acsmetadata", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "957", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Cache-Control" : "no-cache, max-age=0, s-maxage=0, private", + "Server" : "Microsoft-HTTPAPI/2.0", + "Content-Range" : "bytes 0-956/957", + "retry-after" : "0", + "StatusCode" : "206", + "Body" : "ew0KICAicmVzb3VyY2VJZCI6ICI2MzFmYThkOC1hYWI1LTRhYzUtOGUxNS0yNjFhYTI1OTA3NTAiLA0KICAiY2FsbElkIjogImEzMjdhOGU0LTRjMjQtNGM4NC05ZmUyLTA5ZmZlNjIzYzg1OCIsDQogICJjaHVua0RvY3VtZW50SWQiOiAiMC1ldXMtZDItM2NjYTIxNzU4OTFmMjFjNmM5YTU5NzVhMTJjMDE0MWMiLA0KICAiY2h1bmtJbmRleCI6IDAsDQogICJjaHVua1N0YXJ0VGltZSI6ICIyMDIxLTA2LTAyVDIxOjQ1OjQxLjY0OTQyMjRaIiwNCiAgImNodW5rRHVyYXRpb24iOiA1NTgwLjAsDQogICJwYXVzZVJlc3VtZUludGVydmFscyI6IFtdLA0KICAicmVjb3JkaW5nSW5mbyI6IHsNCiAgICAiY29udGVudFR5cGUiOiAibWl4ZWQiLA0KICAgICJjaGFubmVsVHlwZSI6ICJhdWRpb1ZpZGVvIiwNCiAgICAiZm9ybWF0IjogIm1wNCIsDQogICAgImF1ZGlvQ29uZmlndXJhdGlvbiI6IHsNCiAgICAgICJzYW1wbGVSYXRlIjogMTYwMDAsDQogICAgICAiYml0UmF0ZSI6IDEyODAwMCwNCiAgICAgICJjaGFubmVscyI6IDENCiAgICB9LA0KICAgICJ2aWRlb0NvbmZpZ3VyYXRpb24iOiB7DQogICAgICAibG9uZ2VyU2lkZUxlbmd0aCI6IDE5MjAsDQogICAgICAic2hvcnRlclNpZGVMZW5ndGgiOiAxMDgwLA0KICAgICAgImZyYW1lcmF0ZSI6IDgsDQogICAgICAiYml0UmF0ZSI6IDEwMDAwMDANCiAgICB9DQogIH0sDQogICJwYXJ0aWNpcGFudHMiOiBbDQogICAgew0KICAgICAgInBhcnRpY2lwYW50SWQiOiAiODphY3M6NjMxZmE4ZDgtYWFiNS00YWM1LThlMTUtMjYxYWEyNTkwNzUwXzAwMDAwMDBhLTZlOGItYjMzYy1kZWZkLThiM2EwZDAwNTFjYiINCiAgICB9LA0KICAgIHsNCiAgICAgICJwYXJ0aWNpcGFudElkIjogIjg6YWNzOjYzMWZhOGQ4LWFhYjUtNGFjNS04ZTE1LTI2MWFhMjU5MDc1MF8wMDAwMDAwYS02ZThiLWNhMTctZGVmZC04YjNhMGQwMDUxY2QiDQogICAgfQ0KICBdDQp9", + "Date" : "Thu, 03 Jun 2021 00:07:30 GMT", + "Content-Type" : "application/octet-stream" + }, + "Exception" : null + } ], + "variables" : [ ] +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadMetadataToStreamAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadMetadataToStreamAsync[1].json new file mode 100644 index 0000000000000..2b2b8b37d5917 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadMetadataToStreamAsync[1].json @@ -0,0 +1,23 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/acsmetadata", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "957", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Cache-Control" : "no-cache, max-age=0, s-maxage=0, private", + "Server" : "Microsoft-HTTPAPI/2.0", + "Content-Range" : "bytes 0-956/957", + "retry-after" : "0", + "StatusCode" : "206", + "Body" : "ew0KICAicmVzb3VyY2VJZCI6ICI2MzFmYThkOC1hYWI1LTRhYzUtOGUxNS0yNjFhYTI1OTA3NTAiLA0KICAiY2FsbElkIjogImEzMjdhOGU0LTRjMjQtNGM4NC05ZmUyLTA5ZmZlNjIzYzg1OCIsDQogICJjaHVua0RvY3VtZW50SWQiOiAiMC1ldXMtZDItM2NjYTIxNzU4OTFmMjFjNmM5YTU5NzVhMTJjMDE0MWMiLA0KICAiY2h1bmtJbmRleCI6IDAsDQogICJjaHVua1N0YXJ0VGltZSI6ICIyMDIxLTA2LTAyVDIxOjQ1OjQxLjY0OTQyMjRaIiwNCiAgImNodW5rRHVyYXRpb24iOiA1NTgwLjAsDQogICJwYXVzZVJlc3VtZUludGVydmFscyI6IFtdLA0KICAicmVjb3JkaW5nSW5mbyI6IHsNCiAgICAiY29udGVudFR5cGUiOiAibWl4ZWQiLA0KICAgICJjaGFubmVsVHlwZSI6ICJhdWRpb1ZpZGVvIiwNCiAgICAiZm9ybWF0IjogIm1wNCIsDQogICAgImF1ZGlvQ29uZmlndXJhdGlvbiI6IHsNCiAgICAgICJzYW1wbGVSYXRlIjogMTYwMDAsDQogICAgICAiYml0UmF0ZSI6IDEyODAwMCwNCiAgICAgICJjaGFubmVscyI6IDENCiAgICB9LA0KICAgICJ2aWRlb0NvbmZpZ3VyYXRpb24iOiB7DQogICAgICAibG9uZ2VyU2lkZUxlbmd0aCI6IDE5MjAsDQogICAgICAic2hvcnRlclNpZGVMZW5ndGgiOiAxMDgwLA0KICAgICAgImZyYW1lcmF0ZSI6IDgsDQogICAgICAiYml0UmF0ZSI6IDEwMDAwMDANCiAgICB9DQogIH0sDQogICJwYXJ0aWNpcGFudHMiOiBbDQogICAgew0KICAgICAgInBhcnRpY2lwYW50SWQiOiAiODphY3M6NjMxZmE4ZDgtYWFiNS00YWM1LThlMTUtMjYxYWEyNTkwNzUwXzAwMDAwMDBhLTZlOGItYjMzYy1kZWZkLThiM2EwZDAwNTFjYiINCiAgICB9LA0KICAgIHsNCiAgICAgICJwYXJ0aWNpcGFudElkIjogIjg6YWNzOjYzMWZhOGQ4LWFhYjUtNGFjNS04ZTE1LTI2MWFhMjU5MDc1MF8wMDAwMDAwYS02ZThiLWNhMTctZGVmZC04YjNhMGQwMDUxY2QiDQogICAgfQ0KICBdDQp9", + "Date" : "Thu, 03 Jun 2021 00:07:30 GMT", + "Content-Type" : "application/octet-stream" + }, + "Exception" : null + } ], + "variables" : [ ] +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadToFileAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadToFileAsync[1].json new file mode 100644 index 0000000000000..63697a2391105 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadToFileAsync[1].json @@ -0,0 +1,43 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/acsmetadata", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "439", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Cache-Control" : "no-cache, max-age=0, s-maxage=0, private", + "Server" : "Microsoft-HTTPAPI/2.0", + "Content-Range" : "bytes 0-438/957", + "retry-after" : "0", + "StatusCode" : "206", + "Body" : "ew0KICAicmVzb3VyY2VJZCI6ICI2MzFmYThkOC1hYWI1LTRhYzUtOGUxNS0yNjFhYTI1OTA3NTAiLA0KICAiY2FsbElkIjogImEzMjdhOGU0LTRjMjQtNGM4NC05ZmUyLTA5ZmZlNjIzYzg1OCIsDQogICJjaHVua0RvY3VtZW50SWQiOiAiMC1ldXMtZDItM2NjYTIxNzU4OTFmMjFjNmM5YTU5NzVhMTJjMDE0MWMiLA0KICAiY2h1bmtJbmRleCI6IDAsDQogICJjaHVua1N0YXJ0VGltZSI6ICIyMDIxLTA2LTAyVDIxOjQ1OjQxLjY0OTQyMjRaIiwNCiAgImNodW5rRHVyYXRpb24iOiA1NTgwLjAsDQogICJwYXVzZVJlc3VtZUludGVydmFscyI6IFtdLA0KICAicmVjb3JkaW5nSW5mbyI6IHsNCiAgICAiY29udGVudFR5cGUiOiAibWl4ZWQiLA0KICAgICJjaGFubmVsVHlwZSI6ICJhdWRpb1ZpZGVvIiwNCiAgICAiZm9ybWF0IjogIm1wNCIsDQogICAgImF1ZGlvQ29uZmlndXJhdGlvbiI6IHsNCiAgICAgICJzYW1wbGVSYXRlIjogMTYwMDAsDQogIC", + "Date" : "Thu, 03 Jun 2021 00:07:30 GMT", + "Content-Type" : "application/octet-stream" + }, + "Exception" : null + }, + { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/acsmetadata", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "438", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Cache-Control" : "no-cache, max-age=0, s-maxage=0, private", + "Server" : "Microsoft-HTTPAPI/2.0", + "Content-Range" : "bytes 439-956/957", + "retry-after" : "0", + "StatusCode" : "206", + "Body" : "AgICAiYml0UmF0ZSI6IDEyODAwMCwNCiAgICAgICJjaGFubmVscyI6IDENCiAgICB9LA0KICAgICJ2aWRlb0NvbmZpZ3VyYXRpb24iOiB7DQogICAgICAibG9uZ2VyU2lkZUxlbmd0aCI6IDE5MjAsDQogICAgICAic2hvcnRlclNpZGVMZW5ndGgiOiAxMDgwLA0KICAgICAgImZyYW1lcmF0ZSI6IDgsDQogICAgICAiYml0UmF0ZSI6IDEwMDAwMDANCiAgICB9DQogIH0sDQogICJwYXJ0aWNpcGFudHMiOiBbDQogICAgew0KICAgICAgInBhcnRpY2lwYW50SWQiOiAiODphY3M6NjMxZmE4ZDgtYWFiNS00YWM1LThlMTUtMjYxYWEyNTkwNzUwXzAwMDAwMDBhLTZlOGItYjMzYy1kZWZkLThiM2EwZDAwNTFjYiINCiAgICB9LA0KICAgIHsNCiAgICAgICJwYXJ0aWNpcGFudElkIjogIjg6YWNzOjYzMWZhOGQ4LWFhYjUtNGFjNS04ZTE1LTI2MWFhMjU5MDc1MF8wMDAwMDAwYS02ZThiLWNhMTctZGVmZC04YjNhMGQwMDUxY2QiDQogICAgfQ0KICBdDQp9", + "Date" : "Thu, 03 Jun 2021 00:07:30 GMT", + "Content-Type" : "application/octet-stream" + }, + "Exception" : null + } ], + "variables" : [ ] +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadToFileRetryingAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadToFileRetryingAsync[1].json new file mode 100644 index 0000000000000..843bf2f508aa3 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadToFileRetryingAsync[1].json @@ -0,0 +1,40 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/acsmetadata", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "0", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Server" : "Microsoft-HTTPAPI/2.0", + "Content-Range" : "bytes */957", + "retry-after" : "0", + "StatusCode" : "416", + "Date" : "Thu, 03 Jun 2021 00:22:03 GMT" + }, + "Exception" : null + }, + { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/acsmetadata", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "957", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Cache-Control" : "no-cache, max-age=0, s-maxage=0, private", + "Server" : "Microsoft-HTTPAPI/2.0", + "Content-Range" : "bytes 0-956/957", + "retry-after" : "0", + "StatusCode" : "206", + "Body" : "ew0KICAicmVzb3VyY2VJZCI6ICI2MzFmYThkOC1hYWI1LTRhYzUtOGUxNS0yNjFhYTI1OTA3NTAiLA0KICAiY2FsbElkIjogImEzMjdhOGU0LTRjMjQtNGM4NC05ZmUyLTA5ZmZlNjIzYzg1OCIsDQogICJjaHVua0RvY3VtZW50SWQiOiAiMC1ldXMtZDItM2NjYTIxNzU4OTFmMjFjNmM5YTU5NzVhMTJjMDE0MWMiLA0KICAiY2h1bmtJbmRleCI6IDAsDQogICJjaHVua1N0YXJ0VGltZSI6ICIyMDIxLTA2LTAyVDIxOjQ1OjQxLjY0OTQyMjRaIiwNCiAgImNodW5rRHVyYXRpb24iOiA1NTgwLjAsDQogICJwYXVzZVJlc3VtZUludGVydmFscyI6IFtdLA0KICAicmVjb3JkaW5nSW5mbyI6IHsNCiAgICAiY29udGVudFR5cGUiOiAibWl4ZWQiLA0KICAgICJjaGFubmVsVHlwZSI6ICJhdWRpb1ZpZGVvIiwNCiAgICAiZm9ybWF0IjogIm1wNCIsDQogICAgImF1ZGlvQ29uZmlndXJhdGlvbiI6IHsNCiAgICAgICJzYW1wbGVSYXRlIjogMTYwMDAsDQogICAgICAiYml0UmF0ZSI6IDEyODAwMCwNCiAgICAgICJjaGFubmVscyI6IDENCiAgICB9LA0KICAgICJ2aWRlb0NvbmZpZ3VyYXRpb24iOiB7DQogICAgICAibG9uZ2VyU2lkZUxlbmd0aCI6IDE5MjAsDQogICAgICAic2hvcnRlclNpZGVMZW5ndGgiOiAxMDgwLA0KICAgICAgImZyYW1lcmF0ZSI6IDgsDQogICAgICAiYml0UmF0ZSI6IDEwMDAwMDANCiAgICB9DQogIH0sDQogICJwYXJ0aWNpcGFudHMiOiBbDQogICAgew0KICAgICAgInBhcnRpY2lwYW50SWQiOiAiODphY3M6NjMxZmE4ZDgtYWFiNS00YWM1LThlMTUtMjYxYWEyNTkwNzUwXzAwMDAwMDBhLTZlOGItYjMzYy1kZWZkLThiM2EwZDAwNTFjYiINCiAgICB9LA0KICAgIHsNCiAgICAgICJwYXJ0aWNpcGFudElkIjogIjg6YWNzOjYzMWZhOGQ4LWFhYjUtNGFjNS04ZTE1LTI2MWFhMjU5MDc1MF8wMDAwMDAwYS02ZThiLWNhMTctZGVmZC04YjNhMGQwMDUxY2QiDQogICAgfQ0KICBdDQp9", + "Date" : "Thu, 03 Jun 2021 00:07:30 GMT", + "Content-Type" : "application/octet-stream" + }, + "Exception" : null + } ], + "variables" : [ ] +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadVideoAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadVideoAsync[1].json new file mode 100644 index 0000000000000..deb335ac212b1 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentAsyncLiveTests.downloadVideoAsync[1].json @@ -0,0 +1,23 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/video", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "89666", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Cache-Control" : "no-cache, max-age=0, s-maxage=0, private", + "Server" : "Microsoft-HTTPAPI/2.0", + "Content-Range" : "bytes 0-89665/89666", + "retry-after" : "0", + "StatusCode" : "206", + "Body" : "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MCBhbmFseXNlPTA6MCBtZT1kaWEgc3VibWU9MCBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0wIHRocmVhZHM9NiBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTAgd2VpZ2h0cD0wIGtleWludD00OCBrZXlpbnRfbWluPTQgc2NlbmVjdXQ9MCBpbnRyYV9yZWZyZXNoPTAgcmM9YWJyIG1idHJlZT0wIGJpdHJhdGU9MTAwMCByYXRldG9sPTEuMCBxY29tcD0wLjYwIHFwbWluPTIwIHFwbWF4PTUxIHFwc3RlcD00IGlwX3JhdGlvPTEuNDAgYXE9MACAAAAYDmWIhAZImKAARk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5Ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu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/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5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UX+O9//x8P9Zs0sWld/xlV1XBl+P31F/xfCi0Xucgwp//T5DQPjXi6pVT0EDT8R1B/kD7UkeGMHB5XllXFr4qxV/+O2J5iPP5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P4hcQuIXELiFxC4jxHn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/iFxC4heIXELiFxHn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/hPAHujio/wJqfcl5ypnV198lPbC8v51MkT9/mE//5mwaX8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8J4LNLf+ELt+iXbG/nulJIL18T+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz/yWIb7bfC3zEfjs6f//+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fwn/4cZ8//iPN08KppMfwdu/x8/z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fxHiPP5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/Eefz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fwAAAHCNBmuAkuAgP//////////////////////////////////////////////////////////////////////////////////////////////////+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+f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/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/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fz+fz+fz+fz+fz+fz+fg9O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+IXELiF+IXELxC4hc65/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5++LgDCV/Q+blLaAxuE9j9vq7gsNwl34G2A1N2f/efL4j+PweeDk75/CeAC5NgctEfDj9L/AAkyfxN6ODo2BSEFTqc9XgPOEKMi4vy477MjAzIFRdzDMAEb51f93k/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/fGbVMz0mbBFis4IFmVVVVVXxmF3Aabffd9RXeuoOTvhPAAhPdd938ACqphs1uy9/h/hTcAksRl7QQf7xmw0EB00ABtw+Eh8jYfJ/aPAzwM8DFAxQMUDFAxQMUDFAx9nf5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/PwenfP51z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+AAAAltQZsgqB7gID/////////////////////////////////////////////////ABiYE8X//4AwPV528FFVwiCUBS0OQcovlmkNpnri5aZyKquIiWTeDnhjhmEeWBBho3LgGkMXv6OLl0Vf+aMhRn+WoahSf4ueDPZsxtq8MBhd//gDL7uO3+8G3DGoScO7VRdoAgTv513/643y/v8OwwnSYIIlegIE1dx/tCNpqF8cEt4zEP3UePY8J9B3zfy8PQ9ayg+GK6ZcDhETR/0GgBzD8wwqfRV/+N8I+DlsBxkuGrEJI8P2d0BYWrCRQEpLhVgAMIvX/3uUkZ9LPwa8EHh1DtcEzx60TQJyA06GVeLG431f+HZ1IJG5rUMNzvpfszYMMmbQ1/2U5IZWmr++pCrhTP080GmDYxG3n/ejwIn/g/18XstvqkF37TU0wIkzqr43EkhmnETCZFFkDhSwrUUUkwcMk8OtMXbHu8GnCOgRwIyFAQScdxSC+BneyH/wQxC/gdzP/0RxkfHnFjADCkTG5ElkwmBiDrRMDBEr8f6Hgz4IKhDxnxHg8A5xdwyiYgT+PmRiG1nH/8gs/G+/8EO0vD/+n5Oin8P4Urx6RQQeQ/JJJmRJOIUmYlW1iMGFVNCmEGXC/hDwD0EzhbJ740fKxXestIl/+CGiSUf/D69E8bpBlJCIuEyKJJDtoEzFzikXG4zFHuz/+DHgn1j0AD8XQ3CATVwTW+vgh3oAz/4IrhSqQ46IiYTIomBUxxdwTJegFxRJaZFhBjxuEbtytBddh94fmEGL91d26qSq5mtRnX/x27XwSYcWomWL0osb4zCfFR4T53rpmfLSWkZmu5fuEXoD62uoZ2YN1vjb4N//CkpEJGLSZkzYqP4EHbu45vkdoDhaU+v/IIdUb9u0lky+G4hegL8e+vylXBwo9/Fv9fD2ZMX5fkOshtHXZoYv2tKUryDBjev8GPG1CfE0VHDqG+l+fmEfd1puXpuZrFyXiv5OCTDi1EyxelARfBTh5LhBPneumImtDdbaz+kx7X5f+IV0OqwzsMG63xt9fG2kEcFp/MHA7d2mOb73GTl+4CHUb4W0lkypozx/8N0UE75uls3ilKxnvycbbav/fdW9vobrRoX9W27tu7xCsFxdyL8f3laxfwYc2dFOi8J4BR9Ma1k4qNCV8KhwjZ/P/C1SB371E6iuVE6J/89ZoTQ/wTZHxHVr1glBBo39+pOCLNnAGMAtfLQ4fi0WDDhShO7u7u7u7u79JxUNJHPY+xQI3v5z/PlROif/DX0qDuYJc0Jof4n46JV0Jl4ING/vJwRaeSGC18tCh8i0mC/j+klHBEpOA8wIxgAIjX7ScNcNoKWa/a1jXf/G2B4PX6Kjg2VzyhEEOwPexhlCp1At+dkrNqGu/8byK2B72HBEIMG7ZHfP+95xjVyuFui0SFT/K0zY/3+FeQBj1mGd1rbbbbwj/c/LxN74yJrMGJz3BgYL+TCri4MBLw18EakBuVNftaxrv/jbA8Hr9Ex4DZR/GwSDHA97wyhU9iW4f2R6zah3X/w7zVge9rQSAQaGjojvn/e+hQRWmluFFytB9fr+/wzcAAfIGmffhX9z+Le0nEyZxsDAaAp14L+F9DDuGkQa+bYR1SsANYWbn8nFw/u5IilW7uIJiCbuX7uAy8MTEExBQMycv3DLVFu7T8XiWHk4WjYddUAG0SKwa0rclCm5/wX82cMBkYJxcP7uSIpV10GwYJ3l+44DLwxYNg0DBE8v3Ay1Rbu4+LxLDydT0GHCkoajtze+lAnex+cWnSfchXwvhVCJwvIu8zgCWyibsUf4YhlElWkUycv3c7Z59cFuMzMl/l4aQRb5eCThJ31/fDHKRMRDiWURXzq1Mlo/71c2jHv8d0gYsPA5o4f1aZdN+BB+P2/VBR98E+ErkvKbL3hglCCjRUOEGPC0cT9zENxHc1xfuSrNV4r++NzBgMMohjhPmP7lHxtemE+AgXY2Nh3J/4YhlElWkWU8v3H5+ec14LcZmZL/Lw0gi3y8OcJc+4Ugvd4ae/wxy4MHURDiWURWjrbILR/3q5/QUw/8d0gYsPA4SOH9WmXTfgQfj9v1QUfi3tXC+yrIqOIUC2pU1i/gx435vh/tMxmFHuiS/N7uQHwJj37HAgYdeehp94h/k4V1BCwIv/QQRtXPwYZyZ/8EvdmUDDev1Y0mNuIlviOOPGg+EuBFRA1gOf4JaNhB+tIkHV/8fzlJxvhDtqAk/n7ePuoBLdKgXYIPHw40DXe6wx8Fvf4MeFMpIX9cbmETfX3XlZ7EC46SXItBsyk4V4QsCL+zRc/6r///wr1OgQ32qtBsH0bX/+vfC8cITRqr3J2dTTWLmirXxf6O9cO1VRLFQKvleh7kXY/6kI+G3WKrLUn/Bjz1jvf/G0DQMYBupmIKBtpTTIu2bsDuNNOhdH2g8tF4We/E8EHjQiOEeHcMPL/EwarrttgB9eFPf42uDVMJVVV5VVVVVVHf8GPJzyo/D/DSC85G8N4mg6co4kjpEUT5cd78Twx4yHhDqx3nMgQLilrbUY7/E5YyK2M0TGWJjIMuGoZlt1YeHYAgFuDb7/4K/CRgP04R/o4fbaQNX/DcNMliXSn4LPD0u7CDj1hUAwRkuqDT3mG2v4vjY0DQZulkriIYmeAu7Nw15Q9gFyqqhqhkFCvz9/gz4IqSHBqg8Ayjg6oC3wxHxImDja6fhNwTKiQ/rDjvR+fgg8PJdrAh1H+krCBdEUqxwwsVDpjHf4UjY2ky/RhCea+f97NfP+9nlaPkOofywgqrCCwgkKDTk6G+r+Pq3WaPte8V3DcS0wEGyDtjb/gopF/CsAA3qRYGcdie+CiqrdVPlU242kuIxfF8xEE+ja91njDkN4KKnDyiJwzAmNVc5fHTEExBGQsJk4/Xx8Cs1YAV6CHVdB0GwfghxtQGt//JYng14ehDwT7KspeDZiN+MfXcS8IM88zhFv/427yruq11Uk5kYyXJot/4Y5i42uAZd3Y+N/fUyZlcWzb/go8J2GZOwaODYvhvZHlmbi2Tf8bx8XTRhYEHV+1HEB2xQNHla5VVEJlBf/BL0OVVNNNNNdABerxcfHiE+6PvykZ4C+HT4Vn8JGvA3/ZQ4p6Dzi4BC/8H/B//5gwHBK0v/xRxMEHnJMz3wf/JrqDcf/c1YP7Hwd8EsOuzIX2N+ItZ+nP5AAC0SeoCygAAAHmJBm0BaB71f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+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P5/P5/P5/P5+Cc75/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fgnO+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4Jzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n875/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+Cc75/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fgnO+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4Jzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n875/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+Cc75/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fgnO+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4Jzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n875/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+Cc75/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fgnO+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4Jzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n875/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+Cc75/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fgnO+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4Jzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n875/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+Cc75/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fgnO+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4Jzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n875/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+Cc75/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fgnO+fz+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/PZwARggBwEYIAcAAAAIQZpgagAP8MAAAAAIQZqAagAP8MAAAAAIQZqgagAP8MAAAAAIQZrAagAP8MAAAAAIQZrgagAP8MAAAAAIQZsAagAP8MAAAAAIQZsgagAP8MAAABpnQZtAageicUAAtifE+J8T4nxPifE+J8T4nxPifE+J8T4nxPifE+J8T4nxPifE+J8T4nxPifE+J8T4nxPifE+J8T4nxPifE+J8T4nxPifE+J8T4nxPifE+J8T4ng9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz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/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fwngAWIRMND7Rvcsfse+v8ABxMKQ2/4hrUsJ4PQMoAkd0ii7f4OoMBLABpQILNObl1cXif9gTyxFpADwOigIv/8Bc1IAAQAgAwmE5/IqsT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp/CeAAk02mk02mv4ADmNlLEqJXrrn8p/D/Ck0AVgeVl6GqjgDtLffyAB3uAAAG8J48oAb4ria+toAb38HgAG54ABueAAbigAG4oABuKAAbigAG4oABuKAAbigAG44A7P/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8R5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/ARggBwEYIAc=", + "Date" : "Thu, 03 Jun 2021 00:33:58 GMT", + "Content-Type" : "application/octet-stream" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadContent404[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadContent404[1].json new file mode 100644 index 0000000000000..3cfa278b89837 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadContent404[1].json @@ -0,0 +1,19 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141d/content/acsmetadata", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "0", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Server" : "Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "404", + "Date" : "Thu, 03 Jun 2021 00:22:03 GMT" + }, + "Exception" : null + } ], + "variables" : [ ] +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadContentStreamFailure[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadContentStreamFailure[1].json new file mode 100644 index 0000000000000..f7c2e682ed0e8 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadContentStreamFailure[1].json @@ -0,0 +1,22 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/acsmetadata", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "957", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Cache-Control" : "no-cache, max-age=0, s-maxage=0, private", + "Server" : "Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "Body" : "ew0KICAicmVzb3VyY2VJZCI6ICI2MzFmYThkOC1hYWI1LTRhYzUtOGUxNS0yNjFhYTI1OTA3NTAiLA0KICAiY2FsbElkIjogImEzMjdhOGU0LTRjMjQtNGM4NC05ZmUyLTA5ZmZlNjIzYzg1OCIsDQogICJjaHVua0RvY3VtZW50SWQiOiAiMC1ldXMtZDItM2NjYTIxNzU4OTFmMjFjNmM5YTU5NzVhMTJjMDE0MWMiLA0KICAiY2h1bmtJbmRleCI6IDAsDQogICJjaHVua1N0YXJ0VGltZSI6ICIyMDIxLTA2LTAyVDIxOjQ1OjQxLjY0OTQyMjRaIiwNCiAgImNodW5rRHVyYXRpb24iOiA1NTgwLjAsDQogICJwYXVzZVJlc3VtZUludGVydmFscyI6IFtdLA0KICAicmVjb3JkaW5nSW5mbyI6IHsNCiAgICAiY29udGVudFR5cGUiOiAibWl4ZWQiLA0KICAgICJjaGFubmVsVHlwZSI6ICJhdWRpb1ZpZGVvIiwNCiAgICAiZm9ybWF0IjogIm1wNCIsDQogICAgImF1ZGlvQ29uZmlndXJhdGlvbiI6IHsNCiAgICAgICJzYW1wbGVSYXRlIjogMTYwMDAsDQogICAgICAiYml0UmF0ZSI6IDEyODAwMCwNCiAgICAgICJjaGFubmVscyI6IDENCiAgICB9LA0KICAgICJ2aWRlb0NvbmZpZ3VyYXRpb24iOiB7DQogICAgICAibG9uZ2VyU2lkZUxlbmd0aCI6IDE5MjAsDQogICAgICAic2hvcnRlclNpZGVMZW5ndGgiOiAxMDgwLA0KICAgICAgImZyYW1lcmF0ZSI6IDgsDQogICAgICAiYml0UmF0ZSI6IDEwMDAwMDANCiAgICB9DQogIH0sDQogICJwYXJ0aWNpcGFudHMiOiBbDQogICAgew0KICAgICAgInBhcnRpY2lwYW50SWQiOiAiODphY3M6NjMxZmE4ZDgtYWFiNS00YWM1LThlMTUtMjYxYWEyNTkwNzUwXzAwMDAwMDBhLTZlOGItYjMzYy1kZWZkLThiM2EwZDAwNTFjYiINCiAgICB9LA0KICAgIHsNCiAgICAgICJwYXJ0aWNpcGFudElkIjogIjg6YWNzOjYzMWZhOGQ4LWFhYjUtNGFjNS04ZTE1LTI2MWFhMjU5MDc1MF8wMDAwMDAwYS02ZThiLWNhMTctZGVmZC04YjNhMGQwMDUxY2QiDQogICAgfQ0KICBdDQp9", + "Date" : "Thu, 03 Jun 2021 00:07:30 GMT", + "Content-Type" : "application/octet-stream" + }, + "Exception" : null + }], + "variables" : [ ] +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadContentWrongUrl[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadContentWrongUrl[1].json new file mode 100644 index 0000000000000..6b9e994a3c258 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadContentWrongUrl[1].json @@ -0,0 +1,4 @@ +{ + "networkCallRecords" : [ ], + "variables" : [ ] +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadMetadataStreaming[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadMetadataStreaming[1].json new file mode 100644 index 0000000000000..2b2b8b37d5917 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadMetadataStreaming[1].json @@ -0,0 +1,23 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/acsmetadata", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "957", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Cache-Control" : "no-cache, max-age=0, s-maxage=0, private", + "Server" : "Microsoft-HTTPAPI/2.0", + "Content-Range" : "bytes 0-956/957", + "retry-after" : "0", + "StatusCode" : "206", + "Body" : "ew0KICAicmVzb3VyY2VJZCI6ICI2MzFmYThkOC1hYWI1LTRhYzUtOGUxNS0yNjFhYTI1OTA3NTAiLA0KICAiY2FsbElkIjogImEzMjdhOGU0LTRjMjQtNGM4NC05ZmUyLTA5ZmZlNjIzYzg1OCIsDQogICJjaHVua0RvY3VtZW50SWQiOiAiMC1ldXMtZDItM2NjYTIxNzU4OTFmMjFjNmM5YTU5NzVhMTJjMDE0MWMiLA0KICAiY2h1bmtJbmRleCI6IDAsDQogICJjaHVua1N0YXJ0VGltZSI6ICIyMDIxLTA2LTAyVDIxOjQ1OjQxLjY0OTQyMjRaIiwNCiAgImNodW5rRHVyYXRpb24iOiA1NTgwLjAsDQogICJwYXVzZVJlc3VtZUludGVydmFscyI6IFtdLA0KICAicmVjb3JkaW5nSW5mbyI6IHsNCiAgICAiY29udGVudFR5cGUiOiAibWl4ZWQiLA0KICAgICJjaGFubmVsVHlwZSI6ICJhdWRpb1ZpZGVvIiwNCiAgICAiZm9ybWF0IjogIm1wNCIsDQogICAgImF1ZGlvQ29uZmlndXJhdGlvbiI6IHsNCiAgICAgICJzYW1wbGVSYXRlIjogMTYwMDAsDQogICAgICAiYml0UmF0ZSI6IDEyODAwMCwNCiAgICAgICJjaGFubmVscyI6IDENCiAgICB9LA0KICAgICJ2aWRlb0NvbmZpZ3VyYXRpb24iOiB7DQogICAgICAibG9uZ2VyU2lkZUxlbmd0aCI6IDE5MjAsDQogICAgICAic2hvcnRlclNpZGVMZW5ndGgiOiAxMDgwLA0KICAgICAgImZyYW1lcmF0ZSI6IDgsDQogICAgICAiYml0UmF0ZSI6IDEwMDAwMDANCiAgICB9DQogIH0sDQogICJwYXJ0aWNpcGFudHMiOiBbDQogICAgew0KICAgICAgInBhcnRpY2lwYW50SWQiOiAiODphY3M6NjMxZmE4ZDgtYWFiNS00YWM1LThlMTUtMjYxYWEyNTkwNzUwXzAwMDAwMDBhLTZlOGItYjMzYy1kZWZkLThiM2EwZDAwNTFjYiINCiAgICB9LA0KICAgIHsNCiAgICAgICJwYXJ0aWNpcGFudElkIjogIjg6YWNzOjYzMWZhOGQ4LWFhYjUtNGFjNS04ZTE1LTI2MWFhMjU5MDc1MF8wMDAwMDAwYS02ZThiLWNhMTctZGVmZC04YjNhMGQwMDUxY2QiDQogICAgfQ0KICBdDQp9", + "Date" : "Thu, 03 Jun 2021 00:07:30 GMT", + "Content-Type" : "application/octet-stream" + }, + "Exception" : null + } ], + "variables" : [ ] +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadMetadata[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadMetadata[1].json new file mode 100644 index 0000000000000..2b2b8b37d5917 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadMetadata[1].json @@ -0,0 +1,23 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/acsmetadata", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "957", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Cache-Control" : "no-cache, max-age=0, s-maxage=0, private", + "Server" : "Microsoft-HTTPAPI/2.0", + "Content-Range" : "bytes 0-956/957", + "retry-after" : "0", + "StatusCode" : "206", + "Body" : "ew0KICAicmVzb3VyY2VJZCI6ICI2MzFmYThkOC1hYWI1LTRhYzUtOGUxNS0yNjFhYTI1OTA3NTAiLA0KICAiY2FsbElkIjogImEzMjdhOGU0LTRjMjQtNGM4NC05ZmUyLTA5ZmZlNjIzYzg1OCIsDQogICJjaHVua0RvY3VtZW50SWQiOiAiMC1ldXMtZDItM2NjYTIxNzU4OTFmMjFjNmM5YTU5NzVhMTJjMDE0MWMiLA0KICAiY2h1bmtJbmRleCI6IDAsDQogICJjaHVua1N0YXJ0VGltZSI6ICIyMDIxLTA2LTAyVDIxOjQ1OjQxLjY0OTQyMjRaIiwNCiAgImNodW5rRHVyYXRpb24iOiA1NTgwLjAsDQogICJwYXVzZVJlc3VtZUludGVydmFscyI6IFtdLA0KICAicmVjb3JkaW5nSW5mbyI6IHsNCiAgICAiY29udGVudFR5cGUiOiAibWl4ZWQiLA0KICAgICJjaGFubmVsVHlwZSI6ICJhdWRpb1ZpZGVvIiwNCiAgICAiZm9ybWF0IjogIm1wNCIsDQogICAgImF1ZGlvQ29uZmlndXJhdGlvbiI6IHsNCiAgICAgICJzYW1wbGVSYXRlIjogMTYwMDAsDQogICAgICAiYml0UmF0ZSI6IDEyODAwMCwNCiAgICAgICJjaGFubmVscyI6IDENCiAgICB9LA0KICAgICJ2aWRlb0NvbmZpZ3VyYXRpb24iOiB7DQogICAgICAibG9uZ2VyU2lkZUxlbmd0aCI6IDE5MjAsDQogICAgICAic2hvcnRlclNpZGVMZW5ndGgiOiAxMDgwLA0KICAgICAgImZyYW1lcmF0ZSI6IDgsDQogICAgICAiYml0UmF0ZSI6IDEwMDAwMDANCiAgICB9DQogIH0sDQogICJwYXJ0aWNpcGFudHMiOiBbDQogICAgew0KICAgICAgInBhcnRpY2lwYW50SWQiOiAiODphY3M6NjMxZmE4ZDgtYWFiNS00YWM1LThlMTUtMjYxYWEyNTkwNzUwXzAwMDAwMDBhLTZlOGItYjMzYy1kZWZkLThiM2EwZDAwNTFjYiINCiAgICB9LA0KICAgIHsNCiAgICAgICJwYXJ0aWNpcGFudElkIjogIjg6YWNzOjYzMWZhOGQ4LWFhYjUtNGFjNS04ZTE1LTI2MWFhMjU5MDc1MF8wMDAwMDAwYS02ZThiLWNhMTctZGVmZC04YjNhMGQwMDUxY2QiDQogICAgfQ0KICBdDQp9", + "Date" : "Thu, 03 Jun 2021 00:07:30 GMT", + "Content-Type" : "application/octet-stream" + }, + "Exception" : null + } ], + "variables" : [ ] +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadVideo[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadVideo[1].json new file mode 100644 index 0000000000000..a3ead96978ed5 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/DownloadContentLiveTests.downloadVideo[1].json @@ -0,0 +1,23 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://REDACTED.asm.skype.com/v1/objects/0-eus-d2-3cca2175891f21c6c9a5975a12c0141c/content/video", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)" + }, + "Response" : { + "content-length" : "89666", + "Strict-Transport-Security" : "max-age=31536000; includeSubDomains", + "Cache-Control" : "no-cache, max-age=0, s-maxage=0, private", + "Server" : "Microsoft-HTTPAPI/2.0", + "Content-Range" : "bytes 0-89665/89666", + "retry-after" : "0", + "StatusCode" : "206", + "Body" : "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MCBhbmFseXNlPTA6MCBtZT1kaWEgc3VibWU9MCBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0wIHRocmVhZHM9NiBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTAgd2VpZ2h0cD0wIGtleWludD00OCBrZXlpbnRfbWluPTQgc2NlbmVjdXQ9MCBpbnRyYV9yZWZyZXNoPTAgcmM9YWJyIG1idHJlZT0wIGJpdHJhdGU9MTAwMCByYXRldG9sPTEuMCBxY29tcD0wLjYwIHFwbWluPTIwIHFwbWF4PTUxIHFwc3RlcD00IGlwX3JhdGlvPTEuNDAgYXE9MACAAAAYDmWIhAZImKAARk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5Ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu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/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5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UX+O9//x8P9Zs0sWld/xlV1XBl+P31F/xfCi0Xucgwp//T5DQPjXi6pVT0EDT8R1B/kD7UkeGMHB5XllXFr4qxV/+O2J5iPP5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P4hcQuIXELiFxC4jxHn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/iFxC4heIXELiFxHn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/hPAHujio/wJqfcl5ypnV198lPbC8v51MkT9/mE//5mwaX8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8J4LNLf+ELt+iXbG/nulJIL18T+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz/yWIb7bfC3zEfjs6f//+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fwn/4cZ8//iPN08KppMfwdu/x8/z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fxHiPP5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/Eefz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fwAAAHCNBmuAkuAgP//////////////////////////////////////////////////////////////////////////////////////////////////+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+f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/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PTvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/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fz+fz+fz+fz+fz+fz+fg9O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+IXELiF+IXELxC4hc65/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D075/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5++LgDCV/Q+blLaAxuE9j9vq7gsNwl34G2A1N2f/efL4j+PweeDk75/CeAC5NgctEfDj9L/AAkyfx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K0v/xRxMEHnJMz3wf/JrqDcf/c1YP7Hwd8EsOuzIX2N+ItZ+nP5AAC0SeoCygAAAHmJBm0BaB71f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q/1f6v9X+r/V/q8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp3z+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+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P5/P5/P5/P5+Cc75/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fgnO+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4Jzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n875/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+Cc75/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fgnO+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4Jzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n875/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+Cc75/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fgnO+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4Jzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n875/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+Cc75/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fgnO+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4Jzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n875/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+Cc75/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fgnO+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4Jzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n875/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+Cc75/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fgnO+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4Jzvn8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n875/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+Cc75/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/O+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fgnO+fz+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/PZwARggBwEYIAcAAAAIQZpgagAP8MAAAAAIQZqAagAP8MAAAAAIQZqgagAP8MAAAAAIQZrAagAP8MAAAAAIQZrgagAP8MAAAAAIQZsAagAP8MAAAAAIQZsgagAP8MAAABpnQZtAageicUAAtifE+J8T4nxPifE+J8T4nxPifE+J8T4nxPifE+J8T4nxPifE+J8T4nxPifE+J8T4nxPifE+J8T4nxPifE+J8T4nxPifE+J8T4nxPifE+J8T4ng9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz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/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fg9P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n8/n4PT+fwngAWIRMND7Rvcsfse+v8ABxMKQ2/4hrUsJ4PQMoAkd0ii7f4OoMBLABpQILNObl1cXif9gTyxFpADwOigIv/8Bc1IAAQAgAwmE5/IqsT+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz+fz8Hp/CeAAk02mk02mv4ADmNlLEqJXrrn8p/D/Ck0AVgeVl6GqjgDtLffyAB3uAAAG8J48oAb4ria+toAb38HgAG54ABueAAbigAG4oABuKAAbigAG4oABuKAAbigAG44A7P/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5+D0/n8R5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/P5/ARggBwEYIAc=", + "Date" : "Thu, 03 Jun 2021 00:33:58 GMT", + "Content-Type" : "application/octet-stream" + }, + "Exception" : null + } ], + "variables" : [ ] +} diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runAddRemoveScenarioAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runAddRemoveScenarioAsync[1].json new file mode 100644 index 0000000000000..ecbcff49b8048 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runAddRemoveScenarioAsync[1].json @@ -0,0 +1,76 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0OcjBYAAAAADzW8ZTDBKsTq21yxFYY9OWREZXMzBFREdFMDUwNgA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "82742b74-df60-4a19-8da6-3b0e3ae692de", + "Body" : "{\"callLegId\":\"1a201300-71e3-4213-8948-ef00e41c86eb\",\"callConnectionId\":\"1a201300-71e3-4213-8948-ef00e41c86eb\"}", + "Date" : "Thu, 10 Jun 2021 08:07:22 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "218c4162-ca90-4679-894e-5f59331c2707" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L3NXdWxkazBmMEVpdnAxWjhiU2NuUHc_aT0yJmU9NjM3NTg0Mzk2NDM5NzQ5NzY4/participants?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0XMjBYAAAAADIiDOyF3E/T53NVWLhJHjoREZXMzBFREdFMDUwNgA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "4911578f-008d-461e-a423-76b935c0210e", + "Date" : "Thu, 10 Jun 2021 08:07:55 GMT", + "Client-Request-Id" : "9dfe019d-033d-444a-95d1-7cc0f1f089dc" + }, + "Exception" : null + }, { + "Method" : "DELETE", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L3NXdWxkazBmMEVpdnAxWjhiU2NuUHc_aT0yJmU9NjM3NTg0Mzk2NDM5NzQ5NzY4/participants/206ac04a-1aae-4d82-9015-9c30cb174888?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0fMjBYAAAAABG2y1vltVGT7qfGYgMfgmfREZXMzBFREdFMDUwNgA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "f30173fe-0445-4ea2-97cf-ff1872c5645f", + "Date" : "Thu, 10 Jun 2021 08:08:28 GMT", + "Client-Request-Id" : "65b2bedf-eecf-42ab-92f0-3058dd96501c" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/1a201300-71e3-4213-8948-ef00e41c86eb/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0gcjBYAAAAACfuBACVyiaSbWz12G4eejZREZXMzBFREdFMDUwNgA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "2b591cf6-16f4-4bda-ba5e-e1a513d69399", + "Date" : "Thu, 10 Jun 2021 08:08:33 GMT", + "Client-Request-Id" : "aed39731-e425-463e-9a08-16b4ca4606c9" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runAddRemoveScenarioWithResponseAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runAddRemoveScenarioWithResponseAsync[1].json new file mode 100644 index 0000000000000..4d61978964da9 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runAddRemoveScenarioWithResponseAsync[1].json @@ -0,0 +1,76 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0YMnBYAAAAABubX4mfQ8KS6SLXpHWvdSCREZXMzBFREdFMDUxNAA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "1ba536ab-1a96-46ee-9ff5-0313529f1a8f", + "Body" : "{\"callLegId\":\"841f1300-1337-4a7c-9e2e-843ea6ee00fd\",\"callConnectionId\":\"841f1300-1337-4a7c-9e2e-843ea6ee00fd\"}", + "Date" : "Thu, 10 Jun 2021 08:12:17 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "8faa6ad1-2a47-47fb-92b1-d1f74da10305" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L0NUT014YmNIRmttZ1BqbE5kYjExNlE_aT0yJmU9NjM3NTg0Mzk2NDM5NzQ5NzY4/participants?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0hsnBYAAAAAAbk5Bb74xVSqHaTlc3/pYnREZXMzBFREdFMDUxNAA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "b732e3f0-12b3-429d-aed7-846dbe0de149", + "Date" : "Thu, 10 Jun 2021 08:12:54 GMT", + "Client-Request-Id" : "ba8d41fa-d366-4a39-a3fd-d30f2a8dfd07" + }, + "Exception" : null + }, { + "Method" : "DELETE", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L0NUT014YmNIRmttZ1BqbE5kYjExNlE_aT0yJmU9NjM3NTg0Mzk2NDM5NzQ5NzY4/participants/b133b1f3-4a11-49e4-abe0-ac9fdd660634?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0psnBYAAAAACjREq/ejDiSovPTuo6B1RFREZXMzBFREdFMDUxNAA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "83f7ce54-d253-401e-a7e4-527e020a39f2", + "Date" : "Thu, 10 Jun 2021 08:13:26 GMT", + "Client-Request-Id" : "74fd86de-709d-4d7d-b4f6-0e941f01ea13" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/841f1300-1337-4a7c-9e2e-843ea6ee00fd/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0rMnBYAAAAABekUSsuaL8TpN3SYsaq/lYREZXMzBFREdFMDUxNAA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "63d62f4c-7b2e-4f27-9d5e-7a64ccf9b612", + "Date" : "Thu, 10 Jun 2021 08:13:32 GMT", + "Client-Request-Id" : "f24ddcbf-5eb8-4caa-8b80-71e06f473a85" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runAllClientFunctionsAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runAllClientFunctionsAsync[1].json new file mode 100644 index 0000000000000..0e2e9381b7c7b --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runAllClientFunctionsAsync[1].json @@ -0,0 +1,217 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "6a0b0821-8413-43ed-8f0a-0f8c99dff386", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0ALXDYAAAAAAExZQ3D9C0QafdGM65C9VhV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "004c40f0-403d-4b34-b648-32e40e7179e4", + "Body" : "{\"callLegId\":\"c4201300-169c-4258-9a6f-a0a70cecef6f\",\"callConnectionId\":\"c4201300-169c-4258-9a6f-a0a70cecef6f\"}", + "Date" : "Fri, 11 Jun 2021 19:09:51 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "9bb37dfe-fe3c-4e98-a4c8-986fdaaddbb1" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "9122e8f8-ef20-4aff-80d9-aaac87cff1a5", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0AbXDYAAAAAB7A3aL3Gx7RIpULMsuAHjTV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "a28cbabd-a298-4f50-9e68-b893449f5b7f", + "Body" : "{\"callLegId\":\"c4201300-2fcb-4763-a059-995c22a452d3\",\"callConnectionId\":\"c4201300-2fcb-4763-a059-995c22a452d3\"}", + "Date" : "Fri, 11 Jun 2021 19:09:52 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "cd944daf-0739-4265-bb70-9ee38cd103ec" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "cacaebd2-62b4-4003-9b52-d3c508f57000", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "180", + "retry-after" : "0", + "X-Azure-Ref" : "0ArXDYAAAAAAvOA1LRm/HTbF5DBcrN8EUV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "cc9bd4e5-a4c2-49d7-91da-f0c14ea83136", + "Body" : "{\"recordingId\":\"eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiJjNDIwMTMwMC05ZTk4LTQyNDMtOTQyNS1kN2M5ZWU3MmU2NGIiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiJkZWE1NDdiYS03NjI3LTQ1YmYtYmI2Yi01YTBhN2Q2N2UwMzAifQ\"}", + "Date" : "Fri, 11 Jun 2021 19:09:56 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "2ad199f7-eec6-42bf-9c76-03000a84f6fd" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiJjNDIwMTMwMC05ZTk4LTQyNDMtOTQyNS1kN2M5ZWU3MmU2NGIiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiJkZWE1NDdiYS03NjI3LTQ1YmYtYmI2Yi01YTBhN2Q2N2UwMzAifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "364584da-23a8-4287-a573-220a4bcb5ac0" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "27", + "retry-after" : "0", + "X-Azure-Ref" : "0C7XDYAAAAAB1byELGf7lRIEquoneiG46V1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "98f8db9a-67d4-4706-87c4-aeb84078c698", + "Body" : "{\"recordingState\":\"active\"}", + "Date" : "Fri, 11 Jun 2021 19:10:02 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "fd9a3c39-1a0e-44cb-9b02-08a66b54a7ea" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiJjNDIwMTMwMC05ZTk4LTQyNDMtOTQyNS1kN2M5ZWU3MmU2NGIiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiJkZWE1NDdiYS03NjI3LTQ1YmYtYmI2Yi01YTBhN2Q2N2UwMzAifQ/:pause?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "93833a59-57bf-4f02-8d97-a3f9df3aa4a5" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0C7XDYAAAAABbISWKmzlYT5okAukfKDe5V1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "27ed8b05-dfff-41fd-9ad3-fbefbae03f83", + "Date" : "Fri, 11 Jun 2021 19:10:02 GMT", + "Client-Request-Id" : "0e9330af-2ee2-4459-8a75-74e2a5b3c4c0" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiJjNDIwMTMwMC05ZTk4LTQyNDMtOTQyNS1kN2M5ZWU3MmU2NGIiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiJkZWE1NDdiYS03NjI3LTQ1YmYtYmI2Yi01YTBhN2Q2N2UwMzAifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "582e390c-e048-4734-b5ff-db08fe097a72" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "29", + "retry-after" : "0", + "X-Azure-Ref" : "0EbXDYAAAAADukQ4YnI//RqXDIj6R/5fJV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "32fb1e61-622d-4b36-a722-581a5c47d478", + "Body" : "{\"recordingState\":\"inactive\"}", + "Date" : "Fri, 11 Jun 2021 19:10:08 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "efd677b9-da9c-481a-952c-bac5fa312127" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiJjNDIwMTMwMC05ZTk4LTQyNDMtOTQyNS1kN2M5ZWU3MmU2NGIiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiJkZWE1NDdiYS03NjI3LTQ1YmYtYmI2Yi01YTBhN2Q2N2UwMzAifQ/:resume?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "f638b8b6-92d3-42db-b5c6-9f48a89cec7f" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0EbXDYAAAAACS3dulCxE1T4KseNrw5NsmV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "7fe75aa4-1bd2-45c3-abe4-680602c66513", + "Date" : "Fri, 11 Jun 2021 19:10:09 GMT", + "Client-Request-Id" : "d87fc4ee-9395-4a9a-ab77-8da73f32669a" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiJjNDIwMTMwMC05ZTk4LTQyNDMtOTQyNS1kN2M5ZWU3MmU2NGIiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiJkZWE1NDdiYS03NjI3LTQ1YmYtYmI2Yi01YTBhN2Q2N2UwMzAifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "d8b25b8a-1835-40ed-8413-392642bd8558" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "27", + "retry-after" : "0", + "X-Azure-Ref" : "0GLXDYAAAAACCwi1DDTItSLjZd/fm8ksvV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "55847d0e-0f60-47e7-848c-5c6d97a14042", + "Body" : "{\"recordingState\":\"active\"}", + "Date" : "Fri, 11 Jun 2021 19:10:15 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "8c518b14-0340-4e86-aa97-af57705abd3e" + }, + "Exception" : null + }, { + "Method" : "DELETE", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiJjNDIwMTMwMC05ZTk4LTQyNDMtOTQyNS1kN2M5ZWU3MmU2NGIiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiJkZWE1NDdiYS03NjI3LTQ1YmYtYmI2Yi01YTBhN2Q2N2UwMzAifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "a753ea08-bba4-4522-ae2c-a58c31d967f2" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0GLXDYAAAAACxG+0JRqZKSZRBne60xtuUV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "1c220ed4-5659-4e18-8149-46c493b1e45a", + "Date" : "Fri, 11 Jun 2021 19:10:15 GMT", + "Client-Request-Id" : "8c5228ab-1388-451e-88b5-444a30dceb1e" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/c4201300-169c-4258-9a6f-a0a70cecef6f/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "c5f085e4-8e4d-48cc-ae59-26a266e27014" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0GLXDYAAAAACkQPRMdvJFT6bGYXVmQlHvV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "03e97947-0324-42d1-8ba6-1ec06652d390", + "Date" : "Fri, 11 Jun 2021 19:10:15 GMT", + "Client-Request-Id" : "595c36a4-4153-4052-b710-f82794692018" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/c4201300-2fcb-4763-a059-995c22a452d3/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "f57b2ab5-d8ab-4d63-a3c7-c504813b6b95" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0GLXDYAAAAABhaQqIA1uxRLYAtQuAZwbDV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "f8348a79-1f05-4dc8-a679-4ac7eb6c55df", + "Date" : "Fri, 11 Jun 2021 19:10:15 GMT", + "Client-Request-Id" : "a2dd40b1-4a68-477f-8ca8-b7d22b781521" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runAllClientFunctionsWithResponseAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runAllClientFunctionsWithResponseAsync[1].json new file mode 100644 index 0000000000000..3307934df6375 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runAllClientFunctionsWithResponseAsync[1].json @@ -0,0 +1,217 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "0053ab3a-3325-456b-99a0-5292103cb991", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0HbXDYAAAAADNVodxRoxvRJPucM0dQJT+V1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "3997f93e-b8ad-4bc1-b364-ec5500dad12f", + "Body" : "{\"callLegId\":\"c4201300-5cc7-48af-974b-e969c17d49dd\",\"callConnectionId\":\"c4201300-5cc7-48af-974b-e969c17d49dd\"}", + "Date" : "Fri, 11 Jun 2021 19:10:20 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "9d40f9d8-c8e4-4a23-bfc6-558945405377" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "947b550e-0a43-43e1-b990-8a499cc97396", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0HrXDYAAAAAAiMYhF5sfuRpavR2DXBl2kV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "48a697fd-04ed-4835-bf98-7887ebe7d9af", + "Body" : "{\"callLegId\":\"c4201300-9125-4e84-a254-2878f8dd9b1d\",\"callConnectionId\":\"c4201300-9125-4e84-a254-2878f8dd9b1d\"}", + "Date" : "Fri, 11 Jun 2021 19:10:21 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "b5b0f71f-67d6-4ebb-a30e-edf4c9e46cce" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "113f22bb-858a-4a73-9494-6cb98f60fa5e", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "180", + "retry-after" : "0", + "X-Azure-Ref" : "0H7XDYAAAAABBe4HHEKAlQ4MKS5A+k6TZV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "ea117199-25a8-4840-825b-cb1cd09e662b", + "Body" : "{\"recordingId\":\"eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiJjNDIwMTMwMC0zMTJkLTQwMWUtOGE3MS1kODZiODhlYjg4NDkiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiI5OWExMjgyZi0wZjQ0LTRkYmQtODQ4Yi02NTc1N2ExNzMxMjEifQ\"}", + "Date" : "Fri, 11 Jun 2021 19:10:24 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "c26c0e4e-2134-432b-836a-50624c85bcd7" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiJjNDIwMTMwMC0zMTJkLTQwMWUtOGE3MS1kODZiODhlYjg4NDkiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiI5OWExMjgyZi0wZjQ0LTRkYmQtODQ4Yi02NTc1N2ExNzMxMjEifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "06828595-0363-482b-8d02-f987bf4920c3" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "27", + "retry-after" : "0", + "X-Azure-Ref" : "0J7XDYAAAAAD04O1+cyCKSKPHm89DzPV+V1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "d36c8931-e523-49a6-b080-d47a58e06b58", + "Body" : "{\"recordingState\":\"active\"}", + "Date" : "Fri, 11 Jun 2021 19:10:30 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "ec5ae584-9d1f-441a-a478-05d7eb94f9f2" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiJjNDIwMTMwMC0zMTJkLTQwMWUtOGE3MS1kODZiODhlYjg4NDkiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiI5OWExMjgyZi0wZjQ0LTRkYmQtODQ4Yi02NTc1N2ExNzMxMjEifQ/:pause?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "a51bafc3-eb60-493c-8b85-2f302a9c6f71" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0J7XDYAAAAAAhwxpTQOQIQq1tLDHaCdOnV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "a667e396-9591-4abd-b1ce-44424bbeb85c", + "Date" : "Fri, 11 Jun 2021 19:10:30 GMT", + "Client-Request-Id" : "46ea0b20-5328-412a-87bc-cbea9552399f" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiJjNDIwMTMwMC0zMTJkLTQwMWUtOGE3MS1kODZiODhlYjg4NDkiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiI5OWExMjgyZi0wZjQ0LTRkYmQtODQ4Yi02NTc1N2ExNzMxMjEifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "3a7c119e-3d42-4b54-b98a-b0921032be4f" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "29", + "retry-after" : "0", + "X-Azure-Ref" : "0LbXDYAAAAAA0buWQUGhiRpmKBH2fA/pOV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "8b4e9c2a-ccb6-4372-ab19-0fda60f12cad", + "Body" : "{\"recordingState\":\"inactive\"}", + "Date" : "Fri, 11 Jun 2021 19:10:36 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "6960a360-8f92-4453-9f36-18841a16fba6" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiJjNDIwMTMwMC0zMTJkLTQwMWUtOGE3MS1kODZiODhlYjg4NDkiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiI5OWExMjgyZi0wZjQ0LTRkYmQtODQ4Yi02NTc1N2ExNzMxMjEifQ/:resume?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "c4c03b5a-e6f9-4d49-a607-7d86e73d6019" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0LbXDYAAAAAChUWooVj/VTYXHFO+zsUQVV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "7383bef2-9c31-4cc5-9deb-6f59259c9978", + "Date" : "Fri, 11 Jun 2021 19:10:36 GMT", + "Client-Request-Id" : "7013f454-b386-4603-8d17-3e98dd1cbbb2" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiJjNDIwMTMwMC0zMTJkLTQwMWUtOGE3MS1kODZiODhlYjg4NDkiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiI5OWExMjgyZi0wZjQ0LTRkYmQtODQ4Yi02NTc1N2ExNzMxMjEifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "f5224d5e-cc9f-401a-8c2f-c5d468940946" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "27", + "retry-after" : "0", + "X-Azure-Ref" : "0M7XDYAAAAAD4DWxPXqflSIQ5DyASocqEV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "fe74cc38-1b34-4f09-b3e1-ddbf6af8b9b1", + "Body" : "{\"recordingState\":\"active\"}", + "Date" : "Fri, 11 Jun 2021 19:10:43 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "06dcfc4a-ddb1-4963-9317-9ea3fda95318" + }, + "Exception" : null + }, { + "Method" : "DELETE", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiJjNDIwMTMwMC0zMTJkLTQwMWUtOGE3MS1kODZiODhlYjg4NDkiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiI5OWExMjgyZi0wZjQ0LTRkYmQtODQ4Yi02NTc1N2ExNzMxMjEifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "da837afa-baf8-4c00-b4f1-09c531af154b" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0NLXDYAAAAAAD6yWe0VfBSIcJNfCAONSEV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "2153d6f0-1813-423b-84d3-7c10baa391a9", + "Date" : "Fri, 11 Jun 2021 19:10:44 GMT", + "Client-Request-Id" : "77f95e9a-5b96-4d56-9ed5-22f35689d051" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/c4201300-5cc7-48af-974b-e969c17d49dd/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "eaec8e84-d4a0-4f0e-bb30-85f2d947f5ce" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0NLXDYAAAAABjFpGicRlDQJR2fSjFNRbfV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "7187bbe9-d000-493e-b56d-7b06dbf6ffc1", + "Date" : "Fri, 11 Jun 2021 19:10:44 GMT", + "Client-Request-Id" : "565aa9da-7c56-4fdf-b05c-ea709b9b222f" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/c4201300-9125-4e84-a254-2878f8dd9b1d/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "5bea9098-d35b-427b-8a40-a1313792c60d" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0NLXDYAAAAACESiRVqsO9RIxqeV2mSsiHV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "cf48b63d-88ac-4fb9-b4f9-a2822218fd36", + "Date" : "Fri, 11 Jun 2021 19:10:44 GMT", + "Client-Request-Id" : "132e5f7b-96a2-4d2d-835c-95bc7c5093a0" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runPlayAudioFunctionAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runPlayAudioFunctionAsync[1].json new file mode 100644 index 0000000000000..8acd89e87ea73 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runPlayAudioFunctionAsync[1].json @@ -0,0 +1,103 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "01318c00-17d4-42fb-8f2a-de7fddc4958f", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0/LTDYAAAAAAbwq9M8Z8ZS6hkOmf+KG4JV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "7ae4533c-91ea-45d5-87e2-982da839c31e", + "Body" : "{\"callLegId\":\"c4201300-4c8d-41ee-9d51-863e9037882b\",\"callConnectionId\":\"c4201300-4c8d-41ee-9d51-863e9037882b\"}", + "Date" : "Fri, 11 Jun 2021 19:09:47 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "78972d83-253b-4feb-879a-2f9d574cc1d5" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "19bc48d8-cdf7-437e-85ac-c18ec1b7993c", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0/bTDYAAAAABYYrouXu91TpphTFl5pd+5V1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "c3b47723-753f-47a1-8e22-689973d11b42", + "Body" : "{\"callLegId\":\"c4201300-16a0-44c1-8063-7e401497bc23\",\"callConnectionId\":\"c4201300-16a0-44c1-8063-7e401497bc23\"}", + "Date" : "Fri, 11 Jun 2021 19:09:48 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "4357f1c7-e5f4-4035-885b-134484f5a0dc" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:playAudio?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "b29bdc33-c728-49fe-b087-069ecb0b58fc", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "122", + "retry-after" : "0", + "X-Azure-Ref" : "0/rTDYAAAAABn6veL1/IoRZc5YGhuQj51V1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "df0ea299-f457-40bb-9811-3b71ce3b88ba", + "Body" : "{\"id\":\"c4201300-cfdc-4bd8-977a-58773bdf3f01\",\"status\":\"running\",\"operationContext\":\"a871a8bd-04c5-4f44-aa06-1dff06911799\"}", + "Date" : "Fri, 11 Jun 2021 19:09:51 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "3fdf94c6-8209-4107-a6f6-a906b2331d35" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/c4201300-4c8d-41ee-9d51-863e9037882b/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "43052b98-54f6-4a29-9ded-5050b7fed6ad" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0ALXDYAAAAAAVxx4nj/DITbGeB/zadXA1V1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "d5cbe725-769b-49b1-b1b2-1a3b5792ce51", + "Date" : "Fri, 11 Jun 2021 19:09:51 GMT", + "Client-Request-Id" : "b620bdf1-42df-4903-8c6c-74a9a984727c" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/c4201300-16a0-44c1-8063-7e401497bc23/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "7b934e9a-7335-4461-a49f-494896fbc4e4" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0ALXDYAAAAAClVLzLD7C7RIiD2wIp1E7JV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "85256d76-61ef-4831-8a02-8e5a34e21203", + "Date" : "Fri, 11 Jun 2021 19:09:51 GMT", + "Client-Request-Id" : "640b46dd-fb71-4669-8261-88dd91f1ad51" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runPlayAudioFunctionWithResponseAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runPlayAudioFunctionWithResponseAsync[1].json new file mode 100644 index 0000000000000..d8574d03aedb1 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.runPlayAudioFunctionWithResponseAsync[1].json @@ -0,0 +1,103 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "d3e8b29c-9873-4ffa-92e8-5eef00a9e6c9", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0GLXDYAAAAADoFx/wpD5QSJzM+PjWNUiXV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "364812b0-6ab3-4c76-beed-65a88601485e", + "Body" : "{\"callLegId\":\"c4201300-4a43-4705-9d19-99bf6b09e0c9\",\"callConnectionId\":\"c4201300-4a43-4705-9d19-99bf6b09e0c9\"}", + "Date" : "Fri, 11 Jun 2021 19:10:15 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "a0627df9-9cf7-4265-b435-dd4375983df9" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "089f8f90-48cf-4fb4-83f5-f9495a38613d", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0GbXDYAAAAADd5a5tMwTtR4wUGI0nBxK8V1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "ff2e84cb-5829-4ae2-9fa2-dd58dc39869b", + "Body" : "{\"callLegId\":\"c4201300-da5a-4dc0-bfe1-cef0be59f51d\",\"callConnectionId\":\"c4201300-da5a-4dc0-bfe1-cef0be59f51d\"}", + "Date" : "Fri, 11 Jun 2021 19:10:16 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "6dbfe459-58c4-4534-a411-5e6b02421f0c" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:playAudio?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "50b9512a-853d-4866-9cc4-bf05cde1d092", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "122", + "retry-after" : "0", + "X-Azure-Ref" : "0GrXDYAAAAABH5nSIyKTuTKo9zQTjQv8NV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "d2e06f5c-07fa-4e79-bd8e-6d5df18f1f4d", + "Body" : "{\"id\":\"c4201300-4e84-4a3e-8c95-b3076ba3ce5b\",\"status\":\"running\",\"operationContext\":\"b7c82fb9-93ee-4c6b-89ed-d2ea68600811\"}", + "Date" : "Fri, 11 Jun 2021 19:10:19 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "e9d51b8a-bee3-4491-ac6c-7be4afe27cf9" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/c4201300-4a43-4705-9d19-99bf6b09e0c9/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "a1828c92-db7c-459d-aa56-88ea23e28b98" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0HLXDYAAAAACLjsYqsJoHQbF7a5ftV6noV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "8ebe25fc-655e-4383-bea2-8d21210e1995", + "Date" : "Fri, 11 Jun 2021 19:10:19 GMT", + "Client-Request-Id" : "105a7bbe-2853-4406-984e-761122395ac5" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/c4201300-da5a-4dc0-bfe1-cef0be59f51d/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "3fbed21a-f20a-44cf-a731-c698bd8dc6c5" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0HLXDYAAAAADvEA8FvFeyRYpqiSEXkEo5V1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "279cf4d7-e1f8-470c-8825-0ac85173dbf2", + "Date" : "Fri, 11 Jun 2021 19:10:19 GMT", + "Client-Request-Id" : "66aadc4d-d7b9-42df-9daa-8dbddae6fb7d" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.startRecordingFailsAsync[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.startRecordingFailsAsync[1].json new file mode 100644 index 0000000000000..ffdafb4a0380d --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallAsyncLiveTests.startRecordingFailsAsync[1].json @@ -0,0 +1,25 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/aHR0cHM6Ly9jb252LXVzd2UtMDkuY29udi5za3lwZS5jb20vY29udi9EZVF2WEJGVVlFV1NNZkFXYno2azN3P2k9MTEmZT02Mzc1NzIyMjk0Mjc0NTI4Nzk=/recordings?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "5cd22ad4-75f7-41f2-b55c-f8236308916a", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "83", + "retry-after" : "0", + "X-Azure-Ref" : "0HLXDYAAAAACKjLlzpwOwT6tuxNe9RifSV1NURURHRTA4MTIAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "400", + "X-Microsoft-Skype-Chain-ID" : "6ae47b8c-882f-4944-af44-7324d076eb5c", + "Body" : "{\"code\":\"8501\",\"message\":\"Action is invalid when call is not in Established state\"}", + "Date" : "Fri, 11 Jun 2021 19:10:20 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "8d977355-a1f3-4bdb-81a5-81046073d41a" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runAddRemoveScenarioWithResponse[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runAddRemoveScenarioWithResponse[1].json new file mode 100644 index 0000000000000..7efc291049395 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runAddRemoveScenarioWithResponse[1].json @@ -0,0 +1,76 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0csXBYAAAAAD8TwvuuVaQSZs0/7eyxTguREZXMzBFREdFMDUyMgA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "24cbd24f-2e98-42dd-b8f9-a71a24d8a636", + "Body" : "{\"callLegId\":\"bf201300-7563-4daf-9b7a-3f0c759b645f\",\"callConnectionId\":\"bf201300-7563-4daf-9b7a-3f0c759b645f\"}", + "Date" : "Thu, 10 Jun 2021 07:55:31 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "81c06d4f-03ad-4dac-b115-24d3c75c1075" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L1lXS2R2TTNRc0Vpc0VNYVUtNlhvSlE_aT0yJmU9NjM3NTg0Mzk2NDM5NzQ5NzY4/participants?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0o8XBYAAAAABl5a6WccLzTYKEt0d0YVkKREZXMzBFREdFMDUyMgA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "e8177e83-7a99-4999-9ed5-2d361e1af7ee", + "Date" : "Thu, 10 Jun 2021 07:56:19 GMT", + "Client-Request-Id" : "32339b79-6aea-49cb-aca4-37eaf3a08176" + }, + "Exception" : null + }, { + "Method" : "DELETE", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L1lXS2R2TTNRc0Vpc0VNYVUtNlhvSlE_aT0yJmU9NjM3NTg0Mzk2NDM5NzQ5NzY4/participants/76b33acb-5097-4af0-a646-e07ccee48957?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0ycXBYAAAAAAN4NnqJX2IQ44vzNuFBHpVREZXMzBFREdFMDUyMgA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "37291ab9-cc92-4a7a-8759-97329c2d6d4e", + "Date" : "Thu, 10 Jun 2021 07:56:58 GMT", + "Client-Request-Id" : "7e2eab4f-43c6-4efb-87ae-fe3469f3a53f" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/bf201300-7563-4daf-9b7a-3f0c759b645f/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0zMXBYAAAAABfEyALuTz4S5BX8j7vKyaxREZXMzBFREdFMDUyMgA5ZmM3YjUxOS1hOGNjLTRmODktOTM1ZS1jOTE0OGFlMDllODE=", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "ea2bfc55-cf96-48b2-9fcf-2a2583352f76", + "Date" : "Thu, 10 Jun 2021 07:56:59 GMT", + "Client-Request-Id" : "dd759dce-79c7-4690-b8dd-ac66fa8cb337" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runAddRemoveScenario[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runAddRemoveScenario[1].json new file mode 100644 index 0000000000000..ac1dc7360f4e1 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runAddRemoveScenario[1].json @@ -0,0 +1,76 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0ksTBYAAAAADYFgWmLmlmSIyjgN7n/OiKQ0hHRURHRTE2MTMAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "201", + "X-Microsoft-Skype-Chain-ID" : "5f3b3522-1472-4c56-9e31-b1f1fc4aee58", + "Body" : "{\"callLegId\":\"5b1f1300-2023-41f9-9ff8-1f61191e18b3\",\"callConnectionId\":\"5b1f1300-2023-41f9-9ff8-1f61191e18b3\"}", + "Date" : "Thu, 10 Jun 2021 07:51:47 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "8aa11694-a230-4096-bfff-81fd9be7bead" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L1ktWjZ5dzFzWVVTUUdWX2xPQWk1X2c_aT0xJmU9NjM3NTg0MzkzMzg3ODg3MDI3/participants?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0scTBYAAAAAADoWtTLrSPS6r+ycoTKN9lQ0hHRURHRTE2MTMAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "a5619337-8e2d-4016-9de9-f7ffbcb27f7a", + "Date" : "Thu, 10 Jun 2021 07:52:17 GMT", + "Client-Request-Id" : "c61c1a4d-4411-4353-a78e-394da9de2968" + }, + "Exception" : null + }, { + "Method" : "DELETE", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/aHR0cHM6Ly94LWNvbnYtdXN3ZS0wMS5jb252LnNreXBlLmNvbS9jb252L1ktWjZ5dzFzWVVTUUdWX2xPQWk1X2c_aT0xJmU9NjM3NTg0MzkzMzg3ODg3MDI3/participants/72647661-033a-4d1a-b858-465375977be0?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "02MTBYAAAAABfV+k0Qca0QqmmBsR1O00QQ0hHRURHRTE2MTMAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "fca27eb7-a86a-457c-82c3-a2d52e0ae9cd", + "Date" : "Thu, 10 Jun 2021 07:52:55 GMT", + "Client-Request-Id" : "1652deef-be42-4e58-a016-58e2e1871b5c" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/5b1f1300-2023-41f9-9ff8-1f61191e18b3/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (16.0.1; Windows 10; 10.0)" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "03MTBYAAAAACatTaHAzB2SKJWBGpW7Sj6Q0hHRURHRTE2MTMAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "ddf56d8b-9b3e-4468-9018-f3d7ac44aa56", + "Date" : "Thu, 10 Jun 2021 07:52:59 GMT", + "Client-Request-Id" : "0a7658bc-6048-4f5a-af64-397f1d0a7289" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runAllClientFunctionsWithResponse[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runAllClientFunctionsWithResponse[1].json new file mode 100644 index 0000000000000..0f17c530111ca --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runAllClientFunctionsWithResponse[1].json @@ -0,0 +1,217 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "1ab4a329-62f4-444f-8dde-0bff2a7b281d", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0zrTDYAAAAAA1+8/oHqmcQYmPO81uFWcsV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "dd7661ea-eec3-4a85-b7be-9f4d76800675", + "Body" : "{\"callLegId\":\"711f1300-ce1b-4397-b911-32aafe7161d7\",\"callConnectionId\":\"711f1300-ce1b-4397-b911-32aafe7161d7\"}", + "Date" : "Fri, 11 Jun 2021 19:09:01 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "f5ecee0d-ccf6-4c18-b74f-aab60d3614f8" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "671137a5-814b-41e5-94d1-8faac29dbb22", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0z7TDYAAAAADnj8lqQXUhSaBmmIWqQZAPV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "0aeb2c01-843d-4135-ac4f-289be71185bc", + "Body" : "{\"callLegId\":\"711f1300-7975-4f2c-b182-49ae185b88b3\",\"callConnectionId\":\"711f1300-7975-4f2c-b182-49ae185b88b3\"}", + "Date" : "Fri, 11 Jun 2021 19:09:02 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "703b2f64-6f9f-4de4-bc94-7c6219afff20" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "745728cc-cb9f-4259-ac68-351097d0f1bf", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "180", + "retry-after" : "0", + "X-Azure-Ref" : "00LTDYAAAAADfVm7YRvZ/SbJhN8brdpoXV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "8b46baaf-7cfe-4a08-b672-deb335fe9536", + "Body" : "{\"recordingId\":\"eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiI3MTFmMTMwMC1hYzhiLTQwZmYtOTdmNy1jMjM5ZTBkZTM2MTUiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiIzM2ZhOWFkYS1mNGE2LTQwMDQtOTk2OS02YjY0NmI0NDk1ZDYifQ\"}", + "Date" : "Fri, 11 Jun 2021 19:09:05 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "662a2c9e-32e4-4f08-a778-71762cd0ee53" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiI3MTFmMTMwMC1hYzhiLTQwZmYtOTdmNy1jMjM5ZTBkZTM2MTUiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiIzM2ZhOWFkYS1mNGE2LTQwMDQtOTk2OS02YjY0NmI0NDk1ZDYifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "2cdd8eb4-ef17-4d93-9537-2bb4446e9ee4" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "27", + "retry-after" : "0", + "X-Azure-Ref" : "02LTDYAAAAAAz80sXPdZmSrVbEeFRQo/yV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "0af7e2a2-a540-4af9-880b-c58f146ee618", + "Body" : "{\"recordingState\":\"active\"}", + "Date" : "Fri, 11 Jun 2021 19:09:12 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "2dd35851-d8f0-498c-8328-07dc8f028d89" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiI3MTFmMTMwMC1hYzhiLTQwZmYtOTdmNy1jMjM5ZTBkZTM2MTUiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiIzM2ZhOWFkYS1mNGE2LTQwMDQtOTk2OS02YjY0NmI0NDk1ZDYifQ/:pause?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "e703d07d-b96a-4352-9aaf-63761b92b5bc" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "02LTDYAAAAAASQu5d/go1S6GGaEv0PWJhV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "9eb4993c-4808-4500-bfc1-cee60bf1bdd5", + "Date" : "Fri, 11 Jun 2021 19:09:12 GMT", + "Client-Request-Id" : "f6145cf0-c998-41af-9004-29b00adf0ee4" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiI3MTFmMTMwMC1hYzhiLTQwZmYtOTdmNy1jMjM5ZTBkZTM2MTUiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiIzM2ZhOWFkYS1mNGE2LTQwMDQtOTk2OS02YjY0NmI0NDk1ZDYifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "56f3f354-578d-434e-85fb-12e40ada475e" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "29", + "retry-after" : "0", + "X-Azure-Ref" : "03rTDYAAAAAB5QuA6bXQ4So0eb6wQLC4KV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "db14327d-5356-4141-b48e-e4a9090407f0", + "Body" : "{\"recordingState\":\"inactive\"}", + "Date" : "Fri, 11 Jun 2021 19:09:18 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "4b5f59a9-ce8e-44d5-b91f-8ad128c7510a" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiI3MTFmMTMwMC1hYzhiLTQwZmYtOTdmNy1jMjM5ZTBkZTM2MTUiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiIzM2ZhOWFkYS1mNGE2LTQwMDQtOTk2OS02YjY0NmI0NDk1ZDYifQ/:resume?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "026684a5-6b1f-421c-a697-a5437466efa1" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "03rTDYAAAAABvxfXZe27gSKFzlSKzP/eRV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "31d549a2-70df-48a6-9b94-402e2f139c36", + "Date" : "Fri, 11 Jun 2021 19:09:18 GMT", + "Client-Request-Id" : "837102de-72d1-4ff1-8621-feb8f32e6ca8" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiI3MTFmMTMwMC1hYzhiLTQwZmYtOTdmNy1jMjM5ZTBkZTM2MTUiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiIzM2ZhOWFkYS1mNGE2LTQwMDQtOTk2OS02YjY0NmI0NDk1ZDYifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "aafe7634-b9b8-4664-9f8d-ab0c01965693" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "27", + "retry-after" : "0", + "X-Azure-Ref" : "05LTDYAAAAAAJFqHWx3RLSbqzHFTh8rTkV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "c1e87598-5046-42a5-8176-a041df1ee65a", + "Body" : "{\"recordingState\":\"active\"}", + "Date" : "Fri, 11 Jun 2021 19:09:24 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "aefa3af8-687a-4913-aaf7-00f46af7676d" + }, + "Exception" : null + }, { + "Method" : "DELETE", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiI3MTFmMTMwMC1hYzhiLTQwZmYtOTdmNy1jMjM5ZTBkZTM2MTUiLCJSZXNvdXJjZVNwZWNpZmljSWQiOiIzM2ZhOWFkYS1mNGE2LTQwMDQtOTk2OS02YjY0NmI0NDk1ZDYifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "026ac5d4-081a-496a-876a-b477006b5da6" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "05bTDYAAAAAA9+rgJrqmLQLmw0463HwLJV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "75d5c9ac-c7e3-4e89-b843-3d473645db37", + "Date" : "Fri, 11 Jun 2021 19:09:24 GMT", + "Client-Request-Id" : "6623a873-a7ea-4ec8-8e22-01c4122a6f95" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/711f1300-ce1b-4397-b911-32aafe7161d7/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "9e8bac23-14fd-43e9-a3d5-dae31abd139b" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "05bTDYAAAAABXtYcvIg3vQrCfs9LnDy9UV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "b58854fe-da55-4942-9f0b-0bc203705145", + "Date" : "Fri, 11 Jun 2021 19:09:24 GMT", + "Client-Request-Id" : "e1bf5c6b-3a28-4c93-a143-6dfd93ecfdcc" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/711f1300-7975-4f2c-b182-49ae185b88b3/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "38a2ef09-525d-489e-91ca-1defe3673d77" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "05bTDYAAAAAAPjPwccsTPTow6DYDsJymWV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "83fb2c68-8d99-478c-ba9d-3647d8d0851d", + "Date" : "Fri, 11 Jun 2021 19:09:24 GMT", + "Client-Request-Id" : "0d191f76-e613-4f6f-bf7a-4b28d652df1b" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runAllClientFunctions[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runAllClientFunctions[1].json new file mode 100644 index 0000000000000..cdb6620190678 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runAllClientFunctions[1].json @@ -0,0 +1,217 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "785bfbff-6a4c-4160-9e88-9f9d49059b3c", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0rbTDYAAAAADdv40O3PW6R4iogm0qZAoSV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "d01eaee8-c1ca-439e-9048-2be5af0ebb0b", + "Body" : "{\"callLegId\":\"711f1300-16c6-4c44-b343-9b88885799f5\",\"callConnectionId\":\"711f1300-16c6-4c44-b343-9b88885799f5\"}", + "Date" : "Fri, 11 Jun 2021 19:08:29 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "4e59028a-47a2-4b78-9e58-0eea71506965" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "690c2e90-8886-4cd7-869a-c9587b58ad13", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0r7TDYAAAAACMvehoMWqOR5qdJGE23rvRV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "75f8c959-d8f7-4eb7-a196-4f9dd490e3d0", + "Body" : "{\"callLegId\":\"711f1300-c678-4a6e-8313-1fc0339f60b7\",\"callConnectionId\":\"711f1300-c678-4a6e-8313-1fc0339f60b7\"}", + "Date" : "Fri, 11 Jun 2021 19:08:30 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "a512ae1e-a7b3-4a1d-8689-d9dc2778fc28" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "ddbb1a94-11d2-4a91-ac14-568301139241", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "180", + "retry-after" : "0", + "X-Azure-Ref" : "0sLTDYAAAAACP8ewFfgo8SY+DmybdDs9pV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "fb24359e-1520-4fc8-ae16-c12baf339c32", + "Body" : "{\"recordingId\":\"eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiI3MTFmMTMwMC03MDI3LTQ4NDYtOGRhNC1kMTU2NWM5M2NlNTciLCJSZXNvdXJjZVNwZWNpZmljSWQiOiI4YTk5YTljMy04MWYwLTQ5MmUtYjA1ZS0wYTY1ZWIyZWNjOWUifQ\"}", + "Date" : "Fri, 11 Jun 2021 19:08:34 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "7f960c25-8daa-419d-a5cc-c21fd06ba032" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiI3MTFmMTMwMC03MDI3LTQ4NDYtOGRhNC1kMTU2NWM5M2NlNTciLCJSZXNvdXJjZVNwZWNpZmljSWQiOiI4YTk5YTljMy04MWYwLTQ5MmUtYjA1ZS0wYTY1ZWIyZWNjOWUifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "737c5f43-bc9a-414e-8151-eaf5d73c580d" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "27", + "retry-after" : "0", + "X-Azure-Ref" : "0ubTDYAAAAAAYS4D55EjUTKr4cZeXtwn0V1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "5afbfd52-f0e9-40de-8ddc-1a57a65705cf", + "Body" : "{\"recordingState\":\"active\"}", + "Date" : "Fri, 11 Jun 2021 19:08:40 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "10025db2-dbb9-49fb-ab4c-c2014f29d5f2" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiI3MTFmMTMwMC03MDI3LTQ4NDYtOGRhNC1kMTU2NWM5M2NlNTciLCJSZXNvdXJjZVNwZWNpZmljSWQiOiI4YTk5YTljMy04MWYwLTQ5MmUtYjA1ZS0wYTY1ZWIyZWNjOWUifQ/:pause?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "60b1f488-0c02-4bfe-bfe6-8c18bae0e799" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0ubTDYAAAAAA/MECPAquRRYMKy0YPeCYkV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "3e68d0c8-1ea5-45c7-9194-bce79f6f4b4d", + "Date" : "Fri, 11 Jun 2021 19:08:41 GMT", + "Client-Request-Id" : "2930b36d-6ddb-41f8-a1c5-bd5bad5bdb4f" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiI3MTFmMTMwMC03MDI3LTQ4NDYtOGRhNC1kMTU2NWM5M2NlNTciLCJSZXNvdXJjZVNwZWNpZmljSWQiOiI4YTk5YTljMy04MWYwLTQ5MmUtYjA1ZS0wYTY1ZWIyZWNjOWUifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "2a80afc8-dbb2-473d-bdb4-491d54c1945c" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "29", + "retry-after" : "0", + "X-Azure-Ref" : "0v7TDYAAAAACZmof/4dX0SLy1eLA7XRSWV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "59affe09-dfd5-4bde-a9c5-1db58efbcfb2", + "Body" : "{\"recordingState\":\"inactive\"}", + "Date" : "Fri, 11 Jun 2021 19:08:47 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "d2e3f9b4-28dc-46df-8490-76a3a76df2e7" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiI3MTFmMTMwMC03MDI3LTQ4NDYtOGRhNC1kMTU2NWM5M2NlNTciLCJSZXNvdXJjZVNwZWNpZmljSWQiOiI4YTk5YTljMy04MWYwLTQ5MmUtYjA1ZS0wYTY1ZWIyZWNjOWUifQ/:resume?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "e611f5e9-7260-4b88-a895-185ae6469605" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0v7TDYAAAAADCtDUtxWUASatgxk7r+/uaV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "f410a2d9-064c-4767-b4d9-5beb16c5c430", + "Date" : "Fri, 11 Jun 2021 19:08:47 GMT", + "Client-Request-Id" : "8f24914e-27ac-4c44-9f5a-e83ae183d060" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiI3MTFmMTMwMC03MDI3LTQ4NDYtOGRhNC1kMTU2NWM5M2NlNTciLCJSZXNvdXJjZVNwZWNpZmljSWQiOiI4YTk5YTljMy04MWYwLTQ5MmUtYjA1ZS0wYTY1ZWIyZWNjOWUifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "51916d9b-eeaa-476e-98d7-7ed269da222d" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "27", + "retry-after" : "0", + "X-Azure-Ref" : "0xrTDYAAAAABk3JuqmKzSQanlQRDVGF1wV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "347c0f23-8893-4ef9-9de4-69b03ae7c6d1", + "Body" : "{\"recordingState\":\"active\"}", + "Date" : "Fri, 11 Jun 2021 19:08:53 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "e06e71c4-e23f-4ea6-8604-3719ea9af832" + }, + "Exception" : null + }, { + "Method" : "DELETE", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/recordings/eyJQbGF0Zm9ybUVuZHBvaW50SWQiOiI3MTFmMTMwMC03MDI3LTQ4NDYtOGRhNC1kMTU2NWM5M2NlNTciLCJSZXNvdXJjZVNwZWNpZmljSWQiOiI4YTk5YTljMy04MWYwLTQ5MmUtYjA1ZS0wYTY1ZWIyZWNjOWUifQ?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "c11f4f48-2b00-4ce4-917d-84e5ef9ddccb" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0xrTDYAAAAAA2tB0oI1P9R4lgtAcDbSigV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "200", + "X-Microsoft-Skype-Chain-ID" : "58e201d5-490e-41ea-8ece-ee58c88431fe", + "Date" : "Fri, 11 Jun 2021 19:08:54 GMT", + "Client-Request-Id" : "7926548a-2d32-4969-a51a-2c1a3abd6f3e" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/711f1300-16c6-4c44-b343-9b88885799f5/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "6ebdca41-6e8e-47f7-8dab-bcf666053a69" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0xrTDYAAAAACWdHww2JlNSaf0BvANI+7MV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "c69c6a60-66db-4315-89f5-5ea91f5184cd", + "Date" : "Fri, 11 Jun 2021 19:08:54 GMT", + "Client-Request-Id" : "132d9370-352e-4f28-95c2-3bae80cdb1e0" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/711f1300-c678-4a6e-8313-1fc0339f60b7/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "803b4ee6-35ee-4c2d-a329-35a3a77483ea" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0xrTDYAAAAADAO+CyTRqzSbVT2fuOy8E3V1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "c1353ecd-0d8e-4773-b085-4053ea6128b2", + "Date" : "Fri, 11 Jun 2021 19:08:54 GMT", + "Client-Request-Id" : "d3f6b604-3f7e-488b-8095-62bbb33ae51b" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runPlayAudioFunctionWithResponse[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runPlayAudioFunctionWithResponse[1].json new file mode 100644 index 0000000000000..58b8c0e2f8673 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runPlayAudioFunctionWithResponse[1].json @@ -0,0 +1,103 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "44e161bf-2026-4f78-ac83-ef69ea4e9f54", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0yrTDYAAAAAD68ZaqcOaXR7xtuD1kv3e/V1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "cc593fee-bb00-475b-8e3a-944f3f64b518", + "Body" : "{\"callLegId\":\"711f1300-a631-4228-bdab-71a7a985879d\",\"callConnectionId\":\"711f1300-a631-4228-bdab-71a7a985879d\"}", + "Date" : "Fri, 11 Jun 2021 19:08:58 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "12a4f9b4-0683-414f-9e4c-8190c837d8d2" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "fcbe23fc-d42f-485d-b83d-021021815657", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0zLTDYAAAAACHxpay/jbbTJWL+6OQ+T7KV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "0bf112b9-7084-4f8c-a8c5-5f15c28601e3", + "Body" : "{\"callLegId\":\"711f1300-97ee-4aed-a606-fd2a8c97f6db\",\"callConnectionId\":\"711f1300-97ee-4aed-a606-fd2a8c97f6db\"}", + "Date" : "Fri, 11 Jun 2021 19:08:59 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "1fabd153-c146-44f4-8db6-2a5707b2d055" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:playAudio?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "b1cefe60-8bee-4720-aeca-0cd7de97dbe7", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "122", + "retry-after" : "0", + "X-Azure-Ref" : "0zbTDYAAAAADHoPbrIsdFRJfckldC317lV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "5b4cc20b-2d71-4ffc-9644-237e78a6d27e", + "Body" : "{\"id\":\"711f1300-12d8-423a-8a98-2810a7903497\",\"status\":\"running\",\"operationContext\":\"9a0d9a98-8044-4548-a780-4757ce77be3a\"}", + "Date" : "Fri, 11 Jun 2021 19:09:01 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "e9b89185-725c-4059-9b82-275c99a8297e" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/711f1300-a631-4228-bdab-71a7a985879d/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "9817d6b7-43fc-4eb0-9d67-9d9cad3f2262" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0zrTDYAAAAAAMv8yfn4dgTpZT8Yry1T9tV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "18c69c8e-78a8-4e5b-8863-1dc6d41dbe36", + "Date" : "Fri, 11 Jun 2021 19:09:01 GMT", + "Client-Request-Id" : "a4e5e4cf-4af0-4187-9fc3-3b8dd9f057b7" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/711f1300-97ee-4aed-a606-fd2a8c97f6db/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "2a21d701-7632-4535-932b-4439806d10ba" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0zrTDYAAAAABLkItLgWV4TpKci2L9sTKmV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "f01a2df4-9cb8-44d3-9a01-78e3f7a7ee06", + "Date" : "Fri, 11 Jun 2021 19:09:01 GMT", + "Client-Request-Id" : "5ad46571-41e4-4010-832d-64182d89f435" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runPlayAudioFunction[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runPlayAudioFunction[1].json new file mode 100644 index 0000000000000..2fdaa8717f266 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.runPlayAudioFunction[1].json @@ -0,0 +1,103 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "0d26fe6b-0b02-45ac-8d4d-c4a1ebc225d6", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0xrTDYAAAAACi+GhXqrIFQabdb7nWLfk3V1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "055d160e-7c8f-4e20-9e00-35abbc765e4a", + "Body" : "{\"callLegId\":\"711f1300-aef8-4015-88fa-b3e296c27c51\",\"callConnectionId\":\"711f1300-aef8-4015-88fa-b3e296c27c51\"}", + "Date" : "Fri, 11 Jun 2021 19:08:54 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "0761fefc-6701-462c-b85a-3c52f01cebde" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:join?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "a5acca44-11dd-48af-b515-3931ff662de1", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "110", + "retry-after" : "0", + "X-Azure-Ref" : "0x7TDYAAAAAAwy/jkWiHuQrb9hP+53f5+V1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "c7e12f74-b156-43f6-8522-76ecc93ac54a", + "Body" : "{\"callLegId\":\"711f1300-caea-4e75-b8c5-6a4c99f2a0d1\",\"callConnectionId\":\"711f1300-caea-4e75-b8c5-6a4c99f2a0d1\"}", + "Date" : "Fri, 11 Jun 2021 19:08:55 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "b390344e-6049-4d44-8539-b543c279853d" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/c400789f-e11b-4ceb-88cb-bc8df2a01568/:playAudio?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "08f44654-3b37-41ec-8220-e2e386b3750c", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "122", + "retry-after" : "0", + "X-Azure-Ref" : "0ybTDYAAAAADDIPhaL6I4Q7c++H0wNSwJV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "35a3e4dc-b0cc-41ed-83cb-fdadb14a2b6c", + "Body" : "{\"id\":\"711f1300-08f1-44f6-adaa-ec6c0e7867ef\",\"status\":\"running\",\"operationContext\":\"f07dc34c-0927-4a54-8d97-951c15275660\"}", + "Date" : "Fri, 11 Jun 2021 19:08:58 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "e116312f-b8d5-46d0-8ca5-ebf5d094bf67" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/711f1300-aef8-4015-88fa-b3e296c27c51/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "92e68af9-73c8-4106-be8b-03b4dfda3ced" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0yrTDYAAAAAB7zFDNiyuISowJgESzxh/tV1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "61a85e45-302b-4e82-9ccd-5216d7034786", + "Date" : "Fri, 11 Jun 2021 19:08:58 GMT", + "Client-Request-Id" : "20c1b6cd-63f8-42b1-acae-fb23bf990d29" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/callConnections/711f1300-caea-4e75-b8c5-6a4c99f2a0d1/:hangup?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "1516b878-78be-4bad-9ec5-e53f99c85897" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "0", + "retry-after" : "0", + "X-Azure-Ref" : "0yrTDYAAAAACMToDca6/7RIywYhc1gN70V1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "202", + "X-Microsoft-Skype-Chain-ID" : "c2ba9386-861c-4430-a6d9-ccb7f1dbf9b9", + "Date" : "Fri, 11 Jun 2021 19:08:58 GMT", + "Client-Request-Id" : "0d72cdc1-af5c-4617-bdb3-ac8b4b46be1d" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.startRecordingFails[1].json b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.startRecordingFails[1].json new file mode 100644 index 0000000000000..549bc7e87e14c --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/src/test/resources/session-records/ServerCallLiveTests.startRecordingFails[1].json @@ -0,0 +1,25 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.communication.azure.com/calling/serverCalls/aHR0cHM6Ly9jb252LXVzd2UtMDkuY29udi5za3lwZS5jb20vY29udi9EZVF2WEJGVVlFV1NNZkFXYno2azN3P2k9MTEmZT02Mzc1NzIyMjk0Mjc0NTI4Nzk=/recordings?api-version=2021-06-15-preview", + "Headers" : { + "User-Agent" : "azsdk-java-azure-communication-callingserver/1.0.0-beta.1 (11.0.11; Windows 10; 10.0)", + "x-ms-client-request-id" : "5593f679-c772-4dfa-913e-991fadc10fa6", + "Content-Type" : "application/json" + }, + "Response" : { + "X-Cache" : "CONFIG_NOCACHE", + "content-length" : "83", + "retry-after" : "0", + "X-Azure-Ref" : "0yrTDYAAAAAAhaNuRdLLwQb+0Ow+i9sV0V1NURURHRTA4MjEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "StatusCode" : "400", + "X-Microsoft-Skype-Chain-ID" : "e2217417-47af-4945-8f00-6b82b33dcca7", + "Body" : "{\"code\":\"8501\",\"message\":\"Action is invalid when call is not in Established state\"}", + "Date" : "Fri, 11 Jun 2021 19:08:58 GMT", + "Content-Type" : "application/json; charset=utf-8", + "Client-Request-Id" : "83cef93f-702d-4629-985d-db634ca06157" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-callingserver/swagger/README.md b/sdk/communication/azure-communication-callingserver/swagger/README.md new file mode 100644 index 0000000000000..ac64b1b9c6e81 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/swagger/README.md @@ -0,0 +1,95 @@ +# Azure Communication Calling Service client library for Java + +> see https://aka.ms/autorest +## Getting Started + +To build the SDK for Server Calling Client, simply Install AutoRest and in this folder, run: + +### Setup +```ps +Fork and clone https://github.com/Azure/autorest.java +git checkout v4 +git submodule update --init --recursive +mvn package -Dlocal +npm install +npm install -g autorest +``` + +### Generation + +There is one swagger for Calling management APIs. + +```ps +cd +autorest README.md --java --v4 --use=@autorest/java@4.0.20 --use=@autorest/modelerfour@4.15.442 +``` + +## Update generated files for server calling service +To update generated files for calling service, run the following command + +> autorest README.md --java --v4 --use=@autorest/java@4.0.20 --use=@autorest/modelerfour@4.15.442 + +### Code generation settings +``` yaml +tag: package-2021-06-15-preview +require: + - https://raw.githubusercontent.com/Azure/azure-rest-api-specs/74575fadb83fcd08a70d1168c8d04a6ca5e90715/specification/communication/data-plane/CallingServer/readme.md +java: true +output-folder: ..\ +license-header: MICROSOFT_MIT_SMALL +namespace: com.azure.communication.callingserver +custom-types: ToneValue,OperationStatus,CallRecordingState,CallConnectionState,EventSubscriptionType,CallModality +custom-types-subpackage: models +generate-client-as-impl: true +models-subpackage: implementation.models +sync-methods: all +add-context-parameter: true +context-client-method-parameter: true +title: Azure Communication CallingServer Service +directive: +- rename-model: + from: CallRecordingStateChangeEvent + to: CallRecordingStateChangeEventInternal +- rename-model: + from: InviteParticipantsResultEvent + to: InviteParticipantsResultEventInternal +- rename-model: + from: PlayAudioResultEvent + to: PlayAudioResultEventInternal +- rename-model: + from: ToneReceivedEvent + to: ToneReceivedEventInternal +- rename-model: + from: CallConnectionStateChangedEvent + to: CallConnectionStateChangedEventInternal +- rename-model: + from: ParticipantsUpdatedEvent + to: ParticipantsUpdatedEventInternal +- rename-model: + from: CommunicationParticipant + to: CommunicationParticipantInternal +- rename-model: + from: JoinCallResult + to: JoinCallResultInternal +- rename-model: + from: PlayAudioResult + to: PlayAudioResultInternal +- rename-model: + from: CallRecordingStateResult + to: CallRecordingStateResultInternal +- rename-model: + from: StartCallRecordingResult + to: StartCallRecordingResultInternal +- rename-model: + from: CreateCallResult + to: CreateCallResultInternal +- rename-model: + from: CancelAllMediaOperationsResult + to: CancelAllMediaOperationsResultInternal +- rename-model: + from: ResultInfo + to: ResultInfoInternal +- rename-model: + from: ToneInfo + to: ToneInfoInternal +``` diff --git a/sdk/communication/azure-communication-callingserver/tests.yml b/sdk/communication/azure-communication-callingserver/tests.yml new file mode 100644 index 0000000000000..b66a735649257 --- /dev/null +++ b/sdk/communication/azure-communication-callingserver/tests.yml @@ -0,0 +1,7 @@ +trigger: none + +stages: + - template: /sdk/communication/communication-tests-template.yml + parameters: + PackageName: azure-communication-callingserver + SafeName: azurecommunicationcallingserver diff --git a/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/implementation/HmacAuthenticationPolicy.java b/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/implementation/HmacAuthenticationPolicy.java index ea43fabf58252..890ef1496eed4 100644 --- a/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/implementation/HmacAuthenticationPolicy.java +++ b/sdk/communication/azure-communication-common/src/main/java/com/azure/communication/common/implementation/HmacAuthenticationPolicy.java @@ -81,8 +81,13 @@ public Mono process(HttpPipelineCallContext context, HttpPipelineN } try { + URL hostnameToSignWith = context.getData("hmacSignatureURL") + .filter(alternativeUrl -> alternativeUrl instanceof URL) + .map(alternativeUrl -> (URL) alternativeUrl) + .orElse(context.getHttpRequest().getUrl()); + return appendAuthorizationHeaders( - context.getHttpRequest().getUrl(), + hostnameToSignWith, context.getHttpRequest().getHttpMethod().toString(), contents) .flatMap(headers -> { 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 809cdf7f52391..e8c3df4aac49a 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 @@ -15,5 +15,6 @@ com.azure.communication.sms, com.azure.communication.identity, com.azure.communication.phonenumbers, - com.azure.communication.chat; + com.azure.communication.chat, + com.azure.communication.callingserver; } diff --git a/sdk/communication/ci.yml b/sdk/communication/ci.yml index 85193840a324c..76fb93d82f9e1 100644 --- a/sdk/communication/ci.yml +++ b/sdk/communication/ci.yml @@ -28,6 +28,8 @@ extends: parameters: ServiceDirectory: communication Artifacts: + - name: azure-communication-callingserver + groupId: com.azure - name: azure-communication-common groupId: com.azure safeName: azurecommunicationcommon diff --git a/sdk/communication/pom.xml b/sdk/communication/pom.xml index 1e4a5f23188af..7e43163f1ed91 100644 --- a/sdk/communication/pom.xml +++ b/sdk/communication/pom.xml @@ -13,6 +13,7 @@ coverage + azure-communication-callingserver azure-communication-chat azure-communication-common azure-communication-identity @@ -21,6 +22,11 @@ + + com.azure + azure-communication-callingserver + 1.0.0-beta.1 + com.azure azure-communication-chat @@ -56,6 +62,7 @@ 0.8.5 + com/azure/communication/callingserver/*.class com/azure/communication/chat/*.class com/azure/communication/sms/*.class com/azure/communication/phonenumbers/*.class @@ -83,6 +90,7 @@ true + azure-communication-callingserver azure-communication-chat azure-communication-common azure-communication-identity