diracx-db 0.0.1a50__tar.gz → 0.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/PKG-INFO +4 -1
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/pyproject.toml +2 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/__main__.py +1 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/auth/db.py +69 -2
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/utils/base.py +5 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/.gitignore +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/README.md +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/__init__.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/exceptions.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/os/__init__.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/os/job_parameters.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/os/utils.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/py.typed +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/__init__.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/auth/__init__.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/auth/schema.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/dummy/__init__.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/dummy/db.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/dummy/schema.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/job/__init__.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/job/db.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/job/schema.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/job_logging/__init__.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/job_logging/db.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/job_logging/schema.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/pilot_agents/__init__.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/pilot_agents/db.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/pilot_agents/schema.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/sandbox_metadata/__init__.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/sandbox_metadata/db.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/sandbox_metadata/schema.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/task_queue/__init__.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/task_queue/db.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/task_queue/schema.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/utils/__init__.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/utils/functions.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/src/diracx/db/sql/utils/types.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/tests/auth/test_authorization_flow.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/tests/auth/test_device_flow.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/tests/auth/test_refresh_token.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/tests/jobs/test_job_db.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/tests/jobs/test_job_logging_db.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/tests/jobs/test_sandbox_metadata.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/tests/opensearch/test_connection.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/tests/opensearch/test_index_template.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/tests/opensearch/test_search.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/tests/pilot_agents/__init__.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/tests/pilot_agents/test_pilot_agents_db.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/tests/test_dummy_db.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/tests/test_freeze_time.py +0 -0
- {diracx_db-0.0.1a50 → diracx_db-0.0.2}/tests/utils/test_uuid.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: diracx-db
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Summary: TODO
|
|
5
5
|
License: GPL-3.0-only
|
|
6
6
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -12,8 +12,11 @@ Requires-Python: >=3.11
|
|
|
12
12
|
Requires-Dist: diracx-core
|
|
13
13
|
Requires-Dist: opensearch-py[async]
|
|
14
14
|
Requires-Dist: pydantic>=2.10
|
|
15
|
+
Requires-Dist: python-dateutil
|
|
15
16
|
Requires-Dist: sqlalchemy[aiomysql,aiosqlite]>=2
|
|
16
17
|
Requires-Dist: uuid-utils
|
|
17
18
|
Provides-Extra: testing
|
|
18
19
|
Requires-Dist: diracx-testing; extra == 'testing'
|
|
19
20
|
Requires-Dist: freezegun; extra == 'testing'
|
|
21
|
+
Provides-Extra: types
|
|
22
|
+
Requires-Dist: types-python-dateutil; extra == 'types'
|
|
@@ -18,11 +18,13 @@ dependencies = [
|
|
|
18
18
|
"pydantic >=2.10",
|
|
19
19
|
"sqlalchemy[aiomysql,aiosqlite] >= 2",
|
|
20
20
|
"uuid-utils",
|
|
21
|
+
"python-dateutil",
|
|
21
22
|
]
|
|
22
23
|
dynamic = ["version"]
|
|
23
24
|
|
|
24
25
|
[project.optional-dependencies]
|
|
25
26
|
testing = ["diracx-testing", "freezegun"]
|
|
27
|
+
types = ["types-python-dateutil"]
|
|
26
28
|
|
|
27
29
|
[project.entry-points."diracx.dbs.sql"]
|
|
28
30
|
AuthDB = "diracx.db.sql:AuthDB"
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
import secrets
|
|
5
|
+
from datetime import UTC, datetime
|
|
6
|
+
from itertools import pairwise
|
|
4
7
|
|
|
5
|
-
from
|
|
8
|
+
from dateutil.rrule import MONTHLY, rrule
|
|
9
|
+
from sqlalchemy import insert, select, text, update
|
|
6
10
|
from sqlalchemy.exc import IntegrityError, NoResultFound
|
|
11
|
+
from sqlalchemy.ext.asyncio import AsyncConnection
|
|
7
12
|
from uuid_utils import UUID, uuid7
|
|
8
13
|
|
|
9
14
|
from diracx.core.exceptions import (
|
|
10
15
|
AuthorizationError,
|
|
11
16
|
TokenNotFoundError,
|
|
12
17
|
)
|
|
13
|
-
from diracx.db.sql.utils import BaseSQLDB, hash, substract_date
|
|
18
|
+
from diracx.db.sql.utils import BaseSQLDB, hash, substract_date, uuid7_from_datetime
|
|
14
19
|
|
|
15
20
|
from .schema import (
|
|
16
21
|
AuthorizationFlows,
|
|
@@ -25,10 +30,72 @@ from .schema import Base as AuthDBBase
|
|
|
25
30
|
USER_CODE_ALPHABET = "BCDFGHJKLMNPQRSTVWXZ"
|
|
26
31
|
MAX_RETRY = 5
|
|
27
32
|
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
28
35
|
|
|
29
36
|
class AuthDB(BaseSQLDB):
|
|
30
37
|
metadata = AuthDBBase.metadata
|
|
31
38
|
|
|
39
|
+
@classmethod
|
|
40
|
+
async def post_create(cls, conn: AsyncConnection) -> None:
|
|
41
|
+
"""Create partitions if it is a MySQL DB and it does not have
|
|
42
|
+
it yet and the table does not have any data yet.
|
|
43
|
+
We do this as a post_create step as sqlalchemy does not support
|
|
44
|
+
partition so well.
|
|
45
|
+
"""
|
|
46
|
+
if conn.dialect.name == "mysql":
|
|
47
|
+
check_partition_query = text(
|
|
48
|
+
"SELECT PARTITION_NAME FROM information_schema.partitions "
|
|
49
|
+
"WHERE TABLE_NAME = 'RefreshTokens' AND PARTITION_NAME is not NULL"
|
|
50
|
+
)
|
|
51
|
+
partition_names = (await conn.execute(check_partition_query)).all()
|
|
52
|
+
|
|
53
|
+
if not partition_names:
|
|
54
|
+
# Create a monthly partition from today until 2 years
|
|
55
|
+
# The partition are named p_<year>_<month>
|
|
56
|
+
start_date = datetime.now(tz=UTC).replace(
|
|
57
|
+
day=1, hour=0, minute=0, second=0, microsecond=0
|
|
58
|
+
)
|
|
59
|
+
end_date = start_date.replace(year=start_date.year + 2)
|
|
60
|
+
|
|
61
|
+
dates = [
|
|
62
|
+
dt for dt in rrule(MONTHLY, dtstart=start_date, until=end_date)
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
partition_list = []
|
|
66
|
+
for name, limit in pairwise(dates):
|
|
67
|
+
partition_list.append(
|
|
68
|
+
f"PARTITION p_{name.year}_{name.month} "
|
|
69
|
+
f"VALUES LESS THAN ('{str(uuid7_from_datetime(limit, randomize=False)).replace('-', '')}')"
|
|
70
|
+
)
|
|
71
|
+
partition_list.append("PARTITION p_future VALUES LESS THAN (MAXVALUE)")
|
|
72
|
+
|
|
73
|
+
alter_query = text(
|
|
74
|
+
f"ALTER TABLE RefreshTokens PARTITION BY RANGE COLUMNS (JTI) ({','.join(partition_list)})"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
check_table_empty_query = text("SELECT * FROM RefreshTokens LIMIT 1")
|
|
78
|
+
refresh_table_content = (
|
|
79
|
+
await conn.execute(check_table_empty_query)
|
|
80
|
+
).all()
|
|
81
|
+
if refresh_table_content:
|
|
82
|
+
logger.warning(
|
|
83
|
+
"RefreshTokens table not empty. Run the following query yourself"
|
|
84
|
+
)
|
|
85
|
+
logger.warning(alter_query)
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
await conn.execute(alter_query)
|
|
89
|
+
|
|
90
|
+
partition_names = (
|
|
91
|
+
await conn.execute(
|
|
92
|
+
check_partition_query, {"table_name": "RefreshTokens"}
|
|
93
|
+
)
|
|
94
|
+
).all()
|
|
95
|
+
assert partition_names, (
|
|
96
|
+
f"There should be partitions now {partition_names}"
|
|
97
|
+
)
|
|
98
|
+
|
|
32
99
|
async def device_flow_validate_user_code(
|
|
33
100
|
self, user_code: str, max_validity: int
|
|
34
101
|
) -> str:
|
|
@@ -155,6 +155,11 @@ class BaseSQLDB(metaclass=ABCMeta):
|
|
|
155
155
|
raise
|
|
156
156
|
return db_urls
|
|
157
157
|
|
|
158
|
+
@classmethod
|
|
159
|
+
async def post_create(cls, conn: AsyncConnection) -> None:
|
|
160
|
+
"""Execute actions after the schema has been created."""
|
|
161
|
+
return
|
|
162
|
+
|
|
158
163
|
@classmethod
|
|
159
164
|
def transaction(cls) -> Self:
|
|
160
165
|
raise NotImplementedError("This should never be called")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|