mirror of
https://github.com/discourse/discourse.git
synced 2025-05-24 03:36:18 +08:00
avoid async report pattern and replace with simpler hijack
This commit is contained in:
@ -16,7 +16,7 @@ export default Ember.Component.extend(AsyncReport, {
|
|||||||
fetchReport() {
|
fetchReport() {
|
||||||
this._super();
|
this._super();
|
||||||
|
|
||||||
let payload = { data: { async: true, facets: ["total", "prev30Days"] } };
|
let payload = { data: { cache: true, facets: ["total", "prev30Days"] } };
|
||||||
|
|
||||||
if (this.get("startDate")) {
|
if (this.get("startDate")) {
|
||||||
payload.data.start_date = this.get("startDate").format("YYYY-MM-DD[T]HH:mm:ss.SSSZZ");
|
payload.data.start_date = this.get("startDate").format("YYYY-MM-DD[T]HH:mm:ss.SSSZZ");
|
||||||
@ -31,13 +31,11 @@ export default Ember.Component.extend(AsyncReport, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.set("reports", Ember.Object.create());
|
this.set("reports", Ember.Object.create());
|
||||||
this.set("reportKeys", []);
|
|
||||||
|
|
||||||
return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => {
|
return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => {
|
||||||
return ajax(dataSource, payload)
|
return ajax(dataSource, payload)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.set(`reports.${response.report.report_key}`, this.loadReport(response.report));
|
this.set(`reports.${response.report.report_key}`, this.loadReport(response.report));
|
||||||
this.get("reportKeys").pushObject(response.report.report_key);
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ export default Ember.Component.extend(AsyncReport, {
|
|||||||
this._super();
|
this._super();
|
||||||
|
|
||||||
let payload = {
|
let payload = {
|
||||||
data: { async: true, facets: ["prev_period"] }
|
data: { cache: true, facets: ["prev_period"] }
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.get("startDate")) {
|
if (this.get("startDate")) {
|
||||||
@ -58,13 +58,11 @@ export default Ember.Component.extend(AsyncReport, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.set("reports", Ember.Object.create());
|
this.set("reports", Ember.Object.create());
|
||||||
this.set("reportKeys", []);
|
|
||||||
|
|
||||||
return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => {
|
return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => {
|
||||||
return ajax(dataSource, payload)
|
return ajax(dataSource, payload)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.set(`reports.${response.report.report_key}`, this.loadReport(response.report));
|
this.set(`reports.${response.report.report_key}`, this.loadReport(response.report));
|
||||||
this.get("reportKeys").pushObject(response.report.report_key);
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
@ -4,44 +4,12 @@ export default Ember.Mixin.create({
|
|||||||
classNameBindings: ["isLoading"],
|
classNameBindings: ["isLoading"],
|
||||||
|
|
||||||
reports: null,
|
reports: null,
|
||||||
reportKeys: null,
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
dataSourceNames: "",
|
dataSourceNames: "",
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._super();
|
this._super();
|
||||||
|
|
||||||
this.set("reports", Ember.Object.create());
|
this.set("reports", Ember.Object.create());
|
||||||
this.set("reportKeys", []);
|
|
||||||
|
|
||||||
this._channels = this.get("dataSources");
|
|
||||||
this._callback = (report) => {
|
|
||||||
if (this.get("reportKeys").includes(report.report_key)) {
|
|
||||||
Em.run.next(() => {
|
|
||||||
if (this.get("reportKeys").includes(report.report_key)) {
|
|
||||||
const previousReport = this.get(`reports.${report.report_key}`);
|
|
||||||
this.set(`reports.${report.report_key}`, this.loadReport(report, previousReport));
|
|
||||||
this.renderReport();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// in case we did not subscribe in time ensure we always grab the
|
|
||||||
// last thing on the channel
|
|
||||||
this.subscribe(-2);
|
|
||||||
},
|
|
||||||
|
|
||||||
subscribe(position) {
|
|
||||||
this._channels.forEach(channel => {
|
|
||||||
this.messageBus.subscribe(channel, this._callback, position);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
unsubscribe() {
|
|
||||||
this._channels.forEach(channel => {
|
|
||||||
this.messageBus.unsubscribe(channel, this._callback);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("dataSourceNames")
|
@computed("dataSourceNames")
|
||||||
@ -49,21 +17,12 @@ export default Ember.Mixin.create({
|
|||||||
return dataSourceNames.split(",").map(source => `/admin/reports/${source}`);
|
return dataSourceNames.split(",").map(source => `/admin/reports/${source}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
this._super();
|
|
||||||
|
|
||||||
this.unsubscribe();
|
|
||||||
},
|
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
this._super();
|
this._super();
|
||||||
|
this.fetchReport()
|
||||||
Ember.run.later(this, function() {
|
.finally(() => {
|
||||||
this.fetchReport()
|
this.renderReport();
|
||||||
.finally(() => {
|
});
|
||||||
this.renderReport();
|
|
||||||
});
|
|
||||||
}, 500);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
didUpdateAttrs() {
|
didUpdateAttrs() {
|
||||||
|
@ -32,18 +32,36 @@ class Admin::ReportsController < Admin::AdminController
|
|||||||
limit = params[:limit].to_i
|
limit = params[:limit].to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
report = Report.find(report_type,
|
args = {
|
||||||
start_date: start_date,
|
start_date: start_date,
|
||||||
end_date: end_date,
|
end_date: end_date,
|
||||||
category_id: category_id,
|
category_id: category_id,
|
||||||
group_id: group_id,
|
group_id: group_id,
|
||||||
facets: facets,
|
facets: facets,
|
||||||
limit: limit,
|
limit: limit
|
||||||
async: params[:async])
|
}
|
||||||
|
|
||||||
raise Discourse::NotFound if report.blank?
|
report = nil
|
||||||
|
if (params[:cache])
|
||||||
|
report = Report.find_cached(report_type, args)
|
||||||
|
end
|
||||||
|
|
||||||
|
if report
|
||||||
|
return render_json_dump(report: report)
|
||||||
|
end
|
||||||
|
|
||||||
|
hijack do
|
||||||
|
report = Report.find(report_type, args)
|
||||||
|
|
||||||
|
raise Discourse::NotFound if report.blank?
|
||||||
|
|
||||||
|
if (params[:cache])
|
||||||
|
Report.cache(report, 35.minutes)
|
||||||
|
end
|
||||||
|
|
||||||
|
render_json_dump(report: report)
|
||||||
|
end
|
||||||
|
|
||||||
render_json_dump(report: report)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
require_dependency 'report'
|
|
||||||
|
|
||||||
module Jobs
|
|
||||||
class RetrieveReport < Jobs::Base
|
|
||||||
sidekiq_options retry: false
|
|
||||||
|
|
||||||
def execute(args)
|
|
||||||
raise Discourse::InvalidParameters.new(:report_type) if !args['report_type']
|
|
||||||
|
|
||||||
type = args.delete('report_type')
|
|
||||||
report = Report.new(type)
|
|
||||||
report.start_date = (args['start_date'].present? ? args['start_date'].to_date : 30.days.ago).beginning_of_day
|
|
||||||
report.end_date = (args['end_date'].present? ? args['end_date'].to_date : start_date + 30.days).end_of_day
|
|
||||||
report.category_id = args['category_id'] if args['category_id']
|
|
||||||
report.group_id = args['group_id'] if args['group_id']
|
|
||||||
report.facets = args['facets'].map(&:to_sym) if args['facets']
|
|
||||||
report.limit = args['limit'].to_i if args['limit']
|
|
||||||
report.processing = false
|
|
||||||
report.average = args[:average] || false
|
|
||||||
report.percent = args[:percent] || false
|
|
||||||
|
|
||||||
Report.send("report_#{type}", report)
|
|
||||||
json = report.as_json
|
|
||||||
|
|
||||||
Discourse.cache.write(Report.cache_key(report), json, force: true, expires_in: 30.minutes)
|
|
||||||
|
|
||||||
MessageBus.publish("/admin/reports/#{type}", json, user_ids: User.staff.pluck(:id))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -71,7 +71,7 @@ class Report
|
|||||||
singleton_class.instance_eval { define_method("report_#{name}", &block) }
|
singleton_class.instance_eval { define_method("report_#{name}", &block) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find(type, opts = nil)
|
def self._get(type, opts = nil)
|
||||||
opts ||= {}
|
opts ||= {}
|
||||||
|
|
||||||
# Load the report
|
# Load the report
|
||||||
@ -80,26 +80,30 @@ class Report
|
|||||||
report.end_date = opts[:end_date] if opts[:end_date]
|
report.end_date = opts[:end_date] if opts[:end_date]
|
||||||
report.category_id = opts[:category_id] if opts[:category_id]
|
report.category_id = opts[:category_id] if opts[:category_id]
|
||||||
report.group_id = opts[:group_id] if opts[:group_id]
|
report.group_id = opts[:group_id] if opts[:group_id]
|
||||||
report.async = opts[:async] || false
|
|
||||||
report.facets = opts[:facets] || [:total, :prev30Days]
|
report.facets = opts[:facets] || [:total, :prev30Days]
|
||||||
report.limit = opts[:limit] if opts[:limit]
|
report.limit = opts[:limit] if opts[:limit]
|
||||||
report.processing = false
|
report.processing = false
|
||||||
report.average = opts[:average] || false
|
report.average = opts[:average] || false
|
||||||
report.percent = opts[:percent] || false
|
report.percent = opts[:percent] || false
|
||||||
|
|
||||||
|
report
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.find_cached(type, opts = nil)
|
||||||
|
report = _get(type, opts)
|
||||||
|
Discourse.cache.read(cache_key(report))
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.cache(report, duration)
|
||||||
|
Discourse.cache.write(Report.cache_key(report), report.as_json, force: true, expires_in: duration)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.find(type, opts = nil)
|
||||||
|
report = _get(type, opts)
|
||||||
report_method = :"report_#{type}"
|
report_method = :"report_#{type}"
|
||||||
|
|
||||||
if respond_to?(report_method)
|
if respond_to?(report_method)
|
||||||
cached_report = Discourse.cache.read(cache_key(report))
|
send(report_method, report)
|
||||||
if report.async
|
|
||||||
if cached_report
|
|
||||||
return cached_report
|
|
||||||
else
|
|
||||||
Jobs.enqueue(:retrieve_report, opts.merge(report_type: type))
|
|
||||||
report.processing = true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
send(report_method, report)
|
|
||||||
end
|
|
||||||
elsif type =~ /_reqs$/
|
elsif type =~ /_reqs$/
|
||||||
req_report(report, type.split(/_reqs$/)[0].to_sym)
|
req_report(report, type.split(/_reqs$/)[0].to_sym)
|
||||||
else
|
else
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
require_dependency 'jobs/base'
|
|
||||||
|
|
||||||
describe Jobs::RetrieveReport do
|
|
||||||
it "correctly included full day in report" do
|
|
||||||
freeze_time '2016-02-03 10:00'.to_date
|
|
||||||
|
|
||||||
_topic = Fabricate(:topic)
|
|
||||||
|
|
||||||
args = {
|
|
||||||
report_type: :topics,
|
|
||||||
start_date: Time.now.beginning_of_day,
|
|
||||||
end_date: Time.now.end_of_day,
|
|
||||||
facets: [:total]
|
|
||||||
}.to_json
|
|
||||||
|
|
||||||
messages = MessageBus.track_publish("/admin/reports/topics") do
|
|
||||||
Jobs::RetrieveReport.new.execute(JSON.parse(args))
|
|
||||||
end
|
|
||||||
|
|
||||||
data = messages.first.data[:data]
|
|
||||||
expect(data).to eq([y: 1, x: Time.now.to_date])
|
|
||||||
end
|
|
||||||
end
|
|
Reference in New Issue
Block a user