diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java index c962c03a..c8a7159b 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java @@ -20,6 +20,10 @@ package io.openvidu.java.client; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; /** * See {@link io.openvidu.java.client.Session#getActiveConnections()} @@ -28,35 +32,52 @@ public class Connection { private String connectionId; private long createdAt; - private OpenViduRole role; - private String token; private String location; private String platform; - private String serverData; private String clientData; + private Token token; - protected Map publishers; - protected List subscribers; + protected Map publishers = new ConcurrentHashMap<>(); + protected List subscribers = new ArrayList<>(); - protected Connection(String connectionId, long createdAt, OpenViduRole role, String token, String location, - String platform, String serverData, String clientData, Map publishers, - List subscribers) { - this.connectionId = connectionId; - this.createdAt = createdAt; - this.role = role; - this.token = token; - this.location = location; - this.platform = platform; - this.serverData = serverData; - this.clientData = clientData; - this.publishers = publishers; - this.subscribers = subscribers; + protected Connection(JsonObject json) { + JsonArray jsonArrayPublishers = json.get("publishers").getAsJsonArray(); + jsonArrayPublishers.forEach(publisher -> { + JsonObject pubJson = publisher.getAsJsonObject(); + JsonObject mediaOptions = pubJson.get("mediaOptions").getAsJsonObject(); + Publisher pub = new Publisher(pubJson.get("streamId").getAsString(), pubJson.get("createdAt").getAsLong(), + mediaOptions.get("hasAudio").getAsBoolean(), mediaOptions.get("hasVideo").getAsBoolean(), + mediaOptions.get("audioActive"), mediaOptions.get("videoActive"), mediaOptions.get("frameRate"), + mediaOptions.get("typeOfVideo"), mediaOptions.get("videoDimensions")); + this.publishers.put(pub.getStreamId(), pub); + }); + + JsonArray jsonArraySubscribers = json.get("subscribers").getAsJsonArray(); + jsonArraySubscribers.forEach(subscriber -> { + this.subscribers.add((subscriber.getAsJsonObject()).get("streamId").getAsString()); + }); + + this.connectionId = json.get("connectionId").getAsString(); + this.createdAt = json.get("createdAt").getAsLong(); + + this.location = json.get("location").getAsString(); + this.platform = json.get("platform").getAsString(); + this.clientData = json.get("clientData").getAsString(); + + String token = json.has("token") ? json.get("token").getAsString() : null; + OpenViduRole role = OpenViduRole.valueOf(json.get("role").getAsString()); + String data = json.get("serverData").getAsString(); + Boolean record = json.get("record").getAsBoolean(); + + TokenOptions tokenOptions = new TokenOptions(role, data, record, null); + this.token = new Token(token, this.connectionId, tokenOptions); } /** - * Returns the identifier of the connection. You can call - * {@link io.openvidu.java.client.Session#forceDisconnect(String)} passing this - * property as parameter + * Returns the identifier of the connection. You can call methods + * {@link io.openvidu.java.client.Session#forceDisconnect(String)} or + * {@link io.openvidu.java.client.Session#updateConnection(String, TokenOptions)} + * passing this property as parameter */ public String getConnectionId() { return connectionId; @@ -74,21 +95,41 @@ public class Connection { * Returns the role of the connection */ public OpenViduRole getRole() { - return role; + return this.token.getRole(); } /** - * Returns the token associated to the connection + * Returns the data associated to the connection on the server-side. This value + * is set with {@link io.openvidu.java.client.TokenOptions.Builder#data(String)} + * when calling {@link io.openvidu.java.client.Session#generateToken()} + */ + public String getServerData() { + return this.token.getData(); + } + + /** + * Whether the streams published by this Connection will be recorded or not. + * This only affects INDIVIDUAL recording. + */ + public boolean record() { + return this.token.record(); + } + + /** + * Returns the token string associated to the connection */ public String getToken() { - return token; + return this.token.getToken(); } /** - * PRO + * PRO * * Returns the geo location of the connection, with the following format: * "CITY, COUNTRY" ("unknown" if it wasn't possible to @@ -106,20 +147,11 @@ public class Connection { return platform; } - /** - * Returns the data associated to the connection on the server-side. This value - * is set with {@link io.openvidu.java.client.TokenOptions.Builder#data(String)} - * when calling {@link io.openvidu.java.client.Session#generateToken()} - */ - public String getServerData() { - return serverData; - } - /** * Returns the data associated to the connection on the client-side. This value - * is set with second parameter of method - * Session.connect in OpenVidu Browser + * is set with second parameter of method Session.connect in OpenVidu Browser */ public String getClientData() { return clientData; @@ -147,6 +179,16 @@ public class Connection { return this.subscribers; } + /** + * For now only properties data, role and record can be updated + */ + protected void overrideTokenOptions(TokenOptions tokenOptions) { + TokenOptions.Builder modifiableTokenOptions = new TokenOptions.Builder().role(tokenOptions.getRole()) + .record(tokenOptions.record()); + modifiableTokenOptions.data(this.token.getData()); + this.token.overrideTokenOptions(modifiableTokenOptions.build()); + } + protected void setSubscribers(List subscribers) { this.subscribers = subscribers; } diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java index c2e3bcc4..dc39bc28 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java @@ -29,6 +29,7 @@ import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPatch; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.util.EntityUtils; @@ -70,7 +71,7 @@ public class Session { } /** - * Gets the unique identifier of the Session + * Gets the unique identifier of the Session. * * @return The sessionId */ @@ -80,78 +81,112 @@ public class Session { /** * Timestamp when this session was created, in UTC milliseconds (ms since Jan 1, - * 1970, 00:00:00 UTC) + * 1970, 00:00:00 UTC). */ public long createdAt() { return this.createdAt; } /** - * Gets a new token associated to Session object with default values for - * {@link io.openvidu.java.client.TokenOptions}. This always translates into a - * new request to OpenVidu Server + * @deprecated Use {@link Session#createToken() Session.createToken()} instead + * to get a {@link io.openvidu.java.client.Token} object. * - * @return The generated token + * @return The generated token String * * @throws OpenViduJavaClientException * @throws OpenViduHttpException */ + @Deprecated public String generateToken() throws OpenViduJavaClientException, OpenViduHttpException { - return this.generateToken(new TokenOptions.Builder().role(OpenViduRole.PUBLISHER).build()); + return createToken().getToken(); } /** - * Gets a new token associated to Session object configured with - * tokenOptions. This always translates into a new request to - * OpenVidu Server + * @deprecated Use + * {@link Session#createToken(io.openvidu.java.client.TokenOptions) + * Session.createToken(TokenOptions)} instead to get a + * {@link io.openvidu.java.client.Token} object. * - * @return The generated token + * @return The generated token String * * @throws OpenViduJavaClientException * @throws OpenViduHttpException */ + @Deprecated public String generateToken(TokenOptions tokenOptions) throws OpenViduJavaClientException, OpenViduHttpException { + return createToken(tokenOptions).getToken(); + } + /** + * Gets a new token object associated to Session object with default values for + * {@link io.openvidu.java.client.TokenOptions}. The token string value to send + * to the client side can be retrieved with + * {@link io.openvidu.java.client.Token#getToken() Token.getToken()}.
+ *
+ * You can use method {@link io.openvidu.java.client.Token#getConnectionId() + * Token.getConnectionId()} to get the connection identifier that will be given + * to the user consuming the token. With connectionId you can call + * the following methods without having to fetch and search for the actual + * {@link io.openvidu.java.client.Connection Connection} object: + *
    + *
  • Call {@link io.openvidu.java.client.Session#forceDisconnect(String) + * Session.forceDisconnect()} to invalidate the token if no client has used it + * yet or force the connected client to leave the session if it has.
  • + *
  • Call + * {@link io.openvidu.java.client.Session#updateConnection(String, TokenOptions) + * Session.updateConnection()} to update the + * {@link io.openvidu.java.client.Connection Connection} options. And this is + * valid for unused tokens, but also for already used tokens, so you can + * dynamically change the connection options on the fly.
  • + *
+ * + * @return The generated {@link io.openvidu.java.client.Token Token} object. + * + * @throws OpenViduJavaClientException + * @throws OpenViduHttpException + */ + public Token createToken() throws OpenViduJavaClientException, OpenViduHttpException { + return createToken(new TokenOptions.Builder().data("").role(OpenViduRole.PUBLISHER).record(true).build()); + } + + /** + * Gets a new token object associated to Session object configured with + * tokenOptions. The token string value to send to the client side + * can be retrieved with {@link io.openvidu.java.client.Token#getToken() + * Token.getToken()}.
+ *
+ * You can use method {@link io.openvidu.java.client.Token#getConnectionId() + * Token.getConnectionId()} to get the connection identifier that will be given + * to the user consuming the token. With connectionId you can call + * the following methods without having to fetch and search for the actual + * {@link io.openvidu.java.client.Connection Connection} object: + *
    + *
  • Call {@link io.openvidu.java.client.Session#forceDisconnect(String) + * Session.forceDisconnect()} to invalidate the token if no client has used it + * yet or force the connected client to leave the session if it has.
  • + *
  • Call + * {@link io.openvidu.java.client.Session#updateConnection(String, TokenOptions) + * Session.updateConnection()} to update the + * {@link io.openvidu.java.client.Connection Connection} options. And this is + * valid for unused tokens, but also for already used tokens, so you can + * dynamically change the user connection options on the fly.
  • + *
+ * + * @return The generated {@link io.openvidu.java.client.Token Token} object. + * + * @throws OpenViduJavaClientException + * @throws OpenViduHttpException + */ + public Token createToken(TokenOptions tokenOptions) throws OpenViduJavaClientException, OpenViduHttpException { if (!this.hasSessionId()) { this.getSessionId(); } HttpPost request = new HttpPost(this.openVidu.hostname + OpenVidu.API_TOKENS); - JsonObject json = new JsonObject(); - json.addProperty("session", this.sessionId); - json.addProperty("role", tokenOptions.getRole().name()); - json.addProperty("data", tokenOptions.getData()); - if (tokenOptions.getKurentoOptions() != null) { - JsonObject kurentoOptions = new JsonObject(); - if (tokenOptions.getKurentoOptions().getVideoMaxRecvBandwidth() != null) { - kurentoOptions.addProperty("videoMaxRecvBandwidth", - tokenOptions.getKurentoOptions().getVideoMaxRecvBandwidth()); - } - if (tokenOptions.getKurentoOptions().getVideoMinRecvBandwidth() != null) { - kurentoOptions.addProperty("videoMinRecvBandwidth", - tokenOptions.getKurentoOptions().getVideoMinRecvBandwidth()); - } - if (tokenOptions.getKurentoOptions().getVideoMaxSendBandwidth() != null) { - kurentoOptions.addProperty("videoMaxSendBandwidth", - tokenOptions.getKurentoOptions().getVideoMaxSendBandwidth()); - } - if (tokenOptions.getKurentoOptions().getVideoMinSendBandwidth() != null) { - kurentoOptions.addProperty("videoMinSendBandwidth", - tokenOptions.getKurentoOptions().getVideoMinSendBandwidth()); - } - if (tokenOptions.getKurentoOptions().getAllowedFilters().length > 0) { - JsonArray allowedFilters = new JsonArray(); - for (String filter : tokenOptions.getKurentoOptions().getAllowedFilters()) { - allowedFilters.add(filter); - } - kurentoOptions.add("allowedFilters", allowedFilters); - } - json.add("kurentoOptions", kurentoOptions); - } StringEntity params; try { - params = new StringEntity(json.toString()); + params = new StringEntity(tokenOptions.toJsonObject(sessionId).toString()); } catch (UnsupportedEncodingException e1) { throw new OpenViduJavaClientException(e1.getMessage(), e1.getCause()); } @@ -169,8 +204,8 @@ public class Session { try { int statusCode = response.getStatusLine().getStatusCode(); if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { - String token = httpResponseToJson(response).get("id").getAsString(); - log.info("Returning a TOKEN: {}", token); + Token token = new Token(httpResponseToJson(response)); + log.info("Returning a TOKEN: {}", token.getToken()); return token; } else { throw new OpenViduHttpException(statusCode); @@ -182,7 +217,7 @@ public class Session { /** * Gracefully closes the Session: unpublishes all streams and evicts every - * participant + * participant. * * @throws OpenViduJavaClientException * @throws OpenViduHttpException @@ -217,15 +252,17 @@ public class Session { * connections to the Session * ({@link io.openvidu.java.client.Session#getActiveConnections()}) and use * those values to call - * {@link io.openvidu.java.client.Session#forceDisconnect(Connection)} or - * {@link io.openvidu.java.client.Session#forceUnpublish(Publisher)}.
+ * {@link io.openvidu.java.client.Session#forceDisconnect(Connection)}, + * {@link io.openvidu.java.client.Session#forceUnpublish(Publisher)} or + * {@link io.openvidu.java.client.Session#updateConnection(String, TokenOptions)}.
+ *
* - * To update every Session object owned by OpenVidu object, call - * {@link io.openvidu.java.client.OpenVidu#fetch()} + * To update all Session objects owned by OpenVidu object at once, call + * {@link io.openvidu.java.client.OpenVidu#fetch()}. * * @return true if the Session status has changed with respect to the server, * false if not. This applies to any property or sub-property of the - * object + * object. * * @throws OpenViduHttpException * @throws OpenViduJavaClientException @@ -263,11 +300,19 @@ public class Session { * OpenVidu Browser will trigger the proper events on the client-side * (streamDestroyed, connectionDestroyed, * sessionDisconnected) with reason set to - * "forceDisconnectByServer"
+ * "forceDisconnectByServer".
+ *
* * You can get connection parameter with * {@link io.openvidu.java.client.Session#fetch()} and then - * {@link io.openvidu.java.client.Session#getActiveConnections()} + * {@link io.openvidu.java.client.Session#getActiveConnections()}.
+ *
+ * + * This method automatically updates the properties of the local affected + * objects. This means that there is no need to call + * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} to see the + * changes consequence of the execution of this method applied in the local + * objects. * * @throws OpenViduJavaClientException * @throws OpenViduHttpException @@ -278,15 +323,34 @@ public class Session { /** * Forces the user with Connection connectionId to leave the - * session. OpenVidu Browser will trigger the proper events on the client-side + * session, or invalidates the {@link Token} associated with that + * connectionId if no user has used it yet.
+ *
+ * + * In the first case you can get connectionId parameter from + * {@link io.openvidu.java.client.Connection#getConnectionId() + * Connection.getConnectionId()}. Connection objects can be listed with + * {@link io.openvidu.java.client.Session#getActiveConnections() + * Session.getActiveConnections()} (remember to use first + * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} to fetch the + * current active connections from OpenVidu Server). As a result, OpenVidu + * Browser will trigger the proper events on the client-side * (streamDestroyed, connectionDestroyed, * sessionDisconnected) with reason set to - * "forceDisconnectByServer"
+ * "forceDisconnectByServer".
+ *
* - * You can get connectionId parameter with - * {@link io.openvidu.java.client.Session#fetch()} (use - * {@link io.openvidu.java.client.Connection#getConnectionId()} to get the - * `connectionId` you want) + * In the second case you can get connectionId parameter from a + * {@link Token} with {@link Token#getConnectionId()}. As a result, the token + * will be invalidated and no user will be able to connect to the session with + * it.
+ *
+ * + * This method automatically updates the properties of the local affected + * objects. This means that there is no need to call + * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} to see the + * changes consequence of the execution of this method applied in the local + * objects. * * @throws OpenViduJavaClientException * @throws OpenViduHttpException @@ -336,14 +400,22 @@ public class Session { /** * Forces some user to unpublish a Stream. OpenVidu Browser will trigger the * proper events on the client-side (streamDestroyed) with reason - * set to "forceUnpublishByServer".
+ * set to "forceUnpublishByServer".
+ *
* * You can get publisher parameter with * {@link io.openvidu.java.client.Session#getActiveConnections()} and then for * each Connection you can call * {@link io.openvidu.java.client.Connection#getPublishers()}. Remember to call * {@link io.openvidu.java.client.Session#fetch()} before to fetch the current - * actual properties of the Session from OpenVidu Server + * actual properties of the Session from OpenVidu Server.
+ *
+ * + * This method automatically updates the properties of the local affected + * objects. This means that there is no need to call + * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} to see the + * changes consequence of the execution of this method applied in the local + * objects. * * @throws OpenViduJavaClientException * @throws OpenViduHttpException @@ -355,7 +427,8 @@ public class Session { /** * Forces some user to unpublish a Stream. OpenVidu Browser will trigger the * proper events on the client-side (streamDestroyed) with reason - * set to "forceUnpublishByServer".
+ * set to "forceUnpublishByServer".
+ *
* * You can get streamId parameter with * {@link io.openvidu.java.client.Session#getActiveConnections()} and then for @@ -364,7 +437,14 @@ public class Session { * {@link io.openvidu.java.client.Publisher#getStreamId()}) will give you the * streamId. Remember to call * {@link io.openvidu.java.client.Session#fetch()} before to fetch the current - * actual properties of the Session from OpenVidu Server + * actual properties of the Session from OpenVidu Server.
+ *
+ * + * This method automatically updates the properties of the local affected + * objects. This means that there is no need to call + * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} to see the + * changes consequence of the execution of this method applied in the local + * objects. * * @throws OpenViduJavaClientException * @throws OpenViduHttpException @@ -401,6 +481,94 @@ public class Session { } } + /** + * Updates the properties of a Connection. These properties are the ones defined + * by the {@link io.openvidu.java.client.TokenOptions} parameter when generating + * the token used to create the Connection. These are the properties that can be + * updated: + *
    + *
  • {@link io.openvidu.java.client.TokenOptions.Builder#role(OpenViduRole) + * TokenOptions.Builder.role(OpenViduRole)}
  • + *
  • {@link io.openvidu.java.client.TokenOptions.Builder#record(boolean) + * TokenOptions.Builder.record(boolean)}
  • + *
+ *
+ * + * The connectionId parameter can be obtained from a Connection + * object with {@link io.openvidu.java.client.Connection#getConnectionId() + * Connection.getConnectionId()}, in which case the updated properties will + * modify an active Connection. But connectionId can also be + * obtained from a Token with {@link Token#getConnectionId()}, which allows + * modifying a still not used token.
+ *
+ * + * This method automatically updates the properties of the local affected + * objects. This means that there is no need to call + * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} to see the + * changes consequence of the execution of this method applied in the local + * objects. + * + * @param connectionId The Connection (or a still not used Token) to modify + * @param tokenOptions A new TokenOptions object with the updated values to + * apply + * + * @return The updated {@link io.openvidu.java.client.Connection Connection} + * object + * + * @throws OpenViduJavaClientException + * @throws OpenViduHttpException + */ + public Connection updateConnection(String connectionId, TokenOptions tokenOptions) + throws OpenViduJavaClientException, OpenViduHttpException { + + HttpPatch request = new HttpPatch( + this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection/" + connectionId); + + StringEntity params; + try { + params = new StringEntity(tokenOptions.toJsonObject(this.sessionId).toString()); + } catch (UnsupportedEncodingException e1) { + throw new OpenViduJavaClientException(e1.getMessage(), e1.getCause()); + } + request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json"); + request.setEntity(params); + + HttpResponse response; + try { + response = this.openVidu.httpClient.execute(request); + } catch (IOException e) { + throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); + } + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { + log.info("Connection {} updated", connectionId); + } else if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) { + log.info("Properties of Connection {} remain the same", connectionId); + } else { + throw new OpenViduHttpException(statusCode); + } + + // Update the actual Connection object with the new options + Connection existingConnection = this.activeConnections.get(connectionId); + + if (existingConnection == null) { + // The updated Connection is not available in local map + Connection newConnection = new Connection(httpResponseToJson(response)); + this.activeConnections.put(connectionId, newConnection); + return newConnection; + } else { + // The updated Connection was available in local map + existingConnection.overrideTokenOptions(tokenOptions); + return existingConnection; + } + + } finally { + EntityUtils.consumeQuietly(response.getEntity()); + } + } + /** * Returns the list of active connections to the session. This value * will remain unchanged since the last time method @@ -408,38 +576,37 @@ public class Session { * Exceptions to this rule are: *
    *
  • Calling {@link io.openvidu.java.client.Session#forceUnpublish(String)} - * updates each affected Connection status
  • + * automatically updates each affected local Connection object. *
  • Calling {@link io.openvidu.java.client.Session#forceDisconnect(String)} - * updates each affected Connection status
  • + * automatically updates each affected local Connection object. + *
  • Calling + * {@link io.openvidu.java.client.Session#updateConnection(String, TokenOptions)} + * automatically updates the attributes of the affected local Connection + * object.
  • *
*
* To get the list of active connections with their current actual value, you * must call first {@link io.openvidu.java.client.Session#fetch()} and then - * {@link io.openvidu.java.client.Session#getActiveConnections()} + * {@link io.openvidu.java.client.Session#getActiveConnections()}. */ public List getActiveConnections() { return new ArrayList<>(this.activeConnections.values()); } /** - * Returns whether the session is being recorded or not + * Returns whether the session is being recorded or not. */ public boolean isBeingRecorded() { return this.recording; } /** - * Returns the properties defining the session + * Returns the properties defining the session. */ public SessionProperties getProperties() { return this.properties; } - @Override - public String toString() { - return this.sessionId; - } - private boolean hasSessionId() { return (this.sessionId != null && !this.sessionId.isEmpty()); } @@ -528,35 +695,9 @@ public class Session { this.properties = builder.build(); JsonArray jsonArrayConnections = (json.get("connections").getAsJsonObject()).get("content").getAsJsonArray(); this.activeConnections.clear(); - jsonArrayConnections.forEach(connection -> { - JsonObject con = connection.getAsJsonObject(); - - Map publishers = new ConcurrentHashMap<>(); - JsonArray jsonArrayPublishers = con.get("publishers").getAsJsonArray(); - jsonArrayPublishers.forEach(publisher -> { - JsonObject pubJson = publisher.getAsJsonObject(); - JsonObject mediaOptions = pubJson.get("mediaOptions").getAsJsonObject(); - Publisher pub = new Publisher(pubJson.get("streamId").getAsString(), - pubJson.get("createdAt").getAsLong(), mediaOptions.get("hasAudio").getAsBoolean(), - mediaOptions.get("hasVideo").getAsBoolean(), mediaOptions.get("audioActive"), - mediaOptions.get("videoActive"), mediaOptions.get("frameRate"), mediaOptions.get("typeOfVideo"), - mediaOptions.get("videoDimensions")); - publishers.put(pub.getStreamId(), pub); - }); - - List subscribers = new ArrayList<>(); - JsonArray jsonArraySubscribers = con.get("subscribers").getAsJsonArray(); - jsonArraySubscribers.forEach(subscriber -> { - subscribers.add((subscriber.getAsJsonObject()).get("streamId").getAsString()); - }); - - Connection c = new Connection(con.get("connectionId").getAsString(), con.get("createdAt").getAsLong(), - OpenViduRole.valueOf(con.get("role").getAsString()), - (con.has("token") ? con.get("token").getAsString() : null), con.get("location").getAsString(), - con.get("platform").getAsString(), con.get("serverData").getAsString(), - con.get("clientData").getAsString(), publishers, subscribers); - - this.activeConnections.put(con.get("connectionId").getAsString(), c); + jsonArrayConnections.forEach(connectionJsonElement -> { + Connection connectionObj = new Connection(connectionJsonElement.getAsJsonObject()); + this.activeConnections.put(connectionObj.getConnectionId(), connectionObj); }); return this; } diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Token.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Token.java new file mode 100644 index 00000000..b629d323 --- /dev/null +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Token.java @@ -0,0 +1,127 @@ +package io.openvidu.java.client; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +public class Token { + + private String token; + private String connectionId; + private TokenOptions tokenOptions; + + protected Token(String token, String connectionId, TokenOptions tokenOptions) { + this.token = token; + this.connectionId = connectionId; + this.tokenOptions = tokenOptions; + } + + protected Token(JsonObject json) { + + this.token = json.get("token").getAsString(); + this.connectionId = json.get("connectionId").getAsString(); + + OpenViduRole role = OpenViduRole.valueOf(json.get("role").getAsString()); + String data = json.get("data").getAsString(); + Boolean record = json.get("record").getAsBoolean(); + + KurentoOptions kurentoOptions = null; + if (json.has("kurentoOptions")) { + JsonObject kurentoOptionsJson = json.get("kurentoOptions").getAsJsonObject(); + + Integer videoMaxRecvBandwidth = null; + Integer videoMinRecvBandwidth = null; + Integer videoMaxSendBandwidth = null; + Integer videoMinSendBandwidth = null; + String[] allowedFilters = null; + + if (kurentoOptionsJson.has("videoMaxRecvBandwidth")) { + videoMaxRecvBandwidth = kurentoOptionsJson.get("videoMaxRecvBandwidth").getAsInt(); + } + if (kurentoOptionsJson.has("videoMinRecvBandwidth")) { + videoMinRecvBandwidth = kurentoOptionsJson.get("videoMinRecvBandwidth").getAsInt(); + } + if (kurentoOptionsJson.has("videoMaxSendBandwidth")) { + videoMaxSendBandwidth = kurentoOptionsJson.get("videoMaxSendBandwidth").getAsInt(); + } + if (kurentoOptionsJson.has("videoMinSendBandwidth")) { + videoMinSendBandwidth = kurentoOptionsJson.get("videoMinSendBandwidth").getAsInt(); + } + if (kurentoOptionsJson.has("allowedFilters")) { + JsonArray filters = kurentoOptionsJson.get("allowedFilters").getAsJsonArray(); + allowedFilters = new String[filters.size()]; + for (int i = 0; i < filters.size(); i++) { + allowedFilters[i] = filters.get(i).getAsString(); + } + } + kurentoOptions = new KurentoOptions(videoMaxRecvBandwidth, videoMinRecvBandwidth, videoMaxSendBandwidth, + videoMinSendBandwidth, allowedFilters); + } + + this.tokenOptions = new TokenOptions(role, data, record, kurentoOptions); + } + + /** + * Returns the token string value that must be sent to clients. They need to use + * it to connect to the session. + */ + public String getToken() { + return this.token; + } + + /** + * Returns the connection identifier that will be associated to the user + * consuming this token. This means that the future + * {@link io.openvidu.java.client.Connection Connection} object created with + * this token will have as connectionId this string. In other + * words, method {@link io.openvidu.java.client.Connection#getConnectionId() + * Connection.getConnectionId()} will return this same value. + * + * With connectionId you can call the following methods without + * having to fetch and search for the actual + * {@link io.openvidu.java.client.Connection Connection} object: + *
    + *
  • Call {@link io.openvidu.java.client.Session#forceDisconnect(String) + * Session.forceDisconnect()} to invalidate the token if no client has used it + * yet or force the connected client to leave the session if it has.
  • + *
  • Call + * {@link io.openvidu.java.client.Session#updateConnection(String, TokenOptions) + * Session.updateConnection()} to update the + * {@link io.openvidu.java.client.Connection Connection} options. And this is + * valid for unused tokens, but also for already used tokens, so you can + * dynamically change the connection options on the fly.
  • + *
+ * + */ + public String getConnectionId() { + return this.connectionId; + } + + /** + * Returns the role assigned to this token. + */ + public OpenViduRole getRole() { + return this.tokenOptions.getRole(); + } + + /** + * Returns the secure (server-side) metadata assigned to this token. + */ + public String getData() { + return this.tokenOptions.getData(); + } + + /** + * Whether the streams published by the participant owning this token will be + * recorded or not. This only affects INDIVIDUAL recording. + */ + public Boolean record() { + return this.tokenOptions.record(); + } + + protected void overrideTokenOptions(TokenOptions tokenOptions) { + this.tokenOptions = tokenOptions; + } + +} diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java index 96c39fd6..f9338064 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java @@ -17,13 +17,18 @@ package io.openvidu.java.client; +import com.google.gson.JsonArray; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; + /** * See {@link io.openvidu.java.client.Session#generateToken(TokenOptions)} */ public class TokenOptions { - private String data; private OpenViduRole role; + private String data; + private Boolean record; private KurentoOptions kurentoOptions; /** @@ -33,15 +38,24 @@ public class TokenOptions { */ public static class Builder { - private String data = ""; private OpenViduRole role = OpenViduRole.PUBLISHER; + private String data; + private Boolean record = true; private KurentoOptions kurentoOptions; /** - * Builder for {@link io.openvidu.java.client.TokenOptions} + * Builder for {@link io.openvidu.java.client.TokenOptions}. */ public TokenOptions build() { - return new TokenOptions(this.data, this.role, this.kurentoOptions); + return new TokenOptions(this.role, this.data, this.record, this.kurentoOptions); + } + + /** + * Call this method to set the role assigned to this token. + */ + public Builder role(OpenViduRole role) { + this.role = role; + return this; } /** @@ -72,16 +86,19 @@ public class TokenOptions { } /** - * Call this method to set the role assigned to this token + * Call this method to flag the streams published by the participant owning this + * token to be recorded or not. This only affects INDIVIDUAL recording. If not set by default will be true. */ - public Builder role(OpenViduRole role) { - this.role = role; + public Builder record(boolean record) { + this.record = record; return this; } /** * Call this method to set a {@link io.openvidu.java.client.KurentoOptions} - * object for this token + * object for this token. */ public Builder kurentoOptions(KurentoOptions kurentoOptions) { this.kurentoOptions = kurentoOptions; @@ -90,31 +107,79 @@ public class TokenOptions { } - private TokenOptions(String data, OpenViduRole role, KurentoOptions kurentoOptions) { - this.data = data; + TokenOptions(OpenViduRole role, String data, Boolean record, KurentoOptions kurentoOptions) { this.role = role; + this.data = data; + this.record = record; this.kurentoOptions = kurentoOptions; } /** - * Returns the secure (server-side) metadata assigned to this token - */ - public String getData() { - return this.data; - } - - /** - * Returns the role assigned to this token + * Returns the role assigned to this token. */ public OpenViduRole getRole() { return this.role; } /** - * Returns the Kurento options assigned to this token + * Returns the secure (server-side) metadata assigned to this token. */ - public KurentoOptions getKurentoOptions() { - return this.kurentoOptions; + public String getData() { + return this.data; + } + + /** + * Whether the streams published by the participant owning this token will be + * recorded or not. This only affects INDIVIDUAL recording. + */ + public Boolean record() { + return this.record; + } + + protected JsonObject toJsonObject(String sessionId) { + JsonObject json = new JsonObject(); + json.addProperty("session", sessionId); + if (getRole() != null) { + json.addProperty("role", getRole().name()); + } else { + json.add("role", JsonNull.INSTANCE); + } + if (getData() != null) { + json.addProperty("data", getData()); + } else { + json.add("data", JsonNull.INSTANCE); + } + if (record() != null) { + json.addProperty("record", record()); + } else { + json.add("record", JsonNull.INSTANCE); + } + if (this.kurentoOptions != null) { + JsonObject kurentoOptions = new JsonObject(); + if (this.kurentoOptions.getVideoMaxRecvBandwidth() != null) { + kurentoOptions.addProperty("videoMaxRecvBandwidth", this.kurentoOptions.getVideoMaxRecvBandwidth()); + } + if (this.kurentoOptions.getVideoMinRecvBandwidth() != null) { + kurentoOptions.addProperty("videoMinRecvBandwidth", this.kurentoOptions.getVideoMinRecvBandwidth()); + } + if (this.kurentoOptions.getVideoMaxSendBandwidth() != null) { + kurentoOptions.addProperty("videoMaxSendBandwidth", this.kurentoOptions.getVideoMaxSendBandwidth()); + } + if (this.kurentoOptions.getVideoMinSendBandwidth() != null) { + kurentoOptions.addProperty("videoMinSendBandwidth", this.kurentoOptions.getVideoMinSendBandwidth()); + } + if (this.kurentoOptions.getAllowedFilters().length > 0) { + JsonArray allowedFilters = new JsonArray(); + for (String filter : this.kurentoOptions.getAllowedFilters()) { + allowedFilters.add(filter); + } + kurentoOptions.add("allowedFilters", allowedFilters); + } + json.add("kurentoOptions", kurentoOptions); + } + return json; } } diff --git a/openvidu-node-client/src/Connection.ts b/openvidu-node-client/src/Connection.ts index 836eaa81..9bb08874 100644 --- a/openvidu-node-client/src/Connection.ts +++ b/openvidu-node-client/src/Connection.ts @@ -17,6 +17,7 @@ import { OpenViduRole } from './OpenViduRole'; import { Publisher } from './Publisher'; +import { TokenOptions } from './TokenOptions'; /** * See [[Session.activeConnections]] @@ -24,7 +25,8 @@ import { Publisher } from './Publisher'; export class Connection { /** - * Identifier of the connection. You can call [[Session.forceDisconnect]] passing this property as parameter + * Identifier of the connection. You can call methods [[Session.forceDisconnect]] + * or [[Session.updateConnection]] passing this property as parameter */ connectionId: string; @@ -38,6 +40,16 @@ export class Connection { */ role: OpenViduRole; + /** + * Data associated to the connection on the server-side. This value is set with property [[TokenOptions.data]] when calling [[Session.generateToken]] + */ + serverData: string; + + /** + * Whether to record the streams published by the participant owning this token or not. This only affects [INDIVIDUAL recording](/en/stable/advanced-features/recording#selecting-streams-to-be-recorded) + */ + record: boolean; + /** * Token associated to the connection */ @@ -54,11 +66,6 @@ export class Connection { */ platform: string; - /** - * Data associated to the connection on the server-side. This value is set with property [[TokenOptions.data]] when calling [[Session.generateToken]] - */ - serverData: string; - /** * Data associated to the connection on the client-side. This value is set with second parameter of method * [Session.connect](/en/stable/api/openvidu-browser/classes/session.html#connect) in OpenVidu Browser @@ -80,18 +87,26 @@ export class Connection { /** * @hidden */ - constructor(connectionId: string, createdAt: number, role: OpenViduRole, token: string, location: string, platform: string, serverData: string, clientData: string, - publishers: Publisher[], subscribers: string[]) { - this.connectionId = connectionId; - this.createdAt = createdAt; - this.role = role; - this.token = token; - this.location = location; - this.platform = platform; - this.serverData = serverData; - this.clientData = clientData; - this.publishers = publishers; - this.subscribers = subscribers; + constructor(json) { + if (json.publishers != null) { + json.publishers.forEach(publisher => { + this.publishers.push(new Publisher(publisher)); + }); + } + if (json.subscribers != null) { + json.subscribers.forEach(subscriber => { + this.subscribers.push(subscriber.streamId); + }); + } + this.connectionId = json.connectionId; + this.createdAt = json.createdAt; + this.role = json.role; + this.serverData = json.serverData; + this.record = json.record; + this.token = json.token; + this.location = json.location; + this.platform = json.platform; + this.clientData = json.clientData; } /** @@ -102,10 +117,11 @@ export class Connection { this.connectionId === other.connectionId && this.createdAt === other.createdAt && this.role === other.role && + this.serverData === other.serverData && + this.record === other.record && this.token === other.token && this.location === other.location && this.platform === other.platform && - this.serverData === other.serverData && this.clientData === other.clientData && this.subscribers.length === other.subscribers.length && this.publishers.length === other.publishers.length); @@ -125,4 +141,17 @@ export class Connection { return false; } } + + /** + * @hidden + */ + overrideTokenOptions(tokenOptions: TokenOptions): void { + if (tokenOptions.role != null) { + this.role = tokenOptions.role; + } + if (tokenOptions.record != null) { + this.record = tokenOptions.record + } + } + } \ No newline at end of file diff --git a/openvidu-node-client/src/Session.ts b/openvidu-node-client/src/Session.ts index 09cde2ee..cdb115be 100644 --- a/openvidu-node-client/src/Session.ts +++ b/openvidu-node-client/src/Session.ts @@ -15,16 +15,16 @@ * */ -import axios from 'axios'; +import axios, { AxiosError } from 'axios'; import { Connection } from './Connection'; import { MediaMode } from './MediaMode'; import { OpenVidu } from './OpenVidu'; -import { OpenViduRole } from './OpenViduRole'; import { Publisher } from './Publisher'; import { Recording } from './Recording'; import { RecordingLayout } from './RecordingLayout'; import { RecordingMode } from './RecordingMode'; import { SessionProperties } from './SessionProperties'; +import { Token } from './Token'; import { TokenOptions } from './TokenOptions'; @@ -49,8 +49,9 @@ export class Session { * Array of active connections to the session. This property always initialize as an empty array and * **will remain unchanged since the last time method [[Session.fetch]] was called**. Exceptions to this rule are: * - * - Calling [[Session.forceUnpublish]] also automatically updates each affected Connection status - * - Calling [[Session.forceDisconnect]] automatically updates each affected Connection status + * - Calling [[Session.forceUnpublish]] automatically updates each affected local Connection object. + * - Calling [[Session.forceDisconnect]] automatically updates each affected local Connection object. + * - Calling [[Session.updateConnection]] automatically updates the attributes of the affected local Connection object. * * To get the array of active connections with their current actual value, you must call [[Session.fetch]] before consulting * property [[activeConnections]] @@ -93,12 +94,36 @@ export class Session { } /** - * Gets a new token associated to Session object - * - * @returns A Promise that is resolved to the _token_ if success and rejected with an Error object if not + * @deprecated Use [[Session.createToken]] instead to get a [[Token]] object. + * + * @returns A Promise that is resolved to the generated _token_ string if success and rejected with an Error object if not */ public generateToken(tokenOptions?: TokenOptions): Promise { return new Promise((resolve, reject) => { + this.createToken(tokenOptions).then(token => resolve(token.token)).catch(error => reject(error)); + }); + } + + /** + * Gets a new token object associated to Session object configured with + * `tokenOptions`. The token string value to send to the client side + * is available at [[Token.token]] property. + * + * Property [[Token.connectionId]] provides the connection identifier that will be given + * to the user consuming the token. With `connectionId` you can call + * the following methods without having to fetch and search for the actual + * [[Connection]] object: + * + * - Call [[Session.forceDisconnect]] to invalidate the token if no client has used it + * yet or force the connected client to leave the session if it has. + * - Call [[Session.updateConnection]] to update the [[Connection]] options. And this is + * valid for unused tokens, but also for already used tokens, so you can + * dynamically change the user connection options on the fly. + * + * @returns A Promise that is resolved to the generated [[Token]] object if success and rejected with an Error object if not + */ + public createToken(tokenOptions?: TokenOptions): Promise { + return new Promise((resolve, reject) => { const data = JSON.stringify({ session: this.sessionId, @@ -120,26 +145,13 @@ export class Session { .then(res => { if (res.status === 200) { // SUCCESS response from openvidu-server. Resolve token - resolve(res.data.id); + resolve(new Token(res.data)); } else { // ERROR response from openvidu-server. Resolve HTTP status reject(new Error(res.status.toString())); } }).catch(error => { - if (error.response) { - // The request was made and the server responded with a status code (not 2xx) - reject(new Error(error.response.status.toString())); - } else if (error.request) { - // The request was made but no response was received - // `error.request` is an instance of XMLHttpRequest in the browser and an instance of - // http.ClientRequest in node.js - console.error(error.request); - reject(new Error(error.request)); - } else { - // Something happened in setting up the request that triggered an Error - console.error('Error', error.message); - reject(new Error(error.message)); - } + this.handleError(error, reject); }); }); } @@ -171,29 +183,17 @@ export class Session { reject(new Error(res.status.toString())); } }).catch(error => { - if (error.response) { - // The request was made and the server responded with a status code (not 2xx) - reject(new Error(error.response.status.toString())); - } else if (error.request) { - // The request was made but no response was received - // `error.request` is an instance of XMLHttpRequest in the browser and an instance of - // http.ClientRequest in node.js - console.error(error.request); - reject(new Error(error.request)); - } else { - // Something happened in setting up the request that triggered an Error - console.error('Error', error.message); - reject(new Error(error.message)); - } + this.handleError(error, reject); }); }); } /** * Updates every property of the Session with the current status it has in OpenVidu Server. This is especially useful for accessing the list of active - * connections of the Session ([[Session.activeConnections]]) and use those values to call [[Session.forceDisconnect]] or [[Session.forceUnpublish]]. + * connections of the Session ([[Session.activeConnections]]) and use those values to call [[Session.forceDisconnect]], [[Session.forceUnpublish]] or + * [[Session.updateConnection]]. * - * To update every Session object owned by OpenVidu object, call [[OpenVidu.fetch]] + * To update all Session objects owned by OpenVidu object at once, call [[OpenVidu.fetch]] * * @returns A promise resolved to true if the Session status has changed with respect to the server, or to false if not. * This applies to any property or sub-property of the Session object @@ -223,31 +223,27 @@ export class Session { reject(new Error(res.status.toString())); } }).catch(error => { - if (error.response) { - // The request was made and the server responded with a status code (not 2xx) - reject(new Error(error.response.status.toString())); - } else if (error.request) { - // The request was made but no response was received - // `error.request` is an instance of XMLHttpRequest in the browser and an instance of - // http.ClientRequest in node.js - console.error(error.request); - reject(new Error(error.request)); - } else { - // Something happened in setting up the request that triggered an Error - console.error('Error', error.message); - reject(new Error(error.message)); - } + this.handleError(error, reject); }); }); } /** - * Forces the user with Connection `connectionId` to leave the session. OpenVidu Browser will trigger the proper events on the client-side - * (`streamDestroyed`, `connectionDestroyed`, `sessionDisconnected`) with reason set to `"forceDisconnectByServer"` + * Forces the user with Connection `connectionId` to leave the session, or invalidates the [[Token]] associated with that + * `connectionId` if no user has used it yet. * - * You can get `connection` parameter from [[Session.activeConnections]] array ([[Connection.connectionId]] for getting each `connectionId` property). - * Remember to call [[Session.fetch]] before to fetch the current actual properties of the Session from OpenVidu Server + * In the first case you can get `connection` parameter from [[Session.activeConnections]] array (remember to call [[Session.fetch]] before + * to fetch the current actual properties of the Session from OpenVidu Server). As a result, OpenVidu Browser will trigger the proper + * events on the client-side (`streamDestroyed`, `connectionDestroyed`, `sessionDisconnected`) with reason set to `"forceDisconnectByServer"`. + * + * In the second case you can get `connectionId` parameter with [[Token.connectionId]]. As a result, the token will be invalidated + * and no user will be able to connect to the session with it. + * + * This method automatically updates the properties of the local affected objects. This means that there is no need to call + * [[Session.fetch]] to see the changes consequence of the execution of this method applied in the local objects. * + * @param connection The Connection object to disconnect from the session, or its `connectionId` property + * * @returns A Promise that is resolved if the user was successfully disconnected and rejected with an Error object if not */ public forceDisconnect(connection: string | Connection): Promise { @@ -302,20 +298,7 @@ export class Session { } }) .catch(error => { - if (error.response) { - // The request was made and the server responded with a status code (not 2xx) - reject(new Error(error.response.status.toString())); - } else if (error.request) { - // The request was made but no response was received - // `error.request` is an instance of XMLHttpRequest in the browser and an instance of - // http.ClientRequest in node.js - console.error(error.request); - reject(new Error(error.request)); - } else { - // Something happened in setting up the request that triggered an Error - console.error('Error', error.message); - reject(new Error(error.message)); - } + this.handleError(error, reject); }); }); } @@ -327,6 +310,9 @@ export class Session { * You can get `publisher` parameter from [[Connection.publishers]] array ([[Publisher.streamId]] for getting each `streamId` property). * Remember to call [[Session.fetch]] before to fetch the current actual properties of the Session from OpenVidu Server * + * This method automatically updates the properties of the local affected objects. This means that there is no need to call + * [[Session.fetch]] to see the changes consequence of the execution of this method applied in the local objects. + * * @returns A Promise that is resolved if the stream was successfully unpublished and rejected with an Error object if not */ public forceUnpublish(publisher: string | Publisher): Promise { @@ -367,20 +353,70 @@ export class Session { reject(new Error(res.status.toString())); } }).catch(error => { - if (error.response) { - // The request was made and the server responded with a status code (not 2xx) - reject(new Error(error.response.status.toString())); - } else if (error.request) { - // The request was made but no response was received - // `error.request` is an instance of XMLHttpRequest in the browser and an instance of - // http.ClientRequest in node.js - console.error(error.request); - reject(new Error(error.request)); - } else { - // Something happened in setting up the request that triggered an Error - console.error('Error', error.message); - reject(new Error(error.message)); + this.handleError(error, reject); + }); + }); + } + + /** + * Updates the properties of a Connection. These properties are the ones defined + * by the [[TokenOptions]] parameter when generating the token used to create the Connection. + * These are the properties that can be updated: + * + * - [[TokenOptions.role]] + * - [[TokenOptions.record]] + * + * The `connectionId` parameter can be obtained from a Connection object with + * [[Connection.connectionId]], in which case the updated properties will + * modify an active Connection. But `connectionId` can also be obtained from a + * Token with [[Token.connectionId]], which allows modifying a still not used token. + * + * This method automatically updates the properties of the local affected objects. This means that there is no need to call + * [[Session.fetch]] to see the changes consequence of the execution of this method applied in the local objects. + * + * @param connectionId The [[Connection.connectionId]] property of the Connection object to modify, + * or the [[Token.connectionId]] property of a still not used token to modify + * @param tokenOptions A new [[TokenOptions]] object with the updated values to apply + * + * @returns A Promise that is resolved to the updated [[Connection]] object if the operation was + * successful and rejected with an Error object if not + */ + public updateConnection(connectionId: string, tokenOptions: TokenOptions): Promise { + return new Promise((resolve, reject) => { + axios.patch( + this.ov.host + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection/" + connectionId, + tokenOptions, + { + headers: { + 'Authorization': this.ov.basicAuth, + 'Content-Type': 'application/json' } + } + ) + .then(res => { + if (res.status === 200) { + console.log('Connection ' + connectionId + ' updated'); + } else if (res.status === 204) { + console.log('Properties of Connection ' + connectionId + ' remain the same'); + } else { + // ERROR response from openvidu-server. Resolve HTTP status + reject(new Error(res.status.toString())); + return; + } + // Update the actual Connection object with the new options + const existingConnection: Connection = this.activeConnections.find(con => con.connectionId === connectionId); + if (!existingConnection) { + // The updated Connection is not available in local map + const newConnection: Connection = new Connection(res.data); + this.activeConnections.push(newConnection); + resolve(newConnection); + } else { + // The updated Connection was available in local map + existingConnection.overrideTokenOptions(tokenOptions); + resolve(existingConnection); + } + }).catch(error => { + this.handleError(error, reject); }); }); } @@ -478,28 +514,8 @@ export class Session { } this.activeConnections = []; - json.connections.content.forEach(connection => { - const publishers: Publisher[] = []; - connection.publishers.forEach(publisher => { - publishers.push(new Publisher(publisher)); - }); - const subscribers: string[] = []; - connection.subscribers.forEach(subscriber => { - subscribers.push(subscriber.streamId); - }); - this.activeConnections.push( - new Connection( - connection.connectionId, - connection.createdAt, - connection.role, - connection.token, - connection.location, - connection.platform, - connection.serverData, - connection.clientData, - publishers, - subscribers)); - }); + json.connections.content.forEach(jsonConnection => this.activeConnections.push(new Connection(jsonConnection))); + // Order connections by time of creation this.activeConnections.sort((c1, c2) => (c1.createdAt > c2.createdAt) ? 1 : ((c2.createdAt > c1.createdAt) ? -1 : 0)); return this; @@ -539,4 +555,24 @@ export class Session { } } + /** + * @hidden + */ + private handleError(error: AxiosError, reject: (reason?: any) => void) { + if (error.response) { + // The request was made and the server responded with a status code (not 2xx) + reject(new Error(error.response.status.toString())); + } else if (error.request) { + // The request was made but no response was received + // `error.request` is an instance of XMLHttpRequest in the browser and an instance of + // http.ClientRequest in node.js + console.error(error.request); + reject(new Error(error.request)); + } else { + // Something happened in setting up the request that triggered an Error + console.error('Error', error.message); + reject(new Error(error.message)); + } + } + } \ No newline at end of file diff --git a/openvidu-node-client/src/Token.ts b/openvidu-node-client/src/Token.ts new file mode 100644 index 00000000..bd9119d1 --- /dev/null +++ b/openvidu-node-client/src/Token.ts @@ -0,0 +1,81 @@ +/* + * (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. + * + */ + +import { TokenOptions } from './TokenOptions'; + +export class Token { + + /** + * The token string value that must be sent to clients. They need to use it to connect to the session. + */ + token: string; + + /** + * The connection identifier that will be associated to the user + * consuming this token. This means that the future [[Connection]] object created with + * this token will have as [[Connection.connectionId]] this same value. + * + * With `connectionId` you can call the following methods without + * having to fetch and search for the actual [[Connection]] object: + * + * - Call [[Session.forceDisconnect]] to invalidate the token if no client has used it + * yet or force the connected client to leave the session if it has. + * - Call [[Session.updateConnection]] to update the [[Connection]] options. And this is + * valid for unused tokens, but also for already used tokens, so you can + * dynamically change the connection options on the fly. + */ + connectionId: string; + + /** + * The [[TokenOptions]] assigned to this token. + */ + tokenOptions: TokenOptions; + + /** + * @hidden + */ + constructor(json) { + this.token = json.token; + this.connectionId = json.connectionId; + let possibleKurentoOptions; + if (!!json.kurentoOptions) { + possibleKurentoOptions = {}; + if (json.kurentoOptions.videoMaxRecvBandwidth != null) { + possibleKurentoOptions['videoMaxRecvBandwidth'] = json.kurentoOptions.videoMaxRecvBandwidth; + } + if (json.kurentoOptions.videoMinRecvBandwidth != null) { + possibleKurentoOptions['videoMinRecvBandwidth'] = json.kurentoOptions.videoMinRecvBandwidth; + } + if (json.kurentoOptions.videoMaxSendBandwidth != null) { + possibleKurentoOptions['videoMaxSendBandwidth'] = json.kurentoOptions.videoMaxSendBandwidth; + } + if (json.kurentoOptions.videoMinSendBandwidth != null) { + possibleKurentoOptions['videoMinSendBandwidth'] = json.kurentoOptions.videoMinSendBandwidth; + } + if (json.kurentoOptions.allowedFilters != null) { + possibleKurentoOptions['allowedFilters'] = json.kurentoOptions.allowedFilters; + } + } + this.tokenOptions = { + role: json.role, + data: json.data, + record: json.record, + kurentoOptions: possibleKurentoOptions + }; + } + +} \ No newline at end of file diff --git a/openvidu-node-client/src/TokenOptions.ts b/openvidu-node-client/src/TokenOptions.ts index 51fa4d0d..0dacc6ed 100644 --- a/openvidu-node-client/src/TokenOptions.ts +++ b/openvidu-node-client/src/TokenOptions.ts @@ -44,7 +44,6 @@ export interface TokenOptions { */ record?: boolean; - /** * **WARNING**: experimental option. This interface may change in the near future * diff --git a/openvidu-node-client/src/index.ts b/openvidu-node-client/src/index.ts index 50ce477a..f08dbba0 100644 --- a/openvidu-node-client/src/index.ts +++ b/openvidu-node-client/src/index.ts @@ -3,6 +3,7 @@ export * from './OpenViduRole'; export * from './Session'; export * from './SessionProperties'; export * from './TokenOptions'; +export * from './Token'; export * from './MediaMode'; export * from './RecordingLayout'; export * from './RecordingMode';