Commit ae262d0c authored by Samir Noir's avatar Samir Noir 🧀
Browse files

Remove notifications: controller, model and test (bug 11928)

parent 170482e7
# Copyright (c) 2009-2011 Cyril Rohr, INRIA Rennes - Bretagne Atlantique
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# notifications_controller.rb
# g5k-api
#
# Created by Cyril Rohr on 2011-10-10.
class NotificationsController < ApplicationController
# Placeholder to be Restfully-compliant.
# Always returns an empty list.
def index
allow :get, :post;
result = {
:items => [],
:total => 0,
:links => [
{
:rel => "parent",
:href => uri_to(root_path)
},
{
:rel => "self",
:href => uri_to(notifications_path)
}
]
}
respond_to do |format|
format.g5kcollectionjson { render :json => result }
format.json { render :json => result }
end
end
# deliver a notification
def create
@notification = Notification.new(params)
if @notification.valid?
EM.add_timer(0) {
Fiber.new{ @notification.deliver }.resume
}
head :accepted
else
render :status => :bad_request,
:plain => "Your notification is invalid: #{@notification.errors.join("; ")}."
end
end
end
# Copyright (c) 2009-2011 Cyril Rohr, INRIA Rennes - Bretagne Atlantique
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module Grid5000
# Abstracts the way to send notifications by forwarding every notification to
# the notifications API. Even if this notifications API is located in the same
# app, it helps decoupling, albeit making the process a bit less efficient.
# So, use this class from any model/lib/controller other than one related to
# the notifications API.
class Notification
class << self
attr_accessor :uri
end
attr_accessor :recipients, :message
def initialize(message, options = {})
@recipients = options[:to] || []
@message = message
end
def deliver!
http = EM::HttpRequest.new(self.class.uri).post(
:timeout => 5,
:body => self.to_json,
:head => {
'Content-Type' => "application/json",
'Accept' => "*/*",
'X-Api-User-Privileges' => 'server',
'X-Api-User-Cn' => 'g5k-api',
}
)
if http.response_header.status == 202
Rails.logger.info "Successfully sent notification #{self.inspect}"
true
else
Rails.logger.warn "Error when trying to send notification #{self.inspect}: #{http.response_header.status} - #{http.response.inspect}"
false
end
end
def to_json(*args)
JSON.pretty_generate({
:to => recipients,
:body => message
})
end
end
end
# Copyright (c) 2009-2011 Cyril Rohr, INRIA Rennes - Bretagne Atlantique
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# encoding: utf-8
require 'uri'
# This is the class that handles notifications sent to the notifications API.
class Notification
VALID_URI_SCHEMES = %w{http https mailto}
attr_reader :errors
attr_accessor :to
attr_accessor :body
def initialize(params = {})
@to = params[:to] || []
@body = params[:body]
end
def normalize_recipients!
unless @to.all?{|uri| uri.kind_of?(URI) }
@to = @to.reject{|uri| uri.blank?}.map{|uri| URI.parse(uri) rescue nil}.compact
end
self
end
def valid?
@errors = []
if to.blank? || !to.kind_of?(Array)
@errors.push("'to' must be an array of URI")
else
normalize_recipients!
if @to.empty?
@errors.push("'to' must be non-empty")
else
invalid = @to.select{|uri| uri.scheme.nil? || !VALID_URI_SCHEMES.include?(uri.scheme) || ["localhost", "127.0.0.1"].include?(uri.host)}.map(&:to_s)
@errors.push("'to' contains invalid URIs (#{invalid.join(",")})") unless invalid.empty?
end
end
@errors.push("'body' can't be blank") if body.blank?
@errors.empty?
end
def deliver
return false unless valid?
to.each do |uri|
process_uri(uri)
end
end
# Takes a <tt>uri</tt> URI as parameter.
def process_uri(uri)
Timeout.timeout(5) do
case uri.scheme
# HTTP processing
when /http/
headers = { 'Content-Type' => 'text/plain', 'Accept' => '*/*' }
request = Net::Http::Post.new(uri, headers)
request.body = body.to_s
response = http.request(request)
Rails.logger.info "Sent notification, received status=#{response.status}: #{response.inspect}"
# EMAIL processing
when /mailto/
subject = uri.headers.detect{|array| array.first == "subject"}
subject = subject.nil? ? "Grid5000 Notification" : subject.last
body_header = uri.headers.detect{|array| array.first == "body"}
body_header = body_header.nil? ? "" : body_header.last
email = {
:domain => Rails.my_config(:smtp_domain),
:host => Rails.my_config(:smtp_host),
:port => Rails.my_config(:smtp_port),
:starttls => false,
:from => Rails.my_config(:smtp_from),
:to => [uri.to],
:header => {"Subject" => subject},
:body => "#{body_header.to_s}#{body.to_s}"
}
Rails.logger.info "Sending email with following options: #{email.inspect}"
result = EM::Synchrony.sync(EM::Protocols::SmtpClient.send(email))
Rails.logger.info "Sent email. Result=#{result.inspect}"
end
end
rescue Timeout::Error, StandardError => e
Rails.logger.warn "Failed to send notification #{self.inspect} : #{e.class.name} - #{e.message}"
Rails.logger.debug e.backtrace.join(";")
end
end
# Copyright (c) 2009-2011 Cyril Rohr, INRIA Rennes - Bretagne Atlantique
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Grid5000::Notification.uri = Rails.my_config(:notifications_uri)
# Copyright (c) 2009-2011 Cyril Rohr, INRIA Rennes - Bretagne Atlantique
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
describe NotificationsController do
render_views
it "should return an empty list of notifications" do
get :index, :format => :json
expect(response.status).to eq(200)
expect(json).to eq({
"items" => [],
"total" => 0,
"links"=>[
{"rel"=>"parent", "href"=>"/"},
{"rel"=>"self", "href"=>"/notifications"}
]
})
end
end
......@@ -102,10 +102,10 @@ describe Grid5000::Deployment do
it "should work [environment description file]" do
@deployment.environment = "http://server.com/some/file.dsc"
expect(@deployment.to_hash).to be == {
"environment"=>{},
"nodes"=>["paradent-1.rennes.grid5000.fr"],
"environment"=>{},
"nodes"=>["paradent-1.rennes.grid5000.fr"],
"hook"=>true
}
}
end
it "should work [environment associated to a specific user]" do
......@@ -151,7 +151,7 @@ describe Grid5000::Deployment do
"vlan" => "3",
"disable_disk_partitioning" => true,
"disable_bootloader_install" => true,
"force" => true,
"force" => true,
"hook" => true
}
end
......@@ -421,39 +421,4 @@ describe Grid5000::Deployment do
end # describe "with a deployment in the :processing status"
end # describe "calls to kadeploy server"
=end
describe "notification delivery" do
it "should not deliver a notification if notifications is blank" do
@deployment.notifications = nil
expect(Grid5000::Notification).not_to receive(:new)
expect(@deployment.deliver_notification).to be true
end
it "should deliver a notification if notifications is not empty" do
@deployment.notifications = ["mailto:cyril.rohr@inria.fr"]
allow(@deployment).to receive(:notification_message).and_return(msg = "msg")
expect(Grid5000::Notification).to receive(:new).
with(msg, :to => ["mailto:cyril.rohr@inria.fr"]).
and_return(notif = double("notif"))
expect(notif).to receive(:deliver!).and_return(true)
expect(@deployment.deliver_notification).to be true
end
it "should always return true even if the notification delivery failed" do
@deployment.notifications = ["mailto:cyril.rohr@inria.fr"]
allow(@deployment).to receive(:notification_message).and_return(msg = "msg")
expect(Grid5000::Notification).to receive(:new).
with(msg, :to => ["mailto:cyril.rohr@inria.fr"]).
and_return(notif = double("notif"))
expect(notif).to receive(:deliver!).and_raise(Exception.new("message"))
expect(@deployment.deliver_notification).to be true
end
it "build the correct notification message" do
@deployment.notifications = ["mailto:cyril.rohr@inria.fr"]
expect(@deployment.notification_message).to be == JSON.pretty_generate(@deployment.as_json)
end
end
end
# Copyright (c) 2009-2011 Cyril Rohr, INRIA Rennes - Bretagne Atlantique
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
describe Grid5000::Notification do
before do
@body = "some message"
@recipients = ["mailto:cyril.rohr@inria.fr"]
end
it "should have the correct URI" do
expect(Grid5000::Notification.uri).to eq "http://fake.api/sid/notifications"
end
it "should correcty populate the attributes" do
notif = Grid5000::Notification.new(@body, :to => @recipients)
expect(notif.message).to eq @body
expect(notif.recipients).to eq @recipients
end
it "should send the HTTP request to the notifications API and return true if successful" do
stub_request(:post, "http://fake.api/sid/notifications").
with(
:body => "{\n \"to\": [\n \"mailto:cyril.rohr@inria.fr\"\n ],\n \"body\": \"some message\"\n}",
:headers => {
'Accept'=>'*/*',
'Content-Type'=>'application/json',
'X-Api-User-Privileges'=>'server',
'X-Api-User-Cn'=>'g5k-api'
}
).
to_return(:status => 202)
notif = Grid5000::Notification.new(@body, :to => @recipients)
expect(notif.deliver!).to be true
end
it "should send the HTTP request to the notifications API and return false if failed" do
stub_request(:post, "http://fake.api/sid/notifications").
with(
:body => "{\n \"to\": [\n \"mailto:cyril.rohr@inria.fr\"\n ],\n \"body\": \"some message\"\n}",
:headers => {
'Accept'=>'*/*',
'Content-Type'=>'application/json',
'X-Api-User-Privileges'=>'server',
'X-Api-User-Cn'=>'g5k-api'
}
).
to_return(:status => 500)
notif = Grid5000::Notification.new(@body, :to => @recipients)
expect(notif.deliver!).to be false
end
end
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment