Skip to content

Commit 4dfbc88

Browse files
committed
add resource_threshold
1 parent c5bcd75 commit 4dfbc88

File tree

8 files changed

+171
-0
lines changed

8 files changed

+171
-0
lines changed

backend/dataall/base/db/exceptions.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,26 @@ def __init__(self, action, message):
193193

194194
def __str__(self):
195195
return f'{self.message}'
196+
197+
198+
class ResourceThresholdExceeded(Exception):
199+
def __init__(self, username, action):
200+
self.username = username
201+
self.action = action
202+
self.message = f"""
203+
An error occurred (ResourceThresholdExceeded) when calling {self.action} operation:
204+
Requests exceeded max daily invocation count for User: {self.username}
205+
"""
206+
207+
def __str__(self):
208+
return f'{self.message}'
209+
210+
211+
class ModelGuardrailException(Exception):
212+
def __init__(self, message):
213+
self.message = f"""
214+
An error occurred (ModelGuardrailException) when invoking the model: {message}
215+
"""
216+
217+
def __str__(self):
218+
return f'{self.message}'

backend/dataall/core/resource_threshold/__init__.py

Whitespace-only changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from dataall.base.db import Base, utils
2+
from sqlalchemy import String, Integer, Column, Date
3+
from datetime import date
4+
5+
6+
class ResourceThreshold(Base):
7+
__tablename__ = 'resource_threshold'
8+
actionUri = Column(String(64), primary_key=True, default=utils.uuid('resource_threshold'))
9+
username = Column(String(64), nullable=False)
10+
actionType = Column(String(64), nullable=False)
11+
date = Column(Date, default=date.today, nullable=False)
12+
count = Column(Integer, default=1, nullable=False)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from dataall.core.resource_threshold.db.resource_threshold_models import ResourceThreshold
2+
from sqlalchemy import and_
3+
from datetime import date
4+
5+
6+
class ResourceThresholdRepository:
7+
@staticmethod
8+
def get_count_today(session, username, action_type):
9+
amount = (
10+
session.query(ResourceThreshold.count)
11+
.filter(
12+
and_(
13+
ResourceThreshold.username == username,
14+
ResourceThreshold.actionType == action_type,
15+
ResourceThreshold.date == date.today(),
16+
)
17+
)
18+
.scalar()
19+
)
20+
return amount if amount else 0
21+
22+
@staticmethod
23+
def add_entry(session, username, action_type):
24+
user_entry = ResourceThresholdRepository._get_user_entry(session, username, action_type)
25+
if user_entry:
26+
session.query(ResourceThreshold).filter(
27+
and_(
28+
ResourceThreshold.username == username,
29+
ResourceThreshold.actionType == action_type,
30+
)
31+
).update({ResourceThreshold.count: 1, ResourceThreshold.date: date.today()}, synchronize_session=False)
32+
session.commit()
33+
else:
34+
action_entry = ResourceThreshold(username=username, actionType=action_type)
35+
session.add(action_entry)
36+
session.commit()
37+
38+
@staticmethod
39+
def increment_count(session, username, action_type):
40+
session.query(ResourceThreshold).filter(
41+
and_(
42+
ResourceThreshold.username == username,
43+
ResourceThreshold.actionType == action_type,
44+
ResourceThreshold.date == date.today(),
45+
)
46+
).update({ResourceThreshold.count: ResourceThreshold.count + 1}, synchronize_session=False)
47+
session.commit()
48+
49+
@staticmethod
50+
def _get_user_entry(session, username, action_type):
51+
entry = (
52+
session.query(ResourceThreshold)
53+
.filter(and_(ResourceThreshold.username == username, ResourceThreshold.actionType == action_type))
54+
.first()
55+
)
56+
return entry

backend/dataall/core/resource_threshold/services/__init__.py

Whitespace-only changes.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from dataall.core.resource_threshold.db.resource_threshold_repositories import ResourceThresholdRepository
2+
from dataall.base.db import exceptions
3+
from functools import wraps
4+
from dataall.base.config import config
5+
from dataall.base.context import get_context
6+
7+
import logging
8+
9+
log = logging.getLogger(__name__)
10+
11+
12+
class ResourceThresholdService:
13+
@staticmethod
14+
def check_invocation_count(action_type, max_count_config_path):
15+
def decorator(func):
16+
@wraps(func)
17+
def wrapper(*args, **kwargs):
18+
context = get_context()
19+
with context.db_engine.scoped_session() as session:
20+
count = ResourceThresholdRepository.get_count_today(
21+
session=session, username=context.username, action_type=action_type
22+
)
23+
max_count = config.get_property(max_count_config_path, 10)
24+
log.info(
25+
f'User {context.username} has invoked {action_type} {count} times today of max {max_count}'
26+
)
27+
if count < max_count:
28+
if count == 0:
29+
ResourceThresholdRepository.add_entry(
30+
session=session, username=context.username, action_type=action_type
31+
)
32+
else:
33+
ResourceThresholdRepository.increment_count(
34+
session=session, username=context.username, action_type=action_type
35+
)
36+
return func(*args, **kwargs)
37+
else:
38+
raise exceptions.ResourceThresholdExceeded(username=context.username, action=action_type)
39+
40+
return wrapper
41+
42+
return decorator

backend/migrations/env.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from dataall.modules.omics.db.omics_models import OmicsWorkflow, OmicsRun
2121
from dataall.modules.metadata_forms.db.metadata_form_models import *
2222
from dataall.modules.redshift_datasets.db.redshift_models import RedshiftDataset, RedshiftTable, RedshiftConnection
23+
from dataall.core.resource_threshold.db.resource_threshold_models import ResourceThreshold
2324
# fmt: on
2425
# enable ruff-format back
2526

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""resource_threshholds_added
2+
3+
Revision ID: 2258cd8d6e9f
4+
Revises: 5a798acc6282
5+
Create Date: 2024-08-22 12:31:38.465650
6+
7+
"""
8+
9+
from alembic import op
10+
import sqlalchemy as sa
11+
12+
13+
# revision identifiers, used by Alembic.
14+
revision = '2258cd8d6e9f'
15+
down_revision = '5a798acc6282'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# ### commands auto generated by Alembic - please adjust! ###
22+
op.create_table(
23+
'resource_threshold',
24+
sa.Column('actionUri', sa.String(length=64), nullable=False),
25+
sa.Column('username', sa.String(length=64), nullable=False),
26+
sa.Column('actionType', sa.String(length=64), nullable=False),
27+
sa.Column('date', sa.Date(), nullable=False),
28+
sa.Column('count', sa.Integer(), nullable=False),
29+
sa.PrimaryKeyConstraint('actionUri'),
30+
)
31+
# ### end Alembic commands ###
32+
33+
34+
def downgrade():
35+
# ### commands auto generated by Alembic - please adjust! ###
36+
op.drop_table('resource_threshold')
37+
# ### end Alembic commands ###

0 commit comments

Comments
 (0)