Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions src/main/java/io/github/hikingc/matrixsdk/api/Event.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import io.github.hikingc.matrixsdk.api.events.*;
import io.github.hikingc.matrixsdk.api.events.content.MessageEventContent;
import io.github.hikingc.matrixsdk.api.events.content.StateEventContent;
import io.github.hikingc.matrixsdk.api.events.model.RoomMemberEvent;
import io.github.hikingc.matrixsdk.api.events.queries.ChronologicalDirection;
import io.github.hikingc.matrixsdk.api.events.queries.Membership;
import io.github.hikingc.matrixsdk.api.events.queries.QueryParametersMessages;
Expand All @@ -12,6 +11,7 @@
import io.github.hikingc.matrixsdk.api.identifiers.RoomID;
import io.github.hikingc.matrixsdk.exceptions.MatrixIOException;
import io.github.hikingc.matrixsdk.exceptions.MatrixNetworkException;
import io.github.hikingc.matrixsdk.api.events.model.RoomMemberEvent;
import java.nio.file.Path;
import java.util.List;

Expand All @@ -33,9 +33,8 @@ 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);
@SuppressWarnings("java:S1452")
ClientEvent<?> getEvent(RoomID roomId, String eventId);

/// Returns currently-joined members
///
Expand Down Expand Up @@ -79,9 +78,7 @@ List<RoomMemberEvent> getMembers(
/// @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
ClientEvent<?> getStateEvent(RoomID roomId, String eventType, String stateKey);
StateEvent<?> getStateEvent(RoomID roomId, String eventType, String stateKey);

/// Returns a list of message and state events for a room. It uses pagination query parameters to
/// paginate history in the room. The content is not parsed or escaped which means newlines (`\n`)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package io.github.hikingc.matrixsdk.api.identifiers;

import java.nio.charset.StandardCharsets;
import java.util.Objects;

/// This class allows for the representation and validation of an Event ID in Matrix.
///
/// Their form is as follows: `$opaque_id`, some room versions include a `domain` component, whereas
/// more recent room versions omit the domain and use a base64-encoded hash instead.
///
/// The length of a [EventID], including the `$` sigil, **MUST NOT** exceed 255 bytes.
///
/// @see <a href="https://spec.matrix.org/v1.19/appendices/#event-ids">Event Identifiers as defined
/// in the specification</a>
public final class EventID implements Validator {
private final String opaqueId;

private EventID(String opaqueId) {
this.opaqueId = opaqueId;
}

/// Builds and validates a [RoomID]
///
/// @param rawRoomId the [String] to validate.
/// @return a [RoomID].
/// @throws IllegalArgumentException if the [String] has broken a rule from the spec.
/// @throws NullPointerException if the [String] is null.
public static EventID parse(String rawRoomId) {
Objects.requireNonNull(rawRoomId, "Room ID" + " must not be null");

if (rawRoomId.getBytes(StandardCharsets.UTF_8).length > MAX_BYTES) {
throw new IllegalArgumentException("Event ID exceeds " + MAX_BYTES + " bytes");
}

if (rawRoomId.isEmpty()) {
throw new IllegalArgumentException("Event ID must not be empty");
}

if (rawRoomId.charAt(0) != '$') {
throw new IllegalArgumentException("Event ID must start with '$'");
}

if (rawRoomId.contentEquals("$")) {
throw new IllegalArgumentException("Event ID must not only contain '$'");
}

return new EventID(rawRoomId);
}

@Override
public int hashCode() {
return Objects.hash(opaqueId);
}

@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (EventID) obj;
return Objects.equals(this.opaqueId, that.opaqueId);
}

@Override
public String toString() {
return "$" + opaqueId;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.hikingc.matrixsdk.api.identifiers;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import java.util.Objects;

/// This class allows for the representation and validation of a Room Alias in Matrix.
Expand Down Expand Up @@ -32,6 +34,7 @@ private RoomAlias(String opaqueId, String domain) {
/// @return a [RoomAlias].
/// @throws IllegalArgumentException if the [String] has broken a rule from the spec.
/// @throws NullPointerException if the [String] is null.
@JsonCreator
public static RoomAlias parse(String rawAliasId) {
Objects.requireNonNull(rawAliasId, "Alias ID" + " must not be null");

Expand All @@ -58,6 +61,7 @@ public boolean equals(Object obj) {
}

@Override
@JsonValue
public String toString() {
return "#" + opaqueId + ":" + domain;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.hikingc.matrixsdk.api.identifiers;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import java.util.Objects;

/// This class allows for the representation and validation of a Room Identifier in Matrix.
Expand Down Expand Up @@ -32,6 +34,7 @@ private RoomID(String opaqueId, String domain) {
/// @return a [RoomID].
/// @throws IllegalArgumentException if the [String] has broken a rule from the spec.
/// @throws NullPointerException if the [String] is null.
@JsonCreator
public static RoomID parse(String rawRoomId) {
Objects.requireNonNull(rawRoomId, "Room ID" + " must not be null");

Expand All @@ -58,6 +61,7 @@ public boolean equals(Object obj) {
}

@Override
@JsonValue
public String toString() {
return "!" + opaqueId + ":" + domain;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.hikingc.matrixsdk.api.identifiers;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import java.util.Objects;

/// This class allows for the representation and validation of a User Identifier in Matrix.
Expand Down Expand Up @@ -30,6 +32,7 @@ private UserID(String opaqueId, String domain) {
/// @return a [UserID].
/// @throws IllegalArgumentException if the [String] has broken a rule from the spec.
/// @throws NullPointerException if the [String] is null.
@JsonCreator
public static UserID parse(String rawUserId) {
Objects.requireNonNull(rawUserId, "User ID" + " must not be null");

Expand Down Expand Up @@ -61,7 +64,8 @@ public boolean equals(Object obj) {
}

@Override
@JsonValue
public String toString() {
return "!" + localpart + ":" + domain;
return "@" + localpart + ":" + domain;
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package io.github.hikingc.matrixsdk.api.rooms;

import io.github.hikingc.matrixsdk.api.identifiers.UserID;

import java.util.Objects;

/// This record represents the required values to be supplied to actions like banning or kicking.
///
/// @param reason The reason of the expulsion, the target will receive this message.
/// @param userId The id of the target to expel.
public record RoomMembershipRequest(String reason, String userId) {
public record RoomMembershipRequest(String reason, UserID userId) {

/// Compact constructor designed to validate nullity.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Map;

import io.github.hikingc.matrixsdk.api.identifiers.UserID;
import org.jspecify.annotations.NullMarked;

/// Holds information to supply the server and verify a `m.room.third_party_invite` event.
Expand All @@ -12,7 +14,7 @@
/// @param token the state key of the `m.third_party_invite` event.
@NullMarked
public record ThirdPartySigned(
@JsonProperty(required = true) String mxid,
@JsonProperty(required = true) String sender,
@JsonProperty(required = true) UserID mxid,
@JsonProperty(required = true) UserID sender,
@JsonProperty(required = true) Map<String, Map<String, String>> signatures,
@JsonProperty(required = true) String token) {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.fasterxml.jackson.annotation.JsonProperty;
import io.github.hikingc.matrixsdk.api.Room;
import io.github.hikingc.matrixsdk.api.identifiers.RoomAlias;
import io.github.hikingc.matrixsdk.api.identifiers.RoomID;
import io.github.hikingc.matrixsdk.api.identifiers.Validator;
import java.net.URI;
import java.util.List;
Expand All @@ -25,12 +27,12 @@
/// additional values for a determinate room
public record PublishedRoomsChunk(
URI avatarUrl,
String canonicalAlias,
RoomAlias canonicalAlias,
@JsonProperty(required = true) boolean guestCanJoin,
String joinRule,
String name,
@JsonProperty(required = true) int numJoinedMembers,
@NonNull @JsonProperty(required = true) String roomId,
@NonNull @JsonProperty(required = true) RoomID roomId,
String roomType,
String topic,
@JsonProperty(required = true) boolean worldReadable) {}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package io.github.hikingc.matrixsdk.api.rooms.models;

import io.github.hikingc.matrixsdk.api.identifiers.RoomID;
import java.util.List;

/// This record contains data when resolving a room alias.
///
/// @param roomId the room id for the room alias.
/// @param servers a list of servers aware of said alias.
public record ResolvedAlias(String roomId, List<String> servers) {}
public record ResolvedAlias(RoomID roomId, List<String> servers) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package io.github.hikingc.matrixsdk.api.identifiers;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EmptySource;
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;

class EventIDTest {

@ParameterizedTest(name = "[{index}] \"{0}\"")
@ValueSource(
strings = {
// v3+ reference-hash shape
"$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI9dSaz3jRoiQ-fXE",
"$acR1l0raRc2h8DzKlR4E9RAxwbrIY8v_4V-1kfBGCiA",
"$LWXstUyAjMr8vBiVjTMH_hEcnKMhc0zVi52gxHYzc-4",
"$1c-AYXvOG3AH0z9OTfHktZ4b6l3f1uK1Wv4h5CkQY9U",
// legacy v1/v2 shape
"$acR1l0raRc2h8DzKlR4E9RAxwbrIY8v_4V-1kfBGCiA:matrix.org",
"$event1:example.com",
"$143273582443PhrSn:example.org",
// opaque content is allowed to contain "unusual" characters per spec —
// clients must not impose structure beyond the sigil
"$acR1l0raRc2h8DzKlR4E9RAxwbrIY8v/4V+1kfBGCiA", // non-base64url chars, still opaque
"$has spaces in it",
"$has\nnewline",
"$emoji🎉event",
"$has\"quote",
"$ " // single space after sigil is still non-empty content
})
void withValidStrings_ReturnEventID(String id) {
assertDoesNotThrow(() -> EventID.parse(id), "Exception not expected for input: " + id);
}

@ParameterizedTest(name = "[{index}] \"{0}\"")
@ValueSource(
strings = {
"acR1l0raRc2h8DzKlR4E9RAxwbrIY8v_4V-1kfBGCiA", // missing sigil
"@acR1l0raRc2h8DzKlR4E9RAxwbrIY8v_4V-1kfBGCiA", // wrong sigil (User ID)
"!acR1l0raRc2h8DzKlR4E9RAxwbrIY8v_4V-1kfBGCiA", // wrong sigil (Room ID)
"#acR1l0raRc2h8DzKlR4E9RAxwbrIY8v_4V-1kfBGCiA", // wrong sigil (Room Alias)
" ", // no sigil at all, just whitespace
"$" // sigil present but zero content after it
})
void withInvalidStrings_ThrowsException(String id) {
assertThrows(
IllegalArgumentException.class,
() -> EventID.parse(id),
"Exception expected for input: " + id);
}

@ParameterizedTest
@NullSource
void withNull_ThrowsException(String id) {
assertThrows(NullPointerException.class, () -> EventID.parse(id));
}

@ParameterizedTest
@EmptySource
void withEmpty_ThrowsException(String id) {
assertThrows(IllegalArgumentException.class, () -> EventID.parse(id));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
import io.github.hikingc.matrixsdk.api.MatrixClient;
import io.github.hikingc.matrixsdk.api.events.*;
import io.github.hikingc.matrixsdk.api.events.content.RoomJoinRules;
import io.github.hikingc.matrixsdk.api.events.content.RoomMessage;
import io.github.hikingc.matrixsdk.api.events.content.StateEventContent;
import io.github.hikingc.matrixsdk.api.events.content.roommessages.FileContent;
import io.github.hikingc.matrixsdk.api.events.content.roommessages.TextContent;
import io.github.hikingc.matrixsdk.api.events.queries.ChronologicalDirection;
Expand Down Expand Up @@ -538,7 +540,9 @@ void getInitialSync_WithACorrectPayload_ThenReturnRoomInfo() {

@Test
void sendStateEvent_WithACorrectPayload_ThenReturnAString() {
// TODO pending interface to confirm fields to assert
StateEventContent content = new RoomJoinRules(new RoomJoinRules.AllowCondition("EXAMPLE","TYPE"),"JOINRULE");
var response = client.events().sendStateEvent(ROOM_ID,"",content);
assertThat(response).isNotNull();
}

@Test
Expand Down
Loading
Loading