diff --git a/src/main/java/io/github/hikingc/matrixsdk/api/Event.java b/src/main/java/io/github/hikingc/matrixsdk/api/Event.java index f80486a..cec5af7 100644 --- a/src/main/java/io/github/hikingc/matrixsdk/api/Event.java +++ b/src/main/java/io/github/hikingc/matrixsdk/api/Event.java @@ -20,6 +20,10 @@ /// All operations in this interface are blocking. Implementations must ensure thread safety and /// avoid synchronization blocks that cause carrier thread pinning during network I/O. /// +/// Unless otherwise noted, every method in this interface throws [MatrixIOException] if the request +/// or response payload cannot be processed, and [MatrixNetworkException] if the server's response +/// status is not successful. +/// /// @see Matrix Client-Server API /// Specification for Events public interface Event { @@ -29,12 +33,16 @@ public interface Event { /// @param roomId the room ID where the event is. /// @param eventId the event ID to retrieve. /// @return the full event. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. ClientEvent getEvent(RoomID roomId, String eventId); /// Returns currently-joined members /// /// @param roomId the room ID to fetch data from. /// @return a list of room members. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. RoomMembers getJoinedMembers(RoomID roomId); /// Returns a filterable list of members and their current membership state in a room. @@ -47,14 +55,18 @@ public interface Event { /// @param notMembership the kind of membership to exclude from the results. Defaults to no /// filtering if unspecified. /// @return a list of [ClientEvent]s with the membership information of room members. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. List getMembers( RoomID roomId, String at, Membership membership, Membership notMembership); /// Get the state events for the current state of a room. /// /// @param roomId the room ID to fetch data from. - /// @return the current state of the room - List> getStateEvents(RoomID roomId); + /// @return the current state of the room. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. + List> getStateEvents(RoomID roomId); /// Looks up the contents of a state event in a room. If the user is joined to the room then the /// state is taken from the current state of the room. If the user has left the room then the @@ -64,6 +76,8 @@ List getMembers( /// @param eventType the type of state to look up. /// @param stateKey the room to look up the state in. /// @return the content of the event, including all additional metadata fields. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. @SuppressWarnings("java:S1452") // Caller doesn't know content type ahead of time; polymorphic dispatch via @JsonTypeInfo resolves // it @@ -78,6 +92,7 @@ List getMembers( /// @param dir the [ChronologicalDirection] in which to search /// @return [Messages] with available data. /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. /// @throws NullPointerException when the roomId is null. Messages getMessages(RoomID roomId, ChronologicalDirection dir, QueryParametersMessages params); @@ -88,6 +103,8 @@ List getMembers( /// @param dir the [ChronologicalDirection] in which to search /// @param timestamp the timestamp to search from, as given in milliseconds since the Unix epoch. /// @return [EventMetadata] if an event was found. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. EventMetadata getEventClosestToTimestamp( RoomID roomId, ChronologicalDirection dir, int timestamp); @@ -96,6 +113,8 @@ EventMetadata getEventClosestToTimestamp( /// /// @param roomId the room ID to fetch data from. /// @return [RoomInfo] with current state of the room. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. RoomInfo getInitialSync(RoomID roomId); /// Sends a state event. @@ -106,15 +125,19 @@ EventMetadata getEventClosestToTimestamp( /// @return a [String] representing a unique identifier of the event. /// @throws MatrixIOException when the payload cannot be processed. /// @throws MatrixNetworkException when the response status is not successful. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. String sendStateEvent(RoomID roomId, String stateKey, StateEventContent content); /// Sends a message event. /// - /// @param roomId the room ID where to send the event. - /// @param txnId for this event. Clients should generate an ID unique across requests with the + /// @param roomId the room ID where to send the event. + /// @param txnId for this event. Clients should generate an ID unique across requests with the /// same access token; it will be used by the server to ensure idempotency of requests. /// @param content of any type of message event. /// @return a [String] representing a unique identifier of the event. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. String sendMessageEvent(RoomID roomId, String txnId, MessageEventContent content); /// Strips all information out of an event which isn’t critical to the integrity of the diff --git a/src/main/java/io/github/hikingc/matrixsdk/api/Filter.java b/src/main/java/io/github/hikingc/matrixsdk/api/Filter.java index 4e498ce..f5a1279 100644 --- a/src/main/java/io/github/hikingc/matrixsdk/api/Filter.java +++ b/src/main/java/io/github/hikingc/matrixsdk/api/Filter.java @@ -2,12 +2,18 @@ import io.github.hikingc.matrixsdk.api.filters.FilterDefinition; import io.github.hikingc.matrixsdk.api.identifiers.UserID; +import io.github.hikingc.matrixsdk.exceptions.MatrixIOException; +import io.github.hikingc.matrixsdk.exceptions.MatrixNetworkException; /// Core interface for executing protocol operations for filtering. /// /// All operations in this interface are blocking. Implementations must ensure thread safety and /// avoid synchronization blocks that cause carrier thread pinning during network I/O. /// +/// Unless otherwise noted, every method in this interface throws [MatrixIOException] if the request +/// or response payload cannot be processed, and [MatrixNetworkException] if the server's response +/// status is not successful. +/// /// @see Matrix Client-Server API /// Specification for Rooms public interface Room { @@ -24,8 +28,8 @@ public interface Room { /// /// @param configuration of the room. /// @return the created room’s ID. - /// @throws MatrixIOException when the payload cannot be processed - /// @throws MatrixNetworkException when the response status is not successful + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. String create(InitialRoomConfiguration configuration); /// Requests the server to resolve a room alias if not possible, the server will use the @@ -34,18 +38,24 @@ public interface Room { /// @param roomAlias the room alias. /// @return a [ResolvedAlias] containing the room ids for the requested alias and which servers /// are aware of it. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. ResolvedAlias resolveAlias(RoomAlias roomAlias); /// Sets a room alias to a room. /// /// @param roomAlias a [RoomAlias]. /// @param roomId the [RoomID] to receive the alias. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. void setAlias(RoomAlias roomAlias, RoomID roomId); /// Requests the server to remove a mapping of a room alias to a room id. On success, servers /// might modify `m.room.canonical_alias` /// /// @param roomAlias the [RoomAlias] to remove. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. void deleteAlias(RoomAlias roomAlias); /// Requests a list of aliases maintained by the local server for the given room, requires to be @@ -56,12 +66,16 @@ public interface Room { /// /// @param roomId the [RoomID] to find local aliases of. /// @return a [List] of Room aliases. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. List getAliasesOfARoom(RoomID roomId); /// Requests the server to retrieve a list of the user's current rooms (in simple terms whoever /// calls this method). /// /// @return a [List] of the rooms. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. List getJoinedRooms(); /// Send an invitation to a user to participate in a room, this endpoint requires the caller to be @@ -80,7 +94,10 @@ public interface Room { /// @param request a [JoinRoomRequest] where additional information can be passed. /// @param via the servers to attempt to join the room through. One of the servers must be /// participating in the room. - /// @return the room ID + /// @return the room ID. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. + /// @throws IllegalArgumentException when using an incorrect [Validator]. String joinByRoomIdOrAliasIfAllowed( Validator roomIdOrAlias, JoinRoomRequest request, List via); @@ -90,7 +107,9 @@ String joinByRoomIdOrAliasIfAllowed( /// @param request a [JoinRoomRequest] where additional information can be passed. /// @param via the servers to attempt to join the room through. One of the servers must be /// participating in the room. - /// @return the room ID + /// @return the room ID. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. String joinByRoomIdIfAllowed(RoomID roomId, JoinRoomRequest request, List via); /// Knock on a room to ask for permission to join. Acceptance of this request happens out of band. @@ -100,6 +119,8 @@ String joinByRoomIdOrAliasIfAllowed( /// @param via the servers to attempt to join the room through. One of the servers must be /// participating in the room. /// @return the room ID of the knocked room. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. String knockOn(Validator roomIdOrAlias, String reason, List via); /// Sends a request to leave the room, upon success, you will forget all messages from this room. @@ -171,8 +192,7 @@ String joinByRoomIdOrAliasIfAllowed( /// @param since a pagination token from a previous request, allowing you to get the next or /// previous batch of rooms. The direction of pagination is specified by which token is /// supplied. - /// @return a [PublicRoomDirectory] containing [io.github.hikingc.matrixsdk.api.rooms.models.PublishedRoomsChunk] records of the published - /// rooms on the server. + /// @return a [PublicRoomDirectory] containing records of the published rooms on the server. /// @throws MatrixIOException when the payload cannot be processed. /// @throws NullPointerException when the roomId is null. /// @see #getPublishedRoomDirectory(PublicRoomRequest) @@ -182,18 +202,21 @@ String joinByRoomIdOrAliasIfAllowed( /// Lists a server’s published room directory. /// /// @param request a [PublicRoomRequest] with additional filters for the request. - /// @return a [PublicRoomDirectory] containing [io.github.hikingc.matrixsdk.api.rooms.models.PublishedRoomsChunk] records of the published - /// rooms on the server. + /// @return a [PublicRoomDirectory] containing records of the published rooms on the server. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. PublicRoomDirectory getPublishedRoomDirectory(PublicRoomRequest request); /// Retrieves a summary for a room. The response data might yield outdated, partial or even with /// no data. /// - /// @param roomIdOrAlias a [RoomID] or [RoomAlias] of the room to target + /// @param roomIdOrAlias a [RoomID] or [RoomAlias] of the room to target. /// @param via the servers to attempt to request the summary from when the local server cannot - /// generate it + /// generate it. /// @return a [RoomSummary] containing all the information about the room. /// @throws MatrixIOException when the payload cannot be processed. /// @throws NullPointerException when the roomId is null. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. RoomSummary getRoomSummary(Validator roomIdOrAlias, List via); } diff --git a/src/main/java/io/github/hikingc/matrixsdk/api/UserData.java b/src/main/java/io/github/hikingc/matrixsdk/api/UserData.java index 1a0760a..33074e4 100644 --- a/src/main/java/io/github/hikingc/matrixsdk/api/UserData.java +++ b/src/main/java/io/github/hikingc/matrixsdk/api/UserData.java @@ -3,12 +3,18 @@ import io.github.hikingc.matrixsdk.api.identifiers.UserID; import io.github.hikingc.matrixsdk.api.userdata.UserProfile; import io.github.hikingc.matrixsdk.api.userdata.UsersFound; +import io.github.hikingc.matrixsdk.exceptions.MatrixIOException; +import io.github.hikingc.matrixsdk.exceptions.MatrixNetworkException; /// Core interface for executing protocol operations against User data. /// /// All operations in this interface are blocking. Implementations must ensure thread safety and /// avoid synchronization blocks that cause carrier thread pinning during network I/O. /// +/// Unless otherwise noted, every method in this interface throws [MatrixIOException] if the request +/// or response payload cannot be processed, and [MatrixNetworkException] if the server's response +/// status is not successful. +/// /// @see Matrix Client-Server /// API Specification for User Data public interface UserData { @@ -22,32 +28,42 @@ public interface UserData { /// @param limit the maximum number of results. /// @param searchTerm the term to search for. /// @return all the [UsersFound] by the server. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. UsersFound searchUsersByTerm(Integer limit, String searchTerm); /// Get the profile of a user /// - /// @param userId the [UserID] to profile. - /// @return the corresponding [UserProfile]. + /// @param userId the [UserID] to target. + /// @return its [UserProfile]. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. UserProfile getUserProfile(UserID userId); /// Get the value of a profile field for a user /// - /// @param userId the [UserID] to profile. - /// @param keyName a property field - /// @return the value of the key property. + /// @param userId the [UserID] to target. + /// @param keyName the key name. + /// @return the corresponding value of the pair. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. String getUserProfileByProperty( UserID userId, String keyName); // only 1 property allowed so no Map /// Set or update a profile field for a user. /// - /// @param userId the [UserID] that'll receive the K-V. - /// @param keyName the key to insert in the profile. - /// @param valueName the value for the key. + /// @param userId the [UserID] to target. + /// @param keyName the key name. + /// @param valueName the value name. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. void setUserProfileProperty(UserID userId, String keyName, String valueName); /// Remove a specific field from a user’s profile. /// - /// @param userId the [UserID] that'll have his K-V deleted. - /// @param keyName the key to be deleted. + /// @param userId the [UserID] that'll have a key-value pair removed from its profile. + /// @param keyName the key name. + /// @throws MatrixIOException when the payload cannot be processed. + /// @throws MatrixNetworkException when the response status is not successful. void deleteUserProfileProperty(UserID userId, String keyName); } diff --git a/src/main/java/io/github/hikingc/matrixsdk/exceptions/MatrixIOException.java b/src/main/java/io/github/hikingc/matrixsdk/exceptions/MatrixIOException.java index 2571c37..994486b 100644 --- a/src/main/java/io/github/hikingc/matrixsdk/exceptions/MatrixIOException.java +++ b/src/main/java/io/github/hikingc/matrixsdk/exceptions/MatrixIOException.java @@ -1,7 +1,7 @@ package io.github.hikingc.matrixsdk.exceptions; /// Thrown to indicate that the code has attempted to process an I/O event to which it has failed. -public class MatrixIOException extends RuntimeException { +public class MatrixIOException extends MatrixException { /// Constructs a [MatrixIOException] with a message. /// /// @param message The detail message. The detail message is saved for later retrieval by the diff --git a/src/main/java/io/github/hikingc/matrixsdk/exceptions/MatrixNetworkException.java b/src/main/java/io/github/hikingc/matrixsdk/exceptions/MatrixNetworkException.java index 84735c0..2069376 100644 --- a/src/main/java/io/github/hikingc/matrixsdk/exceptions/MatrixNetworkException.java +++ b/src/main/java/io/github/hikingc/matrixsdk/exceptions/MatrixNetworkException.java @@ -1,7 +1,7 @@ package io.github.hikingc.matrixsdk.exceptions; /// Thrown to indicate that the code has not received a successful HTTP status code. -public class MatrixNetworkException extends RuntimeException { +public class MatrixNetworkException extends MatrixException { /// Constructs a [MatrixNetworkException] with a message. /// /// @param message The detail message. The detail message is saved for later retrieval by the diff --git a/src/main/java/io/github/hikingc/matrixsdk/services/events/EventService.java b/src/main/java/io/github/hikingc/matrixsdk/services/events/EventService.java index bd982b6..2d3b39d 100644 --- a/src/main/java/io/github/hikingc/matrixsdk/services/events/EventService.java +++ b/src/main/java/io/github/hikingc/matrixsdk/services/events/EventService.java @@ -39,7 +39,7 @@ public EventService(ClientContext context) { public ClientEvent getEvent(RoomID roomId, String eventId) { Objects.requireNonNull(eventId, "The event ID must not be null"); String response = - httpTransport.getEvent( + httpTransport.getRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + ROOM_ENDPOINT @@ -55,7 +55,7 @@ public ClientEvent getEvent(RoomID roomId, String eventId) { public RoomMembers getJoinedMembers(RoomID roomId) { String response = - httpTransport.getEvent( + httpTransport.getRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + ROOM_ENDPOINT @@ -78,7 +78,7 @@ public List getMembers( ROOM_ENDPOINT + roomId + "/members", args); - String response = httpTransport.getEvent(uri, context.token()); + String response = httpTransport.getRequest(uri, context.token()); // We can skip the chunk parent, we don't use ObjectFromString because it is NOT a raw Array as // detailed // on the spec. @@ -88,7 +88,7 @@ public List getMembers( @Override public List> getStateEvents(RoomID roomId) { String response = - httpTransport.getEvent( + httpTransport.getRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + ROOM_ENDPOINT @@ -109,7 +109,7 @@ public ClientEvent getStateEvent(RoomID roomId, String eventType, String stat context.discoveryResponse().homeserver().baseUrl(), ROOM_ENDPOINT + roomId + "/state/" + eventType + "/" + stateKey, args); - String response = httpTransport.getEvent(uri, context.token()); + String response = httpTransport.getRequest(uri, context.token()); return Mapper.getObjectFromString(response, ClientEvent.class); } @@ -128,7 +128,7 @@ public Messages getMessages( context.discoveryResponse().homeserver().baseUrl(), ROOM_ENDPOINT + roomId + "/messages", args); - String queryResponse = httpTransport.getEvent(uri, context.token()); + String queryResponse = httpTransport.getRequest(uri, context.token()); return Mapper.getObjectFromString(queryResponse, Messages.class); } @@ -145,7 +145,7 @@ public EventMetadata getEventClosestToTimestamp( var uri = httpTransport.generateEncodedURI( context.discoveryResponse().homeserver().baseUrl(), ROOM_ENDPOINT + roomId, args); - String response = httpTransport.getEvent(uri, context.token()); + String response = httpTransport.getRequest(uri, context.token()); return Mapper.getObjectFromString(response, EventMetadata.class); } @@ -153,7 +153,7 @@ public EventMetadata getEventClosestToTimestamp( public RoomInfo getInitialSync(RoomID roomId) { String response = - httpTransport.getEvent( + httpTransport.getRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + ROOM_ENDPOINT @@ -178,7 +178,7 @@ public String sendStateEvent(RoomID roomId, String stateKey, StateEventContent c context.discoveryResponse().homeserver().baseUrl(), ROOM_ENDPOINT + roomId + "/state/" + type + "/" + stateKey, null); - String response = httpTransport.putEvent(uri, jsonPayload, context.token()); + String response = httpTransport.putRequest(uri, jsonPayload, context.token()); return Mapper.getStringValueOfAJsonKey(response, "event_id"); } @@ -198,7 +198,7 @@ public String sendMessageEvent(RoomID roomId, String txnId, MessageEventContent context.discoveryResponse().homeserver().baseUrl(), ROOM_ENDPOINT + roomId + "/send/" + type + "/" + txnId, null); - String response = httpTransport.putEvent(uri, jsonPayload, context.token()); + String response = httpTransport.putRequest(uri, jsonPayload, context.token()); return Mapper.getStringValueOfAJsonKey(response, "event_id"); } @@ -211,7 +211,7 @@ public String redactEvent(RoomID roomId, String eventId, String txnId, @Nullable json = Mapper.createObjectFromMap(Map.ofEntries(Map.entry("reason", reason))); } String response = - httpTransport.putEvent( + httpTransport.putRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + ROOM_ENDPOINT @@ -261,7 +261,7 @@ public Sync sync(QueryParametersSync params) { httpTransport.generateEncodedURI( context.discoveryResponse().homeserver().baseUrl(), "/_matrix/client/v3/sync", args); - String response = httpTransport.getEvent(query, context.token()); + String response = httpTransport.getRequest(query, context.token()); return Mapper.getObjectFromString(response, Sync.class); } @@ -270,7 +270,7 @@ public Sync sync(QueryParametersSync params) { /// @return a [String] representing the MXC private String createAndReserveMXC() throws JacksonException { String queryResponse = - httpTransport.postEvent( + httpTransport.postRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + "/_matrix" diff --git a/src/main/java/io/github/hikingc/matrixsdk/services/filtering/FilterService.java b/src/main/java/io/github/hikingc/matrixsdk/services/filtering/FilterService.java index d53dac9..b771b6e 100644 --- a/src/main/java/io/github/hikingc/matrixsdk/services/filtering/FilterService.java +++ b/src/main/java/io/github/hikingc/matrixsdk/services/filtering/FilterService.java @@ -36,7 +36,7 @@ public String publishFilter(UserID userId, FilterDefinition filter) { context.discoveryResponse().homeserver().baseUrl(), USER_FILTER_ENDPOINT + userId + "/filter", null); - String responseBody = httpTransport.postEvent(uri, serializedInputData, context.token()); + String responseBody = httpTransport.postRequest(uri, serializedInputData, context.token()); return Mapper.getStringValueOfAJsonKey(responseBody, "filter_id"); } @@ -50,6 +50,6 @@ public FilterDefinition getFilter(UserID userId, String filterId) { USER_FILTER_ENDPOINT + userId + "/filter/" + filterId, null); return Mapper.getObjectFromString( - httpTransport.getEvent(uri, context.token()), FilterDefinition.class); + httpTransport.getRequest(uri, context.token()), FilterDefinition.class); } } diff --git a/src/main/java/io/github/hikingc/matrixsdk/services/rooms/RoomService.java b/src/main/java/io/github/hikingc/matrixsdk/services/rooms/RoomService.java index 60645e7..8f0a899 100644 --- a/src/main/java/io/github/hikingc/matrixsdk/services/rooms/RoomService.java +++ b/src/main/java/io/github/hikingc/matrixsdk/services/rooms/RoomService.java @@ -63,7 +63,7 @@ public String create(InitialRoomConfiguration configuration) { String responseBody; responseBody = - httpTransport.postEvent( + httpTransport.postRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + "/_matrix/client/v3/createRoom"), @@ -81,7 +81,7 @@ public ResolvedAlias resolveAlias(RoomAlias roomAlias) { DIRECTORY_ENDPOINT_ROOM + roomAlias, null); - var responseBody = httpTransport.getEvent(uri, context.token()); + var responseBody = httpTransport.getRequest(uri, context.token()); return Mapper.getObjectFromString(responseBody, ResolvedAlias.class); } @@ -96,7 +96,7 @@ public void setAlias(RoomAlias roomAlias, RoomID roomId) { Map map = new HashMap<>(); map.put(ROOM_ID, roomId); - httpTransport.putEvent(uri, Mapper.createObjectFromMap(map), context.token()); + httpTransport.putRequest(uri, Mapper.createObjectFromMap(map), context.token()); } @Override @@ -106,14 +106,14 @@ public void deleteAlias(RoomAlias roomAlias) { context.discoveryResponse().homeserver().baseUrl(), DIRECTORY_ENDPOINT_ROOM + roomAlias, null); - httpTransport.deleteEvent(uri, context.token()); + httpTransport.deleteRequest(uri, context.token()); } @Override public List getAliasesOfARoom(RoomID roomId) { String response = - httpTransport.getEvent( + httpTransport.getRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + ROOM_ENDPOINT @@ -137,7 +137,7 @@ public List getAliasesOfARoom(RoomID roomId) { @Override public List getJoinedRooms() { String response = - httpTransport.getEvent( + httpTransport.getRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + "/_matrix" @@ -155,7 +155,7 @@ public void inviteUser(RoomID roomId, RoomMembershipRequest event) { } catch (JacksonException e) { throw new MatrixIOException("Failed to parse input data", e); } - httpTransport.postEvent( + httpTransport.postRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + ROOM_ENDPOINT @@ -169,7 +169,7 @@ public void inviteUser(RoomID roomId, RoomMembershipRequest event) { public String joinByRoomIdOrAliasIfAllowed( Validator roomIdOrAlias, JoinRoomRequest request, List via) { if (Objects.requireNonNull(roomIdOrAlias) instanceof UserID) { - throw new MatrixException("Wrong format type"); + throw new IllegalArgumentException("Wrong format type"); } Map params = new HashMap<>(); @@ -185,7 +185,7 @@ public String joinByRoomIdOrAliasIfAllowed( } catch (JacksonException e) { throw new MatrixIOException("Failed to parse input data", e); } - var responseBody = httpTransport.postEvent(uri, serializedInputData, context.token()); + var responseBody = httpTransport.postRequest(uri, serializedInputData, context.token()); return Mapper.getStringValueOfAJsonKey(responseBody, ROOM_ID); } @@ -204,7 +204,7 @@ public String joinByRoomIdIfAllowed(RoomID roomId, JoinRoomRequest request, List } catch (JacksonException e) { throw new MatrixIOException("Failed to parse input data", e); } - var responseBody = httpTransport.postEvent(uri, serializedInputData, context.token()); + var responseBody = httpTransport.postRequest(uri, serializedInputData, context.token()); return Mapper.getStringValueOfAJsonKey(responseBody, ROOM_ID); } @@ -223,7 +223,7 @@ public String knockOn(Validator roomIdOrAlias, String reason, List via) map.put("reason", reason); String responseBody = - httpTransport.postEvent(uri, Mapper.createObjectFromMap(map), context.token()); + httpTransport.postRequest(uri, Mapper.createObjectFromMap(map), context.token()); try { return Mapper.getStringValueOfAJsonKey(responseBody, ROOM_ID); } catch (JacksonException e) { @@ -233,7 +233,7 @@ public String knockOn(Validator roomIdOrAlias, String reason, List via) @Override public void forget(RoomID roomId) { - httpTransport.postEvent( + httpTransport.postRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + ROOM_ENDPOINT @@ -245,7 +245,7 @@ public void forget(RoomID roomId) { @Override public void leave(RoomID roomId) { - httpTransport.postEvent( + httpTransport.postRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + ROOM_ENDPOINT + roomId + "/leave"), null, @@ -260,7 +260,7 @@ public void kick(RoomID roomId, RoomMembershipRequest event) { } catch (JacksonException e) { throw new MatrixIOException("Failed to parse input data", e); } - httpTransport.postEvent( + httpTransport.postRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + ROOM_ENDPOINT + roomId + "/kick"), serializedInputData, @@ -275,7 +275,7 @@ public void ban(RoomID roomId, RoomMembershipRequest event) { } catch (JacksonException e) { throw new MatrixIOException("Failed to parse input data", e); } - httpTransport.postEvent( + httpTransport.postRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + ROOM_ENDPOINT + roomId + "/ban"), serializedInputData, @@ -290,7 +290,7 @@ public void unban(RoomID roomId, RoomMembershipRequest event) { } catch (JacksonException e) { throw new MatrixIOException("Failed to parse input data", e); } - httpTransport.postEvent( + httpTransport.postRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + ROOM_ENDPOINT + roomId + "/unban"), responseBody, @@ -300,7 +300,7 @@ public void unban(RoomID roomId, RoomMembershipRequest event) { @Override public String getRoomDirectoryVisibilityType(RoomID roomId) { var responseBody = - httpTransport.getEvent( + httpTransport.getRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + DIRECTORY_ENDPOINT + roomId), null); @@ -312,7 +312,7 @@ public void setRoomDirectoryVisibilityType(RoomID roomId, VisibilityRoomType roo Map map = new HashMap<>(); map.put("visibility", roomType); - httpTransport.putEvent( + httpTransport.putRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + DIRECTORY_ENDPOINT + roomId), Mapper.createObjectFromMap(map), @@ -332,7 +332,7 @@ public PublicRoomDirectory getPublishedRoomDirectory( context.discoveryResponse().homeserver().baseUrl(), "/_matrix/client/v3/publicRooms", params); - var responseBody = httpTransport.getEvent(uri, context.token()); + var responseBody = httpTransport.getRequest(uri, context.token()); return Mapper.getObjectFromString(responseBody, PublicRoomDirectory.class); } @@ -342,7 +342,7 @@ public PublicRoomDirectory getPublishedRoomDirectory(PublicRoomRequest request) String serializedInputData = objectMapper.writeValueAsString(request); var responseBody = - httpTransport.postEvent( + httpTransport.postRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + "/_matrix/client/v3/publicRooms"), @@ -365,7 +365,7 @@ public RoomSummary getRoomSummary(Validator roomIdOrAlias, List via) { context.discoveryResponse().homeserver().baseUrl(), "/_matrix/client/v1/room_summary/" + roomIdOrAlias, args); - var responseBody = httpTransport.getEvent(uri, context.token()); + var responseBody = httpTransport.getRequest(uri, context.token()); return Mapper.getObjectFromString(responseBody, RoomSummary.class); } diff --git a/src/main/java/io/github/hikingc/matrixsdk/services/userdata/UserDataService.java b/src/main/java/io/github/hikingc/matrixsdk/services/userdata/UserDataService.java index 744b20d..0120359 100644 --- a/src/main/java/io/github/hikingc/matrixsdk/services/userdata/UserDataService.java +++ b/src/main/java/io/github/hikingc/matrixsdk/services/userdata/UserDataService.java @@ -44,7 +44,7 @@ public UsersFound searchUsersByTerm(@Nullable Integer limit, String searchTerm) .formatted(limitToUse, searchTerm); String responseBody = - httpTransport.postEvent( + httpTransport.postRequest( URI.create(context.discoveryResponse().homeserver().baseUrl() + USER_DIR), rawTextPayload, context.token()); @@ -54,7 +54,7 @@ public UsersFound searchUsersByTerm(@Nullable Integer limit, String searchTerm) @Override public UserProfile getUserProfile(UserID userId) { String responseBody = - httpTransport.getEvent( + httpTransport.getRequest( URI.create(context.discoveryResponse().homeserver().baseUrl() + PROFILE_DIR + userId), context.token()); return Mapper.getObjectFromString(responseBody, UserProfile.class); @@ -64,7 +64,7 @@ public UserProfile getUserProfile(UserID userId) { public String getUserProfileByProperty(UserID userId, @Nullable String keyName) { Objects.requireNonNull(keyName, "The key name must no be null"); String responseBody = - httpTransport.getEvent( + httpTransport.getRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + PROFILE_DIR @@ -82,7 +82,7 @@ public void setUserProfileProperty(UserID userId, String keyName, String valueNa Objects.requireNonNull(valueName, "The value name must no be null"); var serializedJson = Mapper.createObjectFromMap(Map.ofEntries(Map.entry(keyName, valueName))); - httpTransport.putEvent( + httpTransport.putRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + PROFILE_DIR @@ -97,7 +97,7 @@ public void setUserProfileProperty(UserID userId, String keyName, String valueNa public void deleteUserProfileProperty(UserID userId, String keyName) { Objects.requireNonNull(keyName, "The key name must no be null"); - httpTransport.deleteEvent( + httpTransport.deleteRequest( URI.create( context.discoveryResponse().homeserver().baseUrl() + PROFILE_DIR diff --git a/src/main/java/io/github/hikingc/matrixsdk/services/utils/HttpTransport.java b/src/main/java/io/github/hikingc/matrixsdk/services/utils/HttpTransport.java index 46bf423..3b30155 100644 --- a/src/main/java/io/github/hikingc/matrixsdk/services/utils/HttpTransport.java +++ b/src/main/java/io/github/hikingc/matrixsdk/services/utils/HttpTransport.java @@ -19,6 +19,7 @@ import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import tools.jackson.core.exc.StreamReadException; @@ -31,7 +32,7 @@ /// Failed requests are validated against the server's response and throw [MatrixNetworkException], /// populated with the HTTP status code and any error message returned by the server, and /// [MatrixIOException] if the server JSON response wasn't even sent. -@Nullable +@NullMarked public class HttpTransport { private static final String CONTENT_TYPE = "Content-Type"; private static final String APPLICATION_JSON = "application/json"; @@ -46,6 +47,12 @@ public HttpTransport(int timeOut) { client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(timeOut)).build(); } + /// Handles return code validation from Matrix servers. + /// + /// @param code an HTTP Code. + /// @param body the response body from the server. + /// @throws MatrixIOException if an I/O error has occurred while parsing the response. + /// @throws MatrixNetworkException when the server responds with an unsuccessful HTTP Code. private void validateResponse(int code, String body) { if (code >= 200 && code < 300) { return; @@ -74,8 +81,10 @@ private void validateResponse(int code, String body) { /// @param path the [URI] of the endpoint to `GET`. /// @param authToken if supplied, the `Bearer` token. /// @return a JSON [String]. - /// @throws IllegalArgumentException if the path was not supplied - public String getEvent(URI path, String authToken) { + /// @throws MatrixIOException if an I/O error has occurred while sending the request. + /// @throws MatrixNetworkException if the operation has been interrupted. + /// @throws IllegalArgumentException if the path was not supplied. + public String getRequest(URI path, @Nullable String authToken) { var builderRequest = HttpRequest.newBuilder().uri(path).header(CONTENT_TYPE, APPLICATION_JSON).GET(); @@ -104,9 +113,10 @@ public String getEvent(URI path, String authToken) { /// @param body a JSON [String]. /// @param authToken if supplied, the `Bearer` token. /// @return a JSON [String]. - /// @throws MatrixIOException if an I/O error has occurred while sending the request - /// @throws MatrixNetworkException if the path was not supplied - public String postEvent(URI path, String body, String authToken) { + /// @throws MatrixIOException if an I/O error has occurred while sending the request. + /// @throws MatrixNetworkException if the operation has been interrupted. + /// @throws IllegalArgumentException if the path was not supplied. + public String postRequest(URI path, @Nullable String body, @Nullable String authToken) { var builderRequest = HttpRequest.newBuilder().uri(path); if (body != null) { @@ -139,12 +149,15 @@ public String postEvent(URI path, String body, String authToken) { /// Sends a `POST` request to the given endpoint. /// + /// This endpoint is exclusively used for Authentication workflows with OAuth 2.0. + /// /// @param path the [URI] of the endpoint to query. /// @param body a JSON [String]. /// @return a JSON [String]. - /// @throws MatrixIOException if an I/O error has occurred while sending the request - /// @throws MatrixNetworkException if the path was not supplied - public String postEventAuth(URI path, String body) { + /// @throws MatrixIOException if an I/O error has occurred while sending the request. + /// @throws MatrixNetworkException if the operation has been interrupted. + /// @throws IllegalArgumentException if the path was not supplied. + public String postAuth(URI path, @Nullable String body) { var builderRequest = HttpRequest.newBuilder().uri(path); builderRequest.header(CONTENT_TYPE, "application/x-www-form-urlencoded"); @@ -176,10 +189,10 @@ public String postEventAuth(URI path, String body) { /// @param body a JSON [String] /// @param authToken if supplied, the `Bearer` token. /// @return a JSON [String] when the operation is successful. - /// @throws MatrixIOException if an I/O error has occurred while sending the request - /// @throws MatrixNetworkException if the operation has been interrupted - /// @throws IllegalArgumentException if the path was not supplied - public String putEvent(URI path, String body, String authToken) { + /// @throws MatrixIOException if an I/O error has occurred while sending the request. + /// @throws MatrixNetworkException if the operation has been interrupted. + /// @throws IllegalArgumentException if the path was not supplied. + public String putRequest(URI path, @Nullable String body, String authToken) { var builderRequest = HttpRequest.newBuilder() @@ -206,7 +219,9 @@ public String putEvent(URI path, String body, String authToken) { return response.body(); } - /// Sends a `PUT` request to the given endpoint. + /// Sends a `PUT` request to the given endpoint to upload a resource. + /// + /// The [#CONTENT_TYPE] will be generated using [Files#probeContentType(Path)] /// /// @param path the [URI] of the endpoint to query. /// @param resource a [Path] pointing to the resource to be uploaded. @@ -249,10 +264,10 @@ public String putResource(URI path, Path resource, String authToken) { /// @param path the [URI] of the endpoint to query. /// @param authToken if supplied, the `Bearer` token. /// @return a JSON [String]. - /// @throws MatrixIOException if an I/O error has occurred while sending the request - /// @throws MatrixNetworkException if the operation has been interrupted - /// @throws IllegalArgumentException if the path was not supplied - public String deleteEvent(URI path, String authToken) { + /// @throws MatrixIOException if an I/O error has occurred while sending the request. + /// @throws MatrixNetworkException if the operation has been interrupted. + /// @throws IllegalArgumentException if the path was not supplied. + public String deleteRequest(URI path, String authToken) { HttpRequest deleteRequest = HttpRequest.newBuilder() .uri(path) @@ -276,8 +291,8 @@ public String deleteEvent(URI path, String authToken) { /// URL-encodes a string using UTF-8. /// - /// @param value the string to encode - /// @return the URL-encoded string + /// @param value the string to encode. + /// @return the URL-encoded string. private String encode(String value) { return URLEncoder.encode(value, StandardCharsets.UTF_8); } @@ -289,8 +304,8 @@ private String encode(String value) { /// @param path the path, for example: `/_matrix/client/v3/join/!room:example.org` /// @param params query parameters; accepts wrapped primitives and Lists for repeated parameters. /// Null values, null list items, or a null/empty map are all safely ignored. - /// @return a safe, fully composed [URI] - public URI generateEncodedURI(String baseUrl, String path, Map params) { + /// @return a safe, fully composed [URI]. + public URI generateEncodedURI(String baseUrl, String path, @Nullable Map params) { String query = encodeQueryParams(params); try { URI base = URI.create(baseUrl); @@ -308,7 +323,7 @@ public URI generateEncodedURI(String baseUrl, String path, Map p /// @param path the path, for example: `/_matrix/client/v3/join/!room:example.org` /// @param params query parameters; accepts wrapped primitives and Lists for repeated parameters. /// Null values, null list items, or a null/empty map are all safely ignored. - /// @return a safe, fully composed [URI] + /// @return a safe, fully composed [URI]. public URI generateRawURI(String baseUrl, String path, Map params) { String query = rawQueryParams(params); try { @@ -320,7 +335,7 @@ public URI generateRawURI(String baseUrl, String path, Map param } } - private String encodeQueryParams(Map params) { + private String encodeQueryParams(@Nullable Map params) { if (params == null || params.isEmpty()) return ""; return params.entrySet().stream() .filter(e -> e.getValue() != null) @@ -329,7 +344,7 @@ private String encodeQueryParams(Map params) { .collect(Collectors.joining("&")); } - private String rawQueryParams(Map params) { + private String rawQueryParams(@Nullable Map params) { if (params == null || params.isEmpty()) return ""; return params.entrySet().stream() .filter(e -> e.getValue() != null) diff --git a/src/main/java/io/github/hikingc/matrixsdk/services/utils/Mapper.java b/src/main/java/io/github/hikingc/matrixsdk/services/utils/Mapper.java index c28b2e7..ca09421 100644 --- a/src/main/java/io/github/hikingc/matrixsdk/services/utils/Mapper.java +++ b/src/main/java/io/github/hikingc/matrixsdk/services/utils/Mapper.java @@ -40,6 +40,7 @@ private static ObjectMapper buildMapper() { /// @param json a JSON [String]. /// @param key the key of the JSON Object. /// @return the corresponding value. + /// @throws MatrixIOException when the key was not in the response public static String getStringValueOfAJsonKey(String json, String key) { JsonNode tree = INSTANCE.readTree(json); if (tree == null || tree.isMissingNode()) { @@ -57,6 +58,7 @@ public static String getStringValueOfAJsonKey(String json, String key) { /// @param elementType the [Class] to deserialize each element into. /// @param the type to deserialize each element into /// @return the deserialized [List] of values for the given key + /// @throws MatrixIOException when the key was not in the response or the key value was not an Array public static List getListFromAJsonKey(String json, String key, Class elementType) { JsonNode tree = INSTANCE.readTree(json); JsonNode value = tree.get(key); @@ -107,7 +109,7 @@ public static String createObjectFromMap(@Nullable Map /// @param type the target class to deserialize into /// @param the [Class] type to deserialize into /// @return the deserialized [Object] - /// @throws MatrixIOException if the JSON cannot be parsed into the target type + /// @throws MatrixIOException if the response cannot be parsed into the target type. public static T getObjectFromString(@Nullable String responseBody, @Nullable Class type) { if (responseBody == null || type == null) { throw new IllegalArgumentException("responseBody and type must not be null"); @@ -128,7 +130,7 @@ public static T getObjectFromString(@Nullable String responseBody, @Nullable /// @param type a [TypeReference] /// @param the [Class] type to deserialize into /// @return the deserialized [Object] - /// @throws MatrixIOException if the JSON cannot be parsed into the target type + /// @throws MatrixIOException if the response cannot be parsed into the target type public static T getObjectFromString( @Nullable String responseBody, @Nullable TypeReference type) { if (responseBody == null || type == null) {