mirror of
https://github.com/Fishwaldo/huginn.git
synced 2025-03-15 19:31:26 +00:00
Merge pull request #1404 from omniscopeio/onboarding
Sets the new user up with an example set of agents
This commit is contained in:
commit
15622e2c7e
15 changed files with 424 additions and 90 deletions
|
@ -81,6 +81,13 @@ UNLOCK_AFTER=1.hour
|
|||
# Duration for which the user will be remembered without asking for credentials again.
|
||||
REMEMBER_FOR=4.weeks
|
||||
|
||||
# Set to 'true' if you would prefer new users to start with a default set of agents
|
||||
IMPORT_DEFAULT_SCENARIO_FOR_ALL_USERS=true
|
||||
|
||||
# Users can be given a default set of agents to get them started
|
||||
# You can override this scenario with your own scenario via file path or URL
|
||||
# DEFAULT_SCENARIO_FILE=path-or-url-to-scenario.json
|
||||
|
||||
#############################
|
||||
# Email Configuration #
|
||||
#############################
|
||||
|
|
|
@ -26,6 +26,7 @@ class Admin::UsersController < ApplicationController
|
|||
|
||||
respond_to do |format|
|
||||
if @user.save
|
||||
DefaultScenarioImporter.import(@user)
|
||||
format.html { redirect_to admin_users_path, notice: "User '#{@user.username}' was successfully created." }
|
||||
format.json { render json: @user, status: :ok, location: admin_users_path(@user) }
|
||||
else
|
||||
|
|
11
app/controllers/users/registrations_controller.rb
Normal file
11
app/controllers/users/registrations_controller.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
module Users
|
||||
class RegistrationsController < Devise::RegistrationsController
|
||||
after_action :create_default_scenario, only: :create
|
||||
|
||||
private
|
||||
|
||||
def create_default_scenario
|
||||
DefaultScenarioImporter.import(@user) if @user.persisted?
|
||||
end
|
||||
end
|
||||
end
|
20
app/importers/default_scenario_importer.rb
Normal file
20
app/importers/default_scenario_importer.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
require 'open-uri'
|
||||
class DefaultScenarioImporter
|
||||
def self.import(user)
|
||||
return unless ENV['IMPORT_DEFAULT_SCENARIO_FOR_ALL_USERS'] == 'true'
|
||||
seed(user)
|
||||
end
|
||||
|
||||
def self.seed(user)
|
||||
scenario_import = ScenarioImport.new()
|
||||
scenario_import.set_user(user)
|
||||
scenario_file = ENV['DEFAULT_SCENARIO_FILE'].presence || File.join(Rails.root, "data", "default_scenario.json")
|
||||
begin
|
||||
scenario_import.file = open(scenario_file)
|
||||
raise "Import failed" unless scenario_import.valid? && scenario_import.import
|
||||
ensure
|
||||
scenario_import.file.close
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
|
@ -82,7 +82,10 @@ Huginn::Application.routes.draw do
|
|||
post "/users/:user_id/update_location/:secret" => "web_requests#update_location" # legacy
|
||||
|
||||
devise_for :users,
|
||||
controllers: { omniauth_callbacks: 'omniauth_callbacks' },
|
||||
controllers: {
|
||||
omniauth_callbacks: 'omniauth_callbacks',
|
||||
registrations: 'users/registrations'
|
||||
},
|
||||
sign_out_via: [:post, :delete]
|
||||
|
||||
if Rails.env.development?
|
||||
|
|
162
data/default_scenario.json
Normal file
162
data/default_scenario.json
Normal file
|
@ -0,0 +1,162 @@
|
|||
{
|
||||
"schema_version": 1,
|
||||
"name": "default-scenario",
|
||||
"description": "This scenario has a few agents to get you started. Feel free to change them or delete them as you see fit!",
|
||||
"source_url": false,
|
||||
"guid": "ee4299225e6531c401a8bbbce0771ce4",
|
||||
"tag_fg_color": "#ffffff",
|
||||
"tag_bg_color": "#5bc0de",
|
||||
"exported_at": "2016-04-03T18:24:42Z",
|
||||
"agents": [
|
||||
{
|
||||
"type": "Agents::TriggerAgent",
|
||||
"name": "Rain Notifier",
|
||||
"disabled": false,
|
||||
"guid": "361ee2e955d4726b52c8b044d4f75e25",
|
||||
"options": {
|
||||
"expected_receive_period_in_days": "2",
|
||||
"rules": [
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "rain|storm",
|
||||
"path": "conditions"
|
||||
}
|
||||
],
|
||||
"message": "Just so you know, it looks like '{{conditions}}' tomorrow in {{location}}"
|
||||
},
|
||||
"keep_events_for": 0,
|
||||
"propagate_immediately": false
|
||||
},
|
||||
{
|
||||
"type": "Agents::WebsiteAgent",
|
||||
"name": "XKCD Source",
|
||||
"disabled": false,
|
||||
"guid": "505c9bba65507c40e5786afff36f688c",
|
||||
"options": {
|
||||
"url": "http://xkcd.com",
|
||||
"mode": "on_change",
|
||||
"expected_update_period_in_days": 5,
|
||||
"extract": {
|
||||
"url": {
|
||||
"css": "#comic img",
|
||||
"value": "@src"
|
||||
},
|
||||
"title": {
|
||||
"css": "#comic img",
|
||||
"value": "@alt"
|
||||
},
|
||||
"hovertext": {
|
||||
"css": "#comic img",
|
||||
"value": "@title"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schedule": "every_1d",
|
||||
"keep_events_for": 0,
|
||||
"propagate_immediately": false
|
||||
},
|
||||
{
|
||||
"type": "Agents::EmailDigestAgent",
|
||||
"name": "Afternoon Digest",
|
||||
"disabled": false,
|
||||
"guid": "65e8ae4533881537de3c346b5178b75d",
|
||||
"options": {
|
||||
"subject": "Your Afternoon Digest",
|
||||
"expected_receive_period_in_days": "7"
|
||||
},
|
||||
"schedule": "5pm",
|
||||
"propagate_immediately": false
|
||||
},
|
||||
{
|
||||
"type": "Agents::EmailDigestAgent",
|
||||
"name": "Morning Digest",
|
||||
"disabled": false,
|
||||
"guid": "b34eaee75d8dc67843c3bd257c213852",
|
||||
"options": {
|
||||
"subject": "Your Morning Digest",
|
||||
"expected_receive_period_in_days": "30"
|
||||
},
|
||||
"schedule": "6am",
|
||||
"propagate_immediately": false
|
||||
},
|
||||
{
|
||||
"type": "Agents::WeatherAgent",
|
||||
"name": "SF Weather Agent",
|
||||
"disabled": false,
|
||||
"guid": "bdae6dfdf9d01a123ddd513e695fd466",
|
||||
"options": {
|
||||
"location": "94103",
|
||||
"api_key": "put-your-key-here"
|
||||
},
|
||||
"schedule": "10pm",
|
||||
"keep_events_for": 0
|
||||
},
|
||||
{
|
||||
"type": "Agents::WebsiteAgent",
|
||||
"name": "iTunes Trailer Source",
|
||||
"disabled": false,
|
||||
"guid": "e9afa65457d0a736b9ec20a8dd452fc8",
|
||||
"options": {
|
||||
"url": "http://trailers.apple.com/trailers/home/rss/newtrailers.rss",
|
||||
"mode": "on_change",
|
||||
"type": "xml",
|
||||
"expected_update_period_in_days": 5,
|
||||
"extract": {
|
||||
"title": {
|
||||
"css": "item title",
|
||||
"value": ".//text()"
|
||||
},
|
||||
"url": {
|
||||
"css": "item link",
|
||||
"value": ".//text()"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schedule": "every_1d",
|
||||
"keep_events_for": 0,
|
||||
"propagate_immediately": false
|
||||
},
|
||||
{
|
||||
"type": "Agents::EventFormattingAgent",
|
||||
"name": "Comic Formatter",
|
||||
"disabled": false,
|
||||
"guid": "d86b069650edadfc61db9df767c8b65c",
|
||||
"options": {
|
||||
"instructions": {
|
||||
"message": "<h2>{{title}}</h2><img src=\"{{url}}\"/> <p>{{hovertext}}</p>"
|
||||
},
|
||||
"matchers": [
|
||||
|
||||
],
|
||||
"mode": "clean"
|
||||
},
|
||||
"keep_events_for": 2592000,
|
||||
"propagate_immediately": false
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": 0,
|
||||
"receiver": 3
|
||||
},
|
||||
{
|
||||
"source": 1,
|
||||
"receiver": 6
|
||||
},
|
||||
{
|
||||
"source": 4,
|
||||
"receiver": 0
|
||||
},
|
||||
{
|
||||
"source": 5,
|
||||
"receiver": 2
|
||||
},
|
||||
{
|
||||
"source": 6,
|
||||
"receiver": 2
|
||||
}
|
||||
],
|
||||
"control_links": [
|
||||
|
||||
]
|
||||
}
|
91
db/seeds.rb
91
db/seeds.rb
|
@ -1,93 +1,6 @@
|
|||
# This file should contain all the record creation needed to seed the database with its default values.
|
||||
# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
|
||||
|
||||
user = User.find_or_initialize_by(:email => ENV['SEED_EMAIL'] || "admin@example.com")
|
||||
require_relative 'seeds/seeder'
|
||||
|
||||
if user.persisted?
|
||||
puts "User with email '#{user.email}' already exists, not seeding."
|
||||
exit
|
||||
end
|
||||
|
||||
user.username = ENV['SEED_USERNAME'] || "admin"
|
||||
user.password = ENV['SEED_PASSWORD'] || "password"
|
||||
user.password_confirmation = ENV['SEED_PASSWORD'] || "password"
|
||||
user.invitation_code = User::INVITATION_CODES.first
|
||||
user.admin = true
|
||||
user.save!
|
||||
|
||||
puts
|
||||
puts
|
||||
|
||||
unless user.agents.where(:name => "SF Weather Agent").exists?
|
||||
Agent.build_for_type("Agents::WeatherAgent", user,
|
||||
:name => "SF Weather Agent",
|
||||
:schedule => "10pm",
|
||||
:options => { 'location' => "94103", 'api_key' => "put-your-key-here" }).save!
|
||||
|
||||
puts "NOTE: The example 'SF Weather Agent' will not work until you edit it and put in a free API key from http://www.wunderground.com/weather/api/"
|
||||
end
|
||||
|
||||
unless user.agents.where(:name => "XKCD Source").exists?
|
||||
Agent.build_for_type("Agents::WebsiteAgent", user,
|
||||
:name => "XKCD Source",
|
||||
:schedule => "every_1d",
|
||||
:type => "html",
|
||||
:options => {
|
||||
'url' => "http://xkcd.com",
|
||||
'mode' => "on_change",
|
||||
'expected_update_period_in_days' => 5,
|
||||
'extract' => {
|
||||
'url' => { 'css' => "#comic img", 'value' => "@src" },
|
||||
'title' => { 'css' => "#comic img", 'value' => "@alt" },
|
||||
'hovertext' => { 'css' => "#comic img", 'value' => "@title" }
|
||||
}
|
||||
}).save!
|
||||
end
|
||||
|
||||
unless user.agents.where(:name => "iTunes Trailer Source").exists?
|
||||
Agent.build_for_type("Agents::WebsiteAgent", user, :name => "iTunes Trailer Source",
|
||||
:schedule => "every_1d",
|
||||
:options => {
|
||||
'url' => "http://trailers.apple.com/trailers/home/rss/newtrailers.rss",
|
||||
'mode' => "on_change",
|
||||
'type' => "xml",
|
||||
'expected_update_period_in_days' => 5,
|
||||
'extract' => {
|
||||
'title' => { 'css' => "item title", 'value' => ".//text()"},
|
||||
'url' => { 'css' => "item link", 'value' => ".//text()"}
|
||||
}
|
||||
}).save!
|
||||
end
|
||||
|
||||
unless user.agents.where(:name => "Rain Notifier").exists?
|
||||
Agent.build_for_type("Agents::TriggerAgent", user,
|
||||
:name => "Rain Notifier",
|
||||
:source_ids => user.agents.where(:name => "SF Weather Agent").pluck(:id),
|
||||
:options => {
|
||||
'expected_receive_period_in_days' => "2",
|
||||
'rules' => [{
|
||||
'type' => "regex",
|
||||
'value' => "rain|storm",
|
||||
'path' => "conditions"
|
||||
}],
|
||||
'message' => "Just so you know, it looks like '{{conditions}}' tomorrow in {{location}}"
|
||||
}).save!
|
||||
end
|
||||
|
||||
unless user.agents.where(:name => "Morning Digest").exists?
|
||||
Agent.build_for_type("Agents::EmailDigestAgent", user,
|
||||
:name => "Morning Digest",
|
||||
:schedule => "6am",
|
||||
:options => { 'subject' => "Your Morning Digest", 'expected_receive_period_in_days' => "30" },
|
||||
:source_ids => user.agents.where(:name => "Rain Notifier").pluck(:id)).save!
|
||||
end
|
||||
|
||||
unless user.agents.where(:name => "Afternoon Digest").exists?
|
||||
Agent.build_for_type("Agents::EmailDigestAgent", user,
|
||||
:name => "Afternoon Digest",
|
||||
:schedule => "5pm",
|
||||
:options => { 'subject' => "Your Afternoon Digest", 'expected_receive_period_in_days' => "7" },
|
||||
:source_ids => user.agents.where(:name => ["iTunes Trailer Source", "XKCD Source"]).pluck(:id)).save!
|
||||
end
|
||||
|
||||
puts "See the Huginn Wiki for more Agent examples! https://github.com/cantino/huginn/wiki"
|
||||
Seeder.seed
|
||||
|
|
23
db/seeds/seeder.rb
Normal file
23
db/seeds/seeder.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
class Seeder
|
||||
def self.seed
|
||||
user = User.find_or_initialize_by(:email => ENV['SEED_EMAIL'] || "admin@example.com")
|
||||
if user.persisted?
|
||||
puts "User with email '#{user.email}' already exists, not seeding."
|
||||
exit
|
||||
end
|
||||
|
||||
user.username = ENV['SEED_USERNAME'] || "admin"
|
||||
user.password = ENV['SEED_PASSWORD'] || "password"
|
||||
user.password_confirmation = ENV['SEED_PASSWORD'] || "password"
|
||||
user.invitation_code = User::INVITATION_CODES.first
|
||||
user.admin = true
|
||||
user.save!
|
||||
|
||||
if DefaultScenarioImporter.seed(user)
|
||||
puts "NOTE: The example 'SF Weather Agent' will not work until you edit it and put in a free API key from http://www.wunderground.com/weather/api/"
|
||||
puts "See the Huginn Wiki for more Agent examples! https://github.com/cantino/huginn/wiki"
|
||||
else
|
||||
raise('Unable to import the default scenario')
|
||||
end
|
||||
end
|
||||
end
|
22
spec/controllers/admin/users_controller_spec.rb
Normal file
22
spec/controllers/admin/users_controller_spec.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe Admin::UsersController do
|
||||
describe 'POST #create' do
|
||||
context 'with valid user params' do
|
||||
it 'imports the default scenario for the new user' do
|
||||
mock(DefaultScenarioImporter).import(is_a(User))
|
||||
sign_in users(:jane)
|
||||
post :create, :user => {username: 'jdoe', email: 'jdoe@example.com',
|
||||
password: 's3cr3t55', password_confirmation: 's3cr3t55', admin: false }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid user params' do
|
||||
it 'does not import the default scenario' do
|
||||
stub(DefaultScenarioImporter).import(is_a(User)) { fail "Should not attempt import" }
|
||||
sign_in users(:jane)
|
||||
post :create, :user => {}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
29
spec/controllers/users/registrations_controller_spec.rb
Normal file
29
spec/controllers/users/registrations_controller_spec.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
require 'rails_helper'
|
||||
|
||||
module Users
|
||||
describe RegistrationsController do
|
||||
include Devise::TestHelpers
|
||||
|
||||
describe "POST create" do
|
||||
context 'with valid params' do
|
||||
it "imports the default scenario for the new user" do
|
||||
mock(DefaultScenarioImporter).import(is_a(User))
|
||||
|
||||
@request.env["devise.mapping"] = Devise.mappings[:user]
|
||||
post :create, :user => {username: 'jdoe', email: 'jdoe@example.com',
|
||||
password: 's3cr3t55', password_confirmation: 's3cr3t55', admin: false, invitation_code: 'try-huginn'}
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid params' do
|
||||
it "does not import the default scenario" do
|
||||
stub(DefaultScenarioImporter).import(is_a(User)) { fail "Should not attempt import" }
|
||||
|
||||
@request.env["devise.mapping"] = Devise.mappings[:user]
|
||||
setup_controller_for_warden
|
||||
post :create, :user => {}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
29
spec/db/seeds/admin_and_default_scenario_spec.rb
Normal file
29
spec/db/seeds/admin_and_default_scenario_spec.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
require 'rails_helper'
|
||||
require_relative '../../../db/seeds/seeder'
|
||||
|
||||
describe Seeder do
|
||||
before do
|
||||
stub_puts_to_prevent_spew_in_spec_output
|
||||
end
|
||||
|
||||
describe '.seed' do
|
||||
it 'imports a default scenario' do
|
||||
expect { Seeder.seed }.to change(Agent, :count).by(7)
|
||||
end
|
||||
|
||||
it 'creates an admin' do
|
||||
expect { Seeder.seed }.to change(User, :count).by(1)
|
||||
expect(User.last).to be_admin
|
||||
end
|
||||
|
||||
it 'can be run multiple times and exit normally' do
|
||||
Seeder.seed
|
||||
expect { Seeder.seed }.to raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
def stub_puts_to_prevent_spew_in_spec_output
|
||||
stub(Seeder).puts(anything)
|
||||
stub(Seeder).puts
|
||||
end
|
||||
end
|
68
spec/fixtures/test_default_scenario.json
vendored
Normal file
68
spec/fixtures/test_default_scenario.json
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"schema_version": 1,
|
||||
"name": "default-scenario",
|
||||
"description": "This scenario has a few agents to get you started. Feel free to change them or delete them as you see fit!",
|
||||
"source_url": false,
|
||||
"guid": "ee4299225e6531c401a8bbbce0771ce4",
|
||||
"tag_fg_color": "#ffffff",
|
||||
"tag_bg_color": "#5bc0de",
|
||||
"exported_at": "2016-04-03T18:24:42Z",
|
||||
"agents": [
|
||||
{
|
||||
"type": "Agents::TriggerAgent",
|
||||
"name": "Rain Notifier",
|
||||
"disabled": false,
|
||||
"guid": "361ee2e955d4726b52c8b044d4f75e25",
|
||||
"options": {
|
||||
"expected_receive_period_in_days": "2",
|
||||
"rules": [
|
||||
{
|
||||
"type": "regex",
|
||||
"value": "rain|storm",
|
||||
"path": "conditions"
|
||||
}
|
||||
],
|
||||
"message": "Just so you know, it looks like '{{conditions}}' tomorrow in {{location}}"
|
||||
},
|
||||
"keep_events_for": 0,
|
||||
"propagate_immediately": false
|
||||
},
|
||||
{
|
||||
"type": "Agents::EmailDigestAgent",
|
||||
"name": "Morning Digest",
|
||||
"disabled": false,
|
||||
"guid": "b34eaee75d8dc67843c3bd257c213852",
|
||||
"options": {
|
||||
"subject": "Your Morning Digest",
|
||||
"expected_receive_period_in_days": "30"
|
||||
},
|
||||
"schedule": "6am",
|
||||
"propagate_immediately": false
|
||||
},
|
||||
{
|
||||
"type": "Agents::WeatherAgent",
|
||||
"name": "SF Weather Agent",
|
||||
"disabled": false,
|
||||
"guid": "bdae6dfdf9d01a123ddd513e695fd466",
|
||||
"options": {
|
||||
"location": "94103",
|
||||
"api_key": "put-your-key-here"
|
||||
},
|
||||
"schedule": "10pm",
|
||||
"keep_events_for": 0
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": 2,
|
||||
"receiver": 0
|
||||
},
|
||||
{
|
||||
"source": 0,
|
||||
"receiver": 1
|
||||
}
|
||||
],
|
||||
"control_links": [
|
||||
|
||||
]
|
||||
}
|
46
spec/importers/default_scenario_importer_spec.rb
Normal file
46
spec/importers/default_scenario_importer_spec.rb
Normal file
|
@ -0,0 +1,46 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe DefaultScenarioImporter do
|
||||
let(:user) { users(:bob) }
|
||||
describe '.import' do
|
||||
it 'imports a set of agents to get the user going when they are first created' do
|
||||
mock(DefaultScenarioImporter).seed(is_a(User))
|
||||
stub.proxy(ENV).[](anything)
|
||||
stub(ENV).[]('IMPORT_DEFAULT_SCENARIO_FOR_ALL_USERS') { 'true' }
|
||||
DefaultScenarioImporter.import(user)
|
||||
end
|
||||
|
||||
it 'can be turned off' do
|
||||
stub(DefaultScenarioImporter).seed { fail "seed should not have been called"}
|
||||
stub.proxy(ENV).[](anything)
|
||||
stub(ENV).[]('IMPORT_DEFAULT_SCENARIO_FOR_ALL_USERS') { 'false' }
|
||||
DefaultScenarioImporter.import(user)
|
||||
end
|
||||
|
||||
it 'is turned off for existing instances of Huginn' do
|
||||
stub(DefaultScenarioImporter).seed { fail "seed should not have been called"}
|
||||
stub.proxy(ENV).[](anything)
|
||||
stub(ENV).[]('IMPORT_DEFAULT_SCENARIO_FOR_ALL_USERS') { nil }
|
||||
DefaultScenarioImporter.import(user)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe '.seed' do
|
||||
it 'imports a set of agents to get the user going when they are first created' do
|
||||
expect { DefaultScenarioImporter.seed(user) }.to change(user.agents, :count).by(7)
|
||||
end
|
||||
|
||||
it 'respects an environment variable that specifies a path or URL to a different scenario' do
|
||||
stub.proxy(ENV).[](anything)
|
||||
stub(ENV).[]('DEFAULT_SCENARIO_FILE') { File.join(Rails.root, "spec", "fixtures", "test_default_scenario.json") }
|
||||
expect { DefaultScenarioImporter.seed(user) }.to change(user.agents, :count).by(3)
|
||||
end
|
||||
|
||||
it 'can not be turned off' do
|
||||
stub.proxy(ENV).[](anything)
|
||||
stub(ENV).[]('IMPORT_DEFAULT_SCENARIO_FOR_ALL_USERS') { 'true' }
|
||||
expect { DefaultScenarioImporter.seed(user) }.to change(user.agents, :count).by(7)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue