-
Notifications
You must be signed in to change notification settings - Fork 2.8k
feat: application api key #3224
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,17 @@ | ||
| from django.db.models import QuerySet | ||
| from django.utils.translation import gettext_lazy as _ | ||
| from drf_spectacular.utils import extend_schema | ||
| from rest_framework.request import Request | ||
| from rest_framework.views import APIView | ||
| from django.utils.translation import gettext_lazy as _ | ||
|
|
||
| from application.api.application_api_key import ApplicationKeyCreateAPI | ||
| from application.api.application_api_key import ApplicationKeyAPI | ||
| from application.models import ApplicationApiKey | ||
| from application.serializers.application_api_key import ApplicationKeySerializer | ||
| from common.auth import TokenAuth | ||
| from common.auth.authentication import has_permissions | ||
| from common.constants.permission_constants import PermissionConstants | ||
| from common.log.log import log | ||
| from common.result import result, success | ||
| from common.result import result, success, DefaultResultSerializer | ||
|
|
||
|
|
||
| def get_application_operation_object(application_api_key_id): | ||
|
|
@@ -31,7 +31,9 @@ class ApplicationKey(APIView): | |
| description=_('Create application ApiKey'), | ||
| summary=_('Create application ApiKey'), | ||
| operation_id=_('Create application ApiKey'), # type: ignore | ||
| parameters=ApplicationKeyCreateAPI.get_parameters(), | ||
| parameters=ApplicationKeyAPI.get_parameters(), | ||
| request=None, | ||
| responses=ApplicationKeyAPI.get_response(), | ||
| tags=[_('Application Api Key')] # type: ignore | ||
| ) | ||
| @log(menu='Application', operate="Add ApiKey", | ||
|
|
@@ -47,26 +49,50 @@ def post(self, request: Request, workspace_id: str, application_id: str): | |
| description=_('GET application ApiKey List'), | ||
| summary=_('Create application ApiKey List'), | ||
| operation_id=_('Create application ApiKey List'), # type: ignore | ||
| parameters=ApplicationKeyCreateAPI.get_parameters(), | ||
| parameters=ApplicationKeyAPI.get_parameters(), | ||
| responses=ApplicationKeyAPI.List.get_response(), | ||
| tags=[_('Application Api Key')] # type: ignore | ||
| ) | ||
| @has_permissions(PermissionConstants.APPLICATION_OVERVIEW_API_KEY.get_workspace_application_permission()) | ||
| def get(self, request: Request, workspace_id: str, application_id: str): | ||
| return result, success(ApplicationKeySerializer( | ||
| data={'application_id': application_id, 'user_id': request.user.id, | ||
| return result.success(ApplicationKeySerializer( | ||
| data={'application_id': application_id, | ||
| 'workspace_id': workspace_id}).list()) | ||
|
|
||
| class Operate(APIView): | ||
| authentication_classes = [TokenAuth] | ||
|
|
||
| @extend_schema( | ||
| methods=['GET'], | ||
| description=_('GET application ApiKey List'), | ||
| summary=_('Create application ApiKey List'), | ||
| operation_id=_('Create application ApiKey List'), # type: ignore | ||
| parameters=ApplicationKeyCreateAPI.get_parameters(), | ||
| methods=['PUT'], | ||
| description=_('Modify application API_KEY'), | ||
| summary=_('Modify application API_KEY'), | ||
| operation_id=_('Modify application API_KEY'), # type: ignore | ||
| parameters=ApplicationKeyAPI.Operate.get_parameters(), | ||
| request=ApplicationKeyAPI.Operate.get_request(), | ||
| responses=DefaultResultSerializer, | ||
| tags=[_('Application Api Key')] # type: ignore | ||
| ) | ||
| @has_permissions(PermissionConstants.APPLICATION_OVERVIEW_API_KEY.get_workspace_application_permission()) | ||
| def put(self, request: Request, workspace_id: str, application_id: str, api_key_id: str): | ||
| return result.success( | ||
| ApplicationKeySerializer.Operate( | ||
| data={'workspace_id': workspace_id, 'application_id': application_id, | ||
| 'api_key_id': api_key_id}).edit( | ||
| request.data)) | ||
|
|
||
| @extend_schema( | ||
| methods=['DELETE'], | ||
| description=_('Delete Application API_KEY'), | ||
| summary=_('Delete Application API_KEY'), | ||
| operation_id=_('Delete Application API_KEY'), # type: ignore | ||
| parameters=ApplicationKeyAPI.Operate.get_parameters(), | ||
| request=ApplicationKeyAPI.Operate.get_request(), | ||
| responses=DefaultResultSerializer, | ||
| tags=[_('Application Api Key')] # type: ignore | ||
| ) | ||
| @has_permissions(PermissionConstants.APPLICATION_OVERVIEW_API_KEY.get_workspace_application_permission()) | ||
| def put(self, request: Request, application_id: str, workspace_id: str): | ||
| return result.success(ApplicationKeySerializer.Operate()) | ||
| def delete(self, request: Request, workspace_id: str, application_id: str, api_key_id: str): | ||
| return result.success( | ||
| ApplicationKeySerializer.Operate( | ||
| data={'workspace_id': workspace_id, 'application_id': application_id, | ||
| 'api_key_id': api_key_id}).delete()) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The provided Django view code looks generally well-structured, but there are several improvements and corrections that could be made:
Here’s an improved version of the code with these changes: from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _
from drf_spectacular.utils import extend_schema
from rest_framework.request import Request
from rest_framework.views import APIView
from common.auth.authentication import has_permissions
from application.api.application_api_key import ApplicationKeyAPI, ApplicationKeyCreateAPI
from application.models import ApplicationApiKey
from application.serializers.application_api_key import ApplicationKeySerializer
from common.auth.auth import TokenAuth
from common.constants.permission_constants import PermissionConstants
from common.log.log import log
class ApplicationKey(APIView):
authentication_classes = [TokenAuth]
@extend_schema(
methods=['POST', 'GET'],
description=_('Manage application ApiKey'),
summary=_('Application ApiKey Management'),
operation_id=_('Management application ApiKey'), # type: ignore
parameters=ApplicationKeyAPI.get_parameters(),
responses={
status.HTTP_200_OK: ApplicationKeyAPI.List.response_class,
status.HTTP_201_CREATED: None
},
tags=[_('Application Api Key')]
)
@log(menu='Application', operate="Add/Retrive ApiKey",
permission_required=PermissionConstants.APPLICATION_OVERVIEW_API_KEY.get_workspace_application_permission())
def post(self, request: Request, workspace_id: str, application_id: str) -> QuerySet | dict:
user_data = {'user_id': request.user.id}
if request.method == 'GET':
# Assuming you have logic here to fetch and format results.
serializer = ApplicationKeySerializer(data={"data": "some_data"})
return serializer.dict()
serializer = ApplicationKeySerializer(data=user_data)
if serializer.is_valid():
key_instance = serializer.save(workspace_id=workspace_id, application_id=application_id)
return serializer.data if serializer.instance is not None else {}
return {}
class Operate(APIView):
authentication_classes = [TokenAuth]
@extend_schema(
methods=['PUT', 'DELETE'],
description=_({
'PUT': _('Modify application API_KEY'),
'DELETE': _('Delete Application API_KEY')
}),
summary=_({
'PUT': _('Modify application API_KEY'),
'DELETE': _('Delete Application API_KEY')
}),
operation_id={'PUT': _('Modification application API_KEY'), 'DELETE': _("Deletion Application API_KEY")},
parameters=ApplicationKeyAPI.Operate.get_parameters(),
request=ApplicationKeyAPI.Operate.get_request(type='form-data'),
responses={
status.HTTP_200_OK: DefaultResultSerializer()
},
tags=[_('Application Api Key')]
)
@has_permissions(PermissionConstants.APPLICATION_OVERVIEW_API_KEY.get_workspace_application_permission())
def put(self, request: Request, *args, **kwargs): # Note: Use variable-length arguments for more flexibility
api_key_id = kwargs.pop('api_key_id', '') # Get API key ID from URL
try:
key_model_query = ApplicationKey.objects.select_related("application").filter(id=api_key_id)
except ApplicationKeyName.DoesNotExist:
return result.fail(detail="Invalid API key")
data_to_update = request.data.copy()
update_serializer = ApplicationKeySerializer(key_model_query.first(), data=data_to_update)
if update_serializer.is_valid() and update_serializer.update(key_model_query.first()):
return {"success": "API key updated successfully"}
return result.fail(detail=str(update_serializer.errors))
@has_permissions(PermissionConstants.APPLICATION_OVERVIEW_API_KEY.get_workspace_application_permission())
def delete(self, request: Request, *args, **kwargs):
api_key_id = kwargs.pop('api_key_id', '')
try:
key_model_query = ApplicationKey.objects.select_related("application").filter(id=api_key_id)
except ApplicationKeyName.DoesNotExist:
return result.fail(detail="Invalid API key")
if key_model_query.exists() and key_model_query.first().delete()[0] > 0:
return {"success": "API key deleted successfully"}
return result.fail(detail="Failed to delete API key")Summary of Changes:
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # coding=utf-8 | ||
| """ | ||
| @project: MaxKB | ||
| @Author:虎 | ||
| @file: application_api_key_cache.py | ||
| @date:2024/7/25 11:30 | ||
| @desc: | ||
| """ | ||
| from django.core.cache import cache | ||
| from django.db.models import QuerySet | ||
|
|
||
| from application.models import ApplicationApiKey | ||
| from common.constants.cache_version import Cache_Version | ||
| from common.utils.cache_util import get_cache | ||
|
|
||
|
|
||
| @get_cache(cache_key=Cache_Version.APPLICATION_API_KEY.get_key_func(), | ||
| use_get_data=lambda secret_key, use_get_data: use_get_data, | ||
| version=Cache_Version.APPLICATION_API_KEY.get_version()) | ||
| def get_application_api_key(secret_key, use_get_data): | ||
| application_api_key = QuerySet(ApplicationApiKey).filter(secret_key=secret_key).first() | ||
| return {'allow_cross_domain': application_api_key.allow_cross_domain, | ||
| 'cross_domain_list': application_api_key.cross_domain_list} | ||
|
|
||
|
|
||
| def del_application_api_key(secret_key): | ||
| cache.delete(Cache_Version.APPLICATION_API_KEY.get_key(secret_key=secret_key, use_get_data=True), | ||
| version=Cache_Version.APPLICATION_API_KEY.get_version()) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| # coding=utf-8 | ||
| """ | ||
| @project: MaxKB | ||
| @Author:虎 | ||
| @file: cache_util.py | ||
| @date:2024/7/24 19:23 | ||
| @desc: | ||
| """ | ||
| from django.core.cache import cache | ||
|
|
||
|
|
||
| def get_data_by_default_cache(key: str, get_data, cache_instance=cache, version=None, kwargs=None): | ||
| """ | ||
| 获取数据, 先从缓存中获取,如果获取不到再调用get_data 获取数据 | ||
| @param kwargs: get_data所需参数 | ||
| @param key: key | ||
| @param get_data: 获取数据函数 | ||
| @param cache_instance: cache实例 | ||
| @param version: 版本用于隔离 | ||
| @return: | ||
| """ | ||
| if kwargs is None: | ||
| kwargs = {} | ||
| if cache_instance.has_key(key, version=version): | ||
| return cache_instance.get(key, version=version) | ||
| data = get_data(**kwargs) | ||
| cache_instance.add(key, data, version=version) | ||
| return data | ||
|
|
||
|
|
||
| def set_data_by_default_cache(key: str, get_data, cache_instance=cache, version=None): | ||
| data = get_data() | ||
| cache_instance.set(key, data, version=version) | ||
| return data | ||
|
|
||
|
|
||
| def get_cache(cache_key, use_get_data: any = True, cache_instance=cache, version=None): | ||
| def inner(get_data): | ||
| def run(*args, **kwargs): | ||
| key = cache_key(*args, **kwargs) if callable(cache_key) else cache_key | ||
| is_use_get_data = use_get_data(*args, **kwargs) if callable(use_get_data) else use_get_data | ||
| if is_use_get_data: | ||
| if cache_instance.has_key(key, version=version): | ||
| return cache_instance.get(key, version=version) | ||
| data = get_data(*args, **kwargs) | ||
| cache_instance.add(key, data, timeout=None, version=version) | ||
| return data | ||
| data = get_data(*args, **kwargs) | ||
| cache_instance.set(key, data, timeout=None, version=version) | ||
| return data | ||
|
|
||
| return run | ||
|
|
||
| return inner | ||
|
|
||
|
|
||
| def del_cache(cache_key, cache_instance=cache, version=None): | ||
| def inner(func): | ||
| def run(*args, **kwargs): | ||
| key = cache_key(*args, **kwargs) if callable(cache_key) else cache_key | ||
| func(*args, **kwargs) | ||
| cache_instance.delete(key, version=version) | ||
|
|
||
| return run | ||
|
|
||
| return inner | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code looks generally correct for implementing a Django-based caching utility with support for both default and custom cache keys, expiration times, and versioning. However, there are areas where improvements can be made:
Here's an improved version of the code: # coding=utf-8
"""
@project: MaxKB
@Author:虎
@file: cache_util.py
@date:2024/7/24 19:23
@desc:
"""
from django.core.cache import cache
def get_data_by_default_cache(
key: str,
get_data,
cache_instance: cache = cache,
version: int | None = None,
kwargs: dict | None = None
):
"""
获取数据,先从缓存中获取,如果获取不到再调用get_data 获取数据
@param kwargs: get_data所需参数
@param key: key
@param get_data: 获取数据函数
@param cache_instance: cache实例
@param version: 版本用于隔离
@return: 数据或None
"""
if kwargs is None:
kwargs = {}
if cache_instance.has_key(key=key, version=version):
return cache_instance.get(key=key, version=version)
data = get_data(**kwargs)
cache_instance.add(key=key, value=data, timeout=None, version=version)
return data
def set_data_by_default_cache(
key: str,
get_data,
cache_instance: cache = cache,
version: int | None = None
):
"""
设置数据到默认缓存中
@param key: 键
@param get_data: 获取数据函数
@param cache_instance: 缓存实例
@param version: 版本号(用于缓存隔离)
@return: 数据或None
"""
data = get_data()
cache_instance.set(key=key, value=data, timeout=None, version=version)
return data
def get_cache(
cache_key,
use_get_data: bool | Callable = True,
cache_instance: cache = cache,
version: int | None = None
):
"""
创建缓存装饰器函数
@param cache_key: 唯一标识符函数或者字符串键
@param use_get_data: 使用get_data生成key的标志位或回调函数是否使用get_data生成key
或者一个布尔值,指示是否使用get_data。
@param cache_instance: 缓存实例
@param version: 缓存版本号(用于缓存隔离)
@return: 返回内层run函数对象
"""
def generate_unique_key(*args, **kwargs):
"""Generate unique cache key based on function args and kwargs."""
if isinstance(cache_key, (str)):
return cache_key
elif hasattr(cache_key, '__call__'):
return cache_key(*args, **kwargs)
class CacheManager(object):
def __init__(self, key_generator, cache_instance, use_get_data, version):
self.key_generator = key_generator
self.use_get_data = use_get_data
self.version = version
self._cache_instance = cache_instance
def run(self, *args, **kwargs):
cache_key = self.generate_unique_key(*args, **kwargs)
should_use_get_data = False
if callable(use_get_data):
should_use_get_data = use_get_data(*args, **kwargs)
cached_data = getattr(self._cache_instance, 'get', lambda k, v=True, t=False, o='default': None)(
key=self.key_generator(*args, **kwargs), version=self.version)
if should_use_get_data and cached_data is None:
actual_value = self.retrieve_from_function(args, kwargs)
setattr(self._cache_instance, 'set', lambda k, v, t=t, o=o: None)(key=self.key_generator(*args, **kwargs),
value=actual_value,
timeout=None,
version=self.version)
return cached_data
def retrieve_from_function(self, args, kwargs):
# Replace this call with your real logic for getting data from a db or api
actual_value = get_data(*args, **kwargs) // Update to your actual getter method.
return actual_value
cm = CacheManager(generate_unique_key, cache_instance, use_get_data, version)
inner_run = (
lambda *args, **kwargs: cm.run(*args, **kwargs)
)
return inner_run
def del_cache(cache_key, cache_instance: cache = cache, version: int| None = None):
"""
删除指定缓存项
@param cache_key: 缓存唯一标识符
@param cache_instance: 缓存实例
@param version: 空间版本标识
@return:
"""
cache_instance.delete(key=cache_key, version=version)Improvements Made:
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The provided code looks generally correct, but there are some improvements and considerations to make:
Comments: Add comments explaining what each method is intended to do.
Model Field Names: Rename variables like
user_idinEditApplicationKeySerializertoapplication_key_user_idfor consistency with other models.Error Handling: Ensure that all exceptions are properly handled and returned to the client with appropriate error codes and messages.
Cache Management: The cache insertion should be done in a synchronous manner within the
app.editfunction since it returns a boolean value indicating success or failure.Here's an optimized version of the code with these improvements:
Notes:
validate_domain) for cross-domain addresses inEditApplicatonKeySerializer.OperateSerializer, moved the caching logic inside theupdatemethod where it actually saves the object without relying on asynchronous calls.