Skip to content

Commit f640f31

Browse files
Merge pull request #387 from supertokens/fix/mt-dashboard
fix: Update dashboard recipe based on multitenancy
2 parents 8a0ac5a + 07da54a commit f640f31

File tree

2 files changed

+140
-81
lines changed

2 files changed

+140
-81
lines changed

supertokens_python/recipe/dashboard/recipe.py

Lines changed: 136 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,11 @@
1313
# under the License.
1414
from __future__ import annotations
1515

16-
import re
1716
from os import environ
1817
from typing import TYPE_CHECKING, Awaitable, Callable, List, Optional, Union, Dict, Any
1918

2019
from supertokens_python.normalised_url_path import NormalisedURLPath
21-
from supertokens_python.recipe_module import APIHandled, RecipeModule, ApiIdWithTenantId
20+
from supertokens_python.recipe_module import APIHandled, RecipeModule
2221

2322
from .api import (
2423
api_key_protector,
@@ -47,7 +46,6 @@
4746
from .exceptions import SuperTokensDashboardError
4847
from .interfaces import APIInterface, APIOptions
4948
from .recipe_implementation import RecipeImplementation
50-
from ..multitenancy.constants import DEFAULT_TENANT_ID
5149

5250
if TYPE_CHECKING:
5351
from supertokens_python.framework.request import BaseRequest
@@ -56,7 +54,7 @@
5654
from supertokens_python.types import APIResponse
5755

5856
from supertokens_python.exceptions import SuperTokensError, raise_general_exception
59-
from supertokens_python.recipe.multitenancy.recipe import MultitenancyRecipe
57+
from supertokens_python.recipe.dashboard.utils import get_api_path_with_dashboard_base
6058

6159
from .constants import (
6260
DASHBOARD_ANALYTICS_API,
@@ -77,8 +75,6 @@
7775
)
7876
from .utils import (
7977
InputOverrideConfig,
80-
get_api_if_matched,
81-
is_api_path,
8278
validate_and_normalise_user_input,
8379
)
8480

@@ -119,14 +115,140 @@ def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
119115
)
120116

121117
def get_apis_handled(self) -> List[APIHandled]:
122-
# Normally this array is used by the SDK to decide whether the recipe
123-
# handles a specific API path and method and then returns the ID.
124-
125-
# However, for the dashboard recipe this logic is fully custom and handled inside the
126-
# `return_api_id_if_can_handle_request` method of this class. Since this array is never
127-
# used for this recipe, we simply return an empty array.
128-
129-
return []
118+
return [
119+
APIHandled(
120+
NormalisedURLPath(get_api_path_with_dashboard_base("/")),
121+
"get",
122+
DASHBOARD_API,
123+
False,
124+
),
125+
APIHandled(
126+
NormalisedURLPath(
127+
get_api_path_with_dashboard_base(EMAIL_PASSWORD_SIGN_IN)
128+
),
129+
"post",
130+
EMAIL_PASSWORD_SIGN_IN,
131+
False,
132+
),
133+
APIHandled(
134+
NormalisedURLPath(get_api_path_with_dashboard_base(VALIDATE_KEY_API)),
135+
"post",
136+
VALIDATE_KEY_API,
137+
False,
138+
),
139+
APIHandled(
140+
NormalisedURLPath(
141+
get_api_path_with_dashboard_base(EMAIL_PASSSWORD_SIGNOUT)
142+
),
143+
"post",
144+
EMAIL_PASSSWORD_SIGNOUT,
145+
False,
146+
),
147+
APIHandled(
148+
NormalisedURLPath(get_api_path_with_dashboard_base(USERS_LIST_GET_API)),
149+
"get",
150+
USERS_LIST_GET_API,
151+
False,
152+
),
153+
APIHandled(
154+
NormalisedURLPath(get_api_path_with_dashboard_base(USERS_COUNT_API)),
155+
"get",
156+
USERS_COUNT_API,
157+
False,
158+
),
159+
APIHandled(
160+
NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
161+
"get",
162+
USER_API,
163+
False,
164+
),
165+
APIHandled(
166+
NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
167+
"post",
168+
USER_API,
169+
False,
170+
),
171+
APIHandled(
172+
NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
173+
"put",
174+
USER_API,
175+
False,
176+
),
177+
APIHandled(
178+
NormalisedURLPath(get_api_path_with_dashboard_base(USER_API)),
179+
"delete",
180+
USER_API,
181+
False,
182+
),
183+
APIHandled(
184+
NormalisedURLPath(
185+
get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
186+
),
187+
"get",
188+
USER_EMAIL_VERIFY_API,
189+
False,
190+
),
191+
APIHandled(
192+
NormalisedURLPath(
193+
get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
194+
),
195+
"put",
196+
USER_EMAIL_VERIFY_API,
197+
False,
198+
),
199+
APIHandled(
200+
NormalisedURLPath(get_api_path_with_dashboard_base(USER_METADATA_API)),
201+
"get",
202+
USER_METADATA_API,
203+
False,
204+
),
205+
APIHandled(
206+
NormalisedURLPath(get_api_path_with_dashboard_base(USER_METADATA_API)),
207+
"put",
208+
USER_METADATA_API,
209+
False,
210+
),
211+
APIHandled(
212+
NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
213+
"get",
214+
USER_SESSION_API,
215+
False,
216+
),
217+
APIHandled(
218+
NormalisedURLPath(get_api_path_with_dashboard_base(USER_PASSWORD_API)),
219+
"put",
220+
USER_PASSWORD_API,
221+
False,
222+
),
223+
APIHandled(
224+
NormalisedURLPath(
225+
get_api_path_with_dashboard_base(USER_EMAIL_VERIFY_API)
226+
),
227+
"post",
228+
USER_EMAIL_VERIFY_API,
229+
False,
230+
),
231+
APIHandled(
232+
NormalisedURLPath(get_api_path_with_dashboard_base(SEARCH_TAGS_API)),
233+
"get",
234+
SEARCH_TAGS_API,
235+
False,
236+
),
237+
APIHandled(
238+
NormalisedURLPath(
239+
get_api_path_with_dashboard_base(DASHBOARD_ANALYTICS_API)
240+
),
241+
"post",
242+
DASHBOARD_ANALYTICS_API,
243+
False,
244+
),
245+
APIHandled(
246+
NormalisedURLPath(get_api_path_with_dashboard_base(TENANTS_LIST_API)),
247+
"get",
248+
TENANTS_LIST_API,
249+
False,
250+
),
251+
]
130252

131253
async def handle_api_request(
132254
self,
@@ -261,70 +383,3 @@ def reset():
261383
):
262384
raise_general_exception("calling testing function in non testing env")
263385
DashboardRecipe.__instance = None
264-
265-
async def return_api_id_if_can_handle_request(
266-
self, path: NormalisedURLPath, method: str, user_context: Dict[str, Any]
267-
) -> Union[ApiIdWithTenantId, None]:
268-
dashboard_bundle_path = self.app_info.api_base_path.append(
269-
NormalisedURLPath(DASHBOARD_API)
270-
)
271-
272-
base_path_str = self.app_info.api_base_path.get_as_string_dangerous()
273-
path_str = path.get_as_string_dangerous()
274-
regex = rf"^{base_path_str}(?:/([a-zA-Z0-9-]+))?(/.*)$"
275-
# some examples against for above regex:
276-
# books => match = None
277-
# public/books => match = None
278-
# /books => match.group(1) = None, match.group(2) = /dashboard
279-
# /public/books => match.group(1) = 'public', match.group(2) = '/books'
280-
# /public/book/1 => match.group(1) = 'public', match.group(2) = '/book/1'
281-
282-
match = re.match(regex, path_str)
283-
match_group_1 = match.group(1) if match is not None else None
284-
match_group_2 = match.group(2) if match is not None else None
285-
286-
tenant_id: str = DEFAULT_TENANT_ID
287-
remaining_path: Optional[NormalisedURLPath] = None
288-
289-
if (
290-
match is not None
291-
and isinstance(match_group_1, str)
292-
and isinstance(match_group_2, str)
293-
):
294-
tenant_id = match_group_1
295-
remaining_path = NormalisedURLPath(match_group_2)
296-
297-
mt_recipe = MultitenancyRecipe.get_instance()
298-
299-
if is_api_path(path, self.app_info.api_base_path) or (
300-
remaining_path is not None
301-
and is_api_path(
302-
path,
303-
self.app_info.api_base_path.append(NormalisedURLPath(f"/{tenant_id}")),
304-
)
305-
):
306-
# check remainingPath first as path that contains tenantId might match as well
307-
# since getApiIdIfMatched uses endsWith to match
308-
if remaining_path is not None:
309-
id_ = get_api_if_matched(remaining_path, method)
310-
if id_ is not None:
311-
final_tenant_id = (
312-
await mt_recipe.recipe_implementation.get_tenant_id(
313-
DEFAULT_TENANT_ID if tenant_id is None else tenant_id,
314-
user_context,
315-
)
316-
)
317-
return ApiIdWithTenantId(id_, final_tenant_id)
318-
319-
id_ = get_api_if_matched(path, method)
320-
if id_ is not None:
321-
final_tenant_id = await mt_recipe.recipe_implementation.get_tenant_id(
322-
DEFAULT_TENANT_ID, user_context
323-
)
324-
return ApiIdWithTenantId(id_, final_tenant_id)
325-
326-
if path.startswith(dashboard_bundle_path):
327-
return ApiIdWithTenantId(DASHBOARD_API, DEFAULT_TENANT_ID)
328-
329-
# tenantId is not supported for bundlePath, so not matching for it
330-
return None

supertokens_python/recipe/dashboard/utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,3 +408,7 @@ def validate_api_key(
408408
# We receieve the api key as `Bearer API_KEY`, this retrieves just the key
409409
api_key_header_value = api_key_header_value.split(" ")[1]
410410
return api_key_header_value == config.api_key
411+
412+
413+
def get_api_path_with_dashboard_base(path: str) -> str:
414+
return DASHBOARD_API + path

0 commit comments

Comments
 (0)