Skip to content

feat: Add search APIs to the dashboard recipe #304

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Apr 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## unreleased

## [0.12.6] - 2023-03-31

- Adds search APIs to the dashboard recipe

## [0.12.5] - 2023-03-30

- Adds a telemetry API to the dashboard recipe
Expand Down
3 changes: 2 additions & 1 deletion coreDriverInterfaceSupported.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"2.16",
"2.17",
"2.18",
"2.19"
"2.19",
"2.20"
]
}
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@

setup(
name="supertokens_python",
version="0.12.5",
version="0.12.6",
author="SuperTokens",
license="Apache 2.0",
author_email="[email protected]",
Expand Down
5 changes: 3 additions & 2 deletions supertokens_python/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@
"2.17",
"2.18",
"2.19",
"2.20",
]
VERSION = "0.12.5"
VERSION = "0.12.6"
TELEMETRY = "/telemetry"
USER_COUNT = "/users/count"
USER_DELETE = "/user/remove"
Expand All @@ -37,4 +38,4 @@
FDI_KEY_HEADER = "fdi-version"
API_VERSION = "/apiversion"
API_VERSION_HEADER = "cdi-version"
DASHBOARD_VERSION = "0.5"
DASHBOARD_VERSION = "0.6"
5 changes: 4 additions & 1 deletion supertokens_python/framework/django/django_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from __future__ import annotations

import json
from typing import TYPE_CHECKING, Any, Union
from typing import TYPE_CHECKING, Any, Dict, Union
from urllib.parse import parse_qsl

from supertokens_python.framework.request import BaseRequest
Expand All @@ -35,6 +35,9 @@ def get_query_param(
) -> Union[str, None]:
return self.request.GET.get(key, default)

def get_query_params(self) -> Dict[str, Any]:
return self.request.GET.dict()

async def json(self) -> Union[Any, None]:
try:
body = json.loads(self.request.body)
Expand Down
5 changes: 4 additions & 1 deletion supertokens_python/framework/fastapi/fastapi_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# under the License.
from __future__ import annotations

from typing import TYPE_CHECKING, Any, Union
from typing import TYPE_CHECKING, Any, Dict, Union
from urllib.parse import parse_qsl

from supertokens_python.framework.request import BaseRequest
Expand All @@ -35,6 +35,9 @@ def get_query_param(
) -> Union[str, None]:
return self.request.query_params.get(key, default)

def get_query_params(self) -> Dict[str, Any]:
return dict(self.request.query_params.items()) # type: ignore

async def json(self) -> Union[Any, None]:
try:
return await self.request.json()
Expand Down
3 changes: 3 additions & 0 deletions supertokens_python/framework/flask/flask_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ def __init__(self, req: Request):
def get_query_param(self, key: str, default: Union[str, None] = None):
return self.request.args.get(key, default)

def get_query_params(self) -> Dict[str, Any]:
return self.request.args.to_dict()

async def json(self) -> Union[Any, None]:
try:
return self.request.get_json()
Expand Down
4 changes: 4 additions & 0 deletions supertokens_python/framework/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ def get_query_param(
) -> Union[str, None]:
pass

@abstractmethod
def get_query_params(self) -> Dict[str, Any]:
pass

@abstractmethod
async def json(self) -> Union[Any, None]:
pass
Expand Down
2 changes: 2 additions & 0 deletions supertokens_python/recipe/dashboard/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .analytics import handle_analytics_post
from .api_key_protector import api_key_protector
from .dashboard import handle_dashboard_api
from .search.getTags import handle_get_tags
from .signin import handle_emailpassword_signin_api
from .signout import handle_emailpassword_signout_api
from .userdetails.user_delete import handle_user_delete
Expand Down Expand Up @@ -50,5 +51,6 @@
"handle_email_verify_token_post",
"handle_emailpassword_signin_api",
"handle_emailpassword_signout_api",
"handle_get_tags",
"handle_analytics_post",
]
12 changes: 12 additions & 0 deletions supertokens_python/recipe/dashboard/api/implementation.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from supertokens_python import Supertokens
from supertokens_python.normalised_url_domain import NormalisedURLDomain
from supertokens_python.normalised_url_path import NormalisedURLPath
from supertokens_python.querier import Querier
from supertokens_python.utils import is_version_gte

from ..constants import DASHBOARD_API
from ..interfaces import APIInterface
Expand Down Expand Up @@ -54,6 +56,14 @@ async def dashboard_get(
NormalisedURLPath(DASHBOARD_API)
).get_as_string_dangerous()

is_search_enabled: bool = False
querier = Querier.get_instance(options.recipe_id)
cdiVersion = await querier.get_api_version()
if not cdiVersion:
is_search_enabled = True
elif is_version_gte(cdiVersion, "2.20"):
is_search_enabled = True

