Commit b8e46e81 by John Doe

working on folder edits

parent 4612cc91
...@@ -24,3 +24,4 @@ vendor/bundle ...@@ -24,3 +24,4 @@ vendor/bundle
spec/cassettes spec/cassettes
vagrant_boxes/*.log vagrant_boxes/*.log
vagrant_boxes/.vagrant vagrant_boxes/.vagrant
coverage
\ No newline at end of file
...@@ -62,6 +62,10 @@ group :development do ...@@ -62,6 +62,10 @@ group :development do
gem 'guard-rubocop' gem 'guard-rubocop'
end end
group :test do
gem 'simplecov', :require => false
end
group :development do group :development do
# Access an IRB console on exception pages or by using <%= console %> in views # Access an IRB console on exception pages or by using <%= console %> in views
gem 'web-console', '~> 2.0' gem 'web-console', '~> 2.0'
...@@ -69,4 +73,3 @@ group :development do ...@@ -69,4 +73,3 @@ group :development do
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring' gem 'spring'
end end
...@@ -81,6 +81,7 @@ GEM ...@@ -81,6 +81,7 @@ GEM
database_cleaner (1.5.3) database_cleaner (1.5.3)
debug_inspector (0.0.2) debug_inspector (0.0.2)
diff-lcs (1.2.5) diff-lcs (1.2.5)
docile (1.1.5)
erubis (2.7.0) erubis (2.7.0)
execjs (2.7.0) execjs (2.7.0)
factory_girl (4.7.0) factory_girl (4.7.0)
...@@ -238,6 +239,11 @@ GEM ...@@ -238,6 +239,11 @@ GEM
json (~> 1.7, >= 1.7.7) json (~> 1.7, >= 1.7.7)
rdoc (~> 4.0) rdoc (~> 4.0)
shellany (0.0.1) shellany (0.0.1)
simplecov (0.12.0)
docile (~> 1.1.0)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
slop (3.6.0) slop (3.6.0)
spring (1.7.2) spring (1.7.2)
spring-commands-rspec (1.0.4) spring-commands-rspec (1.0.4)
...@@ -303,6 +309,7 @@ DEPENDENCIES ...@@ -303,6 +309,7 @@ DEPENDENCIES
rubocop rubocop
sass-rails (~> 5.0) sass-rails (~> 5.0)
sdoc (~> 0.4.0) sdoc (~> 0.4.0)
simplecov
spring spring
spring-commands-rspec spring-commands-rspec
sqlite3 sqlite3
......
...@@ -36,26 +36,55 @@ class DbAdapter ...@@ -36,26 +36,55 @@ class DbAdapter
end end
end end
def set_folder_metadata(db_folder)
params = { path: "#{db_folder.path}/info",
data: __build_folder_metadata(db_folder) }.to_json
response = self.class.post("#{@url}/stream/update_metadata",
body: params,
headers: { 'Content-Type' => 'application/json' })
if response.code != 200
Rails.logger.warn (
"#{@url}: update_metadata(#{db_folder.path})"+
" => #{response.code}:#{response.body}"
)
return { error: true, msg: "error updating #{db_folder.path} metadata" }
end
{ error: false, msg: 'success' }
end
# convert folder attributes to __config_key json
def __build_folder_metadata(db_folder)
attribs = db_folder.attributes
.slice('name', 'description', 'hidden')
.to_json
{ config_key__: attribs }.to_json
end
# retrieve metadata for a particular stream # retrieve metadata for a particular stream
def __get_metadata(path) def __get_metadata(path)
dump = self.class.get("#{@url}/stream/get_metadata?path=#{path}") dump = self.class.get("#{@url}/stream/get_metadata?path=#{path}")
metadata = JSON.parse(dump.parsed_response['config_key__'] || '{}') # find legacy parameters in raw metadata
# Add plain-text metadata keys (retrofit for *info streams which keep metadata = dump.parsed_response.except('config_key__')
# attributes in seperate metadata tags # parse values from config_key entry if it exists
metadata.merge!(dump.parsed_response) config_key = JSON.parse(dump.parsed_response['config_key__'] || '{}')
# merge legacy data with config_key values
metadata.merge!(config_key)
# make sure nothing bad got in (eg extraneous metadata keys)
__sanitize_metadata(metadata) __sanitize_metadata(metadata)
end end
# make sure all the keys are valid parameters # make sure all the keys are valid parameters
# this function does not know the difference between folders and streams
# this *should* be ok as long as nobody tinkers with the config_key__ entries
def __sanitize_metadata(metadata) def __sanitize_metadata(metadata)
metadata.slice!('delete_locked', 'description', 'hidden', 'name', metadata.slice!('delete_locked', 'description', 'hidden',
'streams') 'name', 'name_abbrev', 'streams')
if(metadata['streams'] != nil) unless metadata['streams'].nil?
# sanitize 'streams' (elements) parameters # sanitize 'streams' (elements) parameters
element_attrs = DbElement.attribute_names.map(&:to_sym) element_attrs = DbElement.attribute_names.map(&:to_sym)
metadata['streams'].map! do |element| metadata['streams'].map! do |element|
element.symbolize_keys element.symbolize_keys
.slice(*element_attrs) .slice(*element_attrs)
end end
end end
metadata.symbolize_keys metadata.symbolize_keys
......
...@@ -7,10 +7,14 @@ class DbFoldersController < ApplicationController ...@@ -7,10 +7,14 @@ class DbFoldersController < ApplicationController
render json: folder, shallow: false render json: folder, shallow: false
end end
#TODO: add db attribute to folders and streams
#TODO: add timespan and disk usage stats to folders
#TODO: create info stream on folders on edit
def update def update
folder = DbFolder.find(params[:id]) folder = DbFolder.find(params[:id])
folder.update!(folder_params) adapter = DbAdapter.new(folder.db.url)
render json: folder service = EditFolder.new(adapter)
render json: service.run(folder, params)
end end
private private
......
...@@ -12,6 +12,8 @@ class DbFolder < ActiveRecord::Base ...@@ -12,6 +12,8 @@ class DbFolder < ActiveRecord::Base
has_many :db_streams, has_many :db_streams,
dependent: :destroy dependent: :destroy
validates_presence_of :name
def self.defined_attributes def self.defined_attributes
[:name, :description, :hidden] [:name, :description, :hidden]
end end
......
...@@ -9,25 +9,25 @@ class EditFolder ...@@ -9,25 +9,25 @@ class EditFolder
@db_adapter = db_adapter @db_adapter = db_adapter
end end
def run(db_stream, **attribs) def run(db_folder, **attribs)
# only accept valid attributes # only accept valid attributes
attribs.slice!([:name, :description]) attribs.slice!(:name, :description, :hidden)
# assign the new attributes and check if the # assign the new attributes and check if the
# result is valid (eg stream's can't have the same name) # result is valid (eg folder's can't have the same name)
db_stream.assign_attributes(attribs) db_folder.assign_attributes(attribs)
unless db_stream.valid? unless db_folder.valid?
add_error(db_stream.errors) add_error(db_folder.errors)
return self return self
end end
# local model checks out, update the remote NilmDB # local model checks out, update the remote NilmDB
@db_adapter.update_metadata(db_stream.path, attribs) status = @db_adapter.set_folder_metadata(db_folder)
# if there was an error don't save the model # if there was an error don't save the model
if db_adapter.status == ERROR if status[:error]
add_error(db_adapter.error_msg) add_error(status[:msg])
return self return self
end end
# everything went well, save the model # everything went well, save the model
db_stream.save! db_folder.save!
self self
end end
end end
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
require 'rails_helper' require 'rails_helper'
describe DbAdapter do describe DbAdapter do
# use the vagrant box loaded with example database
let (:url) {'http://localhost:8080/nilmdb'}
it 'retrieves basic schema', :vcr do it 'retrieves basic schema', :vcr do
# use the vagrant box loaded with example database adapter = DbAdapter.new(url)
db = double(url: 'http://localhost:8080/nilmdb')
adapter = DbAdapter.new(db.url)
adapter.schema.each do |entry| adapter.schema.each do |entry|
expect(entry).to include(:path, :attributes) expect(entry).to include(:path, :attributes)
expect(entry[:attributes]).to( expect(entry[:attributes]).to(
...@@ -15,5 +15,21 @@ describe DbAdapter do ...@@ -15,5 +15,21 @@ describe DbAdapter do
) )
end end
end end
describe 'set_folder_metadata' do
it 'updates config_key in metadata', :vcr do
adapter = DbAdapter.new(url)
folder = DbFolder.new(path: '/tutorial',
name: 'test', description: 'new')
result = adapter.set_folder_metadata(folder)
expect(result[:error]).to be false
end
it 'returns error on server failure', :vcr do
adapter = DbAdapter.new(url)
folder = DbFolder.new(path: '/badpath')
result = adapter.set_folder_metadata(folder)
expect(result[:error]).to be true
expect(result[:msg]).to match(/badpath/)
end
end
end end
...@@ -38,4 +38,12 @@ RSpec.describe 'DbFolder' do ...@@ -38,4 +38,12 @@ RSpec.describe 'DbFolder' do
expect(new_stream.db_folder).to eq(db_folder) expect(new_stream.db_folder).to eq(db_folder)
end end
end end
describe 'validation' do
let(:db_folder) { FactoryGirl.create(:db_folder) }
it 'forbids an empty name' do
db_folder.name = ''
expect(db_folder.valid?).to be false
end
end
end end
...@@ -3,15 +3,43 @@ ...@@ -3,15 +3,43 @@
require 'rails_helper' require 'rails_helper'
describe 'EditFolder service' do describe 'EditFolder service' do
let(:mock_adapter) {} let(:db_adapter) { instance_double(DbAdapter) }
let(:service) { EditFolder.new(mock_adapter) } let(:folder) { DbFolder.new(path: '/folder/path', name: 'old') }
let(:service) { EditFolder.new(db_adapter) }
# db adapter return values
let(:success) { { error: false, msg: '' } }
let(:error) { { error: true, msg: 'server error' } }
it 'changes folder attributes' do it 'changes folder attributes' do
folder = DbFolder.new(name: 'old') attribs = { id: 0, invalid_attrib: 'ignore',
service.run(folder, name: 'new') name: 'new', description: 'updated' }
expect(mock_adapter).to be called once allow(folder).to receive(:save!)
allow(db_adapter).to receive(:set_folder_metadata).and_return(success)
# run the service, it should call the adapter and save the folder
service.run(folder, attribs)
expect(db_adapter).to have_received(:set_folder_metadata).with(folder)
expect(folder.name).to eq('new') expect(folder.name).to eq('new')
expect(folder.description).to eq('updated')
expect(folder).to have_received(:save!)
end end
it 'does not change folder on a server error' it 'checks to make sure new attributes are valid' do
attribs = { name: '' } # cannot have empty name
allow(db_adapter).to receive(:set_folder_metadata).and_return(success)
# run the service, it shouldn't call the database adapter
service.run(folder, attribs)
expect(service.errors?).to be true
expect(db_adapter).to_not have_received(:set_folder_metadata)
end
it 'does not change folder on a server error' do
attribs = { name: 'new' }
allow(db_adapter).to receive(:set_folder_metadata).and_return(error)
allow(folder).to receive(:save!)
# run the service, it shouldn't save the folder object
service.run(folder, attribs)
expect(service.errors?).to be true
expect(folder).to_not have_received(:save!)
end
end end
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require 'rails_helper'
RSpec.describe 'InsertFile' do RSpec.describe 'InsertStream' do
describe 'insert_file' do describe 'insert_stream' do
# mock the DbService and DbBuilder # mock the DbService and DbBuilder
let(:db_service) { double(create_file: true) } let(:db_service) { double(create_stream: true) }
let(:db_builder) { double(build_path: '/test/file') } let(:db_builder) { double(build_path: '/test/file') }
# a file to insert # a stream to insert
let(:new_file) { FactoryGirl.build_stubbed(:db_file) } let(:new_stream) { FactoryGirl.build_stubbed(:db_stream) }
# a folder to insert it in # a folder to insert it in
let(:parent_folder) { FactoryGirl.build_stubbed(:db_folder) } let(:parent_folder) { FactoryGirl.build_stubbed(:db_folder) }
it 'adds the given file to the folder' do it 'adds the given stream to the folder' do
file_inserter = InsertFile.new(db_service: db_service, stream_inserter = InsertStream.new(db_service: db_service,
db_builder: db_builder) db_builder: db_builder)
file_inserter.insert_file(folder: parent_folder, file: new_file) stream_inserter.insert_stream(folder: parent_folder, stream: new_stream)
expect(new_file.db_folder).to eq(parent_folder) expect(new_stream.db_folder).to eq(parent_folder)
end end
it 'does not add the file if the db_service fails'
end end
end end
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
# users commonly want. # users commonly want.
# #
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
require 'simplecov'
SimpleCov.start
require 'webmock/rspec' require 'webmock/rspec'
......
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