Commit 66c231f1 by John Doe

added endpoint to remove nilms

parent 697bd4ec
......@@ -21,7 +21,12 @@ class DbAdapter
rescue
return nil
end
# if the site exists but is not a nilm...
required_keys = %w(size other free reserved)
unless info.respond_to?(:has_key?) &&
required_keys.all? { |s| info.key? s }
return nil
end
{
version: version,
size_db: info['size'],
......@@ -38,7 +43,10 @@ class DbAdapter
rescue
return nil
end
#if the url exists but is not a nilm...
unless resp.parsed_response.respond_to?(:map)
return nil
end
resp.parsed_response.map do |entry|
metadata = if entry[0].match(UpdateStream.decimation_tag).nil?
__get_metadata(entry[0])
......@@ -85,7 +93,7 @@ class DbAdapter
return { error: true, msg: 'cannot contact NilmDB server' }
end
unless response.success?
Rails.logger.warn("#{@url}: update_metadata(#{path})"+
Rails.logger.warn("#{@url}: update_metadata(#{path})"\
" => #{response.code}:#{response.body}")
return { error: true, msg: "error updating #{path} metadata" }
end
......
......@@ -13,16 +13,9 @@ class DbsController < ApplicationController
# PATCH/PUT /dbs/1.json
def update
@service = StubService.new
prev_url = @db.url
if @db.update_attributes(db_params)
if prev_url != @db.url || params[:refresh]
# refresh the database
@service = refresh
render status: @service.success? ? :ok : :unprocessable_entity
else
@service.add_notice('database updated')
render status: :ok
end
@service.add_notice('Database updated')
render status: :ok
else
@service.errors = @db.errors.full_messages
render status: :unprocessable_entity
......@@ -31,14 +24,14 @@ class DbsController < ApplicationController
private
def refresh
adapter = DbAdapter.new(@db.url)
service = UpdateDb.new(db: @db)
service.run(adapter.dbinfo, adapter.schema)
end
#def refresh
# adapter = DbAdapter.new(@db.url)
# service = UpdateDb.new(db: @db)
# service.run(adapter.dbinfo, adapter.schema)
#end
def db_params
params.permit(:url, :max_points_per_plot)
params.permit(:max_points_per_plot)
end
def set_db
......
......@@ -3,20 +3,26 @@
# controller for NILM objects
class NilmsController < ApplicationController
before_action :authenticate_user!
before_action :set_nilm, only: [:show, :update]
before_action :set_nilm, only: [:update, :refresh, :destroy]
before_action :authorize_viewer, only: [:show]
before_action :authorize_owner, only: [:update]
before_action :authorize_owner, only: [:update, :refresh]
before_action :authorize_admin, only: [:destroy]
# GET /nilms
# GET /nilms.json
def index
@nilms = current_user.retrieve_nilms_by_permission
end
# GET /nilms/1
# GET /nilms/1.json
def show
# renders nilms/show
# POST /nilms.json
def create
@service = CreateNilm.new
@service.run(name: nilm_params[:name],
url: nilm_params[:url],
description: nilm_params[:description],
owner: current_user)
@nilm = @service.nilm
render :show, status: @service.success? ? :ok : :unprocessable_entity
end
# PATCH/PUT /nilms/1
......@@ -24,14 +30,28 @@ class NilmsController < ApplicationController
def update
@service = StubService.new
if @nilm.update(nilm_params)
@service.add_notice('NILM Updated')
render status: :ok
@service.add_notice('Installation Updated')
render :show, status: :ok
else
@service.errors = @nilm.errors.full_messages
render status: :unprocessable_entity
render :show, status: :unprocessable_entity
end
end
# PATCH/PUT /nilms/1/refresh.json
def refresh
@service = UpdateNilm.new()
@service.run(@nilm)
render status: @service.success? ? :ok : :unprocessable_entity
end
# DELETE /nilms/1.json
def destroy
@service = StubService.new
@nilm.destroy
@service.set_notice('removed nilm')
end
private
# Use callbacks to share common setup or constraints between actions.
......@@ -45,6 +65,9 @@ class NilmsController < ApplicationController
end
#authorization based on nilms
def authorize_admin
head :unauthorized unless current_user.admins_nilm?(@nilm)
end
def authorize_owner
head :unauthorized unless current_user.owns_nilm?(@nilm)
end
......
......@@ -18,8 +18,8 @@ class Db < ApplicationRecord
return super unless super.nil? || super.empty?
# no default URL if no parent NILM available
return '--error, no parent NILM--' if nilm.nil?
# return the default URL"
"#{nilm.url}/nilmdb"
# return the default URL
nilm.url
end
def self.json_keys
......
......@@ -10,8 +10,8 @@ class Nilm < ApplicationRecord
has_many :user_groups, through: :permissions
#---Validations-----
validates :name, presence: true
validates :name, presence: true, uniqueness: true
validates :url, presence: true, uniqueness: true
def self.json_keys
[:id, :name, :description, :url]
......
......@@ -2,22 +2,42 @@
# Agent class for DbFolders
class CreateNilm
attr_accessor :errors, :warnings, :nilm
include ServiceStatus
attr_reader :nilm
def initialize
@errors = []
@warnings = []
end
def run(name:, url:, description: '', db_url: '')
# create the NILM object
@nilm = Nilm.new(name: name, url: url,
description: description)
@nilm.save
def run(name:, url:, owner:, description:'')
# note: url should be NilmDB url
@nilm = Nilm.new(name: name,
description: description,
url: url)
unless @nilm.valid?
add_errors(@nilm.errors.full_messages)
return self
end
# create the database object and update it
db = Db.create(nilm: @nilm, url: db_url)
# pass NILM url onto database since we are using
# a single endpoint (eventually this will be joule)
db = Db.new(nilm: @nilm, url: url)
unless db.valid?
add_errors(db.errors.full_messages.map{|msg| "Database: #{msg}"})
return self
end
#everything is valid, save the objects
nilm.save
db.save
#give the owner 'admin' permissions on the nilm
Permission.create(user: owner, nilm: nilm, role: 'admin')
#update the database
service = UpdateDb.new(db: db)
adapter = DbAdapter.new(db.url)
service.run(adapter.dbinfo, adapter.schema)
#errors on the database update are warnings on this service
#because we can still add the NILM, it will just be offline
add_warnings(service.errors + service.warnings)
add_notice('Created installation')
self
end
end
# frozen_string_literal: true
# Handles refreshing NILM's
class UpdateNilm
include ServiceStatus
def run(nilm)
if nilm.db.nil?
add_error('no associated db object')
return self
end
adapter = DbAdapter.new(nilm.url)
service = UpdateDb.new(db: nilm.db)
absorb_status(
service.run(adapter.dbinfo, adapter.schema)
)
self
end
end
......@@ -45,7 +45,7 @@ module ServiceStatus
def add_errors(messages)
messages.each{|m| self.add_error(m)}
end
def errors?
!@errors.empty?
end
......@@ -56,6 +56,10 @@ module ServiceStatus
@warnings << String(message)
end
def add_warnings(messages)
messages.each{|m| self.add_warning(m)}
end
def warnings?
!@warnings.empty?
end
......
json.extract! db, *Db.json_keys
json.contents do
root = db.root_folder
json.extract! root, *DbFolder.json_keys
if(db.root_folder != nil)
json.contents do
root = db.root_folder
json.extract! root, *DbFolder.json_keys
json.subfolders(root.subfolders) do |folder|
json.extract! folder, *DbFolder.json_keys
end
json.subfolders(root.subfolders) do |folder|
json.extract! folder, *DbFolder.json_keys
end
json.streams(root.db_streams) do |stream|
json.extract! stream, *DbStream.json_keys
json.elements(stream.db_elements) do |element|
json.extract! element, *DbElement.json_keys
json.streams(root.db_streams) do |stream|
json.extract! stream, *DbStream.json_keys
json.elements(stream.db_elements) do |element|
json.extract! element, *DbElement.json_keys
end
end
end
end
json.extract! nilm, *Nilm.json_keys
json.db_id nilm.db.id
json.available nilm.db.available
json.data do
json.partial! "nilms/nilm", nilm: @nilm
# nothing here
end
json.partial! "helpers/messages", service: @service
# frozen_string_literal: true
json.data do
json.extract! @nilm, *Nilm.json_keys
unless @nilm.db.nil?
json.db_id @nilm.db.id
json.available @nilm.db.available
#show the database which was refreshed
json.db do
json.partial! 'dbs/db', db: @nilm.db
end
end
end
json.partial! 'helpers/messages', service: @service
json.partial! "nilms/nilm", nilm: @nilm
# frozen_string_literal: true
json.data do
json.extract! @nilm, *Nilm.json_keys
unless @nilm.db.nil?
json.db_id @nilm.db.id
json.available @nilm.db.available
end
end
json.partial! 'helpers/messages', service: @service
Rails.application.routes.draw do
resources :nilms, only: [:index, :show, :update]
resources :nilms, only: [:index, :create, :update, :destroy] do
member do
put 'refresh'
end
end
resources :dbs, only: [:show, :update]
resources :db_folders, only: [:show, :update]
resources :db_streams, only: [:update]
......
......@@ -37,9 +37,8 @@ lab = FactoryGirl.create(:user_group, name: 'Lab', owner: john, members: [steve,
# create real nilms
nc = CreateNilm.new
nc.run(name: 'Local', url: 'http://localhost:8080')
nc.run(name: 'Local', url: 'http://localhost:8080', owner: john)
home = nc.nilm
FactoryGirl.create(:permission, nilm: home, user: john, role: 'admin')
FactoryGirl.create(:permission, nilm: home, user_group: donnals, role: 'owner')
FactoryGirl.create(:permission, nilm: home, user: steve, role: 'viewer')
......
......@@ -2,8 +2,8 @@
require 'rails_helper'
RSpec.describe DbFoldersController, type: :request do
let(:john) { create(:user, first_name: 'John') }
let(:steve) { create(:user, first_name: 'Steve') }
let(:john) { create(:confirmed_user, first_name: 'John') }
let(:steve) { create(:confirmed_user, first_name: 'Steve') }
let(:john_nilm) { create(:nilm, name: "John's NILM", admins: [john]) }
let(:john_folder) do
create(:db_folder, name: 'John Folder',
......@@ -20,10 +20,6 @@ RSpec.describe DbFoldersController, type: :request do
# index action does not exist
describe 'GET show' do
before do
john.confirm
steve.confirm
end
context 'with any permissions' do
it 'returns the db_folder as json' do
# john has some permission on all 3 nilms
......@@ -58,8 +54,6 @@ RSpec.describe DbFoldersController, type: :request do
describe 'PUT update' do
before do
john.confirm
steve.confirm
@mock_adapter = double(DbAdapter) # MockDbAdapter.new #instance_double(DbAdapter)
@db_success = { error: false, msg: 'success' }
@db_failure = { error: true, msg: 'dberror' }
......@@ -67,7 +61,6 @@ RSpec.describe DbFoldersController, type: :request do
end
context 'with owner permissions' do
it 'updates nilmdb and local database' do
@auth_headers = john.create_new_auth_token
expect(@mock_adapter).to receive(:set_folder_metadata)
......
......@@ -11,7 +11,6 @@ RSpec.describe DbsController, type: :request do
let(:hidden_nilm) { create(:nilm, name: "Private NILM", owners: [steve])}
# index action does not exist
describe 'GET show' do
before do
john.confirm
......@@ -56,23 +55,6 @@ RSpec.describe DbsController, type: :request do
steve.confirm
end
context 'with owner permissions' do
it 'refreshes db if URL is changed or refresh paramter is set' do
# TODO: this is a mess, figure out how instance doubles work
mock_adapter = spy #instance_double(DbAdapter)
allow(DbAdapter).to receive(:new).and_return(mock_adapter)
mock_service = spy #---_ this line is to provide dummy messages
allow(mock_service).to receive(:run).and_return(StubService.new)
allow(UpdateDb).to receive(:new).and_return(mock_service)
@auth_headers = john.create_new_auth_token
put "/dbs/#{john_nilm.db.id}.json",
params: {url: 'http://new/url'},
headers: @auth_headers
expect(response.status).to eq(200)
expect(mock_service).to have_received(:run)
# service is stubbed so message is empty
# expect(response).to have_notice_message
end
it 'returns 422 on invalid parameters' do
# max points must be a positive number
@auth_headers = john.create_new_auth_token
......@@ -86,14 +68,14 @@ RSpec.describe DbsController, type: :request do
it 'only allows configurable parameters to be changed' do
# should ignore size and accept max_points
@auth_headers = john.create_new_auth_token
size = john_nilm.db.size_db
url = john_nilm.db.url
num_points = john_nilm.db.max_points_per_plot
put "/dbs/#{john_nilm.db.id}.json",
params: {max_points_per_plot: num_points+10, size_db: size+10},
params: {max_points_per_plot: num_points+10, url: 'different'},
headers: @auth_headers
expect(response.status).to eq(200)
expect(response).to have_notice_message()
expect(john_nilm.db.reload.size_db).to eq(size)
expect(john_nilm.db.reload.url).to eq(url)
expect(john_nilm.db.max_points_per_plot).to eq(num_points+10)
end
end
......
......@@ -3,18 +3,15 @@
require 'rails_helper'
RSpec.describe NilmsController, type: :request do
let(:john) { create(:user, first_name: 'John') }
let(:steve) { create(:user, first_name: 'Steve') }
let(:john_nilm) { create(:nilm, name: "John's NILM", admins: [john]) }
let(:john) { create(:confirmed_user, first_name: 'John') }
let(:nicky) {create(:confirmed_user, first_name: 'Nicky')}
let(:steve) { create(:confirmed_user, first_name: 'Steve') }
let(:john_nilm) { create(:nilm, name: "John's NILM", admins: [john], owners: [nicky]) }
let(:lab_nilm) { create(:nilm, name: 'Lab NILM', owners: [john]) }
let(:pete_nilm) { create(:nilm, name: "Pete's NILM", viewers: [john])}
let(:hidden_nilm) { create(:nilm, name: "Private NILM", owners: [steve])}
describe 'GET index' do
before do
john.confirm
steve.confirm
end
context 'with authenticated user' do
it 'returns authorized nilms' do
# force lazy evaluation of let to build NILMs
......@@ -31,17 +28,12 @@ RSpec.describe NilmsController, type: :request do
context 'without sign-in' do
it 'returns unauthorized' do
get "/nilms.json"
expect(response.status).to eq(401)
expect(response).to have_http_status(:unauthorized)
end
end
end
describe 'PUT update' do
before do
john.confirm
steve.confirm
end
context 'with owner permissions' do
it 'updates parameters' do
@auth_headers = john.create_new_auth_token
......@@ -49,7 +41,7 @@ RSpec.describe NilmsController, type: :request do
put "/nilms/#{nilm.id}.json",
params: {id: nilm.id, name: "changed:#{nilm.id}"},
headers: @auth_headers
expect(response.status).to eq(200)
expect(response).to have_http_status(:ok)
expect(response).to have_notice_message
expect(nilm.reload.name).to eq("changed:#{nilm.id}")
end
......@@ -84,39 +76,102 @@ RSpec.describe NilmsController, type: :request do
end
end
describe 'GET show' do
before do
john.confirm
steve.confirm
end
context 'with any permissions' do
it 'returns the nilm as json' do
# john has some permission on all 3 nilms
describe 'PUT refresh' do
context 'with owner or admin' do
it 'updates the db' do
@auth_headers = john.create_new_auth_token
[pete_nilm, lab_nilm, john_nilm].each do |nilm|
get "/nilms/#{nilm.id}.json",
[john_nilm, lab_nilm].each do |nilm|
mock_service = UpdateNilm.new
expect(mock_service).to receive(:run).and_return StubService.new
allow(UpdateNilm).to receive(:new)
.and_return(mock_service)
put "/nilms/#{nilm.id}/refresh.json",
headers: @auth_headers
expect(response.status).to eq(200)
expect(response.header['Content-Type']).to include('application/json')
body = JSON.parse(response.body)
expect(body['name']).to eq(nilm.name)
expect(response).to have_http_status(:ok)
end
end
end
context 'without permissions' do
context 'with anyone else' do
it 'returns unauthorized' do
# steve does NOT have permissions on john_nilm
@auth_headers = steve.create_new_auth_token
get "/nilms/#{john_nilm.id}.json",
headers: @auth_headers
expect(response.status).to eq(401)
put "/nilms/#{john_nilm.id}/refresh.json",
headers: @auth_headers
expect(response).to have_http_status(:unauthorized)
end
end
context 'without sign-in' do
it 'returns unauthorized' do
put "/nilms/#{pete_nilm.id}/refresh.json"
expect(response).to have_http_status(:unauthorized)
end
end
end
describe 'POST create' do
context 'with authenticated user' do
it 'creates a NILM' do
@auth_headers = john.create_new_auth_token
post "/nilms.json",
params: {name: 'new', url: 'http://sampleurl/nilmdb'},
headers: @auth_headers
expect(response).to have_http_status(:ok)
#should have a warning that NILM cannot see database
expect(response).to have_warning_message
# make sure the NILM was built
nilm = Nilm.find_by_name('new')
expect(nilm).to_not be nil
expect(nilm.db.available).to be false
# user should be an admin
expect(john.admins_nilm?(nilm)).to be true
end
it 'returns errors on invalid request' do
# name must be unique
create(:nilm, url: 'http://can/only/be/one')
@auth_headers = john.create_new_auth_token
post "/nilms.json",
params: {name: 'CanOnlyBeOne', url: 'http://can/only/be/one'},
headers: @auth_headers
expect(response).to have_http_status(:unprocessable_entity)
expect(response).to have_error_message
# make sure the NILM was not built
expect(Nilm.where(url: 'http://can/only/be/one').count).to eq 1
end
end
context 'without sign-in' do
it 'returns unauthorized' do
# no headers: nobody is signed in, deny all
get "/nilms/#{john_nilm.id}.json"
expect(response.status).to eq(401)
post "/nilms.json",
params: {name: 'new', url: 'http://sampleurl/nilmdb'}
expect(response).to have_http_status(:unauthorized)
end
end
end
describe 'DELETE destroy' do
context 'with nilm admin' do
it 'removes the nilm' do
@auth_headers = john.create_new_auth_token
delete "/nilms/#{john_nilm.id}.json",
headers: @auth_headers
expect(response).to have_http_status(:ok)
expect(response).to have_notice_message
expect(Nilm.exists?(john_nilm.id)).to be false
end
end
context 'with anybody else' do
it 'returns unauthorized' do
@auth_headers = nicky.create_new_auth_token
delete "/nilms/#{john_nilm.id}.json",
headers: @auth_headers
expect(response).to have_http_status(:unauthorized)
expect(Nilm.exists?(john_nilm.id)).to be true
end
end
context 'without sign-in' do
it 'returns unauthorized' do
delete "/nilms/#{john_nilm.id}.json"
expect(response).to have_http_status(:unauthorized)
expect(Nilm.exists?(john_nilm.id)).to be true
end
end
end
......
......@@ -5,8 +5,9 @@ FactoryGirl.define do
factory :nilm do
db
name {Faker::StarWars.unique.planet}
name {Faker::Lorem.unique.words(3).join(' ')}
description { Faker::Lorem.sentence }
url {Faker::Internet.unique.url}
transient do
admins []
owners []
......
......@@ -23,6 +23,7 @@ RSpec.describe 'Db' do
expect(DbFolder.find_by_id(root_folder.id)).to be nil
end
#TODO: nilms will only have joule endpoint
describe 'url' do
it 'can be customized' do
db_x = Db.create(url: 'custom_string')
......@@ -31,7 +32,7 @@ RSpec.describe 'Db' do
it 'can be left as default' do
nilm = Nilm.create(url: 'base')
db_x = Db.new(nilm: nilm, url: '')
expect(db_x.url).to eq('base/nilmdb')
expect(db_x.url).to eq('base')
end
end
end
# frozen_string_literal: true
require 'rails_helper'
test_nilm_url = 'http://localhost:8080'
test_nilm_url = 'http://localhost:8080/nilmdb'
RSpec.describe 'CreateNilm' do
describe 'build' do
it 'creates and populates a Db object', :vcr do
# mock the database updater
service = instance_double(UpdateDb, run: '')
service = instance_double(UpdateDb,
run: StubService.new,
errors: ['cannot contact database'],
warnings: [])
allow(UpdateDb).to receive(:new).and_return(service)
user = create(:confirmed_user, first_name: "John")
# run the NILM creation
nilm_creator = CreateNilm.new
nilm_creator.run(name: 'test', url: test_nilm_url)
nilm_creator.run(
name: 'test',
description: 'test description',
url: test_nilm_url,
owner: user
)
# verify NILM components are present
expect(nilm_creator.nilm).to be_present
expect(nilm_creator.nilm.db).to be_present
nilm = nilm_creator.nilm
#update errors show up as warnings
expect(nilm_creator.warnings.length).to eq 1
expect(nilm).to_not be nil
expect(nilm.db).to be_present
# ...and the database has been populated
expect(service).to have_received(:run)
expect(user.owns_nilm?(nilm)).to be true
end
end
end
# frozen_string_literal: true
require 'rails_helper'
describe 'UpdateNilm' do
it 'updates the db when NilmDB is accessible' do
mock_service = instance_double(UpdateDb,
run: StubService.new,
errors: [],
warnings: [])
allow(UpdateDb).to receive(:new)
.and_return(mock_service)
nilm = create(:nilm)
service = UpdateNilm.new()
service.run(nilm)
expect(service.success?).to be true
end
it 'returns error if db is nil' do
nilm = Nilm.create(name: 'test', url: 'invalid')
mock_service = instance_double(UpdateDb,
run: StubService.new)
allow(UpdateDb).to receive(:new)
.and_return(mock_service)
service = UpdateNilm.new()
service.run(nilm)
expect(service.success?).to be false
expect(mock_service).to_not have_received(:run)
end
it 'returns error if db is offline' do
nilm = create(:nilm)
resp = StubService.new
resp.add_error('offline')
mock_service = instance_double(UpdateDb,
run: resp)
allow(UpdateDb).to receive(:new)
.and_return(mock_service)
nilm = create(:nilm)
service = UpdateNilm.new()
service.run(nilm)
expect(service.success?).to be false
end
end
......@@ -26,6 +26,34 @@ RSpec::Matchers.define :have_error_message do |regex|
end
end
RSpec::Matchers.define :have_warning_message do |regex|
match do |response|
body = JSON.parse(response.body)
# omit regex to test if there are any warning messages
if(regex == nil)
return false if body["messages"]["warnings"].empty?
return true
end
# specify regex to match a particular warning message
body["messages"]["warnings"].each do |warning|
return true if(regex.match(warning))
end
return false
end
failure_message do |str|
body = JSON.parse(response.body)
"Expected #{regex} to match in [ " +
body["messages"]["warnings"].join(", ")+" ]"
end
failure_message_when_negated do |str|
body = JSON.parse(response.body)
"Expected #{regex} to not match in:\n" +
body["messages"]["warnings"].join(", ")
end
end
RSpec::Matchers.define :have_notice_message do |regex|
match do |response|
body = JSON.parse(response.body)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment