def get(self, request): charts = [] charts.append( generate_chart(ChartType.SLACK_DISCOVER_TOTAL_PERIOD, discover_total_period)) charts.append( generate_chart(ChartType.SLACK_DISCOVER_TOTAL_PERIOD, discover_multi_y_axis)) charts.append( generate_chart(ChartType.SLACK_DISCOVER_TOTAL_PERIOD, discover_empty)) charts.append( generate_chart(ChartType.SLACK_DISCOVER_TOTAL_DAILY, discover_total_daily)) charts.append( generate_chart(ChartType.SLACK_DISCOVER_TOTAL_DAILY, discover_total_daily_multi)) charts.append( generate_chart(ChartType.SLACK_DISCOVER_TOTAL_DAILY, discover_empty)) charts.append( generate_chart(ChartType.SLACK_DISCOVER_TOP5_PERIOD, discover_top5)) charts.append( generate_chart(ChartType.SLACK_DISCOVER_TOP5_PERIOD, discover_empty)) charts.append( generate_chart(ChartType.SLACK_DISCOVER_TOP5_DAILY, discover_top5)) charts.append( generate_chart(ChartType.SLACK_DISCOVER_TOP5_DAILY, discover_empty)) return render_to_response("sentry/debug/chart-renderer.html", context={"charts": charts})
def test_simple(self, mock_uuid): mock_uuid.return_value = self.get_mock_uuid() chart_data = { "seriesName": "Discover total period", "series": [ [1616168400, [{"count": 0}]], [1616168700, [{"count": 12}]], [1616169000, [{"count": 13}]], ], } service_url = "http://chartcuterie" image_data = b"this is png data" responses.add( method=responses.POST, url=f"{service_url}/render", status=200, content_type="image/png", body=image_data, ) options = { "chart-rendering.enabled": True, "chart-rendering.chartcuterie": {"url": service_url}, } # Don't upload our image anywhere with self.options(options): data = generate_chart(ChartType.SLACK_DISCOVER_TOTAL_PERIOD, chart_data, upload=False) assert data == image_data request = responses.calls[0].request payload = json.loads(request.body) assert payload == { "requestId": "abc123", "style": ChartType.SLACK_DISCOVER_TOTAL_PERIOD.value, "data": chart_data, } # Test the image can be uploaded and we get a URL back with self.options(options): url = generate_chart(ChartType.SLACK_DISCOVER_TOTAL_PERIOD, chart_data) assert url == absolute_uri(reverse("sentry-serve-media", args=["abc123.png"])) resp = self.client.get(url) assert next(resp.streaming_content) == image_data
def test_failed(self): chart_data = {"seriesName": "Discover total period", "series": []} service_url = "http://chartcuterie" responses.add( method=responses.POST, url=f"{service_url}/render", status=500, body="Service down" ) options = { "chart-rendering.enabled": True, "chart-rendering.chartcuterie": {"url": service_url}, } with self.options(options), pytest.raises( RuntimeError, match="Chartcuterie responded with 500: Service down" ): generate_chart(ChartType.SLACK_DISCOVER_TOTAL_PERIOD, chart_data)
def get(self, request): charts = [] url = generate_chart(ChartType.SLACK_DISCOVER_TOTAL_PERIOD, discover_total) charts.append(url) return render_to_response("sentry/debug/chart-renderer.html", context={"charts": charts})
def unfurl_discover(data, integration, links: List[UnfurlableUrl]) -> UnfurledUrl: orgs_by_slug = {org.slug: org for org in integration.organizations.all()} unfurls = {} for link in links: org_slug = link.args["org_slug"] org = orgs_by_slug.get(org_slug) # If the link shared is an org w/o the slack integration do not unfurl if not org: continue if not features.has("organizations:chart-unfurls", org): continue params = link.args["query"] query_id = params.get("id", None) saved_query = {} if query_id: try: response = client.get( auth=ApiKey(organization=org, scope_list=["org:read"]), path= f"/organizations/{org_slug}/discover/saved/{query_id}/", ) except Exception as exc: logger.error( "Failed to load saved query for unfurl: %s", str(exc), exc_info=True, ) else: saved_query = response.data # Override params from Discover Saved Query if they aren't in the URL params.setlist( "order", params.getlist("sort") or to_list(saved_query.get("orderby"))) params.setlist( "name", params.getlist("name") or to_list(saved_query.get("name"))) params.setlist( "yAxis", params.getlist("yAxis") or to_list(saved_query.get("yAxis", "count()"))) params.setlist( "field", params.getlist("field") or to_list(saved_query.get("fields"))) # Only override if key doesn't exist since we want to account for # an intermediate state where the query could have been cleared if "query" not in params: params.setlist( "query", params.getlist("query") or to_list(saved_query.get("query"))) display_mode = str( params.get("display") or saved_query.get("display", "default")) if "daily" in display_mode: params.setlist("interval", ["1d"]) if "top5" in display_mode: params.setlist("topEvents", [f"{TOP_N}"]) try: resp = client.get( auth=ApiKey(organization=org, scope_list=["org:read"]), path=f"/organizations/{org_slug}/events-stats/", params=params, ) except Exception as exc: logger.error( "Failed to load events-stats for unfurl: %s", str(exc), exc_info=True, ) continue chart_data = {"seriesName": params.get("yAxis"), "stats": resp.data} style = display_modes.get(display_mode, display_modes["default"]) try: url = generate_chart(style, chart_data) except RuntimeError as exc: logger.error( "Failed to generate chat for discover unfurl: %s", str(exc), exc_info=True, ) continue unfurls[link.url] = build_discover_attachment( title=link.args["query"].get("name", "Dashboards query"), chart_url=url, ) return unfurls
def unfurl_discover(data, integration, links: List[UnfurlableUrl]) -> UnfurledUrl: orgs_by_slug = {org.slug: org for org in integration.organizations.all()} unfurls = {} for link in links: org_slug = link.args["org_slug"] org = orgs_by_slug.get(org_slug) # If the link shared is an org w/o the slack integration do not unfurl if not org: continue if not features.has("organizations:chart-unfurls", org): continue params = link.args["query"] params.setlist("order", params.get("sort")) display_mode = str(link.args["query"].get("display", "default")) if "daily" in display_mode: params.setlist("interval", ["1d"]) if "top5" in display_mode: params.setlist("topEvents", [f"{TOP_N}"]) try: resp = client.get( auth=ApiKey(organization=org, scope_list=["org:read"]), path=f"/organizations/{org_slug}/events-stats/", params=params, ) except Exception as exc: logger.error( "Failed to load events-stats for unfurl: %s", str(exc), exc_info=True, ) continue chart_data = { "seriesName": link.args["query"].get("yAxis", "count()"), "stats": resp.data } style = display_modes.get(display_mode, display_modes["default"]) try: url = generate_chart(style, chart_data) except RuntimeError as exc: logger.error( "Failed to generate chat for discover unfurl: %s", str(exc), exc_info=True, ) continue unfurls[link.url] = build_discover_attachment( title=link.args["query"].get("name", "Dashboards query"), chart_url=url, ) return unfurls
def unfurl_discover( data: HttpRequest, integration: Integration, links: List[UnfurlableUrl], user: Optional["User"], ) -> UnfurledUrl: orgs_by_slug = {org.slug: org for org in integration.organizations.all()} unfurls = {} for link in links: org_slug = link.args["org_slug"] org = orgs_by_slug.get(org_slug) # If the link shared is an org w/o the slack integration do not unfurl if not org: continue if not features.has("organizations:discover-basic", org): continue params = link.args["query"] query_id = params.get("id", None) saved_query = {} if query_id: try: response = client.get( auth=ApiKey(organization=org, scope_list=["org:read"]), path=f"/organizations/{org_slug}/discover/saved/{query_id}/", ) except Exception as exc: logger.error( f"Failed to load saved query for unfurl: {exc}", exc_info=True, ) else: saved_query = response.data # Override params from Discover Saved Query if they aren't in the URL params.setlist( "order", params.getlist("sort") or (to_list(saved_query.get("orderby")) if saved_query.get("orderby") else []), ) params.setlist("name", params.getlist("name") or to_list(saved_query.get("name"))) fields = params.getlist("field") or to_list(saved_query.get("fields")) # Mimic Discover to pick the first aggregate as the yAxis option if # one isn't specified. axis_options = [field for field in fields if is_aggregate(field)] + [DEFAULT_AXIS_OPTION] params.setlist( "yAxis", params.getlist("yAxis") or to_list(saved_query.get("yAxis", axis_options[0])) ) params.setlist("field", params.getlist("field") or to_list(saved_query.get("fields"))) params.setlist( "project", params.getlist("project") or (to_list(saved_query.get("project")) if saved_query.get("project") else []), ) # Only override if key doesn't exist since we want to account for # an intermediate state where the query could have been cleared if "query" not in params: params.setlist( "query", params.getlist("query") or to_list(saved_query.get("query", "")) ) display_mode = str(params.get("display") or saved_query.get("display", "default")) if "daily" in display_mode: params.setlist("interval", ["1d"]) if "top5" in display_mode: params.setlist( "topEvents", params.getlist("topEvents") or to_list(saved_query.get("topEvents", f"{TOP_N}")), ) y_axis = params.getlist("yAxis")[0] if display_mode != "dailytop5": display_mode = get_top5_display_mode(y_axis) else: # topEvents param persists in the URL in some cases, we want to discard # it if it's not a top n display type. params.pop("topEvents", None) if "previous" in display_mode: stats_period = params.getlist("statsPeriod", [DEFAULT_PERIOD])[0] parsed_period = parse_stats_period(stats_period) if parsed_period and parsed_period <= timedelta(days=MAX_PERIOD_DAYS_INCLUDE_PREVIOUS): stats_period = get_double_period(stats_period) params.setlist("statsPeriod", [stats_period]) endpoint = "events-stats/" if "worldmap" in display_mode: endpoint = "events-geo/" params.setlist("field", params.getlist("yAxis")) params.pop("sort", None) try: resp = client.get( auth=ApiKey(organization=org, scope_list=["org:read"]), user=user, path=f"/organizations/{org_slug}/{endpoint}", params=params, ) except Exception as exc: logger.error( f"Failed to load {endpoint} for unfurl: {exc}", exc_info=True, ) continue chart_data = {"seriesName": params.get("yAxis"), "stats": resp.data} style = display_modes.get(display_mode, display_modes["default"]) try: url = generate_chart(style, chart_data) except RuntimeError as exc: logger.error( f"Failed to generate chart for discover unfurl: {exc}", exc_info=True, ) continue unfurls[link.url] = SlackDiscoverMessageBuilder( title=link.args["query"].get("name", "Dashboards query"), chart_url=url, ).build() analytics.record( "integrations.slack.chart_unfurl", organization_id=integration.organizations.all()[0].id, user_id=user.id if user else None, unfurls_count=len(unfurls), ) return unfurls