return Template(
dedent(
"""
Expand All @@ -65,6 +75,7 @@ async def dashboard_get(
window.dashboardAppPath = "${dashboardPath}"
window.connectionURI = "${connectionURI}"
window.authMode = "${authMode}"
window.isSearchEnabled = "${isSearchEnabled}"
</script>
<script defer src="${bundleDomain}/static/js/bundle.js"></script></head>
<link href="${bundleDomain}/static/css/main.css" rel="stylesheet" type="text/css">
Expand All @@ -82,6 +93,7 @@ async def dashboard_get(
dashboardPath=dashboard_path,
connectionURI=connection_uri,
authMode=auth_mode,
isSearchEnabled=str(is_search_enabled).lower(),
)

self.dashboard_get = dashboard_get
Empty file.
30 changes: 30 additions & 0 deletions supertokens_python/recipe/dashboard/api/search/getTags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
#
# This software is licensed under the Apache License, Version 2.0 (the
# "License") as published by the Apache Software Foundation.
#
# You may not use this file except in compliance with the License. You may
# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from supertokens_python.recipe.dashboard.interfaces import APIInterface, APIOptions

from supertokens_python.normalised_url_path import NormalisedURLPath
from supertokens_python.querier import Querier
from supertokens_python.recipe.dashboard.interfaces import SearchTagsOK


async def handle_get_tags(_: APIInterface, __: APIOptions) -> SearchTagsOK:
response = await Querier.get_instance().send_get_request(
NormalisedURLPath("/user/search/tags")
)
return SearchTagsOK(tags=response["tags"])
1 change: 1 addition & 0 deletions supertokens_python/recipe/dashboard/api/users_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ async def handle_users_get_api(
time_joined_order=time_joined_order, # type: ignore
pagination_token=pagination_token,
include_recipe_ids=None,
query=api_options.request.get_query_params(),
)

# user metadata bulk fetch with batches:
Expand Down
1 change: 1 addition & 0 deletions supertokens_python/recipe/dashboard/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
USER_EMAIL_VERIFY_TOKEN_API = "/api/user/email/verify/token"
EMAIL_PASSWORD_SIGN_IN = "/api/signin"
EMAIL_PASSSWORD_SIGNOUT = "/api/signout"
SEARCH_TAGS_API = "/api/search/tags"
DASHBOARD_ANALYTICS_API = "/api/analytics"
11 changes: 11 additions & 0 deletions supertokens_python/recipe/dashboard/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,17 @@ def to_json(self):
return {"status": self.status}


class SearchTagsOK(APIResponse):
status: str = "OK"
tags: List[str]

def __init__(self, tags: List[str]) -> None:
self.tags = tags

def to_json(self):
return {"status": self.status, "tags": self.tags}


class AnalyticsResponse(APIResponse):
status: str = "OK"

Expand Down
4 changes: 4 additions & 0 deletions supertokens_python/recipe/dashboard/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
handle_email_verify_token_post,
handle_emailpassword_signin_api,
handle_emailpassword_signout_api,
handle_get_tags,
handle_metadata_get,
handle_metadata_put,
handle_sessions_get,
Expand Down Expand Up @@ -58,6 +59,7 @@
DASHBOARD_API,
EMAIL_PASSSWORD_SIGNOUT,
EMAIL_PASSWORD_SIGN_IN,
SEARCH_TAGS_API,
USER_API,
USER_EMAIL_VERIFY_API,
USER_EMAIL_VERIFY_TOKEN_API,
Expand Down Expand Up @@ -183,6 +185,8 @@ async def handle_api_request(
api_function = handle_email_verify_token_post
elif request_id == EMAIL_PASSSWORD_SIGNOUT:
api_function = handle_emailpassword_signout_api
elif request_id == SEARCH_TAGS_API:
api_function = handle_get_tags
elif request_id == DASHBOARD_ANALYTICS_API:
if method == "post":
api_function = handle_analytics_post
Expand Down
3 changes: 3 additions & 0 deletions supertokens_python/recipe/dashboard/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
DASHBOARD_API,
EMAIL_PASSSWORD_SIGNOUT,
EMAIL_PASSWORD_SIGN_IN,
SEARCH_TAGS_API,
USER_API,
USER_EMAIL_VERIFY_API,
USER_EMAIL_VERIFY_TOKEN_API,
Expand Down Expand Up @@ -238,6 +239,8 @@ def get_api_if_matched(path: NormalisedURLPath, method: str) -> Optional[str]:
return EMAIL_PASSWORD_SIGN_IN
if path_str.endswith(EMAIL_PASSSWORD_SIGNOUT) and method == "post":
return EMAIL_PASSSWORD_SIGNOUT
if path_str.endswith(SEARCH_TAGS_API) and method == "get":
return SEARCH_TAGS_API
if path_str.endswith(DASHBOARD_ANALYTICS_API) and method == "post":
return DASHBOARD_ANALYTICS_API

Expand Down
4 changes: 4 additions & 0 deletions supertokens_python/supertokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ async def get_users( # pylint: disable=no-self-use
limit: Union[int, None],
pagination_token: Union[str, None],
include_recipe_ids: Union[None, List[str]],
query: Union[Dict[str, str], None] = None,
) -> UsersResponse:
querier = Querier.get_instance(None)
params = {"timeJoinedOrder": time_joined_order}
Expand All @@ -285,6 +286,9 @@ async def get_users( # pylint: disable=no-self-use
include_recipe_ids_str = ",".join(include_recipe_ids)
params = {"includeRecipeIds": include_recipe_ids_str, **params}

if query is not None:
params = {**params, **query}

response = await querier.send_get_request(NormalisedURLPath(USERS), params)
next_pagination_token = None
if "nextPaginationToken" in response:
Expand Down
Loading