From ce1655aab1da9f57ec5f494a6634d7fe2f13c47e Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 26 Feb 2026 12:29:22 -0500 Subject: [PATCH 1/3] chore: add AGENTS.md --- .claude/.gitignore | 4 + .claude/CLAUDE.md | 1 + .claude/settings.json | 26 ++ AGENTS.md | 637 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 668 insertions(+) create mode 100644 .claude/.gitignore create mode 100644 .claude/CLAUDE.md create mode 100644 .claude/settings.json create mode 100644 AGENTS.md diff --git a/.claude/.gitignore b/.claude/.gitignore new file mode 100644 index 000000000..3a2f7f6a1 --- /dev/null +++ b/.claude/.gitignore @@ -0,0 +1,4 @@ +CLAUDE.local.md +settings.local.json +worktrees/ +plans/ diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 000000000..dba71e970 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1 @@ +@../AGENTS.md diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 000000000..4792a1717 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,26 @@ +{ + "permissions": { + "allow": [ + "Bash(gh issue view:*)", + "Bash(gh label list:*)", + "Bash(gh pr checks:*)", + "Bash(gh pr diff:*)", + "Bash(gh pr list:*)", + "Bash(gh pr status:*)", + "Bash(gh pr update-branch:*)", + "Bash(gh pr view:*)", + "Bash(gh search code:*)", + "Bash(git diff:*)", + "Bash(git grep:*)", + "Bash(git log:*)", + "Bash(git show:*)", + "Bash(git status:*)", + "Bash(grep:*)", + "Bash(ls:*)", + "Bash(tree:*)", + "WebFetch(domain:github.com)", + "WebFetch(domain:docs.slack.dev)", + "WebFetch(domain:central.sonatype.com)" + ] + } +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..5a333b743 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,637 @@ +# AGENTS.md - java-slack-sdk + +## Project Overview + +This is the **Slack SDK for Java** (`com.slack.api`), a multi-module Maven project providing Java/JVM libraries for building Slack apps and calling Slack APIs. The repository is at https://github.com/slackapi/java-slack-sdk and the documentation lives at https://docs.slack.dev/tools/java-slack-sdk. + +There are two main components: + +- **Bolt for Java**: A framework for building modern Slack apps with a simple listener-based API +- **Slack API Client**: Low-level HTTP clients for all Slack APIs (Web API, Socket Mode, SCIM, Audit Logs) + +## Build System + +- **Build tool**: Apache Maven via Maven Wrapper (`./mvnw`) +- **Java target**: Source and target compatibility is Java 1.8 (OpenJDK 8+) +- **Group ID**: `com.slack.api` +- **Current version**: Check `` in root `pom.xml` + +### Key Commands + +```bash +# Run CI tests (local-only tests, no Slack API access needed) +./scripts/run_no_prep_tests.sh + +# Install all modules locally (skips tests) +./scripts/install_local.sh + +# Run all tests (requires Slack API tokens - see .github/maintainers_guide.md) +./mvnw test + +# Check for dependency updates +./scripts/check_dependency_updates.sh + +# Check for duplicate classes in JARs +./mvnw install -Dmaven.test.skip=true -Dmaven.javadoc.skip=true && ./mvnw duplicate-finder:check + +# Set project version across all modules +./scripts/set_version.sh # e.g., 1.48.0 or 1.48.0-SNAPSHOT +``` + +### CI Tests + +CI runs `./scripts/run_no_prep_tests.sh -ci` across JDK 8, 14, and 17. Only tests in the `test_locally` package are executed in CI: + +```text +-Dtest=test_locally.**.*Test +``` + +Some modules are excluded on JDK 8 (Jakarta EE modules, GCF, Helidon, http4k, Micronaut). JDK 17 runs the full suite. + +## Module Architecture + +All published modules use group ID `com.slack.api` and are released to Maven Central simultaneously. + +### Foundation Modules + +- **`slack-api-model`** — A collection of the classes representing the Slack core objects such as conversations, messages, users, blocks, and surfaces. All other modules depend on this. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/slack-api-model) + +- **`slack-api-client`** — The official Slack API client for Java. Provides HTTP clients for Web API, Socket Mode, SCIM, Audit Logs, and Status API. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/slack-api-client) + +- **`slack-app-backend`** — A set of Slack app server-side handlers and data classes for Events API, Interactive Components, Slash Commands, Actions, and OAuth flow. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/slack-app-backend) + +- **`slack-jakarta-socket-mode-client`** — An option to switch to Jakarta EE compatible Socket Mode client. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/slack-jakarta-socket-mode-client) + +### Kotlin Extensions + +- **`slack-api-model-kotlin-extension`** — Contains the Block Kit Kotlin DSL builder, which allows you to define block kit structures via a Kotlin-native DSL. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/slack-api-model-kotlin-extension) + +- **`slack-api-client-kotlin-extension`** — Contains extension methods for various Slack client message builders so you can seamlessly use the Block Kit Kotlin DSL directly on the Java message builders. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/slack-api-client-kotlin-extension) + +### Bolt Framework + +- **`bolt`** — The official Slack Bolt framework for Java, used for building modern Slack apps. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/bolt) + +### Bolt Socket Mode Adapters + +- **`bolt-socket-mode`** — A handy way to run Slack Bolt apps through Socket Mode connections. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/bolt-socket-mode) + +- **`bolt-jakarta-socket-mode`** — Provides WebSocket connectivity to the Slack API using Socket Mode, compatible with Jakarta EE. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/bolt-jakarta-socket-mode) + +### Bolt Servlet Adapters + +- **`bolt-servlet`** — A handy way to run Slack Bolt apps on the Java EE Servlet environments. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/bolt-servlet) + +- **`bolt-jakarta-servlet`** — A handy way to run Slack Bolt apps on the Jakarta EE Servlet environments. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/bolt-jakarta-servlet) + +### Bolt HTTP Server Adapters + +- **`bolt-jetty`** — A handy way to run Slack Bolt apps on the Java EE compatible Jetty HTTP server. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/bolt-jetty) + +- **`bolt-jakarta-jetty`** — A handy way to run Slack Bolt apps on Jakarta EE compatible Jetty HTTP server. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/bolt-jakarta-jetty) + +### Bolt Serverless Adapters + +- **`bolt-aws-lambda`** — An adapter for running Slack Bolt for Java applications on AWS Lambda. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/bolt-aws-lambda) + +- **`bolt-google-cloud-functions`** — An adapter for running Slack Bolt for Java applications on Google Cloud Functions. Requires **Java 11+**. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/bolt-google-cloud-functions) + +### Bolt Framework Adapters + +- **`bolt-micronaut`** — An adapter for running Slack Bolt for Java applications on Micronaut. Requires **Java 17+**. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/bolt-micronaut) + +- **`bolt-ktor`** — An adapter for running Slack Bolt for Java applications on Ktor. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/bolt-ktor) + +- **`bolt-http4k`** — An adapter for running Slack Bolt for Java applications on http4k. Requires **Java 11+**. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/bolt-http4k) + +- **`bolt-helidon`** — An adapter for running Slack Bolt for Java applications on Helidon. Requires **Java 11+**. + [Maven Central](https://central.sonatype.com/artifact/com.slack.api/bolt-helidon) + +### Example Modules (not published to Maven Central) + +`bolt-kotlin-examples`, `bolt-quarkus-examples`, `bolt-spring-boot-examples`, `bolt-docker-examples`, `bolt-google-cloud-functions-example` + +## Key Dependencies + +| Library | Version Property | Notes | +|---------|-----------------|-------| +| OkHttp | `okhttp.version` (4.x) | HTTP client | +| Gson | `gson.version` | JSON serialization; uses `LOWER_CASE_WITH_UNDERSCORES` field naming via `GsonFactory.createSnakeCase()` | +| SLF4J | `slf4j.version` (1.7.x) | Logging facade. **Do not upgrade to v2** - see issue #1034 | +| Lombok | `lombok.version` | Used extensively for `@Data`, `@Builder`, `@Slf4j` | +| Kotlin | `kotlin.version` (1.9.x) | API/language version pinned to 1.6 for backward compat | +| JUnit | `junit.version` (4.x) | **JUnit 4, not 5** | +| Mockito | `mockito-core.version` (<5.0) | Capped below v5 for Java 8 compatibility | +| Hamcrest | `hamcrest.version` | Used for test assertions | + +## Code Conventions + +### Java Style + +- **Java 8 compatibility is mandatory**. Do not use Java 9+ APIs (e.g., `List.of()`, `Map.of()`, `var`, `HttpClient`, text blocks). +- Lombok annotations are used pervasively: `@Data`, `@Builder`, `@Slf4j`, `@EqualsAndHashCode(callSuper = false)`. +- JSON field naming uses **snake_case** via Gson's `LOWER_CASE_WITH_UNDERSCORES` policy. Java fields are camelCase. +- Logging uses SLF4J via Lombok's `@Slf4j` (gives you a `log` field). +- Checked exceptions: API methods throw `IOException` and `SlackApiException`. + +### Naming Conventions + +- Request classes: `{MethodName}Request` with `@Data @Builder` and implementing `SlackApiRequest` +- Response classes: `{MethodName}Response` with `@Data` and implementing `SlackApiTextResponse` +- API method constants: `Methods.java` has `public static final String` for each API method (e.g., `USERS_INFO = "users.info"`) +- Test packages: `test_locally` for CI-safe tests, `test_with_remote_apis` for integration tests needing tokens + +### JSON Serialization + +- Use `GsonFactory.createSnakeCase()` for most Slack APIs (snake_case JSON keys) +- Use `GsonFactory.createCamelCase()` for SCIM APIs +- The factory is in `slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java` + +## Model Types (`slack-api-model`) + +The `slack-api-model` module contains all core data types. When adding or modifying model objects, follow these patterns. + +### Block Kit Elements + +Directory: `slack-api-model/src/main/java/com/slack/api/model/block/element/` + +All block elements extend `BlockElement` and use a `TYPE` constant: + +```java +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class DatePickerElement extends BlockElement { + public static final String TYPE = "datepicker"; + private final String type = TYPE; + + private String actionId; + private PlainTextObject placeholder; + private String initialDate; + private ConfirmationDialogObject confirm; + private Boolean focusOnLoad; +} +``` + +### Event Types + +Directory: `slack-api-model/src/main/java/com/slack/api/model/event/` + +Events implement the `Event` interface. Nested objects use inner `@Data` classes: + +```java +@Data +public class ChannelCreatedEvent implements Event { + public static final String TYPE_NAME = "channel_created"; + private final String type = TYPE_NAME; + private Channel channel; + + @Data + public static class Channel { + private String id; + private String name; + // ... + } +} +``` + +### Key conventions for model types + +- Use `@Data`, `@Builder`, `@NoArgsConstructor`, `@AllArgsConstructor`, `@EqualsAndHashCode(callSuper = false)` on concrete types. +- Define `public static final String TYPE` (or `TYPE_NAME`) and `private final String type = TYPE;`. +- Java fields are camelCase; Gson's `LOWER_CASE_WITH_UNDERSCORES` policy maps them to snake_case JSON automatically. +- Use `@SerializedName` only when the Slack API field name does not follow snake_case convention. +- Do **not** initialize `List` fields to empty collections — Slack sometimes returns errors with empty arrays, and null vs empty has semantic meaning. + +## Adding a New Slack API Method + +When Slack adds a new API method, follow this pattern. The examples below are based on a real PR that added `conversations.requestSharedInvite.approve` and `conversations.requestSharedInvite.deny`. Substitute the real method name, request fields, and response fields as needed. + +### 1. Look up the API method documentation + +Check the Slack API reference at `https://docs.slack.dev/reference/methods/{method.name}` to understand the method's request parameters, response fields, and rate limit tier. This is the source of truth for field names and types. + +### 2. Add the method constant to `Methods.java` + +File: `slack-api-client/src/main/java/com/slack/api/methods/Methods.java` + +Add a `public static final String` constant. The constant name is the API method name in `UPPER_SNAKE_CASE` with dots replaced by underscores: + +```java +public static final String CONVERSATIONS_REQUEST_SHARED_INVITE_APPROVE = "conversations.requestSharedInvite.approve"; +public static final String CONVERSATIONS_REQUEST_SHARED_INVITE_DENY = "conversations.requestSharedInvite.deny"; +``` + +### 3. Create the Request class + +File: `slack-api-client/src/main/java/com/slack/api/methods/request/conversations/request_shared_invite/ConversationsRequestSharedInviteApproveRequest.java` + +The package path mirrors the API namespace (dots become directory separators, camelCase segments become snake_case). The class implements `SlackApiRequest` and uses `@Data @Builder`: + +```java +package com.slack.api.methods.request.conversations.request_shared_invite; + +import com.slack.api.methods.SlackApiRequest; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.approve + */ +@Data +@Builder +public class ConversationsRequestSharedInviteApproveRequest implements SlackApiRequest { + + private String token; + + /** + * ID of the requested shared channel invite to approve. + */ + private String inviteId; + + /** + * Optional channel_id to which external user will be invited to. + * Will override the value on the requested invite. + */ + private String channelId; + + /** + * Optional boolean on whether the invited team will have post-only permissions in the channel. + * Will override the value on the requested invite. + */ + private Boolean isExternalLimited; + + /** + * Object describing the text to send along with the invite. + */ + private Message message; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Message { + private String text; + private boolean isOverride; + } +} +``` + +A simpler request class (for `conversations.requestSharedInvite.deny`): + +```java +package com.slack.api.methods.request.conversations.request_shared_invite; + +import com.slack.api.methods.SlackApiRequest; +import lombok.Builder; +import lombok.Data; + +/** + * https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.deny + */ +@Data +@Builder +public class ConversationsRequestSharedInviteDenyRequest implements SlackApiRequest { + + private String token; + + /** + * ID of the requested shared channel invite to deny. + */ + private String inviteId; + + /** + * Optional message explaining why the request to invite was denied. + */ + private String message; +} +``` + +### 4. Create the Response class + +File: `slack-api-client/src/main/java/com/slack/api/methods/response/conversations/request_shared_invite/ConversationsRequestSharedInviteApproveResponse.java` + +The response class implements `SlackApiTextResponse` and uses `@Data`. Every response must include the standard fields (`ok`, `warning`, `error`, `needed`, `provided`, `httpResponseHeaders`). Add any method-specific response fields from the API docs: + +```java +package com.slack.api.methods.response.conversations.request_shared_invite; + +import com.slack.api.methods.SlackApiTextResponse; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class ConversationsRequestSharedInviteApproveResponse implements SlackApiTextResponse { + + private boolean ok; + private String warning; + private String error; + private String needed; + private String provided; + private transient Map> httpResponseHeaders; + + private String inviteId; +} +``` + +### 5. Add to `MethodsClient` interface + +File: `slack-api-client/src/main/java/com/slack/api/methods/MethodsClient.java` + +Every method needs two overloads — one accepting the Request object, one accepting a `RequestConfigurator` lambda: + +```java +ConversationsRequestSharedInviteApproveResponse conversationsRequestSharedInviteApprove( + ConversationsRequestSharedInviteApproveRequest req) throws IOException, SlackApiException; + +ConversationsRequestSharedInviteApproveResponse conversationsRequestSharedInviteApprove( + RequestConfigurator req) + throws IOException, SlackApiException; +``` + +### 6. Add to `AsyncMethodsClient` interface + +File: `slack-api-client/src/main/java/com/slack/api/methods/AsyncMethodsClient.java` + +Same two overloads, but returning `CompletableFuture`: + +```java +CompletableFuture conversationsRequestSharedInviteApprove( + ConversationsRequestSharedInviteApproveRequest req); + +CompletableFuture conversationsRequestSharedInviteApprove( + RequestConfigurator req); +``` + +### 7. Implement in `MethodsClientImpl` + +File: `slack-api-client/src/main/java/com/slack/api/methods/impl/MethodsClientImpl.java` + +The sync implementation uses `postFormWithTokenAndParseResponse` to send the form, authenticate with the token, and deserialize the response: + +```java +@Override +public ConversationsRequestSharedInviteApproveResponse conversationsRequestSharedInviteApprove( + ConversationsRequestSharedInviteApproveRequest req) throws IOException, SlackApiException { + return postFormWithTokenAndParseResponse(toForm(req), + Methods.CONVERSATIONS_REQUEST_SHARED_INVITE_APPROVE, getToken(req), + ConversationsRequestSharedInviteApproveResponse.class); +} + +@Override +public ConversationsRequestSharedInviteApproveResponse conversationsRequestSharedInviteApprove( + RequestConfigurator req) + throws IOException, SlackApiException { + return conversationsRequestSharedInviteApprove( + req.configure(ConversationsRequestSharedInviteApproveRequest.builder()).build()); +} +``` + +### 8. Implement in `AsyncMethodsClientImpl` + +File: `slack-api-client/src/main/java/com/slack/api/methods/impl/AsyncMethodsClientImpl.java` + +The async implementation delegates to the sync `MethodsClientImpl` via an executor: + +```java +@Override +public CompletableFuture conversationsRequestSharedInviteApprove( + ConversationsRequestSharedInviteApproveRequest req) { + return executor.execute(CONVERSATIONS_REQUEST_SHARED_INVITE_APPROVE, toMap(req), + () -> methods.conversationsRequestSharedInviteApprove(req)); +} + +@Override +public CompletableFuture conversationsRequestSharedInviteApprove( + RequestConfigurator req) { + return conversationsRequestSharedInviteApprove( + req.configure(ConversationsRequestSharedInviteApproveRequest.builder()).build()); +} +``` + +### 9. Add form builder support in `RequestFormBuilder` + +File: `slack-api-client/src/main/java/com/slack/api/methods/RequestFormBuilder.java` + +Add a `toForm` method that maps request fields to form parameters. Use `setIfNotNull` for simple fields. For complex nested objects, serialize to JSON with `GSON.toJson()`: + +```java +public static FormBody.Builder toForm(ConversationsRequestSharedInviteApproveRequest req) { + FormBody.Builder form = new FormBody.Builder(); + setIfNotNull("invite_id", req.getInviteId(), form); + setIfNotNull("channel_id", req.getChannelId(), form); + setIfNotNull("is_external_limited", req.getIsExternalLimited(), form); + if (req.getMessage() != null) { + setIfNotNull("message", GSON.toJson(req.getMessage()), form); + } + return form; +} + +public static FormBody.Builder toForm(ConversationsRequestSharedInviteDenyRequest req) { + FormBody.Builder form = new FormBody.Builder(); + setIfNotNull("invite_id", req.getInviteId(), form); + setIfNotNull("message", req.getMessage(), form); + return form; +} +``` + +### 10. Add rate limit tier in `MethodsRateLimits` + +File: `slack-api-client/src/main/java/com/slack/api/methods/MethodsRateLimits.java` + +Look up the rate limit tier from the API docs and add a `setRateLimitTier` call in the appropriate alphabetical position: + +```java +setRateLimitTier(CONVERSATIONS_REQUEST_SHARED_INVITE_APPROVE, Tier2); +setRateLimitTier(CONVERSATIONS_REQUEST_SHARED_INVITE_DENY, Tier2); +``` + +### 11. Add rate limit entry in `rate_limit_tiers.json` + +File: `metadata/web-api/rate_limit_tiers.json` + +Add the method name and its tier in alphabetical order within the JSON object: + +```json +"conversations.requestSharedInvite.approve": "Tier2", +"conversations.requestSharedInvite.deny": "Tier2", +``` + +### 12. Add tests and update `MethodsTest` + +#### Add method-specific tests + +File: `slack-api-client/src/test/java/test_locally/api/methods/ConversationsTest.java` (or the relevant existing test file for the API category) + +Test both sync and async variants using the configurator lambda pattern: + +```java +@Test +public void conversationsRequestSharedInviteApprove() throws Exception { + assertThat(slack.methods(ValidToken).conversationsRequestSharedInviteApprove(r -> r) + .isOk(), is(true)); +} + +@Test +public void conversationsRequestSharedInviteApprove_async() throws Exception { + assertThat(slack.methodsAsync(ValidToken).conversationsRequestSharedInviteApprove(r -> r) + .get().isOk(), is(true)); +} +``` + +#### Update endpoint coverage in `MethodsTest` + +File: `slack-api-client/src/test/java/test_locally/api/MethodsTest.java` + +This test validates that every known Slack API method has a corresponding `Methods.java` constant and rate limit entry. Update the `methods` string and the endpoint count comment to include the new method names. If the method names are not already in the list, add them in the correct alphabetical position. + +### Summary checklist + +When adding a new Slack API method, make sure you have: + +- [ ] Looked up the method docs at `https://docs.slack.dev/reference/methods/{method.name}` +- [ ] Added the method constant in `Methods.java` +- [ ] Created the Request class in `request/{category}/` +- [ ] Created the Response class in `response/{category}/` +- [ ] Added two overloads in `MethodsClient.java` +- [ ] Added two overloads in `AsyncMethodsClient.java` +- [ ] Implemented both overloads in `MethodsClientImpl.java` +- [ ] Implemented both overloads in `AsyncMethodsClientImpl.java` +- [ ] Added `toForm()` in `RequestFormBuilder.java` +- [ ] Added rate limit tier in `MethodsRateLimits.java` +- [ ] Added rate limit entry in `metadata/web-api/rate_limit_tiers.json` +- [ ] Added sync and async tests in `test_locally/api/methods/` +- [ ] Updated the endpoint list and count in `MethodsTest.java` + +## Testing + +### Test Framework + +- **JUnit 4** with `@Test`, `@Before`, `@After` annotations +- **Hamcrest** matchers for assertions: `assertThat(value, is(expected))` +- **MockSlackApiServer** for HTTP-level mocking (custom mock server, not Mockito-based HTTP) +- Test methods use the configurator lambda pattern: `slack.methods(ValidToken).methodName(r -> r.field("value"))` + +### Test Organization + +```text +src/test/java/ + test_locally/ # Runs in CI - no external dependencies + api/methods/ # API client tests + api/model/ # Model serialization tests + test_with_remote_apis/ # Manual only - requires Slack workspace tokens + util/ # Test utilities (mock servers, helpers) + config/ # Test configuration +``` + +### Running Tests + +```bash +# CI tests only (recommended for development) +./scripts/run_no_prep_tests.sh + +# All Bolt framework tests +./scripts/run_all_bolt_tests.sh + +# All client tests +./scripts/run_all_client_tests.sh + +# Single module tests +./mvnw test -pl slack-api-client '-Dtest=test_locally.**.*Test' +``` + +### Code Coverage + +- CodeCov with patch target of 30% and project threshold of 1.5% +- JaCoCo plugin generates coverage reports + +## Bolt Framework Concepts + +For reference when working on `bolt` or `bolt-*` modules: + +- **App**: Central class. Listeners are registered on it via `app.command()`, `app.event()`, `app.blockAction()`, etc. +- **Acknowledgment**: All interactions must call `ctx.ack()` within 3 seconds +- **Context (`ctx`)**: Provides `ctx.client()` (pre-authenticated API client), `ctx.say()` (post to channel), `ctx.respond()` (use response_url) +- **Middleware**: Chain of handlers. Built-in middleware handles request verification, auth, SSL checks. Custom middleware can be added. +- **Listener types**: `command`, `event`, `blockAction`, `blockSuggestion`, `viewSubmission`, `viewClosed`, `globalShortcut`, `messageShortcut`, `message`, `function` + +Documentation: https://docs.slack.dev/tools/java-slack-sdk/guides/bolt-basics + +## Release Process + +Releases publish all modules simultaneously to Maven Central via Sonatype. + +- **Snapshot**: Version must end with `-SNAPSHOT`. Run `scripts/set_version.sh 1.x.y-SNAPSHOT` then `scripts/release.sh` +- **Stable**: Run `scripts/set_version.sh 1.x.y` then `scripts/release.sh`. Requires JDK 17, GPG signing, and Sonatype credentials in `~/.m2/settings.xml` +- Version is set across all `pom.xml` files AND three generated version classes: + - `slack-api-model/src/main/java/com/slack/api/meta/SlackApiModelLibraryVersion.java` + - `slack-api-client/src/main/java/com/slack/api/meta/SlackApiClientLibraryVersion.java` + - `bolt/src/main/java/com/slack/api/bolt/meta/BoltLibraryVersion.java` +- Tags follow `v{version}` convention. Semantic Versioning is used. +- `set_version.sh` requires `gnu-sed` (`gsed`) on macOS: `brew install gnu-sed` + +## Repository Governance + +- **CODEOWNERS**: `@slackapi/slack-platform-java` reviews all changes; `@slackapi/developer-education` reviews `/docs/` +- **CLA**: Contributors must sign the Salesforce Contributor License Agreement +- **Branch**: `main` is the active development branch +- **PR Template**: Categorize changes and reference related issues +- **Dependabot**: Auto-merges patch/minor dependency updates monthly + +## Common Pitfalls + +- **Do not use Java 9+ features**. The project targets Java 8. This includes `List.of()`, `Map.of()`, `var`, `Stream.toList()`, `HttpClient`, text blocks, records, sealed classes, and pattern matching. +- **Do not upgrade SLF4J to v2**. It would break users on SLF4J v1 bindings. See https://github.com/slackapi/java-slack-sdk/issues/1034. +- **Do not upgrade Mockito to v5+**. It requires Java 11+ and would break JDK 8 tests. +- **Gson, not Jackson**. This project uses Gson for JSON. Do not introduce Jackson dependencies. +- **JUnit 4, not JUnit 5**. Tests use `@Test` from `org.junit.Test`, assertions from Hamcrest, not JUnit 5's `@Test` or `Assertions`. +- **snake_case JSON, camelCase Java**. Gson's field naming policy handles the mapping. Do not use `@SerializedName` unless the API field name doesn't follow snake_case convention. +- **Two overloads per API method**. Every `MethodsClient` method needs both the direct `Request` object overload and the `RequestConfigurator` lambda overload. +- **Async variants**. Every sync method in `MethodsClient` has a corresponding async method in `AsyncMethodsClient` returning `CompletableFuture`. +- **Test placement matters**. Tests in `test_locally` run in CI. Tests in `test_with_remote_apis` require Slack API tokens and only run manually. +- **The `set_version.sh` script generates source files**. The three `*LibraryVersion.java` files are generated by the version script, not hand-edited. They will show up as changes after running the script. + +## Development Philosophy + +- **Always follow existing architecture patterns.** Before implementing anything new, study how similar features are already implemented in the codebase and replicate those patterns. The codebase has strong internal consistency — every API method follows the same structure across `Methods.java`, Request/Response classes, `MethodsClient`, `AsyncMethodsClient`, `MethodsClientImpl`, `AsyncMethodsClientImpl`, `RequestFormBuilder`, `MethodsRateLimits`, and tests. + +- **Reuse existing utilities and abstractions.** Look for existing helper methods, base classes, and shared utilities before writing new code. Key reusable patterns include: + - `setIfNotNull()` in `RequestFormBuilder` for mapping request fields to form parameters + - `postFormWithTokenAndParseResponse()` in `MethodsClientImpl` for sending authenticated requests + - `executor.execute()` in `AsyncMethodsClientImpl` for wrapping sync calls in async futures + - `GSON.toJson()` in `RequestFormBuilder` for serializing complex nested objects + - `SlackApiTextResponse` as the base interface for all API responses + +- **Match naming conventions exactly.** Follow the established naming patterns for classes, methods, packages, and constants: + - Method constants: API method name with dots replaced by underscores, in `UPPER_SNAKE_CASE` (e.g., `conversations.requestSharedInvite.approve` → `CONVERSATIONS_REQUEST_SHARED_INVITE_APPROVE`) + - Java method names: API method name in `camelCase` with dots removed (e.g., `conversationsRequestSharedInviteApprove`) + - Request/Response classes: method name in `PascalCase` + `Request`/`Response` suffix + - Package paths: API namespace segments in `snake_case` directories (e.g., `request/conversations/request_shared_invite/`) + +- **Maintain structural consistency.** New files should be placed in packages that follow the same hierarchy as existing related files. Request classes go in `request/{category}/`, response classes go in `response/{category}/`, and tests go in `test_locally/api/methods/{Category}Test.java`. + +- **When in doubt, find the nearest precedent.** If you are unsure how to implement something, find the most similar existing implementation and follow it exactly. For example, when adding a new `conversations.*` method, look at how existing `conversations.*` methods are implemented across all the files listed in the checklist above. From e0dd32aa8f2c160b8ee26c4c383b742209ffbf39 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 26 Feb 2026 13:56:06 -0500 Subject: [PATCH 2/3] Improve the aganets.md --- .claude/settings.json | 11 ++- AGENTS.md | 155 ++++++++++++++++++++++++------------------ 2 files changed, 98 insertions(+), 68 deletions(-) diff --git a/.claude/settings.json b/.claude/settings.json index 4792a1717..f4851355e 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,6 +1,13 @@ { "permissions": { "allow": [ + "Bash(./mvnw:*)", + "Bash(./scripts/check_dependency_updates.sh:*)", + "Bash(./scripts/install_local.sh:*)", + "Bash(./scripts/run_all_bolt_tests.sh:*)", + "Bash(./scripts/run_all_client_tests.sh:*)", + "Bash(./scripts/run_no_prep_tests.sh:*)", + "Bash(./scripts/set_version.sh:*)", "Bash(gh issue view:*)", "Bash(gh label list:*)", "Bash(gh pr checks:*)", @@ -18,9 +25,9 @@ "Bash(grep:*)", "Bash(ls:*)", "Bash(tree:*)", - "WebFetch(domain:github.com)", + "WebFetch(domain:central.sonatype.com)", "WebFetch(domain:docs.slack.dev)", - "WebFetch(domain:central.sonatype.com)" + "WebFetch(domain:github.com)" ] } } diff --git a/AGENTS.md b/AGENTS.md index 5a333b743..204e4de22 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,9 +12,9 @@ There are two main components: ## Build System - **Build tool**: Apache Maven via Maven Wrapper (`./mvnw`) -- **Java target**: Source and target compatibility is Java 1.8 (OpenJDK 8+) -- **Group ID**: `com.slack.api` -- **Current version**: Check `` in root `pom.xml` +- **Java target**: Source and target compatibility is Java 1.8 (OpenJDK 8+) — see `pom.xml` properties `maven.compiler.source` and `maven.compiler.target` +- **Group ID**: `com.slack.api` — see `pom.xml` `` +- **Current version**: See `` in root `pom.xml` (also reflected in `*LibraryVersion.java` files generated by `scripts/set_version.sh`) ### Key Commands @@ -50,7 +50,7 @@ Some modules are excluded on JDK 8 (Jakarta EE modules, GCF, Helidon, http4k, Mi ## Module Architecture -All published modules use group ID `com.slack.api` and are released to Maven Central simultaneously. +All published modules use group ID `com.slack.api` (see `pom.xml` ``) and are released to Maven Central simultaneously. The complete list of modules is defined in root `pom.xml` `` section. ### Foundation Modules @@ -131,39 +131,43 @@ All published modules use group ID `com.slack.api` and are released to Maven Cen ## Key Dependencies -| Library | Version Property | Notes | -|---------|-----------------|-------| -| OkHttp | `okhttp.version` (4.x) | HTTP client | +All dependency versions are managed in root `pom.xml` `` section. + +| Library | Version Property | Notes | +|---------|------------------|-------| +| OkHttp | `okhttp.version` | HTTP client | | Gson | `gson.version` | JSON serialization; uses `LOWER_CASE_WITH_UNDERSCORES` field naming via `GsonFactory.createSnakeCase()` | -| SLF4J | `slf4j.version` (1.7.x) | Logging facade. **Do not upgrade to v2** - see issue #1034 | +| SLF4J | `slf4j.version` | Logging facade. **Do not upgrade to v2** - see issue #1034 | | Lombok | `lombok.version` | Used extensively for `@Data`, `@Builder`, `@Slf4j` | -| Kotlin | `kotlin.version` (1.9.x) | API/language version pinned to 1.6 for backward compat | -| JUnit | `junit.version` (4.x) | **JUnit 4, not 5** | -| Mockito | `mockito-core.version` (<5.0) | Capped below v5 for Java 8 compatibility | +| Kotlin | `kotlin.version` | Compiler version is 1.9.x; `apiVersion` and `languageVersion` pinned to 1.6 for backward compat | +| JUnit | `junit.version` | **JUnit 4, not 5** | +| Mockito | `mockito-core.version` | Capped below v5 for Java 8 compatibility | | Hamcrest | `hamcrest.version` | Used for test assertions | +**Note**: Always check `pom.xml` `` for actual current versions. + ## Code Conventions ### Java Style -- **Java 8 compatibility is mandatory**. Do not use Java 9+ APIs (e.g., `List.of()`, `Map.of()`, `var`, `HttpClient`, text blocks). +- **Java 8 compatibility is mandatory** (see `pom.xml` `maven.compiler.source` and `maven.compiler.target` = 1.8). Do not use Java 9+ APIs (e.g., `List.of()`, `Map.of()`, `var`, `HttpClient`, text blocks). - Lombok annotations are used pervasively: `@Data`, `@Builder`, `@Slf4j`, `@EqualsAndHashCode(callSuper = false)`. -- JSON field naming uses **snake_case** via Gson's `LOWER_CASE_WITH_UNDERSCORES` policy. Java fields are camelCase. +- JSON field naming uses **snake_case** via Gson's `LOWER_CASE_WITH_UNDERSCORES` policy (see `slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java`). Java fields are camelCase. - Logging uses SLF4J via Lombok's `@Slf4j` (gives you a `log` field). - Checked exceptions: API methods throw `IOException` and `SlackApiException`. ### Naming Conventions -- Request classes: `{MethodName}Request` with `@Data @Builder` and implementing `SlackApiRequest` -- Response classes: `{MethodName}Response` with `@Data` and implementing `SlackApiTextResponse` -- API method constants: `Methods.java` has `public static final String` for each API method (e.g., `USERS_INFO = "users.info"`) +- Request classes: `{MethodName}Request` with `@Data @Builder` and implementing `SlackApiRequest` (see `slack-api-client/src/main/java/com/slack/api/methods/SlackApiRequest.java`) +- Response classes: `{MethodName}Response` with `@Data` and implementing `SlackApiTextResponse` (see `slack-api-client/src/main/java/com/slack/api/methods/SlackApiTextResponse.java`) +- API method constants: `Methods.java` has `public static final String` for each API method (e.g., `USERS_INFO = "users.info"`) — see `slack-api-client/src/main/java/com/slack/api/methods/Methods.java` - Test packages: `test_locally` for CI-safe tests, `test_with_remote_apis` for integration tests needing tokens ### JSON Serialization -- Use `GsonFactory.createSnakeCase()` for most Slack APIs (snake_case JSON keys) +- Use `GsonFactory.createSnakeCase()` for most Slack APIs (snake_case JSON keys with `LOWER_CASE_WITH_UNDERSCORES` policy) - Use `GsonFactory.createCamelCase()` for SCIM APIs -- The factory is in `slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java` +- The factory implementation is in `slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java` ## Model Types (`slack-api-model`) @@ -219,7 +223,7 @@ public class ChannelCreatedEvent implements Event { - Use `@Data`, `@Builder`, `@NoArgsConstructor`, `@AllArgsConstructor`, `@EqualsAndHashCode(callSuper = false)` on concrete types. - Define `public static final String TYPE` (or `TYPE_NAME`) and `private final String type = TYPE;`. -- Java fields are camelCase; Gson's `LOWER_CASE_WITH_UNDERSCORES` policy maps them to snake_case JSON automatically. +- Java fields are camelCase; Gson's `LOWER_CASE_WITH_UNDERSCORES` policy (see `GsonFactory.java`) maps them to snake_case JSON automatically. - Use `@SerializedName` only when the Slack API field name does not follow snake_case convention. - Do **not** initialize `List` fields to empty collections — Slack sometimes returns errors with empty arrays, and null vs empty has semantic meaning. @@ -299,41 +303,13 @@ public class ConversationsRequestSharedInviteApproveRequest implements SlackApiR } ``` -A simpler request class (for `conversations.requestSharedInvite.deny`): - -```java -package com.slack.api.methods.request.conversations.request_shared_invite; - -import com.slack.api.methods.SlackApiRequest; -import lombok.Builder; -import lombok.Data; - -/** - * https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.deny - */ -@Data -@Builder -public class ConversationsRequestSharedInviteDenyRequest implements SlackApiRequest { - - private String token; - - /** - * ID of the requested shared channel invite to deny. - */ - private String inviteId; - - /** - * Optional message explaining why the request to invite was denied. - */ - private String message; -} -``` +For simpler methods without nested objects, the same pattern applies but without the inner `@Data` classes (e.g., `ConversationsRequestSharedInviteDenyRequest` has only `token`, `inviteId`, and `message` fields). ### 4. Create the Response class File: `slack-api-client/src/main/java/com/slack/api/methods/response/conversations/request_shared_invite/ConversationsRequestSharedInviteApproveResponse.java` -The response class implements `SlackApiTextResponse` and uses `@Data`. Every response must include the standard fields (`ok`, `warning`, `error`, `needed`, `provided`, `httpResponseHeaders`). Add any method-specific response fields from the API docs: +The response class implements `SlackApiTextResponse` and uses `@Data`. Every response must include the standard fields defined by the `SlackApiTextResponse` interface (`ok`, `warning`, `error`, `needed`, `provided`, `httpResponseHeaders`). Add any method-specific response fields from the API docs: ```java package com.slack.api.methods.response.conversations.request_shared_invite; @@ -463,7 +439,7 @@ public static FormBody.Builder toForm(ConversationsRequestSharedInviteDenyReques File: `slack-api-client/src/main/java/com/slack/api/methods/MethodsRateLimits.java` -Look up the rate limit tier from the API docs and add a `setRateLimitTier` call in the appropriate alphabetical position: +Look up the rate limit tier from the API docs (or check `metadata/web-api/rate_limit_tiers.json` for existing patterns) and add a `setRateLimitTier` call in the appropriate alphabetical position: ```java setRateLimitTier(CONVERSATIONS_REQUEST_SHARED_INVITE_APPROVE, Tier2); @@ -474,18 +450,36 @@ setRateLimitTier(CONVERSATIONS_REQUEST_SHARED_INVITE_DENY, Tier2); File: `metadata/web-api/rate_limit_tiers.json` -Add the method name and its tier in alphabetical order within the JSON object: +Add the method name and its tier in alphabetical order within the JSON object. This file is the source of truth for rate limit tiers and should match the tier set in `MethodsRateLimits.java`: ```json "conversations.requestSharedInvite.approve": "Tier2", "conversations.requestSharedInvite.deny": "Tier2", ``` -### 12. Add tests and update `MethodsTest` +### 12. Check mock server JSON fixture + +File (new): `json-logs/samples/api/{method.name}.json` + +The `MockSlackApiServer` used in tests serves mock responses from JSON files in this directory. Every API method **must** have a corresponding JSON fixture file, or tests will fail with `NoSuchFileException` / HTTP 500. The file name is the full API method name with dots (e.g., `conversations.requestSharedInvite.approve.json`). + +The mock server reads these files and replaces `"ok": false,` with `"ok": true,` automatically, so the fixture should always have `"ok": false`. See existing files in this directory for examples: + +```json +{ + "ok": false, + "error": "", + "needed": "", + "provided": "", + "warning": "" +} +``` + +### 13. Add tests and update `MethodsTest` #### Add method-specific tests -File: `slack-api-client/src/test/java/test_locally/api/methods/ConversationsTest.java` (or the relevant existing test file for the API category) +File: `slack-api-client/src/test/java/test_locally/api/methods/{Category}Test.java` (the test file matching the top-level API namespace, e.g., `AppsTest.java` for `apps.*`, `ConversationsTest.java` for `conversations.*`) Test both sync and async variants using the configurator lambda pattern: @@ -507,25 +501,54 @@ public void conversationsRequestSharedInviteApprove_async() throws Exception { File: `slack-api-client/src/test/java/test_locally/api/MethodsTest.java` -This test validates that every known Slack API method has a corresponding `Methods.java` constant and rate limit entry. Update the `methods` string and the endpoint count comment to include the new method names. If the method names are not already in the list, add them in the correct alphabetical position. +This test validates that every known Slack API method has a corresponding constant in `Methods.java` and rate limit entry in `metadata/web-api/rate_limit_tiers.json`. Update the `methods` string and the endpoint count comment to include the new method names. If the method names are not already in the list, add them in the correct alphabetical position. + +### 14. Add imports to all modified files + +Each file that references the new Request and Response classes needs corresponding import statements. When modifying the following files, add imports for both the new Request and Response classes: + +- `MethodsClient.java` — import both `request` and `response` classes +- `AsyncMethodsClient.java` — import both `request` and `response` classes +- `MethodsClientImpl.java` — import both `request` and `response` classes +- `AsyncMethodsClientImpl.java` — import both `request` and `response` classes +- `RequestFormBuilder.java` — import the `request` class only + +Place each new import adjacent to the existing imports from the same `apps.*` (or equivalent) package, in alphabetical order. + +### 15. Verify + +Before considering the work complete, run the tests. The `slack-api-client` module depends on `slack-api-model`, so you must install all modules locally first if they haven't been built yet: + +```bash +# Install all modules locally (required before running single-module tests) +./scripts/install_local.sh + +# Run the specific tests for the new method and the coverage test +./mvnw test -pl slack-api-client '-Dtest=test_locally.api.methods.AppsTest,test_locally.api.MethodsTest' +``` + +Note: `install_local.sh` may fail on unrelated example modules (e.g., `bolt-quarkus-examples`). This is fine as long as `slack-api-client` and its dependencies (`slack-api-model`) install successfully. ### Summary checklist When adding a new Slack API method, make sure you have: - [ ] Looked up the method docs at `https://docs.slack.dev/reference/methods/{method.name}` -- [ ] Added the method constant in `Methods.java` -- [ ] Created the Request class in `request/{category}/` -- [ ] Created the Response class in `response/{category}/` -- [ ] Added two overloads in `MethodsClient.java` -- [ ] Added two overloads in `AsyncMethodsClient.java` -- [ ] Implemented both overloads in `MethodsClientImpl.java` -- [ ] Implemented both overloads in `AsyncMethodsClientImpl.java` -- [ ] Added `toForm()` in `RequestFormBuilder.java` -- [ ] Added rate limit tier in `MethodsRateLimits.java` +- [ ] Added the method constant in `slack-api-client/src/main/java/com/slack/api/methods/Methods.java` +- [ ] Created the Request class in `slack-api-client/src/main/java/com/slack/api/methods/request/{category}/` +- [ ] Created the Response class in `slack-api-client/src/main/java/com/slack/api/methods/response/{category}/` +- [ ] Added two overloads in `slack-api-client/src/main/java/com/slack/api/methods/MethodsClient.java` +- [ ] Added two overloads in `slack-api-client/src/main/java/com/slack/api/methods/AsyncMethodsClient.java` +- [ ] Implemented both overloads in `slack-api-client/src/main/java/com/slack/api/methods/impl/MethodsClientImpl.java` +- [ ] Implemented both overloads in `slack-api-client/src/main/java/com/slack/api/methods/impl/AsyncMethodsClientImpl.java` +- [ ] Added `toForm()` in `slack-api-client/src/main/java/com/slack/api/methods/RequestFormBuilder.java` +- [ ] Added rate limit tier in `slack-api-client/src/main/java/com/slack/api/methods/MethodsRateLimits.java` - [ ] Added rate limit entry in `metadata/web-api/rate_limit_tiers.json` -- [ ] Added sync and async tests in `test_locally/api/methods/` -- [ ] Updated the endpoint list and count in `MethodsTest.java` +- [ ] Created mock server JSON fixture in `json-logs/samples/api/{method.name}.json` +- [ ] Added sync and async tests in `slack-api-client/src/test/java/test_locally/api/methods/{Category}Test.java` +- [ ] Updated the endpoint list and count in `slack-api-client/src/test/java/test_locally/api/MethodsTest.java` +- [ ] Added import statements for the new Request/Response classes in all modified files +- [ ] Verified tests pass: `./mvnw test -pl slack-api-client '-Dtest=test_locally.api.methods.{Category}Test,test_locally.api.MethodsTest'` ## Testing @@ -587,7 +610,7 @@ Releases publish all modules simultaneously to Maven Central via Sonatype. - **Snapshot**: Version must end with `-SNAPSHOT`. Run `scripts/set_version.sh 1.x.y-SNAPSHOT` then `scripts/release.sh` - **Stable**: Run `scripts/set_version.sh 1.x.y` then `scripts/release.sh`. Requires JDK 17, GPG signing, and Sonatype credentials in `~/.m2/settings.xml` -- Version is set across all `pom.xml` files AND three generated version classes: +- Version is set across all `pom.xml` files AND three generated version classes (generated by `scripts/set_version.sh`): - `slack-api-model/src/main/java/com/slack/api/meta/SlackApiModelLibraryVersion.java` - `slack-api-client/src/main/java/com/slack/api/meta/SlackApiClientLibraryVersion.java` - `bolt/src/main/java/com/slack/api/bolt/meta/BoltLibraryVersion.java` From cd189631ba0594e8c7ba45c0bf6d5926b44bcb25 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 26 Feb 2026 15:54:01 -0500 Subject: [PATCH 3/3] feat: add support for apps.user.connection.update --- .../api/apps.user.connection.update.json | 7 ++++++ metadata/web-api/rate_limit_tiers.json | 1 + .../slack/api/methods/AsyncMethodsClient.java | 10 ++++++++ .../java/com/slack/api/methods/Methods.java | 6 +++++ .../com/slack/api/methods/MethodsClient.java | 13 ++++++++++ .../slack/api/methods/MethodsRateLimits.java | 1 + .../slack/api/methods/RequestFormBuilder.java | 8 ++++++ .../methods/impl/AsyncMethodsClientImpl.java | 12 +++++++++ .../api/methods/impl/MethodsClientImpl.java | 12 +++++++++ .../AppsUserConnectionUpdateRequest.java | 25 +++++++++++++++++++ .../AppsUserConnectionUpdateResponse.java | 21 ++++++++++++++++ .../java/test_locally/api/MethodsTest.java | 4 +-- .../test_locally/api/methods/AppsTest.java | 12 +++++++++ 13 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 json-logs/samples/api/apps.user.connection.update.json create mode 100644 slack-api-client/src/main/java/com/slack/api/methods/request/apps/user/connection/AppsUserConnectionUpdateRequest.java create mode 100644 slack-api-client/src/main/java/com/slack/api/methods/response/apps/user/connection/AppsUserConnectionUpdateResponse.java diff --git a/json-logs/samples/api/apps.user.connection.update.json b/json-logs/samples/api/apps.user.connection.update.json new file mode 100644 index 000000000..6b6ba1fe1 --- /dev/null +++ b/json-logs/samples/api/apps.user.connection.update.json @@ -0,0 +1,7 @@ +{ + "ok": false, + "error": "", + "needed": "", + "provided": "", + "warning": "" +} \ No newline at end of file diff --git a/metadata/web-api/rate_limit_tiers.json b/metadata/web-api/rate_limit_tiers.json index ba7956c74..93f929e67 100644 --- a/metadata/web-api/rate_limit_tiers.json +++ b/metadata/web-api/rate_limit_tiers.json @@ -113,6 +113,7 @@ "apps.permissions.users.list": "Tier2", "apps.permissions.users.request": "Tier2", "apps.uninstall": "Tier1", + "apps.user.connection.update": "Tier2", "assistant.threads.setStatus": "SpecialTier_assistant_threads_setStatus", "assistant.threads.setSuggestedPrompts": "Tier3", "assistant.threads.setTitle": "Tier3", diff --git a/slack-api-client/src/main/java/com/slack/api/methods/AsyncMethodsClient.java b/slack-api-client/src/main/java/com/slack/api/methods/AsyncMethodsClient.java index 7566d7643..4d9b57ead 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/AsyncMethodsClient.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/AsyncMethodsClient.java @@ -42,6 +42,7 @@ import com.slack.api.methods.request.apps.AppsUninstallRequest; import com.slack.api.methods.request.apps.connections.AppsConnectionsOpenRequest; import com.slack.api.methods.request.apps.event.authorizations.AppsEventAuthorizationsListRequest; +import com.slack.api.methods.request.apps.user.connection.AppsUserConnectionUpdateRequest; import com.slack.api.methods.request.apps.manifest.*; import com.slack.api.methods.request.assistant.threads.AssistantThreadsSetStatusRequest; import com.slack.api.methods.request.assistant.threads.AssistantThreadsSetSuggestedPromptsRequest; @@ -180,6 +181,7 @@ import com.slack.api.methods.response.apps.AppsUninstallResponse; import com.slack.api.methods.response.apps.connections.AppsConnectionsOpenResponse; import com.slack.api.methods.response.apps.event.authorizations.AppsEventAuthorizationsListResponse; +import com.slack.api.methods.response.apps.user.connection.AppsUserConnectionUpdateResponse; import com.slack.api.methods.response.apps.manifest.*; import com.slack.api.methods.response.asssistant.threads.AssistantThreadsSetStatusResponse; import com.slack.api.methods.response.asssistant.threads.AssistantThreadsSetSuggestedPromptsResponse; @@ -816,6 +818,14 @@ CompletableFuture CompletableFuture appsConnectionsOpen(RequestConfigurator req); + // ------------------------------ + // apps.user.connection + // ------------------------------ + + CompletableFuture appsUserConnectionUpdate(AppsUserConnectionUpdateRequest req); + + CompletableFuture appsUserConnectionUpdate(RequestConfigurator req); + // ------------------------------ // apps.event.authorizations // ------------------------------ diff --git a/slack-api-client/src/main/java/com/slack/api/methods/Methods.java b/slack-api-client/src/main/java/com/slack/api/methods/Methods.java index 22702c87c..2e39655d4 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/Methods.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/Methods.java @@ -237,6 +237,12 @@ private Methods() { public static final String APPS_CONNECTIONS_OPEN = "apps.connections.open"; + // ------------------------------ + // apps.user.connection + // ------------------------------ + + public static final String APPS_USER_CONNECTION_UPDATE = "apps.user.connection.update"; + // ------------------------------ // apps.manifest // ------------------------------ diff --git a/slack-api-client/src/main/java/com/slack/api/methods/MethodsClient.java b/slack-api-client/src/main/java/com/slack/api/methods/MethodsClient.java index 784d2e969..3a4722eb0 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/MethodsClient.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/MethodsClient.java @@ -42,6 +42,7 @@ import com.slack.api.methods.request.apps.AppsUninstallRequest; import com.slack.api.methods.request.apps.connections.AppsConnectionsOpenRequest; import com.slack.api.methods.request.apps.event.authorizations.AppsEventAuthorizationsListRequest; +import com.slack.api.methods.request.apps.user.connection.AppsUserConnectionUpdateRequest; import com.slack.api.methods.request.apps.manifest.*; import com.slack.api.methods.request.apps.permissions.AppsPermissionsInfoRequest; import com.slack.api.methods.request.apps.permissions.AppsPermissionsRequestRequest; @@ -193,6 +194,7 @@ import com.slack.api.methods.response.apps.AppsUninstallResponse; import com.slack.api.methods.response.apps.connections.AppsConnectionsOpenResponse; import com.slack.api.methods.response.apps.event.authorizations.AppsEventAuthorizationsListResponse; +import com.slack.api.methods.response.apps.user.connection.AppsUserConnectionUpdateResponse; import com.slack.api.methods.response.apps.manifest.*; import com.slack.api.methods.response.apps.permissions.AppsPermissionsInfoResponse; import com.slack.api.methods.response.apps.permissions.AppsPermissionsRequestResponse; @@ -1158,6 +1160,17 @@ AppsConnectionsOpenResponse appsConnectionsOpen( RequestConfigurator req) throws IOException, SlackApiException; + // ------------------------------ + // apps.user.connection + // ------------------------------ + + AppsUserConnectionUpdateResponse appsUserConnectionUpdate(AppsUserConnectionUpdateRequest req) + throws IOException, SlackApiException; + + AppsUserConnectionUpdateResponse appsUserConnectionUpdate( + RequestConfigurator req) + throws IOException, SlackApiException; + // ------------------------------ // apps.event.authorizations // ------------------------------ diff --git a/slack-api-client/src/main/java/com/slack/api/methods/MethodsRateLimits.java b/slack-api-client/src/main/java/com/slack/api/methods/MethodsRateLimits.java index 0c3fdb14a..9ecc6fee6 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/MethodsRateLimits.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/MethodsRateLimits.java @@ -227,6 +227,7 @@ public static void setRateLimitTier(String methodName, MethodsRateLimitTier tier setRateLimitTier(APPS_CONNECTIONS_OPEN, Tier1); setRateLimitTier(APPS_UNINSTALL, Tier1); setRateLimitTier(APPS_EVENT_AUTHORIZATIONS_LIST, Tier4); + setRateLimitTier(APPS_USER_CONNECTION_UPDATE, Tier2); setRateLimitTier(ASSISTANT_THREADS_SET_STATUS, SpecialTier_assistant_threads_setStatus); setRateLimitTier(ASSISTANT_THREADS_SET_SUGGESTED_PROMPTS, Tier3); diff --git a/slack-api-client/src/main/java/com/slack/api/methods/RequestFormBuilder.java b/slack-api-client/src/main/java/com/slack/api/methods/RequestFormBuilder.java index 455387faa..2ca47afe1 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/RequestFormBuilder.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/RequestFormBuilder.java @@ -43,6 +43,7 @@ import com.slack.api.methods.request.apps.AppsUninstallRequest; import com.slack.api.methods.request.apps.connections.AppsConnectionsOpenRequest; import com.slack.api.methods.request.apps.event.authorizations.AppsEventAuthorizationsListRequest; +import com.slack.api.methods.request.apps.user.connection.AppsUserConnectionUpdateRequest; import com.slack.api.methods.request.apps.manifest.*; import com.slack.api.methods.request.apps.permissions.AppsPermissionsInfoRequest; import com.slack.api.methods.request.apps.permissions.AppsPermissionsRequestRequest; @@ -1063,6 +1064,13 @@ public static FormBody.Builder toForm(AppsConnectionsOpenRequest req) { return form; } + public static FormBody.Builder toForm(AppsUserConnectionUpdateRequest req) { + FormBody.Builder form = new FormBody.Builder(); + setIfNotNull("user_id", req.getUserId(), form); + setIfNotNull("status", req.getStatus(), form); + return form; + } + public static FormBody.Builder toForm(AppsEventAuthorizationsListRequest req) { FormBody.Builder form = new FormBody.Builder(); setIfNotNull("event_context", req.getEventContext(), form); diff --git a/slack-api-client/src/main/java/com/slack/api/methods/impl/AsyncMethodsClientImpl.java b/slack-api-client/src/main/java/com/slack/api/methods/impl/AsyncMethodsClientImpl.java index a4d0b64a2..21e007510 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/impl/AsyncMethodsClientImpl.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/impl/AsyncMethodsClientImpl.java @@ -48,6 +48,7 @@ import com.slack.api.methods.request.apps.connections.AppsConnectionsOpenRequest; import com.slack.api.methods.request.apps.event.authorizations.AppsEventAuthorizationsListRequest; import com.slack.api.methods.request.apps.manifest.*; +import com.slack.api.methods.request.apps.user.connection.AppsUserConnectionUpdateRequest; import com.slack.api.methods.request.assistant.threads.AssistantThreadsSetStatusRequest; import com.slack.api.methods.request.assistant.threads.AssistantThreadsSetSuggestedPromptsRequest; import com.slack.api.methods.request.assistant.threads.AssistantThreadsSetTitleRequest; @@ -186,6 +187,7 @@ import com.slack.api.methods.response.apps.connections.AppsConnectionsOpenResponse; import com.slack.api.methods.response.apps.event.authorizations.AppsEventAuthorizationsListResponse; import com.slack.api.methods.response.apps.manifest.*; +import com.slack.api.methods.response.apps.user.connection.AppsUserConnectionUpdateResponse; import com.slack.api.methods.response.asssistant.threads.AssistantThreadsSetStatusResponse; import com.slack.api.methods.response.asssistant.threads.AssistantThreadsSetSuggestedPromptsResponse; import com.slack.api.methods.response.asssistant.threads.AssistantThreadsSetTitleResponse; @@ -1345,6 +1347,16 @@ public CompletableFuture appsConnectionsOpen(Reques return appsConnectionsOpen(req.configure(AppsConnectionsOpenRequest.builder()).build()); } + @Override + public CompletableFuture appsUserConnectionUpdate(AppsUserConnectionUpdateRequest req) { + return executor.execute(APPS_USER_CONNECTION_UPDATE, toMap(req), () -> methods.appsUserConnectionUpdate(req)); + } + + @Override + public CompletableFuture appsUserConnectionUpdate(RequestConfigurator req) { + return appsUserConnectionUpdate(req.configure(AppsUserConnectionUpdateRequest.builder()).build()); + } + @Override public CompletableFuture appsEventAuthorizationsList(AppsEventAuthorizationsListRequest req) { return executor.execute(APPS_EVENT_AUTHORIZATIONS_LIST, toMap(req), () -> methods.appsEventAuthorizationsList(req)); diff --git a/slack-api-client/src/main/java/com/slack/api/methods/impl/MethodsClientImpl.java b/slack-api-client/src/main/java/com/slack/api/methods/impl/MethodsClientImpl.java index ac9fcf215..dc58206a3 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/impl/MethodsClientImpl.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/impl/MethodsClientImpl.java @@ -44,6 +44,7 @@ import com.slack.api.methods.request.apps.AppsUninstallRequest; import com.slack.api.methods.request.apps.connections.AppsConnectionsOpenRequest; import com.slack.api.methods.request.apps.event.authorizations.AppsEventAuthorizationsListRequest; +import com.slack.api.methods.request.apps.user.connection.AppsUserConnectionUpdateRequest; import com.slack.api.methods.request.apps.manifest.*; import com.slack.api.methods.request.apps.permissions.AppsPermissionsInfoRequest; import com.slack.api.methods.request.apps.permissions.AppsPermissionsRequestRequest; @@ -195,6 +196,7 @@ import com.slack.api.methods.response.apps.AppsUninstallResponse; import com.slack.api.methods.response.apps.connections.AppsConnectionsOpenResponse; import com.slack.api.methods.response.apps.event.authorizations.AppsEventAuthorizationsListResponse; +import com.slack.api.methods.response.apps.user.connection.AppsUserConnectionUpdateResponse; import com.slack.api.methods.response.apps.manifest.*; import com.slack.api.methods.response.apps.permissions.AppsPermissionsInfoResponse; import com.slack.api.methods.response.apps.permissions.AppsPermissionsRequestResponse; @@ -1386,6 +1388,16 @@ public AppsConnectionsOpenResponse appsConnectionsOpen(RequestConfigurator req) throws IOException, SlackApiException { + return appsUserConnectionUpdate(req.configure(AppsUserConnectionUpdateRequest.builder()).build()); + } + @Override public AppsEventAuthorizationsListResponse appsEventAuthorizationsList(AppsEventAuthorizationsListRequest req) throws IOException, SlackApiException { return postFormWithTokenAndParseResponse(toForm(req), Methods.APPS_EVENT_AUTHORIZATIONS_LIST, getToken(req), AppsEventAuthorizationsListResponse.class); diff --git a/slack-api-client/src/main/java/com/slack/api/methods/request/apps/user/connection/AppsUserConnectionUpdateRequest.java b/slack-api-client/src/main/java/com/slack/api/methods/request/apps/user/connection/AppsUserConnectionUpdateRequest.java new file mode 100644 index 000000000..6bfe7bd73 --- /dev/null +++ b/slack-api-client/src/main/java/com/slack/api/methods/request/apps/user/connection/AppsUserConnectionUpdateRequest.java @@ -0,0 +1,25 @@ +package com.slack.api.methods.request.apps.user.connection; + +import com.slack.api.methods.SlackApiRequest; +import lombok.Builder; +import lombok.Data; + +/** + * https://docs.slack.dev/reference/methods/apps.user.connection.update + */ +@Data +@Builder +public class AppsUserConnectionUpdateRequest implements SlackApiRequest { + + private String token; + + /** + * The user ID to update the connection status for. + */ + private String userId; + + /** + * The connection status to set. + */ + private String status; +} diff --git a/slack-api-client/src/main/java/com/slack/api/methods/response/apps/user/connection/AppsUserConnectionUpdateResponse.java b/slack-api-client/src/main/java/com/slack/api/methods/response/apps/user/connection/AppsUserConnectionUpdateResponse.java new file mode 100644 index 000000000..e5acb3a16 --- /dev/null +++ b/slack-api-client/src/main/java/com/slack/api/methods/response/apps/user/connection/AppsUserConnectionUpdateResponse.java @@ -0,0 +1,21 @@ +package com.slack.api.methods.response.apps.user.connection; + +import com.slack.api.methods.SlackApiTextResponse; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * https://docs.slack.dev/reference/methods/apps.user.connection.update + */ +@Data +public class AppsUserConnectionUpdateResponse implements SlackApiTextResponse { + + private boolean ok; + private String warning; + private String error; + private String needed; + private String provided; + private transient Map> httpResponseHeaders; +} diff --git a/slack-api-client/src/test/java/test_locally/api/MethodsTest.java b/slack-api-client/src/test/java/test_locally/api/MethodsTest.java index 9aaeb030c..39f74b8bb 100644 --- a/slack-api-client/src/test/java/test_locally/api/MethodsTest.java +++ b/slack-api-client/src/test/java/test_locally/api/MethodsTest.java @@ -19,8 +19,8 @@ public class MethodsTest { public void verifyTheCoverage() { // https://docs.slack.dev/reference/methods // var methodNames = [].slice.call(document.getElementsByClassName('apiReferenceFilterableList__listItemLink')).map(e => e.href.replace("https://docs.slack.dev/reference/methods/", ""));console.log(methodNames.toString());console.log(methodNames.length); - // 307 endpoints as of December 18, 2025 - String methods = "admin.analytics.getFile,admin.apps.activities.list,admin.apps.approve,admin.apps.clearResolution,admin.apps.restrict,admin.apps.uninstall,admin.apps.approved.list,admin.apps.config.lookup,admin.apps.config.set,admin.apps.requests.cancel,admin.apps.requests.list,admin.apps.restricted.list,admin.audit.anomaly.allow.getItem,admin.audit.anomaly.allow.updateItem,admin.auth.policy.assignEntities,admin.auth.policy.getEntities,admin.auth.policy.removeEntities,admin.barriers.create,admin.barriers.delete,admin.barriers.list,admin.barriers.update,admin.conversations.archive,admin.conversations.bulkArchive,admin.conversations.bulkDelete,admin.conversations.bulkMove,admin.conversations.convertToPrivate,admin.conversations.convertToPublic,admin.conversations.create,admin.conversations.createForObjects,admin.conversations.delete,admin.conversations.disconnectShared,admin.conversations.getConversationPrefs,admin.conversations.getCustomRetention,admin.conversations.getTeams,admin.conversations.invite,admin.conversations.linkObjects,admin.conversations.lookup,admin.conversations.removeCustomRetention,admin.conversations.rename,admin.conversations.search,admin.conversations.setConversationPrefs,admin.conversations.setCustomRetention,admin.conversations.setTeams,admin.conversations.unarchive,admin.conversations.unlinkObjects,admin.conversations.ekm.listOriginalConnectedChannelInfo,admin.conversations.restrictAccess.addGroup,admin.conversations.restrictAccess.listGroups,admin.conversations.restrictAccess.removeGroup,admin.emoji.add,admin.emoji.addAlias,admin.emoji.list,admin.emoji.remove,admin.emoji.rename,admin.functions.list,admin.functions.permissions.lookup,admin.functions.permissions.set,admin.inviteRequests.approve,admin.inviteRequests.deny,admin.inviteRequests.list,admin.inviteRequests.approved.list,admin.inviteRequests.denied.list,admin.roles.addAssignments,admin.roles.listAssignments,admin.roles.removeAssignments,admin.teams.admins.list,admin.teams.create,admin.teams.list,admin.teams.owners.list,admin.teams.settings.info,admin.teams.settings.setDefaultChannels,admin.teams.settings.setDescription,admin.teams.settings.setDiscoverability,admin.teams.settings.setIcon,admin.teams.settings.setName,admin.usergroups.addChannels,admin.usergroups.addTeams,admin.usergroups.listChannels,admin.usergroups.removeChannels,admin.users.assign,admin.users.getExpiration,admin.users.invite,admin.users.list,admin.users.remove,admin.users.setAdmin,admin.users.setExpiration,admin.users.setOwner,admin.users.setRegular,admin.users.session.clearSettings,admin.users.session.getSettings,admin.users.session.invalidate,admin.users.session.list,admin.users.session.reset,admin.users.session.resetBulk,admin.users.session.setSettings,admin.users.unsupportedVersions.export,admin.workflows.collaborators.add,admin.workflows.collaborators.remove,admin.workflows.permissions.lookup,admin.workflows.search,admin.workflows.unpublish,admin.workflows.triggers.types.permissions.lookup,admin.workflows.triggers.types.permissions.set,api.test,apps.activities.list,apps.auth.external.delete,apps.auth.external.get,apps.connections.open,apps.datastore.bulkDelete,apps.datastore.bulkGet,apps.datastore.bulkPut,apps.datastore.count,apps.datastore.delete,apps.datastore.get,apps.datastore.put,apps.datastore.query,apps.datastore.update,apps.event.authorizations.list,apps.manifest.create,apps.manifest.delete,apps.manifest.export,apps.manifest.update,apps.manifest.validate,apps.uninstall,assistant.search.context,assistant.threads.setStatus,assistant.threads.setSuggestedPrompts,assistant.threads.setTitle,auth.revoke,auth.test,auth.teams.list,bookmarks.add,bookmarks.edit,bookmarks.list,bookmarks.remove,bots.info,calls.add,calls.end,calls.info,calls.update,calls.participants.add,calls.participants.remove,canvases.access.delete,canvases.access.set,canvases.create,canvases.delete,canvases.edit,canvases.sections.lookup,channels.mark,chat.appendStream,chat.delete,chat.deleteScheduledMessage,chat.getPermalink,chat.meMessage,chat.postEphemeral,chat.postMessage,chat.scheduleMessage,chat.startStream,chat.stopStream,chat.unfurl,chat.update,chat.scheduledMessages.list,conversations.acceptSharedInvite,conversations.approveSharedInvite,conversations.archive,conversations.close,conversations.create,conversations.declineSharedInvite,conversations.history,conversations.info,conversations.invite,conversations.inviteShared,conversations.join,conversations.kick,conversations.leave,conversations.list,conversations.listConnectInvites,conversations.mark,conversations.members,conversations.open,conversations.rename,conversations.replies,conversations.setPurpose,conversations.setTopic,conversations.unarchive,conversations.canvases.create,conversations.externalInvitePermissions.set,conversations.requestSharedInvite.approve,conversations.requestSharedInvite.deny,conversations.requestSharedInvite.list,dialog.open,dnd.endDnd,dnd.endSnooze,dnd.info,dnd.setSnooze,dnd.teamInfo,emoji.list,files.comments.delete,files.completeUploadExternal,files.delete,files.getUploadURLExternal,files.info,files.list,files.revokePublicURL,files.sharedPublicURL,files.upload,files.remote.add,files.remote.info,files.remote.list,files.remote.remove,files.remote.share,files.remote.update,functions.completeError,functions.completeSuccess,functions.distributions.permissions.add,functions.distributions.permissions.list,functions.distributions.permissions.remove,functions.distributions.permissions.set,functions.workflows.steps.list,functions.workflows.steps.responses.export,groups.mark,migration.exchange,oauth.access,oauth.v2.access,oauth.v2.exchange,openid.connect.token,openid.connect.userInfo,pins.add,pins.list,pins.remove,reactions.add,reactions.get,reactions.list,reactions.remove,reminders.add,reminders.complete,reminders.delete,reminders.info,reminders.list,rtm.connect,rtm.start,search.all,search.files,search.messages,slackLists.access.delete,slackLists.access.set,slackLists.create,slackLists.download.get,slackLists.download.start,slackLists.items.create,slackLists.items.delete,slackLists.items.deleteMultiple,slackLists.items.info,slackLists.items.list,slackLists.items.update,slackLists.update,stars.add,stars.list,stars.remove,team.accessLogs,team.billableInfo,team.info,team.integrationLogs,team.billing.info,team.externalTeams.disconnect,team.externalTeams.list,team.preferences.list,team.profile.get,tooling.tokens.rotate,usergroups.create,usergroups.disable,usergroups.enable,usergroups.list,usergroups.update,usergroups.users.list,usergroups.users.update,users.conversations,users.deletePhoto,users.getPresence,users.identity,users.info,users.list,users.lookupByEmail,users.setActive,users.setPhoto,users.setPresence,users.discoverableContacts.lookup,users.profile.get,users.profile.set,views.open,views.publish,views.push,views.update,workflows.featured.add,workflows.featured.list,workflows.featured.remove,workflows.featured.set,workflows.stepCompleted,workflows.stepFailed,workflows.updateStep,workflows.triggers.permissions.add,workflows.triggers.permissions.list,workflows.triggers.permissions.remove,workflows.triggers.permissions.set,im.list,im.mark,mpim.list,mpim.mark"; + // 308 endpoints as of February 26, 2026 + String methods = "admin.analytics.getFile,admin.apps.activities.list,admin.apps.approve,admin.apps.clearResolution,admin.apps.restrict,admin.apps.uninstall,admin.apps.approved.list,admin.apps.config.lookup,admin.apps.config.set,admin.apps.requests.cancel,admin.apps.requests.list,admin.apps.restricted.list,admin.audit.anomaly.allow.getItem,admin.audit.anomaly.allow.updateItem,admin.auth.policy.assignEntities,admin.auth.policy.getEntities,admin.auth.policy.removeEntities,admin.barriers.create,admin.barriers.delete,admin.barriers.list,admin.barriers.update,admin.conversations.archive,admin.conversations.bulkArchive,admin.conversations.bulkDelete,admin.conversations.bulkMove,admin.conversations.convertToPrivate,admin.conversations.convertToPublic,admin.conversations.create,admin.conversations.createForObjects,admin.conversations.delete,admin.conversations.disconnectShared,admin.conversations.getConversationPrefs,admin.conversations.getCustomRetention,admin.conversations.getTeams,admin.conversations.invite,admin.conversations.linkObjects,admin.conversations.lookup,admin.conversations.removeCustomRetention,admin.conversations.rename,admin.conversations.search,admin.conversations.setConversationPrefs,admin.conversations.setCustomRetention,admin.conversations.setTeams,admin.conversations.unarchive,admin.conversations.unlinkObjects,admin.conversations.ekm.listOriginalConnectedChannelInfo,admin.conversations.restrictAccess.addGroup,admin.conversations.restrictAccess.listGroups,admin.conversations.restrictAccess.removeGroup,admin.emoji.add,admin.emoji.addAlias,admin.emoji.list,admin.emoji.remove,admin.emoji.rename,admin.functions.list,admin.functions.permissions.lookup,admin.functions.permissions.set,admin.inviteRequests.approve,admin.inviteRequests.deny,admin.inviteRequests.list,admin.inviteRequests.approved.list,admin.inviteRequests.denied.list,admin.roles.addAssignments,admin.roles.listAssignments,admin.roles.removeAssignments,admin.teams.admins.list,admin.teams.create,admin.teams.list,admin.teams.owners.list,admin.teams.settings.info,admin.teams.settings.setDefaultChannels,admin.teams.settings.setDescription,admin.teams.settings.setDiscoverability,admin.teams.settings.setIcon,admin.teams.settings.setName,admin.usergroups.addChannels,admin.usergroups.addTeams,admin.usergroups.listChannels,admin.usergroups.removeChannels,admin.users.assign,admin.users.getExpiration,admin.users.invite,admin.users.list,admin.users.remove,admin.users.setAdmin,admin.users.setExpiration,admin.users.setOwner,admin.users.setRegular,admin.users.session.clearSettings,admin.users.session.getSettings,admin.users.session.invalidate,admin.users.session.list,admin.users.session.reset,admin.users.session.resetBulk,admin.users.session.setSettings,admin.users.unsupportedVersions.export,admin.workflows.collaborators.add,admin.workflows.collaborators.remove,admin.workflows.permissions.lookup,admin.workflows.search,admin.workflows.unpublish,admin.workflows.triggers.types.permissions.lookup,admin.workflows.triggers.types.permissions.set,api.test,apps.activities.list,apps.auth.external.delete,apps.auth.external.get,apps.connections.open,apps.datastore.bulkDelete,apps.datastore.bulkGet,apps.datastore.bulkPut,apps.datastore.count,apps.datastore.delete,apps.datastore.get,apps.datastore.put,apps.datastore.query,apps.datastore.update,apps.event.authorizations.list,apps.manifest.create,apps.manifest.delete,apps.manifest.export,apps.manifest.update,apps.manifest.validate,apps.uninstall,apps.user.connection.update,assistant.search.context,assistant.threads.setStatus,assistant.threads.setSuggestedPrompts,assistant.threads.setTitle,auth.revoke,auth.test,auth.teams.list,bookmarks.add,bookmarks.edit,bookmarks.list,bookmarks.remove,bots.info,calls.add,calls.end,calls.info,calls.update,calls.participants.add,calls.participants.remove,canvases.access.delete,canvases.access.set,canvases.create,canvases.delete,canvases.edit,canvases.sections.lookup,channels.mark,chat.appendStream,chat.delete,chat.deleteScheduledMessage,chat.getPermalink,chat.meMessage,chat.postEphemeral,chat.postMessage,chat.scheduleMessage,chat.startStream,chat.stopStream,chat.unfurl,chat.update,chat.scheduledMessages.list,conversations.acceptSharedInvite,conversations.approveSharedInvite,conversations.archive,conversations.close,conversations.create,conversations.declineSharedInvite,conversations.history,conversations.info,conversations.invite,conversations.inviteShared,conversations.join,conversations.kick,conversations.leave,conversations.list,conversations.listConnectInvites,conversations.mark,conversations.members,conversations.open,conversations.rename,conversations.replies,conversations.setPurpose,conversations.setTopic,conversations.unarchive,conversations.canvases.create,conversations.externalInvitePermissions.set,conversations.requestSharedInvite.approve,conversations.requestSharedInvite.deny,conversations.requestSharedInvite.list,dialog.open,dnd.endDnd,dnd.endSnooze,dnd.info,dnd.setSnooze,dnd.teamInfo,emoji.list,files.comments.delete,files.completeUploadExternal,files.delete,files.getUploadURLExternal,files.info,files.list,files.revokePublicURL,files.sharedPublicURL,files.upload,files.remote.add,files.remote.info,files.remote.list,files.remote.remove,files.remote.share,files.remote.update,functions.completeError,functions.completeSuccess,functions.distributions.permissions.add,functions.distributions.permissions.list,functions.distributions.permissions.remove,functions.distributions.permissions.set,functions.workflows.steps.list,functions.workflows.steps.responses.export,groups.mark,migration.exchange,oauth.access,oauth.v2.access,oauth.v2.exchange,openid.connect.token,openid.connect.userInfo,pins.add,pins.list,pins.remove,reactions.add,reactions.get,reactions.list,reactions.remove,reminders.add,reminders.complete,reminders.delete,reminders.info,reminders.list,rtm.connect,rtm.start,search.all,search.files,search.messages,slackLists.access.delete,slackLists.access.set,slackLists.create,slackLists.download.get,slackLists.download.start,slackLists.items.create,slackLists.items.delete,slackLists.items.deleteMultiple,slackLists.items.info,slackLists.items.list,slackLists.items.update,slackLists.update,stars.add,stars.list,stars.remove,team.accessLogs,team.billableInfo,team.info,team.integrationLogs,team.billing.info,team.externalTeams.disconnect,team.externalTeams.list,team.preferences.list,team.profile.get,tooling.tokens.rotate,usergroups.create,usergroups.disable,usergroups.enable,usergroups.list,usergroups.update,usergroups.users.list,usergroups.users.update,users.conversations,users.deletePhoto,users.getPresence,users.identity,users.info,users.list,users.lookupByEmail,users.setActive,users.setPhoto,users.setPresence,users.discoverableContacts.lookup,users.profile.get,users.profile.set,views.open,views.publish,views.push,views.update,workflows.featured.add,workflows.featured.list,workflows.featured.remove,workflows.featured.set,workflows.stepCompleted,workflows.stepFailed,workflows.updateStep,workflows.triggers.permissions.add,workflows.triggers.permissions.list,workflows.triggers.permissions.remove,workflows.triggers.permissions.set,im.list,im.mark,mpim.list,mpim.mark"; final List existingMethods = new ArrayList<>(); for (Field f : Methods.class.getDeclaredFields()) { int modifiers = f.getModifiers(); diff --git a/slack-api-client/src/test/java/test_locally/api/methods/AppsTest.java b/slack-api-client/src/test/java/test_locally/api/methods/AppsTest.java index 23d7951ce..d7ecb4d68 100644 --- a/slack-api-client/src/test/java/test_locally/api/methods/AppsTest.java +++ b/slack-api-client/src/test/java/test_locally/api/methods/AppsTest.java @@ -29,6 +29,18 @@ public void tearDown() throws Exception { server.stop(); } + @Test + public void appsUserConnectionUpdate() throws Exception { + assertThat(slack.methods(ValidToken).appsUserConnectionUpdate(r -> r + .userId("U12345678").status("connected")).isOk(), is(true)); + } + + @Test + public void appsUserConnectionUpdate_async() throws Exception { + assertThat(slack.methodsAsync(ValidToken).appsUserConnectionUpdate(r -> r + .userId("U12345678").status("connected")).get().isOk(), is(true)); + } + @Test public void appsUninstall_async() throws Exception { assertThat(slack.methodsAsync(ValidToken).appsUninstall(r -> r.clientId("x").clientSecret("y"))