Skip to content
Closed
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
2 changes: 2 additions & 0 deletions cluster-info.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- `fcd1957` feat(export-controller): limit CSV delimiter to printable ASCII
- `fcc6ffb` fix(export-controller): do not allow alfa-numeric CSV delimiters
25 changes: 25 additions & 0 deletions cluster.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"id": "C013",
"title": "Restrict CSV delimiter pattern to printable non-alphanumeric ASCII",
"services": [
"gooddata-automation-client",
"gooddata-export-client"
],
"commits": [
{
"sha": "fcd19575046d93ed0c58976ceccf16d3f9e198f4",
"author": "Vladim\u00edr Kro\u010d\u00e1k",
"author_email": "vladimir.krocak@gooddata.com",
"message": "feat(export-controller): limit CSV delimiter to printable ASCII"
},
{
"sha": "fcc6ffb550c40f93db098662056ac819c41cb1a7",
"author": "Vladim\u00edr Kro\u010d\u00e1k",
"author_email": "vladimir.krocak@gooddata.com",
"message": "fix(export-controller): do not allow alfa-numeric CSV delimiters"
}
],
"diff": "--- a/gooddata-automation-client.json\n+++ b/gooddata-automation-client.json\n@@ DashboardTabularExportRequest.csvDelimiter @@\n- \"pattern\": \"^[^\\\\r\\\\n\\\"]$\",\n+ \"pattern\": \"^[\\\\t !#$%&()*+\\\\-.,/:;<=>?@\\\\[\\\\\\\\]^_{|}~]$\",\n--- a/gooddata-export-client.json\n+++ b/gooddata-export-client.json\n@@ TabularExportRequest.csvDelimiter @@\n- \"pattern\": \"^[^\\\\r\\\\n\\\"]$\",\n+ \"pattern\": \"^[\\\\t !#$%&()*+\\\\-.,/:;<=>?@\\\\[\\\\\\\\]^_{|}~]$\"",
"jira_tickets": [],
"sdk_impact": "modification"
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# (C) 2023 GoodData Corporation
from __future__ import annotations

import re
from typing import Literal

from attrs import define
Expand All @@ -16,6 +17,9 @@

GrandTotalsPosition = Literal["pinnedBottom", "pinnedTop", "bottom", "top"]

# Pattern matching printable non-alphanumeric ASCII characters (tab, space, and special chars)
_CSV_DELIMITER_PATTERN = re.compile(r"^[\t !#$%&()*+\-.,/:;<=>?@\[\\\]^_{|}~]$")


@define(kw_only=True)
class ExportCustomLabel(Base):
Expand Down Expand Up @@ -51,6 +55,15 @@ class ExportSettings(Base):
merge_headers: bool
show_filters: bool
grand_totals_position: GrandTotalsPosition | None = None
delimiter: str | None = None

def __attrs_post_init__(self) -> None:
if self.delimiter is not None and not _CSV_DELIMITER_PATTERN.match(self.delimiter):
raise ValueError(
f"delimiter '{self.delimiter}' is not a valid CSV delimiter. "
"It must be a single printable non-alphanumeric ASCII character "
"(tab, space, or one of: !#$%&()*+\\-.,/:;<=>?@[\\\\]^_{{|}}~)."
)

@staticmethod
def client_class() -> type[ApiSettings]:
Expand Down
10 changes: 10 additions & 0 deletions result.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"status": "implemented",
"cluster_id": "C013",
"summary": "Added `delimiter: str | None = None` field to `ExportSettings` in the SDK wrapper (`catalog/export/request.py`) to expose CSV delimiter functionality from the underlying `Settings.delimiter` API field. Added `__attrs_post_init__` validation using the new restricted pattern `^[\\t !#$%&()*+\\-.,/:;<=>?@\\[\\\\\\]^_{|}~]$` (printable non-alphanumeric ASCII only, excluding alphanumeric chars and control chars like CR/LF). Also added a module-level `_CSV_DELIMITER_PATTERN` regex constant and `import re`. The `Base.to_api()` serialization automatically includes `delimiter` when set and excludes it when `None`. No changes to `__init__.py` needed (ExportSettings was already exported).",
"files_changed": [
"packages/gooddata-sdk/src/gooddata_sdk/catalog/export/request.py"
],
"reason": "",
"cost_usd": 1.2988483499999997
}
Loading