Skip to content

Commit

Permalink
Merge pull request #42 from sinch/DEVEXP-215-SAVML-helpers-shared-by-…
Browse files Browse the repository at this point in the history
  • Loading branch information
JPPortier authored Jan 31, 2024
2 parents 5c93bf4 + 5c23a7f commit d1fc1d8
Show file tree
Hide file tree
Showing 127 changed files with 5,357 additions and 2,009 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.sinch.sdk.domains.common.adapters.converters;

import java.time.Instant;
import java.time.OffsetDateTime;

public class OffsetDateTimeDtoConverter {

public static Instant convert(OffsetDateTime dto) {
return null != dto ? dto.toInstant() : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ boolean validateAuthenticatedRequest(
VerificationEvent unserializeVerificationEvent(String jsonPayload) throws ApiMappingException;

/**
* This function can be called to serialize a verification response to be send as JSON
* This function can be called to serialize a verification response to be sent as JSON
*
* @param response The response to be serialized
* @return The JSON string to be sent
Expand Down
7 changes: 3 additions & 4 deletions client/src/main/com/sinch/sdk/domains/voice/CallsService.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.sinch.sdk.domains.voice;

import com.sinch.sdk.domains.voice.models.CallLegType;
import com.sinch.sdk.domains.voice.models.requests.CallsUpdateRequestParameters;
import com.sinch.sdk.domains.voice.models.response.CallInformation;
import com.sinch.sdk.domains.voice.models.svaml.SVAMLControl;

/**
* Using the Calls service, you can manage on-going calls or retrieve information about a call.
Expand Down Expand Up @@ -37,7 +37,7 @@ public interface CallsService {
* @param callId The unique identifier of the call. This value is generated by the system
* @param parameters Tasks to be used related to this call
*/
void update(String callId, CallsUpdateRequestParameters parameters);
void update(String callId, SVAMLControl parameters);

/**
* This method is used to manage ongoing, connected calls. This method is only used when using the
Expand All @@ -51,6 +51,5 @@ public interface CallsService {
* @param callId The unique identifier of the call. This value is generated by the system
* @param parameters Tasks to be used related to this call
*/
void manageWithCallLeg(
String callId, CallLegType callLeg, CallsUpdateRequestParameters parameters);
void manageWithCallLeg(String callId, CallLegType callLeg, SVAMLControl parameters);
}
8 changes: 8 additions & 0 deletions client/src/main/com/sinch/sdk/domains/voice/VoiceService.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,12 @@ public interface VoiceService {
* @since 1.0
*/
ApplicationsService applications();

/**
* Webhooks helpers instance
*
* @return instance service related to webhooks helpers
* @since 1.0
*/
WebHooksService webhooks();
}
59 changes: 59 additions & 0 deletions client/src/main/com/sinch/sdk/domains/voice/WebHooksService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.sinch.sdk.domains.voice;

import com.sinch.sdk.core.exceptions.ApiMappingException;
import com.sinch.sdk.domains.voice.models.svaml.SVAMLControl;
import com.sinch.sdk.domains.voice.models.webhooks.WebhooksEvent;
import java.util.Map;

/**
* Webhooks service
*
* @see <a
* href="https://developers.sinch.com/docs/voice/api-reference/voice/tag/Callbacks">https://developers.sinch.com/docs/voice/api-reference/voice/tag/Callbacks/</a>
* @since 1.0
*/
public interface WebHooksService {

/**
* The Sinch Platform can initiate callback requests to a URL you define (Callback URL) on request
* and result events. All callback requests are signed using your Application key and secret pair
* found on your dashboard. The signature is included in the Authorization header of the request
*
* <p>By using following function, you can ensure authentication according to received payload
* from your backend
*
* @param method The HTTP method used ot handle the callback
* @param path The path to you backend endpoint used for callback
* @param headers Received headers
* @param jsonPayload Received payload
* @return Is authentication is validated (true) or not (false)
* <p>see <a
* href="https://developers.sinch.com/docs/voice/api-reference/authentication/callback-signed-request">https://developers.sinch.com/docs/voice/api-reference/authentication/callback-signed-request/</a>
* @since 1.0
*/
boolean validateAuthenticatedRequest(
String method, String path, Map<String, String> headers, String jsonPayload);

/**
* This function can be called to deserialize received payload onto callback onto proper java
* Voice event class
*
* @param jsonPayload Received payload to be deserialized
* @return The Voice event instance class
* <p>see <a
* href="https://developers.sinch.com/docs/voice/api-reference/voice/tag/Callbacks/">https://developers.sinch.com/docs/voice/api-reference/voice/tag/Callbacks/</a>
* @since 1.0
*/
WebhooksEvent unserializeWebhooksEvent(String jsonPayload) throws ApiMappingException;

/**
* This function can be called to serialize a Voice response to be sent as JSON
*
* @param response The response to be serialized
* @return The JSON string to be sent
* <p>see <a
* href="https://developers.sinch.com/docs/voice/api-reference/voice/tag/Callbacks/">https://developers.sinch.com/docs/voice/api-reference/voice/tag/Callbacks/</a>
* @since 1.0
*/
String serializeWebhooksResponse(SVAMLControl response) throws ApiMappingException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import com.sinch.sdk.domains.voice.adapters.api.v1.CallsApi;
import com.sinch.sdk.domains.voice.adapters.converters.CallsDtoConverter;
import com.sinch.sdk.domains.voice.models.CallLegType;
import com.sinch.sdk.domains.voice.models.requests.CallsUpdateRequestParameters;
import com.sinch.sdk.domains.voice.models.response.CallInformation;
import com.sinch.sdk.domains.voice.models.svaml.SVAMLControl;
import com.sinch.sdk.models.Configuration;
import java.util.Map;

Expand All @@ -30,12 +30,11 @@ public CallInformation get(String callId) {
return CallsDtoConverter.convert(getApi().callingGetCallResult(callId));
}

public void update(String callId, CallsUpdateRequestParameters parameters) {
public void update(String callId, SVAMLControl parameters) {
getApi().callingUpdateCall(callId, CallsDtoConverter.convert(parameters));
}

public void manageWithCallLeg(
String callId, CallLegType callLeg, CallsUpdateRequestParameters parameters) {
public void manageWithCallLeg(String callId, CallLegType callLeg, SVAMLControl parameters) {
getApi()
.callingManageCallWithCallLeg(
callId, callLeg.value(), CallsDtoConverter.convert(parameters));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.sinch.sdk.core.http.HttpClient;
import com.sinch.sdk.domains.voice.ApplicationsService;
import com.sinch.sdk.domains.voice.CalloutsService;
import com.sinch.sdk.domains.voice.WebHooksService;
import com.sinch.sdk.models.Configuration;
import java.util.Map;
import java.util.Objects;
Expand All @@ -26,6 +27,7 @@ public class VoiceService implements com.sinch.sdk.domains.voice.VoiceService {
private ConferencesService conferences;
private CallsService calls;
private ApplicationsService applications;
private WebHooksService webhooks;

private Map<String, AuthManager> clientAuthManagers;
private Map<String, AuthManager> webhooksAuthManagers;
Expand Down Expand Up @@ -99,6 +101,15 @@ public ApplicationsService applications() {
return this.applications;
}

public WebHooksService webhooks() {
checkCredentials();
if (null == this.webhooks) {
this.webhooks =
new com.sinch.sdk.domains.voice.adapters.WebHooksService(webhooksAuthManagers);
}
return this.webhooks;
}

private void checkCredentials() throws ApiAuthException {
if (null == clientAuthManagers || clientAuthManagers.isEmpty()) {
throw new ApiAuthException(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.sinch.sdk.domains.voice.adapters;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.sinch.sdk.core.exceptions.ApiMappingException;
import com.sinch.sdk.core.http.AuthManager;
import com.sinch.sdk.core.utils.databind.Mapper;
import com.sinch.sdk.domains.voice.adapters.converters.CallsDtoConverter;
import com.sinch.sdk.domains.voice.adapters.converters.WebhooksEventDtoConverter;
import com.sinch.sdk.domains.voice.models.dto.v1.SVAMLRequestBodyDto;
import com.sinch.sdk.domains.voice.models.dto.v1.WebhooksEventDto;
import com.sinch.sdk.domains.voice.models.svaml.SVAMLControl;
import com.sinch.sdk.domains.voice.models.webhooks.WebhooksEvent;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Logger;

public class WebHooksService implements com.sinch.sdk.domains.voice.WebHooksService {
private static final Logger LOGGER = Logger.getLogger(WebHooksService.class.getName());

private final Map<String, AuthManager> authManagers;

public WebHooksService(Map<String, AuthManager> authManagers) {
this.authManagers = authManagers;
}

public boolean validateAuthenticatedRequest(
String method, String path, Map<String, String> headers, String jsonPayload) {

// convert header keys to use case-insensitive map keys
Map<String, String> caseInsensitiveHeaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
caseInsensitiveHeaders.putAll(headers);

String authorizationHeader = caseInsensitiveHeaders.get("Authorization");

// no authorization required
if (null == authorizationHeader) {
return true;
}

String[] split = authorizationHeader.split(" ");
String authorizationKeyword = split.length > 0 ? split[0] : "";

AuthManager authManager = authManagers.get(authorizationKeyword);
if (null == authManager) {
// unknown auth manager
LOGGER.severe(
String.format("Auth manager for authorization '%s' not found", authorizationKeyword));
return false;
}
return authManager.validateAuthenticatedRequest(method, path, headers, jsonPayload);
}

@Override
public WebhooksEvent unserializeWebhooksEvent(String jsonPayload) throws ApiMappingException {
try {
WebhooksEventDto o = Mapper.getInstance().readValue(jsonPayload, WebhooksEventDto.class);
return WebhooksEventDtoConverter.convert(o);
} catch (JsonProcessingException e) {
throw new ApiMappingException(jsonPayload, e);
}
}

@Override
public String serializeWebhooksResponse(SVAMLControl response) throws ApiMappingException {
SVAMLRequestBodyDto dto = CallsDtoConverter.convert(response);
try {
return Mapper.getInstance().writeValueAsString(dto);
} catch (JsonProcessingException e) {
throw new ApiMappingException(response.toString(), e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.sinch.sdk.domains.voice.models.dto.v1.ConferenceCalloutRequestConferenceDtmfOptionsDto;
import com.sinch.sdk.domains.voice.models.dto.v1.ConferenceCalloutRequestDto;
import com.sinch.sdk.domains.voice.models.dto.v1.CustomCalloutRequestDto;
import com.sinch.sdk.domains.voice.models.dto.v1.DomainDto;
import com.sinch.sdk.domains.voice.models.dto.v1.GetCalloutResponseObjDto;
import com.sinch.sdk.domains.voice.models.dto.v1.TtsCalloutRequestDto;
import com.sinch.sdk.domains.voice.models.requests.CalloutRequestParameters;
Expand Down Expand Up @@ -56,7 +57,7 @@ private static CalloutRequestDto convert(CalloutRequestParametersConference clie
client.getEnablePie().ifPresent(dto::setEnablePie);
client.getLocale().ifPresent(dto::setLocale);
client.getGreeting().ifPresent(dto::setGreeting);
client.getMohClass().ifPresent(f -> dto.setMohClass(EnumDynamicConverter.convert(f)));
client.getMusicOnHold().ifPresent(f -> dto.setMohClass(EnumDynamicConverter.convert(f)));
client.getDomain().ifPresent(f -> dto.setDomain(EnumDynamicConverter.convert(f)));

return new CalloutRequestDto()
Expand All @@ -76,7 +77,9 @@ private static CalloutRequestDto convert(CalloutRequestParametersTTS client) {
client.getEnableDice().ifPresent(dto::setEnableDice);
client.getEnablePie().ifPresent(dto::setEnablePie);
client.getLocale().ifPresent(dto::setLocale);
client.getDomain().ifPresent(f -> dto.setDomain(EnumDynamicConverter.convert(f)));
client
.getDomain()
.ifPresent(f -> dto.setDomain(DomainDto.fromValue(EnumDynamicConverter.convert(f))));
client.getText().ifPresent(dto::setText);
client.getPrompts().ifPresent(dto::setPrompts);

Expand All @@ -93,9 +96,9 @@ private static CalloutRequestDto convert(CalloutRequestParametersCustom client)
client.getCustom().ifPresent(dto::setCustom);

client.getMaxDuration().ifPresent(dto::setMaxDuration);
client.getIce().ifPresent(dto::setIce);
client.getAce().ifPresent(dto::setAce);
client.getPie().ifPresent(dto::setPie);
client.getIce().ifPresent(f -> dto.setIce(ControlDtoConverter.convert(f)));
client.getAce().ifPresent(f -> dto.setAce(ControlDtoConverter.convert(f)));
client.getPie().ifPresent(f -> dto.setPie(ControlDtoConverter.convert(f)));

return new CalloutRequestDto().method(MethodEnum.CUSTOMCALLOUT.getValue()).customCallout(dto);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.sinch.sdk.domains.voice.adapters.converters;

import com.sinch.sdk.domains.voice.models.CallReasonType;
import com.sinch.sdk.domains.voice.models.CallResultType;
import com.sinch.sdk.domains.voice.models.DomainType;
import com.sinch.sdk.domains.voice.models.dto.v1.GetCallResponseObjDto;
import com.sinch.sdk.domains.voice.models.dto.v1.SVAMLRequestBodyDto;
import com.sinch.sdk.domains.voice.models.requests.CallsUpdateRequestParameters;
import com.sinch.sdk.domains.voice.models.response.CallInformation;
import com.sinch.sdk.domains.voice.models.response.CallReasonType;
import com.sinch.sdk.domains.voice.models.response.CallResultType;
import com.sinch.sdk.domains.voice.models.response.CallStatusType;
import com.sinch.sdk.domains.voice.models.svaml.SVAMLControl;

public class CallsDtoConverter {

Expand All @@ -22,7 +22,7 @@ public static CallInformation convert(GetCallResponseObjDto dto) {
.setCallId(dto.getCallId())
.setDuration(dto.getDuration())
.setStatus(null != dto.getStatus() ? CallStatusType.from(dto.getStatus()) : null)
.setResult(null != dto.getResult() ? CallResultType.from(dto.getResult()) : null)
.setResult(null != dto.getResult() ? CallResultType.from(dto.getResult().getValue()) : null)
.setReason(null != dto.getReason() ? CallReasonType.from(dto.getReason()) : null)
.setTimeStamp(null != dto.getTimestamp() ? dto.getTimestamp().toInstant() : null)
.setCustom(null != dto.getCustom() ? dto.getCustom() : null)
Expand All @@ -31,15 +31,16 @@ public static CallInformation convert(GetCallResponseObjDto dto) {
.build();
}

public static SVAMLRequestBodyDto convert(CallsUpdateRequestParameters client) {
public static SVAMLRequestBodyDto convert(SVAMLControl client) {
if (null == client) {
return null;
}
SVAMLRequestBodyDto dto = new SVAMLRequestBodyDto();

dto.instructions(SAVMLInstructionDtoConverter.convert(client.getInstructions()));
dto.action(SAVMLActionDtoConverter.convert(client.getAction()));

client
.getInstructions()
.ifPresent(f -> dto.instructions(SVAMLInstructionDtoConverter.convert(f)));
client.getAction().ifPresent(f -> dto.action(SVAMLActionDtoConverter.convert(f)));
return dto;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static ManageConferenceParticipantRequestDto convert(

ManageConferenceParticipantRequestDto dto = new ManageConferenceParticipantRequestDto();
client.getCommand().ifPresent(f -> dto.command(EnumDynamicConverter.convert(f)));
client.getMoh().ifPresent(f -> dto.moh(EnumDynamicConverter.convert(f)));
client.getMusicOnHold().ifPresent(f -> dto.moh(EnumDynamicConverter.convert(f)));
return dto;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.sinch.sdk.domains.voice.adapters.converters;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.sinch.sdk.core.exceptions.ApiMappingException;
import com.sinch.sdk.core.utils.databind.Mapper;
import com.sinch.sdk.domains.voice.models.requests.Control;
import com.sinch.sdk.domains.voice.models.requests.ControlUrl;
import com.sinch.sdk.domains.voice.models.svaml.SVAMLControl;
import java.util.logging.Logger;

public class ControlDtoConverter {

private static final Logger LOGGER = Logger.getLogger(SVAMLActionDtoConverter.class.getName());

public static String convert(Control client) {
if (null == client) {
return null;
}
String dto;
if (client instanceof SVAMLControl) {
SVAMLControl value = (SVAMLControl) client;
dto = convertControlToEscapedJSON(value);
} else if (client instanceof ControlUrl) {
ControlUrl value = (ControlUrl) client;
dto = value.getURL();
} else {
LOGGER.severe(String.format("Unexpected class '%s'", client.getClass()));
dto = client.toString();
}
return dto;
}

private static String convertControlToEscapedJSON(SVAMLControl client) {
try {
return Mapper.getInstance().writeValueAsString(CallsDtoConverter.convert(client));
} catch (JsonProcessingException e) {
throw new ApiMappingException(client.toString(), e);
}
}
}
Loading

0 comments on commit d1fc1d8

Please sign in to comment.