Add DeviceConfigs. NEXT: Tests, FE Integration
parent
3a25110c8b
commit
fd5aea8013
|
@ -0,0 +1,35 @@
|
||||||
|
# Api::DeviceConfigController is the RESTful endpoint for managing key/value
|
||||||
|
# configuration pairs.
|
||||||
|
module Api
|
||||||
|
class DeviceConfigsController < Api::AbstractController
|
||||||
|
def create
|
||||||
|
mutate DeviceConfigs::Create.run(params.as_json, device: current_device)
|
||||||
|
end
|
||||||
|
|
||||||
|
def index
|
||||||
|
render json: configs
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
render json: config
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
mutate DeviceConfigs::Update.run(params.as_json, config: config)
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
render json: config.destroy! && ""
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def config
|
||||||
|
configs.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def configs
|
||||||
|
current_device.device_configs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,9 +1,11 @@
|
||||||
# Farmbot Device models all data related to an actual FarmBot in the real world.
|
# Farmbot Device models all data related to an actual FarmBot in the real world.
|
||||||
class Device < ApplicationRecord
|
class Device < ApplicationRecord
|
||||||
DEFAULT_MAX_LOGS = 100
|
DEFAULT_MAX_LOGS = 100
|
||||||
DEFAULT_MAX_IMAGES = 100
|
DEFAULT_MAX_IMAGES = 100
|
||||||
TIMEZONES = TZInfo::Timezone.all_identifiers
|
DEFAULT_MAX_CONFIGS = 100
|
||||||
BAD_TZ = "%{value} is not a valid timezone"
|
|
||||||
|
TIMEZONES = TZInfo::Timezone.all_identifiers
|
||||||
|
BAD_TZ = "%{value} is not a valid timezone"
|
||||||
|
|
||||||
has_many :users
|
has_many :users
|
||||||
has_many :farm_events, dependent: :destroy
|
has_many :farm_events, dependent: :destroy
|
||||||
|
@ -17,10 +19,9 @@ class Device < ApplicationRecord
|
||||||
has_many :images, dependent: :destroy
|
has_many :images, dependent: :destroy
|
||||||
has_many :webcam_feeds, dependent: :destroy
|
has_many :webcam_feeds, dependent: :destroy
|
||||||
has_many :sensor_readings, dependent: :destroy
|
has_many :sensor_readings, dependent: :destroy
|
||||||
validates :timezone, inclusion: { in: TIMEZONES,
|
|
||||||
message: BAD_TZ,
|
|
||||||
allow_nil: true }
|
|
||||||
validates_presence_of :name
|
validates_presence_of :name
|
||||||
|
validates :timezone,
|
||||||
|
inclusion: { in: TIMEZONES, message: BAD_TZ, allow_nil: true }
|
||||||
[FbosConfig, FirmwareConfig, WebAppConfig].map do |klass|
|
[FbosConfig, FirmwareConfig, WebAppConfig].map do |klass|
|
||||||
name = klass.table_name.singularize.to_sym
|
name = klass.table_name.singularize.to_sym
|
||||||
has_one name, dependent: :destroy
|
has_one name, dependent: :destroy
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
class DeviceConfig < ApplicationRecord
|
||||||
|
belongs_to :device
|
||||||
|
serialize :value
|
||||||
|
validate :primitives_only
|
||||||
|
|
||||||
|
PRIMITIVES_ONLY = "`value` must be a string, number or boolean"
|
||||||
|
|
||||||
|
def primitives_only
|
||||||
|
errors.add(:value, PRIMITIVES_ONLY) unless is_primitve
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_primitve
|
||||||
|
[String, Integer, Float, TrueClass, FalseClass].include?(value.class)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,24 @@
|
||||||
|
module DeviceConfigs
|
||||||
|
class Create < Mutations::Command
|
||||||
|
LIMIT = Device::DEFAULT_MAX_CONFIGS
|
||||||
|
|
||||||
|
required do
|
||||||
|
model :device, class: Device
|
||||||
|
string :key
|
||||||
|
duck :value, methods: [:to_json]
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate
|
||||||
|
# Ensure you're not over the limit
|
||||||
|
if device.device_configs.length > LIMIT
|
||||||
|
add_error :configs,
|
||||||
|
:configs,
|
||||||
|
"You are over the limit of #{LIMIT} configs."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
DeviceConfig.create!(inputs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,16 @@
|
||||||
|
module DeviceConfigs
|
||||||
|
class Update < Mutations::Command
|
||||||
|
required {
|
||||||
|
model :config, class: DeviceConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
optional do
|
||||||
|
string :key
|
||||||
|
duck :value, methods: [:to_json]
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
config.update_attributes!(inputs.except(:config)) && config
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,21 +2,21 @@ FarmBot::Application.routes.draw do
|
||||||
namespace :api, defaults: {format: :json}, constraints: { format: "json" } do
|
namespace :api, defaults: {format: :json}, constraints: { format: "json" } do
|
||||||
# Standard API Resources:
|
# Standard API Resources:
|
||||||
{
|
{
|
||||||
corpuses: [:index, :show],
|
corpuses: [:index, :show],
|
||||||
farm_events: [:create, :destroy, :index, :update],
|
farm_events: [:create, :destroy, :index, :update],
|
||||||
farmware_installations:
|
farmware_installations: [:create, :destroy, :index],
|
||||||
[:create, :destroy, :index],
|
images: [:create, :destroy, :index, :show],
|
||||||
images: [:create, :destroy, :index, :show],
|
logs: [:create, :destroy, :index],
|
||||||
logs: [:create, :destroy, :index],
|
password_resets: [:create, :update],
|
||||||
password_resets: [:create, :update],
|
peripherals: [:create, :destroy, :index, :update],
|
||||||
peripherals: [:create, :destroy, :index, :update],
|
sensors: [:create, :destroy, :index, :update],
|
||||||
sensors: [:create, :destroy, :index, :update],
|
regimens: [:create, :destroy, :index, :update],
|
||||||
regimens: [:create, :destroy, :index, :update],
|
sensor_readings: [:create, :destroy, :index, :show],
|
||||||
sensor_readings: [:create, :destroy, :index, :show],
|
sequences: [:create, :destroy, :index, :show, :update],
|
||||||
sequences: [:create, :destroy, :index, :show, :update],
|
tools: [:create, :destroy, :index, :show, :update],
|
||||||
tools: [:create, :destroy, :index, :show, :update],
|
webcam_feeds: [:create, :destroy, :index, :show, :update],
|
||||||
webcam_feeds: [:create, :destroy, :index, :show, :update],
|
device_configs: [:create, :destroy, :index, :show],
|
||||||
}.to_a.map{|(name, only)| resources name, only: only}
|
}.to_a.map { |(name, only)| resources name, only: only }
|
||||||
|
|
||||||
# Singular API Resources:
|
# Singular API Resources:
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
class CreateDeviceConfigs < ActiveRecord::Migration[5.1]
|
||||||
|
def change
|
||||||
|
create_table :device_configs do |t|
|
||||||
|
t.references :device, foreign_key: true
|
||||||
|
t.string :key, limit: 100
|
||||||
|
t.string :value, limit: 300
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
12
db/schema.rb
12
db/schema.rb
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20180226164100) do
|
ActiveRecord::Schema.define(version: 20180227172811) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -31,6 +31,15 @@ ActiveRecord::Schema.define(version: 20180226164100) do
|
||||||
t.index ["priority", "run_at"], name: "delayed_jobs_priority"
|
t.index ["priority", "run_at"], name: "delayed_jobs_priority"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "device_configs", force: :cascade do |t|
|
||||||
|
t.bigint "device_id"
|
||||||
|
t.string "key", limit: 100
|
||||||
|
t.string "value", limit: 300
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["device_id"], name: "index_device_configs_on_device_id"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "devices", id: :serial, force: :cascade do |t|
|
create_table "devices", id: :serial, force: :cascade do |t|
|
||||||
t.string "name"
|
t.string "name"
|
||||||
t.integer "max_log_count", default: 100
|
t.integer "max_log_count", default: 100
|
||||||
|
@ -434,6 +443,7 @@ ActiveRecord::Schema.define(version: 20180226164100) do
|
||||||
t.index ["device_id"], name: "index_webcam_feeds_on_device_id"
|
t.index ["device_id"], name: "index_webcam_feeds_on_device_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_foreign_key "device_configs", "devices"
|
||||||
add_foreign_key "edge_nodes", "sequences"
|
add_foreign_key "edge_nodes", "sequences"
|
||||||
add_foreign_key "farmware_installations", "devices"
|
add_foreign_key "farmware_installations", "devices"
|
||||||
add_foreign_key "log_dispatches", "devices"
|
add_foreign_key "log_dispatches", "devices"
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :device_config do
|
||||||
|
device
|
||||||
|
key { Faker::Pokemon.move }
|
||||||
|
value { Faker::Pokemon.move }
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,13 @@
|
||||||
|
require "spec_helper"
|
||||||
|
|
||||||
|
describe DeviceConfig do
|
||||||
|
it 'has a length limit' do
|
||||||
|
p = {
|
||||||
|
device: FactoryBot.create(:device),
|
||||||
|
key: Faker::Pokemon.name,
|
||||||
|
value: "===" * 300
|
||||||
|
}
|
||||||
|
expect { DeviceConfig.create!(p) }
|
||||||
|
.to raise_error(ActiveRecord::ValueTooLong)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,3 +1,4 @@
|
||||||
|
require "spec_helper"
|
||||||
|
|
||||||
describe Tool do
|
describe Tool do
|
||||||
describe 'names' do
|
describe 'names' do
|
||||||
|
|
Loading…
Reference in New Issue