diff --git a/slack-api-model/src/main/java/com/slack/api/model/event/UserConnectionEvent.java b/slack-api-model/src/main/java/com/slack/api/model/event/UserConnectionEvent.java new file mode 100644 index 000000000..41e8bd20a --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/event/UserConnectionEvent.java @@ -0,0 +1,23 @@ +package com.slack.api.model.event; + +import lombok.Data; + +/** + * Slack sends the user_connection event when a user clicks Connect or Manage on an app with user connections. + * The subtype is either connect or disconnect. For connect, open the connection modal with the + * apps.user.connection.requestAuth API method and the trigger_id. + *

+ * https://docs.slack.dev/reference/events/user_connection + */ +@Data +public class UserConnectionEvent implements Event { + + public static final String TYPE_NAME = "user_connection"; + + private final String type = TYPE_NAME; + private String subtype; + private String user; + private String triggerId; + private String enterpriseId; + private String eventTs; +} diff --git a/slack-api-model/src/main/java/com/slack/api/model/event/UserConnectionStatusRequestedEvent.java b/slack-api-model/src/main/java/com/slack/api/model/event/UserConnectionStatusRequestedEvent.java new file mode 100644 index 000000000..22938c54c --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/event/UserConnectionStatusRequestedEvent.java @@ -0,0 +1,19 @@ +package com.slack.api.model.event; + +import lombok.Data; + +/** + * Slack sends the user_connection_status_requested event when it has no connection status for a user. + * Answer it with the apps.user.connection.update API method. + *

+ * https://docs.slack.dev/reference/events/user_connection_status_requested + */ +@Data +public class UserConnectionStatusRequestedEvent implements Event { + + public static final String TYPE_NAME = "user_connection_status_requested"; + + private final String type = TYPE_NAME; + private String user; + private String eventTs; +} diff --git a/slack-api-model/src/test/java/test_locally/api/model/event/UserConnectionEventTest.java b/slack-api-model/src/test/java/test_locally/api/model/event/UserConnectionEventTest.java new file mode 100644 index 000000000..a0e66ae31 --- /dev/null +++ b/slack-api-model/src/test/java/test_locally/api/model/event/UserConnectionEventTest.java @@ -0,0 +1,54 @@ +package test_locally.api.model.event; + +import com.google.gson.Gson; +import com.slack.api.model.event.UserConnectionEvent; +import org.junit.Test; +import test_locally.unit.GsonFactory; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +public class UserConnectionEventTest { + + @Test + public void typeName() { + assertThat(UserConnectionEvent.TYPE_NAME, is("user_connection")); + } + + @Test + public void deserializeConnect() { + String json = "{\"type\":\"user_connection\",\"subtype\":\"connect\",\"user\":\"U123ABC456\"," + + "\"trigger_id\":\"1293638028594.1249184885746.0d121a0e01d2e7a795ecc7a62880a406\"," + + "\"event_ts\":\"1764264284.841251\"}"; + UserConnectionEvent event = GsonFactory.createSnakeCase().fromJson(json, UserConnectionEvent.class); + assertThat(event.getType(), is("user_connection")); + assertThat(event.getSubtype(), is("connect")); + assertThat(event.getUser(), is("U123ABC456")); + assertThat(event.getTriggerId(), is("1293638028594.1249184885746.0d121a0e01d2e7a795ecc7a62880a406")); + assertThat(event.getEnterpriseId(), is(nullValue())); + assertThat(event.getEventTs(), is("1764264284.841251")); + } + + @Test + public void deserializeDisconnect() { + String json = "{\"type\":\"user_connection\",\"subtype\":\"disconnect\",\"user\":\"U123ABC456\"," + + "\"enterprise_id\":\"E123ABC456\",\"event_ts\":\"1764264317.061589\"}"; + UserConnectionEvent event = GsonFactory.createSnakeCase().fromJson(json, UserConnectionEvent.class); + assertThat(event.getSubtype(), is("disconnect")); + assertThat(event.getUser(), is("U123ABC456")); + assertThat(event.getTriggerId(), is(nullValue())); + assertThat(event.getEnterpriseId(), is("E123ABC456")); + assertThat(event.getEventTs(), is("1764264317.061589")); + } + + @Test + public void serialize() { + Gson gson = GsonFactory.createSnakeCase(); + UserConnectionEvent event = new UserConnectionEvent(); + event.setUser("U123ABC456"); + String generatedJson = gson.toJson(event); + String expectedJson = "{\"type\":\"user_connection\",\"user\":\"U123ABC456\"}"; + assertThat(generatedJson, is(expectedJson)); + } +} diff --git a/slack-api-model/src/test/java/test_locally/api/model/event/UserConnectionStatusRequestedEventTest.java b/slack-api-model/src/test/java/test_locally/api/model/event/UserConnectionStatusRequestedEventTest.java new file mode 100644 index 000000000..6c6e3fec9 --- /dev/null +++ b/slack-api-model/src/test/java/test_locally/api/model/event/UserConnectionStatusRequestedEventTest.java @@ -0,0 +1,37 @@ +package test_locally.api.model.event; + +import com.google.gson.Gson; +import com.slack.api.model.event.UserConnectionStatusRequestedEvent; +import org.junit.Test; +import test_locally.unit.GsonFactory; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class UserConnectionStatusRequestedEventTest { + + @Test + public void typeName() { + assertThat(UserConnectionStatusRequestedEvent.TYPE_NAME, is("user_connection_status_requested")); + } + + @Test + public void deserialize() { + String json = "{\"type\":\"user_connection_status_requested\",\"user\":\"U123ABC456\",\"event_ts\":\"1764264284.841251\"}"; + UserConnectionStatusRequestedEvent event = + GsonFactory.createSnakeCase().fromJson(json, UserConnectionStatusRequestedEvent.class); + assertThat(event.getType(), is("user_connection_status_requested")); + assertThat(event.getUser(), is("U123ABC456")); + assertThat(event.getEventTs(), is("1764264284.841251")); + } + + @Test + public void serialize() { + Gson gson = GsonFactory.createSnakeCase(); + UserConnectionStatusRequestedEvent event = new UserConnectionStatusRequestedEvent(); + event.setUser("U123ABC456"); + String generatedJson = gson.toJson(event); + String expectedJson = "{\"type\":\"user_connection_status_requested\",\"user\":\"U123ABC456\"}"; + assertThat(generatedJson, is(expectedJson)); + } +} diff --git a/slack-app-backend/src/main/java/com/slack/api/app_backend/events/handler/UserConnectionHandler.java b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/handler/UserConnectionHandler.java new file mode 100644 index 000000000..9034facfa --- /dev/null +++ b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/handler/UserConnectionHandler.java @@ -0,0 +1,13 @@ +package com.slack.api.app_backend.events.handler; + +import com.slack.api.app_backend.events.EventHandler; +import com.slack.api.app_backend.events.payload.UserConnectionPayload; +import com.slack.api.model.event.UserConnectionEvent; + +public abstract class UserConnectionHandler extends EventHandler { + + @Override + public String getEventType() { + return UserConnectionEvent.TYPE_NAME; + } +} diff --git a/slack-app-backend/src/main/java/com/slack/api/app_backend/events/handler/UserConnectionStatusRequestedHandler.java b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/handler/UserConnectionStatusRequestedHandler.java new file mode 100644 index 000000000..54792dbba --- /dev/null +++ b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/handler/UserConnectionStatusRequestedHandler.java @@ -0,0 +1,13 @@ +package com.slack.api.app_backend.events.handler; + +import com.slack.api.app_backend.events.EventHandler; +import com.slack.api.app_backend.events.payload.UserConnectionStatusRequestedPayload; +import com.slack.api.model.event.UserConnectionStatusRequestedEvent; + +public abstract class UserConnectionStatusRequestedHandler extends EventHandler { + + @Override + public String getEventType() { + return UserConnectionStatusRequestedEvent.TYPE_NAME; + } +} diff --git a/slack-app-backend/src/main/java/com/slack/api/app_backend/events/payload/UserConnectionPayload.java b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/payload/UserConnectionPayload.java new file mode 100644 index 000000000..cd4bf22c1 --- /dev/null +++ b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/payload/UserConnectionPayload.java @@ -0,0 +1,25 @@ +package com.slack.api.app_backend.events.payload; + +import com.slack.api.model.event.UserConnectionEvent; +import lombok.Data; + +import java.util.List; + +@Data +public class UserConnectionPayload implements EventsApiPayload { + + private String token; + private String enterpriseId; + private String teamId; + private String apiAppId; + private String type; + private List authedUsers; + private List authedTeams; + private List authorizations; + private boolean isExtSharedChannel; + private String eventId; + private Integer eventTime; + private String eventContext; + + private UserConnectionEvent event; +} diff --git a/slack-app-backend/src/main/java/com/slack/api/app_backend/events/payload/UserConnectionStatusRequestedPayload.java b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/payload/UserConnectionStatusRequestedPayload.java new file mode 100644 index 000000000..ad3cd8cae --- /dev/null +++ b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/payload/UserConnectionStatusRequestedPayload.java @@ -0,0 +1,25 @@ +package com.slack.api.app_backend.events.payload; + +import com.slack.api.model.event.UserConnectionStatusRequestedEvent; +import lombok.Data; + +import java.util.List; + +@Data +public class UserConnectionStatusRequestedPayload implements EventsApiPayload { + + private String token; + private String enterpriseId; + private String teamId; + private String apiAppId; + private String type; + private List authedUsers; + private List authedTeams; + private List authorizations; + private boolean isExtSharedChannel; + private String eventId; + private Integer eventTime; + private String eventContext; + + private UserConnectionStatusRequestedEvent event; +}