Skip to content

Commit bbea659

Browse files
Merge pull request #69 from DataKitchen/release/5.70.2
Release/5.70.2
2 parents 0ded486 + 0bbbd2e commit bbea659

282 files changed

Lines changed: 20835 additions & 2592 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ __pycache__/
77
work_area/
88
.envrc
99
environment.list
10+
.claude/settings.local.json
11+
.claude/repo.yml
1012

1113
# C extensions
1214
*.so

deploy/testgen-base.dockerfile

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ RUN apk update && apk upgrade && apk add --no-cache \
2727
openblas=0.3.30-r2 \
2828
openblas-dev=0.3.30-r2 \
2929
unixodbc=2.3.14-r0 \
30-
unixodbc-dev=2.3.14-r0 \
31-
libarrow=21.0.0-r4 \
32-
apache-arrow-dev=21.0.0-r4
30+
unixodbc-dev=2.3.14-r0
3331

3432
COPY --chmod=775 ./deploy/install_linuxodbc.sh /tmp/dk/install_linuxodbc.sh
3533
RUN /tmp/dk/install_linuxodbc.sh
@@ -39,7 +37,7 @@ COPY ./pyproject.toml /tmp/dk/pyproject.toml
3937
RUN mkdir /dk
4038

4139
# Upgrading pip for security
42-
RUN python3 -m pip install --no-cache-dir --upgrade pip==26.0
40+
RUN python3 -m pip install --no-cache-dir --upgrade pip==26.1.2
4341

4442
# hdbcli only ships manylinux wheels (no musl). pip 26+ correctly rejects these on Alpine.
4543
# We download the wheel for the correct arch, then extract it directly into site-packages
@@ -73,8 +71,7 @@ RUN apk del \
7371
openssl \
7472
linux-headers \
7573
openblas-dev \
76-
unixodbc-dev \
77-
apache-arrow-dev
74+
unixodbc-dev
7875

7976
# Remove interactive ODBC tools — not needed at runtime, and iusql triggers
8077
# false-positive secret detection in security scanners (SECRET-3010)

deploy/testgen.dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ARG TESTGEN_BASE_LABEL=v16
1+
ARG TESTGEN_BASE_LABEL=v17
22

33
FROM datakitchen/dataops-testgen-base:${TESTGEN_BASE_LABEL} AS release-image
44

pyproject.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
88

