Skip to content

Commit 8d19a33

Browse files
FIX: Optionally linkify URL columns server-side (#330)
Followup da1c99e We already had the functionality to convert results like this: ``` |blah_url| |--------| |3,https://test.com| ``` To a link clientside, so in the UI it looks like this: ``` <a href="https://test.com">3</a> ``` With the addition of the recurring report to post automation, and the existing report to PM automation, we also need to be able to do this server-side, so reports don't come out malformed if they have these type of count + URL columns.
1 parent b43d82d commit 8d19a33

File tree

6 files changed

+73
-18
lines changed

6 files changed

+73
-18
lines changed

assets/javascripts/discourse/components/query-row-content.js

+10-9
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import TextViewComponent from "./result-types/text";
88
export default class QueryRowContent extends Component {
99
@cached
1010
get results() {
11-
return this.args.columnComponents.map((t, idx) => {
11+
return this.args.columnComponents.map((componentDefinition, idx) => {
1212
const value = this.args.row[idx],
1313
id = parseInt(value, 10);
1414

@@ -23,27 +23,28 @@ export default class QueryRowContent extends Component {
2323
component: TextViewComponent,
2424
textValue: "NULL",
2525
};
26-
} else if (t.name === "text") {
26+
} else if (componentDefinition.name === "text") {
2727
return {
2828
component: TextViewComponent,
2929
textValue: escapeExpression(this.args.row[idx].toString()),
3030
};
3131
}
3232

33-
const lookupFunc = this.args[`lookup${capitalize(t.name)}`];
33+
const lookupFunc =
34+
this.args[`lookup${capitalize(componentDefinition.name)}`];
3435
if (lookupFunc) {
35-
ctx[t.name] = lookupFunc.call(this.args, id);
36+
ctx[componentDefinition.name] = lookupFunc.call(this.args, id);
3637
}
3738

38-
if (t.name === "url") {
39+
if (componentDefinition.name === "url") {
3940
let [url, name] = guessUrl(value);
4041
ctx["href"] = url;
4142
ctx["target"] = name;
4243
}
4344

4445
try {
4546
return {
46-
component: t.component || TextViewComponent,
47+
component: componentDefinition.component || TextViewComponent,
4748
ctx,
4849
};
4950
} catch (e) {
@@ -53,10 +54,10 @@ export default class QueryRowContent extends Component {
5354
}
5455
}
5556

56-
function guessUrl(t) {
57-
let [dest, name] = [t, t];
57+
function guessUrl(columnValue) {
58+
let [dest, name] = [columnValue, columnValue];
5859

59-
const split = t.split(/,(.+)/);
60+
const split = columnValue.split(/,(.+)/);
6061

6162
if (split.length > 1) {
6263
name = split[0];

lib/discourse_data_explorer/report_generator.rb

+4-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ def self.generate(query_id, query_params, recipients, opts = {})
1313
query.update!(last_run_at: Time.now)
1414

1515
return [] if opts[:skip_empty] && result[:pg_result].values.empty?
16-
table = ResultToMarkdown.convert(result[:pg_result])
16+
table =
17+
ResultToMarkdown.convert(result[:pg_result], render_url_columns: opts[:render_url_columns])
1718

1819
build_report_pms(query, table, recipients, attach_csv: opts[:attach_csv], result:)
1920
end
@@ -28,7 +29,8 @@ def self.generate_post(query_id, query_params, opts = {})
2829
query.update!(last_run_at: Time.now)
2930

3031
return {} if opts[:skip_empty] && result[:pg_result].values.empty?
31-
table = ResultToMarkdown.convert(result[:pg_result])
32+
table =
33+
ResultToMarkdown.convert(result[:pg_result], render_url_columns: opts[:render_url_columns])
3234

3335
build_report_post(query, table, attach_csv: opts[:attach_csv], result:)
3436
end

lib/discourse_data_explorer/result_to_markdown.rb

+12-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
module ::DiscourseDataExplorer
66
class ResultToMarkdown
7-
def self.convert(pg_result)
7+
def self.convert(pg_result, render_url_columns = false)
88
relations, colrender = DataExplorer.add_extra_data(pg_result)
99
result_data = []
1010

@@ -17,7 +17,8 @@ def self.convert(pg_result)
1717

1818
row.each_with_index do |col, col_index|
1919
col_name = pg_result.fields[col_index]
20-
related = relations.dig(colrender[col_index].to_sym) unless colrender[col_index].nil?
20+
col_render = colrender[col_index]
21+
related = relations.dig(col_render.to_sym) if col_render.present?
2122

2223
if related.is_a?(ActiveModel::ArraySerializer)
2324
related_row = related.object.find_by(id: col)
@@ -32,6 +33,9 @@ def self.convert(pg_result)
3233
else
3334
row_data[col_index] = "#{related_row[column]} (#{col})"
3435
end
36+
elsif col_render == "url" && render_url_columns
37+
url, text = guess_url(col)
38+
row_data[col_index] = "[#{text}](#{url})"
3539
else
3640
row_data[col_index] = col
3741
end
@@ -45,5 +49,11 @@ def self.convert(pg_result)
4549

4650
"|#{table_headers}\n|#{table_body}\n#{result_data.join}"
4751
end
52+
53+
def self.guess_url(column_value)
54+
text, url = column_value.split(/,(.+)/)
55+
56+
[url || column_value, text || column_value]
57+
end
4858
end
4959
end

plugin.rb

+7-2
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,12 @@ module ::DiscourseDataExplorer
105105
end
106106

107107
DiscourseDataExplorer::ReportGenerator
108-
.generate(query_id, query_params, recipients, { skip_empty:, attach_csv: })
108+
.generate(
109+
query_id,
110+
query_params,
111+
recipients,
112+
{ skip_empty:, attach_csv:, render_url_columns: true },
113+
)
109114
.each do |pm|
110115
begin
111116
utils.send_pm(pm, automation_id: automation.id, prefers_encrypt: false)
@@ -153,7 +158,7 @@ module ::DiscourseDataExplorer
153158
DiscourseDataExplorer::ReportGenerator.generate_post(
154159
query_id,
155160
query_params,
156-
{ skip_empty:, attach_csv: },
161+
{ skip_empty:, attach_csv:, render_url_columns: true },
157162
)
158163

159164
next if post.empty?

spec/automation/recurring_data_explorer_result_pm_spec.rb

+21-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
end
1919
fab!(:query) { Fabricate(:query, sql: "SELECT 'testabcd' AS data") }
2020
fab!(:query_group) { Fabricate(:query_group, query: query, group: group) }
21-
fab!(:query_group) { Fabricate(:query_group, query: query, group: another_group) }
21+
fab!(:query_group_2) { Fabricate(:query_group, query: query, group: another_group) }
2222

2323
let!(:recipients) do
2424
[user.username, not_allowed_user.username, another_user.username, another_group.name]
@@ -105,5 +105,25 @@
105105

106106
expect { automation.trigger! }.to_not change { Post.count }
107107
end
108+
109+
it "works with a query that returns URLs in a number,url format" do
110+
freeze_time
111+
query.update!(sql: "SELECT 3 || ',https://test.com' AS some_url")
112+
automation.update(last_updated_by_id: admin.id)
113+
automation.trigger!
114+
115+
expect(Post.last.raw).to eq(
116+
I18n.t(
117+
"data_explorer.report_generator.private_message.body",
118+
recipient_name: another_group.name,
119+
query_name: query.name,
120+
table: "| some_url |\n| :----- |\n| [3](https://test.com) |\n",
121+
base_url: Discourse.base_url,
122+
query_id: query.id,
123+
created_at: Time.zone.now.strftime("%Y-%m-%d at %H:%M:%S"),
124+
timezone: Time.zone.name,
125+
).chomp,
126+
)
127+
end
108128
end
109129
end

spec/automation/recurring_data_explorer_result_topic_spec.rb

+19-2
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,12 @@
1212
fab!(:topic)
1313

1414
fab!(:group) { Fabricate(:group, users: [user, another_user]) }
15-
fab!(:another_group) { Fabricate(:group, users: [group_user]) }
1615

1716
fab!(:automation) do
1817
Fabricate(:automation, script: "recurring_data_explorer_result_topic", trigger: "recurring")
1918
end
2019
fab!(:query) { Fabricate(:query, sql: "SELECT 'testabcd' AS data") }
2120
fab!(:query_group) { Fabricate(:query_group, query: query, group: group) }
22-
fab!(:query_group) { Fabricate(:query_group, query: query, group: another_group) }
2321

2422
before do
2523
SiteSetting.data_explorer_enabled = true
@@ -88,5 +86,24 @@
8886

8987
expect { automation.trigger! }.to_not change { Post.count }
9088
end
89+
90+
it "works with a query that returns URLs in a number,url format" do
91+
freeze_time
92+
query.update!(sql: "SELECT 3 || ',https://test.com' AS some_url")
93+
automation.update(last_updated_by_id: admin.id)
94+
automation.trigger!
95+
96+
expect(topic.posts.last.raw).to eq(
97+
I18n.t(
98+
"data_explorer.report_generator.post.body",
99+
query_name: query.name,
100+
table: "| some_url |\n| :----- |\n| [3](https://test.com) |\n",
101+
base_url: Discourse.base_url,
102+
query_id: query.id,
103+
created_at: Time.zone.now.strftime("%Y-%m-%d at %H:%M:%S"),
104+
timezone: Time.zone.name,
105+
).chomp,
106+
)
107+
end
91108
end
92109
end

0 commit comments

Comments
 (0)