Skip to content

Commit 35e51fa

Browse files
committed
Add find_installation support in #877
1 parent 5a06382 commit 35e51fa

13 files changed

Lines changed: 738 additions & 40 deletions

File tree

slack_sdk/oauth/installation_store/amazon_s3/__init__.py

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,22 @@ def save(self, installation: Installation):
6060
)
6161
self.logger.debug(f"S3 put_object response: {response}")
6262

63+
# per workspace
64+
entity: str = json.dumps(installation.__dict__)
65+
response = self.s3_client.put_object(
66+
Bucket=self.bucket_name,
67+
Body=entity,
68+
Key=f"{workspace_path}/installer-latest",
69+
)
70+
self.logger.debug(f"S3 put_object response: {response}")
71+
response = self.s3_client.put_object(
72+
Bucket=self.bucket_name,
73+
Body=entity,
74+
Key=f"{workspace_path}/installer-{history_version}",
75+
)
76+
self.logger.debug(f"S3 put_object response: {response}")
77+
78+
# per workspace per user
6379
u_id = installation.user_id or none
6480
entity: str = json.dumps(installation.__dict__)
6581
response = self.s3_client.put_object(
@@ -84,6 +100,16 @@ def save(self, installation: Installation):
84100
)
85101
self.logger.debug(f"S3 put_object response: {response}")
86102

103+
# per workspace
104+
entity: str = json.dumps(installation.__dict__)
105+
response = self.s3_client.put_object(
106+
Bucket=self.bucket_name,
107+
Body=entity,
108+
Key=f"{workspace_path}/installer-latest",
109+
)
110+
self.logger.debug(f"S3 put_object response: {response}")
111+
112+
# per workspace per user
87113
u_id = installation.user_id or none
88114
entity: str = json.dumps(installation.__dict__)
89115
response = self.s3_client.put_object(
@@ -94,17 +120,30 @@ def save(self, installation: Installation):
94120
self.logger.debug(f"S3 put_object response: {response}")
95121

96122
async def async_find_bot(
97-
self, *, enterprise_id: Optional[str], team_id: Optional[str],
123+
self,
124+
*,
125+
enterprise_id: Optional[str],
126+
team_id: Optional[str],
127+
is_enterprise_install: Optional[bool] = False,
98128
) -> Optional[Bot]:
99-
return self.find_bot(enterprise_id=enterprise_id, team_id=team_id)
129+
return self.find_bot(
130+
enterprise_id=enterprise_id,
131+
team_id=team_id,
132+
is_enterprise_install=is_enterprise_install,
133+
)
100134

101135
def find_bot(
102-
self, *, enterprise_id: Optional[str], team_id: Optional[str],
136+
self,
137+
*,
138+
enterprise_id: Optional[str],
139+
team_id: Optional[str],
140+
is_enterprise_install: Optional[bool] = False,
103141
) -> Optional[Bot]:
104-
# Not yet implemented: org-apps support
105142
none = "none"
106143
e_id = enterprise_id or none
107144
t_id = team_id or none
145+
if is_enterprise_install:
146+
t_id = none
108147
workspace_path = f"{self.client_id}/{e_id}-{t_id}"
109148
try:
110149
fetch_response = self.s3_client.get_object(
@@ -118,3 +157,50 @@ def find_bot(
118157
message = f"Failed to find bot installation data for enterprise: {e_id}, team: {t_id}: {e}"
119158
self.logger.warning(message)
120159
return None
160+
161+
async def async_find_installation(
162+
self,
163+
*,
164+
enterprise_id: Optional[str],
165+
team_id: Optional[str],
166+
user_id: Optional[str] = None,
167+
is_enterprise_install: Optional[bool] = False,
168+
) -> Optional[Installation]:
169+
return self.find_installation(
170+
enterprise_id=enterprise_id,
171+
team_id=team_id,
172+
user_id=user_id,
173+
is_enterprise_install=is_enterprise_install,
174+
)
175+
176+
def find_installation(
177+
self,
178+
*,
179+
enterprise_id: Optional[str],
180+
team_id: Optional[str],
181+
user_id: Optional[str] = None,
182+
is_enterprise_install: Optional[bool] = False,
183+
) -> Optional[Installation]:
184+
none = "none"
185+
e_id = enterprise_id or none
186+
t_id = team_id or none
187+
if is_enterprise_install:
188+
t_id = none
189+
workspace_path = f"{self.client_id}/{e_id}-{t_id}"
190+
try:
191+
key = (
192+
f"{workspace_path}/installer-{user_id}-latest"
193+
if user_id
194+
else f"{workspace_path}/installer-latest"
195+
)
196+
fetch_response = self.s3_client.get_object(
197+
Bucket=self.bucket_name, Key=key,
198+
)
199+
self.logger.debug(f"S3 get_object response: {fetch_response}")
200+
body = fetch_response["Body"].read().decode("utf-8")
201+
data = json.loads(body)
202+
return Installation(**data)
203+
except Exception as e: # skipcq: PYL-W0703
204+
message = f"Failed to find an installation data for enterprise: {e_id}, team: {t_id}: {e}"
205+
self.logger.warning(message)
206+
return None

slack_sdk/oauth/installation_store/async_cacheable_installation_store.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
class AsyncCacheableInstallationStore(AsyncInstallationStore):
1111
underlying: AsyncInstallationStore
1212
cached_bots: Dict[str, Bot]
13+
cached_installations: Dict[str, Installation]
1314

1415
def __init__(self, installation_store: AsyncInstallationStore):
1516
"""A simple memory cache wrapper for any installation stores.
@@ -18,6 +19,7 @@ def __init__(self, installation_store: AsyncInstallationStore):
1819
"""
1920
self.underlying = installation_store
2021
self.cached_bots = {}
22+
self.cached_installations = {}
2123

2224
@property
2325
def logger(self) -> Logger:
@@ -27,14 +29,45 @@ async def async_save(self, installation: Installation):
2729
return await self.underlying.async_save(installation)
2830

2931
async def async_find_bot(
30-
self, *, enterprise_id: Optional[str], team_id: Optional[str]
32+
self,
33+
*,
34+
enterprise_id: Optional[str],
35+
team_id: Optional[str],
36+
is_enterprise_install: Optional[bool] = False,
3137
) -> Optional[Bot]:
38+
if is_enterprise_install or team_id is None:
39+
team_id = ""
3240
key = f"{enterprise_id}-{team_id}"
3341
if key in self.cached_bots:
3442
return self.cached_bots[key]
3543
bot = await self.underlying.async_find_bot(
36-
enterprise_id=enterprise_id, team_id=team_id
44+
enterprise_id=enterprise_id,
45+
team_id=team_id,
46+
is_enterprise_install=is_enterprise_install,
3747
)
3848
if bot:
3949
self.cached_bots[key] = bot
4050
return bot
51+
52+
async def async_find_installation(
53+
self,
54+
*,
55+
enterprise_id: Optional[str],
56+
team_id: Optional[str],
57+
user_id: Optional[str] = None,
58+
is_enterprise_install: Optional[bool] = False,
59+
) -> Optional[Installation]:
60+
if is_enterprise_install or team_id is None:
61+
team_id = ""
62+
key = f"{enterprise_id}-{team_id}-{user_id}"
63+
if key in self.cached_installations:
64+
return self.cached_installations[key]
65+
installation = await self.underlying.async_find_installation(
66+
enterprise_id=enterprise_id,
67+
team_id=team_id,
68+
user_id=user_id,
69+
is_enterprise_install=is_enterprise_install,
70+
)
71+
if installation:
72+
self.cached_installations[key] = installation
73+
return installation

slack_sdk/oauth/installation_store/async_installation_store.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,20 @@ async def async_save(self, installation: Installation):
1414
raise NotImplementedError()
1515

1616
async def async_find_bot(
17-
self, *, enterprise_id: Optional[str], team_id: Optional[str],
17+
self,
18+
*,
19+
enterprise_id: Optional[str],
20+
team_id: Optional[str],
21+
is_enterprise_install: Optional[bool] = False,
1822
) -> Optional[Bot]:
1923
raise NotImplementedError()
24+
25+
async def async_find_installation(
26+
self,
27+
*,
28+
enterprise_id: Optional[str],
29+
team_id: Optional[str],
30+
user_id: Optional[str] = None,
31+
is_enterprise_install: Optional[bool] = False,
32+
) -> Optional[Installation]:
33+
raise NotImplementedError()

slack_sdk/oauth/installation_store/cacheable_installation_store.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
class CacheableInstallationStore(InstallationStore):
99
underlying: InstallationStore
1010
cached_bots: Dict[str, Bot]
11+
cached_installations: Dict[str, Installation]
1112

1213
def __init__(self, installation_store: InstallationStore):
1314
"""A simple memory cache wrapper for any installation stores.
@@ -16,6 +17,7 @@ def __init__(self, installation_store: InstallationStore):
1617
"""
1718
self.underlying = installation_store
1819
self.cached_bots = {}
20+
self.cached_installations = {}
1921

2022
@property
2123
def logger(self) -> Logger:
@@ -25,12 +27,45 @@ def save(self, installation: Installation):
2527
return self.underlying.save(installation)
2628

2729
def find_bot(
28-
self, *, enterprise_id: Optional[str], team_id: Optional[str]
30+
self,
31+
*,
32+
enterprise_id: Optional[str],
33+
team_id: Optional[str],
34+
is_enterprise_install: Optional[bool] = False,
2935
) -> Optional[Bot]:
36+
if is_enterprise_install or team_id is None:
37+
team_id = ""
3038
key = f"{enterprise_id}-{team_id}"
3139
if key in self.cached_bots:
3240
return self.cached_bots[key]
33-
bot = self.underlying.find_bot(enterprise_id=enterprise_id, team_id=team_id)
41+
bot = self.underlying.find_bot(
42+
enterprise_id=enterprise_id,
43+
team_id=team_id,
44+
is_enterprise_install=is_enterprise_install,
45+
)
3446
if bot:
3547
self.cached_bots[key] = bot
3648
return bot
49+
50+
def find_installation(
51+
self,
52+
*,
53+
enterprise_id: Optional[str],
54+
team_id: Optional[str],
55+
user_id: Optional[str] = None,
56+
is_enterprise_install: Optional[bool] = False,
57+
) -> Optional[Installation]:
58+
if is_enterprise_install or team_id is None:
59+
team_id = ""
60+
key = f"{enterprise_id}-{team_id}={user_id}"
61+
if key in self.cached_installations:
62+
return self.cached_installations[key]
63+
installation = self.underlying.find_installation(
64+
enterprise_id=enterprise_id,
65+
team_id=team_id,
66+
user_id=user_id,
67+
is_enterprise_install=is_enterprise_install,
68+
)
69+
if installation:
70+
self.cached_installations[key] = installation
71+
return installation

slack_sdk/oauth/installation_store/file/__init__.py

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ def save(self, installation: Installation):
5353
with open(f"{team_installation_dir}/bot-{history_version}", "w") as f:
5454
f.write(entity)
5555

56+
# per workspace
57+
entity: str = json.dumps(installation.__dict__)
58+
with open(f"{team_installation_dir}/installer-latest", "w") as f:
59+
f.write(entity)
60+
with open(f"{team_installation_dir}/installer-{history_version}", "w") as f:
61+
f.write(entity)
62+
63+
# per workspace per user
5664
u_id = installation.user_id or none
5765
entity: str = json.dumps(installation.__dict__)
5866
with open(f"{team_installation_dir}/installer-{u_id}-latest", "w") as f:
@@ -74,17 +82,30 @@ def save(self, installation: Installation):
7482
f.write(entity)
7583

7684
async def async_find_bot(
77-
self, *, enterprise_id: Optional[str], team_id: Optional[str],
85+
self,
86+
*,
87+
enterprise_id: Optional[str],
88+
team_id: Optional[str],
89+
is_enterprise_install: Optional[bool] = False,
7890
) -> Optional[Bot]:
79-
return self.find_bot(enterprise_id=enterprise_id, team_id=team_id)
91+
return self.find_bot(
92+
enterprise_id=enterprise_id,
93+
team_id=team_id,
94+
is_enterprise_install=is_enterprise_install,
95+
)
8096

8197
def find_bot(
82-
self, *, enterprise_id: Optional[str], team_id: Optional[str],
98+
self,
99+
*,
100+
enterprise_id: Optional[str],
101+
team_id: Optional[str],
102+
is_enterprise_install: Optional[bool] = False,
83103
) -> Optional[Bot]:
84-
# Not yet implemented: org-apps support
85104
none = "none"
86105
e_id = enterprise_id or none
87106
t_id = team_id or none
107+
if is_enterprise_install:
108+
t_id = none
88109
bot_filepath = f"{self.base_dir}/{e_id}-{t_id}/bot-latest"
89110
try:
90111
with open(bot_filepath) as f:
@@ -95,6 +116,49 @@ def find_bot(
95116
self.logger.warning(message)
96117
return None
97118

119+
async def async_find_installation(
120+
self,
121+
*,
122+
enterprise_id: Optional[str],
123+
team_id: Optional[str],
124+
user_id: Optional[str] = None,
125+
is_enterprise_install: Optional[bool] = False,
126+
) -> Optional[Installation]:
127+
return self.find_installation(
128+
enterprise_id=enterprise_id,
129+
team_id=team_id,
130+
user_id=user_id,
131+
is_enterprise_install=is_enterprise_install,
132+
)
133+
134+
def find_installation(
135+
self,
136+
*,
137+
enterprise_id: Optional[str],
138+
team_id: Optional[str],
139+
user_id: Optional[str] = None,
140+
is_enterprise_install: Optional[bool] = False,
141+
) -> Optional[Installation]:
142+
none = "none"
143+
e_id = enterprise_id or none
144+
t_id = team_id or none
145+
if is_enterprise_install:
146+
t_id = none
147+
installation_filepath = f"{self.base_dir}/{e_id}-{t_id}/installer-latest"
148+
if user_id is not None:
149+
installation_filepath = (
150+
f"{self.base_dir}/{e_id}-{t_id}/installer-{user_id}-latest"
151+
)
152+
153+
try:
154+
with open(installation_filepath) as f:
155+
data = json.loads(f.read())
156+
return Installation(**data)
157+
except FileNotFoundError as e:
158+
message = f"Failed to find an installation data for enterprise: {e_id}, team: {t_id}: {e}"
159+
self.logger.warning(message)
160+
return None
161+
98162
@staticmethod
99163
def _mkdir(path: Union[str, Path]):
100164
if isinstance(path, str):

slack_sdk/oauth/installation_store/installation_store.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,20 @@ def save(self, installation: Installation):
1414
raise NotImplementedError()
1515

1616
def find_bot(
17-
self, *, enterprise_id: Optional[str], team_id: Optional[str],
17+
self,
18+
*,
19+
enterprise_id: Optional[str],
20+
team_id: Optional[str],
21+
is_enterprise_install: Optional[bool] = False,
1822
) -> Optional[Bot]:
1923
raise NotImplementedError()
24+
25+
def find_installation(
26+
self,
27+
*,
28+
enterprise_id: Optional[str],
29+
team_id: Optional[str],
30+
user_id: Optional[str] = None,
31+
is_enterprise_install: Optional[bool] = False,
32+
) -> Optional[Installation]:
33+
raise NotImplementedError()

0 commit comments

Comments
 (0)