FEATURE: Add JSON result type component (#260)
If a column is payload or contains _payload it will be assumed it has JSON data in it, then we will show the truncated JSON in the result column with a button to show the full-screen formatted JSON using our full-screen code viewer. We also do the same if the column is the `json` postgres data type.
This commit is contained in:
parent
3e5f679fee
commit
5776aa7fc9
|
@ -17,6 +17,7 @@ import UrlViewComponent from "./result-types/url";
|
|||
import UserViewComponent from "./result-types/user";
|
||||
import GroupViewComponent from "./result-types/group";
|
||||
import HtmlViewComponent from "./result-types/html";
|
||||
import JsonViewComponent from "./result-types/json";
|
||||
import CategoryViewComponent from "./result-types/category";
|
||||
|
||||
const VIEW_COMPONENTS = {
|
||||
|
@ -29,6 +30,7 @@ const VIEW_COMPONENTS = {
|
|||
user: UserViewComponent,
|
||||
group: GroupViewComponent,
|
||||
html: HtmlViewComponent,
|
||||
json: JsonViewComponent,
|
||||
category: CategoryViewComponent,
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<div class="result-json">
|
||||
<div class="result-json-value">{{@ctx.value}}</div>
|
||||
<DButton
|
||||
class="result-json-button"
|
||||
@action={{action "viewJson"}}
|
||||
@icon="ellipsis-h"
|
||||
@title="explorer.view_json"
|
||||
/>
|
||||
</div>
|
|
@ -0,0 +1,31 @@
|
|||
import Component from "@glimmer/component";
|
||||
import FullscreenCodeModal from "discourse/components/modal/fullscreen-code";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
import { cached } from "@glimmer/tracking";
|
||||
|
||||
export default class Json extends Component {
|
||||
@service dialog;
|
||||
@service modal;
|
||||
|
||||
@cached
|
||||
get parsedJson() {
|
||||
try {
|
||||
return JSON.parse(this.args.ctx.value);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
viewJson() {
|
||||
this.modal.show(FullscreenCodeModal, {
|
||||
model: {
|
||||
code: this.parsedJson
|
||||
? JSON.stringify(this.parsedJson, null, 2)
|
||||
: this.args.ctx.value,
|
||||
codeClasses: "",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
|
@ -416,6 +416,18 @@ table.group-reports {
|
|||
display: block;
|
||||
color: inherit !important;
|
||||
}
|
||||
.result-json {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.result-json-value {
|
||||
flex: 1;
|
||||
margin-right: 0.5em;
|
||||
max-width: 250px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.explorer-pad-bottom {
|
||||
margin-bottom: 200px;
|
||||
|
|
|
@ -56,6 +56,7 @@ en:
|
|||
no: "No"
|
||||
null_: "Null"
|
||||
export: "Export"
|
||||
view_json: "View JSON"
|
||||
save: "Save Changes"
|
||||
saverun: "Save Changes and Run"
|
||||
run: "Run"
|
||||
|
|
|
@ -5,6 +5,10 @@ module ::DiscourseDataExplorer
|
|||
end
|
||||
|
||||
module DataExplorer
|
||||
# Used for ftype calls, see https://www.rubydoc.info/gems/pg/0.17.1/PG%2FResult:ftype
|
||||
# and /usr/include/postgresql/server/catalog/pg_type_d.h
|
||||
PG_TYPE_OID_JSON = 114
|
||||
|
||||
# Run a data explorer query on the currently connected database.
|
||||
#
|
||||
# @param [Query] query the Query object to run
|
||||
|
@ -131,6 +135,9 @@ module ::DiscourseDataExplorer
|
|||
html: {
|
||||
ignore: true,
|
||||
},
|
||||
json: {
|
||||
ignore: true,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -145,7 +152,6 @@ module ::DiscourseDataExplorer
|
|||
needed_classes = {}
|
||||
ret = {}
|
||||
col_map = {}
|
||||
|
||||
pg_result.fields.each_with_index do |col, idx|
|
||||
rgx = column_regexes.find { |r| r.match col }
|
||||
if rgx
|
||||
|
@ -158,6 +164,8 @@ module ::DiscourseDataExplorer
|
|||
needed_classes[cls] << idx
|
||||
elsif col =~ /^\w+_url$/
|
||||
col_map[idx] = "url"
|
||||
elsif col =~ /^\w+_payload$/ || col == "payload" || pg_result.ftype(idx) == PG_TYPE_OID_JSON
|
||||
col_map[idx] = "json"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -57,5 +57,36 @@ describe DiscourseDataExplorer::DataExplorer do
|
|||
expect(result[:pg_result].to_a.size).to eq(1)
|
||||
expect(result[:pg_result][0]["id"]).to eq(topic2.id)
|
||||
end
|
||||
|
||||
describe ".add_extra_data" do
|
||||
it "treats any column with payload in the name as 'json'" do
|
||||
Fabricate(:reviewable_queued_post)
|
||||
sql = <<~SQL
|
||||
SELECT id, payload FROM reviewables LIMIT 10
|
||||
SQL
|
||||
query = DiscourseDataExplorer::Query.create!(name: "some query", sql: sql)
|
||||
result = described_class.run_query(query)
|
||||
_, colrender = DiscourseDataExplorer::DataExplorer.add_extra_data(result[:pg_result])
|
||||
expect(colrender).to eq({ 1 => "json" })
|
||||
end
|
||||
|
||||
it "treats columns with the actual json data type as 'json'" do
|
||||
ApiKeyScope.create(
|
||||
resource: "topics",
|
||||
action: "update",
|
||||
api_key_id: Fabricate(:api_key).id,
|
||||
allowed_parameters: {
|
||||
"category_id" => ["#{topic.category_id}"],
|
||||
},
|
||||
)
|
||||
sql = <<~SQL
|
||||
SELECT id, allowed_parameters FROM api_key_scopes LIMIT 10
|
||||
SQL
|
||||
query = DiscourseDataExplorer::Query.create!(name: "some query", sql: sql)
|
||||
result = described_class.run_query(query)
|
||||
_, colrender = DiscourseDataExplorer::DataExplorer.add_extra_data(result[:pg_result])
|
||||
expect(colrender).to eq({ 1 => "json" })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -42,4 +42,18 @@ RSpec.describe "Reports", type: :system, js: true do
|
|||
find(".query-run .btn-primary").click
|
||||
expect(page).to have_css(".query-results .result-header")
|
||||
end
|
||||
|
||||
it "allows user to run a report with a JSON column and open a fullscreen code viewer" do
|
||||
Fabricate(:reviewable_queued_post)
|
||||
sql = <<~SQL
|
||||
SELECT id, payload FROM reviewables LIMIT 10
|
||||
SQL
|
||||
json_query = DiscourseDataExplorer::Query.create!(name: "some query", sql: sql)
|
||||
sign_in(user)
|
||||
visit("/g/group/reports/#{json_query.id}")
|
||||
find(".query-run .btn-primary").click
|
||||
expect(page).to have_css(".query-results .result-json")
|
||||
first(".query-results .result-json .btn.result-json-button").click
|
||||
expect(page).to have_css(".fullscreen-code-modal")
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue