slackapi / java-slack-sdk

Slack Developer Kit (including Bolt for Java) for any JVM language
https://tools.slack.dev/java-slack-sdk/
MIT License
576 stars 214 forks source link

Is there a way how to list all enterprise and team ID of the installed applications? #1055

Closed musketyr closed 2 years ago

musketyr commented 2 years ago

I'm working on example with a distributed application. I know I can use InstallationService to get the bot information but is there a method to list all the installed bots somewhere?

If not, what is the way how to hook into the installation process to store the bot information separately?

The Slack SDK version

1.25.1

Java Runtime version

openjdk version "11.0.16" 2022-07-19 LTS
OpenJDK Runtime Environment Zulu11.58+15-CA (build 11.0.16+8-LTS)
OpenJDK 64-Bit Server VM Zulu11.58+15-CA (build 11.0.16+8-LTS, mixed mode)

OS info

ProductName:    macOS
ProductVersion: 12.2.1
BuildVersion:   21D62
Darwin Kernel Version 21.3.0: Wed Jan  5 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_ARM64_T6000

Steps to reproduce:

  1. Create a distributed app
  2. Install the app into the workspace

Expected result:

There is a method which can list all the available installed bots

Actual result:

I'm unable to locate such method.

Requirements

Please make sure if this topic is specific to this SDK. For general questions/issues about Slack API platform or its server-side, could you submit questions at https://my.slack.com/help/requests/new instead. :bow:

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you agree to those rules.

hello-ashleyintech commented 2 years ago

Hi, @musketyr! Thank you for submitting your question! 🙌

If you are only looking for installed bots and not installed applications, you can use the users.list API method to achieve what you're looking for. Since all bots have bot users, they will show up in this list of all users in your workspace. You'll need to iterate through the returned list of users and also check for the is_bot property for each entry in the response.

musketyr commented 2 years ago

Hello @hello-ashleyintech,

I'm afraid this is not the use case I was looking for. For a demo purposes (but it can be also used for announcements etc.), I would like to find all installed applications for which I have a bot token available (e.g. at least the pairs of enterprise and team ids). Something like InstallationService#findAllBots. As there is no such method, I expect that the application is supposed to handle such thing itself so I wonder if there is a handler which will be triggered when a new Bot is stored by the InstallationService so the application itself can store the enterprise and team id.

hello-ashleyintech commented 2 years ago

@musketyr, gotcha! Thanks for clarifying! 🙌

How are you currently implementing your InstallationService initialization for your OAuth? Are you using a custom InstallationService class or one of the built-in ones such as AmazonS3InstallationService? (This will depend on what datastore you're planning to use. For more info on what datastores are currently support by built-in classes, as well as how to implement a custom InstallationService class for your datastore if it's not yet supported, you can view the "Choose Proper Storage Services" heading under this guide.)

If you're using a built-in class, such as the AmazonS3InstallationService, you'd need to query the related datastore for that class to get all installation instances and the related Enterprise and Team IDs. In the scenario of the AmazonS3InstallationService, it would be the Amazon S3 bucket you are passing in to the initial configuration. As you can see in the saveBot() method of this class, this is how the Bot and its related properties are saved into the S3 bucket.

If you're using a custom class that implements the InstallationService interface, you should be able to create your own save() method for your datastore (similar to this one from the AmazonS3InstallationService) and then save the bot info (which includes Enterprise and Team ID) through the saveBot() function so that it is added to your datastore. You can then query your datastore where needed to get all installation instances and their Enterprise and Team IDs.

I hope this helps! If not, feel free to provide more info and I'd be happy to help further.

musketyr commented 2 years ago

@hello-ashleyintech I think it would be great to have such a method for the builtin implementation out of the box. What would you say on these implementations in #1056?

seratch commented 2 years ago

@musketyr As I mentioned at https://github.com/slackapi/java-slack-sdk/pull/1056#issuecomment-1255879894, you can use your own service instance with the additional method.

seratch commented 2 years ago

Hi @musketyr, please consider implementing something like this on your own. I believe that this is flexible enough for any enhancements that you may want to add in the future. Also, you don't need to add the find-all method to the InstallationService compatible class. You can add a different component if it's a clearer approach.

package example;

import com.slack.api.bolt.App;
import com.slack.api.bolt.context.builtin.EventContext;
import com.slack.api.bolt.model.Bot;
import com.slack.api.bolt.service.builtin.AmazonS3InstallationService;
import com.slack.api.model.event.AppHomeOpenedEvent;
import com.slack.api.model.view.View;

import java.util.Collections;
import java.util.List;

public class Main {

    static class MyAmazonS3InstallationService extends AmazonS3InstallationService {
        public MyAmazonS3InstallationService(String bucketName) {
            super(bucketName);
        }

        public List<Bot> findAllBots(String enterpriseId, String teamId) {
            // TODO: implement this
            return Collections.emptyList();
        }

    }

    public static void main(String[] args) {
        App app = new App();
        MyAmazonS3InstallationService installationService = new MyAmazonS3InstallationService("my-bucket");
        app.service(installationService);
        app.event(AppHomeOpenedEvent.class, (req, ctx) -> {
            ctx.client().viewsPublish(r -> r.view(buildHomeTab(ctx, installationService)));
            return ctx.ack();
        });
        app.start();
    }

    private static View buildHomeTab(EventContext ctx, MyAmazonS3InstallationService installationService) {
        // TODO: list all the installations and embed them in the Home tab
        List<Bot> allBots = installationService.findAllBots(ctx.getEnterpriseId(), ctx.getTeamId());
        return null;
    }
}
musketyr commented 2 years ago

HI @seratch I can see you point but such implementations would be too dangerous as they relay on the internal implementation of a classes which are out of my control. even it would be very unexpected, there is no guarantee that internals won't change in the future - e.g. the directory layout or the bot format. for that reason I wanted to keep this particular functionality close to the library source code.

seratch commented 2 years ago

@musketyr Regarding the Amazon S3 implementation, I see your point. However, we still don't plan to add all the requested method supports to the interface. So, for your specific use case, I'd suggest forking the built-in implementation to take full control of the way to manage the underlying installation data.

I know this does not sound ideal to you, but it'd be appreciated if you could understand.

seratch commented 2 years ago

Happy to see you've implemented your own solution for this issue: https://github.com/agorapulse/micronaut-slack/blob/master/libs/micronaut-slack-core/src/main/java/com/agorapulse/slack/install/enumerate/AmazonS3InstallationEnumerationService.java

Great work! Since we've shared all the information we have for this topic, let us close this issue now.