Skip to content
Merged
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
23 changes: 21 additions & 2 deletions conserver/links/post_analysis_to_slack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The Post Analysis to Slack link is a specialized plugin that posts vCon analysis
default_options = {
"token": None, # Slack API token
"channel_name": None, # Default Slack channel name
"url": "Url to hex sheet", # URL for the details button
"url": "Url to link to the details page", # URL template for the details button
Comment thread
ScottHenshaw86 marked this conversation as resolved.
"analysis_to_post": "summary", # Type of analysis to post
"only_if": {
"analysis_type": "customer_frustration", # Analysis type to match
Expand All @@ -31,12 +31,31 @@ default_options = {

- `token`: Your Slack API token
- `channel_name`: Default Slack channel for notifications
- `url`: URL for the details button in the Slack message
- `url`: URL template for the details button (see below)
- `analysis_to_post`: Type of analysis to post (e.g., "summary")
- `only_if`: Conditions that must be met for posting:
- `analysis_type`: Type of analysis to match
- `includes`: Text that must be included in the analysis body

### URL templating

The `url` option is a template. If it contains `{vcon_id}`, the vCon uuid
is substituted at that exact position — the template owns the quoting and
placement. This lets the same link work with destinations that embed the
id in different shapes:

```yaml
# Hex sheet — uuid must be wrapped in double quotes inside a query param.
url: 'https://app.hex.tech/<workspace>/app/<app>/latest?_vcon_id="{vcon_id}"'

# Path-based destination — uuid embedded raw in the path.
url: "https://app.example.com/conversations/{vcon_id}"
```

If the `url` has no `{vcon_id}` placeholder, the link falls back to the
legacy Hex-shaped suffix (`?_vcon_id="<uuid>"`) so existing configs keep
working unchanged.

## Usage

The link processes vCons by:
Expand Down
21 changes: 19 additions & 2 deletions conserver/links/post_analysis_to_slack/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,29 @@
default_options = {
"token": None,
"channel_name": None,
"url": "Url to hex sheet",
"url": "Url to link to the details page",
Comment thread
ScottHenshaw86 marked this conversation as resolved.
"analysis_to_post": "summary",
"only_if": {"analysis_type": "customer_frustration", "includes": "NEEDS REVIEW"},
}


def build_details_url(url_template: str, vcon_uuid: str) -> str:
"""Render the details URL for a vCon.

If ``url_template`` contains a ``{vcon_id}`` placeholder, substitute the
uuid into it — letting each deployment decide where and how the id is
embedded (e.g. quoted for Hex, raw in a path for the portal).

Otherwise, fall back to the legacy ``?_vcon_id="<uuid>"`` suffix so
existing configs keep working unchanged.
"""
if "{vcon_id}" in url_template:
# str.replace (not str.format) so unrelated ``{...}`` segments in
# the configured URL can't raise KeyError/ValueError at runtime.
return url_template.replace("{vcon_id}", vcon_uuid)
return f'{url_template}?_vcon_id="{vcon_uuid}"'
Comment thread
ScottHenshaw86 marked this conversation as resolved.


def get_team(vcon):
team_name = None
for a in vcon.attachments:
Expand Down Expand Up @@ -108,7 +125,7 @@ def run(vcon_id, link_name, opts=default_options):
# and uses ``only_if.analysis_type`` + ``only_if.includes``.
# Unifying the two is its own refactor.

url = f"{opts['url']}?_vcon_id=\"{vcon.uuid}\""
url = build_details_url(opts["url"], vcon.uuid)
team_name = get_team(vcon)
dealer_name = get_dealer(vcon)
summary = get_summary(vcon, a["dialog"])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pytest

from links.post_analysis_to_slack import (
build_details_url,
get_dealer,
get_summary,
get_team,
Expand Down Expand Up @@ -48,6 +49,42 @@ def test_helper_functions_extract_team_dealer_and_summary():
assert get_summary(vcon, 1) is None


def test_build_details_url_legacy_appends_quoted_query_param():
# Old config style with no placeholder — keep the legacy Hex-shaped suffix.
assert (
build_details_url("https://details.test", "test-uuid")
== 'https://details.test?_vcon_id="test-uuid"'
)


Comment thread
ScottHenshaw86 marked this conversation as resolved.
def test_build_details_url_hex_placeholder_quotes_uuid():
# Hex still expects the uuid wrapped in double quotes; the template owns the quoting.
template = 'https://app.hex.tech/x/app/y/latest?_vcon_id="{vcon_id}"'
assert (
build_details_url(template, "abc-123")
== 'https://app.hex.tech/x/app/y/latest?_vcon_id="abc-123"'
)


def test_build_details_url_portal_placeholder_embeds_raw_uuid():
# Portal embeds the uuid directly in the path, no quotes.
template = "https://portal.strolidcxm.com/app/conversations/{vcon_id}"
assert (
build_details_url(template, "abc-123")
== "https://portal.strolidcxm.com/app/conversations/abc-123"
)


def test_build_details_url_leaves_unrelated_braces_alone():
# Unrelated ``{...}`` segments in the template must NOT raise — only
# ``{vcon_id}`` is substituted.
template = "https://app.example.com/users/{user_id}/conversations/{vcon_id}"
assert (
build_details_url(template, "abc-123")
== "https://app.example.com/users/{user_id}/conversations/abc-123"
)


@patch("links.post_analysis_to_slack.increment_counter")
@patch("links.post_analysis_to_slack.WebClient")
def test_post_blocks_to_channel_falls_back_and_reraises(mock_web_client, mock_increment_counter):
Expand Down
Loading