diff --git a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventName.java b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventName.java index 2144b588..19b515ee 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventName.java +++ b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventName.java @@ -21,6 +21,6 @@ public enum CDREventName { sessionCreated, sessionDestroyed, participantJoined, participantLeft, webrtcConnectionCreated, webrtcConnectionDestroyed, recordingStarted, recordingStopped, recordingStatusChanged, filterEventDispatched, - mediaNodeStatusChanged, autoscaling + signalSent, mediaNodeStatusChanged, autoscaling } diff --git a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventSignal.java b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventSignal.java new file mode 100644 index 00000000..9f3d232d --- /dev/null +++ b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventSignal.java @@ -0,0 +1,52 @@ +/* + * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package io.openvidu.server.cdr; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +public class CDREventSignal extends CDREvent { + + private String from; + private String[] to; + private String type; + private String data; + + public CDREventSignal(String sessionId, String from, String[] to, String type, String data) { + super(CDREventName.signalSent, sessionId, System.currentTimeMillis()); + this.from = from; + this.to = to; + this.type = type; + this.data = data; + } + + @Override + public JsonObject toJson() { + JsonObject json = super.toJson(); + json.addProperty("from", this.from); + JsonArray toArray = new JsonArray(); + for (String id : this.to) { + toArray.add(id); + } + json.add("to", toArray); + json.addProperty("type", this.type); + json.addProperty("data", this.data); + return json; + } + +} diff --git a/openvidu-server/src/main/java/io/openvidu/server/cdr/CallDetailRecord.java b/openvidu-server/src/main/java/io/openvidu/server/cdr/CallDetailRecord.java index 9228d8fd..06df7084 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/cdr/CallDetailRecord.java +++ b/openvidu-server/src/main/java/io/openvidu/server/cdr/CallDetailRecord.java @@ -42,52 +42,8 @@ import io.openvidu.server.summary.SessionSummary; import io.openvidu.server.webhook.CDRLoggerWebhook; /** - * CDR logger to register all information of a Session. - * Enabled by property 'OPENVIDU_CDR=true' - * - * - 'sessionCreated': {sessionId, timestamp} - * - 'sessionDestroyed': {sessionId, timestamp, startTime, duration, reason} - * - 'participantJoined': {sessionId, timestamp, participantId, location, platform} - * - 'participantLeft': {sessionId, timestamp, participantId, startTime, duration, reason} - * - 'webrtcConnectionCreated' {sessionId, timestamp, participantId, connection, [receivingFrom], audioEnabled, videoEnabled, [videoSource], [videoFramerate]} - * - 'webrtcConnectionDestroyed' {sessionId, timestamp, participantId, startTime, duration, connection, [receivingFrom], audioEnabled, videoEnabled, [videoSource], [videoFramerate], reason} - * - 'recordingStarted' {sessionId, timestamp, id, name, hasAudio, hasVideo, resolution, recordingLayout, size} - * - 'recordingStopped' {sessionId, timestamp, id, name, hasAudio, hasVideo, resolution, recordingLayout, size} - * - 'recordingStatusChanged' {sessionId, timestamp, id, name, hasAudio, hasVideo, resolution, recordingLayout, size, status} - * - 'filterEventDispatched' {sessionId, timestamp, participantId, streamId, filterType, eventType, data} - * - * PROPERTIES VALUES: - * - * - sessionId: string - * - timestamp: number - * - startTime: number - * - duration: number - * - participantId: string - * - connection: "INBOUND", "OUTBOUND" - * - receivingFrom: string - * - audioEnabled: boolean - * - videoEnabled: boolean - * - videoSource: "CAMERA", "SCREEN", "CUSTOM", "IPCAM" - * - videoFramerate: number - * - videoDimensions: string - * - id: string - * - name: string - * - hasAudio: boolean - * - hasVideo: boolean - * - resolution string - * - recordingLayout: string - * - size: number - * - status: string - * - webrtcConnectionDestroyed.reason: "unsubscribe", "unpublish", "disconnect", "networkDisconnect", "mediaServerDisconnect", "openviduServerStopped" - * - participantLeft.reason: "unsubscribe", "unpublish", "disconnect", "networkDisconnect", "mediaServerDisconnect", "openviduServerStopped" - * - sessionDestroyed.reason: "lastParticipantLeft", "mediaServerDisconnect", "openviduServerStopped" - * - recordingStopped.reason: "recordingStoppedByServer", "lastParticipantLeft", "sessionClosedByServer", "automaticStop", "mediaServerDisconnect", "openviduServerStopped" - * - * [OPTIONAL_PROPERTIES]: - * - receivingFrom: only if connection = "INBOUND" - * - videoSource: only if videoEnabled = true - * - videoFramerate: only if videoEnabled = true - * - videoDimensions: only if videoEnabled = true + * CDR logger to register all information of a Session. Enabled by property + * 'OPENVIDU_CDR=true' * * @author Pablo Fuente (pablofuenteperez@gmail.com) */ @@ -228,6 +184,13 @@ public class CallDetailRecord { this.log(new CDREventFilterEvent(sessionId, participantId, streamId, filterType, event)); } + public void recordSignalSent(String sessionId, String from, String[] to, String type, String data) { + if (from != null) { + type = type.replaceFirst("^signal:", ""); + } + this.log(new CDREventSignal(sessionId, from, to, type, data)); + } + protected void log(CDREvent event) { this.loggers.forEach(logger -> { diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java index 70f1fa6d..989df39c 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java @@ -321,7 +321,7 @@ public class SessionEventsHandler { } public void onSendMessage(Participant participant, JsonObject message, Set participants, - Integer transactionId, OpenViduException error) { + String sessionId, Integer transactionId, OpenViduException error) { boolean isRpcCall = transactionId != null; if (isRpcCall) { @@ -332,16 +332,22 @@ public class SessionEventsHandler { } } + String from = null; + String type = null; + String data = null; + JsonObject params = new JsonObject(); if (message.has("data")) { - params.addProperty(ProtocolElements.PARTICIPANTSENDMESSAGE_DATA_PARAM, message.get("data").getAsString()); + data = message.get("data").getAsString(); + params.addProperty(ProtocolElements.PARTICIPANTSENDMESSAGE_DATA_PARAM, data); } if (message.has("type")) { - params.addProperty(ProtocolElements.PARTICIPANTSENDMESSAGE_TYPE_PARAM, message.get("type").getAsString()); + type = message.get("type").getAsString(); + params.addProperty(ProtocolElements.PARTICIPANTSENDMESSAGE_TYPE_PARAM, type); } if (participant != null) { - params.addProperty(ProtocolElements.PARTICIPANTSENDMESSAGE_FROM_PARAM, - participant.getParticipantPublicId()); + from = participant.getParticipantPublicId(); + params.addProperty(ProtocolElements.PARTICIPANTSENDMESSAGE_FROM_PARAM, from); } Set toSet = new HashSet(); @@ -360,6 +366,7 @@ public class SessionEventsHandler { if (toSet.isEmpty()) { for (Participant p : participants) { + toSet.add(p.getParticipantPublicId()); rpcNotificationService.sendNotification(p.getParticipantPrivateId(), ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD, params); } @@ -382,6 +389,8 @@ public class SessionEventsHandler { if (isRpcCall) { rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject()); } + + CDR.recordSignalSent(sessionId, from, toSet.toArray(new String[toSet.size()]), type, data); } public void onStreamPropertyChanged(Participant participant, Integer transactionId, Set participants, diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java index 0bcc6155..7e066dbc 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java @@ -116,7 +116,7 @@ public abstract class SessionManager { public void sendMessage(String message, String sessionId) { try { JsonObject messageJson = JsonParser.parseString(message).getAsJsonObject(); - sessionEventsHandler.onSendMessage(null, messageJson, getParticipants(sessionId), null, null); + sessionEventsHandler.onSendMessage(null, messageJson, getParticipants(sessionId), sessionId, null, null); } catch (JsonSyntaxException | IllegalStateException e) { throw new OpenViduException(Code.SIGNAL_FORMAT_INVALID_ERROR_CODE, "Provided signal object '" + message + "' has not a valid JSON format"); @@ -127,7 +127,7 @@ public abstract class SessionManager { try { JsonObject messageJson = JsonParser.parseString(message).getAsJsonObject(); sessionEventsHandler.onSendMessage(participant, messageJson, getParticipants(participant.getSessionId()), - transactionId, null); + participant.getSessionId(), transactionId, null); } catch (JsonSyntaxException | IllegalStateException e) { throw new OpenViduException(Code.SIGNAL_FORMAT_INVALID_ERROR_CODE, "Provided signal object '" + message + "' has not a valid JSON format"); diff --git a/openvidu-server/src/main/resources/application.properties b/openvidu-server/src/main/resources/application.properties index 45a69b5c..917f6324 100644 --- a/openvidu-server/src/main/resources/application.properties +++ b/openvidu-server/src/main/resources/application.properties @@ -24,7 +24,7 @@ OPENVIDU_CDR_PATH=/opt/openvidu/cdr OPENVIDU_WEBHOOK=false OPENVIDU_WEBHOOK_ENDPOINT= OPENVIDU_WEBHOOK_HEADERS=[] -OPENVIDU_WEBHOOK_EVENTS=["sessionCreated","sessionDestroyed","participantJoined","participantLeft","webrtcConnectionCreated","webrtcConnectionDestroyed","recordingStatusChanged","filterEventDispatched","mediaNodeStatusChanged","autoscaling"] +OPENVIDU_WEBHOOK_EVENTS=["sessionCreated","sessionDestroyed","participantJoined","participantLeft","webrtcConnectionCreated","webrtcConnectionDestroyed","recordingStatusChanged","filterEventDispatched","signalSent","mediaNodeStatusChanged","autoscaling"] OPENVIDU_RECORDING=false OPENVIDU_RECORDING_DEBUG=false diff --git a/openvidu-test-e2e/jenkins/Jenkinsfile b/openvidu-test-e2e/jenkins/Jenkinsfile index d0c9b071..e722c6e6 100644 --- a/openvidu-test-e2e/jenkins/Jenkinsfile +++ b/openvidu-test-e2e/jenkins/Jenkinsfile @@ -1,9 +1,11 @@ node('container') { + sh 'docker rm -f e2e chrome firefox opera || true' sh 'rm -rf /opt/openvidu/* || true' sh 'wget https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/barcode.y4m -P /opt/openvidu' sh 'wget https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/fakeaudio.wav -P /opt/openvidu' sh 'wget --directory-prefix=/opt/openvidu/test-layouts/layout1 https://raw.githubusercontent.com/OpenVidu/openvidu/master/openvidu-test-e2e/docker/my-custom-layout/index.html' + docker.image('openvidu/openvidu-test-e2e:$DISTRO').pull() docker.image('selenium/standalone-chrome:latest').pull() docker.image('selenium/standalone-firefox:latest').pull() diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java index 066a94ee..00e5b417 100644 --- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java +++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java @@ -48,6 +48,7 @@ import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.ExpectedConditions; import org.springframework.test.context.junit.jupiter.SpringExtension; +import com.google.gson.JsonArray; import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.google.gson.JsonParser; @@ -3217,6 +3218,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { event = CustomWebhook.waitForEvent("webrtcConnectionCreated", 2); Assert.assertEquals("Wrong number of properties in event 'webrtcConnectionCreated'", 10 + 1, event.keySet().size()); + String connectionId1 = event.get("participantId").getAsString(); event = CustomWebhook.waitForEvent("recordingStatusChanged", 10); Assert.assertEquals("Wrong number of properties in event 'recordingStatusChanged'", 11 + 1, @@ -3258,11 +3260,53 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { user.getDriver().findElement(By.id("add-user-btn")).click(); user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .join-btn")).click(); - CustomWebhook.waitForEvent("participantJoined", 2); + event = CustomWebhook.waitForEvent("participantJoined", 2); CustomWebhook.waitForEvent("webrtcConnectionCreated", 2); CustomWebhook.waitForEvent("webrtcConnectionCreated", 2); CustomWebhook.waitForEvent("webrtcConnectionCreated", 2); + String connectionId2 = event.get("participantId").getAsString(); + + // signalSent from client + long timestamp = System.currentTimeMillis(); + user.getDriver().findElement(By.cssSelector(("#openvidu-instance-0 .message-btn"))).click(); + user.getEventManager().waitUntilEventReaches("signal:chat", 2); + event = CustomWebhook.waitForEvent("signalSent", 1); + Assert.assertEquals("Wrong number of properties in event 'signalSent'", 6 + 1, event.keySet().size()); + Assert.assertEquals("Wrong sessionId in webhook event", "TestSession", + event.get("sessionId").getAsString()); + Assert.assertTrue("Wrong timestamp in webhook event", event.get("timestamp").getAsLong() > timestamp); + Assert.assertEquals("Wrong from in webhook event", connectionId1, event.get("from").getAsString()); + Assert.assertEquals("Wrong type in webhook event", "chat", event.get("type").getAsString()); + Assert.assertTrue("Wrong data in webhook event", !event.get("data").getAsString().isEmpty()); + Assert.assertEquals("Wrong event name in webhook event", "signalSent", event.get("event").getAsString()); + JsonArray toArray = event.get("to").getAsJsonArray(); + Assert.assertEquals("Wrong to array size", 2, toArray.size()); + Assert.assertTrue("Wrong to array content in webhook event", + toArray.contains(JsonParser.parseString(connectionId1))); + Assert.assertTrue("Wrong to array content in webhook event", + toArray.contains(JsonParser.parseString(connectionId2))); + + // signalSent from server + CustomHttpClient restClient = new CustomHttpClient(OPENVIDU_URL, "OPENVIDUAPP", OPENVIDU_SECRET); + restClient.rest(HttpMethod.POST, "/openvidu/api/signal", + "{'session':'TestSession','type':'chat','to':['" + connectionId1 + "'],'data':'SERVER_DATA'}", + HttpStatus.SC_OK); + user.getEventManager().waitUntilEventReaches("signal:chat", 3); + event = CustomWebhook.waitForEvent("signalSent", 1); + Assert.assertEquals("Wrong number of properties in event 'signalSent'", 6 + 1, event.keySet().size()); + Assert.assertEquals("Wrong sessionId in webhook event", "TestSession", + event.get("sessionId").getAsString()); + Assert.assertTrue("Wrong timestamp in webhook event", event.get("timestamp").getAsLong() > timestamp); + Assert.assertTrue("Wrong from in webhook event", event.get("from").isJsonNull()); + Assert.assertEquals("Wrong type in webhook event", "chat", event.get("type").getAsString()); + Assert.assertEquals("Wrong data in webhook event", "SERVER_DATA", event.get("data").getAsString()); + Assert.assertEquals("Wrong event name in webhook event", "signalSent", event.get("event").getAsString()); + toArray = event.get("to").getAsJsonArray(); + Assert.assertEquals("Wrong to array size", 1, toArray.size()); + Assert.assertTrue("Wrong to array content in webhook event", + toArray.contains(JsonParser.parseString(connectionId1))); + user.getDriver().findElement(By.id("session-api-btn-0")).click(); Thread.sleep(1000); user.getDriver().findElement(By.id("close-session-btn")).click();