Skip to content

Commit 3e2d7e8

Browse files
authored
Fix #1427 Add apps.manifest.* & tooling.tokens.rotate API support (#1430)
1 parent ed02c65 commit 3e2d7e8

File tree

5 files changed

+473
-7
lines changed

5 files changed

+473
-7
lines changed
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
import os
2+
import unittest
3+
4+
from slack_sdk.web import WebClient
5+
6+
7+
class TestWebClient(unittest.TestCase):
8+
"""Runs integration tests with real Slack API"""
9+
10+
def setUp(self):
11+
pass
12+
13+
def tearDown(self):
14+
pass
15+
16+
def test_operations(self):
17+
token = os.environ["SLACK_SDK_TEST_TOOLING_TOKEN"] # xoxe.xoxp-...
18+
client = WebClient(token)
19+
client.apps_manifest_validate(manifest=STR_MANIFEST)
20+
client.apps_manifest_validate(manifest=DICT_MANIFEST)
21+
22+
response = client.apps_manifest_create(manifest=STR_MANIFEST)
23+
app_id = response["app_id"]
24+
try:
25+
client.apps_manifest_update(app_id=app_id, manifest=DICT_MANIFEST)
26+
client.apps_manifest_export(app_id=app_id)
27+
finally:
28+
client.apps_manifest_delete(app_id=app_id)
29+
30+
31+
STR_MANIFEST = """{
32+
"display_information": {
33+
"name": "manifest-sandbox"
34+
},
35+
"features": {
36+
"app_home": {
37+
"home_tab_enabled": true,
38+
"messages_tab_enabled": false,
39+
"messages_tab_read_only_enabled": false
40+
},
41+
"bot_user": {
42+
"display_name": "manifest-sandbox",
43+
"always_online": true
44+
},
45+
"shortcuts": [
46+
{
47+
"name": "message one",
48+
"type": "message",
49+
"callback_id": "m",
50+
"description": "message"
51+
},
52+
{
53+
"name": "global one",
54+
"type": "global",
55+
"callback_id": "g",
56+
"description": "global"
57+
}
58+
],
59+
"slash_commands": [
60+
{
61+
"command": "/hey",
62+
"url": "https://www.example.com/",
63+
"description": "What's up?",
64+
"usage_hint": "What's up?",
65+
"should_escape": true
66+
}
67+
],
68+
"unfurl_domains": [
69+
"example.com"
70+
]
71+
},
72+
"oauth_config": {
73+
"redirect_urls": [
74+
"https://www.example.com/foo"
75+
],
76+
"scopes": {
77+
"user": [
78+
"search:read",
79+
"channels:read",
80+
"groups:read",
81+
"mpim:read"
82+
],
83+
"bot": [
84+
"commands",
85+
"incoming-webhook",
86+
"app_mentions:read",
87+
"links:read"
88+
]
89+
}
90+
},
91+
"settings": {
92+
"allowed_ip_address_ranges": [
93+
"123.123.123.123/32"
94+
],
95+
"event_subscriptions": {
96+
"request_url": "https://www.example.com/slack/events",
97+
"user_events": [
98+
"member_joined_channel"
99+
],
100+
"bot_events": [
101+
"app_mention",
102+
"link_shared"
103+
]
104+
},
105+
"interactivity": {
106+
"is_enabled": true,
107+
"request_url": "https://www.example.com/",
108+
"message_menu_options_url": "https://www.example.com/"
109+
},
110+
"org_deploy_enabled": true,
111+
"socket_mode_enabled": false,
112+
"token_rotation_enabled": true
113+
}
114+
}
115+
"""
116+
117+
DICT_MANIFEST = {
118+
"display_information": {
119+
"name": "manifest-sandbox"
120+
},
121+
"features": {
122+
"app_home": {
123+
"home_tab_enabled": True,
124+
"messages_tab_enabled": False,
125+
"messages_tab_read_only_enabled": False
126+
},
127+
"bot_user": {
128+
"display_name": "manifest-sandbox",
129+
"always_online": True
130+
},
131+
"shortcuts": [
132+
{
133+
"name": "message one",
134+
"type": "message",
135+
"callback_id": "m",
136+
"description": "message"
137+
},
138+
{
139+
"name": "global one",
140+
"type": "global",
141+
"callback_id": "g",
142+
"description": "global"
143+
}
144+
],
145+
"slash_commands": [
146+
{
147+
"command": "/hey",
148+
"url": "https://www.example.com/",
149+
"description": "What's up?",
150+
"usage_hint": "What's up?",
151+
"should_escape": True
152+
}
153+
],
154+
"unfurl_domains": [
155+
"example.com"
156+
]
157+
},
158+
"oauth_config": {
159+
"redirect_urls": [
160+
"https://www.example.com/foo"
161+
],
162+
"scopes": {
163+
"user": [
164+
"search:read",
165+
"channels:read",
166+
"groups:read",
167+
"mpim:read"
168+
],
169+
"bot": [
170+
"commands",
171+
"incoming-webhook",
172+
"app_mentions:read",
173+
"links:read"
174+
]
175+
}
176+
},
177+
"settings": {
178+
"allowed_ip_address_ranges": [
179+
"123.123.123.123/32"
180+
],
181+
"event_subscriptions": {
182+
"request_url": "https://www.example.com/slack/events",
183+
"user_events": [
184+
"member_joined_channel"
185+
],
186+
"bot_events": [
187+
"app_mention",
188+
"link_shared"
189+
]
190+
},
191+
"interactivity": {
192+
"is_enabled": True,
193+
"request_url": "https://www.example.com/",
194+
"message_menu_options_url": "https://www.example.com/"
195+
},
196+
"org_deploy_enabled": True,
197+
"socket_mode_enabled": False,
198+
"token_rotation_enabled": True
199+
}
200+
}

slack_sdk/web/async_client.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,91 @@ async def apps_uninstall(
19281928
kwargs.update({"client_id": client_id, "client_secret": client_secret})
19291929
return await self.api_call("apps.uninstall", params=kwargs)
19301930

1931+
async def apps_manifest_create(
1932+
self,
1933+
*,
1934+
manifest: Union[str, Dict[str, Any]],
1935+
**kwargs,
1936+
) -> AsyncSlackResponse:
1937+
"""Create an app from an app manifest
1938+
https://api.slack.com/methods/apps.manifest.create
1939+
"""
1940+
if isinstance(manifest, str):
1941+
kwargs.update({"manifest": manifest})
1942+
else:
1943+
kwargs.update({"manifest": json.dumps(manifest)})
1944+
return await self.api_call("apps.manifest.create", params=kwargs)
1945+
1946+
async def apps_manifest_delete(
1947+
self,
1948+
*,
1949+
app_id: str,
1950+
**kwargs,
1951+
) -> AsyncSlackResponse:
1952+
"""Permanently deletes an app created through app manifests
1953+
https://api.slack.com/methods/apps.manifest.delete
1954+
"""
1955+
kwargs.update({"app_id": app_id})
1956+
return await self.api_call("apps.manifest.delete", params=kwargs)
1957+
1958+
async def apps_manifest_export(
1959+
self,
1960+
*,
1961+
app_id: str,
1962+
**kwargs,
1963+
) -> AsyncSlackResponse:
1964+
"""Export an app manifest from an existing app
1965+
https://api.slack.com/methods/apps.manifest.export
1966+
"""
1967+
kwargs.update({"app_id": app_id})
1968+
return await self.api_call("apps.manifest.export", params=kwargs)
1969+
1970+
async def apps_manifest_update(
1971+
self,
1972+
*,
1973+
app_id: str,
1974+
manifest: Union[str, Dict[str, Any]],
1975+
**kwargs,
1976+
) -> AsyncSlackResponse:
1977+
"""Update an app from an app manifest
1978+
https://api.slack.com/methods/apps.manifest.update
1979+
"""
1980+
if isinstance(manifest, str):
1981+
kwargs.update({"manifest": manifest})
1982+
else:
1983+
kwargs.update({"manifest": json.dumps(manifest)})
1984+
kwargs.update({"app_id": app_id})
1985+
return await self.api_call("apps.manifest.update", params=kwargs)
1986+
1987+
async def apps_manifest_validate(
1988+
self,
1989+
*,
1990+
manifest: Union[str, Dict[str, Any]],
1991+
app_id: Optional[str] = None,
1992+
**kwargs,
1993+
) -> AsyncSlackResponse:
1994+
"""Validate an app manifest
1995+
https://api.slack.com/methods/apps.manifest.validate
1996+
"""
1997+
if isinstance(manifest, str):
1998+
kwargs.update({"manifest": manifest})
1999+
else:
2000+
kwargs.update({"manifest": json.dumps(manifest)})
2001+
kwargs.update({"app_id": app_id})
2002+
return await self.api_call("apps.manifest.validate", params=kwargs)
2003+
2004+
async def tooling_tokens_rotate(
2005+
self,
2006+
*,
2007+
refresh_token: str,
2008+
**kwargs,
2009+
) -> AsyncSlackResponse:
2010+
"""Exchanges a refresh token for a new app configuration token
2011+
https://api.slack.com/methods/tooling.tokens.rotate
2012+
"""
2013+
kwargs.update({"refresh_token": refresh_token})
2014+
return await self.api_call("tooling.tokens.rotate", params=kwargs)
2015+
19312016
async def auth_revoke(
19322017
self,
19332018
*,

slack_sdk/web/client.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1919,6 +1919,91 @@ def apps_uninstall(
19191919
kwargs.update({"client_id": client_id, "client_secret": client_secret})
19201920
return self.api_call("apps.uninstall", params=kwargs)
19211921

1922+
def apps_manifest_create(
1923+
self,
1924+
*,
1925+
manifest: Union[str, Dict[str, Any]],
1926+
**kwargs,
1927+
) -> SlackResponse:
1928+
"""Create an app from an app manifest
1929+
https://api.slack.com/methods/apps.manifest.create
1930+
"""
1931+
if isinstance(manifest, str):
1932+
kwargs.update({"manifest": manifest})
1933+
else:
1934+
kwargs.update({"manifest": json.dumps(manifest)})
1935+
return self.api_call("apps.manifest.create", params=kwargs)
1936+
1937+
def apps_manifest_delete(
1938+
self,
1939+
*,
1940+
app_id: str,
1941+
**kwargs,
1942+
) -> SlackResponse:
1943+
"""Permanently deletes an app created through app manifests
1944+
https://api.slack.com/methods/apps.manifest.delete
1945+
"""
1946+
kwargs.update({"app_id": app_id})
1947+
return self.api_call("apps.manifest.delete", params=kwargs)
1948+
1949+
def apps_manifest_export(
1950+
self,
1951+
*,
1952+
app_id: str,
1953+
**kwargs,
1954+
) -> SlackResponse:
1955+
"""Export an app manifest from an existing app
1956+
https://api.slack.com/methods/apps.manifest.export
1957+
"""
1958+
kwargs.update({"app_id": app_id})
1959+
return self.api_call("apps.manifest.export", params=kwargs)
1960+
1961+
def apps_manifest_update(
1962+
self,
1963+
*,
1964+
app_id: str,
1965+
manifest: Union[str, Dict[str, Any]],
1966+
**kwargs,
1967+
) -> SlackResponse:
1968+
"""Update an app from an app manifest
1969+
https://api.slack.com/methods/apps.manifest.update
1970+
"""
1971+
if isinstance(manifest, str):
1972+
kwargs.update({"manifest": manifest})
1973+
else:
1974+
kwargs.update({"manifest": json.dumps(manifest)})
1975+
kwargs.update({"app_id": app_id})
1976+
return self.api_call("apps.manifest.update", params=kwargs)
1977+
1978+
def apps_manifest_validate(
1979+
self,
1980+
*,
1981+
manifest: Union[str, Dict[str, Any]],
1982+
app_id: Optional[str] = None,
1983+
**kwargs,
1984+
) -> SlackResponse:
1985+
"""Validate an app manifest
1986+
https://api.slack.com/methods/apps.manifest.validate
1987+
"""
1988+
if isinstance(manifest, str):
1989+
kwargs.update({"manifest": manifest})
1990+
else:
1991+
kwargs.update({"manifest": json.dumps(manifest)})
1992+
kwargs.update({"app_id": app_id})
1993+
return self.api_call("apps.manifest.validate", params=kwargs)
1994+
1995+
def tooling_tokens_rotate(
1996+
self,
1997+
*,
1998+
refresh_token: str,
1999+
**kwargs,
2000+
) -> SlackResponse:
2001+
"""Exchanges a refresh token for a new app configuration token
2002+
https://api.slack.com/methods/tooling.tokens.rotate
2003+
"""
2004+
kwargs.update({"refresh_token": refresh_token})
2005+
return self.api_call("tooling.tokens.rotate", params=kwargs)
2006+
19222007
def auth_revoke(
19232008
self,
19242009
*,

0 commit comments

Comments
 (0)