Skip to content

Commit e27bb40

Browse files
committed
Add ReloadableController and modify one page to use it
1 parent bfadc31 commit e27bb40

6 files changed

Lines changed: 120 additions & 26 deletions

File tree

app/controllers/application_controller/wait_for_task.rb

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,38 @@ def wait_for_task
1515

1616
task = MiqTask.find(params[:task_id].to_i)
1717
if task.state != "Finished" # Task not done --> retry
18-
browser_refresh_task(params[:task_id], async_interval)
18+
browser_refresh_task(params[:task_id], async_interval, :response_type => params[:response_type])
1919
else # Task done
2020
async_params = task.context_data[:async_params]
2121
async_params.each { |k, v| @_params[k] = v } # Merge in the original params and
2222
send(async_params[:action]) # call the orig. method
2323
end
2424
end
2525

26-
def browser_refresh_task(task_id, async_interval, should_flash: false)
26+
def browser_refresh_task(task_id, async_interval, should_flash: false, response_type: nil)
27+
response_type ||= :script
28+
2729
async_interval = 1000 if async_interval.to_i < 1000 # if it is not an integer, assign to 1 second
2830
async_interval += 250 if async_interval.to_i < 5000 # Slowly move up to 5 second retries
29-
render :update do |page|
30-
page << javascript_prologue
31-
ajax_call = remote_function(:url => {:action => 'wait_for_task', :task_id => task_id, :async_interval => async_interval})
32-
page << "setTimeout(\"#{ajax_call}\", #{async_interval});"
33-
page.replace("flash_msg_div", :partial => "layouts/flash_msg") if should_flash
34-
page << "miqScrollTop();" if @flash_array.present?
31+
32+
case response_type.to_sym
33+
when :script
34+
render :update do |page|
35+
page << javascript_prologue
36+
ajax_call = remote_function(:url => {:action => 'wait_for_task', :task_id => task_id, :async_interval => async_interval})
37+
page << "setTimeout(\"#{ajax_call}\", #{async_interval});"
38+
page.replace("flash_msg_div", :partial => "layouts/flash_msg") if should_flash
39+
page << "miqScrollTop();" if @flash_array.present?
40+
end
41+
when :json
42+
render :status => 202, :json => {
43+
:url => url_for(:action => 'wait_for_task', :task_id => task_id, :async_interval => async_interval, :response_type => :json),
44+
:async_interval => async_interval,
45+
:task_id => task_id,
46+
:should_flash => should_flash
47+
}
48+
else
49+
raise "Invalid response type: #{response_type}"
3550
end
3651
end
3752
private :browser_refresh_task
@@ -65,7 +80,7 @@ def initiate_wait_for_task(options = {})
6580
task.context_data = (task.context_data || {}).merge(:async_params => async_params)
6681
task.save!
6782

68-
browser_refresh_task(task_id, async_interval, :should_flash => !!options[:flash])
83+
browser_refresh_task(task_id, async_interval, :should_flash => !!options[:flash], :response_type => options[:response_type])
6984
end
7085
private :initiate_wait_for_task
7186

app/controllers/report_controller/reports.rb

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ def miq_report_run
2222

2323
def show_preview
2424
assert_privileges(session.fetch_path(:edit, :rpt_id) ? "miq_report_edit" : "miq_report_new")
25-
2625
unless params[:task_id] # First time thru, kick off the report generate task
2726
@rpt = create_report_object # Build a report object from the latest edit fields
2827
initiate_wait_for_task(:task_id => @rpt.async_generate_table(:userid => session[:userid],
2928
:session_id => request.session_options[:id],
3029
:limit => 50,
31-
:mode => "adhoc"))
30+
:mode => "adhoc"),
31+
:response_type => :json)
3232
return
3333
end
3434
miq_task = MiqTask.find(params[:task_id]) # Not first time, read the task record
@@ -49,11 +49,7 @@ def show_preview
4949
end
5050
end
5151
miq_task.destroy
52-
render :update do |page|
53-
page << javascript_prologue
54-
page.replace_html("form_preview", :partial => "form_preview")
55-
page << "miqSparkle(false);"
56-
end
52+
render :partial => "form_preview", :layout => false
5753
end
5854

5955
def miq_report_delete

app/javascript/controllers/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@
33
// ./bin/rails generate stimulus controllerName
44

55
import { application } from "./application"
6+
7+
import ReloadableController from "./reloadable_controller"
8+
application.register("reloadable", ReloadableController)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { Controller } from "@hotwired/stimulus"
2+
import { miqFetch } from '../http_api/fetch'
3+
4+
export default class extends Controller {
5+
static targets = ["content"]
6+
static values = {url: String}
7+
8+
async reload() {
9+
try {
10+
window.miqSparkleOn()
11+
12+
// request the new content and get back a task reference
13+
const data = await miqFetch({
14+
url: this.urlValue,
15+
method: 'POST',
16+
cookieAndCsrf: true
17+
})
18+
19+
const url = data.url
20+
if (!url) {
21+
throw new Error(__('No task returned from server'))
22+
}
23+
24+
const taskContent = await this.waitForTask(url)
25+
26+
if (taskContent && this.hasContentTarget) {
27+
// Safe to use innerHTML: Content is server-rendered
28+
this.contentTarget.innerHTML = taskContent
29+
}
30+
} catch (error) {
31+
console.error('Error in reloadable#reload:', error)
32+
if (window.add_flash) {
33+
window.add_flash(sprintf(__('Error loading content: %s'), error.message), 'error')
34+
}
35+
} finally {
36+
window.miqSparkleOff()
37+
}
38+
}
39+
40+
async waitForTask(url) {
41+
const maxAttempts = 100
42+
let asyncInterval = 1000
43+
44+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
45+
this.application.logDebugActivity('reloadable', 'waitForTask', {'attempt': attempt, 'url': url})
46+
await new Promise(resolve => setTimeout(resolve, asyncInterval))
47+
48+
const response = await miqFetch({
49+
url: url,
50+
method: 'POST',
51+
cookieAndCsrf: true,
52+
skipJsonParsing: true,
53+
skipErrors: true
54+
})
55+
56+
if (response.status === 200) {
57+
const content = await response.text()
58+
59+
// HACK: Handle a 200 response that contains an error. We should fix the server to return
60+
// proper error codes, but this code path is used all over the place, so I don't know what
61+
// that will break.
62+
if (content.includes('throw "error"')) {
63+
throw new Error(__('Task failed with server error'))
64+
}
65+
66+
return content
67+
} else if (response.status === 202) {
68+
// 202 Accepted means the task is still running, so try again
69+
const data = await response.json()
70+
71+
url = data.url
72+
if (!url) {
73+
throw new Error(__('Invalid task response: missing URL'))
74+
}
75+
76+
asyncInterval = data.async_interval || asyncInterval
77+
} else {
78+
throw new Error(sprintf(__('Task failed with status: %s'), response.status))
79+
}
80+
}
81+
82+
throw new Error(sprintf(__('Task did not complete after %d attempts'), maxAttempts))
83+
}
84+
}

app/javascript/http_api/api.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { miqFetch } from './fetch';
22

3-
const { miqDeferred } = window;
4-
53
/*
64
* API.get(url, options) - use API.get('/api'), returns a Promise
75
* API.delete - (the same)
@@ -63,7 +61,7 @@ API.ws_init = () => API.get('/api/auth?requester_type=ws').then((response) => {
6361
});
6462

6563
API.wait_for_task = (taskId) => {
66-
const deferred = miqDeferred();
64+
const deferred = window.miqDeferred();
6765

6866
const retry = () => {
6967
API.get(`/api/tasks/${taskId}?attributes=task_results`)

app/views/report/_form_preview.html.haml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#form_preview
1+
#form_preview{"data-controller" => "reloadable", "data-reloadable-url-value" => url_for(:action => "show_preview", :id => "#{@edit[:rpt_id] || 'new'}"), "data-reloadable-target" => "content"}
22
- if @html
33
- unless @edit[:chart_data].nil?
44
%fieldset
@@ -15,10 +15,8 @@
1515
= _('Generate Report Preview')
1616
- t = _("Generate Report preview")
1717
&nbsp;
18-
= link_to({:action => "show_preview", :id => "#{@edit[:rpt_id] || 'new'}"},
19-
:alt => t,
20-
"data-miq_sparkle_on" => true,
21-
:remote => true,
22-
"data-method" => :post,
23-
:title => t) do
18+
= link_to("#",
19+
:alt => t,
20+
:title => t,
21+
"data-action" => "click->reloadable#reload") do
2422
%i.fa.fa-refresh

0 commit comments

Comments
 (0)