99
[project]
1010
name = "dataops-testgen"
11-
version = "5.48.0"
11+
version = "5.70.2"
1212
description = "DataKitchen's Data Quality DataOps TestGen"
1313
authors = [
1414
{ "name" = "DataKitchen, Inc.", "email" = "info@datakitchen.io" },
@@ -61,7 +61,7 @@ dependencies = [
6161
"xlsxwriter==3.2.0",
6262
"psutil==5.9.8",
6363
"concurrent_log_handler==0.9.25",
64-
"cryptography==46.0.6",
64+
"cryptography==48.0.1",
6565
"validators==0.33.0",
6666
"reportlab==4.2.2",
6767
"cron-converter==1.2.1",
@@ -72,7 +72,7 @@ dependencies = [
7272
"holidays~=0.89",
7373

7474
# Pinned to match the manually compiled libs or for security
75-
"pyarrow==21.0.0",
75+
"pyarrow==23.0.1",
7676
"matplotlib==3.9.2",
7777
"scipy==1.14.1",
7878
"jinja2==3.1.6",
@@ -82,7 +82,7 @@ dependencies = [
8282
# MCP server
8383
"mcp[cli]==1.26.0",
8484
"uvicorn==0.41.0",
85-
"PyJWT==2.12.0",
85+
"PyJWT==2.13.0",
8686
"bcrypt==5.0.0",
8787

8888
# API & OAuth server
@@ -145,7 +145,7 @@ include-package-data = true
145145
include = [
146146
"testgen*",
147147
]
148-
exclude = [ "*.tests", "tests*", "deploy*", "invocations*", "testlib*"]
148+
exclude = [ "*.tests", "tests*", "deploy*", "invocations*", "testgen.testing*"]
149149

150150
[tool.pytest.ini_options]
151151
minversion = "7.0"

testgen/api/deps.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
77
from sqlalchemy import select
88

9-
from testgen.common.auth import authorize_token, decode_jwt_token
9+
from testgen.common.auth import AuthError, authorize_token, decode_jwt_token
10+
from testgen.common.enums import PublicJobKey
1011
from testgen.common.models import Session, _current_session_wrapper, get_current_session
11-
from testgen.common.models.job_execution import PUBLIC_JOB_KEYS, JobExecution
12+
from testgen.common.models.job_execution import JobExecution
1213
from testgen.common.models.project_membership import ProjectMembership
1314
from testgen.common.models.table_group import TableGroup
1415
from testgen.common.models.test_suite import TestSuite
@@ -47,7 +48,7 @@ def get_authorized_user(credentials: HTTPAuthorizationCredentials = _bearer_secu
4748

4849
try:
4950
payload = decode_jwt_token(credentials.credentials)
50-
except ValueError:
51+
except AuthError:
5152
raise _invalid from None
5253

5354
username = payload.get("username")
@@ -57,7 +58,7 @@ def get_authorized_user(credentials: HTTPAuthorizationCredentials = _bearer_secu
5758
session = get_current_session()
5859
try:
5960
return authorize_token(credentials.credentials, username, session)
60-
except ValueError:
61+
except AuthError:
6162
raise _invalid from None
6263

6364

@@ -122,15 +123,15 @@ def dependency(test_suite_id: UUID, user: User = _require_user) -> TestSuite:
122123
def resolve_job(permission: str, *extra_filters):
123124
"""Resolve a JobExecution by ``job_id`` path param and verify project permission.
124125
125-
Only jobs whose ``job_key`` is in ``PUBLIC_JOB_KEYS`` are exposed via the API.
126+
Only externally-triggerable jobs (``PublicJobKey``) are exposed via the API.
126127
Internal kinds (score rollups, recalculations, monitor runs) are filtered out
127128
by construction. Extra ORM clauses are appended to the WHERE clause to further
128129
restrict by job_key when a caller wants a single kind.
129130
"""
130131
def dependency(job_id: UUID, user: User = _require_user) -> JobExecution:
131132
query = select(JobExecution).where(
132133
JobExecution.id == job_id,
133-
JobExecution.job_key.in_(PUBLIC_JOB_KEYS),
134+
JobExecution.job_key.in_(list(PublicJobKey)),
134135
*extra_filters,
135136
)
136137
return _check_access(get_current_session().scalars(query).first(), user, permission)

testgen/api/enums.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""Lowercase presentation enums for API v1 request filters and responses.
2+
3+
Shared home for StrEnums that map a DB-stored value to the lowercase snake_case
4+
form exposed through the API. Several DB columns store title-case values
5+
(``test_results.result_status``, ``*.disposition``); the mapping dicts here are the
6+
single seam that normalizes them to the API surface — the DB is never changed.
7+
"""
8+
9+
from enum import StrEnum
10+
11+
from testgen.common.enums import Disposition as DbDisposition
12+
from testgen.common.models.test_result import TestResultStatus
13+
14+
15+
class ResultStatus(StrEnum):
16+
"""Outcome of a single test result."""
17+
18+
passed = "passed"
19+
failed = "failed"
20+
warning = "warning"
21+
error = "error"
22+
log = "log"
23+
24+
25+
class Disposition(StrEnum):
26+
"""Triage state of a test result. ``no_decision`` is the state of a result no one
27+
has triaged yet. Omitting the filter returns active results (``confirmed`` and
28+
``no_decision``); pass an explicit value to filter to a single state."""
29+
30+
confirmed = "confirmed"
31+
dismissed = "dismissed"
32+
muted = "muted"
33+
no_decision = "no_decision"
34+
35+
36+
RESULT_STATUS_TO_DB: dict[ResultStatus, TestResultStatus] = {
37+
ResultStatus.passed: TestResultStatus.Passed,
38+
ResultStatus.failed: TestResultStatus.Failed,
39+
ResultStatus.warning: TestResultStatus.Warning,
40+
ResultStatus.error: TestResultStatus.Error,
41+
ResultStatus.log: TestResultStatus.Log,
42+
}
43+
RESULT_STATUS_FROM_DB: dict[TestResultStatus, ResultStatus] = {v: k for k, v in RESULT_STATUS_TO_DB.items()}
44+
45+
# ``no_decision`` has no stored DB value — it corresponds to a NULL ``disposition``
46+
# column, handled explicitly at the API boundary (not present in these dicts).
47+
DISPOSITION_TO_DB: dict[Disposition, DbDisposition] = {
48+
Disposition.confirmed: DbDisposition.CONFIRMED,
49+
Disposition.dismissed: DbDisposition.DISMISSED,
50+
Disposition.muted: DbDisposition.INACTIVE,
51+
}
52+
DISPOSITION_FROM_DB: dict[DbDisposition, Disposition] = {v: k for k, v in DISPOSITION_TO_DB.items()}

testgen/api/jobs.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
resolve_test_suite,
1212
)
1313
from testgen.api.schemas import ErrorResponse, JobListResponse, JobResponse, JobSubmittedResponse
14-
from testgen.common.enums import JobKey, JobSource, JobStatus
15-
from testgen.common.models.job_execution import PUBLIC_JOB_KEYS, JobExecution
14+
from testgen.common.enums import JobKey, JobSource, JobStatus, PublicJobKey
15+
from testgen.common.models.job_execution import JobExecution
1616
from testgen.common.models.table_group import TableGroup
1717
from testgen.common.models.test_suite import TestSuite
1818

@@ -98,15 +98,15 @@ def cancel_job(job: JobExecution = resolve_job("edit")): # noqa: B008
9898
)
9999
def list_jobs(
100100
project_code: str = resolve_project_code("view"),
101-
job_key: JobKey | None = Query(default=None), # noqa: B008
101+
job_key: PublicJobKey | None = Query(default=None), # noqa: B008
102102
status: JobStatus | None = Query(default=None), # noqa: B008
103103
page: int = Query(default=1, ge=1),
104104
limit: int = Query(default=20, ge=1, le=100),
105105
):
106106
"""List job executions for a project, with optional filters and pagination."""
107107
items, total = JobExecution.list_for_project(
108108
project_code,
109-
JobExecution.job_key.in_(PUBLIC_JOB_KEYS),
109+
JobExecution.job_key.in_(list(PublicJobKey)),
110110
job_key=job_key,
111111
status=status,
112112
page=page,

testgen/api/oauth/metadata.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
def authorization_server_metadata():
1313
"""Return OAuth 2.1 Authorization Server Metadata per RFC 8414.
1414
15-
MCP clients use this for server discovery.
15+
OAuth clients use this for server discovery.
1616
"""
1717
base_url = settings.BASE_URL.rstrip("/")
1818

testgen/api/oauth/models.py

Lines changed: 0 additions & 45 deletions
This file was deleted.

testgen/api/oauth/routes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121
from testgen import settings
2222
from testgen.api.deps import db_session
2323
from testgen.api.oauth.login import render_login_page
24-
from testgen.api.oauth.models import OAuth2Client
2524
from testgen.api.oauth.server import TestGenAuthorizationServer
2625
from testgen.common.auth import create_jwt_token, decode_jwt_token, verify_password
2726
from testgen.common.models import get_current_session
27+
from testgen.common.models.oauth import OAuth2Client
2828
from testgen.common.models.user import User
2929

3030
LOG = logging.getLogger("testgen")

0 commit comments

Comments
 (0)