Skip to content

Commit

Permalink
Add web api generator
Browse files Browse the repository at this point in the history
This introduces a mix task called `update_slack_api` which fetches the
Slack documentation github repo and copies the json API documentation to
`lib/slack/web/docs`. The application uses these json documentation
files to generates the modules necessary for working with the entire
Slack web API.

This change does introduce a breaking change to the bot API. Maps will
no longer use atoms as their keys and will now be binaries.
  • Loading branch information
BlakeWilliams committed Jul 4, 2016
1 parent 1926281 commit 488ad22
Show file tree
Hide file tree
Showing 98 changed files with 2,372 additions and 15 deletions.
37 changes: 34 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ by creating a new [bot integration].
[Web API page]: https://api.slack.com/web
[bot integration]: https://my.slack.com/services/new/bot

## Usage
## Installing

Add Slack to your `mix.exs` `application` and `dependencies` methods. You'll
also need [websocket_client] since hex.pm doesn't install git based
Expand All @@ -30,6 +30,8 @@ def deps do
end
```

## RTM Usage

Define a module that uses the Slack behaviour and defines the appropriate
callback methods.

Expand Down Expand Up @@ -65,7 +67,36 @@ Slack has *a lot* of message types so it's a good idea to define a callback like
above where unhandled message types don't crash your application. You can find a
list of message types and examples on the [RTM API page].

You can find more detailed documentation on the [Slack hexdocs page].
You can find more detailed documentation on the [Slack hexdocs
page][documentation].

[RTM API page]: https://api.slack.com/rtm
[Slack hexdocs page]: http://hexdocs.pm/slack/

## Web API Usage

The complete Slack Web API is implemented by generating modules/functions from
the JSON documentation. You can view this project's [documentation] for more
details.

There are two ways to authenticate your API calls. You can configure `api_token`
on `slack` that will authenticate all calls to the API automatically.

```
config :slack, api_token: "VALUE"
```

Alternatively you can pass in `%{token: "VALUE"}` to any API call in
`optional_params`. This also allows you to override the configured `api_token`
value if desired.

Quick example, getting the names of everyone on your team:

```
names = Slack.Web.Users.list(%{token: "TOKEN_HERE"})
|> Map.get("members")
|> Enum.map(fn(member) ->
member["real_name"]
end)
```

[documentation]: http://hexdocs.pm/slack/
34 changes: 34 additions & 0 deletions lib/mix/tasks/update_slack_api.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
defmodule Mix.Tasks.UpdateSlackApi do
use Mix.Task
@dir System.tmp_dir

def run(_) do
try do
System.cmd("git", ["clone", "https://github.com/slackhq/slack-api-docs", "#{@dir}/slack-api-docs"])
files
|> filter_json
|> copy_files
after
System.cmd("rm", ["-rf", "#{@dir}/slack-api-docs"])
end
end

defp files do
File.ls!("#{@dir}slack-api-docs/methods")
end

defp filter_json(files) do
Enum.filter(files, fn(file) ->
String.ends_with?(file, "json")
end)
end

defp copy_files(files) do
File.mkdir_p!("lib/slack/web/docs")
Enum.map(files, fn(file) ->
origin = "#{@dir}slack-api-docs/methods/#{file}"
dest = "lib/slack/web/docs/#{file}"
File.cp!(origin, dest)
end)
end
end
2 changes: 1 addition & 1 deletion lib/slack/state.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ defmodule Slack.State do
end

def update(%{type: "channel_joined", channel: channel}, slack) do
slack = slack
slack
|> put_in([:channels, channel.id, :members], channel.members)
|> put_in([:channels, channel.id, :is_member], true)
end
Expand Down
19 changes: 19 additions & 0 deletions lib/slack/web/docs/api.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"desc": "Checks API calling code.",

"args": {
"error": {
"required" : false,
"example" : "my_error",
"desc" : "Error response to return"
},
"foo": {
"required" : false,
"example" : "bar",
"desc" : "example property to return"
}
},

"errors": {
}
}
9 changes: 9 additions & 0 deletions lib/slack/web/docs/auth.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"desc": "Checks authentication & identity.",

"args": {
},

"errors": {
}
}
19 changes: 19 additions & 0 deletions lib/slack/web/docs/channels.archive.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"desc": "Archives a channel.",

"args": {
"channel": {
"type" : "channel",
"required" : true,
"desc" : "Channel to archive"
}
},

"errors": {
"channel_not_found" : "Value passed for `channel` was invalid.",
"already_archived" : "Channel has already been archived.",
"cant_archive_general" : "You cannot archive the general channel",
"last_ra_channel" : "You cannot archive the last channel for a restricted account",
"restricted_action" : "A team preference prevents the authenticated user from archiving."
}
}
17 changes: 17 additions & 0 deletions lib/slack/web/docs/channels.create.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"desc": "Creates a channel.",

"args": {
"name": {
"required" : true,
"desc" : "Name of channel to create",
"example" : "mychannel"
}
},

"errors": {
"name_taken" : "A channel cannot be created with the given name.",
"restricted_action": "A team preference prevents the authenticated user from creating channels.",
"no_channel" : "Value passed for `name` was empty."
}
}
52 changes: 52 additions & 0 deletions lib/slack/web/docs/channels.history.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"desc": "Fetches history of messages and events from a channel.",

"args": {
"channel": {
"type" : "channel",
"required" : true,
"desc" : "Channel to fetch history for."
},

"latest": {
"type" : "timestamp",
"required" : false,
"default" : "now",
"desc" : "End of time range of messages to include in results."
},

"oldest": {
"type" : "timestamp",
"required" : false,
"default" : "0",
"desc" : "Start of time range of messages to include in results."
},

"inclusive": {
"example" : 1,
"default" : 0,
"desc" : "Include messages with latest or oldest timestamp in results."
},

"count": {
"required" : false,
"default" : "100",
"example" : "100",
"desc" : "Number of messages to return, between 1 and 1000."
},

"unreads": {
"required" : false,
"default" : 0,
"example" : 1,
"desc" : "Include `unread_count_display` in the output?"
}
},

"errors": {

"channel_not_found" : "Value passed for `channel` was invalid.",
"invalid_ts_latest" : "Value passed for `latest` was invalid",
"invalid_ts_oldest" : "Value passed for `oldest` was invalid"
}
}
15 changes: 15 additions & 0 deletions lib/slack/web/docs/channels.info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"desc": "Gets information about a channel.",

"args": {
"channel": {
"type" : "channel",
"required" : true,
"desc" : "Channel to get info on"
}
},

"errors": {
"channel_not_found" : "Value passed for `channel` was invalid."
}
}
26 changes: 26 additions & 0 deletions lib/slack/web/docs/channels.invite.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"desc": "Invites a user to a channel.",

"args": {
"channel": {
"type" : "channel",
"required" : true,
"desc" : "Channel to invite user to."
},
"user": {
"type" : "user",
"required" : true,
"desc" : "User to invite to channel."
}
},

"errors": {
"channel_not_found" : "Value passed for `channel` was invalid.",
"user_not_found": "Value passed for `user` was invalid.",
"cant_invite_self" : "Authenticated user cannot invite themselves to a channel.",
"not_in_channel": "Authenticated user is not in the channel.",
"already_in_channel": "Invited user is already in the channel.",
"is_archived" : "Channel has been archived.",
"cant_invite" : "User cannot be invited to this channel."
}
}
18 changes: 18 additions & 0 deletions lib/slack/web/docs/channels.join.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"desc": "Joins a channel, creating it if needed.",

"args": {
"name": {
"required" : true,
"desc" : "Name of channel to join"
}
},

"errors": {
"channel_not_found" : "Value passed for `channel` was invalid.",
"name_taken" : "A channel cannot be created with the given name.",
"restricted_action": "A team preference prevents the authenticated user from creating channels.",
"no_channel" : "Value passed for `name` was empty.",
"is_archived" : "Channel has been archived."
}
}
26 changes: 26 additions & 0 deletions lib/slack/web/docs/channels.kick.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"desc": "Removes a user from a channel.",

"args": {
"channel": {
"type" : "channel",
"required" : true,
"desc" : "Channel to remove user from."
},
"user": {
"type" : "user",
"required" : true,
"desc" : "User to remove from channel."
}
},

"errors": {
"channel_not_found" : "Value passed for `channel` was invalid.",
"user_not_found" : "Value passed for `user` was invalid.",
"cant_kick_self" : "Authenticated user can't kick themselves from a channel.",
"not_in_channel" : "User was not in the channel.",
"cant_kick_from_general": "User cannot be removed from #general.",
"cant_kick_from_last_channel": "User cannot be removed from the last channel they're in.",
"restricted_action" : "A team preference prevents the authenticated user from kicking."
}
}
17 changes: 17 additions & 0 deletions lib/slack/web/docs/channels.leave.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"desc": "Leaves a channel.",

"args": {
"channel": {
"type" : "channel",
"required" : true,
"desc" : "Channel to leave"
}
},

"errors": {
"channel_not_found" : "Value passed for `channel` was invalid.",
"is_archived" : "Channel has been archived.",
"cant_leave_general" : "Authenticated user cannot leave the general channel"
}
}
11 changes: 11 additions & 0 deletions lib/slack/web/docs/channels.list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"desc": "Lists all channels in a Slack team.",

"args": {
"exclude_archived": {
"example" : 1,
"default" : 0,
"desc" : "Don't return archived channels."
}
}
}
24 changes: 24 additions & 0 deletions lib/slack/web/docs/channels.mark.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"desc": "Sets the read cursor in a channel.",

"args": {
"channel": {
"type" : "channel",
"required" : true,
"desc" : "Channel to set reading cursor in."
},

"ts": {
"type" : "timestamp",
"required" : true,
"desc" : "Timestamp of the most recently seen message."
}
},

"errors": {

"channel_not_found" : "Value passed for `channel` was invalid.",
"invalid_timestamp" : "Value passed for `timestamp` was invalid.",
"not_in_channel" : "Caller is not a member of the channel."
}
}
25 changes: 25 additions & 0 deletions lib/slack/web/docs/channels.rename.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"desc": "Renames a channel.",

"args": {
"channel": {
"type" : "channel",
"required" : true,
"desc" : "Channel to rename"
},

"name": {
"required" : true,
"desc" : "New name for channel."
}
},

"errors": {

"channel_not_found" : "Value passed for `channel` was invalid.",
"not_in_channel" : "Caller is not a member of the channel.",
"not_authorized" : "Caller cannot rename this channel",
"invalid_name" : "New name is invalid",
"name_taken" : "New channel name is taken"
}
}
Loading

0 comments on commit 488ad22

Please sign in to comment.