Skip to content

Commit c8a9dd7

Browse files
authored
Merge pull request #31 from gtt-project/dkastl/issue27
Enables server-side proxy
2 parents 3f51079 + 81f8350 commit c8a9dd7

File tree

16 files changed

+216
-84
lines changed

16 files changed

+216
-84
lines changed

app/controllers/subscription_templates_controller.rb

Lines changed: 108 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
require 'net/http'
2+
13
class SubscriptionTemplatesController < ApplicationController
24
layout 'base'
35

46
before_action :find_project_by_project_id, except: [:index, :set_subscription_id]
57
before_action :get_issue_statuses, only: [:new, :create, :edit, :update]
68
before_action :get_issue_priorities, only: [:new, :create, :edit, :update]
79
before_action :get_issue_categories, only: [:new, :create, :edit, :update]
10+
before_action :find_subscription_template, only: [:edit, :update, :destroy, :copy, :publish, :unpublish, :update_subscription_id]
11+
before_action :check_fiware_broker_auth_token, only: [:publish, :unpublish]
812

913
accept_api_auth :set_subscription_id
1014
before_action :authorize, except: [:set_subscription_id]
@@ -19,9 +23,7 @@ def new
1923
@subscription_template = SubscriptionTemplate.new
2024
end
2125

22-
def edit
23-
@subscription_template = find_subscription_template
24-
end
26+
def edit; end
2527

2628
def create
2729
r = RedmineGttFiware::SaveSubscriptionTemplate.(subscription_template_params, project: @project)
@@ -34,8 +36,6 @@ def create
3436
end
3537

3638
def update
37-
@subscription_template = find_subscription_template
38-
3939
r = RedmineGttFiware::SaveSubscriptionTemplate.(subscription_template_params, subscription_template: @subscription_template)
4040
if r.subscription_template_saved?
4141
redirect_to index_path
@@ -55,15 +55,13 @@ def update_subscription_id
5555
end
5656

5757
def set_subscription_id
58-
# Check if a valid API key is provided
5958
unless User.current.logged?
6059
render json: { error: 'API key is missing or invalid' }, status: :unauthorized
6160
return
6261
end
6362

6463
@subscription_template = SubscriptionTemplate.find(params[:subscription_template_id])
6564

66-
# Check if the user has permissions to manage subscription templates
6765
unless User.current.allowed_to?(:manage_subscription_templates, @subscription_template.project)
6866
render json: { error: 'You do not have permission to manage subscription templates' }, status: :forbidden
6967
return
@@ -75,7 +73,6 @@ def set_subscription_id
7573
end
7674

7775
def destroy
78-
@subscription_template = find_subscription_template
7976
@subscription_template.destroy
8077
redirect_to index_path
8178
end
@@ -89,36 +86,53 @@ def copy
8986
end
9087

9188
def publish
92-
prepare_payload
93-
94-
respond_to do |format|
95-
format.js # This will render `publish.js.erb`
96-
end
89+
handle_publish_unpublish('publish', l(:subscription_published), 'publish.js.erb')
9790
end
9891

9992
def unpublish
100-
@subscription_template = SubscriptionTemplate.find(params[:id])
10193
@broker_url = URI.join(@subscription_template.broker_url, "/v2/subscriptions/", @subscription_template.subscription_id).to_s
94+
handle_publish_unpublish('unpublish', l(:subscription_unpublished), 'unpublish.js.erb')
95+
end
10296

103-
respond_to do |format|
104-
format.js # This will render `unpublish.js.erb`
97+
private
98+
99+
def handle_publish_unpublish(action, success_message, js_template)
100+
prepare_payload if action == 'publish'
101+
102+
if Setting.plugin_redmine_gtt_fiware['connect_via_proxy']
103+
if handle_fiware_action(action)
104+
render_subscription_templates(success_message)
105+
else
106+
render_subscription_templates(@error_message)
107+
end
108+
else
109+
respond_to do |format|
110+
format.js { render js_template }
111+
end
105112
end
106113
end
107114

108-
private
115+
def render_subscription_templates(message)
116+
@subscription_templates = subscription_template_scope
117+
respond_to do |format|
118+
format.html {
119+
response.headers['X-Redmine-Message'] = message
120+
render partial: 'subscription_templates/subscription_template', collection: @subscription_templates, as: :subscription_template
121+
}
122+
end
123+
end
109124

110125
def prepare_payload
111-
@subscription_template = SubscriptionTemplate.find(params[:id])
112126
@broker_url = URI.join(@subscription_template.broker_url, "/v2/subscriptions").to_s
113127
@entity_url = URI.join(@subscription_template.broker_url, "/v2/entities").to_s
114128
@member = Member.find(@subscription_template.member_id)
115129

116-
httpCustom = {
130+
http_custom = {
117131
url: URI.join(request.base_url, "/fiware/subscription_template/#{@subscription_template.id}/notification").to_s,
118132
headers: {
119-
"Content-Type": "application/json",
120-
"X-Redmine-API-Key": User.find(@member.user_id).api_key,
121-
"X-Redmine-GTT-Subscription-Template-URL": URI.join(request.base_url, "/fiware/subscription_template/#{@subscription_template.id}/registration/").to_s
133+
"Content-Type" => "application/json",
134+
"X-Redmine-API-Key" => User.find(@member.user_id).api_key,
135+
"X-Redmine-GTT-Subscription-Template-URL" => URI.join(request.base_url, "/fiware/subscription_template/#{@subscription_template.id}/registration/").to_s
122136
},
123137
method: "POST",
124138
json: {
@@ -131,10 +145,8 @@ def prepare_payload
131145
}
132146
}
133147

134-
httpCustom[:json][:attachments] = @subscription_template.attachments if @subscription_template.attachments
135-
136148
@json_payload = {
137-
description: CGI::escape(@subscription_template.name),
149+
description: CGI.escape(@subscription_template.name),
138150
subject: {
139151
entities: @subscription_template.entities,
140152
condition: {
@@ -146,7 +158,7 @@ def prepare_payload
146158
metadata: ["dateCreated", "*"],
147159
onlyChangedAttrs: false,
148160
covered: false,
149-
httpCustom: httpCustom
161+
httpCustom: http_custom
150162
},
151163
throttling: Setting.plugin_redmine_gtt_fiware['fiware_broker_subscription_throttling'].to_i || 1,
152164
status: @subscription_template.status
@@ -169,14 +181,7 @@ def prepare_payload
169181
@json_payload[:subject][:condition][:attrs] = JSON.parse(@subscription_template.attrs) if @subscription_template.attrs.present?
170182
@json_payload[:subject][:condition][:alterationTypes] = @subscription_template.alteration_types if @subscription_template.alteration_types.present?
171183

172-
@json_payload = JSON.pretty_generate(@json_payload)
173-
.gsub("\\", "\\\\\\\\") # escape backslashes
174-
.gsub("\r", "\\r") # escape carriage return
175-
.gsub("\n", "\\n") # escape newline
176-
.gsub("\t", "\\t") # escape tab
177-
.gsub("\f", "\\f") # escape form feed
178-
.gsub("\b", "\\b") # escape backspace
179-
.gsub("\"", "\\\"") # escape double quotes
184+
@json_payload = JSON.generate(@json_payload)
180185
end
181186

182187
def new_path
@@ -188,11 +193,11 @@ def index_path
188193
end
189194

190195
def find_subscription_template
191-
subscription_template_scope.find params[:id]
196+
@subscription_template = subscription_template_scope.find(params[:id])
192197
end
193198

194199
def find_project_by_project_id
195-
@project = Project.find params[:project_id]
200+
@project = Project.find(params[:project_id])
196201
end
197202

198203
def subscription_template_scope
@@ -216,4 +221,71 @@ def subscription_template_params
216221
params.require(:subscription_template).permit(:standard, :broker_url, :fiware_service, :fiware_servicepath, :subscription_id, :name, :expires, :status, :context, :entities_string, :attrs, :expression_query, :expression_georel, :expression_geometry, :expression_coords, :notify_on_metadata_change, :subject, :description, :attachments_string, :is_private, :project_id, :tracker_id, :version_id, :issue_status_id, :issue_category_id, :issue_priority_id, :member_id, :comment, :threshold_create, :threshold_create_hours, :notes, :geometry, :geometry_string, alteration_types: [])
217222
end
218223

224+
def check_fiware_broker_auth_token
225+
@fiware_broker_auth_token = request.headers['HTTP_FIWARE_BROKER_AUTH_TOKEN']
226+
end
227+
228+
def handle_fiware_action(action)
229+
230+
if @fiware_broker_auth_token.blank?
231+
Rails.logger.error "FIWARE Broker Auth Token is missing"
232+
@error_message = l(:subscription_unauthorized_error)
233+
return false
234+
end
235+
236+
uri = URI(@broker_url)
237+
http = Net::HTTP.new(uri.host, uri.port)
238+
http.use_ssl = (uri.scheme == 'https')
239+
240+
request = case action
241+
when 'publish'
242+
Net::HTTP::Post.new(uri.path, initheader = {
243+
'Content-Type' => 'application/json',
244+
'Authorization' => "Bearer #{@fiware_broker_auth_token}"
245+
}).tap { |req| req.body = @json_payload }
246+
when 'unpublish'
247+
Net::HTTP::Delete.new(uri.path, initheader = {
248+
'Authorization' => "Bearer #{@fiware_broker_auth_token}"
249+
})
250+
else
251+
Rails.logger.error "Unknown action: #{action}"
252+
@error_message = l(:general_action_error)
253+
return false
254+
end
255+
256+
response = http.request(request)
257+
258+
Rails.logger.info "FIWARE Broker Response Code: #{response.code}"
259+
Rails.logger.info "FIWARE Broker Response Message: #{response.message}"
260+
261+
if response.code.to_i == 201 && action == 'publish'
262+
location_header = response['location'] || response['Location']
263+
if location_header
264+
subscription_id = location_header.split('/').last
265+
@subscription_template.update(subscription_id: subscription_id)
266+
return true
267+
else
268+
Rails.logger.error "Location header is missing in the response"
269+
@error_message = l(:general_action_error)
270+
return false
271+
end
272+
elsif response.code.to_i == 204 && action == 'unpublish'
273+
@subscription_template.update(subscription_id: nil)
274+
return true
275+
end
276+
277+
if response.code.to_i >= 400
278+
Rails.logger.error "FIWARE Broker error: #{response.body}"
279+
@error_message = l(:general_action_error)
280+
false
281+
else
282+
true
283+
end
284+
rescue StandardError => e
285+
Rails.logger.error "Error handling FIWARE action: #{e.message}"
286+
Rails.logger.error e.backtrace.join("\n")
287+
@error_message = l(:general_action_error)
288+
false
289+
end
290+
219291
end

app/views/gtt_fiware/_settings.html.erb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,19 @@
1010
],
1111
@settings['ngsi_ld_format']) %>
1212
</p>
13+
</div>
1314

15+
<div class="box tabular settings">
1416
<h3><%= l(:gtt_fiware_settings_broker) %></h3>
1517

18+
<p>
19+
<%= content_tag(:label, l(:field_connect_via_proxy)) %>
20+
<%= check_box_tag 'settings[connect_via_proxy]', 1, Setting.plugin_redmine_gtt_fiware['connect_via_proxy'] %>
21+
</p>
22+
1623
<p>
1724
<%= content_tag(:label, l(:field_fiware_broker_subscription_throttling)) %>
1825
<%= number_field_tag 'settings[fiware_broker_subscription_throttling]',
1926
@settings['fiware_broker_subscription_throttling'], :min => 0, :step => 1 %>
2027
</p>
21-
2228
</div>

app/views/subscription_templates/_list.html.erb

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
<div class="box tabular">
33
<p class="min-width">
44
<%= content_tag :label, l(:field_subscription_auth_token) %>
5-
<%= text_field_tag :subscription_auth_token, "", :size => 70, :placeholder => l(:field_subscription_auth_token_placeholder) %><br>
6-
<em><%= l(:field_subscription_auth_token_hint) %></em>
5+
<%= text_field_tag :subscription_auth_token, "", :size => 60, :placeholder => l(:field_subscription_auth_token_placeholder) %>
6+
<%= button_tag l(:button_connect_via_proxy_text), type: 'button', id: 'connect_via_proxy_status' %>
7+
<br><small><i><%= l(:field_subscription_auth_token_hint).html_safe %></i></small>
78
</p>
89
</div>
910

@@ -26,9 +27,39 @@
2627
</tbody>
2728
</table>
2829

29-
<div id="temporaryNotification" class="temporaryNotification">
30-
</div>
30+
<% if Setting.plugin_redmine_gtt_fiware['connect_via_proxy'] %>
31+
<script>
32+
document.addEventListener("DOMContentLoaded", function() {
33+
if (Rails) {
34+
document.addEventListener('ajax:beforeSend', function(event) {
35+
var xhr = event.detail[0];
36+
xhr.setRequestHeader('FIWARE-Broker-Auth-Token', document.getElementById('subscription_auth_token').value);
37+
});
38+
39+
document.addEventListener('ajax:success', function(event) {
40+
var detail = event.detail;
41+
var data = detail[0], status = detail[1], xhr = detail[2];
42+
43+
// Update the subscription templates list with the response data
44+
document.getElementById('subscriptionTemplateList').innerHTML = xhr.responseText;
45+
showNotification(xhr.getResponseHeader('X-Redmine-Message'));
46+
});
47+
48+
document.addEventListener('ajax:error', function(event) {
49+
var detail = event.detail;
50+
var data = detail[0], status = detail[1], xhr = detail[2];
51+
52+
console.error("Error during publish:", data);
53+
showNotification(xhr.getResponseHeader('X-Redmine-Message'));
54+
});
55+
}
56+
57+
document.getElementById('connect_via_proxy_status').style.backgroundColor = '#d3ffc6';
58+
});
59+
</script>
60+
<% end %>
3161

62+
<div id="temporaryNotification" class="temporaryNotification"></div>
3263
<% else %>
3364
<p class="nodata"><%= l :label_no_data %></p>
3465
<% end %>
Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,36 @@
11
<tr>
2-
<td class="text"><%= link_to subscription_template.name, edit_project_subscription_template_path(subscription_template.project, subscription_template) %></td>
3-
<td><%= textilizable subscription_template.standard %></td>
4-
<td class="text"><code><%= subscription_template.broker_url %></code></td>
5-
<td><%= textilizable subscription_template.issue_status.name %></td>
6-
<td><%= textilizable subscription_template.tracker.name %></td>
7-
<td><%= textilizable subscription_template.status %></td>
8-
<td><%= link_to l(:link_to_copy), copy_project_subscription_template_path(@project, subscription_template), remote: true, method: :get, class: 'copy-command-link icon icon-copy', title: l(:link_to_copy_hint) %></td>
2+
<td class="text">
3+
<%= link_to subscription_template.name, edit_project_subscription_template_path(subscription_template.project, subscription_template) %>
4+
</td>
5+
<td>
6+
<%= textilizable subscription_template.standard %>
7+
</td>
8+
<td class="text">
9+
<code><%= subscription_template.broker_url %></code>
10+
</td>
11+
<td>
12+
<%= textilizable subscription_template.issue_status.name %>
13+
</td>
14+
<td>
15+
<%= textilizable subscription_template.tracker.name %>
16+
</td>
17+
<td>
18+
<%= textilizable subscription_template.status %>
19+
</td>
20+
<td>
21+
<%= link_to l(:link_to_copy), copy_project_subscription_template_path(@project, subscription_template),
22+
method: :get, remote: true,class: 'copy-command-link icon icon-copy', title: l(:link_to_copy_hint) %>
23+
</td>
924
<td>
1025
<% if subscription_template.subscription_id.present? %>
11-
<%= link_to l(:link_to_unpublish), unpublish_project_subscription_template_path(@project, subscription_template), method: :get, remote: true, class: 'icon icon-shared' %>
26+
<%= link_to l(:link_to_unpublish), unpublish_project_subscription_template_path(@project, subscription_template),
27+
method: :get, remote: true, class: 'icon icon-shared' %>
1228
<% else %>
13-
<%= link_to l(:link_to_publish), publish_project_subscription_template_path(@project, subscription_template), method: :get, remote: true, class: 'icon icon-shared' %>
29+
<%= link_to l(:link_to_publish), publish_project_subscription_template_path(@project, subscription_template),
30+
method: :get, remote: true, class: 'icon icon-shared' %>
1431
<% end %>
1532
</td>
16-
<td><%= delete_link(subscription_template.project ? project_subscription_template_path(subscription_template.project, subscription_template) : subscription_template_path(subscription_template)) %>
33+
<td>
34+
<%= delete_link(subscription_template.project ? project_subscription_template_path(subscription_template.project, subscription_template) : subscription_template_path(subscription_template)) %>
35+
</td>
1736
</tr>

app/views/subscription_templates/publish.js.erb

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,6 @@ var requestOptions = {
2323
redirect: 'follow'
2424
};
2525

26-
function showNotification(message) {
27-
// Get the notification box
28-
var notification = document.getElementById('temporaryNotification');
29-
30-
// Change the text of the notification box
31-
notification.textContent = message;
32-
33-
// Show the notification box
34-
notification.classList.add('visible');
35-
36-
// Hide the notification box after 3 seconds
37-
setTimeout(function() {
38-
notification.classList.remove('visible');
39-
}, 3000);
40-
}
41-
4226
// Send the POST request
4327
fetch('<%= @broker_url %>', requestOptions)
4428
.then(response => {

0 commit comments

Comments
 (0)